rusefi-1/firmware/controllers/engine_cycle/map_averaging.cpp

374 lines
12 KiB
C++
Raw Normal View History

2015-07-10 06:01:56 -07:00
/**
* @file map_averaging.cpp
*
2017-06-21 23:23:42 -07:00
* In order to have best MAP estimate possible, we real MAP value at a relatively high frequency
* and average the value within a specified angle position window for each cylinder
*
2015-07-10 06:01:56 -07:00
* @date Dec 11, 2013
2020-01-13 18:57:43 -08:00
* @author Andrey Belomutskiy, (c) 2012-2020
2015-07-10 06:01:56 -07:00
*
* This file is part of rusEfi - see http://rusefi.com
*
* rusEfi is free software; you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
2018-09-16 19:26:57 -07:00
#include "global.h"
2019-07-05 17:03:32 -07:00
#include "os_access.h"
2015-07-10 06:01:56 -07:00
#include "map.h"
2019-04-12 19:07:03 -07:00
#if EFI_MAP_AVERAGING
2015-07-10 06:01:56 -07:00
#include "map_averaging.h"
#include "trigger_central.h"
#include "adc_inputs.h"
2019-05-27 15:58:43 -07:00
#include "allsensors.h"
2015-07-10 06:01:56 -07:00
#include "engine_configuration.h"
#include "interpolation.h"
#include "engine.h"
#include "engine_math.h"
2019-10-13 13:14:08 -07:00
#include "perf_trace.h"
2015-07-10 06:01:56 -07:00
2019-04-12 19:07:03 -07:00
#if EFI_SENSOR_CHART
2015-09-12 16:01:20 -07:00
#include "sensor_chart.h"
2015-09-13 09:01:42 -07:00
#endif /* EFI_SENSOR_CHART */
2015-07-10 06:01:56 -07:00
#define FAST_MAP_CHART_SKIP_FACTOR 16
static Logging *logger;
2016-09-06 21:02:11 -07:00
/**
* this instance does not have a real physical pin - it's only used for engine sniffer
*/
2015-07-10 06:01:56 -07:00
static NamedOutputPin mapAveragingPin("map");
/**
* Running counter of measurements per revolution
*/
2015-09-06 18:02:46 -07:00
static volatile int measurementsPerRevolutionCounter = 0;
2015-07-10 06:01:56 -07:00
/**
* Number of measurements in previous shaft revolution
*/
2015-09-06 18:02:46 -07:00
static volatile int measurementsPerRevolution = 0;
2015-07-10 06:01:56 -07:00
/**
* In this lock-free implementation 'readIndex' is always pointing
2015-07-10 06:01:56 -07:00
* to the consistent copy of accumulator and counter pair
*/
static int readIndex = 0;
static float accumulators[2];
static int counters[2];
/**
2017-06-21 23:23:42 -07:00
* Running MAP accumulator - sum of all measurements within averaging window
2015-07-10 06:01:56 -07:00
*/
2018-02-06 13:21:41 -08:00
static volatile float mapAdcAccumulator = 0;
2015-07-10 06:01:56 -07:00
/**
* Running counter of measurements to consider for averaging
*/
static volatile int mapMeasurementsCounter = 0;
/**
* v_ for Voltage
*/
static float v_averagedMapValue;
// allow a bit more smoothing
#define MAX_MAP_BUFFER_LENGTH (INJECTION_PIN_COUNT * 2)
// in MAP units, not voltage!
static float averagedMapRunningBuffer[MAX_MAP_BUFFER_LENGTH];
2017-08-06 13:29:14 -07:00
int mapMinBufferLength = 0;
static int averagedMapBufIdx = 0;
2018-02-06 21:12:34 -08:00
// we need this 'NO_VALUE_YET' to properly handle transition from engine not running to engine already running
// but prior to first processed result
#define NO_VALUE_YET -100
// this is 'minimal averaged' MAP within avegaging window
static float currentPressure = NO_VALUE_YET;
EXTERN_ENGINE;
2015-07-10 06:01:56 -07:00
2017-06-21 23:23:42 -07:00
/**
* here we have averaging start and averaging end points for each cylinder
*/
2015-09-06 20:01:28 -07:00
static scheduling_s startTimer[INJECTION_PIN_COUNT][2];
static scheduling_s endTimer[INJECTION_PIN_COUNT][2];
2015-07-10 06:01:56 -07:00
/**
* that's a performance optimization: let's not bother averaging
* if we are outside of of the window
*/
2016-01-11 14:01:33 -08:00
static bool isAveraging = false;
2015-07-10 06:01:56 -07:00
static void endAveraging(void *arg);
static void startAveraging(scheduling_s *endAveragingScheduling) {
2019-02-23 09:33:49 -08:00
efiAssertVoid(CUSTOM_ERR_6649, getCurrentRemainingStack() > 128, "lowstck#9");
2015-07-10 06:01:56 -07:00
bool wasLocked = lockAnyContext();
// with locking we would have a consistent state
2018-02-06 13:21:41 -08:00
mapAdcAccumulator = 0;
2015-07-10 06:01:56 -07:00
mapMeasurementsCounter = 0;
isAveraging = true;
if (!wasLocked) {
2017-05-25 11:51:21 -07:00
unlockAnyContext();
}
2017-04-21 16:23:20 -07:00
mapAveragingPin.setHigh();
#if ! EFI_UNIT_TEST
scheduleByAngle(endAveragingScheduling, getTimeNowNt(), ENGINE(engineState.mapAveragingDuration),
endAveraging PASS_ENGINE_PARAMETER_SUFFIX);
#endif
2015-07-10 06:01:56 -07:00
}
2019-04-12 19:07:03 -07:00
#if HAL_USE_ADC
2015-07-10 06:01:56 -07:00
/**
* This method is invoked from ADC callback.
* @note This method is invoked OFTEN, this method is a potential bottle-next - the implementation should be
* as fast as possible
*/
2018-02-06 12:58:57 -08:00
void mapAveragingAdcCallback(adcsample_t adcValue) {
2016-01-30 19:03:36 -08:00
if (!isAveraging && ENGINE(sensorChartMode) != SC_MAP) {
2015-07-10 06:01:56 -07:00
return;
}
/* Calculates the average values from the ADC samples.*/
2015-09-06 18:02:46 -07:00
measurementsPerRevolutionCounter++;
2019-02-23 09:33:49 -08:00
efiAssertVoid(CUSTOM_ERR_6650, getCurrentRemainingStack() > 128, "lowstck#9a");
2015-07-10 06:01:56 -07:00
2019-04-12 19:07:03 -07:00
#if EFI_SENSOR_CHART && EFI_ANALOG_SENSORS
2016-01-30 19:03:36 -08:00
if (ENGINE(sensorChartMode) == SC_MAP) {
2015-09-06 20:01:28 -07:00
if (measurementsPerRevolutionCounter % FAST_MAP_CHART_SKIP_FACTOR
== 0) {
2015-07-10 06:01:56 -07:00
float voltage = adcToVoltsDivided(adcValue);
float currentPressure = getMapByVoltage(voltage);
2015-09-06 20:01:28 -07:00
scAddData(
2017-05-15 20:28:49 -07:00
getCrankshaftAngleNt(getTimeNowNt() PASS_ENGINE_PARAMETER_SUFFIX),
2015-09-06 20:01:28 -07:00
currentPressure);
2015-07-10 06:01:56 -07:00
}
2015-09-06 18:02:46 -07:00
}
2015-09-13 09:01:42 -07:00
#endif /* EFI_SENSOR_CHART */
2015-07-10 06:01:56 -07:00
/**
* Local copy is now safe, but it's an overkill: we only
* have one writing thread anyway
*/
int readIndexLocal = readIndex;
int writeIndex = readIndexLocal ^ 1;
accumulators[writeIndex] = accumulators[readIndexLocal] + adcValue;
counters[writeIndex] = counters[readIndexLocal] + 1;
// this would commit the new pair of values
readIndex = writeIndex;
// todo: migrate to the lock-free implementation
2017-05-25 11:51:21 -07:00
bool alreadyLocked = lockAnyContext();
2015-07-10 06:01:56 -07:00
;
// with locking we would have a consistent state
2018-02-06 13:21:41 -08:00
mapAdcAccumulator += adcValue;
2015-07-10 06:01:56 -07:00
mapMeasurementsCounter++;
2017-05-25 11:51:21 -07:00
if (!alreadyLocked)
unlockAnyContext();
2015-07-10 06:01:56 -07:00
;
}
#endif
static void endAveraging(void *arg) {
(void) arg;
#if ! EFI_UNIT_TEST
2015-07-10 06:01:56 -07:00
bool wasLocked = lockAnyContext();
#endif
2015-07-10 06:01:56 -07:00
isAveraging = false;
// with locking we would have a consistent state
2019-04-12 19:07:03 -07:00
#if HAL_USE_ADC
2018-02-10 03:02:48 -08:00
if (mapMeasurementsCounter > 0) {
v_averagedMapValue = adcToVoltsDivided(mapAdcAccumulator / mapMeasurementsCounter);
// todo: move out of locked context?
averagedMapRunningBuffer[averagedMapBufIdx] = getMapByVoltage(v_averagedMapValue);
// increment circular running buffer index
averagedMapBufIdx = (averagedMapBufIdx + 1) % mapMinBufferLength;
// find min. value (only works for pressure values, not raw voltages!)
float minPressure = averagedMapRunningBuffer[0];
for (int i = 1; i < mapMinBufferLength; i++) {
if (averagedMapRunningBuffer[i] < minPressure)
minPressure = averagedMapRunningBuffer[i];
}
currentPressure = minPressure;
} else {
warning(CUSTOM_UNEXPECTED_MAP_VALUE, "No MAP values");
}
2015-07-10 06:01:56 -07:00
#endif
#if ! EFI_UNIT_TEST
2015-07-10 06:01:56 -07:00
if (!wasLocked)
2017-05-25 11:51:21 -07:00
unlockAnyContext();
2015-07-10 06:01:56 -07:00
;
#endif
2017-04-21 16:23:20 -07:00
mapAveragingPin.setLow();
2015-07-10 06:01:56 -07:00
}
static void applyMapMinBufferLength(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
2017-08-06 14:05:57 -07:00
// check range
mapMinBufferLength = maxI(minI(CONFIG(mapMinBufferLength), MAX_MAP_BUFFER_LENGTH), 1);
2017-08-06 14:05:57 -07:00
// reset index
averagedMapBufIdx = 0;
// fill with maximum values
for (int i = 0; i < mapMinBufferLength; i++) {
averagedMapRunningBuffer[i] = FLT_MAX;
}
}
2019-04-12 19:07:03 -07:00
#if EFI_TUNER_STUDIO
void postMapState(TunerStudioOutputChannels *tsOutputChannels) {
2018-02-06 13:21:41 -08:00
tsOutputChannels->debugFloatField1 = v_averagedMapValue;
tsOutputChannels->debugFloatField2 = engine->engineState.mapAveragingDuration;
2018-02-06 21:12:34 -08:00
tsOutputChannels->debugFloatField3 = currentPressure;
2018-02-06 13:21:41 -08:00
tsOutputChannels->debugIntField1 = mapMeasurementsCounter;
}
#endif /* EFI_TUNER_STUDIO */
2018-02-06 13:21:41 -08:00
void refreshMapAveragingPreCalc(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
2020-09-03 16:29:15 -07:00
int rpm = GET_RPM();
if (isValidRpm(rpm)) {
MAP_sensor_config_s * c = &engineConfiguration->map;
angle_t start = interpolate2d("mapa", rpm, c->samplingAngleBins, c->samplingAngle);
2019-11-04 06:20:00 -08:00
efiAssertVoid(CUSTOM_ERR_MAP_START_ASSERT, !cisnan(start), "start");
2020-08-24 21:59:07 -07:00
angle_t offsetAngle = ENGINE(triggerCentral.triggerFormDetails).eventAngles[CONFIG(mapAveragingSchedulingAtIndex)];
efiAssertVoid(CUSTOM_ERR_MAP_AVG_OFFSET, !cisnan(offsetAngle), "offsetAngle");
for (int i = 0; i < engineConfiguration->specs.cylindersCount; i++) {
angle_t cylinderOffset = getEngineCycle(engine->getOperationMode(PASS_ENGINE_PARAMETER_SIGNATURE)) * i / engineConfiguration->specs.cylindersCount;
2019-11-04 06:20:00 -08:00
efiAssertVoid(CUSTOM_ERR_MAP_CYL_OFFSET, !cisnan(cylinderOffset), "cylinderOffset");
// part of this formula related to specific cylinder offset is never changing - we can
// move the loop into start-up calculation and not have this loop as part of periodic calculation
// todo: change the logic as described above in order to reduce periodic CPU usage?
float cylinderStart = start + cylinderOffset - offsetAngle + tdcPosition();
2018-07-23 18:38:05 -07:00
fixAngle(cylinderStart, "cylinderStart", CUSTOM_ERR_6562);
engine->engineState.mapAveragingStart[i] = cylinderStart;
}
engine->engineState.mapAveragingDuration = interpolate2d("samp", rpm, c->samplingWindowBins, c->samplingWindow);
} else {
for (int i = 0; i < engineConfiguration->specs.cylindersCount; i++) {
engine->engineState.mapAveragingStart[i] = NAN;
}
engine->engineState.mapAveragingDuration = NAN;
}
}
2015-07-10 06:01:56 -07:00
/**
* Shaft Position callback used to schedule start and end of MAP averaging
*/
void mapAveragingTriggerCallback(trigger_event_e ckpEventType,
uint32_t index, efitick_t edgeTimestamp DECLARE_ENGINE_PARAMETER_SUFFIX) {
2019-10-13 13:14:08 -07:00
ScopePerf perf(PE::MapAveragingTriggerCallback);
2019-04-12 19:07:03 -07:00
#if EFI_ENGINE_CONTROL
2015-07-10 06:01:56 -07:00
// this callback is invoked on interrupt thread
2015-09-06 20:01:28 -07:00
UNUSED(ckpEventType);
2019-11-05 19:49:11 -08:00
if (index != (uint32_t)CONFIG(mapAveragingSchedulingAtIndex))
2015-07-10 06:01:56 -07:00
return;
2020-09-03 16:29:15 -07:00
int rpm = GET_RPM();
2017-05-25 20:23:03 -07:00
if (!isValidRpm(rpm)) {
2015-07-10 06:01:56 -07:00
return;
2017-05-25 20:23:03 -07:00
}
2015-07-10 06:01:56 -07:00
if (CONFIG(mapMinBufferLength) != mapMinBufferLength) {
applyMapMinBufferLength(PASS_ENGINE_PARAMETER_SIGNATURE);
}
2015-09-06 18:02:46 -07:00
measurementsPerRevolution = measurementsPerRevolutionCounter;
measurementsPerRevolutionCounter = 0;
2015-07-10 06:01:56 -07:00
int samplingCount = CONFIG(measureMapOnlyInOneCylinder) ? 1 : engineConfiguration->specs.cylindersCount;
2018-01-24 06:14:30 -08:00
for (int i = 0; i < samplingCount; i++) {
2015-09-06 20:01:28 -07:00
angle_t samplingStart = ENGINE(engineState.mapAveragingStart[i]);
2015-07-10 06:01:56 -07:00
2015-09-06 20:01:28 -07:00
angle_t samplingDuration = ENGINE(engineState.mapAveragingDuration);
2019-06-27 19:23:18 -07:00
assertAngleRange(samplingDuration, "samplingDuration", CUSTOM_ERR_6563);
2015-09-06 20:01:28 -07:00
if (samplingDuration <= 0) {
2017-05-29 16:23:15 -07:00
warning(CUSTOM_MAP_ANGLE_PARAM, "map sampling angle should be positive");
2015-09-06 20:01:28 -07:00
return;
}
2015-07-10 06:01:56 -07:00
2015-09-06 20:01:28 -07:00
angle_t samplingEnd = samplingStart + samplingDuration;
2018-02-06 13:21:41 -08:00
if (cisnan(samplingEnd)) {
// todo: when would this happen?
warning(CUSTOM_ERR_6549, "no map angles");
return;
2015-09-06 20:01:28 -07:00
}
2015-07-10 06:01:56 -07:00
2018-07-23 18:38:05 -07:00
fixAngle(samplingEnd, "samplingEnd", CUSTOM_ERR_6563);
2018-02-06 13:21:41 -08:00
// only if value is already prepared
int structIndex = getRevolutionCounter() % 2;
// at the moment we schedule based on time prediction based on current RPM and angle
// we are loosing precision in case of changing RPM - the further away is the event the worse is precision
2018-02-06 13:21:41 -08:00
// todo: schedule this based on closest trigger event, same as ignition works
scheduleByAngle(&startTimer[i][structIndex], edgeTimestamp, samplingStart,
{ startAveraging, &endTimer[i][structIndex] } PASS_ENGINE_PARAMETER_SUFFIX);
2018-02-06 13:21:41 -08:00
}
2019-01-31 08:57:15 -08:00
#endif
2015-07-10 06:01:56 -07:00
}
static void showMapStats(void) {
2015-09-06 18:02:46 -07:00
scheduleMsg(logger, "per revolution %d", measurementsPerRevolution);
2015-07-10 06:01:56 -07:00
}
2019-04-12 19:07:03 -07:00
#if EFI_PROD_CODE
2015-07-10 06:01:56 -07:00
/**
* Because of MAP window averaging, MAP is only available while engine is spinning
* @return Manifold Absolute Pressure, in kPa
*/
float getMap(void) {
if (engineConfiguration->hasFrequencyReportingMapSensor) {
return getRawMap();
}
2019-04-12 19:07:03 -07:00
#if EFI_ANALOG_SENSORS
2020-09-03 16:29:15 -07:00
if (!isValidRpm(GET_RPM()) || currentPressure == NO_VALUE_YET)
2015-08-30 14:01:21 -07:00
return validateMap(getRawMap()); // maybe return NaN in case of stopped engine?
return validateMap(currentPressure);
2015-07-10 06:01:56 -07:00
#else
return 100;
#endif
}
#endif /* EFI_PROD_CODE */
void initMapAveraging(Logging *sharedLogger DECLARE_ENGINE_PARAMETER_SUFFIX) {
2015-07-10 06:01:56 -07:00
logger = sharedLogger;
2020-07-31 14:41:42 -07:00
#if !EFI_UNIT_TEST
2015-07-10 06:01:56 -07:00
addConsoleAction("faststat", showMapStats);
2019-12-23 20:25:08 -08:00
#endif /* EFI_UNIT_TEST */
applyMapMinBufferLength(PASS_ENGINE_PARAMETER_SIGNATURE);
2015-07-10 06:01:56 -07:00
}
#else
2019-04-12 19:07:03 -07:00
#if EFI_PROD_CODE
2015-07-10 06:01:56 -07:00
float getMap(void) {
2019-04-12 19:07:03 -07:00
#if EFI_ANALOG_SENSORS
2015-07-10 06:01:56 -07:00
return getRawMap();
#else
return NAN;
#endif /* EFI_ANALOG_SENSORS */
}
#endif /* EFI_PROD_CODE */
#endif /* EFI_MAP_AVERAGING */