rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
map_averaging.cpp
Go to the documentation of this file.
1/**
2 * @file map_averaging.cpp
3 *
4 * In order to have best MAP estimate possible, we real MAP value at a relatively high frequency
5 * and average the value within a specified angle position window for each cylinder
6 *
7 * @date Dec 11, 2013
8 * @author Andrey Belomutskiy, (c) 2012-2020
9 *
10 * This file is part of rusEfi - see http://rusefi.com
11 *
12 * rusEfi is free software; you can redistribute it and/or modify it under the terms of
13 * the GNU General Public License as published by the Free Software Foundation; either
14 * version 3 of the License, or (at your option) any later version.
15 *
16 * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
17 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along with this program.
21 * If not, see <http://www.gnu.org/licenses/>.
22 */
23
24#include "pch.h"
25#include "exp_average.h"
26
27
28#if EFI_MAP_AVERAGING && defined (MODULE_MAP_AVERAGING)
29#if !EFI_SHAFT_POSITION_INPUT
30 fail("EFI_SHAFT_POSITION_INPUT required to have EFI_EMULATE_POSITION_SENSORS")
31#endif // EFI_SHAFT_POSITION_INPUT
32
33#include "map_averaging.h"
34#include "trigger_central.h"
35
36
37/**
38 * this instance does not have a real physical pin - it's only used for engine sniffer
39 *
40 * todo: we can kind of add real physical pin just for a very narrow case of troubleshooting but only if we ever need it :)
41 */
42static NamedOutputPin mapAveragingPin("map");
43
44// allow smoothing up to number of cylinders
45#define MAX_MAP_BUFFER_LENGTH (MAX_CYLINDER_COUNT)
46// in MAP units, not voltage!
47static float averagedMapRunningBuffer[MAX_MAP_BUFFER_LENGTH];
48static int mapMinBufferLength = 0;
49static int averagedMapBufIdx = 0;
50
51
52static void endAveraging(MapAverager* arg);
53
54static size_t currentMapAverager = 0;
55
58 // Zero duration means the engine wasn't spinning or something, abort
59 return;
60 }
61 efiAssertVoid(ObdCode::CUSTOM_ERR_6649, hasLotsOfRemainingStack(), "lowstck#9");
62
63 // TODO: set currentMapAverager based on cylinder bank
64 auto& averager = getMapAvg(currentMapAverager);
65 averager.start(s->cylinderNumber);
66
67 mapAveragingPin.setHigh();
69
71 action_s::make<endAveraging>(&averager));
72}
73
74void MapAverager::start(uint8_t cylinderNumber) {
75 chibios_rt::CriticalSectionLocker csl;
76
77 m_counter = 0;
78 m_sum = 0;
79 m_isAveraging = true;
80 m_cylinderNumber = cylinderNumber;
81}
82
84 auto result = m_function ? m_function->convert(volts) : unexpected;
85
86 if (m_isAveraging && result) {
87 chibios_rt::CriticalSectionLocker csl;
88
89 m_counter++;
90 m_sum += result.Value;
91 }
92
93 return result;
94}
95
97
98// huh? why is this killing unit tests _linking_ only on WINDOWS?! PUBLIC_API_WEAK
103
105 chibios_rt::CriticalSectionLocker csl;
106
107 m_isAveraging = false;
108
109 if (m_counter > 0) {
110 float averageMap = m_sum / m_counter;
112
113 // TODO: this should be per-sensor, not one for all MAP sensors
115 // increment circular running buffer index
117 // find min. value (only works for pressure values, not raw voltages!)
118 float minPressure = averagedMapRunningBuffer[0];
119 for (int i = 1; i < mapMinBufferLength; i++) {
120 if (averagedMapRunningBuffer[i] < minPressure)
121 minPressure = averagedMapRunningBuffer[i];
122 }
123
126 } else {
127#if EFI_PROD_CODE
128 warning(ObdCode::CUSTOM_UNEXPECTED_MAP_VALUE, "No MAP values to average");
129#endif
130 }
131}
132
133#if HAL_USE_ADC
134
135/**
136 * This method is invoked from ADC callback.
137 * @note This method is invoked OFTEN, this method is a potential bottleneck - the implementation should be
138 * as fast as possible
139 */
140void mapAveragingAdcCallback(float instantVoltage) {
141 efiAssertVoid(ObdCode::CUSTOM_ERR_6650, hasLotsOfRemainingStack(), "lowstck#9a");
142
143 SensorResult mapResult = getMapAvg(currentMapAverager).submit(instantVoltage);
144
145 if (!mapResult) {
146 // hopefully this warning is not too much CPU consumption for fast ADC callback
147 warning(ObdCode::CUSTOM_INSTANT_MAP_DECODING, "Invalid MAP at %f", instantVoltage);
149 } else {
151 }
152
153#if EFI_TUNER_STUDIO
154 float instantMap = mapResult.value_or(0);
156#endif // EFI_TUNER_STUDIO
157}
158#endif
159
160static void endAveraging(MapAverager* arg) {
161 arg->stop();
162
164 mapAveragingPin.setLow();
165}
166
168 // check range
169 mapMinBufferLength = maxI(minI(engineConfiguration->mapMinBufferLength, MAX_MAP_BUFFER_LENGTH), 1);
170 // reset index
172 // fill with maximum values
173 for (int i = 0; i < mapMinBufferLength; i++) {
174 averagedMapRunningBuffer[i] = FLT_MAX;
175 }
176}
177
181 angle_t start = interpolate2d(rpm, c->samplingAngleBins, c->samplingAngle);
182 efiAssertVoid(ObdCode::CUSTOM_ERR_MAP_START_ASSERT, !std::isnan(start), "start");
183
184 for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) {
185 float cylinderStart = start + engine->cylinders[i].getAngleOffset();
186 wrapAngle(cylinderStart, "cylinderStart", ObdCode::CUSTOM_ERR_6562);
187 engine->engineState.mapAveragingStart[i] = cylinderStart;
188 }
189
190 angle_t duration = interpolate2d(rpm, c->samplingWindowBins, c->samplingWindow);
191 assertAngleRange(duration, "samplingDuration", ObdCode::CUSTOM_ERR_6563);
192
193 // Clamp the duration to slightly less than one cylinder period
195 engine->engineState.mapAveragingDuration = clampF(10, duration, cylinderPeriod - 10);
196}
197
198// Callback to schedule the start of map averaging for each cylinder
200 efitick_t edgeTimestamp,
201 float currentPhase,
202 float nextPhase) {
204 return;
205 }
206
208
210
211 for (int i = 0; i < samplingCount; i++) {
212 angle_t samplingStart = engine->engineState.mapAveragingStart[i];
213
214 if (!isPhaseInRange(samplingStart, currentPhase, nextPhase)) {
215 continue;
216 }
217
218 float angleOffset = samplingStart - currentPhase;
219 if (angleOffset < 0) {
220 angleOffset += engine->engineState.engineCycle;
221 }
222
223 auto& s = samplers[i];
224
225 scheduleByAngle(&s.timer, edgeTimestamp, angleOffset, action_s::make<startAveraging>(&s));
226 }
227}
228
230 if (!previousConfig || engineConfiguration->mapMinBufferLength != previousConfig->mapMinBufferLength) {
232 }
233}
234
236 for (size_t cylinderIndex = 0; cylinderIndex < MAX_CYLINDER_COUNT; cylinderIndex++) {
237 samplers[cylinderIndex].cylinderNumber = cylinderIndex;
238 }
239
241 efiPrintf("initMapAveraging...");
243 } else {
244 efiPrintf("Running without MapAveraging...");
245 }
246}
247
248#else
252void MapAveragingModule::onEnginePhase(float, efitick_t, float, float){}
253#endif /* EFI_MAP_AVERAGING */
EngineState engineState
Definition engine.h:344
TunerStudioOutputChannels outputChannels
Definition engine.h:109
OneCylinder cylinders[MAX_CYLINDER_COUNT]
Definition engine.h:291
angle_t mapAveragingStart[MAX_CYLINDER_COUNT]
angle_t engineCycle
angle_t mapAveragingDuration
void setSmoothingFactor(float p_smoothingFactor)
Definition exp_average.h:26
float initOrAverage(float value)
Definition exp_average.h:9
size_t m_lastCounter
void start(uint8_t cylinderNumber)
size_t m_counter
SensorResult submit(float sensorVolts)
SensorConverter * m_function
uint8_t m_cylinderNumber
void onEnginePhase(float, efitick_t edgeTimestamp, float currentPhase, float nextPhase) override
void onFastCallback() override
void onConfigurationChange(engine_configuration_s const *previousConfig) override
mapSampler samplers[MAX_CYLINDER_COUNT]
angle_t getAngleOffset() const
static float getOrZero(SensorType type)
Definition sensor.h:83
void setValidValue(float value, efitick_t timestamp)
bool isPhaseInRange(float test, float current, float next)
Definition efilib.cpp:176
efitick_t getTimeNowNt()
Definition efitime.cpp:19
static EngineAccessor engine
Definition engine.h:413
static constexpr engine_configuration_s * engineConfiguration
bool warning(ObdCode code, const char *fmt,...)
void startAveraging(mapSampler *s)
static float averagedMapRunningBuffer[MAX_MAP_BUFFER_LENGTH]
void mapAveragingAdcCallback(float instantVoltage)
static ExpAverage expAverage
static void endAveraging(MapAverager *arg)
static void applyMapMinBufferLength()
static size_t currentMapAverager
static int averagedMapBufIdx
static int mapMinBufferLength
float filterMapValue(float value)
fail("EFI_SHAFT_POSITION_INPUT required to have EFI_EMULATE_POSITION_SENSORS") static NamedOutputPin mapAveragingPin("map")
float filterMapValue(float value)
MapAverager & getMapAvg(size_t idx)
Definition init_map.cpp:28
@ CUSTOM_ERR_6650
@ CUSTOM_UNEXPECTED_MAP_VALUE
@ CUSTOM_INSTANT_MAP_DECODING
@ CUSTOM_ERR_6649
@ CUSTOM_ERR_6562
@ CUSTOM_ERR_MAP_START_ASSERT
@ CUSTOM_ERR_6563
@ MapAveragingTriggerCallback
efitick_t scheduleByAngle(scheduling_s *timer, efitick_t nowNt, angle_t angle, action_s const &action)
expected< float > SensorResult
Definition sensor.h:46
virtual SensorResult convert(float raw) const =0
scheduling_s timer
uint8_t cylinderNumber
uint8_t mapPerCylinder[MAX_CYLINDER_COUNT]
scaled_channel< uint16_t, 30, 1 > instantMAPValue
void wrapAngle(angle_t &angle, const char *msg, ObdCode code)