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

316 lines
9.2 KiB
C++
Raw Normal View History

2015-07-10 06:01:56 -07:00
/**
* @file rpm_calculator.cpp
* @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
2017-01-03 03:05:22 -08:00
* @author Andrey Belomutskiy, (c) 2012-2017
2015-07-10 06:01:56 -07:00
*/
#include "main.h"
#include "rpm_calculator.h"
#if EFI_SHAFT_POSITION_INPUT || defined(__DOXYGEN__)
#include "trigger_central.h"
#include "engine_configuration.h"
#include "engine_math.h"
#if EFI_PROD_CODE
#include "rfiutil.h"
#include "engine.h"
#endif
2015-09-13 09:01:42 -07:00
#if EFI_SENSOR_CHART || defined(__DOXYGEN__)
2015-09-12 16:01:20 -07:00
#include "sensor_chart.h"
2015-07-10 06:01:56 -07:00
#endif
#include "efilib2.h"
2015-07-15 18:01:45 -07:00
#if EFI_ENGINE_SNIFFER
#include "engine_sniffer.h"
2015-07-10 06:01:56 -07:00
extern WaveChart waveChart;
2015-07-15 18:01:45 -07:00
#endif /* EFI_ENGINE_SNIFFER */
2015-07-10 06:01:56 -07:00
EXTERN_ENGINE
;
efitime_t notRunnintNow;
efitime_t notRunningPrev;
2016-06-01 17:01:36 -07:00
extern bool hasFirmwareErrorFlag;
2015-07-10 06:01:56 -07:00
static Logging * logger;
2016-09-04 22:03:25 -07:00
int revolutionCounterSinceBootForUnitTest = 0;
2015-07-10 06:01:56 -07:00
RpmCalculator::RpmCalculator() {
#if !EFI_PROD_CODE
mockRpm = MOCK_UNDEFINED;
#endif
rpmValue = 0;
2016-03-15 19:03:43 -07:00
assignRpmValue(0);
2015-07-10 06:01:56 -07:00
// we need this initial to have not_running at first invocation
lastRpmEventTimeNt = (efitime_t) -10 * US2NT(US_PER_SECOND_LL);
revolutionCounterSinceStart = 0;
2016-09-04 22:03:25 -07:00
revolutionCounterSinceBootForUnitTest = revolutionCounterSinceBoot = 0;
2015-07-10 06:01:56 -07:00
lastRpmEventTimeNt = 0;
oneDegreeUs = NAN;
}
/**
* @return true if there was a full shaft revolution within the last second
*/
2017-05-15 20:28:49 -07:00
bool RpmCalculator::isRunning(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
2015-07-10 06:01:56 -07:00
efitick_t nowNt = getTimeNowNt();
2017-05-25 19:37:43 -07:00
if (ENGINE(stopEngineRequestTimeNt) != 0) {
2015-07-10 06:01:56 -07:00
if (nowNt
2017-05-25 19:37:43 -07:00
- ENGINE(stopEngineRequestTimeNt)< 3 * US2NT(US_PER_SECOND_LL)) {
2015-07-10 06:01:56 -07:00
return false;
}
}
2016-08-27 09:02:29 -07:00
if (lastRpmEventTimeNt == 0) {
// here we assume 64 bit time does not overflow, zero value is the default meaning no events so far
return false;
}
2015-07-10 06:01:56 -07:00
/**
* note that the result of this subtraction could be negative, that would happen if
* we have a trigger event between the time we've invoked 'getTimeNow' and here
*/
2017-05-26 05:48:40 -07:00
bool result = nowNt - lastRpmEventTimeNt < US2NT(2 * US_PER_SECOND_LL); // Anything below 60 rpm is not running
2015-07-10 06:01:56 -07:00
if (!result) {
notRunnintNow = nowNt;
notRunningPrev = lastRpmEventTimeNt;
}
return result;
}
2016-03-15 19:03:43 -07:00
// private method
void RpmCalculator::assignRpmValue(int value) {
2015-07-10 06:01:56 -07:00
previousRpmValue = rpmValue;
rpmValue = value;
if (rpmValue <= 0) {
oneDegreeUs = NAN;
} else {
oneDegreeUs = getOneDegreeTimeUs(rpmValue);
}
}
2017-05-15 20:28:49 -07:00
void RpmCalculator::setRpmValue(int value DECLARE_ENGINE_PARAMETER_SUFFIX) {
2016-03-15 19:03:43 -07:00
assignRpmValue(value);
if (previousRpmValue == 0 && rpmValue > 0) {
/**
* this would make sure that we have good numbers for first cranking revolution
* #275 cranking could be improved
*/
2017-05-25 19:37:43 -07:00
ENGINE(periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE));
2016-03-15 19:03:43 -07:00
}
}
2015-07-10 06:01:56 -07:00
void RpmCalculator::onNewEngineCycle() {
revolutionCounterSinceBoot++;
revolutionCounterSinceStart++;
2016-09-04 22:03:25 -07:00
#if EFI_UNIT_TEST
revolutionCounterSinceBootForUnitTest = revolutionCounterSinceBoot;
2017-05-25 19:37:43 -07:00
#endif /* EFI_UNIT_TEST */
2015-07-10 06:01:56 -07:00
}
uint32_t RpmCalculator::getRevolutionCounter(void) {
return revolutionCounterSinceBoot;
}
uint32_t RpmCalculator::getRevolutionCounterSinceStart(void) {
return revolutionCounterSinceStart;
}
float RpmCalculator::getRpmAcceleration() {
return 1.0 * previousRpmValue / rpmValue;
}
/**
* WARNING: this is a heavy method because 'getRpm()' is relatively heavy
*
* @return -1 in case of isNoisySignal(), current RPM otherwise
*/
// todo: migrate to float return result or add a float version? this would have with calculations
// todo: add a version which does not check time & saves time? need to profile
2017-05-15 20:28:49 -07:00
int RpmCalculator::getRpm(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
2015-07-10 06:01:56 -07:00
#if !EFI_PROD_CODE
if (mockRpm != MOCK_UNDEFINED)
return mockRpm;
#endif
2017-05-15 20:28:49 -07:00
if (!isRunning(PASS_ENGINE_PARAMETER_SIGNATURE)) {
2015-07-10 06:01:56 -07:00
revolutionCounterSinceStart = 0;
if (rpmValue != 0) {
rpmValue = 0;
scheduleMsg(logger,
"templog rpm=0 since not running [%x][%x] [%x][%x]",
(int) (notRunnintNow >> 32), (int) notRunnintNow,
(int) (notRunningPrev >> 32), (int) notRunningPrev);
}
}
return rpmValue;
}
#if (EFI_PROD_CODE || EFI_SIMULATOR) || defined(__DOXYGEN__)
bool isCrankingE(Engine *engine) {
int rpm = getRpmE(engine);
return isCrankingR(rpm);
}
#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.
*/
void rpmShaftPositionCallback(trigger_event_e ckpSignalType,
2017-05-15 20:28:49 -07:00
uint32_t index DECLARE_ENGINE_PARAMETER_SUFFIX) {
2015-07-10 06:01:56 -07:00
efitick_t nowNt = getTimeNowNt();
#if EFI_PROD_CODE
2017-04-05 05:47:06 -07:00
efiAssertVoid(getRemainingStack(chThdGetSelfX()) > 256, "lowstckRCL");
2015-07-10 06:01:56 -07:00
#endif
2017-05-25 19:49:40 -07:00
if (index == 0) {
ENGINE(m.beforeRpmCb) = GET_TIMESTAMP();
RpmCalculator *rpmState = &engine->rpmCalculator;
2015-07-10 06:01:56 -07:00
2017-05-25 19:49:40 -07:00
bool hadRpmRecently = rpmState->isRunning(PASS_ENGINE_PARAMETER_SIGNATURE);
2015-07-10 06:01:56 -07:00
2017-05-25 19:49:40 -07:00
if (hadRpmRecently) {
efitime_t diffNt = nowNt - rpmState->lastRpmEventTimeNt;
2015-07-10 06:01:56 -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
*
*/
2017-05-25 19:49:40 -07:00
if (diffNt == 0) {
rpmState->setRpmValue(NOISY_RPM PASS_ENGINE_PARAMETER_SUFFIX);
} else {
int mult = (int)getEngineCycle(engineConfiguration->operationMode) / 360;
int rpm = (int) (60 * US2NT(US_PER_SECOND_LL) * mult / diffNt);
rpmState->setRpmValue(rpm > UNREALISTIC_RPM ? NOISY_RPM : rpm PASS_ENGINE_PARAMETER_SUFFIX);
}
2015-07-10 06:01:56 -07:00
}
2017-05-25 19:49:40 -07:00
rpmState->onNewEngineCycle();
rpmState->lastRpmEventTimeNt = nowNt;
ENGINE(m.rpmCbTime) = GET_TIMESTAMP() - ENGINE(m.beforeRpmCb);
2015-07-10 06:01:56 -07:00
}
2017-05-25 19:49:40 -07:00
2015-09-13 09:01:42 -07:00
#if EFI_SENSOR_CHART || defined(__DOXYGEN__)
2016-05-28 21:01:59 -07:00
// this 'index==0' case is here so that it happens after cycle callback so
// it goes into sniffer report into the first position
2016-01-30 19:03:36 -08:00
if (ENGINE(sensorChartMode) == SC_TRIGGER) {
2017-05-25 19:49:40 -07:00
angle_t crankAngle = getCrankshaftAngleNt(nowNt PASS_ENGINE_PARAMETER_SUFFIX);
int signal = 1000 * ckpSignalType + index;
2015-07-26 21:01:35 -07:00
scAddData(crankAngle, signal);
}
2015-07-10 06:01:56 -07:00
#endif
2017-05-25 19:49:40 -07:00
2015-07-10 06:01:56 -07:00
}
static scheduling_s tdcScheduler[2];
2016-08-09 21:04:24 -07:00
static char rpmBuffer[_MAX_FILLER];
2015-07-10 06:01:56 -07:00
#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) {
2016-01-18 09:03:32 -08:00
itoa10(rpmBuffer, getRpmE(engine));
2016-01-30 19:03:36 -08:00
addEngineSniffferEvent(TOP_DEAD_CENTER_MESSAGE, (char* ) rpmBuffer);
2015-07-10 06:01:56 -07:00
}
/**
* This trigger callback schedules the actual physical TDC callback in relation to trigger synchronization point.
*/
static void tdcMarkCallback(trigger_event_e ckpSignalType,
2017-05-15 20:28:49 -07:00
uint32_t index0 DECLARE_ENGINE_PARAMETER_SUFFIX) {
2015-07-10 06:01:56 -07:00
(void) ckpSignalType;
bool isTriggerSynchronizationPoint = index0 == 0;
2016-01-30 19:03:36 -08:00
if (isTriggerSynchronizationPoint && ENGINE(isEngineChartEnabled)) {
2015-07-10 06:01:56 -07:00
int revIndex2 = engine->rpmCalculator.getRevolutionCounter() % 2;
2017-05-25 19:45:12 -07:00
int rpm = ENGINE(rpmCalculator.getRpm(PASS_ENGINE_PARAMETER_SIGNATURE));
2015-07-10 06:01:56 -07:00
// todo: use event-based scheduling, not just time-based scheduling
if (isValidRpm(rpm)) {
scheduleByAngle(rpm, &tdcScheduler[revIndex2], tdcPosition(),
(schfunc_t) onTdcCallback, NULL, &engine->rpmCalculator);
}
}
}
#endif
#if EFI_PROD_CODE || EFI_SIMULATOR
int getRevolutionCounter() {
return engine->rpmCalculator.getRevolutionCounter();
}
#endif
/**
* @return Current crankshaft angle, 0 to 720 for four-stroke
*/
2017-05-15 20:28:49 -07:00
float getCrankshaftAngleNt(efitime_t timeNt DECLARE_ENGINE_PARAMETER_SUFFIX) {
2015-07-10 06:01:56 -07:00
efitime_t timeSinceZeroAngleNt = timeNt
- engine->rpmCalculator.lastRpmEventTimeNt;
/**
* 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.
*/
2017-05-15 20:28:49 -07:00
int rpm = engine->rpmCalculator.getRpm(PASS_ENGINE_PARAMETER_SIGNATURE);
2015-07-10 06:01:56 -07:00
return rpm == 0 ? NAN : timeSinceZeroAngleNt / getOneDegreeTimeNt(rpm);
}
void initRpmCalculator(Logging *sharedLogger, Engine *engine) {
logger = sharedLogger;
2016-06-01 17:01:36 -07:00
if (hasFirmwareError()) {
return;
}
2015-07-10 06:01:56 -07:00
#if (EFI_PROD_CODE || EFI_SIMULATOR) || defined(__DOXYGEN__)
// tdcScheduler[0].name = "tdc0";
// tdcScheduler[1].name = "tdc1";
addTriggerEventListener(tdcMarkCallback, "chart TDC mark", engine);
#endif
addTriggerEventListener(rpmShaftPositionCallback, "rpm reporter", engine);
}
#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(int rpm, scheduling_s *timer, angle_t angle,
schfunc_t callback, void *param, RpmCalculator *calc) {
2016-06-06 17:02:51 -07:00
efiAssertVoid(!cisnan(angle), "NaN angle?");
2015-07-10 06:01:56 -07:00
efiAssertVoid(isValidRpm(rpm), "RPM check expected");
float delayUs = calc->oneDegreeUs * angle;
efiAssertVoid(!cisnan(delayUs), "NaN delay?");
2017-05-15 06:35:06 -07:00
scheduleTask(timer, (int) delayUs, callback, param);
2015-07-10 06:01:56 -07:00
}
#endif
#else
RpmCalculator::RpmCalculator() {
}
#endif /* EFI_SHAFT_POSITION_INPUT */