put MAP in the sensor model (#3292)

* map averaging in sensor model

* deadly, deadly code!

* mpxh

* Revert "deadly, deadly code!"

This reverts commit 346fe25267966a313145a809792dced84be348cf.

* comments

* sensor types

* last sensor

* channel init

* correct spot

* deinit properly

* simplify

* fix

* mocks

* map init test

* showInfo

* comment

* singleton identity function

* sensor info print

* multiple cylinder averaging buffer

* comments

Co-authored-by: Matthew Kennedy <makenne@microsoft.com>
This commit is contained in:
Matthew Kennedy 2021-10-04 14:18:08 -07:00 committed by GitHub
parent eccdf18eec
commit db12cdbe19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 217 additions and 44 deletions

View File

@ -158,17 +158,25 @@ static void endAveraging(void*) {
#if HAL_USE_ADC
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];
SensorResult mapValue = convertMap(v_averagedMapValue);
// Skip update if conversion invalid
if (mapValue) {
averagedMapRunningBuffer[averagedMapBufIdx] = mapValue.Value;
// 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];
}
onMapAveraged(minPressure, getTimeNowNt());
currentPressure = minPressure;
}
currentPressure = minPressure;
} else {
warning(CUSTOM_UNEXPECTED_MAP_VALUE, "No MAP values");
}

View File

@ -19,6 +19,9 @@ void refreshMapAveragingPreCalc(DECLARE_ENGINE_PARAMETER_SIGNATURE);
void mapAveragingTriggerCallback(
uint32_t index, efitick_t edgeTimestamp DECLARE_ENGINE_PARAMETER_SUFFIX);
void onMapAveraged(float mapKpa, efitick_t nowNt);
SensorResult convertMap(float volts);
#if EFI_TUNER_STUDIO
void postMapState(TunerStudioOutputChannels *tsOutputChannels);
#endif

View File

@ -0,0 +1,3 @@
#include "identity_func.h"
IdentityFunction identityFunction;

View File

@ -0,0 +1,13 @@
#pragma once
#include "sensor_converter_func.h"
struct IdentityFunction : public SensorConverter {
SensorResult convert(float raw) const {
return raw;
}
void showInfo(float testRawValue) const;
};
extern IdentityFunction identityFunction;

View File

@ -0,0 +1,29 @@
#pragma once
#include "sensor.h"
class FallbackSensor final : public Sensor {
public:
FallbackSensor(SensorType outputType, SensorType primarySensor, SensorType fallbackSensor)
: Sensor(outputType)
, m_primary(primarySensor)
, m_fallback(fallbackSensor)
{
}
SensorResult get() const override {
auto primary = Sensor::get(m_primary);
if (primary) {
return primary;
}
return Sensor::get(m_fallback);
}
void showInfo(const char* sensorName) const override;
private:
const SensorType m_primary;
const SensorType m_fallback;
};

View File

@ -53,6 +53,9 @@ static const char* const s_sensorNames[] = {
"Vehicle speed",
"Turbo speed",
"MAP (fast)",
"MAP (slow)",
};
// This struct represents one sensor in the registry.

View File

