2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
24 #include "common/log.h"
25 #include "common/maths.h"
26 #include "common/time.h"
27 #include "common/utils.h"
29 #include "config/parameter_group.h"
30 #include "config/parameter_group_ids.h"
32 #include "drivers/pitotmeter/pitotmeter.h"
33 #include "drivers/pitotmeter/pitotmeter_ms4525.h"
34 #include "drivers/pitotmeter/pitotmeter_dlvr_l10d.h"
35 #include "drivers/pitotmeter/pitotmeter_adc.h"
36 #include "drivers/pitotmeter/pitotmeter_msp.h"
37 #include "drivers/pitotmeter/pitotmeter_virtual.h"
38 #include "drivers/pitotmeter/pitotmeter_fake.h"
39 #include "drivers/time.h"
41 #include "fc/config.h"
42 #include "fc/runtime_config.h"
43 #include "fc/settings.h"
45 #include "scheduler/protothreads.h"
47 #include "sensors/pitotmeter.h"
48 #include "sensors/barometer.h"
49 #include "sensors/sensors.h"
52 //#include "build/debug.h"
59 pitot_t pitot
= {.lastMeasurementUs
= 0, .lastSeenHealthyMs
= 0};
61 PG_REGISTER_WITH_RESET_TEMPLATE(pitotmeterConfig_t
, pitotmeterConfig
, PG_PITOTMETER_CONFIG
, 2);
63 #define PITOT_HARDWARE_TIMEOUT_MS 500 // Accept 500ms of non-responsive sensor, report HW failure otherwise
66 #define PITOT_HARDWARE_DEFAULT PITOT_AUTODETECT
68 #define PITOT_HARDWARE_DEFAULT PITOT_NONE
71 PG_RESET_TEMPLATE(pitotmeterConfig_t
, pitotmeterConfig
,
72 .pitot_hardware
= SETTING_PITOT_HARDWARE_DEFAULT
,
73 .pitot_lpf_milli_hz
= SETTING_PITOT_LPF_MILLI_HZ_DEFAULT
,
74 .pitot_scale
= SETTING_PITOT_SCALE_DEFAULT
77 bool pitotDetect(pitotDev_t
*dev
, uint8_t pitotHardwareToUse
)
79 pitotSensor_e pitotHardware
= PITOT_NONE
;
80 requestedSensors
[SENSOR_INDEX_PITOT
] = pitotHardwareToUse
;
82 switch (pitotHardwareToUse
) {
83 case PITOT_AUTODETECT
:
85 #ifdef USE_PITOT_MS4525
86 if (ms4525Detect(dev
)) {
87 pitotHardware
= PITOT_MS4525
;
91 /* If we are asked for a specific sensor - break out, otherwise - fall through and continue */
92 if (pitotHardwareToUse
!= PITOT_AUTODETECT
) {
99 // Skip autodetection for DLVR (it is indistinguishable from MS4525) and allow only manual config
100 if (pitotHardwareToUse
!= PITOT_AUTODETECT
&& dlvrDetect(dev
)) {
101 pitotHardware
= PITOT_DLVR
;
107 #if defined(USE_ADC) && defined(USE_PITOT_ADC)
108 if (adcPitotDetect(dev
)) {
109 pitotHardware
= PITOT_ADC
;
113 /* If we are asked for a specific sensor - break out, otherwise - fall through and continue */
114 if (pitotHardwareToUse
!= PITOT_AUTODETECT
) {
120 #if defined(USE_WIND_ESTIMATOR) && defined(USE_PITOT_VIRTUAL)
121 if ((pitotHardwareToUse
!= PITOT_AUTODETECT
) && virtualPitotDetect(dev
)) {
122 pitotHardware
= PITOT_VIRTUAL
;
126 /* If we are asked for a specific sensor - break out, otherwise - fall through and continue */
127 if (pitotHardwareToUse
!= PITOT_AUTODETECT
) {
134 // Skip autodetection for MSP baro, only allow manual config
135 if (pitotHardwareToUse
!= PITOT_AUTODETECT
&& mspPitotmeterDetect(dev
)) {
136 pitotHardware
= PITOT_MSP
;
140 /* If we are asked for a specific sensor - break out, otherwise - fall through and continue */
141 if (pitotHardwareToUse
!= PITOT_AUTODETECT
) {
147 #ifdef USE_PITOT_FAKE
148 if (fakePitotDetect(dev
)) {
149 pitotHardware
= PITOT_FAKE
;
153 /* If we are asked for a specific sensor - break out, otherwise - fall through and continue */
154 if (pitotHardwareToUse
!= PITOT_AUTODETECT
) {
160 pitotHardware
= PITOT_NONE
;
164 if (pitotHardware
== PITOT_NONE
) {
165 sensorsClear(SENSOR_PITOT
);
169 detectedSensors
[SENSOR_INDEX_PITOT
] = pitotHardware
;
170 sensorsSet(SENSOR_PITOT
);
176 if (!pitotDetect(&pitot
.dev
, pitotmeterConfig()->pitot_hardware
)) {
182 bool pitotIsCalibrationComplete(void)
184 return zeroCalibrationIsCompleteS(&pitot
.zeroCalibration
) && zeroCalibrationIsSuccessfulS(&pitot
.zeroCalibration
);
187 void pitotStartCalibration(void)
189 zeroCalibrationStartS(&pitot
.zeroCalibration
, CALIBRATING_PITOT_TIME_MS
, SSL_AIR_PRESSURE
* pitot
.dev
.calibThreshold
, false);
192 static void performPitotCalibrationCycle(void)
194 zeroCalibrationAddValueS(&pitot
.zeroCalibration
, pitot
.pressure
);
196 if (zeroCalibrationIsCompleteS(&pitot
.zeroCalibration
)) {
197 zeroCalibrationGetZeroS(&pitot
.zeroCalibration
, &pitot
.pressureZero
);
198 LOG_DEBUG(PITOT
, "Pitot calibration complete (%d)", (int)lrintf(pitot
.pressureZero
));
202 STATIC_PROTOTHREAD(pitotThread
)
204 ptBegin(pitotThread
);
206 static float pitotPressureTmp
;
207 static float pitotTemperatureTmp
;
208 timeUs_t currentTimeUs
;
211 pitot
.lastMeasurementUs
= micros();
213 pt1FilterInit(&pitot
.lpfState
, pitotmeterConfig()->pitot_lpf_milli_hz
/ 1000.0f
, 0.0f
);
217 while (SIMULATOR_HAS_OPTION(HITL_AIRSPEED
) && SIMULATOR_HAS_OPTION(HITL_PITOT_FAILURE
))
223 if ( pitot
.lastSeenHealthyMs
== 0 ) {
224 if (pitot
.dev
.start(&pitot
.dev
)) {
225 pitot
.lastSeenHealthyMs
= millis();
229 if ( (millis() - pitot
.lastSeenHealthyMs
) >= US2MS(pitot
.dev
.delay
)) {
230 if (pitot
.dev
.get(&pitot
.dev
)) // read current data
231 pitot
.lastSeenHealthyMs
= millis();
233 if (pitot
.dev
.start(&pitot
.dev
)) // init for next read
234 pitot
.lastSeenHealthyMs
= millis();
238 pitot
.dev
.calculate(&pitot
.dev
, &pitotPressureTmp
, &pitotTemperatureTmp
);
241 if (SIMULATOR_HAS_OPTION(HITL_AIRSPEED
)) {
242 pitotPressureTmp
= sq(simulatorData
.airSpeed
) * SSL_AIR_DENSITY
/ 20000.0f
+ SSL_AIR_PRESSURE
;
245 #if defined(USE_PITOT_FAKE)
246 if (pitotmeterConfig()->pitot_hardware
== PITOT_FAKE
) {
247 pitotPressureTmp
= sq(fakePitotGetAirspeed()) * SSL_AIR_DENSITY
/ 20000.0f
+ SSL_AIR_PRESSURE
;
253 if (pitotIsCalibrationComplete()) {
255 // https://en.wikipedia.org/wiki/Indicated_airspeed
256 // Indicated airspeed (IAS) is the airspeed read directly from the airspeed indicator on an aircraft, driven by the pitot-static system.
257 // The IAS is an important value for the pilot because it is the indicated speeds which are specified in the aircraft flight manual for
258 // such important performance values as the stall speed. A typical aircraft will always stall at the same indicated airspeed (for the current configuration)
259 // regardless of density, altitude or true airspeed.
261 // Therefore we shouldn't care about CAS/TAS and only calculate IAS since it's more indicative to the pilot and more useful in calculations
262 // It also allows us to use pitot_scale to calibrate the dynamic pressure sensor scale
264 // NOTE ::filter pressure - apply filter when NOT calibrating for zero !!!
265 currentTimeUs
= micros();
266 pitot
.pressure
= pt1FilterApply3(&pitot
.lpfState
, pitotPressureTmp
, US2S(currentTimeUs
- pitot
.lastMeasurementUs
));
267 pitot
.lastMeasurementUs
= currentTimeUs
;
269 pitot
.airSpeed
= pitotmeterConfig()->pitot_scale
* fast_fsqrtf(2.0f
* fabsf(pitot
.pressure
- pitot
.pressureZero
) / SSL_AIR_DENSITY
) * 100; // cm/s
270 pitot
.temperature
= pitotTemperatureTmp
; // Kelvin
273 pitot
.pressure
= pitotPressureTmp
;
274 performPitotCalibrationCycle();
275 pitot
.airSpeed
= 0.0f
;
278 #if defined(USE_PITOT_FAKE)
279 if (pitotmeterConfig()->pitot_hardware
== PITOT_FAKE
) {
280 pitot
.airSpeed
= fakePitotGetAirspeed();
284 if (SIMULATOR_HAS_OPTION(HITL_AIRSPEED
)) {
285 pitot
.airSpeed
= simulatorData
.airSpeed
;
293 void pitotUpdate(void)
298 float getAirspeedEstimate(void)
300 return pitot
.airSpeed
;
303 bool pitotIsHealthy(void)
305 return (millis() - pitot
.lastSeenHealthyMs
) < PITOT_HARDWARE_TIMEOUT_MS
;