rusefi/firmware/controllers/trigger/rpm_calculator.cpp

188 lines
5.2 KiB
C++

/**
* @file rpm_calculator.c
* @brief RPM calculator
*
* Here we listen to position sensor events in order to figure our if engine is currently running or not.
* Actual getRpm() is calculated once per crankshaft revolution, based on the amount of time passed
* since the start of previous shaft revolution.
*
* @date Jan 1, 2013
* @author Andrey Belomutskiy, (c) 2012-2014
*/
#include "main.h"
#include "rpm_calculator.h"
#include "trigger_central.h"
#include "engine_configuration.h"
#include "ec2.h"
#include "engine_math.h"
#if EFI_PROD_CODE || EFI_SIMULATOR
#include "analog_chart.h"
#endif /* EFI_PROD_CODE */
#include "wave_chart.h"
#include "rfiutil.h"
static rpm_s rpmState;
#define UNREALISTIC_RPM 30000
#define TOP_DEAD_CENTER_MESSAGE "r"
extern engine_configuration_s *engineConfiguration;
extern engine_configuration2_s *engineConfiguration2;
extern WaveChart waveChart;
static Logging logger;
/**
* @return true if there was a full shaft revolution within the last second
*/
int isRunning() {
uint64_t nowUs = getTimeNowUs();
return nowUs - rpmState.lastRpmEventTimeUs < US_PER_SECOND;
}
uint64_t getLastRpmEventTime(void) {
return rpmState.lastRpmEventTimeUs;
}
int isCranking(void) {
int rpm = getRpm();
return isCrankingR(rpm);
}
/**
* @return -1 in case of isNoisySignal(), current RPM otherwise
*/
int getRpm() {
if (!isRunning())
return 0;
return rpmState.rpm;
}
/**
* @return Current crankshaft angle, 0 to 720 for four-stroke
*/
float getCrankshaftAngle(uint64_t timeUs) {
uint64_t timeSinceZeroAngle = timeUs - rpmState.lastRpmEventTimeUs;
float cRevolutionTimeMs = getCrankshaftRevolutionTimeMs(rpmState.rpm);
return 360.0 * timeSinceZeroAngle / cRevolutionTimeMs / 1000;
}
int getRevolutionCounter(void) {
return rpmState.revolutionCounter;
}
/**
* Checks for noise on the trigger input line. Noise is detected by an unexpectedly small time gap between
* current and previous trigger input events.
*
* @return TRUE if noise is detected
*/
static int isNoisySignal(rpm_s * rpmState, uint64_t nowUs) {
uint64_t diff = nowUs - rpmState->lastRpmEventTimeUs;
return diff < 1000; // that's 1ms
}
static uint8_t shaft_signal_msg_index[15];
void addWaveChartEvent(char *name, char * msg, char *msg2) {
addWaveChartEvent3(&waveChart, name, msg, msg2);
}
/**
* @brief Shaft position callback used by RPM calculation logic.
*
* This callback is invoked on interrupt thread.
*/
static void shaftPositionCallback(ShaftEvents ckpSignalType, int index) {
itoa10(&shaft_signal_msg_index[1], index);
if (ckpSignalType == SHAFT_PRIMARY_UP) {
addWaveChartEvent("crank", "up", (char*)shaft_signal_msg_index);
} else if (ckpSignalType == SHAFT_PRIMARY_DOWN) {
addWaveChartEvent("crank", "down", (char*)shaft_signal_msg_index);
} else if (ckpSignalType == SHAFT_SECONDARY_UP) {
addWaveChartEvent("crank2", "up", (char*)shaft_signal_msg_index);
} else if (ckpSignalType == SHAFT_SECONDARY_DOWN) {
addWaveChartEvent("crank2", "down", (char*)shaft_signal_msg_index);
}
if (index != 0) {
#if EFI_PROD_CODE || EFI_SIMULATOR
if (engineConfiguration->analogChartMode == AC_TRIGGER)
acAddData(getCrankshaftAngle(getTimeNowUs()), 1000 * ckpSignalType + index);
#endif
return;
}
rpmState.revolutionCounter++;
uint64_t nowUs = getTimeNowUs();
int hadRpmRecently = isRunning();
if (hadRpmRecently) {
if (isNoisySignal(&rpmState, nowUs)) {
// unexpected state. Noise?
rpmState.rpm = NOISY_RPM;
} else {
uint64_t diff = nowUs - rpmState.lastRpmEventTimeUs;
// 60000 because per minute
// * 2 because each revolution of crankshaft consists of two camshaft revolutions
// / 4 because each cylinder sends a signal
// need to measure time from the previous non-skipped event
int rpm = (int)(60 * US_PER_SECOND / engineConfiguration->rpmMultiplier / diff);
rpmState.rpm = rpm > UNREALISTIC_RPM ? NOISY_RPM : rpm;
}
}
rpmState.lastRpmEventTimeUs = nowUs;
#if EFI_PROD_CODE || EFI_SIMULATOR
if (engineConfiguration->analogChartMode == AC_TRIGGER)
acAddData(getCrankshaftAngle(nowUs), index);
#endif
}
static scheduling_s tdcScheduler;
static uint8_t rpmBuffer[10];
static void onTdcCallback(void) {
itoa10(rpmBuffer, getRpm());
addWaveChartEvent(TOP_DEAD_CENTER_MESSAGE, (char*)rpmBuffer, "");
}
static void tdcMarkCallback(ShaftEvents ckpSignalType, int index) {
if (index == 0) {
scheduleByAngle(&tdcScheduler, engineConfiguration->globalTriggerAngleOffset, (schfunc_t) onTdcCallback, NULL);
}
}
void initRpmCalculator(void) {
initLogging(&logger, "rpm calc");
strcpy((char*)shaft_signal_msg_index, "_");
rpmState.rpm = 0;
// we need this initial to have not_running at first invocation
rpmState.lastRpmEventTimeUs = (uint64_t)-10 * US_PER_SECOND;
addTriggerEventListener(&shaftPositionCallback, "rpm reporter");
addTriggerEventListener(&tdcMarkCallback, "chart TDC mark");
}
/**
* Schedules a callback 'angle' degree of crankshaft from now.
* The callback would be executed once after the duration of time which
* it takes the crankshaft to rotate to the specified angle.
*/
void scheduleByAngle(scheduling_s *timer, float angle, schfunc_t callback, void *param) {
float delayMs = getOneDegreeTimeMs(getRpm()) * angle;
scheduleTask(timer, MS2US(delayMs), callback, param);
}