rusefi/firmware/controllers/trigger/trigger_decoder.cpp

729 lines
24 KiB
C++
Raw Normal View History

2015-07-10 06:01:56 -07:00
/**
* @file trigger_decoder.cpp
*
* @date Dec 24, 2013
2020-01-07 21:02:40 -08:00
* @author Andrey Belomutskiy, (c) 2012-2020
2015-07-10 06:01:56 -07:00
*
2019-09-03 20:35:49 -07:00
*
*
* enable trigger_details
*
2015-07-10 06:01:56 -07:00
* This file is part of rusEfi - see http://rusefi.com
*
* rusEfi is free software; you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
2022-09-15 18:45:48 -07:00
#include <rusefi/isnan.h>
#include <rusefi/math.h>
2022-09-15 19:06:16 -07:00
#include "global_shared.h"
2022-09-15 18:45:48 -07:00
#include "loggingcentral.h"
#include "error_handling.h"
2022-11-05 12:18:50 -07:00
#include "perf_trace.h"
2022-09-15 18:45:48 -07:00
#include "engine_configuration.h"
#include "trigger_central.h"
#include "trigger_decoder.h"
2022-11-05 18:43:12 -07:00
/**
* decoder depends on current RPM for error condition logic
*/
2022-09-15 18:45:48 -07:00
#include "sensor.h"
#include "engine_state.h"
#include "engine_math.h"
2022-11-05 18:43:12 -07:00
/**
* decoder uses TriggerStimulatorHelper in findTriggerZeroEventIndex
*/
2015-09-23 18:02:33 -07:00
#include "trigger_simulator.h"
2015-09-12 18:03:09 -07:00
#ifndef NOISE_RATIO_THRESHOLD
#define NOISE_RATIO_THRESHOLD 3000
#endif
TriggerDecoderBase::TriggerDecoderBase(const char* p_name)
: name(p_name)
{
TriggerDecoderBase::resetState();
2019-01-31 14:55:23 -08:00
}
2022-05-10 01:41:39 -07:00
bool TriggerDecoderBase::getShaftSynchronized() {
2021-07-03 07:37:03 -07:00
return shaft_is_synchronized;
}
2022-05-10 01:41:39 -07:00
void TriggerDecoderBase::setShaftSynchronized(bool value) {
if (value) {
if (!shaft_is_synchronized) {
// just got synchronized
mostRecentSyncTime = getTimeNowNt();
}
} else {
// sync loss
mostRecentSyncTime = 0;
}
shaft_is_synchronized = value;
}
2022-11-05 21:32:38 -07:00
void TriggerDecoderBase::resetState() {
setShaftSynchronized(false);
2019-01-31 14:55:23 -08:00
toothed_previous_time = 0;
memset(toothDurations, 0, sizeof(toothDurations));
crankSynchronizationCounter = 0;
2019-01-31 14:55:23 -08:00
totalTriggerErrorCounter = 0;
orderingErrorCounter = 0;
m_timeSinceDecodeError.init();
2019-01-31 14:55:23 -08:00
prevSignal = SHAFT_PRIMARY_FALLING;
startOfCycleNt = 0;
resetCurrentCycleState();
totalEventCountBase = 0;
isFirstEvent = true;
}
void TriggerDecoderBase::setTriggerErrorState(int errorIncrement) {
m_timeSinceDecodeError.reset();
totalTriggerErrorCounter += errorIncrement;
2020-01-26 03:28:33 -08:00
}
2022-05-10 01:41:39 -07:00
void TriggerDecoderBase::resetCurrentCycleState() {
2019-01-31 14:55:23 -08:00
memset(currentCycle.eventCount, 0, sizeof(currentCycle.eventCount));
currentCycle.current_index = 0;
}
2022-04-16 14:07:57 -07:00
#if EFI_SHAFT_POSITION_INPUT
PrimaryTriggerDecoder::PrimaryTriggerDecoder(const char* p_name)
: TriggerDecoderBase(p_name)
{
2019-01-31 14:55:23 -08:00
}
2019-04-12 19:07:03 -07:00
#if ! EFI_PROD_CODE
2015-07-10 06:01:56 -07:00
bool printTriggerDebug = false;
2020-07-19 12:47:21 -07:00
bool printTriggerTrace = false;
2015-07-10 06:01:56 -07:00
#endif /* ! EFI_PROD_CODE */
2022-05-10 01:41:39 -07:00
void TriggerWaveform::initializeSyncPoint(TriggerDecoderBase& state,
const TriggerConfiguration& triggerConfiguration) {
triggerShapeSynchPointIndex = state.findTriggerZeroEventIndex(*this, triggerConfiguration);
2020-08-26 17:57:11 -07:00
}
void TriggerFormDetails::prepareEventAngles(TriggerWaveform *shape) {
2021-07-02 16:49:00 -07:00
int triggerShapeSynchPointIndex = shape->triggerShapeSynchPointIndex;
if (triggerShapeSynchPointIndex == EFI_ERROR_CODE) {
return;
}
2021-11-24 19:17:29 -08:00
angle_t firstAngle = shape->getAngle(triggerShapeSynchPointIndex);
assertAngleRange(firstAngle, "firstAngle", ObdCode::CUSTOM_TRIGGER_SYNC_ANGLE);
int riseOnlyIndex = 0;
2022-03-22 13:53:24 -07:00
size_t length = shape->getLength();
2020-08-25 09:45:18 -07:00
memset(eventAngles, 0, sizeof(eventAngles));
2020-08-24 21:59:07 -07:00
// this may be <length for some triggers like symmetrical crank Miata NB
2022-03-22 13:53:24 -07:00
size_t triggerShapeLength = shape->getSize();
assertAngleRange(shape->triggerShapeSynchPointIndex, "triggerShapeSynchPointIndex", ObdCode::CUSTOM_TRIGGER_SYNC_ANGLE2);
efiAssertVoid(ObdCode::CUSTOM_TRIGGER_CYCLE, getTriggerCentral()->engineCycleEventCount != 0, "zero engineCycleEventCount");
2022-03-22 13:53:24 -07:00
for (size_t eventIndex = 0; eventIndex < length; eventIndex++) {
if (eventIndex == 0) {
// explicit check for zero to avoid issues where logical zero is not exactly zero due to float nature
eventAngles[0] = 0;
// this value would be used in case of front-only
eventAngles[1] = 0;
} else {
// Rotate the trigger around so that the sync point is at position 0
auto wrappedIndex = (shape->triggerShapeSynchPointIndex + eventIndex) % length;
// Compute this tooth's position within the trigger definition
// (wrap, as the trigger def may be smaller than total trigger length)
auto triggerDefinitionIndex = wrappedIndex % triggerShapeLength;
// Compute the relative angle of this tooth to the sync point's tooth
float angle = shape->getAngle(wrappedIndex) - firstAngle;
efiAssertVoid(ObdCode::CUSTOM_TRIGGER_CYCLE, !cisnan(angle), "trgSyncNaN");
// Wrap the angle back in to [0, 720)
2023-10-19 14:34:29 -07:00
wrapAngle(angle, "trgSync", ObdCode::CUSTOM_TRIGGER_SYNC_ANGLE_RANGE);
if (shape->useOnlyRisingEdges) {
criticalAssertVoid(triggerDefinitionIndex < triggerShapeLength, "trigger shape fail");
assertIsInBounds(triggerDefinitionIndex, shape->isRiseEvent, "isRise");
// In case this is a rising event, replace the following fall event with the rising as well
if (shape->isRiseEvent[triggerDefinitionIndex]) {
riseOnlyIndex += 2;
eventAngles[riseOnlyIndex] = angle;
eventAngles[riseOnlyIndex + 1] = angle;
}
} else {
eventAngles[eventIndex] = angle;
}
}
}
}
2022-05-10 01:41:39 -07:00
int64_t TriggerDecoderBase::getTotalEventCounter() const {
return totalEventCountBase + currentCycle.current_index;
}
int TriggerDecoderBase::getCrankSynchronizationCounter() const {
return crankSynchronizationCounter;
}
2022-11-05 21:32:38 -07:00
void PrimaryTriggerDecoder::resetState() {
TriggerDecoderBase::resetState();
resetHasFullSync();
}
2022-05-10 01:41:39 -07:00
bool TriggerDecoderBase::isValidIndex(const TriggerWaveform& triggerShape) const {
return currentCycle.current_index < triggerShape.getSize();
2015-09-24 19:02:47 -07:00
}
2022-09-11 00:46:50 -07:00
static TriggerWheel eventIndex[4] = { TriggerWheel::T_PRIMARY, TriggerWheel::T_PRIMARY, TriggerWheel::T_SECONDARY, TriggerWheel:: T_SECONDARY };
2022-09-10 23:57:35 -07:00
static TriggerValue eventType[4] = { TriggerValue::FALL, TriggerValue::RISE, TriggerValue::FALL, TriggerValue::RISE };
2015-07-10 06:01:56 -07:00
2019-04-12 19:07:03 -07:00
#if EFI_UNIT_TEST
2020-07-19 12:47:21 -07:00
#define PRINT_INC_INDEX if (printTriggerTrace) {\
2017-03-03 19:07:25 -08:00
printf("nextTriggerEvent index=%d\r\n", currentCycle.current_index); \
}
#else
#define PRINT_INC_INDEX {}
#endif /* EFI_UNIT_TEST */
2015-07-10 06:01:56 -07:00
#define nextTriggerEvent() \
{ \
if (useOnlyRisingEdgeForTrigger) {currentCycle.current_index++;} \
2015-09-08 20:01:07 -07:00
currentCycle.current_index++; \
2017-03-03 19:07:25 -08:00
PRINT_INC_INDEX; \
2015-07-10 06:01:56 -07:00
}
2022-05-10 01:41:39 -07:00
int TriggerDecoderBase::getCurrentIndex() const {
2018-02-05 14:25:01 -08:00
return currentCycle.current_index;
}
2018-02-05 14:29:16 -08:00
2022-05-10 01:41:39 -07:00
angle_t PrimaryTriggerDecoder::syncEnginePhase(int divider, int remainder, angle_t engineCycle) {
efiAssert(ObdCode::OBD_PCM_Processor_Fault, divider > 1, "syncEnginePhase divider", false);
efiAssert(ObdCode::OBD_PCM_Processor_Fault, remainder < divider, "syncEnginePhase remainder", false);
angle_t totalShift = 0;
while (getCrankSynchronizationCounter() % divider != remainder) {
/**
* we are here if we've detected the cam sensor within the wrong crank phase
* let's increase the trigger event counter, that would adjust the state of
* virtual crank-based trigger
*/
incrementShaftSynchronizationCounter();
totalShift += engineCycle / divider;
2021-07-05 15:28:26 -07:00
}
// Allow injection/ignition to happen, we've now fully sync'd the crank based on new cam information
2022-05-09 21:52:29 -07:00
m_hasSynchronizedPhase = true;
2022-05-09 12:46:36 -07:00
if (totalShift > 0) {
2022-08-09 17:32:39 -07:00
camResyncCounter++;
2022-05-09 12:46:36 -07:00
}
return totalShift;
2021-07-05 15:28:26 -07:00
}
void TriggerDecoderBase::incrementShaftSynchronizationCounter() {
crankSynchronizationCounter++;
2018-02-05 14:29:16 -08:00
}
void PrimaryTriggerDecoder::onTriggerError() {
// On trigger error, we've lost full sync
resetHasFullSync();
}
void PrimaryTriggerDecoder::onNotEnoughTeeth(int /*actual*/, int /*expected*/) {
warning(ObdCode::CUSTOM_PRIMARY_NOT_ENOUGH_TEETH, "primary trigger error: not enough teeth between sync points: expected %d/%d got %d/%d",
2022-09-15 18:45:48 -07:00
getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_PRIMARY),
getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_SECONDARY),
currentCycle.eventCount[0],
currentCycle.eventCount[1]);
}
void PrimaryTriggerDecoder::onTooManyTeeth(int /*actual*/, int /*expected*/) {
warning(ObdCode::CUSTOM_PRIMARY_TOO_MANY_TEETH, "primary trigger error: too many teeth between sync points: expected %d/%d got %d/%d",
2022-09-15 18:45:48 -07:00
getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_PRIMARY),
getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_SECONDARY),
currentCycle.eventCount[0],
currentCycle.eventCount[1]);
}
const char *getTrigger_event_e(trigger_event_e value){
switch(value) {
case SHAFT_PRIMARY_FALLING:
return "SHAFT_PRIMARY_FALLING";
case SHAFT_PRIMARY_RISING:
return "SHAFT_PRIMARY_RISING";
case SHAFT_SECONDARY_FALLING:
return "SHAFT_SECONDARY_FALLING";
case SHAFT_SECONDARY_RISING:
return "SHAFT_SECONDARY_RISING";
}
return NULL;
}
2022-09-10 23:57:35 -07:00
const char *getTrigger_value_e(TriggerValue value){
switch(value) {
2022-09-10 23:57:35 -07:00
case TriggerValue::FALL:
return "TriggerValue::FALL";
case TriggerValue::RISE:
return "TriggerValue::RISE";
}
return NULL;
}
void VvtTriggerDecoder::onNotEnoughTeeth(int actual, int expected) {
warning(ObdCode::CUSTOM_CAM_NOT_ENOUGH_TEETH, "cam %s trigger error: not enough teeth between sync points: actual %d expected %d", name, actual, expected);
}
void VvtTriggerDecoder::onTooManyTeeth(int actual, int expected) {
warning(ObdCode::CUSTOM_CAM_TOO_MANY_TEETH, "cam %s trigger error: too many teeth between sync points: %d > %d", name, actual, expected);
}
2022-05-10 01:41:39 -07:00
bool TriggerDecoderBase::validateEventCounters(const TriggerWaveform& triggerShape) const {
// We can check if things are fine by comparing the number of events in a cycle with the expected number of event.
2020-01-26 03:35:51 -08:00
bool isDecodingError = false;
2020-01-26 06:00:46 -08:00
for (int i = 0;i < PWM_PHASE_MAX_WAVE_PER_PWM;i++) {
2022-09-11 00:46:50 -07:00
isDecodingError |= (currentCycle.eventCount[i] != triggerShape.getExpectedEventCount((TriggerWheel)i));
2020-01-26 03:35:51 -08:00
}
2024-02-21 14:31:02 -08:00
#if EFI_DEFAILED_LOGGING
printf("validateEventCounters: isDecodingError=%d\n", isDecodingError);
if (isDecodingError) {
for (int i = 0;i < PWM_PHASE_MAX_WAVE_PER_PWM;i++) {
2024-02-21 14:31:02 -08:00
printf(" count: cur=%d exp=%d\n", currentCycle.eventCount[i], triggerShape.getExpectedEventCount((TriggerWheel)i));
}
}
#endif /* EFI_UNIT_TEST */
return isDecodingError;
}
2022-05-10 01:41:39 -07:00
void TriggerDecoderBase::onShaftSynchronization(
bool wasSynchronized,
2020-08-26 14:30:13 -07:00
const efitick_t nowNt,
const TriggerWaveform& triggerShape) {
startOfCycleNt = nowNt;
resetCurrentCycleState();
if (wasSynchronized) {
incrementShaftSynchronizationCounter();
} else {
// We have just synchronized, this is the zeroth revolution
crankSynchronizationCounter = 0;
}
totalEventCountBase += triggerShape.getSize();
2019-04-12 19:07:03 -07:00
#if EFI_UNIT_TEST
if (printTriggerDebug) {
printf("onShaftSynchronization index=%d %d\r\n",
currentCycle.current_index,
crankSynchronizationCounter);
}
#endif /* EFI_UNIT_TEST */
}
static bool shouldConsiderEdge(const TriggerWaveform& triggerShape, TriggerWheel triggerWheel, TriggerValue edge) {
if (triggerWheel != TriggerWheel::T_PRIMARY && triggerShape.useOnlyPrimaryForSync) {
2024-02-21 14:31:02 -08:00
// Non-primary events ignored
return false;
}
switch (triggerShape.syncEdge) {
case SyncEdge::Both: return true;
case SyncEdge::RiseOnly:
case SyncEdge::Rise: return edge == TriggerValue::RISE;
case SyncEdge::Fall: return edge == TriggerValue::FALL;
}
// how did we get here?
// assert(false)?
return false;
}
2015-07-10 06:01:56 -07:00
/**
* @brief Trigger decoding happens here
* VR falls are filtered out and some VR noise detection happens prior to invoking this method, for
* Hall this method is invoked every time we have a fall or rise on one of the trigger sensors.
2015-07-10 06:01:56 -07:00
* This method changes the state of trigger_state_s data structure according to the trigger event
2015-08-22 08:02:10 -07:00
* @param signal type of event which just happened
* @param nowNt current time
2015-07-10 06:01:56 -07:00
*/
expected<TriggerDecodeResult> TriggerDecoderBase::decodeTriggerEvent(
2022-03-21 17:39:47 -07:00
const char *msg,
const TriggerWaveform& triggerShape,
TriggerStateListener* triggerStateListener,
const TriggerConfiguration& triggerConfiguration,
2020-08-23 22:21:42 -07:00
const trigger_event_e signal,
2020-08-23 23:01:50 -07:00
const efitick_t nowNt) {
ScopePerf perf(PE::DecodeTriggerEvent);
2024-02-21 14:31:02 -08:00
if (previousEventTimer.getElapsedSecondsAndReset(nowNt) > 1) {
2020-01-26 09:02:54 -08:00
/**
* We are here if there is a time gap between now and previous shaft event - that means the engine is not running.
* That means we have lost synchronization since the engine is not running :)
*/
setShaftSynchronized(false);
if (triggerStateListener) {
triggerStateListener->OnTriggerSynchronizationLost();
}
}
bool useOnlyRisingEdgeForTrigger = triggerShape.useOnlyRisingEdges;
2018-12-25 09:27:34 -08:00
efiAssert(ObdCode::CUSTOM_TRIGGER_UNEXPECTED, signal <= SHAFT_SECONDARY_RISING, "unexpected signal", unexpected);
2015-07-10 06:01:56 -07:00
2022-09-11 00:46:50 -07:00
TriggerWheel triggerWheel = eventIndex[signal];
2022-09-10 23:57:35 -07:00
TriggerValue type = eventType[signal];
2015-07-10 06:01:56 -07:00
// Check that we didn't get the same edge twice in a row - that should be impossible
if (!useOnlyRisingEdgeForTrigger && prevSignal == signal) {
2015-07-10 06:01:56 -07:00
orderingErrorCounter++;
}
prevSignal = signal;
2015-07-10 06:01:56 -07:00
2022-09-11 00:46:50 -07:00
currentCycle.eventCount[(int)triggerWheel]++;
2015-07-10 06:01:56 -07:00
2021-01-01 11:07:52 -08:00
if (toothed_previous_time > nowNt) {
firmwareError(ObdCode::CUSTOM_OBD_93, "[%s] toothed_previous_time after nowNt prev=%d now=%d", msg, toothed_previous_time, nowNt);
2021-01-01 11:07:52 -08:00
}
2019-01-22 20:15:33 -08:00
efitick_t currentDurationLong = isFirstEvent ? 0 : nowNt - toothed_previous_time;
2015-07-10 06:01:56 -07:00
/**
* For performance reasons, we want to work with 32 bit values. If there has been more then
* 10 seconds since previous trigger event we do not really care.
*/
2018-10-21 11:03:51 -07:00
toothDurations[0] =
currentDurationLong > 10 * NT_PER_SECOND ? 10 * NT_PER_SECOND : currentDurationLong;
2015-07-10 06:01:56 -07:00
if (!shouldConsiderEdge(triggerShape, triggerWheel, type)) {
2019-04-12 19:07:03 -07:00
#if EFI_UNIT_TEST
2020-07-19 12:47:21 -07:00
if (printTriggerTrace) {
2019-12-02 07:03:50 -08:00
printf("%s isLessImportant %s now=%d index=%d\r\n",
getTrigger_type_e(triggerConfiguration.TriggerType.type),
2015-09-22 20:01:31 -07:00
getTrigger_event_e(signal),
2019-12-02 07:03:50 -08:00
(int)nowNt,
2017-03-03 19:07:25 -08:00
currentCycle.current_index);
2015-07-10 06:01:56 -07:00
}
2017-05-25 20:23:51 -07:00
#endif /* EFI_UNIT_TEST */
2015-07-10 06:01:56 -07:00
// For less important events we simply increment the index.
nextTriggerEvent();
2015-09-13 13:01:38 -07:00
} else {
#if !EFI_PROD_CODE
2020-07-19 12:47:21 -07:00
if (printTriggerTrace) {
printf("%s event %s %lld\r\n",
getTrigger_type_e(triggerConfiguration.TriggerType.type),
2015-09-22 20:01:31 -07:00
getTrigger_event_e(signal),
nowNt);
2020-07-19 11:17:15 -07:00
printf("decodeTriggerEvent ratio %.2f: current=%d previous=%d\r\n", 1.0 * toothDurations[0] / toothDurations[1],
2018-10-21 09:29:41 -07:00
toothDurations[0], toothDurations[1]);
2015-09-23 20:01:40 -07:00
}
2015-07-10 06:01:56 -07:00
#endif
isFirstEvent = false;
2016-01-11 14:01:33 -08:00
bool isSynchronizationPoint;
2021-07-03 07:37:03 -07:00
bool wasSynchronized = getShaftSynchronized();
2015-07-10 06:01:56 -07:00
if (triggerShape.isSynchronizationNeeded) {
triggerSyncGapRatio = (float)toothDurations[0] / toothDurations[1];
2019-09-03 16:30:51 -07:00
if (wasSynchronized && triggerSyncGapRatio > NOISE_RATIO_THRESHOLD) {
setTriggerErrorState(100);
}
isSynchronizationPoint = isSyncPoint(triggerShape, triggerConfiguration.TriggerType.type);
if (isSynchronizationPoint) {
enginePins.debugTriggerSync.toggle();
}
/**
* todo: technically we can afford detailed logging even with 60/2 as long as low RPM
* todo: figure out exact threshold as a function of RPM and tooth count?
* Open question what is 'triggerShape.getSize()' for 60/2 is it 58 or 58*2 or 58*4?
*/
bool silentTriggerError = triggerShape.getSize() > 40 && engineConfiguration->silentTriggerError;
#if EFI_PROD_CODE || EFI_SIMULATOR
bool verbose = getTriggerCentral()->isEngineSnifferEnabled && triggerConfiguration.VerboseTriggerSynchDetails;
if (verbose || (someSortOfTriggerError() && !silentTriggerError)) {
2022-08-22 19:52:48 -07:00
const char * prefix = verbose ? "[vrb]" : "[err]";
for (int i = 0;i<triggerShape.gapTrackingLength;i++) {
2023-09-09 16:53:18 -07:00
float ratioFrom = triggerShape.synchronizationRatioFrom[i];
if (cisnan(ratioFrom)) {
// we do not track gap at this depth
continue;
}
2018-10-28 12:42:15 -07:00
float gap = 1.0 * toothDurations[i] / toothDurations[i + 1];
if (cisnan(gap)) {
2022-08-22 19:52:48 -07:00
efiPrintf("%s index=%d NaN gap, you have noise issues?",
i,
prefix
);
} else {
2023-09-09 16:53:18 -07:00
float ratioTo = triggerShape.synchronizationRatioTo[i];
2022-06-17 19:11:09 -07:00
bool gapOk = isInRange(ratioFrom, gap, ratioTo);
2022-08-22 19:52:48 -07:00
efiPrintf("%s %srpm=%d time=%d eventIndex=%d gapIndex=%d: %s gap=%.3f expected from %.3f to %.3f error=%s",
prefix,
triggerConfiguration.PrintPrefix,
2022-01-20 20:32:59 -08:00
(int)Sensor::getOrZero(SensorType::Rpm),
/* cast is needed to make sure we do not put 64 bit value to stack*/ (int)getTimeNowS(),
2021-05-23 17:06:19 -07:00
currentCycle.current_index,
2018-10-28 12:42:15 -07:00
i,
2022-06-17 19:11:09 -07:00
gapOk ? "Y" : "n",
2018-10-28 12:42:15 -07:00
gap,
ratioFrom,
2022-06-17 19:11:09 -07:00
ratioTo,
boolToString(someSortOfTriggerError()));
}
2018-10-21 08:27:14 -07:00
}
2019-08-24 22:35:36 -07:00
}
2015-07-10 06:01:56 -07:00
#else
2020-07-19 12:47:21 -07:00
if (printTriggerTrace) {
for (int i = 0;i<triggerShape.gapTrackingLength;i++) {
2018-10-28 12:42:15 -07:00
float gap = 1.0 * toothDurations[i] / toothDurations[i + 1];
2021-07-21 20:08:56 -07:00
printf("%sindex=%d: gap=%.2f expected from %.2f to %.2f error=%s\r\n",
triggerConfiguration.PrintPrefix,
2018-10-28 12:42:15 -07:00
i,
gap,
2023-09-09 16:53:18 -07:00
triggerShape.synchronizationRatioFrom[i],
triggerShape.synchronizationRatioTo[i],
boolToString(someSortOfTriggerError()));
2018-10-28 12:42:15 -07:00
}
2019-08-24 22:35:36 -07:00
}
2015-07-10 06:01:56 -07:00
#endif /* EFI_PROD_CODE */
2015-09-23 20:01:40 -07:00
} else {
/**
2017-03-03 18:57:28 -08:00
* We are here in case of a wheel without synchronization - we just need to count events,
* synchronization point simply happens once we have the right number of events
*
* in case of noise the counter could be above the expected number of events, that's why 'more or equals' and not just 'equals'
2015-09-23 20:01:40 -07:00
*/
2017-03-03 18:49:55 -08:00
unsigned int endOfCycleIndex = triggerShape.getSize() - (useOnlyRisingEdgeForTrigger ? 2 : 1);
2017-03-03 18:49:55 -08:00
2021-07-03 07:37:03 -07:00
isSynchronizationPoint = !getShaftSynchronized() || (currentCycle.current_index >= endOfCycleIndex);
2015-07-10 06:01:56 -07:00
2019-04-12 19:07:03 -07:00
#if EFI_UNIT_TEST
2020-07-19 12:47:21 -07:00
if (printTriggerTrace) {
printf("decodeTriggerEvent sync=%d isSynchronizationPoint=%d index=%d size=%d\r\n",
2021-07-03 07:37:03 -07:00
getShaftSynchronized(),
isSynchronizationPoint,
currentCycle.current_index,
triggerShape.getSize());
2017-05-25 20:23:51 -07:00
}
#endif /* EFI_UNIT_TEST */
2015-09-23 20:01:40 -07:00
}
2019-04-12 19:07:03 -07:00
#if EFI_UNIT_TEST
2020-07-19 12:47:21 -07:00
if (printTriggerTrace) {
printf("decodeTriggerEvent %s isSynchronizationPoint=%d index=%d %s\r\n",
getTrigger_type_e(triggerConfiguration.TriggerType.type),
2015-09-08 20:01:07 -07:00
isSynchronizationPoint, currentCycle.current_index,
2015-07-10 06:01:56 -07:00
getTrigger_event_e(signal));
}
2017-05-25 20:23:51 -07:00
#endif /* EFI_UNIT_TEST */
2015-07-10 06:01:56 -07:00
2015-09-23 20:01:40 -07:00
if (isSynchronizationPoint) {
bool isDecodingError = validateEventCounters(triggerShape);
if (triggerStateListener) {
2023-09-09 16:51:09 -07:00
triggerStateListener->OnTriggerSynchronization(wasSynchronized, isDecodingError);
}
// If we got a sync point, but the wrong number of events since the last sync point
// One of two things has happened:
// - We missed a tooth, and this is the real sync point
// - Due to some mistake in timing, we found what looks like a sync point but actually isn't
// In either case, we should wait for another sync point before doing anything to try and run an engine,
// so we clear the synchronized flag.
if (wasSynchronized && isDecodingError) {
setTriggerErrorState();
onNotEnoughTeeth(currentCycle.current_index, triggerShape.getSize());
// Something wrong, no longer synchronized
setShaftSynchronized(false);
// This is a decoding error
onTriggerError();
} else {
// If this was the first sync point OR no decode error, we're synchronized!
setShaftSynchronized(true);
2020-01-22 10:25:35 -08:00
}
2020-01-24 10:42:09 -08:00
// this call would update duty cycle values
nextTriggerEvent();
2015-07-10 06:01:56 -07:00
onShaftSynchronization(wasSynchronized, nowNt, triggerShape);
} else { /* if (!isSynchronizationPoint) */
nextTriggerEvent();
2015-09-23 20:01:40 -07:00
}
2015-07-10 06:01:56 -07:00
for (int i = triggerShape.gapTrackingLength; i > 0; i--) {
2018-10-21 11:55:52 -07:00
toothDurations[i] = toothDurations[i - 1];
}
2015-09-23 20:01:40 -07:00
toothed_previous_time = nowNt;
#if EFI_UNIT_TEST
if (wasSynchronized) {
int uiGapIndex = (currentCycle.current_index) % triggerShape.getLength();
gapRatio[uiGapIndex] = triggerSyncGapRatio;
}
#endif // EFI_UNIT_TEST
2015-09-13 13:01:38 -07:00
}
if (getShaftSynchronized() && !isValidIndex(triggerShape)) {
// We've had too many events since the last sync point, we should have seen a sync point by now.
// This is a trigger error.
// let's not show a warning if we are just starting to spin
if (Sensor::getOrZero(SensorType::Rpm) != 0) {
setTriggerErrorState();
onTooManyTeeth(currentCycle.current_index, triggerShape.getSize());
2015-09-25 06:06:35 -07:00
}
2015-09-24 19:02:47 -07:00
onTriggerError();
setShaftSynchronized(false);
return unexpected;
}
2015-09-13 13:01:38 -07:00
// Needed for early instant-RPM detection
2020-01-22 10:25:35 -08:00
if (triggerStateListener) {
triggerStateListener->OnTriggerStateProperState(nowNt);
}
triggerStateIndex = currentCycle.current_index;
if (getShaftSynchronized()) {
return TriggerDecodeResult{ currentCycle.current_index };
} else {
return unexpected;
}
2015-07-10 06:01:56 -07:00
}
2022-05-10 01:41:39 -07:00
bool TriggerDecoderBase::isSyncPoint(const TriggerWaveform& triggerShape, trigger_type_e triggerType) const {
// Miata NB needs a special decoder.
// The problem is that the crank wheel only has 4 teeth, also symmetrical, so the pattern
// is long-short-long-short for one crank rotation.
2024-02-21 14:31:02 -08:00
// A quick acceleration can result in two successive "short gaps", so we see
// long-short-short-short-long instead of the correct long-short-long-short-long
// This logic expands the lower bound on a "long" tooth, then compares the last
// tooth to the current one.
// Instead of detecting short/long, this logic first checks for "maybe short" and "maybe long",
// then simply tests longer vs. shorter instead of absolute value.
if (triggerType == trigger_type_e::TT_MIATA_VVT) {
auto secondGap = (float)toothDurations[1] / toothDurations[2];
2023-09-09 16:53:18 -07:00
bool currentGapOk = isInRange(triggerShape.synchronizationRatioFrom[0], (float)triggerSyncGapRatio, triggerShape.synchronizationRatioTo[0]);
bool secondGapOk = isInRange(triggerShape.synchronizationRatioFrom[1], secondGap, triggerShape.synchronizationRatioTo[1]);
// One or both teeth was impossible range, this is not the sync point
if (!currentGapOk || !secondGapOk) {
return false;
}
// If both teeth are in the range of possibility, return whether this gap is
// shorter than the last or not. If it is, this is the sync point.
return triggerSyncGapRatio < secondGap;
}
for (int i = 0; i < triggerShape.gapTrackingLength; i++) {
2023-09-09 16:53:18 -07:00
auto from = triggerShape.synchronizationRatioFrom[i];
auto to = triggerShape.synchronizationRatioTo[i];
if (cisnan(from)) {
// don't check this gap, skip it
continue;
}
// This is transformed to avoid a division and use a cheaper multiply instead
// toothDurations[i] / toothDurations[i+1] > from
// is an equivalent comparison to
// toothDurations[i] > toothDurations[i+1] * from
2024-02-21 14:31:02 -08:00
bool isGapCondition =
(toothDurations[i] > toothDurations[i + 1] * from
&& toothDurations[i] < toothDurations[i + 1] * to);
if (!isGapCondition) {
return false;
}
}
return true;
}
2015-07-10 06:01:56 -07:00
/**
* Trigger shape is defined in a way which is convenient for trigger shape definition
* On the other hand, trigger decoder indexing begins from synchronization event.
*
* This function finds the index of synchronization event within TriggerWaveform
2015-07-10 06:01:56 -07:00
*/
2022-05-10 01:41:39 -07:00
uint32_t TriggerDecoderBase::findTriggerZeroEventIndex(
TriggerWaveform& shape,
const TriggerConfiguration& triggerConfiguration) {
2019-04-12 19:07:03 -07:00
#if EFI_PROD_CODE
2023-06-24 23:08:53 -07:00
efiAssert(ObdCode::CUSTOM_ERR_ASSERT, hasLotsOfRemainingStack(), "findPos", -1);
2015-09-13 07:01:39 -07:00
#endif
2015-07-10 06:01:56 -07:00
2020-01-21 21:40:26 -08:00
2022-11-05 21:32:38 -07:00
resetState();
2015-09-13 07:01:39 -07:00
if (shape.shapeDefinitionError) {
2017-02-23 11:00:03 -08:00
return 0;
}
2022-11-05 21:17:24 -07:00
expected<uint32_t> syncIndex = TriggerStimulatorHelper::findTriggerSyncPoint(shape,
2020-08-23 22:21:42 -07:00
triggerConfiguration,
*this);
2022-11-05 21:10:50 -07:00
if (!syncIndex) {
return EFI_ERROR_CODE;
2015-07-10 06:01:56 -07:00
}
// Assert that we found the sync point on the very first revolution
efiAssert(ObdCode::CUSTOM_ERR_ASSERT, getCrankSynchronizationCounter() == 0, "findZero_revCounter", EFI_ERROR_CODE);
2015-07-10 06:01:56 -07:00
2019-04-12 19:07:03 -07:00
#if EFI_UNIT_TEST
2017-03-04 05:55:53 -08:00
if (printTriggerDebug) {
2017-03-04 06:06:06 -08:00
printf("findTriggerZeroEventIndex: syncIndex located %d!\r\n", syncIndex);
2017-03-04 05:55:53 -08:00
}
#endif /* EFI_UNIT_TEST */
2022-11-05 21:17:24 -07:00
TriggerStimulatorHelper::assertSyncPosition(triggerConfiguration,
2022-11-05 21:10:50 -07:00
syncIndex.Value, *this, shape);
2015-07-10 06:01:56 -07:00
2022-11-05 21:10:50 -07:00
return syncIndex.Value % shape.getSize();
2015-07-10 06:01:56 -07:00
}
#endif /* EFI_SHAFT_POSITION_INPUT */
2022-08-22 20:42:48 -07:00