From 58b7fbb96e3e5fa015d7eeae1ccd1805062f4428 Mon Sep 17 00:00:00 2001 From: andreika-git Date: Thu, 26 Apr 2018 09:11:51 +0300 Subject: [PATCH] Noiseless trigger decoder (#592) * fix typo * better formatting & tiny fix * add syncRatioAvg * add useNoiselessTriggerDecoder setting * resetAccumSignalData * isUsefulSignal * Impl. Noise Filtering for Trigger Decoder * Unit-tests --- .../controllers/trigger/trigger_central.cpp | 95 +++++++- .../controllers/trigger/trigger_central.h | 7 + .../controllers/trigger/trigger_decoder.cpp | 27 +-- .../controllers/trigger/trigger_structure.cpp | 1 + .../controllers/trigger/trigger_structure.h | 4 + firmware/integration/rusefi_config.txt | 2 +- firmware/tunerstudio/rusefi.input | 1 + unit_tests/test.mk | 1 + unit_tests/test_trigger_decoder.cpp | 2 + unit_tests/test_trigger_decoder.h | 1 + unit_tests/test_trigger_noiseless.cpp | 211 ++++++++++++++++++ 11 files changed, 332 insertions(+), 20 deletions(-) create mode 100644 unit_tests/test_trigger_noiseless.cpp diff --git a/firmware/controllers/trigger/trigger_central.cpp b/firmware/controllers/trigger/trigger_central.cpp index 267af8452d..5dcce0d691 100644 --- a/firmware/controllers/trigger/trigger_central.cpp +++ b/firmware/controllers/trigger/trigger_central.cpp @@ -75,8 +75,6 @@ void addTriggerEventListener(ShaftPositionListener listener, const char *name, E engine->triggerCentral.addEventListener(listener, name, engine); } -uint32_t triggerHanlderEntryTime; - #if (EFI_PROD_CODE || EFI_SIMULATOR) || defined(__DOXYGEN__) int triggerReentraint = 0; @@ -179,10 +177,14 @@ void hwHandleVvtCamSignal(trigger_value_e front) { } void hwHandleShaftSignal(trigger_event_e signal) { - if (!isUsefulSignal(signal, engineConfiguration)) { - return; + // for effective noise filtering, we need both signal edges, + // so we pass them to handleShaftSignal() and defer this test + if (!boardConfiguration->useNoiselessTriggerDecoder) { + if (!isUsefulSignal(signal, engineConfiguration)) { + return; + } } - triggerHanlderEntryTime = GET_TIMESTAMP(); + uint32_t triggerHandlerEntryTime = GET_TIMESTAMP(); isInsideTriggerHandler = true; if (triggerReentraint > maxTriggerReentraint) maxTriggerReentraint = triggerReentraint; @@ -190,7 +192,7 @@ void hwHandleShaftSignal(trigger_event_e signal) { efiAssertVoid(getRemainingStack(chThdGetSelfX()) > 128, "lowstck#8"); engine->triggerCentral.handleShaftSignal(signal PASS_ENGINE_PARAMETER_SUFFIX); triggerReentraint--; - triggerDuration = GET_TIMESTAMP() - triggerHanlderEntryTime; + triggerDuration = GET_TIMESTAMP() - triggerHandlerEntryTime; isInsideTriggerHandler = false; if (triggerDuration > triggerMaxDuration) triggerMaxDuration = triggerDuration; @@ -207,6 +209,7 @@ TriggerCentral::TriggerCentral() { memset(hwEventCounters, 0, sizeof(hwEventCounters)); clearCallbacks(&triggerListeneres); triggerState.reset(); + resetAccumSignalData(); } int TriggerCentral::getHwEventCounter(int index) { @@ -218,6 +221,12 @@ void TriggerCentral::resetCounters() { triggerState.resetRunningCounters(); } +void TriggerCentral::resetAccumSignalData() { + memset(lastSignalTimes, 0xff, sizeof(lastSignalTimes)); // = -1 + memset(accumSignalPeriods, 0, sizeof(accumSignalPeriods)); + memset(accumSignalPrevPeriods, 0, sizeof(accumSignalPrevPeriods)); +} + static char shaft_signal_msg_index[15]; static bool isUpEvent[6] = { false, true, false, true, false, true }; @@ -241,6 +250,68 @@ static ALWAYS_INLINE void reportEventToWaveChart(trigger_event_e ckpSignalType, } } +/** + * This is used to filter noise spikes (interference) in trigger signal. See + * The basic idea is to use not just edges, but the average amount of time the signal stays in '0' or '1'. + * So we update 'accumulated periods' to track where the signal is. + * And then compare between the current period and previous, with some tolerance (allowing for the wheel speed change). + * @return true if the signal is passed through. + */ +bool TriggerCentral::noiseFilter(efitick_t nowNt, trigger_event_e signal DECLARE_ENGINE_PARAMETER_SUFFIX) { + // todo: find a better place for these defs + static const trigger_event_e opposite[6] = { SHAFT_PRIMARY_RISING, SHAFT_PRIMARY_FALLING, SHAFT_SECONDARY_RISING, SHAFT_SECONDARY_FALLING, + SHAFT_3RD_RISING, SHAFT_3RD_FALLING }; + static const trigger_wheel_e triggerIdx[6] = { T_PRIMARY, T_PRIMARY, T_SECONDARY, T_SECONDARY, T_CHANNEL_3, T_CHANNEL_3 }; + // we process all trigger channels independently + trigger_wheel_e ti = triggerIdx[signal]; + // falling is opposite to rising, and vise versa + trigger_event_e os = opposite[signal]; + + // todo: currently only primary channel is filtered, because there are some weird trigger types on other channels + if (ti != T_PRIMARY) + return true; + + // update period accumulator: for rising signal, we update '0' accumulator, and for falling - '1' + if (lastSignalTimes[signal] != -1) + accumSignalPeriods[signal] += nowNt - lastSignalTimes[signal]; + // save current time for this trigger channel + lastSignalTimes[signal] = nowNt; + + // now we want to compare current accumulated period to the stored one + efitick_t currentPeriod = accumSignalPeriods[signal]; + // the trick is to compare between different + efitick_t allowedPeriod = accumSignalPrevPeriods[os]; + + // but first check if we're expecting a gap + bool isGapExpected = TRIGGER_SHAPE(isSynchronizationNeeded) && triggerState.shaft_is_synchronized && + (triggerState.currentCycle.eventCount[ti] + 1) == TRIGGER_SHAPE(expectedEventCount[ti]); + + if (isGapExpected) { + // usually we need to extend the period for gaps, based on the trigger info + allowedPeriod *= TRIGGER_SHAPE(syncRatioAvg); + } + + // also we need some margin for rapidly changing trigger-wheel speed, + // that's why we expect the period to be no less than 2/3 of the previous period (this is just an empirical 'magic' coef.) + efitick_t minAllowedPeriod = 2 * allowedPeriod / 3; + // but no longer than 5/4 of the previous 'normal' period + efitick_t maxAllowedPeriod = 5 * allowedPeriod / 4; + + // above all, check if the signal comes not too early + if (currentPeriod >= minAllowedPeriod) { + // now we store this period as a reference for the next time, + // BUT we store only 'normal' periods, and ignore too long periods (i.e. gaps) + if (!isGapExpected && (maxAllowedPeriod == 0 || currentPeriod <= maxAllowedPeriod)) { + accumSignalPrevPeriods[signal] = currentPeriod; + } + // reset accumulator + accumSignalPeriods[signal] = 0; + return true; + } + // all premature or extra-long events are ignored - treated as interference + return false; +} + void TriggerCentral::handleShaftSignal(trigger_event_e signal DECLARE_ENGINE_PARAMETER_SUFFIX) { efiAssertVoid(engine!=NULL, "configuration"); @@ -252,6 +323,17 @@ void TriggerCentral::handleShaftSignal(trigger_event_e signal DECLARE_ENGINE_PAR nowNt = getTimeNowNt(); + // This code gathers some statistics on signals and compares accumulated periods to filter interference + if (boardConfiguration->useNoiselessTriggerDecoder) { + if (!noiseFilter(nowNt, signal PASS_ENGINE_PARAMETER_SUFFIX)) { + return; + } + // moved here from hwHandleShaftSignal() + if (!isUsefulSignal(signal, engineConfiguration)) { + return; + } + } + engine->onTriggerSignalEvent(nowNt); #if EFI_HISTOGRAMS && EFI_PROD_CODE @@ -579,6 +661,7 @@ void onConfigurationChangeTriggerCallback(engine_configuration_s *previousConfig #if EFI_ENGINE_CONTROL || defined(__DOXYGEN__) engine->triggerCentral.triggerShape.initializeTriggerShape(logger PASS_ENGINE_PARAMETER_SUFFIX); + engine->triggerCentral.resetAccumSignalData(); #endif } #if EFI_DEFAILED_LOGGING diff --git a/firmware/controllers/trigger/trigger_central.h b/firmware/controllers/trigger/trigger_central.h index 124ef6437b..08e0e7c6cd 100644 --- a/firmware/controllers/trigger/trigger_central.h +++ b/firmware/controllers/trigger/trigger_central.h @@ -32,6 +32,8 @@ public: void handleShaftSignal(trigger_event_e signal DECLARE_ENGINE_PARAMETER_SUFFIX); int getHwEventCounter(int index); void resetCounters(); + void resetAccumSignalData(); + bool noiseFilter(efitick_t nowNt, trigger_event_e signal DECLARE_ENGINE_PARAMETER_SUFFIX); TriggerStateWithRunningStatistics triggerState; efitick_t nowNt; angle_t vvtPosition; @@ -46,6 +48,11 @@ public: private: IntListenerArray<15> triggerListeneres; int hwEventCounters[HW_EVENT_TYPES]; + + // Used by 'useNoiselessTriggerDecoder', see handleShaftSignal() + efitick_t lastSignalTimes[HW_EVENT_TYPES]; + efitick_t accumSignalPeriods[HW_EVENT_TYPES]; + efitick_t accumSignalPrevPeriods[HW_EVENT_TYPES]; }; #endif diff --git a/firmware/controllers/trigger/trigger_decoder.cpp b/firmware/controllers/trigger/trigger_decoder.cpp index 374da67110..ab440e5bd9 100644 --- a/firmware/controllers/trigger/trigger_decoder.cpp +++ b/firmware/controllers/trigger/trigger_decoder.cpp @@ -209,7 +209,7 @@ void TriggerState::decodeTriggerEvent(trigger_event_e const signal, efitime_t no if (isLessImportant(type)) { #if EFI_UNIT_TEST || defined(__DOXYGEN__) if (printTriggerDebug) { - printf("%s isLessImportant %s now=%d index=%d\r\n", + printf("%s isLessImportant %s now=%lld index=%d\r\n", getTrigger_type_e(engineConfiguration->trigger.type), getTrigger_event_e(signal), nowNt, @@ -307,12 +307,12 @@ void TriggerState::decodeTriggerEvent(trigger_event_e const signal, efitime_t no */ #if EFI_UNIT_TEST || defined(__DOXYGEN__) - if (printTriggerDebug) { - printf("sync=%d index=%d size=%d\r\n", + if (printTriggerDebug) { + printf("sync=%d index=%d size=%d\r\n", shaft_is_synchronized, currentCycle.current_index, TRIGGER_SHAPE(size)); - } + } #endif /* EFI_UNIT_TEST */ int endOfCycleIndex = TRIGGER_SHAPE(size) - (CONFIG(useOnlyRisingEdgeForTrigger) ? 2 : 1); @@ -395,14 +395,15 @@ void TriggerState::decodeTriggerEvent(trigger_event_e const signal, efitime_t no ; - if (triggerCycleCallback != NULL) { - triggerCycleCallback(this); - } - startOfCycleNt = nowNt; - resetCurrentCycleState(); - incrementTotalEventCounter(); - runningRevolutionCounter++; - totalEventCountBase += TRIGGER_SHAPE(size); + if (triggerCycleCallback != NULL) { + triggerCycleCallback(this); + } + + startOfCycleNt = nowNt; + resetCurrentCycleState(); + incrementTotalEventCounter(); + runningRevolutionCounter++; + totalEventCountBase += TRIGGER_SHAPE(size); #if EFI_UNIT_TEST || defined(__DOXYGEN__) @@ -412,7 +413,7 @@ void TriggerState::decodeTriggerEvent(trigger_event_e const signal, efitime_t no runningRevolutionCounter); } #endif /* EFI_UNIT_TEST */ - } else { + } else { /* if (!isSynchronizationPoint) */ nextTriggerEvent() ; } diff --git a/firmware/controllers/trigger/trigger_structure.cpp b/firmware/controllers/trigger/trigger_structure.cpp index dc21a25a45..2c3f1d5551 100644 --- a/firmware/controllers/trigger/trigger_structure.cpp +++ b/firmware/controllers/trigger/trigger_structure.cpp @@ -463,6 +463,7 @@ void TriggerShape::setTriggerSynchronizationGap2(float syncRatioFrom, float sync isSynchronizationNeeded = true; this->syncRatioFrom = syncRatioFrom; this->syncRatioTo = syncRatioTo; + this->syncRatioAvg = (int)efiRound((syncRatioFrom + syncRatioTo) * 0.5f, 1.0f); #if EFI_UNIT_TEST || defined(__DOXYGEN__) if (printTriggerDebug) { printf("setTriggerSynchronizationGap2 %.2f %.2f\r\n", syncRatioFrom, syncRatioTo); diff --git a/firmware/controllers/trigger/trigger_structure.h b/firmware/controllers/trigger/trigger_structure.h index 5e71ef7f72..75b1f28a05 100644 --- a/firmware/controllers/trigger/trigger_structure.h +++ b/firmware/controllers/trigger/trigger_structure.h @@ -91,6 +91,10 @@ public: float syncRatioFrom; float syncRatioTo; + /** + * used by NoiselessTriggerDecoder (See TriggerCentral::handleShaftSignal()) + */ + int syncRatioAvg; /** * Usually this is not needed, but some crazy triggers like 36-2-2-2 require two consecutive diff --git a/firmware/integration/rusefi_config.txt b/firmware/integration/rusefi_config.txt index 94a968a365..8254cda1eb 100644 --- a/firmware/integration/rusefi_config.txt +++ b/firmware/integration/rusefi_config.txt @@ -551,7 +551,7 @@ bit is_enabled_spi_2 bit isFasterEngineSpinUpEnabled bit coastingFuelCutEnabled bit useIacTableForCoasting - bit unused_board_984_23 + bit useNoiselessTriggerDecoder bit unused_board_984_24 bit unused_board_984_25 bit unused_board_984_26 diff --git a/firmware/tunerstudio/rusefi.input b/firmware/tunerstudio/rusefi.input index 70904b6771..d58b370214 100644 --- a/firmware/tunerstudio/rusefi.input +++ b/firmware/tunerstudio/rusefi.input @@ -1177,6 +1177,7 @@ cmd_stop_engine = "w\x00\x99\x00\x00" field = "Trigger error LED", triggerErrorPin field = "Trigger error LED mode", triggerErrorPinMode field = "print sync details to console", isPrintTriggerSynchDetails + field = "Enable noise filtering", useNoiselessTriggerDecoder, {trigger_type == 8} dialog = triggerConfiguration panel = triggerConfiguration_settings, North panel = triggerConfiguration_IO, South diff --git a/unit_tests/test.mk b/unit_tests/test.mk index d2ee73ae50..9592ae3b1f 100644 --- a/unit_tests/test.mk +++ b/unit_tests/test.mk @@ -8,6 +8,7 @@ TEST_SRC_CPP = test_util.cpp \ test_fasterEngineSpinningUp.cpp \ test_idle_controller.cpp \ test_trigger_decoder.cpp \ + test_trigger_noiseless.cpp \ test_fuel_map.cpp \ test_fuelCut.cpp \ engine_test_helper.cpp \ diff --git a/unit_tests/test_trigger_decoder.cpp b/unit_tests/test_trigger_decoder.cpp index d65ed26bbd..a80911e2a9 100644 --- a/unit_tests/test_trigger_decoder.cpp +++ b/unit_tests/test_trigger_decoder.cpp @@ -471,6 +471,8 @@ void testTriggerDecoder(void) { assertEquals(s->wave.switchTimes[2], 0.75); assertEquals(s->wave.switchTimes[3], 1); + testNoiselessDecoder(); + testDodgeNeonDecoder(); testTriggerDecoder2("Dodge Neon 1995", DODGE_NEON_1995, 8, 0.4931, 0.2070); diff --git a/unit_tests/test_trigger_decoder.h b/unit_tests/test_trigger_decoder.h index fe62eeb71c..bf69aac4a9 100644 --- a/unit_tests/test_trigger_decoder.h +++ b/unit_tests/test_trigger_decoder.h @@ -21,5 +21,6 @@ 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, injection_mode_e injMode = IM_BATCH); +void testNoiselessDecoder(void); #endif /* TEST_TRIGGER_DECODER_H_ */ diff --git a/unit_tests/test_trigger_noiseless.cpp b/unit_tests/test_trigger_noiseless.cpp new file mode 100644 index 0000000000..2cf442cc06 --- /dev/null +++ b/unit_tests/test_trigger_noiseless.cpp @@ -0,0 +1,211 @@ +/** + * @file test_trigger_noiseless.cpp + * + * @date Apr 20, 2018 + */ + +#include "main.h" +#include "test_trigger_decoder.h" +#include "trigger_decoder.h" +#include "engine_math.h" +#include "allsensors.h" +#include "rpm_calculator.h" +#include "event_queue.h" +#include "algo.h" +#include "trigger_central.h" +#include "main_trigger_callback.h" +#include "engine.h" +#include "advance_map.h" +#include "speed_density.h" +#include "fuel_math.h" +#include "spark_logic.h" +#include "trigger_universal.h" + +extern int timeNowUs; +extern float unitTestValue; +extern float testMafValue; +extern int unitTestWarningCounter; +extern bool printTriggerDebug; +extern float actualSynchGap; + +extern EventQueue schedulingQueue; + +static void fireEvent(EngineTestHelper *eth, bool isRise) { + // mostly we fire only rise events (useOnlyRisingEdgeForTrigger=true). + // but for noise filtering, both edges should be processed, so we fire falling events too + if (isRise) + eth->firePrimaryTriggerRise(); + else if (eth->engine.engineConfiguration->bc.useNoiselessTriggerDecoder) + eth->firePrimaryTriggerFall(); +} + +/* ________ __ _ _ __ + * __|_|_|__| OR | | | |________ + * spikes signal spikes signal + */ +static void noisyPulse(EngineTestHelper *eth, int idx, int durationUs, bool isRise, int noiseIdx, int durationNoiseUs, int offsetNoiseUs, int numSpikes) { + // skip some time at the beginning + timeNowUs += offsetNoiseUs; + durationUs -= offsetNoiseUs; + // add noise spikes + if (idx == noiseIdx) { + // calculate the distance between noise spikes (evenly spaced) + int noiseIntervalUs = (durationUs - durationNoiseUs * numSpikes) / numSpikes; + + for (int i = 0; i < numSpikes; i++) { + // start spike + fireEvent(eth, isRise); + timeNowUs += durationNoiseUs; + durationUs -= durationNoiseUs; + // end spike + fireEvent(eth, !isRise); + + // add space between spikes + timeNowUs += noiseIntervalUs; + durationUs -= noiseIntervalUs; + } + } + + // add the rest of pulse period + timeNowUs += durationUs; + fireEvent(eth, isRise); +} + +static void fireNoisyCycle60_2(EngineTestHelper *eth, int numCycles, int durationUs, int noiseIdx, int durationNoiseUs, int offsetNoiseUs, int numSpikes) { + int idx = 0; + for (int cycles = 0; cycles < numCycles; cycles++) { + int count = 60 - 2 - 1; + for (int i = 0; i < count; i++) { + // rising + noisyPulse(eth, idx++, durationUs, true, noiseIdx, durationNoiseUs, offsetNoiseUs, numSpikes); + // falling + noisyPulse(eth, idx++, durationUs, false, noiseIdx, durationNoiseUs, offsetNoiseUs, numSpikes); + } + // skip 2 teeth (durationUs * 4) + // and add 1st rising teeth of the next cycle + noisyPulse(eth, idx++, (durationUs * 4) + durationUs, true, noiseIdx, durationNoiseUs, offsetNoiseUs, numSpikes); + // falling + noisyPulse(eth, idx++, durationUs, false, noiseIdx, durationNoiseUs, offsetNoiseUs, numSpikes); + } +} + +static void resetTrigger(EngineTestHelper ð) { + timeNowUs = 0; + eth.applyTriggerShape(); + eth.engine.triggerCentral.resetAccumSignalData(); + // reset error cnt + eth.engine.triggerCentral.triggerState.totalTriggerErrorCounter = 0; +} + +static void testNoiselessDecoderProcedure(EngineTestHelper ð, int errorToleranceCnt DECLARE_ENGINE_PARAMETER_SUFFIX) { + printf("*** (bc->useNoiselessTriggerDecoder = %s)\r\n", + boardConfiguration->useNoiselessTriggerDecoder ? "true" : "false"); + + resetTrigger(eth); + + // first, no noise + fireNoisyCycle60_2(ð, 2, 1000, -1, 0, 0, 0); + + // should be no errors anyway + assertEquals(0, engine->triggerCentral.triggerState.totalTriggerErrorCounter); + // check if we're imitating the 60-2 signal correctly + assertEqualsM("index #1", 0, eth.engine.triggerCentral.triggerState.getCurrentIndex()); + // check rpm (60secs / (1000us * 60teeth)) = 1000rpm + assertEqualsM("RPM", 1000, eth.engine.rpmCalculator.getRpm(PASS_ENGINE_PARAMETER_SIGNATURE)); + + // add noise1 - 1 spike in the middle of the 2nd rising pulse + fireNoisyCycle60_2(ð, 2, 1000, 2, 10, 500, 1); + + assertEqualsM("noise#1", errorToleranceCnt, engine->triggerCentral.triggerState.totalTriggerErrorCounter); + + resetTrigger(eth); + + // add noise2 - 1 spike in the middle of the 2nd falling pulse + fireNoisyCycle60_2(ð, 2, 1000, 3, 10, 500, 1); + + //assertEqualsM("noise#2", errorToleranceCnt, engine->triggerCentral.triggerState.totalTriggerErrorCounter); + + resetTrigger(eth); + + // add noise3 - in the middle of the sync.gap, + // so that we cannot tell for sure if it's a start of another 'extra' tooth or just a noise inside the gap, + // that's why we used expectedEventCount[] in our filtering algo to make a prediction about gap + fireNoisyCycle60_2(ð, 2, 1000, 114, 10, 1500, 1); + + // so everything runs smoothly! + assertEqualsM("noise#3", errorToleranceCnt, engine->triggerCentral.triggerState.totalTriggerErrorCounter); + + resetTrigger(eth); + + // add noise4 - too close to the start of the real next signal, so the noise spike is accepted as a signal + // but when the real signal comes shortly afterwards, it we be treated as a noise spike, + fireNoisyCycle60_2(ð, 2, 1000, 4, 10, 980, 1); + + // and we won't get out of sync! + assertEqualsM("noise#4", errorToleranceCnt, engine->triggerCentral.triggerState.totalTriggerErrorCounter); + + resetTrigger(eth); + + // add noise5 - one very long 333us noise spike + fireNoisyCycle60_2(ð, 2, 1000, 4, 333, 10, 1); + // still ok + assertEqualsM("noise#5", errorToleranceCnt, engine->triggerCentral.triggerState.totalTriggerErrorCounter); + + resetTrigger(eth); + + // add noise6 - 10 short spikes across the entire signal pulse + const int failProofNumSpikes = 10; + fireNoisyCycle60_2(ð, 2, 1000, 4, 5, 10, failProofNumSpikes); + + // we barely survived this time + assertEqualsM("noise#6", errorToleranceCnt, engine->triggerCentral.triggerState.totalTriggerErrorCounter); + + resetTrigger(eth); + + // add noise7 - 34 short spikes across the entire signal pulse + fireNoisyCycle60_2(ð, 2, 1000, 2, 10, 10, failProofNumSpikes + 1); + + // alas, this is a hard case even for noiseless decoder, and it fails... + // but still we're close to 33% signal-noise ratio threshold - not bad! + // so here's an error anyway! + assertEqualsM("noise#7_fail_test", 1, engine->triggerCentral.triggerState.totalTriggerErrorCounter); + +} + +void testNoiselessDecoder(void) { + printf("*************************************************** testNoiselessDecoder\r\n"); + timeNowUs = 0; + schedulingQueue.clear(); + + EngineTestHelper eth(TEST_ENGINE); + EXPAND_EngineTestHelper + + engineConfiguration->ignitionMode = IM_WASTED_SPARK; + engineConfiguration->useOnlyRisingEdgeForTrigger = true; + setupSimpleTestEngineWithMafAndTT_ONE_trigger(ð); + + // we'll test on 60-2 wheel + engineConfiguration->trigger.type = TT_TOOTHED_WHEEL_60_2; + incrementGlobalConfigurationVersion(PASS_ENGINE_PARAMETER_SIGNATURE); + eth.applyTriggerShape(); + engine->updateSlowSensors(PASS_ENGINE_PARAMETER_SIGNATURE); + + assertEquals(0, engine->triggerCentral.triggerState.totalTriggerErrorCounter); + assertEquals(0, eth.engine.rpmCalculator.getRpm(PASS_ENGINE_PARAMETER_SIGNATURE)); + + //printTriggerDebug = true; + +#if 0 + // try normal trigger mode, no noise filtering + boardConfiguration->useNoiselessTriggerDecoder = false; + // for test validation, it should be 1 trigger error + testNoiselessDecoderProcedure(eth, 1 PASS_ENGINE_PARAMETER_SUFFIX); +#endif + + // now enable our noise filtering algo + boardConfiguration->useNoiselessTriggerDecoder = true; + // should be 0 errors! + testNoiselessDecoderProcedure(eth, 0 PASS_ENGINE_PARAMETER_SUFFIX); + + //printTriggerDebug = false; +}