@ -3,10 +3,12 @@
#include "functional_sensor.h"
#include "redundant_sensor.h"
#include "redundant_ford_tps.h"
#include "fallback_sensor.h"
#include "Lps25Sensor.h"
#include "linear_func.h"
#include "resistance_func.h"
#include "thermistor_func.h"
#include "identity_func.h"
void ProxySensor::showInfo(const char* sensorName) const {
efiPrintf("Sensor \"%s\" proxied from sensor \"%s\"", sensorName, getSensorName(m_proxiedSensor));
@ -39,6 +41,10 @@ void RedundantFordTps::showInfo(const char* sensorName) const {
efiPrintf("Sensor \"%s\" is Ford-type redundant TPS combining \"%s\" and \"%s\"", sensorName, getSensorName(m_first), getSensorName(m_second));
}
void FallbackSensor::showInfo(const char* sensorName) const {
efiPrintf("Sensor \"%s\" is fallback sensor with primary \"%s\" and fallback \"%s\"", sensorName, getSensorName(m_primary), getSensorName(m_fallback));
}
void RpmCalculator::showInfo(const char* /*sensorName*/) const {
efiPrintf("RPM sensor: stopped: %d spinning up: %d cranking: %d running: %d rpm: %f",
isStopped(),
@ -68,3 +74,7 @@ void ThermistorFunc::showInfo(float testInputValue) const {
const auto [valid, value] = convert(testInputValue);
efiPrintf(" %.1f ohms -> valid: %s. %.1f deg C", testInputValue, boolToString(valid), value);
}
void IdentityFunction::showInfo(float /*testInputValue*/) const {
efiPrintf(" Identity function passes along value.");
}

View File

@ -77,6 +77,11 @@ enum class SensorType : unsigned char {
TurbochargerSpeed = 35,
// Fast MAP is synchronous to crank angle - user selectable phase/window
MapFast = 36,
// Slow MAP is asynchronous - not synced to anything, normal analog sampling
MapSlow = 37,
// Leave me at the end!
PlaceholderLast = 36,
PlaceholderLast = 38,
};

View File

@ -19,4 +19,5 @@ CONTROLLERS_SENSORS_SRC_CPP = $(PROJECT_DIR)/controllers/sensors/thermistors.cp
$(PROJECT_DIR)/controllers/sensors/converters/linear_func.cpp \
$(PROJECT_DIR)/controllers/sensors/converters/resistance_func.cpp \
$(PROJECT_DIR)/controllers/sensors/converters/thermistor_func.cpp \
$(PROJECT_DIR)/controllers/sensors/converters/identity_func.cpp \
$(PROJECT_DIR)/controllers/sensors/vr_pwm.cpp

View File

@ -42,3 +42,4 @@ void deinitOilPressure();
void deInitFlexSensor();
void deInitVehicleSpeedSensor();
void deinitTurbochargerSpeedSensor();
void deinitMap();

View File

@ -3,15 +3,9 @@
#include "init.h"
#include "adc_subscription.h"
#include "functional_sensor.h"
#include "identity_func.h"
// These aux sensors just read voltage - so the converter function has nothing to do
struct IdentityFunction : public SensorConverter {
SensorResult convert(float raw) const {
return raw;
}
};
static IdentityFunction func;
static FunctionalSensor auxSensors[] = {
{ SensorType::Aux1, MS2NT(50) },
@ -32,7 +26,7 @@ void initAuxSensors(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
}
auto& sensor = auxSensors[i];
sensor.setFunction(func);
sensor.setFunction(identityFunction);
sensor.Register();
AdcSubscription::SubscribeSensor(sensor, channel, 10);

View File

@ -1,21 +1,11 @@
#include "pch.h"
#include "adc_subscription.h"
#include "linear_func.h"
#include "fallback_sensor.h"
#include "functional_sensor.h"
#include "function_pointer_sensor.h"
struct GetMapWrapper {
DECLARE_ENGINE_PTR;
float getMap() {
return ::getMap(PASS_ENGINE_PARAMETER_SIGNATURE);
}
};
static GetMapWrapper mapWrapper;
static FunctionPointerSensor mapSensor(SensorType::Map,
[]() {
return mapWrapper.getMap();
});
#include "identity_func.h"
struct GetBaroWrapper {
DECLARE_ENGINE_PTR;
@ -32,13 +22,120 @@ static FunctionPointerSensor baroSensor(SensorType::BarometricPressure,
return baroWrapper.getBaro();
});
// This converter is shared between both fast and slow: the only difference is
// how the *voltage* is determined, not how its converted to a pressure.
static LinearFunc mapConverter;
static FunctionalSensor slowMapSensor(SensorType::MapSlow, MS2NT(50));
// lowest reasonable idle is maybe 600 rpm
// one sample per cycle (1 cylinder, or "sample one cyl" mode) gives a period of 100ms
// add some margin -> 200ms timeout for fast MAP sampling
static FunctionalSensor fastMapSensor(SensorType::MapFast, MS2NT(200));
// This is called from the fast ADC completion callback
void onMapAveraged(float mapKpa, efitick_t nowNt) {
// This sensor uses identity function, so it's kPa in, kPa out
fastMapSensor.postRawValue(mapKpa, nowNt);
}
SensorResult convertMap(float volts) {
return mapConverter.convert(volts);
}
// Combine MAP sensors: prefer fast sensor, but use slow if fast is unavailable.
static FallbackSensor mapCombiner(SensorType::Map, SensorType::MapFast, SensorType::MapSlow);
// helper struct for the local getMapCfg function
struct MapCfg {
float v1, v2;
float map1, map2;
};
static MapCfg getMapCfg(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
auto sensorType = engineConfiguration->map.sensor.type;
switch (sensorType) {
case MT_DENSO183:
return {0, -6.64, 5, 182.78};
case MT_MPX4250:
return {0, 8, 5, 260};
case MT_MPX4100:
return {0.3, 20, 4.9, 105};
case MT_MPX4250A:
return {0.25, 20, 4.875, 250};
case MT_HONDA3BAR:
return {0.5, 91.422, 3.0, 0};
case MT_DODGE_NEON_2003:
return {0.4, 15.34, 4.5, 100};
case MT_SUBY_DENSO:
return {0, 0, 5, 200};
case MT_GM_3_BAR:
return {0.631, 40, 4.914, 304};
case MT_GM_2_BAR:
return {0, 8.8, 5, 208};
case MT_GM_1_BAR:
return {0, 10, 5, 105};
case MT_TOYOTA_89420_02010:
return {3.7 - 2, 33.32, 3.7, 100};
case MT_MAZDA_1_BAR:
return {0, 2.5, 5, 117};
case MT_BOSCH_2_5:
return {0.4 , 20 , 4.65, 250};
case MT_MPXH6400:
return {1, 90, 3, 250};
default:
firmwareError(CUSTOM_ERR_MAP_TYPE, "Unknown MAP type: decoder %d", sensorType);
// falls through to custom
return {};
case MT_CUSTOM: {
auto& mapConfig = engineConfiguration->map.sensor;
return {
engineConfiguration->mapLowValueVoltage,
mapConfig.lowValue,
engineConfiguration->mapHighValueVoltage,
mapConfig.highValue
};
}}
}
void configureMapFunction(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
auto cfg = getMapCfg(PASS_CONFIG_PARAMETER_SIGNATURE);
mapConverter.configure(
cfg.v1,
cfg.map1,
cfg.v2,
cfg.map2,
engineConfiguration->mapErrorDetectionTooLow,
engineConfiguration->mapErrorDetectionTooHigh
);
}
void initMap(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
INJECT_ENGINE_REFERENCE(&mapWrapper);
INJECT_ENGINE_REFERENCE(&baroWrapper);
mapSensor.Register();
auto mapChannel = engineConfiguration->map.sensor.hwChannel;
if (isAdcChannelValid(mapChannel)) {
// Set up the conversion function
configureMapFunction(PASS_CONFIG_PARAMETER_SIGNATURE);
slowMapSensor.setFunction(mapConverter);
fastMapSensor.setFunction(identityFunction);
slowMapSensor.Register();
fastMapSensor.Register();
mapCombiner.Register();
// Configure slow MAP as a normal analog sensor
AdcSubscription::SubscribeSensor(slowMapSensor, mapChannel, 100);
}
// Only register if configured
if (isAdcChannelValid(engineConfiguration->baroSensor.hwChannel)) {
baroSensor.Register();
}
}
void deinitMap() {
AdcSubscription::UnsubscribeSensor(slowMapSensor);
}

View File

@ -33,7 +33,6 @@ void deInitIfValid(const char* msg, adc_channel_e channel) {
static void initOldAnalogInputs(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
initIfValid("AFR", engineConfiguration->afr.hwChannel);
initIfValid("MAP", engineConfiguration->map.sensor.hwChannel);
initIfValid("Baro", engineConfiguration->baroSensor.hwChannel);
initIfValid("AUXF#1", engineConfiguration->auxFastSensor1_adcChannel);
initIfValid("CJ125 UR", engineConfiguration->cj125ur);
@ -42,7 +41,6 @@ static void initOldAnalogInputs(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
static void deInitOldAnalogInputs(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
deInitIfValid("AFR", activeConfiguration.afr.hwChannel);
deInitIfValid("MAP", activeConfiguration.map.sensor.hwChannel);
deInitIfValid("Baro", activeConfiguration.baroSensor.hwChannel);
deInitIfValid("AUXF#1", activeConfiguration.auxFastSensor1_adcChannel);
deInitIfValid("CJ125 UR", activeConfiguration.cj125ur);
@ -87,6 +85,7 @@ void stopSensors(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
deInitFlexSensor();
deInitVehicleSpeedSensor();
deinitTurbochargerSpeedSensor();
deinitMap();
}
void reconfigureSensors(DECLARE_ENGINE_PARAMETER_SIGNATURE) {

View File

@ -206,7 +206,7 @@ public class CommonFunctionalTest extends RusefiTestBase {
ecu.setEngineType(ET_DODGE_NEON_2003_CRANK);
ecu.sendCommand("set wwaeTau 0");
ecu.sendCommand("set wwaeBeta 0");
ecu.sendCommand("set mock_map_voltage 1");
ecu.sendCommand("set_sensor_mock 4 69.12"); // MAP
ecu.sendCommand("set_sensor_mock 27 12");
ecu.sendCommand("disable cylinder_cleanup");
EngineChart chart;
@ -322,7 +322,7 @@ public class CommonFunctionalTest extends RusefiTestBase {
assertWaveNotNull(msg, chart, EngineChart.SPARK_3);
// switching to Speed Density
ecu.sendCommand("set mock_map_voltage 1");
ecu.sendCommand("set_sensor_mock 4 69.12"); // MAP
sendComplexCommand("set algorithm 3");
ecu.changeRpm(2600);
ecu.changeRpm(2000);
@ -351,8 +351,8 @@ public class CommonFunctionalTest extends RusefiTestBase {
public void testFordAspire() {
ecu.setEngineType(ET_FORD_ASPIRE);
ecu.sendCommand("disable cylinder_cleanup");
ecu.sendCommand("set mock_map_voltage 1");
ecu.sendCommand("set_sensor_mock 27 12");
ecu.sendCommand("set_sensor_mock 4 69.12"); // MAP
ecu.sendCommand("set_sensor_mock 27 12"); // vbatt
String msg;
EngineChart chart;
// todo: interesting changeRpm(100);

View File

@ -226,6 +226,13 @@ TEST(SensorInit, Map) {
auto s = Sensor::getSensorOfType(SensorType::Map);
ASSERT_NE(nullptr, s);
engine->mockMapValue = 55;
EXPECT_FLOAT_EQ(55.0f, Sensor::get(SensorType::Map).value_or(0));
Sensor::setMockValue(SensorType::MapFast, 25);
Sensor::setMockValue(SensorType::MapSlow, 75);
// Should prefer fast MAP
EXPECT_FLOAT_EQ(25, Sensor::get(SensorType::Map).value_or(0));
// But when that fails, should return slow MAP
Sensor::resetMockValue(SensorType::MapFast);
EXPECT_FLOAT_EQ(75, Sensor::get(SensorType::Map).value_or(0));
}