trigger refactoring: instance RPM fix #4740

This commit is contained in:
Andrey 2022-11-06 12:03:55 -05:00
parent 65f5c00616
commit 77caa77b4b
6 changed files with 202 additions and 188 deletions

View File

@ -0,0 +1,136 @@
#include "pch.h"
#include "instant_rpm_calculator.h"
#if EFI_SENSOR_CHART
#include "sensor_chart.h"
#endif
/**
* sensorChartMode
*/
#include "engine_state.h"
InstantRpmCalculator::InstantRpmCalculator() :
//https://en.cppreference.com/w/cpp/language/zero_initialization
timeOfLastEvent()
, instantRpmValue()
{
}
void InstantRpmCalculator::movePreSynchTimestamps() {
// here we take timestamps of events which happened prior to synchronization and place them
// at appropriate locations
auto triggerSize = getTriggerCentral()->triggerShape.getLength();
size_t eventsToCopy = minI(spinningEventIndex, triggerSize);
size_t firstSrc;
size_t firstDst;
if (eventsToCopy >= triggerSize) {
// Only copy one trigger length worth of events, filling the whole buffer
firstSrc = spinningEventIndex - triggerSize;
firstDst = 0;
} else {
// There is less than one full cycle, copy to the end of the buffer
firstSrc = 0;
firstDst = triggerSize - spinningEventIndex;
}
memcpy(timeOfLastEvent + firstDst, spinningEvents + firstSrc, eventsToCopy * sizeof(timeOfLastEvent[0]));
}
float InstantRpmCalculator::calculateInstantRpm(
TriggerWaveform const & triggerShape, TriggerFormDetails *triggerFormDetails,
uint32_t current_index, efitick_t nowNt) {
assertIsInBoundsWithResult(current_index, timeOfLastEvent, "calc timeOfLastEvent", 0);
// Record the time of this event so we can calculate RPM from it later
timeOfLastEvent[current_index] = nowNt;
// Determine where we currently are in the revolution
angle_t currentAngle = triggerFormDetails->eventAngles[current_index];
efiAssert(OBD_PCM_Processor_Fault, !cisnan(currentAngle), "eventAngles", 0);
// Hunt for a tooth ~90 degrees ago to compare to the current time
angle_t previousAngle = currentAngle - 90;
fixAngle(previousAngle, "prevAngle", CUSTOM_ERR_TRIGGER_ANGLE_RANGE);
int prevIndex = triggerShape.findAngleIndex(triggerFormDetails, previousAngle);
// now let's get precise angle for that event
angle_t prevIndexAngle = triggerFormDetails->eventAngles[prevIndex];
efitick_t time90ago = timeOfLastEvent[prevIndex];
// No previous timestamp, instant RPM isn't ready yet
if (time90ago == 0) {
return prevInstantRpmValue;
}
// It's OK to truncate from 64b to 32b, ARM with single precision FPU uses an expensive
// software function to convert 64b int -> float, while 32b int -> float is very cheap hardware conversion
// The difference is guaranteed to be short (it's 90 degrees of engine rotation!), so it won't overflow.
uint32_t time = nowNt - time90ago;
angle_t angleDiff = currentAngle - prevIndexAngle;
// Wrap the angle in to the correct range (ie, could be -630 when we want +90)
fixAngle(angleDiff, "angleDiff", CUSTOM_ERR_6561);
// just for safety, avoid divide-by-0
if (time == 0) {
return prevInstantRpmValue;
}
float instantRpm = (60000000.0 / 360 * US_TO_NT_MULTIPLIER) * angleDiff / time;
assertIsInBoundsWithResult(current_index, instantRpmValue, "instantRpmValue", 0);
instantRpmValue[current_index] = instantRpm;
// This fixes early RPM instability based on incomplete data
if (instantRpm < RPM_LOW_THRESHOLD) {
return prevInstantRpmValue;
}
prevInstantRpmValue = instantRpm;
m_instantRpmRatio = instantRpm / instantRpmValue[prevIndex];
return instantRpm;
}
void InstantRpmCalculator::setLastEventTimeForInstantRpm(efitick_t nowNt) {
// here we remember tooth timestamps which happen prior to synchronization
if (spinningEventIndex >= efi::size(spinningEvents)) {
// too many events while trying to find synchronization point
// todo: better implementation would be to shift here or use cyclic buffer so that we keep last
// 'PRE_SYNC_EVENTS' events
return;
}
spinningEvents[spinningEventIndex] = nowNt;
// If we are using only rising edges, we never write in to the odd-index slots that
// would be used by falling edges
spinningEventIndex += engineConfiguration->useOnlyRisingEdgeForTrigger ? 2 : 1;
}
void InstantRpmCalculator::updateInstantRpm(
uint32_t current_index,
TriggerWaveform const & triggerShape, TriggerFormDetails *triggerFormDetails,
uint32_t index, efitick_t nowNt) {
m_instantRpm = calculateInstantRpm(triggerShape, triggerFormDetails, index,
nowNt);
#if EFI_SENSOR_CHART
if (getEngineState()->sensorChartMode == SC_RPM_ACCEL || getEngineState()->sensorChartMode == SC_DETAILED_RPM) {
angle_t currentAngle = triggerFormDetails->eventAngles[current_index];
if (engineConfiguration->sensorChartMode == SC_DETAILED_RPM) {
scAddData(currentAngle, m_instantRpm);
} else {
scAddData(currentAngle, m_instantRpmRatio);
}
}
#endif /* EFI_SENSOR_CHART */
}

