Fix filter cutoff frequency limits
[betaflight.git] / src / main / sensors / rangefinder.c
blob6a7af62d4490453d0f0a4cc75444f11465d60064
1 /*
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)
8 * any later version.
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/>.
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <string.h>
24 #include <math.h>
26 #include "platform.h"
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"
38 #include "pg/pg.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"
49 #include "fc/tasks.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),
85 #endif
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));
104 #endif
105 break;
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));
113 #endif
114 break;
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));
122 #endif
123 break;
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));
131 #endif
132 break;
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));
140 #endif
141 break;
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));
149 #endif
150 break;
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));
158 #endif
159 break;
161 case RANGEFINDER_NONE:
162 rangefinderHardware = RANGEFINDER_NONE;
163 break;
166 if (rangefinderHardware == RANGEFINDER_NONE) {
167 sensorsClear(SENSOR_RANGEFINDER);
168 return false;
171 detectedSensors[SENSOR_INDEX_RANGEFINDER] = rangefinderHardware;
172 sensorsSet(SENSOR_RANGEFINDER);
173 return true;
176 void rangefinderResetDynamicThreshold(void)
178 rangefinder.snrThresholdReached = false;
179 rangefinder.dynamicDistanceThreshold = 0;
182 bool rangefinderInit(void)
184 if (!rangefinderDetect(&rangefinder.dev, rangefinderConfig()->rangefinder_hardware)) {
185 return false;
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();
193 rangefinder.snr = 0;
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
203 return true;
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;
215 ++filterSampleIndex;
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);
234 ++snrSampleIndex;
235 if (snrSampleIndex == SNR_SAMPLES) {
236 snrSampleIndex = 0;
237 snrReady = true;
240 previousReading = newReading;
242 if (snrReady) {
244 for (uint8_t i = 0; i < SNR_SAMPLES; i++) {
245 pseudoSnr += snrSamples[i];
248 return constrain(pseudoSnr, 0, 32000);
249 } else {
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
275 if (
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);
287 } else {
288 return true;
291 } else {
292 return false;
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) {
307 return false;
310 if (distance >= 0) {
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;
318 else {
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());
345 else {
346 // Bad configuration
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;
358 } else {
359 rangefinder.calculatedAltitude = rangefinder.rawAltitude * cosTiltAngle;
362 DEBUG_SET(DEBUG_RANGEFINDER, 1, rangefinder.rawAltitude);
363 DEBUG_SET(DEBUG_RANGEFINDER, 2, rangefinder.calculatedAltitude);
365 return true;
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;
385 #endif