fome-fw/firmware/controllers/trigger/rpm_calculator.cpp

244 lines
6.7 KiB
C++
Raw Normal View History

2014-08-29 07:52:33 -07:00
/**
2014-09-11 16:02:59 -07:00
* @file rpm_calculator.cpp
2014-08-29 07:52:33 -07:00
* @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"
#if EFI_WAVE_CHART
#include "wave_chart.h"
extern WaveChart waveChart;
#endif /* EFI_WAVE_CHART */
#if EFI_SHAFT_POSITION_INPUT || defined(__DOXYGEN__)
#include "trigger_central.h"
#include "engine_configuration.h"
#include "ec2.h"
#include "engine_math.h"
#if EFI_PROD_CODE
#include "rfiutil.h"
#include "engine.h"
#endif
#if EFI_ANALOG_CHART
#include "analog_chart.h"
#endif /* EFI_PROD_CODE */
#define UNREALISTIC_RPM 30000
#define TOP_DEAD_CENTER_MESSAGE "r"
/**
2014-10-02 20:03:25 -07:00
* WARNING: this is a heavy method because 'getRpm()' is relatively heavy
*
2014-08-29 07:52:33 -07:00
* @return -1 in case of isNoisySignal(), current RPM otherwise
*/
int getRpmE(Engine *engine) {
efiAssert(engine->rpmCalculator!=NULL, "rpmCalculator not assigned", -1);
return engine->rpmCalculator->rpm();
}
extern engine_configuration_s *engineConfiguration;
extern engine_configuration2_s *engineConfiguration2;
#if EFI_PROD_CODE || EFI_SIMULATOR
static Logging logger;
extern Engine engine;
#endif
RpmCalculator::RpmCalculator() {
rpmValue = 0;
// we need this initial to have not_running at first invocation
lastRpmEventTimeUs = (uint64_t) -10 * US_PER_SECOND;
}
/**
* @return true if there was a full shaft revolution within the last second
*/
bool RpmCalculator::isRunning(void) {
uint64_t nowUs = getTimeNowUs();
return nowUs - lastRpmEventTimeUs < US_PER_SECOND;
}
2014-09-12 10:02:59 -07:00
// todo: migrate to float return result or add a float verion? this would have with calculations
// todo: add a version which does not check time & saves time? need to profile
2014-08-29 07:52:33 -07:00
int RpmCalculator::rpm(void) {
2014-09-12 10:02:59 -07:00
if (!isRunning()) {
2014-08-29 07:52:33 -07:00
return 0;
2014-09-12 10:02:59 -07:00
}
2014-08-29 07:52:33 -07:00
return rpmValue;
}
bool isValidRpm(int rpm) {
return rpm > 0 && rpm < UNREALISTIC_RPM;
}
#if (EFI_PROD_CODE || EFI_SIMULATOR) || defined(__DOXYGEN__)
2014-10-16 01:02:58 -07:00
bool isCrankingE(Engine *engine) {
int rpm = getRpmE(engine);
return isCrankingR(rpm);
}
2014-10-02 20:03:25 -07:00
/**
* WARNING: this is a heavy method because 'getRpm()' is relatively heavy
*/
2014-08-29 07:52:33 -07:00
bool isCranking(void) {
2014-10-16 01:02:58 -07:00
return isCrankingE(&engine);
2014-08-29 07:52:33 -07:00
}
#endif
/**
* @brief Shaft position callback used by RPM calculation logic.
*
* This callback should always be the first of trigger callbacks because other callbacks depend of values
* updated here.
* This callback is invoked on interrupt thread.
*/
2014-09-24 09:03:00 -07:00
void rpmShaftPositionCallback(trigger_event_e ckpSignalType, uint32_t index, RpmCalculator *rpmState) {
2014-10-02 21:02:57 -07:00
uint64_t nowUs = getTimeNowUs();
2014-08-29 07:52:33 -07:00
if (index != 0) {
#if EFI_ANALOG_CHART || defined(__DOXYGEN__)
if (engineConfiguration->analogChartMode == AC_TRIGGER)
2014-10-02 21:02:57 -07:00
acAddData(getCrankshaftAngle(nowUs), 1000 * ckpSignalType + index);
2014-08-29 07:52:33 -07:00
#endif
return;
}
rpmState->revolutionCounter++;
bool hadRpmRecently = rpmState->isRunning();
if (hadRpmRecently) {
2014-09-12 07:06:12 -07:00
uint64_t diff = nowUs - rpmState->lastRpmEventTimeUs;
/**
* Four stroke cycle is two crankshaft revolutions
*
* We always do '* 2' because the event signal is already adjusted to 'per engine cycle'
* and each revolution of crankshaft consists of two engine cycles revolutions
*
*/
if (diff == 0) {
2014-08-29 07:52:33 -07:00
rpmState->rpmValue = NOISY_RPM;
} else {
int rpm = (int) (60 * US_PER_SECOND * 2 / diff);
rpmState->rpmValue = rpm > UNREALISTIC_RPM ? NOISY_RPM : rpm;
}
}
rpmState->lastRpmEventTimeUs = nowUs;
#if EFI_ANALOG_CHART || defined(__DOXYGEN__)
if (engineConfiguration->analogChartMode == AC_TRIGGER)
acAddData(getCrankshaftAngle(nowUs), index);
#endif
}
static scheduling_s tdcScheduler[2];
static char rpmBuffer[10];
#if (EFI_PROD_CODE || EFI_SIMULATOR) || defined(__DOXYGEN__)
/**
* This callback has nothing to do with actual engine control, it just sends a Top Dead Center mark to the dev console
* digital sniffer.
*/
static void onTdcCallback(void) {
itoa10(rpmBuffer, getRpm());
addWaveChartEvent(TOP_DEAD_CENTER_MESSAGE, (char*) rpmBuffer, "");
}
/**
* This trigger callback schedules the actual physical TDC callback in relation to trigger synchronization point.
*/
2014-09-24 09:03:00 -07:00
static void tdcMarkCallback(trigger_event_e ckpSignalType, uint32_t index0, void *arg) {
2014-09-24 12:03:03 -07:00
(void)arg;
2014-09-24 11:04:07 -07:00
(void)ckpSignalType;
2014-08-29 07:52:33 -07:00
bool isTriggerSynchronizationPoint = index0 == 0;
if (isTriggerSynchronizationPoint) {
int revIndex2 = getRevolutionCounter() % 2;
// todo: use event-based scheduling, not just time-based scheduling
2014-09-12 07:06:12 -07:00
scheduleByAngle(&tdcScheduler[revIndex2], engineConfiguration->globalTriggerAngleOffset,
(schfunc_t) onTdcCallback, NULL);
2014-08-29 07:52:33 -07:00
}
}
#endif
static RpmCalculator rpmState;
uint64_t getLastRpmEventTime(void) {
return rpmState.lastRpmEventTimeUs;
}
int getRevolutionCounter(void) {
return rpmState.revolutionCounter;
}
/**
* @return Current crankshaft angle, 0 to 720 for four-stroke
*/
float getCrankshaftAngle(uint64_t timeUs) {
2014-09-12 08:05:18 -07:00
uint64_t timeSinceZeroAngleUs = timeUs - rpmState.lastRpmEventTimeUs;
2014-08-29 07:52:33 -07:00
2014-09-12 09:02:57 -07:00
/**
* even if we use 'getOneDegreeTimeUs' macros here, it looks like the
* compiler is not smart enough to figure out that "A / ( B / C)" could be optimized into
* "A * C / B" in order to replace a slower division with a faster multiplication.
*/
2014-09-12 08:05:18 -07:00
return timeSinceZeroAngleUs / getOneDegreeTimeUs(rpmState.rpm());
2014-08-29 07:52:33 -07:00
}
void initRpmCalculator(void) {
#if (EFI_PROD_CODE || EFI_SIMULATOR) || defined(__DOXYGEN__)
initLogging(&logger, "rpm calc");
engine.rpmCalculator = &rpmState;
tdcScheduler[0].name = "tdc0";
tdcScheduler[1].name = "tdc1";
addTriggerEventListener(&tdcMarkCallback, "chart TDC mark", NULL);
#endif
2014-09-12 07:06:12 -07:00
addTriggerEventListener((ShaftPositionListener) &rpmShaftPositionCallback, "rpm reporter", &rpmState);
2014-08-29 07:52:33 -07:00
}
#if (EFI_PROD_CODE || EFI_SIMULATOR) || defined(__DOXYGEN__)
/**
* 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) {
int rpm = getRpm();
if (!isValidRpm(rpm)) {
2014-09-29 17:02:57 -07:00
/**
* this might happen in case of a single trigger event after a pause - this is normal, so no
* warning here
*/
2014-08-29 07:52:33 -07:00
return;
}
float delayMs = getOneDegreeTimeMs(rpm) * angle;
if (cisnan(delayMs)) {
firmwareError("NaN delay?");
return;
}
2014-09-12 07:06:12 -07:00
scheduleTask("by angle", timer, (int) MS2US(delayMs), callback, param);
2014-08-29 07:52:33 -07:00
}
#endif
#endif /* EFI_SHAFT_POSITION_INPUT */
void addWaveChartEvent(const char *name, const char * msg, const char *msg2) {
#if EFI_WAVE_CHART
2014-09-11 08:03:38 -07:00
waveChart.addWaveChartEvent3(name, msg, msg2);
2014-08-29 07:52:33 -07:00
#endif /* EFI_WAVE_CHART */
}