Impl. faster engine spin-up mode (alpha-version) (#567)

* Impl. faster engine spin-up mode (alpha-version)

* A comment for RPM_LOW_THRESHOLD

* Faster engine stop detection

* Safety check for instant RPM on spinning-up

* rusefi.xml

* unit-tests & relevant fixes
This commit is contained in:
andreika-git 2018-03-11 03:58:51 +02:00 committed by rusefi
parent 29159e90a3
commit ac0189270e
20 changed files with 322 additions and 35 deletions

View File

@ -106,7 +106,8 @@
#undef EFI_CJ125_DIRECTLY_CONNECTED_UR
#define EFI_CJ125_DIRECTLY_CONNECTED_UR TRUE
#define RPM_LOW_THRESHOLD 60
#define RPM_LOW_THRESHOLD 8 // RPM=8 is an empirical lower sensitivity threshold of MAX9926 for 60-2
#define NO_RPM_EVENTS_TIMEOUT_SECS 5 // (RPM < 12)
#define EFI_PRINT_ERRORS_AS_WARNINGS TRUE

View File

@ -376,9 +376,6 @@ void Engine::watchdog() {
return;
}
efitick_t nowNt = getTimeNowNt();
#ifndef RPM_LOW_THRESHOLD
#define RPM_LOW_THRESHOLD 240
#endif
// note that we are ignoring the number of tooth here - we
// check for duration between tooth as if we only have one tooth per revolution which is not the case
#define REVOLUTION_TIME_HIGH_THRESHOLD (60 * 1000000LL / RPM_LOW_THRESHOLD)

View File

@ -35,6 +35,9 @@ EXTERN_ENGINE
extern EnginePins enginePins;
// Store current ignition mode for prepareIgnitionPinIndices()
static ignition_mode_e ignitionModeForPinIndices;
floatms_t getEngineCycleDuration(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
return getCrankshaftRevolutionTimeMs(rpm) * (engineConfiguration->operationMode == TWO_STROKE ? 1 : 2);
}
@ -461,7 +464,7 @@ int getCylinderId(int index DECLARE_ENGINE_PARAMETER_SUFFIX) {
}
static int getIgnitionPinForIndex(int i DECLARE_ENGINE_PARAMETER_SUFFIX) {
switch (CONFIG(ignitionMode)) {
switch (getIgnitionMode(PASS_ENGINE_PARAMETER_SIGNATURE)) {
case IM_ONE_COIL:
return 0;
break;
@ -479,6 +482,25 @@ static int getIgnitionPinForIndex(int i DECLARE_ENGINE_PARAMETER_SUFFIX) {
}
}
void prepareIgnitionPinIndices(ignition_mode_e ignitionMode DECLARE_ENGINE_PARAMETER_SUFFIX) {
if (ignitionMode != ignitionModeForPinIndices) {
#if EFI_ENGINE_CONTROL || defined(__DOXYGEN__)
for (int i = 0; i < CONFIG(specs.cylindersCount); i++) {
ENGINE(ignitionPin[i]) = getIgnitionPinForIndex(i PASS_ENGINE_PARAMETER_SUFFIX);
}
#endif /* EFI_ENGINE_CONTROL */
ignitionModeForPinIndices = ignitionMode;
}
}
ignition_mode_e getIgnitionMode(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
ignition_mode_e ignitionMode = CONFIG(ignitionMode);
// In spin-up cranking mode we don't have full phase sync. info yet, so wasted spark mode is better
if (ignitionMode == IM_INDIVIDUAL_COILS && ENGINE(rpmCalculator.isSpinningUp(PASS_ENGINE_PARAMETER_SIGNATURE)))
ignitionMode = IM_WASTED_SPARK;
return ignitionMode;
}
void TriggerShape::prepareShape(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
int engineCycleInt = (int) getEngineCycle(CONFIG(operationMode));
for (int angle = 0; angle < engineCycleInt; angle++) {
@ -515,9 +537,10 @@ void prepareOutputSignals(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
for (int i = 0; i < CONFIG(specs.cylindersCount); i++) {
ENGINE(angleExtra[i])= ENGINE(engineCycle) * i / CONFIG(specs.cylindersCount);
ENGINE(ignitionPin[i]) = getIgnitionPinForIndex(i PASS_ENGINE_PARAMETER_SUFFIX);
}
prepareIgnitionPinIndices(CONFIG(ignitionMode) PASS_ENGINE_PARAMETER_SUFFIX);
TRIGGER_SHAPE(prepareShape(PASS_ENGINE_PARAMETER_SIGNATURE));
}

View File

@ -73,6 +73,14 @@ float getEngineLoadT(DECLARE_ENGINE_PARAMETER_SIGNATURE);
floatms_t getSparkDwell(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX);
ignition_mode_e getIgnitionMode(DECLARE_ENGINE_PARAMETER_SIGNATURE);
/**
* This lightweight method is invoked in case of a configuration change or initialization.
* But also it's used for "Spinning-up to Cranking" transition.
*/
void prepareIgnitionPinIndices(ignition_mode_e ignitionMode DECLARE_ENGINE_PARAMETER_SUFFIX);
int getCylinderId(int index DECLARE_ENGINE_PARAMETER_SUFFIX);
void setFuelRpmBin(float from, float to DECLARE_ENGINE_PARAMETER_SUFFIX);

View File

@ -478,8 +478,9 @@ void mainTriggerCallback(trigger_event_e ckpSignalType, uint32_t trgEventIndex D
if (checkIfTriggerConfigChanged()) {
engine->ignitionEvents.isReady = false; // we need to rebuild ignition schedule
engine->injectionEvents.isReady = false;
// todo: move 'triggerIndexByAngle' change into trigger initialization, why is it invoked from here if it's only about trigger shape & optimization?
prepareOutputSignals(PASS_ENGINE_PARAMETER_SIGNATURE);
// moved 'triggerIndexByAngle' into trigger initialization (why was it invoked from here if it's only about trigger shape & optimization?)
// see initializeTriggerShape() -> prepareOutputSignals(PASS_ENGINE_PARAMETER_SIGNATURE)
// we need this to apply new 'triggerIndexByAngle' values
engine->periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
}

View File

@ -58,6 +58,7 @@ RpmCalculator::RpmCalculator() {
previousRpmValue = rpmValue = 0;
oneDegreeUs = NAN;
state = STOPPED;
isSpinning = false;
// we need this initial to have not_running at first invocation
lastRpmEventTimeNt = (efitime_t) -10 * US2NT(US_PER_SECOND_LL);
@ -69,11 +70,17 @@ RpmCalculator::RpmCalculator() {
}
bool RpmCalculator::isStopped(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
return state == STOPPED;
// Spinning-up with zero RPM means that the engine is not ready yet, and is treated as 'stopped'.
return state == STOPPED || (state == SPINNING_UP && rpmValue == 0);
}
bool RpmCalculator::isSpinningUp(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
return state == SPINNING_UP;
}
bool RpmCalculator::isCranking(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
return state == CRANKING;
// Spinning-up with non-zero RPM is suitable for all engine math, as good as cranking
return state == CRANKING || (state == SPINNING_UP && rpmValue > 0);
}
/**
@ -103,8 +110,12 @@ bool RpmCalculator::checkIfSpinning(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
* we have a trigger event between the time we've invoked 'getTimeNow' and here
*/
bool noRpmEventsForTooLong = nowNt - lastRpmEventTimeNt >= US2NT(NO_RPM_EVENTS_TIMEOUT_SECS * US_PER_SECOND_LL); // Anything below 60 rpm is not running
if (noRpmEventsForTooLong) {
setStopped(PASS_ENGINE_PARAMETER_SIGNATURE);
/**
* Also check if there were no trigger events
*/
bool noTriggerEventsForTooLong = nowNt - engine->triggerCentral.previousShaftEventTimeNt >= US2NT(US_PER_SECOND_LL);
if (noRpmEventsForTooLong || noTriggerEventsForTooLong) {
setStopSpinning(PASS_ENGINE_PARAMETER_SIGNATURE);
return false;
}
@ -118,29 +129,42 @@ void RpmCalculator::assignRpmValue(int value DECLARE_ENGINE_PARAMETER_SUFFIX) {
oneDegreeUs = NAN;
} else {
oneDegreeUs = getOneDegreeTimeUs(rpmValue);
if (previousRpmValue == 0) {
/**
* this would make sure that we have good numbers for first cranking revolution
* #275 cranking could be improved
*/
ENGINE(periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE));
}
}
}
void RpmCalculator::setRpmValue(int value DECLARE_ENGINE_PARAMETER_SUFFIX) {
assignRpmValue(value PASS_ENGINE_PARAMETER_SUFFIX);
if (previousRpmValue == 0 && rpmValue > 0) {
/**
* this would make sure that we have good numbers for first cranking revolution
* #275 cranking could be improved
*/
ENGINE(periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE));
}
spinning_state_e oldState = state;
// Change state
if (rpmValue == 0) {
state = STOPPED;
} else if (rpmValue >= CONFIG(cranking.rpm)) {
state = RUNNING;
} else if (state == STOPPED) {
} else if (state == STOPPED || state == SPINNING_UP) {
/**
* We are here if RPM is above zero but we have not seen running RPM yet.
* This gives us cranking hysteresis - a drop of RPM during running is still running, not cranking.
*/
state = CRANKING;
}
#if EFI_ENGINE_CONTROL || defined(__DOXYGEN__)
// This presumably fixes injection mode change for cranking-to-running transition.
// 'isSimultanious' flag should be updated for events if injection modes differ for cranking and running.
if (state != oldState) {
engine->injectionEvents.addFuelEvents(PASS_ENGINE_PARAMETER_SIGNATURE);
}
#endif
}
spinning_state_e RpmCalculator::getState(void) {
return state;
}
void RpmCalculator::onNewEngineCycle() {
@ -172,6 +196,27 @@ void RpmCalculator::setStopped(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
state = STOPPED;
}
void RpmCalculator::setStopSpinning(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
isSpinning = false;
setStopped(PASS_ENGINE_PARAMETER_SIGNATURE);
}
void RpmCalculator::setSpinningUp(efitime_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX) {
if (!boardConfiguration->isFasterEngineSpinUpEnabled)
return;
// Only a completely stopped and non-spinning engine can enter the spinning-up state.
if (isStopped(PASS_ENGINE_PARAMETER_SIGNATURE) && !isSpinning) {
state = SPINNING_UP;
isSpinning = true;
}
// update variables needed by early instant RPM calc.
if (isSpinningUp(PASS_ENGINE_PARAMETER_SIGNATURE)) {
engine->triggerCentral.triggerState.setLastEventTimeForInstantRpm(nowNt PASS_ENGINE_PARAMETER_SUFFIX);
}
// Update ignition pin indices if needed
prepareIgnitionPinIndices(getIgnitionMode(PASS_ENGINE_PARAMETER_SIGNATURE) PASS_ENGINE_PARAMETER_SUFFIX);
}
/**
* WARNING: this is a heavy method because 'getRpm()' is relatively heavy
*
@ -202,9 +247,10 @@ void rpmShaftPositionCallback(trigger_event_e ckpSignalType,
efiAssertVoid(getRemainingStack(chThdGetSelfX()) > 256, "lowstckRCL");
#endif
RpmCalculator *rpmState = &engine->rpmCalculator;
if (index == 0) {
ENGINE(m.beforeRpmCb) = GET_TIMESTAMP();
RpmCalculator *rpmState = &engine->rpmCalculator;
bool hadRpmRecently = rpmState->checkIfSpinning(PASS_ENGINE_PARAMETER_SIGNATURE);
@ -241,6 +287,17 @@ void rpmShaftPositionCallback(trigger_event_e ckpSignalType,
}
#endif
// Replace 'normal' RPM with instant RPM for the initial spin-up period
if (rpmState->isSpinningUp(PASS_ENGINE_PARAMETER_SIGNATURE)) {
int prevIndex;
int iRpm = engine->triggerCentral.triggerState.calculateInstantRpm(&prevIndex, nowNt PASS_ENGINE_PARAMETER_SUFFIX);
// validate instant RPM - we shouldn't skip the cranking state
iRpm = minI(iRpm, CONFIG(cranking.rpm) - 1);
rpmState->assignRpmValue(iRpm PASS_ENGINE_PARAMETER_SUFFIX);
#if 0
scheduleMsg(logger, "** RPM: idx=%d sig=%d iRPM=%d", index, ckpSignalType, iRpm);
#endif
}
}
static scheduling_s tdcScheduler[2];

View File

@ -29,6 +29,10 @@
#define NOISY_RPM -1
#define UNREALISTIC_RPM 30000
#ifndef RPM_LOW_THRESHOLD
#define RPM_LOW_THRESHOLD 240
#endif
#ifdef __cplusplus
typedef enum {
@ -66,7 +70,11 @@ public:
*/
bool isStopped(DECLARE_ENGINE_PARAMETER_SIGNATURE);
/**
* Returns true if the engine is cranking
* Returns true if the engine is spinning up
*/
bool isSpinningUp(DECLARE_ENGINE_PARAMETER_SIGNATURE);
/**
* Returns true if the engine is cranking OR spinning up
*/
bool isCranking(DECLARE_ENGINE_PARAMETER_SIGNATURE);
/**
@ -76,6 +84,20 @@ public:
bool checkIfSpinning(DECLARE_ENGINE_PARAMETER_SIGNATURE);
/**
* This accessor is used in unit-tests.
*/
spinning_state_e getState(void);
/**
* Should be called on every trigger event when the engine is just starting to spin up.
*/
void setSpinningUp(efitime_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX);
/**
* Called if the synchronization is lost due to a trigger timeout.
*/
void setStopSpinning(DECLARE_ENGINE_PARAMETER_SIGNATURE);
int getRpm(DECLARE_ENGINE_PARAMETER_SIGNATURE);
/**
* This method is invoked once per engine cycle right after we calculate new RPM value
@ -83,6 +105,11 @@ public:
void onNewEngineCycle();
uint32_t getRevolutionCounter(void);
void setRpmValue(int value DECLARE_ENGINE_PARAMETER_SUFFIX);
/**
* The same as setRpmValue() but without state change.
* We need this to be public because of calling rpmState->assignRpmValue() from rpmShaftPositionCallback()
*/
void assignRpmValue(int value DECLARE_ENGINE_PARAMETER_SUFFIX);
uint32_t getRevolutionCounterSinceStart(void);
/**
* RPM rate of change between current RPM and RPM measured during previous engine cycle
@ -110,10 +137,6 @@ private:
*/
void setStopped(DECLARE_ENGINE_PARAMETER_SIGNATURE);
/**
* The same as setRpmValue() but without state change
*/
void assignRpmValue(int value DECLARE_ENGINE_PARAMETER_SUFFIX);
/**
* This counter is incremented with each revolution of one of the shafts. Could be
* crankshaft could be camshaft.
@ -125,6 +148,12 @@ private:
volatile uint32_t revolutionCounterSinceStart;
spinning_state_e state;
/**
* True if the engine is spinning (regardless of its state), i.e. if shaft position changes.
* Needed by spinning-up logic.
*/
bool isSpinning;
};
/**

View File

@ -81,7 +81,7 @@ static void turnSparkPinHigh2(IgnitionEvent *event, IgnitionOutputPin *output) {
#if ! EFI_UNIT_TEST || defined(__DOXYGEN__)
if (GET_RPM() > 2 * engineConfiguration->cranking.rpm) {
const char *outputName = output->name;
if (prevSparkName == outputName && engineConfiguration->ignitionMode != IM_ONE_COIL) {
if (prevSparkName == outputName && getIgnitionMode(PASS_ENGINE_PARAMETER_SIGNATURE) != IM_ONE_COIL) {
warning(CUSTOM_OBD_SKIPPED_SPARK, "looks like skipped spark event %d %s", getRevolutionCounter(), outputName);
}
prevSparkName = outputName;
@ -230,7 +230,7 @@ void prepareIgnitionSchedule(IgnitionEvent *event DECLARE_ENGINE_PARAMETER_SUFFI
IgnitionOutputPin *output = &enginePins.coils[coilIndex];
IgnitionOutputPin *secondOutput;
if (CONFIG(ignitionMode) == IM_WASTED_SPARK && CONFIG(twoWireBatchIgnition)) {
if (getIgnitionMode(PASS_ENGINE_PARAMETER_SIGNATURE) == IM_WASTED_SPARK && CONFIG(twoWireBatchIgnition)) {
int secondIndex = index + CONFIG(specs.cylindersCount) / 2;
int secondCoilIndex = ID2INDEX(getCylinderId(secondIndex PASS_ENGINE_PARAMETER_SUFFIX));
secondOutput = &enginePins.coils[secondCoilIndex];
@ -288,7 +288,7 @@ static ALWAYS_INLINE void prepareIgnitionSchedule(DECLARE_ENGINE_PARAMETER_SIGNA
*/
float maxAllowedDwellAngle = (int) (getEngineCycle(engineConfiguration->operationMode) / 2); // the cast is about making Coverity happy
if (engineConfiguration->ignitionMode == IM_ONE_COIL) {
if (getIgnitionMode(PASS_ENGINE_PARAMETER_SIGNATURE) == IM_ONE_COIL) {
maxAllowedDwellAngle = getEngineCycle(engineConfiguration->operationMode) / engineConfiguration->specs.cylindersCount / 1.1;
}
@ -381,7 +381,7 @@ int getNumberOfSparks(ignition_mode_e mode DECLARE_ENGINE_PARAMETER_SUFFIX) {
* @see getInjectorDutyCycle
*/
percent_t getCoilDutyCycle(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
floatms_t totalPerCycle = 1/**getInjectionDuration(rpm PASS_ENGINE_PARAMETER_SUFFIX)*/ * getNumberOfSparks(engineConfiguration->ignitionMode PASS_ENGINE_PARAMETER_SUFFIX);
floatms_t totalPerCycle = 1/**getInjectionDuration(rpm PASS_ENGINE_PARAMETER_SUFFIX)*/ * getNumberOfSparks(getIgnitionMode(PASS_ENGINE_PARAMETER_SIGNATURE) PASS_ENGINE_PARAMETER_SUFFIX);
floatms_t engineCycleDuration = getCrankshaftRevolutionTimeMs(rpm) * (engineConfiguration->operationMode == TWO_STROKE ? 1 : 2);
return 100 * totalPerCycle / engineCycleDuration;
}

View File

@ -602,6 +602,10 @@ bool readIfTriggerConfigChangedForUnitTest(void) {
return isTriggerConfigChanged;
}
void resetTriggerConfigChangedForUnitTest(void) {
isTriggerConfigChanged = false;
}
void initTriggerCentral(Logging *sharedLogger) {
logger = sharedLogger;
strcpy((char*) shaft_signal_msg_index, "x_");

View File

@ -66,5 +66,6 @@ void resetMaxValues();
void onConfigurationChangeTriggerCallback(engine_configuration_s *previousConfiguration DECLARE_ENGINE_PARAMETER_SUFFIX);
bool checkIfTriggerConfigChanged(void);
bool readIfTriggerConfigChangedForUnitTest(void);
void resetTriggerConfigChangedForUnitTest(void);
#endif /* TRIGGER_CENTRAL_H_ */

View File

@ -169,6 +169,8 @@ void TriggerState::resetCurrentCycleState() {
void TriggerState::onSynchronizationLost(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
shaft_is_synchronized = false;
// Needed for early instant-RPM detection
engine->rpmCalculator.setStopSpinning(PASS_ENGINE_PARAMETER_SIGNATURE);
}
/**
@ -436,6 +438,10 @@ void TriggerState::decodeTriggerEvent(trigger_event_e const signal, efitime_t no
runtimeStatistics(nowNt PASS_ENGINE_PARAMETER_SUFFIX);
// Needed for early instant-RPM detection
if (!isInitializingTrigger) {
engine->rpmCalculator.setSpinningUp(nowNt PASS_ENGINE_PARAMETER_SUFFIX);
}
}
/**
@ -644,6 +650,9 @@ void TriggerShape::initializeTriggerShape(Logging *logger DECLARE_ENGINE_PARAMET
unlockAnyContext();
}
#endif
// Moved here from mainTriggerCallback()
prepareOutputSignals(PASS_ENGINE_PARAMETER_SIGNATURE);
}
static void onFindIndexCallback(TriggerState *state) {

View File

@ -134,8 +134,17 @@ public:
* instant RPM calculated at this trigger wheel tooth
*/
float instantRpmValue[PWM_PHASE_MAX_COUNT];
/**
* Stores last non-zero instant RPM value to fix early instability
*/
float prevInstantRpmValue;
float calculateInstantRpm(int *prevIndex, efitime_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX);
virtual void runtimeStatistics(efitime_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX);
/**
* Update timeOfLastEvent[] on every trigger event - even without synchronization
* Needed for early spin-up RPM detection.
*/
void setLastEventTimeForInstantRpm(efitime_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX);
};
angle_t getEngineCycle(operation_mode_e operationMode);

View File

@ -182,6 +182,12 @@ void TriggerState::runtimeStatistics(efitime_t nowNt DECLARE_ENGINE_PARAMETER_SU
TriggerStateWithRunningStatistics::TriggerStateWithRunningStatistics() {
instantRpm = 0;
prevInstantRpmValue = 0;
// avoid ill-defined instant RPM when the data is not gathered yet
efitime_t nowNt = getTimeNowNt();
for (int i = 0; i < PWM_PHASE_MAX_COUNT; i++) {
timeOfLastEvent[i] = nowNt;
}
}
float TriggerStateWithRunningStatistics::calculateInstantRpm(int *prevIndex, efitime_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX) {
@ -203,13 +209,26 @@ float TriggerStateWithRunningStatistics::calculateInstantRpm(int *prevIndex, efi
// todo: angle diff should be pre-calculated
fixAngle(angleDiff, "angleDiff");
// just for safety
if (time == 0)
return prevInstantRpmValue;
float instantRpm = (60000000.0 / 360 * US_TO_NT_MULTIPLIER) * angleDiff / time;
instantRpmValue[current_index] = instantRpm;
timeOfLastEvent[current_index] = nowNt;
// This fixes early RPM instability based on incomplete data
if (instantRpm < RPM_LOW_THRESHOLD)
return prevInstantRpmValue;
prevInstantRpmValue = instantRpm;
return instantRpm;
}
void TriggerStateWithRunningStatistics::setLastEventTimeForInstantRpm(efitime_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX) {
timeOfLastEvent[currentCycle.current_index] = nowNt;
}
void TriggerStateWithRunningStatistics::runtimeStatistics(efitime_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX) {
if (engineConfiguration->debugMode == DBG_INSTANT_RPM) {
int prevIndex;

View File

@ -70,6 +70,7 @@ EngineTestHelper::EngineTestHelper(engine_type_e engineType) : engine (&persiste
engine->triggerCentral.triggerShape.initializeTriggerShape(NULL PASS_ENGINE_PARAMETER_SUFFIX);
engine->triggerCentral.addEventListener(rpmShaftPositionCallback, "rpm reporter", engine);
engine->triggerCentral.addEventListener(mainTriggerCallback, "main loop", engine);
resetTriggerConfigChangedForUnitTest();
}
void EngineTestHelper::firePrimaryTriggerRise() {

View File

@ -29,6 +29,7 @@
#include "test_signal_executor.h"
#include "trigger_central.h"
#include "test_startOfCrankingPrimingPulse.h"
#include "test_fasterEngineSpinningUp.h"
#include "test_util.h"
#include "map_resize.h"
#include "engine_math.h"
@ -74,6 +75,7 @@ int main(void) {
testFindIndex();
testPlainCrankingWithoutAdvancedFeatures();
testStartOfCrankingPrimingPulse();
testFasterEngineSpinningUp();
testInterpolate2d();
testGpsParser();
testMisc();

View File

@ -5,6 +5,7 @@ TEST_SRC_CPP = test_util.cpp \
test_basic_math/test_interpolation_3d.cpp \
test_data_structures/test_engine_math.cpp \
test_startOfCrankingPrimingPulse.cpp \
test_fasterEngineSpinningUp.cpp \
test_idle_controller.cpp \
test_trigger_decoder.cpp \
test_fuel_map.cpp \

View File

@ -0,0 +1,107 @@
/*
* test_fasterEngineSpinningUp.cpp
*
* Created on: Mar 6, 2018
*/
#include "engine_math.h"
#include "test_fasterEngineSpinningUp.h"
#include "test_trigger_decoder.h"
#include "event_queue.h"
#include "unit_test_framework.h"
extern EventQueue schedulingQueue;
extern int timeNowUs;
extern EnginePins enginePins;
void testFasterEngineSpinningUp() {
// this is just a reference unit test implementation
printf("*************************************************** testFasterEngineSpinningUp\r\n");
EngineTestHelper eth(TEST_ENGINE);
EXPAND_EngineTestHelper
// turn on FasterEngineSpinUp mode
engineConfiguration->bc.isFasterEngineSpinUpEnabled = true;
// set ignition mode
engineConfiguration->ignitionMode = IM_INDIVIDUAL_COILS;
// set cranking threshold (used below)
engineConfiguration->cranking.rpm = 999;
// set sequential injection mode to test auto-change to simultaneous when spinning-up
setupSimpleTestEngineWithMafAndTT_ONE_trigger(&eth, IM_SEQUENTIAL);
// check if it's true
assertEquals(IM_SEQUENTIAL, engine->getCurrentInjectionMode(PASS_ENGINE_PARAMETER_SIGNATURE));
assertEquals(IM_INDIVIDUAL_COILS, getIgnitionMode(PASS_ENGINE_PARAMETER_SIGNATURE));
// check if the engine has the right state
assertEquals(STOPPED, engine->rpmCalculator.getState());
// check RPM
assertEqualsM("RPM=0", 0, engine->rpmCalculator.getRpm(PASS_ENGINE_PARAMETER_SIGNATURE));
// the queue should be empty, no trigger events yet
assertEqualsM("plain#1", 0, schedulingQueue.size());
// check all events starting from now
int timeStartUs = timeNowUs;
// advance 1 revolution
timeNowUs += MS2US(200);
eth.firePrimaryTriggerRise();
// check if the mode is changed
assertEquals(SPINNING_UP, engine->rpmCalculator.getState());
// due to isFasterEngineSpinUp=true, we should have already detected RPM!
assertEqualsM("RPM#1", 300, engine->rpmCalculator.getRpm(PASS_ENGINE_PARAMETER_SIGNATURE));
// two simultaneous injections
assertEqualsM("plain#1", 4, schedulingQueue.size());
// test if they are simultaneous
assertEquals(IM_SIMULTANEOUS, engine->getCurrentInjectionMode(PASS_ENGINE_PARAMETER_SIGNATURE));
// test if ignition mode is temporary changed to wasted spark, if set to ind.coils
assertEquals(IM_WASTED_SPARK, getIgnitionMode(PASS_ENGINE_PARAMETER_SIGNATURE));
// check real events
assertEvent5("inj start#1", 0, (void*)startSimultaniousInjection, timeStartUs, MS2US(200) + 97975);
assertEvent5("inj end#1", 1, (void*)endSimultaniousInjection, timeStartUs, MS2US(200) + 100000);
// skip the rest of the cycle
timeNowUs += MS2US(200);
eth.firePrimaryTriggerFall();
// now clear and advance more
eth.clearQueue();
timeNowUs += MS2US(200);
eth.firePrimaryTriggerRise();
// check if the mode is changed when fully synched
assertEquals(CRANKING, engine->rpmCalculator.getState());
// check RPM
assertEqualsM("RPM#2", 300, engine->rpmCalculator.getRpm(PASS_ENGINE_PARAMETER_SIGNATURE));
// test if they are simultaneous in cranking mode too
assertEquals(IM_SIMULTANEOUS, engine->getCurrentInjectionMode(PASS_ENGINE_PARAMETER_SIGNATURE));
// test if ignition mode is restored to ind.coils
assertEquals(IM_INDIVIDUAL_COILS, getIgnitionMode(PASS_ENGINE_PARAMETER_SIGNATURE));
// two simultaneous injections
assertEqualsM("plain#2", 4, schedulingQueue.size());
// check real events
assertEvent5("inj start#2", 0, (void*)startSimultaniousInjection, timeNowUs, 97975);
assertEvent5("inj end#2", 1, (void*)endSimultaniousInjection, timeNowUs, 100000);
// skip, clear & advance 1 more revolution at higher RPM
timeNowUs += MS2US(60);
eth.firePrimaryTriggerFall();
eth.clearQueue();
timeStartUs = timeNowUs;
eth.fireTriggerEvents2(1, MS2US(60));
// check if the mode is now changed to 'running' at higher RPM
assertEquals(RUNNING, engine->rpmCalculator.getState());
// check RPM
assertEqualsM("RPM#3", 1000, engine->rpmCalculator.getRpm(PASS_ENGINE_PARAMETER_SIGNATURE));
// check if the injection mode is back to sequential now
assertEquals(IM_SEQUENTIAL, engine->getCurrentInjectionMode(PASS_ENGINE_PARAMETER_SIGNATURE));
// 4 sequential injections for the full cycle
assertEqualsM("plain#3", 8, schedulingQueue.size());
// check real events for sequential injection
// Note: See addFuelEvents() fix inside setRpmValue()!
assertEvent5("inj start#3", 0, (void*)seTurnPinHigh, timeStartUs, MS2US(60) + 27974);
assertEvent5("inj end#3", 1, (void*)seTurnPinLow, timeStartUs, MS2US(60) + 27974 + 3000);
}

View File

@ -0,0 +1,14 @@
/*
* test_fasterEngineSpinningUp.h
*
* Created on: Mar 6, 2018
*/
#ifndef TEST_FASTERENGINESPINNINGUP_H_
#define TEST_FASTERENGINESPINNINGUP_H_
#include "main.h"
void testFasterEngineSpinningUp();
#endif /* TEST_FASTERENGINESPINNINGUP_H_ */

View File

@ -315,8 +315,9 @@ void testRpmCalculator(void) {
timeNowUs = 0;
assertEquals(0, eth.engine.rpmCalculator.getRpm(PASS_ENGINE_PARAMETER_SIGNATURE));
assertEquals(4, TRIGGER_SHAPE(triggerIndexByAngle[240]));
assertEquals(4, TRIGGER_SHAPE(triggerIndexByAngle[241]));
// triggerIndexByAngle update is now fixed! prepareOutputSignals() wasn't reliably called
assertEquals(5, TRIGGER_SHAPE(triggerIndexByAngle[240]));
assertEquals(5, TRIGGER_SHAPE(triggerIndexByAngle[241]));
eth.fireTriggerEvents(48);
@ -619,7 +620,7 @@ static void assertInjectionEvent(const char *msg, InjectionEvent *ev, int inject
assertEqualsM4(msg, " event offset", angleOffset, ev->injectionStart.angleOffset);
}
void setupSimpleTestEngineWithMafAndTT_ONE_trigger(EngineTestHelper *eth) {
void setupSimpleTestEngineWithMafAndTT_ONE_trigger(EngineTestHelper *eth, injection_mode_e injMode) {
Engine *engine = &eth->engine;
EXPAND_Engine
@ -629,7 +630,10 @@ void setupSimpleTestEngineWithMafAndTT_ONE_trigger(EngineTestHelper *eth) {
assertEquals(LM_PLAIN_MAF, engineConfiguration->fuelAlgorithm);
engineConfiguration->isIgnitionEnabled = false; // let's focus on injection
engineConfiguration->specs.cylindersCount = 4;
engineConfiguration->injectionMode = IM_BATCH;
// a bit of flexibility - the mode may be changed by some tests
engineConfiguration->injectionMode = injMode;
// set cranking mode (it's used by getCurrentInjectionMode())
engineConfiguration->crankingInjectionMode = IM_SIMULTANEOUS;
setArrayValues(config->cltFuelCorrBins, CLT_CURVE_SIZE, 1);
setArrayValues(engineConfiguration->injector.battLagCorr, VBAT_INJECTOR_CURVE_SIZE, 0);

View File

@ -20,6 +20,6 @@ void testRpmCalculator(void);
void testStartupFuelPumping(void);
void test1995FordInline6TriggerDecoder(void);
void testTriggerDecoder2(const char *msg, engine_type_e type, int synchPointIndex, float channel1duty, float channel2duty);
void setupSimpleTestEngineWithMafAndTT_ONE_trigger(EngineTestHelper *eth);
void setupSimpleTestEngineWithMafAndTT_ONE_trigger(EngineTestHelper *eth, injection_mode_e injMode = IM_BATCH);
#endif /* TEST_TRIGGER_DECODER_H_ */