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
This commit is contained in:
parent
e83b70080c
commit
58b7fbb96e
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue