rusefi-1/firmware/controllers/trigger/rpm_calculator.cpp

271 lines
7.4 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
2015-01-12 15:04:10 -08:00
* @author Andrey Belomutskiy, (c) 2012-2015
2014-08-29 07:52:33 -07:00
*/
#include "main.h"
2015-01-20 14:07:26 -08:00
#include "rpm_calculator.h"
2014-08-29 07:52:33 -07:00
#if EFI_SHAFT_POSITION_INPUT || defined(__DOXYGEN__)
#include "trigger_central.h"
#include "engine_configuration.h"
#include "engine_math.h"
2015-01-12 16:05:46 -08:00
2014-08-29 07:52:33 -07:00
#if EFI_PROD_CODE
#include "rfiutil.h"
#include "engine.h"
#endif
2015-01-12 16:05:46 -08:00
#if ! EFI_UNIT_TEST
2014-08-29 07:52:33 -07:00
#include "analog_chart.h"
2015-01-12 16:05:46 -08:00
#endif
2014-08-29 07:52:33 -07:00
2014-11-26 07:03:13 -08:00
#include "efilib2.h"
2015-01-12 16:05:46 -08:00
#if EFI_WAVE_CHART
#include "wave_chart.h"
extern WaveChart waveChart;
#endif /* EFI_WAVE_CHART */
2014-08-29 07:52:33 -07:00
2015-01-01 16:03:31 -08:00
EXTERN_ENGINE;
2014-08-29 07:52:33 -07:00
2015-01-14 18:03:44 -08:00
static Logging * logger;
2014-08-29 07:52:33 -07:00
RpmCalculator::RpmCalculator() {
2014-10-17 12:02:59 -07:00
#if !EFI_PROD_CODE
mockRpm = MOCK_UNDEFINED;
#endif
2014-11-11 11:06:07 -08:00
setRpmValue(0);
2014-08-29 07:52:33 -07:00
// we need this initial to have not_running at first invocation
2014-11-06 18:05:30 -08:00
lastRpmEventTimeNt = (uint64_t) -10 * US2NT(US_PER_SECOND_LL);
2014-11-03 10:03:03 -08:00
revolutionCounterSinceStart = 0;
revolutionCounterSinceBoot = 0;
2014-11-11 13:05:09 -08:00
lastRpmEventTimeNt = 0;
2015-02-12 15:04:08 -08:00
oneDegreeUs = NAN;
2014-08-29 07:52:33 -07:00
}
/**
* @return true if there was a full shaft revolution within the last second
*/
2014-12-03 14:03:09 -08:00
bool RpmCalculator::isRunning(DECLARE_ENGINE_PARAMETER_F) {
2014-11-06 17:04:40 -08:00
uint64_t nowNt = getTimeNowNt();
2015-01-01 14:03:28 -08:00
if (engine->stopEngineRequestTimeNt != 0) {
2014-12-04 12:03:27 -08:00
if (nowNt - lastRpmEventTimeNt < 3 * US2NT(US_PER_SECOND_LL)) {
return false;
}
}
return nowNt - lastRpmEventTimeNt < US2NT(US_PER_SECOND_LL);
2014-08-29 07:52:33 -07:00
}
2014-11-11 11:06:07 -08:00
void RpmCalculator::setRpmValue(int value) {
2015-01-14 20:03:40 -08:00
previousRpmValue = rpmValue;
2014-11-11 11:06:07 -08:00
rpmValue = value;
2014-11-11 13:05:09 -08:00
if (rpmValue <= 0) {
oneDegreeUs = NAN;
} else {
oneDegreeUs = getOneDegreeTimeUs(rpmValue);
}
2014-11-11 11:06:07 -08:00
}
2014-11-03 10:03:03 -08:00
void RpmCalculator::onNewEngineCycle() {
revolutionCounterSinceBoot++;
revolutionCounterSinceStart++;
}
uint32_t RpmCalculator::getRevolutionCounter(void) {
return revolutionCounterSinceBoot;
}
uint32_t RpmCalculator::getRevolutionCounterSinceStart(void) {
return revolutionCounterSinceStart;
}
2015-01-14 20:03:40 -08:00
float RpmCalculator::getRpmAcceleration() {
return 1.0 * previousRpmValue / rpmValue;
}
2014-11-24 07:03:19 -08:00
/**
* WARNING: this is a heavy method because 'getRpm()' is relatively heavy
*
* @return -1 in case of isNoisySignal(), current RPM otherwise
*/
2015-02-10 07:09:58 -08:00
// todo: migrate to float return result or add a float version? this would have with calculations
2014-09-12 10:02:59 -07:00
// todo: add a version which does not check time & saves time? need to profile
2014-12-03 14:03:09 -08:00
int RpmCalculator::rpm(DECLARE_ENGINE_PARAMETER_F) {
2014-10-17 12:02:59 -07:00
#if !EFI_PROD_CODE
if (mockRpm != MOCK_UNDEFINED)
2014-11-06 17:04:40 -08:00
return mockRpm;
2014-10-17 12:02:59 -07:00
#endif
2014-12-03 14:03:09 -08:00
if (!isRunning(PASS_ENGINE_PARAMETER_F)) {
2014-11-03 10:03:03 -08:00
revolutionCounterSinceStart = 0;
2014-12-17 17:04:56 -08:00
rpmValue = 0;
2014-09-12 10:02:59 -07:00
}
2014-08-29 07:52:33 -07:00
return rpmValue;
}
#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-11-07 19:04:45 -08: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-11-24 18:03:34 -08:00
void rpmShaftPositionCallback(trigger_event_e ckpSignalType, uint32_t index DECLARE_ENGINE_PARAMETER_S) {
2014-11-11 13:05:09 -08:00
RpmCalculator *rpmState = &engine->rpmCalculator;
2014-11-06 17:04:40 -08:00
uint64_t nowNt = getTimeNowNt();
2014-11-08 06:03:25 -08:00
#if EFI_PROD_CODE
2014-11-08 16:03:17 -08:00
efiAssertVoid(getRemainingStack(chThdSelf()) > 256, "lowstck#2z");
2014-11-08 06:03:25 -08:00
#endif
2014-08-29 07:52:33 -07:00
if (index != 0) {
#if EFI_ANALOG_CHART || defined(__DOXYGEN__)
2015-01-22 11:04:19 -08:00
if (boardConfiguration->analogChartMode == AC_TRIGGER)
2014-12-03 14:03:09 -08:00
acAddData(getCrankshaftAngleNt(nowNt PASS_ENGINE_PARAMETER), 1000 * ckpSignalType + index);
2014-08-29 07:52:33 -07:00
#endif
return;
}
2014-12-03 14:03:09 -08:00
bool hadRpmRecently = rpmState->isRunning(PASS_ENGINE_PARAMETER_F);
2014-08-29 07:52:33 -07:00
if (hadRpmRecently) {
2014-11-06 17:04:40 -08:00
uint64_t diffNt = nowNt - rpmState->lastRpmEventTimeNt;
2014-09-12 07:06:12 -07:00
/**
* 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
*
*/
2014-11-06 17:04:40 -08:00
if (diffNt == 0) {
2014-11-11 11:06:07 -08:00
rpmState->setRpmValue(NOISY_RPM);
2014-08-29 07:52:33 -07:00
} else {
2014-11-06 17:04:40 -08:00
// todo: interesting what is this *2 about? four stroke magic constant?
2014-11-06 18:05:30 -08:00
int rpm = (int) (60 * US2NT(US_PER_SECOND_LL) * 2 / diffNt);
2014-11-11 11:06:07 -08:00
rpmState->setRpmValue(rpm > UNREALISTIC_RPM ? NOISY_RPM : rpm);
2014-08-29 07:52:33 -07:00
}
}
2014-11-11 12:03:07 -08:00
rpmState->onNewEngineCycle();
2014-11-06 17:04:40 -08:00
rpmState->lastRpmEventTimeNt = nowNt;
2014-08-29 07:52:33 -07:00
#if EFI_ANALOG_CHART || defined(__DOXYGEN__)
2015-01-22 11:04:19 -08:00
if (boardConfiguration->analogChartMode == AC_TRIGGER)
2014-12-03 14:03:09 -08:00
acAddData(getCrankshaftAngleNt(nowNt PASS_ENGINE_PARAMETER), index);
2014-08-29 07:52:33 -07:00
#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());
2014-12-04 12:03:27 -08:00
addWaveChartEvent(TOP_DEAD_CENTER_MESSAGE, (char* ) rpmBuffer);
2014-08-29 07:52:33 -07:00
}
/**
* This trigger callback schedules the actual physical TDC callback in relation to trigger synchronization point.
*/
2014-11-24 18:03:34 -08:00
static void tdcMarkCallback(trigger_event_e ckpSignalType, uint32_t index0 DECLARE_ENGINE_PARAMETER_S) {
2014-11-06 17:04:40 -08:00
(void) ckpSignalType;
2014-08-29 07:52:33 -07:00
bool isTriggerSynchronizationPoint = index0 == 0;
if (isTriggerSynchronizationPoint) {
2014-11-11 13:05:09 -08:00
int revIndex2 = engine->rpmCalculator.getRevolutionCounter() % 2;
2014-11-11 12:03:07 -08:00
int rpm = getRpm();
2014-08-29 07:52:33 -07:00
// todo: use event-based scheduling, not just time-based scheduling
2015-02-04 14:04:24 -08:00
scheduleByAngle(rpm, &tdcScheduler[revIndex2], tdcPosition(),
2014-09-12 07:06:12 -07:00
(schfunc_t) onTdcCallback, NULL);
2014-08-29 07:52:33 -07:00
}
}
#endif
2014-11-11 13:05:09 -08:00
#if EFI_PROD_CODE || EFI_SIMULATOR
int getRevolutionCounter() {
return engine->rpmCalculator.getRevolutionCounter();
2014-08-29 07:52:33 -07:00
}
2014-11-11 13:05:09 -08:00
#endif
2014-08-29 07:52:33 -07:00
/**
* @return Current crankshaft angle, 0 to 720 for four-stroke
*/
2014-12-03 14:03:09 -08:00
float getCrankshaftAngleNt(uint64_t timeNt DECLARE_ENGINE_PARAMETER_S) {
2014-11-11 13:05:09 -08:00
uint64_t timeSinceZeroAngleNt = timeNt - engine->rpmCalculator.lastRpmEventTimeNt;
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-12-03 14:03:09 -08:00
return timeSinceZeroAngleNt / getOneDegreeTimeNt(engine->rpmCalculator.rpm(PASS_ENGINE_PARAMETER_F));
2014-08-29 07:52:33 -07:00
}
2014-11-08 09:03:07 -08:00
void initRpmCalculator(Engine *engine) {
2014-08-29 07:52:33 -07:00
#if (EFI_PROD_CODE || EFI_SIMULATOR) || defined(__DOXYGEN__)
tdcScheduler[0].name = "tdc0";
tdcScheduler[1].name = "tdc1";
2014-11-11 12:03:07 -08:00
addTriggerEventListener(tdcMarkCallback, "chart TDC mark", engine);
2014-08-29 07:52:33 -07:00
#endif
2014-11-11 13:05:09 -08:00
addTriggerEventListener(rpmShaftPositionCallback, "rpm reporter", engine);
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.
*/
2015-01-13 10:06:16 -08:00
void scheduleByAngle(int rpm, scheduling_s *timer, angle_t angle, schfunc_t callback, void *param) {
2014-08-29 07:52:33 -07:00
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;
}
2014-11-11 13:05:09 -08:00
float delayUs = getOneDegreeTimeUs(rpm) * angle;
if (cisnan(delayUs)) {
2014-08-29 07:52:33 -07:00
firmwareError("NaN delay?");
return;
}
2014-11-11 13:05:09 -08:00
scheduleTask("by angle", timer, (int) delayUs, callback, param);
2014-08-29 07:52:33 -07:00
}
#endif
2014-12-23 20:03:31 -08:00
#else
RpmCalculator::RpmCalculator() {
}
2014-08-29 07:52:33 -07:00
#endif /* EFI_SHAFT_POSITION_INPUT */