Merge branch 'master' into abo_stats_pages_auto_swap
[inav.git] / src / main / sensors / opflow.c
blob9c1b6f265ef7dbf7b0af65318f1388a010ac2c14
1 /*
2 * This file is part of INAV Project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
6 * You can obtain one at http://mozilla.org/MPL/2.0/.
8 * Alternatively, the contents of this file may be used under the terms
9 * of the GNU General Public License Version 3, as described below:
11 * This file is free software: you may copy, redistribute and/or modify
12 * it under the terms of the GNU General Public License as published by the
13 * Free Software Foundation, either version 3 of the License, or (at your
14 * option) any later version.
16 * This file is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
19 * Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see http://www.gnu.org/licenses/.
25 #include <stdbool.h>
26 #include <stdint.h>
27 #include <string.h>
28 #include <math.h>
30 #include <platform.h>
32 #include "build/build_config.h"
34 #include "common/axis.h"
35 #include "common/maths.h"
36 #include "common/utils.h"
37 #include "common/time.h"
39 #include "config/feature.h"
40 #include "config/parameter_group.h"
41 #include "config/parameter_group_ids.h"
43 #include "drivers/io.h"
44 #include "drivers/light_led.h"
45 #include "drivers/time.h"
47 #include "drivers/opflow/opflow.h"
48 #include "drivers/opflow/opflow_fake.h"
49 #include "drivers/opflow/opflow_virtual.h"
51 #include "fc/config.h"
52 #include "fc/runtime_config.h"
53 #include "fc/settings.h"
55 #include "sensors/boardalignment.h"
56 #include "sensors/gyro.h"
57 #include "sensors/sensors.h"
58 #include "sensors/opflow.h"
60 #include "scheduler/scheduler.h"
62 #include "io/opflow.h"
64 #include "build/debug.h"
66 opflow_t opflow;
68 #ifdef USE_OPFLOW
69 static bool opflowIsCalibrating = false;
70 static timeMs_t opflowCalibrationStartedAt;
71 static float opflowCalibrationBodyAcc;
72 static float opflowCalibrationFlowAcc;
74 #define OPFLOW_SQUAL_THRESHOLD_HIGH 35 // TBD
75 #define OPFLOW_SQUAL_THRESHOLD_LOW 10 // TBD
76 #define OPFLOW_UPDATE_TIMEOUT_US 200000 // At least 5Hz updates required
77 #define OPFLOW_CALIBRATE_TIME_MS 30000 // 30 second calibration time
79 PG_REGISTER_WITH_RESET_TEMPLATE(opticalFlowConfig_t, opticalFlowConfig, PG_OPFLOW_CONFIG, 2);
81 PG_RESET_TEMPLATE(opticalFlowConfig_t, opticalFlowConfig,
82 .opflow_hardware = SETTING_OPFLOW_HARDWARE_DEFAULT,
83 .opflow_align = SETTING_ALIGN_OPFLOW_DEFAULT,
84 .opflow_scale = SETTING_OPFLOW_SCALE_DEFAULT,
87 static bool opflowDetect(opflowDev_t * dev, uint8_t opflowHardwareToUse)
89 opticalFlowSensor_e opflowHardware = OPFLOW_NONE;
90 requestedSensors[SENSOR_INDEX_OPFLOW] = opflowHardwareToUse;
92 switch (opflowHardwareToUse) {
93 case OPFLOW_FAKE:
94 #if defined(USE_OPFLOW_FAKE)
95 if (fakeOpflowDetect(dev)) {
96 opflowHardware = OPFLOW_FAKE;
98 #endif
99 break;
101 case OPFLOW_CXOF:
102 #if defined(USE_OPFLOW_CXOF)
103 if (virtualOpflowDetect(dev, &opflowCxofVtable)) {
104 opflowHardware = OPFLOW_CXOF;
106 #endif
107 break;
109 case OPFLOW_MSP:
110 #if defined(USE_OPFLOW_MSP)
111 if (virtualOpflowDetect(dev, &opflowMSPVtable)) {
112 opflowHardware = OPFLOW_MSP;
114 #endif
115 break;
117 case OPFLOW_NONE:
118 opflowHardware = OPFLOW_NONE;
119 break;
122 if (opflowHardware == OPFLOW_NONE) {
123 sensorsClear(SENSOR_OPFLOW);
124 return false;
127 detectedSensors[SENSOR_INDEX_OPFLOW] = opflowHardware;
128 sensorsSet(SENSOR_OPFLOW);
129 return true;
132 static void opflowZeroBodyGyroAcc(void)
134 opflow.gyroBodyRateTimeUs = 0;
135 opflow.gyroBodyRateAcc[X] = 0;
136 opflow.gyroBodyRateAcc[Y] = 0;
139 bool opflowInit(void)
141 if (!opflowDetect(&opflow.dev, opticalFlowConfig()->opflow_hardware)) {
142 return false;
145 if (!opflow.dev.initFn(&opflow.dev)) {
146 sensorsClear(SENSOR_OPFLOW);
147 return false;
150 opflowZeroBodyGyroAcc();
152 return true;
155 void opflowStartCalibration(void)
157 opflowCalibrationStartedAt = millis();
158 opflowIsCalibrating = true;
159 opflowCalibrationBodyAcc = 0;
160 opflowCalibrationFlowAcc = 0;
164 * This is called periodically by the scheduler
166 void opflowUpdate(timeUs_t currentTimeUs)
168 if (!opflow.dev.updateFn)
169 return;
171 if (opflow.dev.updateFn(&opflow.dev)) {
172 // Indicate valid update
173 opflow.isHwHealty = true;
174 opflow.lastValidUpdate = currentTimeUs;
175 opflow.rawQuality = opflow.dev.rawData.quality;
177 // Handle state switching
178 switch (opflow.flowQuality) {
179 case OPFLOW_QUALITY_INVALID:
180 if (opflow.dev.rawData.quality >= OPFLOW_SQUAL_THRESHOLD_HIGH) {
181 opflow.flowQuality = OPFLOW_QUALITY_VALID;
183 break;
185 case OPFLOW_QUALITY_VALID:
186 if (opflow.dev.rawData.quality <= OPFLOW_SQUAL_THRESHOLD_LOW) {
187 opflow.flowQuality = OPFLOW_QUALITY_INVALID;
189 break;
192 // Opflow updated. Assume zero valus unless further processing sets otherwise
193 opflow.flowRate[X] = 0;
194 opflow.flowRate[Y] = 0;
195 opflow.bodyRate[X] = 0;
196 opflow.bodyRate[Y] = 0;
198 // In the following code we operate deg/s and do conversion to rad/s in the last step
199 // Calculate body rates
200 if (opflow.gyroBodyRateTimeUs > 0) {
201 opflow.bodyRate[X] = opflow.gyroBodyRateAcc[X] / opflow.gyroBodyRateTimeUs;
202 opflow.bodyRate[Y] = opflow.gyroBodyRateAcc[Y] / opflow.gyroBodyRateTimeUs;
205 // If quality of the flow from the sensor is good - process further
206 if (opflow.flowQuality == OPFLOW_QUALITY_VALID) {
207 const float integralToRateScaler = (opticalFlowConfig()->opflow_scale > 0.01f) ? (1.0e6 / opflow.dev.rawData.deltaTime) / (float)opticalFlowConfig()->opflow_scale : 0.0f;
209 // Apply sensor alignment
210 applySensorAlignment(opflow.dev.rawData.flowRateRaw, opflow.dev.rawData.flowRateRaw, opticalFlowConfig()->opflow_align);
212 // Calculate flow rate and accumulated body rate
213 opflow.flowRate[X] = opflow.dev.rawData.flowRateRaw[X] * integralToRateScaler;
214 opflow.flowRate[Y] = opflow.dev.rawData.flowRateRaw[Y] * integralToRateScaler;
216 // Only update DEBUG_FLOW_RAW if flow is good
217 DEBUG_SET(DEBUG_FLOW_RAW, 0, (opflow.flowRate[X]));
218 DEBUG_SET(DEBUG_FLOW_RAW, 1, (opflow.flowRate[Y]));
219 DEBUG_SET(DEBUG_FLOW_RAW, 2, (opflow.bodyRate[X]));
220 DEBUG_SET(DEBUG_FLOW_RAW, 3, (opflow.bodyRate[Y]));
223 // Process calibration
224 if (opflowIsCalibrating) {
225 // Blink LED
226 LED0_TOGGLE;
228 if ((millis() - opflowCalibrationStartedAt) > OPFLOW_CALIBRATE_TIME_MS) {
229 // Finish calibration if we accumulated more than 3600 deg of rotation over 30 seconds
230 if (opflowCalibrationBodyAcc > 3600.0f) {
231 opticalFlowConfigMutable()->opflow_scale = opflowCalibrationFlowAcc / opflowCalibrationBodyAcc;
232 saveConfigAndNotify();
235 opflowIsCalibrating = 0;
237 else if (opflow.flowQuality == OPFLOW_QUALITY_VALID) {
238 // Ongoing calibration - accumulate body and flow rotation magniture if opflow quality is good enough
239 const float invDt = 1.0e6 / opflow.dev.rawData.deltaTime;
240 opflowCalibrationBodyAcc += sqrtf(sq(opflow.bodyRate[X]) + sq(opflow.bodyRate[Y]));
241 opflowCalibrationFlowAcc += sqrtf(sq(opflow.dev.rawData.flowRateRaw[X]) + sq(opflow.dev.rawData.flowRateRaw[Y])) * invDt;
245 // Convert to radians so NAV doesn't have to do the conversion
246 opflow.bodyRate[X] = DEGREES_TO_RADIANS(opflow.bodyRate[X]);
247 opflow.bodyRate[Y] = DEGREES_TO_RADIANS(opflow.bodyRate[Y]);
248 opflow.flowRate[X] = DEGREES_TO_RADIANS(opflow.flowRate[X]);
249 opflow.flowRate[Y] = DEGREES_TO_RADIANS(opflow.flowRate[Y]);
251 // Zero out gyro accumulators to calculate rotation per flow update
252 opflowZeroBodyGyroAcc();
254 else {
255 // No new data available
256 if (opflow.isHwHealty && ((currentTimeUs - opflow.lastValidUpdate) > OPFLOW_UPDATE_TIMEOUT_US)) {
257 opflow.isHwHealty = false;
259 opflow.flowQuality = OPFLOW_QUALITY_INVALID;
260 opflow.rawQuality = 0;
262 opflow.flowRate[X] = 0;
263 opflow.flowRate[Y] = 0;
264 opflow.bodyRate[X] = 0;
265 opflow.bodyRate[Y] = 0;
267 opflowZeroBodyGyroAcc();
272 /* Run a simple gyro update integrator to estimate average body rate between two optical flow updates */
273 void opflowGyroUpdateCallback(timeUs_t gyroUpdateDeltaUs)
275 if (!opflow.isHwHealty)
276 return;
278 for (int axis = 0; axis < 2; axis++) {
279 opflow.gyroBodyRateAcc[axis] += gyro.gyroADCf[axis] * gyroUpdateDeltaUs;
282 opflow.gyroBodyRateTimeUs += gyroUpdateDeltaUs;
285 bool opflowIsHealthy(void)
287 return opflow.isHwHealty;
289 #endif