View File

@ -0,0 +1,64 @@
/**
* instant_rpm_calculator.h
*/
#pragma once
#include "trigger_structure.h"
class InstantRpmCalculator {
public:
InstantRpmCalculator();
float getInstantRpm() const {
return m_instantRpm;
}
#if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT
void updateInstantRpm(
uint32_t current_index,
TriggerWaveform const & triggerShape, TriggerFormDetails *triggerFormDetails,
uint32_t index, efitick_t nowNt);
#endif
/**
* Update timeOfLastEvent[] on every trigger event - even without synchronization
* Needed for early spin-up RPM detection.
*/
void setLastEventTimeForInstantRpm(efitick_t nowNt);
void movePreSynchTimestamps();
void resetInstantRpm() {
memset(timeOfLastEvent, 0, sizeof(timeOfLastEvent));
memset(spinningEvents, 0, sizeof(spinningEvents));
spinningEventIndex = 0;
prevInstantRpmValue = 0;
m_instantRpm = 0;
}
/**
* timestamp of each trigger wheel tooth
*/
uint32_t timeOfLastEvent[PWM_PHASE_MAX_COUNT];
size_t spinningEventIndex = 0;
// we might need up to one full trigger cycle of events - which on 60-2 means storage for ~120
// todo: change the implementation to reuse 'timeOfLastEvent'
uint32_t spinningEvents[120];
/**
* instant RPM calculated at this trigger wheel tooth
*/
float instantRpmValue[PWM_PHASE_MAX_COUNT];
/**
* Stores last non-zero instant RPM value to fix early instability
*/
float prevInstantRpmValue = 0;
float m_instantRpm = 0;
private:
float calculateInstantRpm(
TriggerWaveform const & triggerShape, TriggerFormDetails *triggerFormDetails,
uint32_t index, efitick_t nowNt);
float m_instantRpmRatio = 0;
};

View File

@ -4,6 +4,7 @@ TRIGGER_DECODERS_SRC_CPP = \
$(CONTROLLERS_DIR)/trigger/decoders/trigger_mazda.cpp \
$(CONTROLLERS_DIR)/trigger/decoders/trigger_chrysler.cpp \
$(CONTROLLERS_DIR)/trigger/decoders/trigger_structure.cpp \
$(CONTROLLERS_DIR)/trigger/decoders/instant_rpm_calculator.cpp \
$(CONTROLLERS_DIR)/trigger/decoders/auto_generated_sync_edge.cpp \
$(CONTROLLERS_DIR)/trigger/trigger_decoder.cpp \
$(CONTROLLERS_DIR)/trigger/trigger_simulator.cpp \

View File

