2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
28 #ifdef USE_RANGEFINDER
30 #include "build/build_config.h"
31 #include "build/debug.h"
33 #include "common/maths.h"
34 #include "common/utils.h"
35 #include "common/time.h"
37 #include "config/feature.h"
39 #include "pg/pg_ids.h"
41 #include "drivers/io.h"
42 #include "drivers/time.h"
43 #include "drivers/rangefinder/rangefinder.h"
44 #include "drivers/rangefinder/rangefinder_hcsr04.h"
45 #include "drivers/rangefinder/rangefinder_lidartf.h"
47 #include "fc/config.h"
48 #include "fc/runtime_config.h"
51 #include "sensors/sensors.h"
52 #include "sensors/rangefinder.h"
53 #include "sensors/battery.h"
55 #include "scheduler/scheduler.h"
57 //#include "uav_interconnect/uav_interconnect.h"
59 // XXX Interface to CF/BF legacy(?) altitude estimation code.
60 // XXX Will be gone once iNav's estimator is ported.
61 int16_t rangefinderMaxRangeCm
;
62 int16_t rangefinderMaxAltWithTiltCm
;
63 int16_t rangefinderCfAltCm
; // Complimentary Filter altitude
65 rangefinder_t rangefinder
;
67 #define RANGEFINDER_HARDWARE_TIMEOUT_MS 500 // Accept 500ms of non-responsive sensor, report HW failure otherwise
69 #define RANGEFINDER_DYNAMIC_THRESHOLD 600 //Used to determine max. usable rangefinder disatance
70 #define RANGEFINDER_DYNAMIC_FACTOR 75
72 PG_REGISTER_WITH_RESET_TEMPLATE(rangefinderConfig_t
, rangefinderConfig
, PG_RANGEFINDER_CONFIG
, 0);
74 PG_RESET_TEMPLATE(rangefinderConfig_t
, rangefinderConfig
,
75 .rangefinder_hardware
= RANGEFINDER_NONE
,
78 #ifdef USE_RANGEFINDER_HCSR04
79 PG_REGISTER_WITH_RESET_TEMPLATE(sonarConfig_t
, sonarConfig
, PG_SONAR_CONFIG
, 1);
81 PG_RESET_TEMPLATE(sonarConfig_t
, sonarConfig
,
82 .triggerTag
= IO_TAG(RANGEFINDER_HCSR04_TRIGGER_PIN
),
83 .echoTag
= IO_TAG(RANGEFINDER_HCSR04_ECHO_PIN
),
88 * Detect which rangefinder is present
90 static bool rangefinderDetect(rangefinderDev_t
* dev
, uint8_t rangefinderHardwareToUse
)
92 rangefinderType_e rangefinderHardware
= RANGEFINDER_NONE
;
93 requestedSensors
[SENSOR_INDEX_RANGEFINDER
] = rangefinderHardwareToUse
;
95 switch (rangefinderHardwareToUse
) {
96 case RANGEFINDER_HCSR04
:
97 #ifdef USE_RANGEFINDER_HCSR04
99 if (hcsr04Detect(dev
, sonarConfig())) { // FIXME: Do actual detection if HC-SR04 is plugged in
100 rangefinderHardware
= RANGEFINDER_HCSR04
;
101 rescheduleTask(TASK_RANGEFINDER
, TASK_PERIOD_MS(RANGEFINDER_HCSR04_TASK_PERIOD_MS
));
107 case RANGEFINDER_SRF10
:
108 #ifdef USE_RANGEFINDER_SRF10
109 if (srf10Detect(dev
)) {
110 rangefinderHardware
= RANGEFINDER_SRF10
;
111 rescheduleTask(TASK_RANGEFINDER
, TASK_PERIOD_MS(RANGEFINDER_SRF10_TASK_PERIOD_MS
));
116 case RANGEFINDER_HCSR04I2C
:
117 #ifdef USE_RANGEFINDER_HCSR04_I2C
118 if (hcsr04i2c0Detect(dev
)) {
119 rangefinderHardware
= RANGEFINDER_HCSR04I2C
;
120 rescheduleTask(TASK_RANGEFINDER
, TASK_PERIOD_MS(RANGEFINDER_HCSR04_i2C_TASK_PERIOD_MS
));
125 case RANGEFINDER_VL53L0X
:
126 #if defined(USE_RANGEFINDER_VL53L0X)
127 if (vl53l0xDetect(dev
)) {
128 rangefinderHardware
= RANGEFINDER_VL53L0X
;
129 rescheduleTask(TASK_RANGEFINDER
, TASK_PERIOD_MS(RANGEFINDER_VL53L0X_TASK_PERIOD_MS
));
134 case RANGEFINDER_UIB
:
135 #if defined(USE_RANGEFINDER_UIB)
136 if (uibRangefinderDetect(dev
)) {
137 rangefinderHardware
= RANGEFINDER_UIB
;
138 rescheduleTask(TASK_RANGEFINDER
, TASK_PERIOD_MS(RANGEFINDER_UIB_TASK_PERIOD_MS
));
143 case RANGEFINDER_TFMINI
:
144 #if defined(USE_RANGEFINDER_TF)
145 if (lidarTFminiDetect(dev
)) {
146 rangefinderHardware
= RANGEFINDER_TFMINI
;
147 rescheduleTask(TASK_RANGEFINDER
, TASK_PERIOD_MS(RANGEFINDER_TF_TASK_PERIOD_MS
));
152 case RANGEFINDER_TF02
:
153 #if defined(USE_RANGEFINDER_TF)
154 if (lidarTF02Detect(dev
)) {
155 rangefinderHardware
= RANGEFINDER_TF02
;
156 rescheduleTask(TASK_RANGEFINDER
, TASK_PERIOD_MS(RANGEFINDER_TF_TASK_PERIOD_MS
));
161 case RANGEFINDER_NONE
:
162 rangefinderHardware
= RANGEFINDER_NONE
;
166 if (rangefinderHardware
== RANGEFINDER_NONE
) {
167 sensorsClear(SENSOR_RANGEFINDER
);
171 detectedSensors
[SENSOR_INDEX_RANGEFINDER
] = rangefinderHardware
;
172 sensorsSet(SENSOR_RANGEFINDER
);
176 void rangefinderResetDynamicThreshold(void)
178 rangefinder
.snrThresholdReached
= false;
179 rangefinder
.dynamicDistanceThreshold
= 0;
182 bool rangefinderInit(void)
184 if (!rangefinderDetect(&rangefinder
.dev
, rangefinderConfig()->rangefinder_hardware
)) {
188 rangefinder
.dev
.init(&rangefinder
.dev
);
189 rangefinder
.rawAltitude
= RANGEFINDER_OUT_OF_RANGE
;
190 rangefinder
.calculatedAltitude
= RANGEFINDER_OUT_OF_RANGE
;
191 rangefinder
.maxTiltCos
= cos_approx(DECIDEGREES_TO_RADIANS(rangefinder
.dev
.detectionConeExtendedDeciDegrees
/ 2.0f
));
192 rangefinder
.lastValidResponseTimeMs
= millis();
195 rangefinderResetDynamicThreshold();
197 // XXX Interface to CF/BF legacy(?) altitude estimation code.
198 // XXX Will be gone once iNav's estimator is ported.
199 rangefinderMaxRangeCm
= rangefinder
.dev
.maxRangeCm
;
200 rangefinderMaxAltWithTiltCm
= rangefinderMaxRangeCm
* rangefinder
.maxTiltCos
;
201 rangefinderCfAltCm
= rangefinder
.dev
.maxRangeCm
/ 2 ; // Complimentary Filter altitude
206 static int32_t applyMedianFilter(int32_t newReading
)
208 #define DISTANCE_SAMPLES_MEDIAN 5
209 static int32_t filterSamples
[DISTANCE_SAMPLES_MEDIAN
];
210 static int filterSampleIndex
= 0;
211 static bool medianFilterReady
= false;
213 if (newReading
> RANGEFINDER_OUT_OF_RANGE
) {// only accept samples that are in range
214 filterSamples
[filterSampleIndex
] = newReading
;
216 if (filterSampleIndex
== DISTANCE_SAMPLES_MEDIAN
) {
217 filterSampleIndex
= 0;
218 medianFilterReady
= true;
221 return medianFilterReady
? quickMedianFilter5(filterSamples
) : newReading
;
224 static int16_t computePseudoSnr(int32_t newReading
) {
225 #define SNR_SAMPLES 5
226 static int16_t snrSamples
[SNR_SAMPLES
];
227 static uint8_t snrSampleIndex
= 0;
228 static int32_t previousReading
= RANGEFINDER_OUT_OF_RANGE
;
229 static bool snrReady
= false;
230 int16_t pseudoSnr
= 0;
232 const int delta
= newReading
- previousReading
;
233 snrSamples
[snrSampleIndex
] = constrain(delta
* delta
/ 10, 0, 6400);
235 if (snrSampleIndex
== SNR_SAMPLES
) {
240 previousReading
= newReading
;
244 for (uint8_t i
= 0; i
< SNR_SAMPLES
; i
++) {
245 pseudoSnr
+= snrSamples
[i
];
248 return constrain(pseudoSnr
, 0, 32000);
250 return RANGEFINDER_OUT_OF_RANGE
;
255 * This is called periodically by the scheduler
257 // XXX Returns timeDelta_t for iNav for pseudo-RT scheduling.
258 void rangefinderUpdate(timeUs_t currentTimeUs
)
260 UNUSED(currentTimeUs
);
262 if (rangefinder
.dev
.update
) {
263 rangefinder
.dev
.update(&rangefinder
.dev
);
266 // return rangefinder.dev.delayMs * 1000; // to microseconds XXX iNav only
269 bool isSurfaceAltitudeValid() {
272 * Preconditions: raw and calculated altidude > 0
273 * SNR lower than threshold
276 rangefinder
.calculatedAltitude
> 0 &&
277 rangefinder
.rawAltitude
> 0 &&
278 rangefinder
.snr
< RANGEFINDER_DYNAMIC_THRESHOLD
282 * When critical altitude was determined, distance reported by rangefinder
283 * has to be lower than it to assume healthy readout
285 if (rangefinder
.snrThresholdReached
) {
286 return (rangefinder
.rawAltitude
< rangefinder
.dynamicDistanceThreshold
);
298 * Get the last distance measured by the sonar in centimeters. When the ground is too far away, RANGEFINDER_OUT_OF_RANGE is returned.
300 bool rangefinderProcess(float cosTiltAngle
)
302 if (rangefinder
.dev
.read
) {
303 const int32_t distance
= rangefinder
.dev
.read(&rangefinder
.dev
);
305 // If driver reported no new measurement - don't do anything
306 if (distance
== RANGEFINDER_NO_NEW_DATA
) {
311 rangefinder
.lastValidResponseTimeMs
= millis();
312 rangefinder
.rawAltitude
= applyMedianFilter(distance
);
314 else if (distance
== RANGEFINDER_OUT_OF_RANGE
) {
315 rangefinder
.lastValidResponseTimeMs
= millis();
316 rangefinder
.rawAltitude
= RANGEFINDER_OUT_OF_RANGE
;
319 // Invalid response / hardware failure
320 rangefinder
.rawAltitude
= RANGEFINDER_HARDWARE_FAILURE
;
323 rangefinder
.snr
= computePseudoSnr(distance
);
325 if (rangefinder
.snrThresholdReached
== false && rangefinder
.rawAltitude
> 0) {
327 if (rangefinder
.snr
< RANGEFINDER_DYNAMIC_THRESHOLD
&& rangefinder
.dynamicDistanceThreshold
< rangefinder
.rawAltitude
) {
328 rangefinder
.dynamicDistanceThreshold
= rangefinder
.rawAltitude
* RANGEFINDER_DYNAMIC_FACTOR
/ 100;
331 if (rangefinder
.snr
>= RANGEFINDER_DYNAMIC_THRESHOLD
) {
332 rangefinder
.snrThresholdReached
= true;
337 DEBUG_SET(DEBUG_RANGEFINDER
, 3, rangefinder
.snr
);
339 DEBUG_SET(DEBUG_RANGEFINDER_QUALITY
, 0, rangefinder
.rawAltitude
);
340 DEBUG_SET(DEBUG_RANGEFINDER_QUALITY
, 1, rangefinder
.snrThresholdReached
);
341 DEBUG_SET(DEBUG_RANGEFINDER_QUALITY
, 2, rangefinder
.dynamicDistanceThreshold
);
342 DEBUG_SET(DEBUG_RANGEFINDER_QUALITY
, 3, isSurfaceAltitudeValid());
347 rangefinder
.rawAltitude
= RANGEFINDER_OUT_OF_RANGE
;
351 * Apply tilt correction to the given raw sonar reading in order to compensate for the tilt of the craft when estimating
352 * the altitude. Returns the computed altitude in centimeters.
354 * When the ground is too far away or the tilt is too large, RANGEFINDER_OUT_OF_RANGE is returned.
356 if (cosTiltAngle
< rangefinder
.maxTiltCos
|| rangefinder
.rawAltitude
< 0) {
357 rangefinder
.calculatedAltitude
= RANGEFINDER_OUT_OF_RANGE
;
359 rangefinder
.calculatedAltitude
= rangefinder
.rawAltitude
* cosTiltAngle
;
362 DEBUG_SET(DEBUG_RANGEFINDER
, 1, rangefinder
.rawAltitude
);
363 DEBUG_SET(DEBUG_RANGEFINDER
, 2, rangefinder
.calculatedAltitude
);
369 * Get the latest altitude that was computed, or RANGEFINDER_OUT_OF_RANGE if sonarCalculateAltitude
370 * has never been called.
372 int32_t rangefinderGetLatestAltitude(void)
374 return rangefinder
.calculatedAltitude
;
377 int32_t rangefinderGetLatestRawAltitude(void) {
378 return rangefinder
.rawAltitude
;
381 bool rangefinderIsHealthy(void)
383 return (millis() - rangefinder
.lastValidResponseTimeMs
) < RANGEFINDER_HARDWARE_TIMEOUT_MS
;