/** * The idea of this method is to execute all heavy calculations in a lower-priority thread, * so that trigger event handler/IO scheduler tasks are faster. */ void Engine::periodicFastCallback(DECLARE_ENGINE_PARAMETER_F) { int rpm = rpmCalculator.rpmValue; if (isValidRpm(rpm)) { MAP_sensor_config_s * c = &engineConfiguration->map; angle_t start = interpolate2d(rpm, c->samplingAngleBins, c->samplingAngle, MAP_ANGLE_SIZE); angle_t offsetAngle = TRIGGER_SHAPE(eventAngles[CONFIG(mapAveragingSchedulingAtIndex)]); for (int i = 0; i < engineConfiguration->specs.cylindersCount; i++) { angle_t cylinderOffset = getEngineCycle(engineConfiguration->operationMode) * i / engineConfiguration->specs.cylindersCount; float cylinderStart = start + cylinderOffset - offsetAngle + tdcPosition(); fixAngle(cylinderStart, "cylinderStart"); engine->engineState.mapAveragingStart[i] = cylinderStart; } engine->engineState.mapAveragingDuration = interpolate2d(rpm, c->samplingWindowBins, c->samplingWindow, MAP_WINDOW_SIZE); } else { for (int i = 0; i < engineConfiguration->specs.cylindersCount; i++) { engine->engineState.mapAveragingStart[i] = NAN; } engine->engineState.mapAveragingDuration = NAN; } engineState.periodicFastCallback(PASS_ENGINE_PARAMETER_F); engine->m.beforeFuelCalc = GET_TIMESTAMP(); ENGINE(fuelMs) = getInjectionDuration(rpm PASS_ENGINE_PARAMETER) * engineConfiguration->globalFuelCorrection; engine->m.fuelCalcTime = GET_TIMESTAMP() - engine->m.beforeFuelCalc; }
static ICUDriver *turnOnTriggerInputPin(brain_pin_e hwPin) { // configure pin turnOnCapturePin(hwPin); shaft_icucfg.channel = ICU_CHANNEL_1; ICUDriver *driver = getInputCaptureDriver(hwPin); scheduleMsg(logger, "turnOnTriggerInputPin %s", hwPortname(hwPin)); // todo: reuse 'setWaveReaderMode' method here? if (driver != NULL) { bool_t needWidthCallback = !CONFIG(useOnlyFrontForTrigger) || TRIGGER_SHAPE(useRiseEdge); shaft_icucfg.width_cb = needWidthCallback ? shaft_icu_width_callback : NULL; bool_t needPeriodCallback = !CONFIG(useOnlyFrontForTrigger) || !TRIGGER_SHAPE(useRiseEdge); shaft_icucfg.period_cb = needPeriodCallback ? shaft_icu_period_callback : NULL; efiIcuStart(driver, &shaft_icucfg); icuEnable(driver); } return driver; }
void TriggerCentral::handleShaftSignal(trigger_event_e signal DECLARE_ENGINE_PARAMETER_S) { efiAssertVoid(engine!=NULL, "configuration"); nowNt = getTimeNowNt(); efiAssertVoid(engine->engineConfiguration!=NULL, "engineConfiguration"); efiAssertVoid(engine->engineConfiguration2!=NULL, "engineConfiguration2"); engine->onTriggerEvent(nowNt); #if EFI_HISTOGRAMS && EFI_PROD_CODE int beforeCallback = hal_lld_get_counter_value(); #endif int eventIndex = (int) signal; efiAssertVoid(eventIndex >= 0 && eventIndex < HW_EVENT_TYPES, "signal type"); hwEventCounters[eventIndex]++; if (nowNt - previousShaftEventTimeNt > US2NT(US_PER_SECOND_LL)) { /** * We are here if there is a time gap between now and previous shaft event - that means the engine is not runnig. * That means we have lost synchronization since the engine is not running :) */ triggerState.shaft_is_synchronized = false; } previousShaftEventTimeNt = nowNt; /** * This invocation changes the state of triggerState */ triggerState.decodeTriggerEvent(signal, nowNt PASS_ENGINE_PARAMETER); if (!triggerState.shaft_is_synchronized) { // we should not propagate event if we do not know where we are return; } /** * If we only have a crank position sensor, here we are extending crank revolutions with a 360 degree * cycle into a four stroke, 720 degrees cycle. TODO */ int triggerIndexForListeners; if (getOperationMode(engine->engineConfiguration) == FOUR_STROKE_CAM_SENSOR) { // That's easy - trigger cycle matches engine cycle triggerIndexForListeners = triggerState.getCurrentIndex(); } else { bool isEven = triggerState.getTotalRevolutionCounter() & 1; triggerIndexForListeners = triggerState.getCurrentIndex() + (isEven ? 0 : TRIGGER_SHAPE(size)); } reportEventToWaveChart(signal, triggerIndexForListeners); if (triggerState.current_index >= TRIGGER_SHAPE(size)) { warning(OBD_PCM_Processor_Fault, "unexpected eventIndex=%d", triggerState.current_index); } else { /** * Here we invoke all the listeners - the main engine control logic is inside these listeners */ for (int i = 0; i < triggerListeneres.currentListenersCount; i++) { ShaftPositionListener listener = (ShaftPositionListener) triggerListeneres.callbacks[i]; (listener)(signal, triggerIndexForListeners PASS_ENGINE_PARAMETER); } } #if EFI_HISTOGRAMS && EFI_PROD_CODE int afterCallback = hal_lld_get_counter_value(); int diff = afterCallback - beforeCallback; // this counter is only 32 bits so it overflows every minute, let's ignore the value in case of the overflow for simplicity if (diff > 0) { hsAdd(&triggerCallback, diff); } #endif /* EFI_HISTOGRAMS */ }
/** * @brief Trigger decoding happens here * This method changes the state of trigger_state_s data structure according to the trigger event */ void TriggerState::decodeTriggerEvent(trigger_event_e const signal, efitime_t nowNt DECLARE_ENGINE_PARAMETER_S) { efiAssertVoid(signal <= SHAFT_3RD_UP, "unexpected signal"); trigger_wheel_e triggerWheel = eventIndex[signal]; if (!engineConfiguration->useOnlyFrontForTrigger && curSignal == prevSignal) { orderingErrorCounter++; } prevSignal = curSignal; curSignal = signal; eventCount[triggerWheel]++; eventCountExt[signal]++; efitime_t currentDurationLong = getCurrentGapDuration(nowNt); /** * 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. */ currentDuration = currentDurationLong > 10 * US2NT(US_PER_SECOND_LL) ? 10 * US2NT(US_PER_SECOND_LL) : currentDurationLong; if (isLessImportant(signal)) { #if EFI_UNIT_TEST if (printTriggerDebug) { printf("%s isLessImportant %s\r\n", getTrigger_type_e(engineConfiguration->trigger.type), getTrigger_event_e(signal)); } #endif /** * For less important events we simply increment the index. */ nextTriggerEvent() ; if (TRIGGER_SHAPE(gapBothDirections)) { toothed_previous_duration = currentDuration; isFirstEvent = false; toothed_previous_time = nowNt; } return; } isFirstEvent = false; // todo: skip a number of signal from the beginning #if EFI_PROD_CODE // scheduleMsg(&logger, "from %f to %f %d %d", triggerConfig->syncRatioFrom, triggerConfig->syncRatioTo, currentDuration, shaftPositionState->toothed_previous_duration); // scheduleMsg(&logger, "ratio %f", 1.0 * currentDuration/ shaftPositionState->toothed_previous_duration); #else if (toothed_previous_duration != 0) { // printf("ratio %f: cur=%d pref=%d\r\n", 1.0 * currentDuration / shaftPositionState->toothed_previous_duration, // currentDuration, shaftPositionState->toothed_previous_duration); } #endif bool_t isSynchronizationPoint; if (TRIGGER_SHAPE(isSynchronizationNeeded)) { isSynchronizationPoint = currentDuration > toothed_previous_duration * TRIGGER_SHAPE(syncRatioFrom) && currentDuration < toothed_previous_duration * TRIGGER_SHAPE(syncRatioTo); #if EFI_PROD_CODE if (engineConfiguration->isPrintTriggerSynchDetails) { #else if (printTriggerDebug) { #endif /* EFI_PROD_CODE */ float gap = 1.0 * currentDuration / toothed_previous_duration; #if EFI_PROD_CODE scheduleMsg(logger, "gap=%f @ %d", gap, current_index); #else actualSynchGap = gap; print("current gap %f\r\n", gap); #endif /* EFI_PROD_CODE */ } } else { /** * in case of noise the counter could be above the expected number of events */ int d = engineConfiguration->useOnlyFrontForTrigger ? 2 : 1; isSynchronizationPoint = !shaft_is_synchronized || (current_index >= TRIGGER_SHAPE(size) - d); } #if EFI_UNIT_TEST if (printTriggerDebug) { printf("%s isSynchronizationPoint=%d index=%d %s\r\n", getTrigger_type_e(engineConfiguration->trigger.type), isSynchronizationPoint, current_index, getTrigger_event_e(signal)); } #endif if (isSynchronizationPoint) { /** * We can check if things are fine by comparing the number of events in a cycle with the expected number of event. */ bool isDecodingError = eventCount[0] != TRIGGER_SHAPE(expectedEventCount[0]) || eventCount[1] != TRIGGER_SHAPE(expectedEventCount[1]) || eventCount[2] != TRIGGER_SHAPE(expectedEventCount[2]); triggerDecoderErrorPin.setValue(isDecodingError); if (isDecodingError) { lastDecodingErrorTime = getTimeNowNt(); totalTriggerErrorCounter++; if (engineConfiguration->isPrintTriggerSynchDetails) { #if EFI_PROD_CODE scheduleMsg(logger, "error: synchronizationPoint @ index %d expected %d/%d/%d got %d/%d/%d", current_index, TRIGGER_SHAPE(expectedEventCount[0]), TRIGGER_SHAPE(expectedEventCount[1]), TRIGGER_SHAPE(expectedEventCount[2]), eventCount[0], eventCount[1], eventCount[2]); #endif /* EFI_PROD_CODE */ } } errorDetection.add(isDecodingError); if (isTriggerDecoderError()) { warning(OBD_PCM_Processor_Fault, "trigger decoding issue. expected %d/%d/%d got %d/%d/%d", TRIGGER_SHAPE(expectedEventCount[0]), TRIGGER_SHAPE(expectedEventCount[1]), TRIGGER_SHAPE(expectedEventCount[2]), eventCount[0], eventCount[1], eventCount[2]); } shaft_is_synchronized = true; // this call would update duty cycle values nextTriggerEvent() ; nextRevolution(); } else { nextTriggerEvent() ; } toothed_previous_duration = currentDuration; toothed_previous_time = nowNt; } float getEngineCycle(operation_mode_e operationMode) { return operationMode == TWO_STROKE ? 360 : 720; } void addSkippedToothTriggerEvents(trigger_wheel_e wheel, TriggerShape *s, int totalTeethCount, int skippedCount, float toothWidth, float offset, float engineCycle, float filterLeft, float filterRight) { efiAssertVoid(totalTeethCount > 0, "total count"); efiAssertVoid(skippedCount >= 0, "skipped count"); for (int i = 0; i < totalTeethCount - skippedCount - 1; i++) { float angleDown = engineCycle / totalTeethCount * (i + (1 - toothWidth)); float angleUp = engineCycle / totalTeethCount * (i + 1); s->addEvent(offset + angleDown, wheel, TV_HIGH, filterLeft, filterRight); s->addEvent(offset + angleUp, wheel, TV_LOW, filterLeft, filterRight); } float angleDown = engineCycle / totalTeethCount * (totalTeethCount - skippedCount - 1 + (1 - toothWidth) ); s->addEvent(offset + angleDown, wheel, TV_HIGH, filterLeft, filterRight); s->addEvent(offset + engineCycle, wheel, TV_LOW, filterLeft, filterRight); }
void triggerInfo(Engine *engine) { #if (EFI_PROD_CODE || EFI_SIMULATOR) || defined(__DOXYGEN__) TriggerShape *ts = &engine->triggerShape; scheduleMsg(logger, "Template %s (%d) trigger %s (%d) useRiseEdge=%s onlyFront=%s", getConfigurationName(engineConfiguration->engineType), engineConfiguration->engineType, getTrigger_type_e(engineConfiguration->trigger.type), engineConfiguration->trigger.type, boolToString(TRIGGER_SHAPE(useRiseEdge)), boolToString(engineConfiguration->useOnlyFrontForTrigger)); scheduleMsg(logger, "trigger#1 event counters up=%d/down=%d", triggerCentral.getHwEventCounter(0), triggerCentral.getHwEventCounter(1)); if (engine->triggerShape.needSecondTriggerInput) { scheduleMsg(logger, "trigger#2 event counters up=%d/down=%d", triggerCentral.getHwEventCounter(2), triggerCentral.getHwEventCounter(3)); } scheduleMsg(logger, "expected cycle events %d/%d/%d", ts->expectedEventCount[0], engine->triggerShape.expectedEventCount[1], ts->expectedEventCount[2]); scheduleMsg(logger, "trigger type=%d/need2ndChannel=%s", engineConfiguration->trigger.type, boolToString(engine->triggerShape.needSecondTriggerInput)); scheduleMsg(logger, "expected duty #0=%f/#1=%f", ts->dutyCycle[0], ts->dutyCycle[1]); scheduleMsg(logger, "isError %s/total errors=%d ord_err=%d/total revolutions=%d/self=%s", boolToString(isTriggerDecoderError()), triggerCentral.triggerState.totalTriggerErrorCounter, triggerCentral.triggerState.orderingErrorCounter, triggerCentral.triggerState.getTotalRevolutionCounter(), boolToString(engineConfiguration->directSelfStimulation)); if (ts->isSynchronizationNeeded) { scheduleMsg(logger, "gap from %f to %f", ts->syncRatioFrom, ts->syncRatioTo); } #endif #if EFI_PROD_CODE scheduleMsg(logger, "sn=%s ignitionMathTime=%d schTime=%d injectonSchTime=%d zeroTestTime=%d advanceTime=%d triggerMaxDuration=%d", boolToString(ts->isSynchronizationNeeded), engine->m.ignitionMathTime, engine->m.ignitionSchTime, engine->m.injectonSchTime, engine->m.zeroTestTime, engine->m.advanceTime, triggerMaxDuration); triggerMaxDuration = 0; scheduleMsg(logger, "maxLockTime=%d / maxTriggerReentraint=%d", maxLockTime, maxTriggerReentraint); scheduleMsg(logger, "maxEventQueueTime=%d", maxEventQueueTime); scheduleMsg(logger, "primary trigger input: %s", hwPortname(boardConfiguration->triggerInputPins[0])); scheduleMsg(logger, "primary trigger simulator: %s %s freq=%d", hwPortname(boardConfiguration->triggerSimulatorPins[0]), getPin_output_mode_e(boardConfiguration->triggerSimulatorPinModes[0]), boardConfiguration->triggerSimulatorFrequency); if (engine->triggerShape.needSecondTriggerInput) { scheduleMsg(logger, "secondary trigger input: %s", hwPortname(boardConfiguration->triggerInputPins[1])); #if EFI_EMULATE_POSITION_SENSORS || defined(__DOXYGEN__) scheduleMsg(logger, "secondary trigger simulator: %s %s phase=%d", hwPortname(boardConfiguration->triggerSimulatorPins[1]), getPin_output_mode_e(boardConfiguration->triggerSimulatorPinModes[1]), triggerSignal.safe.phaseIndex); #endif /* EFI_EMULATE_POSITION_SENSORS */ } // scheduleMsg(logger, "3rd trigger simulator: %s %s", hwPortname(boardConfiguration->triggerSimulatorPins[2]), // getPin_output_mode_e(boardConfiguration->triggerSimulatorPinModes[2])); scheduleMsg(logger, "trigger error extra LED: %s %s", hwPortname(boardConfiguration->triggerErrorPin), getPin_output_mode_e(boardConfiguration->triggerErrorPinMode)); scheduleMsg(logger, "primary logic input: %s", hwPortname(boardConfiguration->logicAnalyzerPins[0])); scheduleMsg(logger, "secondary logic input: %s", hwPortname(boardConfiguration->logicAnalyzerPins[1])); #endif /* EFI_PROD_CODE */ }
bool TriggerState::isValidIndex(DECLARE_ENGINE_PARAMETER_F) { return currentCycle.current_index < TRIGGER_SHAPE(size); }
/** * @brief Trigger decoding happens here * This method is invoked every time we have a fall or rise on one of the trigger sensors. * This method changes the state of trigger_state_s data structure according to the trigger event * @param signal type of event which just happened * @param nowNt current time */ void TriggerState::decodeTriggerEvent(trigger_event_e const signal, efitime_t nowNt DECLARE_ENGINE_PARAMETER_S) { efiAssertVoid(signal <= SHAFT_3RD_UP, "unexpected signal"); trigger_wheel_e triggerWheel = eventIndex[signal]; if (!engineConfiguration->useOnlyFrontForTrigger && curSignal == prevSignal) { orderingErrorCounter++; } prevSignal = curSignal; curSignal = signal; currentCycle.eventCount[triggerWheel]++; efitime_t currentDurationLong = getCurrentGapDuration(nowNt); /** * 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. */ currentDuration = currentDurationLong > 10 * US2NT(US_PER_SECOND_LL) ? 10 * US2NT(US_PER_SECOND_LL) : currentDurationLong; bool isPrimary = triggerWheel == T_PRIMARY; if (isLessImportant(signal)) { #if EFI_UNIT_TEST || defined(__DOXYGEN__) if (printTriggerDebug) { printf("%s isLessImportant %s %d\r\n", getTrigger_type_e(engineConfiguration->trigger.type), getTrigger_event_e(signal), nowNt); } #endif /** * For less important events we simply increment the index. */ nextTriggerEvent() ; if (TRIGGER_SHAPE(gapBothDirections) && considerEventForGap()) { isFirstEvent = false; thirdPreviousDuration = durationBeforePrevious; durationBeforePrevious = toothed_previous_duration; toothed_previous_duration = currentDuration; toothed_previous_time = nowNt; } } else { #if EFI_UNIT_TEST || defined(__DOXYGEN__) if (printTriggerDebug) { printf("%s event %s %d\r\n", getTrigger_type_e(engineConfiguration->trigger.type), getTrigger_event_e(signal), nowNt); } #endif isFirstEvent = false; // todo: skip a number of signal from the beginning #if EFI_PROD_CODE || defined(__DOXYGEN__) // scheduleMsg(&logger, "from %f to %f %d %d", triggerConfig->syncRatioFrom, triggerConfig->syncRatioTo, currentDuration, shaftPositionState->toothed_previous_duration); // scheduleMsg(&logger, "ratio %f", 1.0 * currentDuration/ shaftPositionState->toothed_previous_duration); #else if (toothed_previous_duration != 0) { // printf("ratio %f: cur=%d pref=%d\r\n", 1.0 * currentDuration / shaftPositionState->toothed_previous_duration, // currentDuration, shaftPositionState->toothed_previous_duration); } #endif bool isSynchronizationPoint; if (TRIGGER_SHAPE(isSynchronizationNeeded)) { /** * Here I prefer to have two multiplications instead of one division, that's a micro-optimization */ isSynchronizationPoint = currentDuration > toothed_previous_duration * TRIGGER_SHAPE(syncRatioFrom) && currentDuration < toothed_previous_duration * TRIGGER_SHAPE(syncRatioTo) && toothed_previous_duration > durationBeforePrevious * TRIGGER_SHAPE(secondSyncRatioFrom) && toothed_previous_duration < durationBeforePrevious * TRIGGER_SHAPE(secondSyncRatioTo) // this is getting a little out of hand, any ideas? && durationBeforePrevious > thirdPreviousDuration * TRIGGER_SHAPE(thirdSyncRatioFrom) && durationBeforePrevious < thirdPreviousDuration * TRIGGER_SHAPE(thirdSyncRatioTo) ; #if EFI_PROD_CODE || defined(__DOXYGEN__) if (engineConfiguration->isPrintTriggerSynchDetails || someSortOfTriggerError) { #else if (printTriggerDebug) { #endif /* EFI_PROD_CODE */ float gap = 1.0 * currentDuration / toothed_previous_duration; float prevGap = 1.0 * toothed_previous_duration / durationBeforePrevious; float gap3 = 1.0 * durationBeforePrevious / thirdPreviousDuration; #if EFI_PROD_CODE || defined(__DOXYGEN__) scheduleMsg(logger, "gap=%f/%f/%f @ %d while expected %f/%f and %f/%f error=%d", gap, prevGap, gap3, currentCycle.current_index, TRIGGER_SHAPE(syncRatioFrom), TRIGGER_SHAPE(syncRatioTo), TRIGGER_SHAPE(secondSyncRatioFrom), TRIGGER_SHAPE(secondSyncRatioTo), someSortOfTriggerError); #else actualSynchGap = gap; print("current gap %f/%f/%f c=%d prev=%d\r\n", gap, prevGap, gap3, currentDuration, toothed_previous_duration); #endif /* EFI_PROD_CODE */ } } else { /** * in case of noise the counter could be above the expected number of events */ int d = engineConfiguration->useOnlyFrontForTrigger ? 2 : 1; isSynchronizationPoint = !shaft_is_synchronized || (currentCycle.current_index >= TRIGGER_SHAPE(size) - d); } #if EFI_UNIT_TEST || defined(__DOXYGEN__) if (printTriggerDebug) { printf("%s isSynchronizationPoint=%d index=%d %s\r\n", getTrigger_type_e(engineConfiguration->trigger.type), isSynchronizationPoint, currentCycle.current_index, getTrigger_event_e(signal)); } #endif if (isSynchronizationPoint) { /** * We can check if things are fine by comparing the number of events in a cycle with the expected number of event. */ bool isDecodingError = currentCycle.eventCount[0] != TRIGGER_SHAPE(expectedEventCount[0]) || currentCycle.eventCount[1] != TRIGGER_SHAPE(expectedEventCount[1]) || currentCycle.eventCount[2] != TRIGGER_SHAPE(expectedEventCount[2]); triggerDecoderErrorPin.setValue(isDecodingError); if (isDecodingError) { lastDecodingErrorTime = getTimeNowNt(); someSortOfTriggerError = true; totalTriggerErrorCounter++; if (engineConfiguration->isPrintTriggerSynchDetails || someSortOfTriggerError) { #if EFI_PROD_CODE || defined(__DOXYGEN__) scheduleMsg(logger, "error: synchronizationPoint @ index %d expected %d/%d/%d got %d/%d/%d", currentCycle.current_index, TRIGGER_SHAPE(expectedEventCount[0]), TRIGGER_SHAPE(expectedEventCount[1]), TRIGGER_SHAPE(expectedEventCount[2]), currentCycle.eventCount[0], currentCycle.eventCount[1], currentCycle.eventCount[2]); #endif /* EFI_PROD_CODE */ } } errorDetection.add(isDecodingError); if (isTriggerDecoderError()) { warning(OBD_PCM_Processor_Fault, "trigger decoding issue. expected %d/%d/%d got %d/%d/%d", TRIGGER_SHAPE(expectedEventCount[0]), TRIGGER_SHAPE(expectedEventCount[1]), TRIGGER_SHAPE(expectedEventCount[2]), currentCycle.eventCount[0], currentCycle.eventCount[1], currentCycle.eventCount[2]); } shaft_is_synchronized = true; // this call would update duty cycle values nextTriggerEvent() ; nextRevolution(); } else { nextTriggerEvent() ; } thirdPreviousDuration = durationBeforePrevious; durationBeforePrevious = toothed_previous_duration; toothed_previous_duration = currentDuration; toothed_previous_time = nowNt; } if (!isValidIndex(PASS_ENGINE_PARAMETER_F)) { warning(OBD_PCM_Processor_Fault, "unexpected eventIndex=%d while size %d", currentCycle.current_index, TRIGGER_SHAPE(size)); lastDecodingErrorTime = getTimeNowNt(); someSortOfTriggerError = true; } if (someSortOfTriggerError) { if (getTimeNowNt() - lastDecodingErrorTime > US2NT(US_PER_SECOND_LL)) { someSortOfTriggerError = false; } } if (ENGINE(sensorChartMode) == SC_RPM_ACCEL || ENGINE(sensorChartMode) == SC_DETAILED_RPM) { angle_t currentAngle = TRIGGER_SHAPE(eventAngles[currentCycle.current_index]); // todo: make this '90' depend on cylinder count? angle_t prevAngle = currentAngle - 90; fixAngle(prevAngle); // todo: prevIndex should be pre-calculated int prevIndex = TRIGGER_SHAPE(triggerIndexByAngle[(int)prevAngle]); // now let's get precise angle for that event prevAngle = TRIGGER_SHAPE(eventAngles[prevIndex]); // todo: re-implement this as a subclass. we need two instances of // uint32_t time = nowNt - timeOfLastEvent[prevIndex]; angle_t angleDiff = currentAngle - prevAngle; // todo: angle diff should be pre-calculated fixAngle(angleDiff); // float r = (60000000.0 / 360 * US_TO_NT_MULTIPLIER) * angleDiff / time; #if EFI_SENSOR_CHART || defined(__DOXYGEN__) if (boardConfiguration->sensorChartMode == SC_DETAILED_RPM) { // scAddData(currentAngle, r); } else { // scAddData(currentAngle, r / instantRpmValue[prevIndex]); } #endif // instantRpmValue[currentCycle.current_index] = r; // timeOfLastEvent[currentCycle.current_index] = nowNt; } } angle_t getEngineCycle(operation_mode_e operationMode) { return operationMode == TWO_STROKE ? 360 : 720; } void addSkippedToothTriggerEvents(trigger_wheel_e wheel, TriggerShape *s, int totalTeethCount, int skippedCount, float toothWidth, float offset, float engineCycle, float filterLeft, float filterRight) { efiAssertVoid(totalTeethCount > 0, "total count"); efiAssertVoid(skippedCount >= 0, "skipped count"); for (int i = 0; i < totalTeethCount - skippedCount - 1; i++) { float angleDown = engineCycle / totalTeethCount * (i + (1 - toothWidth)); float angleUp = engineCycle / totalTeethCount * (i + 1); s->addEvent(offset + angleDown, wheel, TV_RISE, filterLeft, filterRight); s->addEvent(offset + angleUp, wheel, TV_FALL, filterLeft, filterRight); } float angleDown = engineCycle / totalTeethCount * (totalTeethCount - skippedCount - 1 + (1 - toothWidth)); s->addEvent(offset + angleDown, wheel, TV_RISE, filterLeft, filterRight); s->addEvent(offset + engineCycle, wheel, TV_FALL, filterLeft, filterRight); }