@ -10,6 +10,7 @@
#include "rusefi_enums.h"
#include "listener_array.h"
#include "trigger_decoder.h"
#include "instant_rpm_calculator.h"
#include "trigger_central_generated.h"
#include "timer.h"
#include "pin_repository.h"

View File

@ -40,9 +40,6 @@
* decoder depends on current RPM for error condition logic
*/
#include "sensor.h"
/**
* sensorChartMode
*/
#include "engine_state.h"
#include "engine_math.h"
/**
@ -50,10 +47,6 @@
*/
#include "trigger_simulator.h"
#if EFI_SENSOR_CHART
#include "sensor_chart.h"
#endif
TriggerDecoderBase::TriggerDecoderBase(const char* name)
: name(name)
{
@ -109,13 +102,6 @@ void TriggerDecoderBase::resetCurrentCycleState() {
#if EFI_SHAFT_POSITION_INPUT
InstantRpmCalculator::InstantRpmCalculator() :
//https://en.cppreference.com/w/cpp/language/zero_initialization
timeOfLastEvent()
, instantRpmValue()
{
}
PrimaryTriggerDecoder::PrimaryTriggerDecoder(const char* name)
: TriggerDecoderBase(name)
{
@ -205,122 +191,6 @@ void PrimaryTriggerDecoder::resetState() {
resetHasFullSync();
}
void InstantRpmCalculator::movePreSynchTimestamps() {
// here we take timestamps of events which happened prior to synchronization and place them
// at appropriate locations
auto triggerSize = getTriggerCentral()->triggerShape.getLength();
size_t eventsToCopy = minI(spinningEventIndex, triggerSize);
size_t firstSrc;
size_t firstDst;
if (eventsToCopy >= triggerSize) {
// Only copy one trigger length worth of events, filling the whole buffer
firstSrc = spinningEventIndex - triggerSize;
firstDst = 0;
} else {
// There is less than one full cycle, copy to the end of the buffer
firstSrc = 0;
firstDst = triggerSize - spinningEventIndex;
}
memcpy(timeOfLastEvent + firstDst, spinningEvents + firstSrc, eventsToCopy * sizeof(timeOfLastEvent[0]));
}
float InstantRpmCalculator::calculateInstantRpm(
TriggerWaveform const & triggerShape, TriggerFormDetails *triggerFormDetails,
uint32_t current_index, efitick_t nowNt) {
assertIsInBoundsWithResult(current_index, timeOfLastEvent, "calc timeOfLastEvent", 0);
// Record the time of this event so we can calculate RPM from it later
timeOfLastEvent[current_index] = nowNt;
// Determine where we currently are in the revolution
angle_t currentAngle = triggerFormDetails->eventAngles[current_index];
efiAssert(OBD_PCM_Processor_Fault, !cisnan(currentAngle), "eventAngles", 0);
// Hunt for a tooth ~90 degrees ago to compare to the current time
angle_t previousAngle = currentAngle - 90;
fixAngle(previousAngle, "prevAngle", CUSTOM_ERR_TRIGGER_ANGLE_RANGE);
int prevIndex = triggerShape.findAngleIndex(triggerFormDetails, previousAngle);
// now let's get precise angle for that event
angle_t prevIndexAngle = triggerFormDetails->eventAngles[prevIndex];
efitick_t time90ago = timeOfLastEvent[prevIndex];
// No previous timestamp, instant RPM isn't ready yet
if (time90ago == 0) {
return prevInstantRpmValue;
}
// It's OK to truncate from 64b to 32b, ARM with single precision FPU uses an expensive
// software function to convert 64b int -> float, while 32b int -> float is very cheap hardware conversion
// The difference is guaranteed to be short (it's 90 degrees of engine rotation!), so it won't overflow.
uint32_t time = nowNt - time90ago;
angle_t angleDiff = currentAngle - prevIndexAngle;
// Wrap the angle in to the correct range (ie, could be -630 when we want +90)
fixAngle(angleDiff, "angleDiff", CUSTOM_ERR_6561);
// just for safety, avoid divide-by-0
if (time == 0) {
return prevInstantRpmValue;
}
float instantRpm = (60000000.0 / 360 * US_TO_NT_MULTIPLIER) * angleDiff / time;
assertIsInBoundsWithResult(current_index, instantRpmValue, "instantRpmValue", 0);
instantRpmValue[current_index] = instantRpm;
// This fixes early RPM instability based on incomplete data
if (instantRpm < RPM_LOW_THRESHOLD) {
return prevInstantRpmValue;
}
prevInstantRpmValue = instantRpm;
m_instantRpmRatio = instantRpm / instantRpmValue[prevIndex];
return instantRpm;
}
void InstantRpmCalculator::setLastEventTimeForInstantRpm(efitick_t nowNt) {
// here we remember tooth timestamps which happen prior to synchronization
if (spinningEventIndex >= efi::size(spinningEvents)) {
// too many events while trying to find synchronization point
// todo: better implementation would be to shift here or use cyclic buffer so that we keep last
// 'PRE_SYNC_EVENTS' events
return;
}
spinningEvents[spinningEventIndex] = nowNt;
// If we are using only rising edges, we never write in to the odd-index slots that
// would be used by falling edges
spinningEventIndex += engineConfiguration->useOnlyRisingEdgeForTrigger ? 2 : 1;
}
void InstantRpmCalculator::updateInstantRpm(
uint32_t current_index,
TriggerWaveform const & triggerShape, TriggerFormDetails *triggerFormDetails,
uint32_t index, efitick_t nowNt) {
m_instantRpm = calculateInstantRpm(triggerShape, triggerFormDetails, index,
nowNt);
#if EFI_SENSOR_CHART
if (getEngineState()->sensorChartMode == SC_RPM_ACCEL || getEngineState()->sensorChartMode == SC_DETAILED_RPM) {
angle_t currentAngle = triggerFormDetails->eventAngles[current_index];
if (engineConfiguration->sensorChartMode == SC_DETAILED_RPM) {
scAddData(currentAngle, m_instantRpm);
} else {
scAddData(currentAngle, m_instantRpmRatio);
}
}
#endif /* EFI_SENSOR_CHART */
}
bool TriggerDecoderBase::isValidIndex(const TriggerWaveform& triggerShape) const {
return currentCycle.current_index < triggerShape.getSize();

View File

@ -182,64 +182,6 @@ private:
Timer m_timeSinceDecodeError;
};
class InstantRpmCalculator {
public:
InstantRpmCalculator();
float getInstantRpm() const {
return m_instantRpm;
}
#if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT
void updateInstantRpm(
uint32_t current_index,
TriggerWaveform const & triggerShape, TriggerFormDetails *triggerFormDetails,
uint32_t index, efitick_t nowNt);
#endif
/**
* Update timeOfLastEvent[] on every trigger event - even without synchronization
* Needed for early spin-up RPM detection.
*/
void setLastEventTimeForInstantRpm(efitick_t nowNt);
void movePreSynchTimestamps();
void resetInstantRpm() {
memset(timeOfLastEvent, 0, sizeof(timeOfLastEvent));
memset(spinningEvents, 0, sizeof(spinningEvents));
spinningEventIndex = 0;
prevInstantRpmValue = 0;
m_instantRpm = 0;
}
/**
* timestamp of each trigger wheel tooth
*/
uint32_t timeOfLastEvent[PWM_PHASE_MAX_COUNT];
size_t spinningEventIndex = 0;
// we might need up to one full trigger cycle of events - which on 60-2 means storage for ~120
// todo: change the implementation to reuse 'timeOfLastEvent'
uint32_t spinningEvents[120];
/**
* instant RPM calculated at this trigger wheel tooth
*/
float instantRpmValue[PWM_PHASE_MAX_COUNT];
/**
* Stores last non-zero instant RPM value to fix early instability
*/
float prevInstantRpmValue = 0;
float m_instantRpm = 0;
private:
float calculateInstantRpm(
TriggerWaveform const & triggerShape, TriggerFormDetails *triggerFormDetails,
uint32_t index, efitick_t nowNt);
float m_instantRpmRatio = 0;
};
/**
* the reason for sub-class is simply to save RAM but not having statistics in the trigger initialization instance
*/