Line data Source code
1 : /*
2 : This program is free software: you can redistribute it and/or modify
3 : it under the terms of the GNU General Public License as published by
4 : the Free Software Foundation, either version 3 of the License, or
5 : (at your option) any later version.
6 :
7 : This program is distributed in the hope that it will be useful,
8 : but WITHOUT ANY WARRANTY; without even the implied warranty of
9 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 : GNU General Public License for more details.
11 :
12 : You should have received a copy of the GNU General Public License
13 : along with this program. If not, see <http://www.gnu.org/licenses/>.
14 : */
15 :
16 : /*
17 : * NavEKF based AHRS (Attitude Heading Reference System) interface for
18 : * ArduPilot
19 : *
20 : */
21 :
22 : #include "AP_AHRS_config.h"
23 :
24 : #if AP_AHRS_ENABLED
25 :
26 : #include <AP_HAL/AP_HAL.h>
27 : #include "AP_AHRS.h"
28 : #include "AP_AHRS_View.h"
29 : #include <AP_BoardConfig/AP_BoardConfig.h>
30 : #include <AP_ExternalAHRS/AP_ExternalAHRS.h>
31 : #include <AP_Module/AP_Module.h>
32 : #include <AP_GPS/AP_GPS.h>
33 : #include <AP_Baro/AP_Baro.h>
34 : #include <AP_Compass/AP_Compass.h>
35 : #include <AP_InternalError/AP_InternalError.h>
36 : #include <AP_Logger/AP_Logger.h>
37 : #include <AP_Notify/AP_Notify.h>
38 : #include <AP_Vehicle/AP_Vehicle_Type.h>
39 : #include <GCS_MAVLink/GCS.h>
40 : #include <AP_InertialSensor/AP_InertialSensor.h>
41 :
42 : #include <AP_Mission/AP_Mission_config.h>
43 : #if AP_MISSION_ENABLED
44 : #include <AP_Mission/AP_Mission.h>
45 : #endif
46 :
47 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
48 : #include <SITL/SITL.h>
49 : #endif
50 : #include <AP_NavEKF3/AP_NavEKF3_feature.h>
51 :
52 : #define ATTITUDE_CHECK_THRESH_ROLL_PITCH_RAD radians(10)
53 : #define ATTITUDE_CHECK_THRESH_YAW_RAD radians(20)
54 :
55 : #ifndef HAL_AHRS_EKF_TYPE_DEFAULT
56 : #define HAL_AHRS_EKF_TYPE_DEFAULT 3
57 : #endif
58 :
59 : #ifndef HAL_AHRS_OPTIONS_DEFAULT
60 : #if APM_BUILD_TYPE(APM_BUILD_Rover)
61 : // DISABLE_DCM_FALLBACK_FW | DISABLE_DCM_FALLBACK_VTOL
62 : #define HAL_AHRS_OPTIONS_DEFAULT 3
63 : #elif APM_BUILD_TYPE(APM_BUILD_ArduSub)
64 : // ENABLE_USE_RECORDED_ORIGIN
65 : #define HAL_AHRS_OPTIONS_DEFAULT 16
66 : #else
67 : #define HAL_AHRS_OPTIONS_DEFAULT 0
68 : #endif
69 : #endif
70 :
71 : // table of user settable parameters
72 : const AP_Param::GroupInfo AP_AHRS::var_info[] = {
73 : // index 0 and 1 are for old parameters that are no longer not used
74 :
75 : // @Param: GPS_GAIN
76 : // @DisplayName: AHRS GPS gain
77 : // @Description: This controls how much to use the GPS to correct the attitude. This should never be set to zero for a plane as it would result in the plane losing control in turns. For a plane please use the default value of 1.0.
78 : // @Range: 0.0 1.0
79 : // @Increment: 0.01
80 : // @User: Advanced
81 : AP_GROUPINFO("GPS_GAIN", 2, AP_AHRS, gps_gain, 1.0f),
82 :
83 : // @Param: GPS_USE
84 : // @DisplayName: AHRS use GPS for DCM navigation and position-down
85 : // @Description: This controls whether to use dead-reckoning or GPS based navigation. If set to 0 then the GPS won't be used for navigation, and only dead reckoning will be used. A value of zero should never be used for normal flight. Currently this affects only the DCM-based AHRS: the EKF uses GPS according to its own parameters. A value of 2 means to use GPS for height as well as position - both in DCM estimation and when determining altitude-above-home.
86 : // @Values: 0:Disabled,1:Use GPS for DCM position,2:Use GPS for DCM position and height
87 : // @User: Advanced
88 : AP_GROUPINFO("GPS_USE", 3, AP_AHRS, _gps_use, float(GPSUse::Enable)),
89 :
90 : // @Param: YAW_P
91 : // @DisplayName: Yaw P
92 : // @Description: This controls the weight the compass or GPS has on the heading. A higher value means the heading will track the yaw source (GPS or compass) more rapidly.
93 : // @Range: 0.1 0.4
94 : // @Increment: 0.01
95 : // @User: Advanced
96 : AP_GROUPINFO("YAW_P", 4, AP_AHRS, _kp_yaw, 0.2f),
97 :
98 : // @Param: RP_P
99 : // @DisplayName: AHRS RP_P
100 : // @Description: This controls how fast the accelerometers correct the attitude
101 : // @Range: 0.1 0.4
102 : // @Increment: 0.01
103 : // @User: Advanced
104 : AP_GROUPINFO("RP_P", 5, AP_AHRS, _kp, 0.2f),
105 :
106 : // @Param: WIND_MAX
107 : // @DisplayName: Maximum wind
108 : // @Description: This sets the maximum allowable difference between ground speed and airspeed. A value of zero means to use the airspeed as is. This allows the plane to cope with a failing airspeed sensor by clipping it to groundspeed plus/minus this limit. See ARSPD_OPTIONS and ARSPD_WIND_MAX to disable airspeed sensors.
109 : // @Range: 0 127
110 : // @Units: m/s
111 : // @Increment: 1
112 : // @User: Advanced
113 : AP_GROUPINFO("WIND_MAX", 6, AP_AHRS, _wind_max, 0.0f),
114 :
115 : // NOTE: 7 was BARO_USE
116 :
117 : // @Param: TRIM_X
118 : // @DisplayName: AHRS Trim Roll
119 : // @Description: Compensates for the roll angle difference between the control board and the frame. Positive values make the vehicle roll right.
120 : // @Units: rad
121 : // @Range: -0.1745 +0.1745
122 : // @Increment: 0.01
123 : // @User: Standard
124 :
125 : // @Param: TRIM_Y
126 : // @DisplayName: AHRS Trim Pitch
127 : // @Description: Compensates for the pitch angle difference between the control board and the frame. Positive values make the vehicle pitch up/back.
128 : // @Units: rad
129 : // @Range: -0.1745 +0.1745
130 : // @Increment: 0.01
131 : // @User: Standard
132 :
133 : // @Param: TRIM_Z
134 : // @DisplayName: AHRS Trim Yaw
135 : // @Description: Not Used
136 : // @Units: rad
137 : // @Range: -0.1745 +0.1745
138 : // @Increment: 0.01
139 : // @User: Advanced
140 : AP_GROUPINFO("TRIM", 8, AP_AHRS, _trim, 0),
141 :
142 : // @Param: ORIENTATION
143 : // @DisplayName: Board Orientation
144 : // @Description: Overall board orientation relative to the standard orientation for the board type. This rotates the IMU and compass readings to allow the board to be oriented in your vehicle at any 90 or 45 degree angle. The label for each option is specified in the order of rotations for that orientation. This option takes affect on next boot. After changing you will need to re-level your vehicle. Firmware versions 4.2 and prior can use a CUSTOM (100) rotation to set the AHRS_CUSTOM_ROLL/PIT/YAW angles for AHRS orientation. Later versions provide two general custom rotations which can be used, Custom 1 and Custom 2, with CUST_ROT1_ROLL/PIT/YAW or CUST_ROT2_ROLL/PIT/YAW angles.
145 : // @Values: 0:None,1:Yaw45,2:Yaw90,3:Yaw135,4:Yaw180,5:Yaw225,6:Yaw270,7:Yaw315,8:Roll180,9:Yaw45Roll180,10:Yaw90Roll180,11:Yaw135Roll180,12:Pitch180,13:Yaw225Roll180,14:Yaw270Roll180,15:Yaw315Roll180,16:Roll90,17:Yaw45Roll90,18:Yaw90Roll90,19:Yaw135Roll90,20:Roll270,21:Yaw45Roll270,22:Yaw90Roll270,23:Yaw135Roll270,24:Pitch90,25:Pitch270,26:Yaw90Pitch180,27:Yaw270Pitch180,28:Pitch90Roll90,29:Pitch90Roll180,30:Pitch90Roll270,31:Pitch180Roll90,32:Pitch180Roll270,33:Pitch270Roll90,34:Pitch270Roll180,35:Pitch270Roll270,36:Yaw90Pitch180Roll90,37:Yaw270Roll90,38:Yaw293Pitch68Roll180,39:Pitch315,40:Pitch315Roll90,42:Roll45,43:Roll315,100:Custom 4.1 and older,101:Custom 1,102:Custom 2
146 : // @User: Advanced
147 : AP_GROUPINFO("ORIENTATION", 9, AP_AHRS, _board_orientation, 0),
148 :
149 : // @Param: COMP_BETA
150 : // @DisplayName: AHRS Velocity Complementary Filter Beta Coefficient
151 : // @Description: This controls the time constant for the cross-over frequency used to fuse AHRS (airspeed and heading) and GPS data to estimate ground velocity. Time constant is 0.1/beta. A larger time constant will use GPS data less and a small time constant will use air data less.
152 : // @Range: 0.001 0.5
153 : // @Increment: 0.01
154 : // @User: Advanced
155 : AP_GROUPINFO("COMP_BETA", 10, AP_AHRS, beta, 0.1f),
156 :
157 : // @Param: GPS_MINSATS
158 : // @DisplayName: AHRS GPS Minimum satellites
159 : // @Description: Minimum number of satellites visible to use GPS for velocity based corrections attitude correction. This defaults to 6, which is about the point at which the velocity numbers from a GPS become too unreliable for accurate correction of the accelerometers.
160 : // @Range: 0 10
161 : // @Increment: 1
162 : // @User: Advanced
163 : AP_GROUPINFO("GPS_MINSATS", 11, AP_AHRS, _gps_minsats, 6),
164 :
165 : // NOTE: index 12 was for GPS_DELAY, but now removed, fixed delay
166 : // of 1 was found to be the best choice
167 :
168 : // 13 was the old EKF_USE
169 :
170 : // @Param: EKF_TYPE
171 : // @DisplayName: Use NavEKF Kalman filter for attitude and position estimation
172 : // @Description: This controls which NavEKF Kalman filter version is used for attitude and position estimation
173 : // @Values: 0:Disabled,2:Enable EKF2,3:Enable EKF3, 10:Sim, 11:ExternalAHRS
174 : // @User: Advanced
175 : AP_GROUPINFO("EKF_TYPE", 14, AP_AHRS, _ekf_type, HAL_AHRS_EKF_TYPE_DEFAULT),
176 :
177 : // index 15 was CUSTOM_ROLL
178 :
179 : // index 16 was CUSTOM_PIT
180 :
181 : // index 17 was CUSTOM_YAW
182 :
183 : // @Param: OPTIONS
184 : // @DisplayName: Optional AHRS behaviour
185 : // @Description: This controls optional AHRS behaviour. Setting DisableDCMFallbackFW will change the AHRS behaviour for fixed wing aircraft in fly-forward flight to not fall back to DCM when the EKF stops navigating. Setting DisableDCMFallbackVTOL will change the AHRS behaviour for fixed wing aircraft in non fly-forward (VTOL) flight to not fall back to DCM when the EKF stops navigating. Setting DontDisableAirspeedUsingEKF disables the EKF based innovation check for airspeed consistency. Setting AutoRecordOrigin will auto-save the EKF origin to parameters when it becomes valid.
186 : // @Bitmask: 0:DisableDCMFallbackFW, 1:DisableDCMFallbackVTOL, 2:DontDisableAirspeedUsingEKF, 3:RecordOrigin, 4:UseRecordedOriginForNonGPS
187 : // @User: Advanced
188 : AP_GROUPINFO("OPTIONS", 18, AP_AHRS, _options, HAL_AHRS_OPTIONS_DEFAULT),
189 :
190 : // @Param: ORIGIN_LAT
191 : // @DisplayName: AHRS last origin latitude
192 : // @Description: AHRS last origin latitude in degrees
193 : // @Range: -180 180
194 : // @Increment: 1
195 : // @User: Advanced
196 : AP_GROUPINFO("ORIGIN_LAT", 19, AP_AHRS, _origin_lat, 0),
197 :
198 : // @Param: ORIGIN_LON
199 : // @DisplayName: AHRS last origin longitude
200 : // @Description: AHRS last origin longitude in degrees
201 : // @Range: -180 180
202 : // @User: Advanced
203 : AP_GROUPINFO("ORIGIN_LON", 20, AP_AHRS, _origin_lon, 0),
204 :
205 : // @Param: ORIGIN_ALT
206 : // @DisplayName: AHRS last origin altitude
207 : // @Description: AHRS last origin altitude in meters
208 : // @Range: -200 5000
209 : // @User: Advanced
210 : AP_GROUPINFO("ORIGIN_ALT", 21, AP_AHRS, _origin_alt, 0),
211 :
212 : AP_GROUPEND
213 : };
214 :
215 : extern const AP_HAL::HAL& hal;
216 :
217 : // constructor
218 2100 : AP_AHRS::AP_AHRS(uint8_t flags) :
219 2100 : _ekf_flags(flags)
220 : {
221 2100 : _singleton = this;
222 :
223 : // load default values from var_info table
224 2100 : AP_Param::setup_object_defaults(this, var_info);
225 :
226 : // we assume active_backend is not nullptr in many places, so make
227 : // sure it is set early:
228 2100 : update_configured_ekf_type();
229 2100 : update_active_EKF_type();
230 2100 : update_secondary_backend_pointers();
231 :
232 : #if APM_BUILD_COPTER_OR_HELI || APM_BUILD_TYPE(APM_BUILD_ArduSub)
233 : // Copter and Sub force the use of EKF
234 1084 : _ekf_flags |= AP_AHRS::FLAG_ALWAYS_USE_EKF;
235 : #endif
236 2100 : state.dcm_matrix.identity();
237 :
238 : // initialise the controller-to-autopilot-body trim state:
239 2100 : _last_trim = _trim.get();
240 2100 : _rotation_autopilot_body_to_vehicle_body.from_euler(_last_trim.x, _last_trim.y, _last_trim.z);
241 2100 : _rotation_vehicle_body_to_autopilot_body = _rotation_autopilot_body_to_vehicle_body.transposed();
242 2100 : }
243 :
244 : // return a pointer to the backend for supplied type
245 133503282 : AP_AHRS_Backend *AP_AHRS::backend_for_type(EKFType type)
246 : {
247 133503282 : switch (type) {
248 : #if AP_AHRS_DCM_ENABLED
249 2250170 : case EKFType::DCM:
250 2250170 : return &dcm;
251 : #endif
252 : #if AP_AHRS_NAVEKF3_ENABLED
253 119632491 : case EKFType::THREE:
254 119632491 : return &ekf3;
255 : #endif
256 : #if AP_AHRS_NAVEKF2_ENABLED
257 673509 : case EKFType::TWO:
258 673509 : return &ekf2;
259 : #endif
260 : #if AP_AHRS_EXTERNAL_ENABLED
261 309843 : case EKFType::EXTERNAL:
262 309843 : return &external;
263 : #endif
264 : #if AP_AHRS_SIM_ENABLED
265 10637269 : case EKFType::SIM:
266 10637269 : return ∼
267 : #endif
268 : }
269 0 : return nullptr;
270 : }
271 :
272 : // updates the cached value for the currently configured EKF type
273 : // (i.e. the one that the user has specified with parameters). Also
274 : // sets the pointer to the allocated backend for that type.
275 41690904 : void AP_AHRS::update_configured_ekf_type()
276 : {
277 41690904 : const auto new_type = _configured_ekf_type();
278 41690904 : auto *new_backend = backend_for_type(new_type);
279 41690904 : if (new_backend != nullptr) {
280 41690904 : state.configured_ekf_type = new_type;
281 41690904 : configured_backend = new_backend;
282 41690904 : configured_estimates = estimates_for_type(new_type);
283 41690904 : return;
284 : }
285 0 : INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
286 : }
287 :
288 41690904 : void AP_AHRS::update_active_EKF_type()
289 : {
290 41690904 : const auto new_type = _active_EKF_type();
291 41690904 : auto *new_backend = backend_for_type(new_type);
292 41690904 : if (new_backend != nullptr) {
293 41690904 : state.active_EKF_type = new_type;
294 41690904 : active_backend = new_backend;
295 41690904 : active_estimates = estimates_for_type(new_type);
296 41690904 : return;
297 : }
298 0 : INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
299 : }
300 :
301 : // update the pointer to the estimates containing the secondary backend's data
302 41690904 : void AP_AHRS::update_secondary_backend_pointers()
303 : {
304 : EKFType secondary_type;
305 41690904 : if (!_get_secondary_EKF_type(secondary_type)) {
306 129619 : secondary_estimates = nullptr;
307 129619 : return;
308 : }
309 41561285 : secondary_estimates = estimates_for_type(secondary_type);
310 : }
311 :
312 : // init sets up INS board orientation
313 2088 : void AP_AHRS::init()
314 : {
315 2088 : update_orientation();
316 :
317 : // EKF1 is no longer supported - handle case where it is selected
318 2088 : if (_ekf_type.get() == 1) {
319 0 : AP_BoardConfig::config_error("EKF1 not available");
320 : }
321 : #if !HAL_NAVEKF2_AVAILABLE && HAL_NAVEKF3_AVAILABLE
322 0 : if (_ekf_type.get() == 2) {
323 0 : _ekf_type.set(EKFType::THREE);
324 0 : ekf3.EKF3.set_enable(true);
325 : }
326 : #elif !HAL_NAVEKF3_AVAILABLE && HAL_NAVEKF2_AVAILABLE
327 : if (_ekf_type.get() == 3) {
328 : _ekf_type.set(EKFType::TWO);
329 : ekf2.EKF2.set_enable(true);
330 : }
331 : #endif
332 :
333 : #if HAL_NAVEKF2_AVAILABLE && HAL_NAVEKF3_AVAILABLE
334 : // a special case to catch users who had AHRS_EKF_TYPE=2 saved and
335 : // updated to a version where EK2_ENABLE=0
336 2088 : if (_ekf_type.get() == 2 && !ekf2.EKF2.get_enable() && ekf3.EKF3.get_enable()) {
337 0 : _ekf_type.set(EKFType::THREE);
338 : }
339 : #endif
340 :
341 : // we may have updated ekf_type()'s results, so set the backend again:
342 2088 : update_configured_ekf_type();
343 2088 : update_active_EKF_type();
344 2088 : update_secondary_backend_pointers();
345 :
346 : // initialise this as no-change from the active type:
347 2088 : last_active_ekf_type = state.active_EKF_type;
348 2088 : }
349 :
350 : // has_status returns information about the EKF health and
351 : // capabilities. It is currently invalid to call this when a
352 : // backend is in charge which returns false for get_filter_status
353 : // - so this will simply return false for DCM, for example.
354 13856072 : bool AP_AHRS::has_status(Status status) const {
355 : nav_filter_status filter_status;
356 13856072 : if (!get_filter_status(filter_status)) {
357 202530 : return false;
358 : }
359 13653542 : return (filter_status.value & uint32_t(status)) != 0;
360 : }
361 :
362 : // updates matrices responsible for rotating vectors from vehicle body
363 : // frame to autopilot body frame from _trim variables
364 41686716 : void AP_AHRS::update_trim_rotation_matrices()
365 : {
366 41686716 : if (_last_trim == _trim.get()) {
367 : // nothing to do
368 41686304 : return;
369 : }
370 :
371 412 : _last_trim = _trim.get();
372 412 : _rotation_autopilot_body_to_vehicle_body.from_euler(_last_trim.x, _last_trim.y, _last_trim.z);
373 412 : _rotation_vehicle_body_to_autopilot_body = _rotation_autopilot_body_to_vehicle_body.transposed();
374 : }
375 :
376 : // return a Quaternion representing our current attitude in NED frame
377 109536 : void AP_AHRS::get_quat_body_to_ned(Quaternion &quat) const
378 : {
379 109536 : quat.from_rotation_matrix(get_rotation_body_to_ned());
380 109536 : }
381 :
382 : // convert a vector from body to earth frame
383 0 : Vector3f AP_AHRS::body_to_earth(const Vector3f &v) const
384 : {
385 0 : return get_rotation_body_to_ned() * v;
386 : }
387 :
388 : // convert a vector from earth to body frame
389 48531 : Vector3f AP_AHRS::earth_to_body(const Vector3f &v) const
390 : {
391 48531 : return get_rotation_body_to_ned().mul_transpose(v);
392 : }
393 :
394 :
395 : // reset the current gyro drift estimate
396 : // should be called if gyro offsets are recalculated
397 7 : void AP_AHRS::reset_gyro_drift(void)
398 : {
399 : // support locked access functions to AHRS data
400 7 : WITH_SEMAPHORE(_rsem);
401 :
402 42 : for (auto &backend_and_estimates : backends_and_estimates) {
403 35 : backend_and_estimates.backend.reset_gyro_drift();
404 : }
405 7 : }
406 :
407 : /*
408 : * copy results from a backend over AP_AHRS canonical results.
409 : * This updates member variables like roll and pitch, as well as
410 : * updating derived values like sin_roll and sin_pitch.
411 : */
412 41686716 : void AP_AHRS::update_state(void)
413 : {
414 41686716 : const uint8_t primary_gyro = active_estimates->primary_gyro;
415 : #if AP_INERTIALSENSOR_ENABLED
416 : // tell the IMUS about primary changes
417 41686716 : if (primary_gyro != state.primary_gyro) {
418 61 : AP::ins().set_primary(primary_gyro);
419 : }
420 : #endif
421 41686716 : state.primary_gyro = primary_gyro;
422 :
423 41686716 : state.primary_accel = active_estimates->primary_accel;
424 :
425 41686716 : state.EAS2TAS = AP_AHRS_Backend::get_EAS2TAS();
426 41686716 : state.airspeed_EAS_ok = _airspeed_EAS(state.airspeed_EAS, state.airspeed_estimate_type);
427 41686716 : state.airspeed_TAS_ok = _airspeed_TAS(state.airspeed_TAS);
428 41686716 : state.airspeed_TAS_vec_ok = _airspeed_TAS(state.airspeed_TAS_vec);
429 :
430 41686716 : roll = active_estimates->roll_rad;
431 41686716 : pitch = active_estimates->pitch_rad;
432 41686716 : yaw = active_estimates->yaw_rad;
433 :
434 41686716 : state.dcm_matrix = active_estimates->dcm_matrix;
435 :
436 41686716 : state.gyro_estimate = active_estimates->gyro_estimate;
437 41686716 : state.gyro_drift = active_estimates->gyro_drift;
438 :
439 41686716 : state.accel_ef = active_estimates->accel_ef;
440 41686716 : state.accel_bias = active_estimates->accel_bias;
441 :
442 41686716 : update_cd_values();
443 41686716 : update_trig();
444 :
445 41686716 : state.quat_ok = active_estimates->get_quaternion(state.quat);
446 41686716 : state.location_ok = _get_location(state.location);
447 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
448 41686716 : if (state.location_ok && !state.location.initialised()) {
449 0 : AP_HAL::panic("uninitialised location returned by _get_location");
450 : }
451 : #endif // CONFIG_HAL_BOARD == HAL_BOARD_SITL
452 41686716 : state.ground_speed_vec = active_estimates->velocity_NE;
453 41686716 : state.ground_speed = state.ground_speed_vec.length();
454 41686716 : state.corrected_dv_valid = _getCorrectedDeltaVelocityNED(state.corrected_dv, state.corrected_dv_dt);
455 :
456 : // check if origin has been set
457 41686716 : bool origin_ok = _get_origin(state.origin);
458 41686716 : if (origin_ok && !state.origin_ok) {
459 : // record origin from backend to AHRS state
460 964 : state.origin_ok = origin_ok;
461 :
462 : #if HAL_LOGGING_ENABLED
463 : // log origin
464 964 : Log_Write_Home_And_Origin();
465 : #endif
466 :
467 : // report origin via MAVLink
468 964 : GCS_SEND_MESSAGE(MSG_ORIGIN);
469 :
470 : // save origin to parameters
471 964 : record_origin();
472 : }
473 :
474 : // if no origin, attempt to use recorded origin
475 41686716 : if (!state.origin_ok) {
476 5099682 : use_recorded_origin_maybe();
477 : }
478 :
479 41686716 : state.velocity_NED_ok = active_estimates->get_velocity_NED(state.velocity_NED);
480 41686716 : }
481 :
482 208433580 : void AP_AHRS::try_set_common_origin(const AP_AHRS_Backend &source_backend, const AP_AHRS_Backend::Estimates &source_estimates)
483 : {
484 208433580 : if (done_common_origin) {
485 208432685 : return;
486 : }
487 :
488 : /*
489 : if we now have an origin then set in all backends
490 : */
491 32424992 : if (!source_estimates.provides_common_origin) {
492 : // e.g. DCM doesn't provide an origin which can be set
493 : // into the other backends
494 19454438 : return;
495 : }
496 12970554 : Location new_origin;
497 12970554 : if (!source_backend.get_origin(new_origin)) {
498 : // no valid origin from this backend
499 12969659 : return;
500 : }
501 : // set the origin in all backends which can take it (except
502 : // the one which supplied it). Invoking AP_AHRS::set_origin
503 : // here will cause warnings from the EKFs.
504 5370 : for (auto &dest_backend_and_estimates : backends_and_estimates) {
505 4475 : if (&dest_backend_and_estimates.estimates == &source_estimates) {
506 895 : continue;
507 : }
508 : // note that SITL and DCM ignore this set_origin call via
509 : // an empty base-class implementation:
510 3580 : dest_backend_and_estimates.backend.set_origin(new_origin);
511 : }
512 :
513 895 : done_common_origin = true;
514 : }
515 :
516 : // method responsible for updating the reset counters in the AHRS.
517 : // These can get bumped if we change backends or the count in the
518 : // current backend changes.
519 41686716 : void AP_AHRS::update_reset_counters()
520 : {
521 41686716 : if (state.active_EKF_type != last_active_ekf_type) {
522 1530 : attitude_reset_tracker.fill(active_estimates->attitude_reset_count);
523 1530 : yaw_reset_tracker.fill(active_estimates->yaw_reset_count);
524 1530 : position_NE_reset_tracker.fill(active_estimates->position_NE_reset_count);
525 1530 : position_D_reset_tracker.fill(active_estimates->position_D_reset_count);
526 1530 : LOGGER_WRITE_EVENT(LogEvent::EKF_YAW_RESET);
527 1530 : return;
528 : }
529 :
530 41685186 : attitude_reset_tracker.update(active_estimates->attitude_reset_count);
531 41685186 : if (yaw_reset_tracker.update(active_estimates->yaw_reset_count)) {
532 1586 : LOGGER_WRITE_EVENT(LogEvent::EKF_YAW_RESET);
533 : }
534 41685186 : position_NE_reset_tracker.update(active_estimates->position_NE_reset_count);
535 41685186 : position_D_reset_tracker.update(active_estimates->position_D_reset_count);
536 : }
537 :
538 : // update run at loop rate
539 41686716 : void AP_AHRS::update(bool skip_ins_update)
540 : {
541 : // periodically checks to see if we should update the AHRS
542 : // orientation (e.g. based on the AHRS_ORIENTATION parameter)
543 : // allow for runtime change of orientation
544 : // this makes initial config easier
545 41686716 : update_orientation();
546 :
547 41686716 : if (!skip_ins_update) {
548 : // tell the IMU to grab some data
549 9093585 : AP::ins().update();
550 : }
551 :
552 : // support locked access functions to AHRS data
553 41686716 : WITH_SEMAPHORE(_rsem);
554 :
555 : // see if we have to restore home after a watchdog reset:
556 41686716 : if (!_checked_watchdog_home) {
557 2086 : load_watchdog_home();
558 2086 : _checked_watchdog_home = true;
559 : }
560 :
561 : // drop back to normal priority if we were boosted by the INS
562 : // calling delay_microseconds_boost()
563 41686716 : hal.scheduler->boost_end();
564 :
565 : // update autopilot-body-to-vehicle-body from _trim parameters:
566 41686716 : update_trim_rotation_matrices();
567 :
568 : // update takeoff/touchdown flags
569 41686716 : update_flags();
570 :
571 : // update the backends, configured-first. Some backends look at
572 : // loop-time-remaining and opt-out of their full update if there
573 : // isn't enough time left. Copy back their results while we are
574 : // at it.
575 41686716 : configured_backend->update();
576 41686716 : *configured_estimates = {};
577 41686716 : configured_backend->get_results(*configured_estimates);
578 : // if we don't have an origin, maybe set one:
579 41686716 : try_set_common_origin(*configured_backend, *configured_estimates);
580 :
581 250120296 : for (auto &backend_and_estimates : backends_and_estimates) {
582 208433580 : if (&backend_and_estimates.backend == configured_backend) {
583 : // already updated
584 41686716 : continue;
585 : }
586 166746864 : backend_and_estimates.backend.update();
587 166746864 : backend_and_estimates.estimates = {};
588 166746864 : backend_and_estimates.backend.get_results(backend_and_estimates.estimates);
589 : // if we don't have an origin, maybe set one:
590 166746864 : try_set_common_origin(backend_and_estimates.backend, backend_and_estimates.estimates);
591 : }
592 :
593 41686716 : update_configured_ekf_type();
594 41686716 : update_active_EKF_type();
595 41686716 : update_secondary_backend_pointers();
596 :
597 : // update blinking lights, buzzer etc bsaed on active EKF type:
598 41686716 : update_notify_from_filter_status(active_estimates->filter_status);
599 :
600 : // update result reset counters. Note that this *must* be called
601 : // before assignment of state.active_EKF_type to
602 : // last_active_ekf_type as it uses the difference between these
603 : // two values to determine what sort of reset to do
604 41686716 : update_reset_counters();
605 :
606 41686716 : if (state.active_EKF_type != last_active_ekf_type) {
607 1530 : last_active_ekf_type = state.active_EKF_type;
608 1530 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "AHRS: %s active", active_backend->shortname());
609 : }
610 :
611 : // update published state, including copying state from the active backend:
612 41686716 : update_state();
613 :
614 : #if AP_MODULE_SUPPORTED
615 : // call AHRS_update hook if any
616 0 : AP_Module::call_hook_AHRS_update(*this);
617 : #endif
618 :
619 : // push gyros if optical flow present
620 41686716 : if (hal.opticalflow) {
621 41686716 : const Vector3f &exported_gyro_bias = get_gyro_drift();
622 41686716 : hal.opticalflow->push_gyro_bias(exported_gyro_bias.x, exported_gyro_bias.y);
623 : }
624 :
625 41686716 : if (_view != nullptr) {
626 : // update optional alternative attitude view
627 36916929 : _view->update();
628 : }
629 :
630 : // update AOA and SSA
631 41686716 : update_AOA_SSA();
632 :
633 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
634 : /*
635 : add timing jitter to simulate slow EKF response
636 : */
637 41686716 : const auto *sitl = AP::sitl();
638 41686716 : if (sitl->loop_time_jitter_us > 0) {
639 0 : hal.scheduler->delay_microseconds(random() % sitl->loop_time_jitter_us);
640 : }
641 : #endif
642 41686716 : }
643 :
644 41686716 : void AP_AHRS::update_notify_from_filter_status(const nav_filter_status &status)
645 : {
646 41686716 : AP_Notify::flags.gps_fusion = status.flags.using_gps; // Drives AP_Notify flag for usable GPS.
647 41686716 : AP_Notify::flags.gps_glitching = status.flags.gps_glitching;
648 41686716 : AP_Notify::flags.have_pos_abs = status.flags.horiz_pos_abs;
649 41686716 : }
650 :
651 2115 : void AP_AHRS::reset()
652 : {
653 : // support locked access functions to AHRS data
654 2115 : WITH_SEMAPHORE(_rsem);
655 :
656 12690 : for (auto &backend_and_estimates : backends_and_estimates) {
657 10575 : backend_and_estimates.backend.reset();
658 : }
659 2115 : }
660 :
661 : // dead-reckoning support
662 41686716 : bool AP_AHRS::_get_location(Location &loc) const
663 : {
664 41686716 : if (active_estimates->get_location(loc)) {
665 38577875 : return true;
666 : }
667 :
668 : #if AP_AHRS_DCM_ENABLED
669 : // fall back to position from DCM
670 3108841 : if (!always_use_EKF()) {
671 481619 : return dcm_estimates.get_location(loc);
672 : }
673 : #endif
674 :
675 2627222 : return false;
676 : }
677 :
678 : // status reporting of estimated errors
679 1117757 : float AP_AHRS::get_error_rp(void) const
680 : {
681 : #if AP_AHRS_DCM_ENABLED
682 1117757 : return dcm.get_error_rp();
683 : #endif
684 : return 0;
685 : }
686 :
687 1117757 : float AP_AHRS::get_error_yaw(void) const
688 : {
689 : #if AP_AHRS_DCM_ENABLED
690 1117757 : return dcm.get_error_yaw();
691 : #endif
692 : return 0;
693 : }
694 :
695 : /*
696 : * Determine how aligned heading_deg is with the wind. Return result
697 : * is 1.0 when perfectly aligned heading into wind, -1 when perfectly
698 : * aligned with-wind, and zero when perfect cross-wind. There is no
699 : * distinction between a left or right cross-wind. Wind speed is ignored
700 : */
701 35442 : float AP_AHRS::wind_alignment(const float heading_deg) const
702 : {
703 35442 : Vector3f wind;
704 35442 : if (!get_wind(wind)) {
705 2288 : return 0;
706 : }
707 33154 : const float wind_heading_rad = atan2f(-wind.y, -wind.x);
708 33154 : return cosf(wind_heading_rad - radians(heading_deg));
709 : }
710 :
711 : /*
712 : * returns forward head-wind component in m/s. Negative means tail-wind.
713 : */
714 35442 : float AP_AHRS::head_wind(void) const
715 : {
716 35442 : Vector3f wind;
717 : // wind_alignment() has already returned zero if we have no valid
718 : // estimate, so the validity of the wind vector is not checked here
719 35442 : IGNORE_RETURN(get_wind(wind));
720 35442 : const float alignment = wind_alignment(get_yaw_deg());
721 35442 : return alignment * wind.xy().length();
722 : }
723 :
724 : /*
725 : return true if the current AHRS airspeed estimate is directly derived from an airspeed sensor
726 : */
727 3837229 : bool AP_AHRS::using_airspeed_sensor() const
728 : {
729 3837229 : return state.airspeed_estimate_type == AirspeedEstimateType::AIRSPEED_SENSOR;
730 : }
731 :
732 : /*
733 : Return true if a airspeed sensor should be used for the AHRS airspeed estimate
734 : */
735 41686716 : bool AP_AHRS::_should_use_airspeed_sensor(uint8_t airspeed_index) const
736 : {
737 41686716 : if (!airspeed_sensor_enabled(airspeed_index)) {
738 33452901 : return false;
739 : }
740 : nav_filter_status filter_status;
741 8233815 : if (!option_set(Options::DISABLE_AIRSPEED_EKF_CHECK) &&
742 12722549 : fly_forward &&
743 8226625 : hal.util->get_soft_armed() &&
744 19618197 : get_filter_status(filter_status) &&
745 3426834 : (filter_status.flags.rejecting_airspeed && !filter_status.flags.dead_reckoning)) {
746 : // special case for when backend is rejecting airspeed data in
747 : // an armed fly_forward state and not dead reckoning. Then the
748 : // airspeed data is highly suspect and will be rejected. We
749 : // will use the synthetic airspeed instead
750 1725 : return false;
751 : }
752 8232090 : return true;
753 : }
754 :
755 : // return an airspeed estimate if available. return true
756 : // if we have an estimate
757 41686716 : bool AP_AHRS::_airspeed_EAS(float &airspeed_ret, AirspeedEstimateType &airspeed_estimate_type) const
758 : {
759 : #if AP_AHRS_DCM_ENABLED || (AP_AIRSPEED_ENABLED && AP_GPS_ENABLED)
760 41686716 : const uint8_t idx = get_active_airspeed_index();
761 : #endif
762 : #if AP_AIRSPEED_ENABLED && AP_GPS_ENABLED
763 41686716 : if (_should_use_airspeed_sensor(idx)) {
764 8232090 : airspeed_ret = AP::airspeed()->get_airspeed(idx);
765 :
766 8232090 : if (_wind_max > 0 && AP::gps().status() >= AP_GPS_FixType::FIX_2D) {
767 : // constrain the airspeed by the ground speed
768 : // and AHRS_WIND_MAX
769 0 : const float gnd_speed = AP::gps().ground_speed();
770 0 : float true_airspeed = airspeed_ret * get_EAS2TAS();
771 0 : true_airspeed = constrain_float(true_airspeed,
772 : gnd_speed - _wind_max,
773 : gnd_speed + _wind_max);
774 0 : airspeed_ret = true_airspeed / get_EAS2TAS();
775 : }
776 8232090 : airspeed_estimate_type = AirspeedEstimateType::AIRSPEED_SENSOR;
777 8232090 : return true;
778 : }
779 : #endif
780 :
781 33454626 : if (!get_wind_estimation_enabled()) {
782 33309065 : airspeed_estimate_type = AirspeedEstimateType::NO_NEW_ESTIMATE;
783 33309065 : return false;
784 : }
785 :
786 : // estimate it via nav velocity and wind estimates
787 :
788 : // get wind estimates
789 145561 : Vector3f wind_vel;
790 145561 : bool have_wind = false;
791 :
792 145561 : switch (active_EKF_type()) {
793 : #if AP_AHRS_DCM_ENABLED
794 64486 : case EKFType::DCM:
795 64486 : airspeed_estimate_type = AirspeedEstimateType::DCM_SYNTHETIC;
796 64486 : return dcm.airspeed_EAS(idx, airspeed_ret);
797 : #endif
798 :
799 : #if AP_AHRS_SIM_ENABLED
800 598 : case EKFType::SIM:
801 598 : airspeed_estimate_type = AirspeedEstimateType::SIM;
802 598 : return sim.airspeed_EAS(airspeed_ret);
803 : #endif
804 :
805 : #if HAL_NAVEKF2_AVAILABLE
806 0 : case EKFType::TWO:
807 : #if AP_AHRS_DCM_ENABLED
808 0 : airspeed_estimate_type = AirspeedEstimateType::DCM_SYNTHETIC;
809 0 : return dcm.airspeed_EAS(idx, airspeed_ret);
810 : #else
811 : return false;
812 : #endif
813 : #endif
814 :
815 : #if HAL_NAVEKF3_AVAILABLE
816 80182 : case EKFType::THREE:
817 80182 : wind_vel = ekf3_estimates.wind;
818 80182 : have_wind = ekf3_estimates.wind_valid;
819 80182 : break;
820 : #endif
821 :
822 : #if AP_AHRS_EXTERNAL_ENABLED
823 295 : case EKFType::EXTERNAL:
824 : #if AP_AHRS_DCM_ENABLED
825 295 : airspeed_estimate_type = AirspeedEstimateType::DCM_SYNTHETIC;
826 295 : return dcm.airspeed_EAS(idx, airspeed_ret);
827 : #else
828 : return false;
829 : #endif
830 : #endif
831 : }
832 :
833 : // estimate it via nav velocity and wind estimates
834 80182 : Vector3f nav_vel;
835 80182 : if (have_wind && have_inertial_nav() && get_velocity_NED(nav_vel)) {
836 73977 : Vector3f true_airspeed_vec = nav_vel - wind_vel;
837 73977 : float true_airspeed = true_airspeed_vec.length();
838 73977 : float gnd_speed = nav_vel.length();
839 73977 : if (_wind_max > 0) {
840 0 : float tas_lim_lower = MAX(0.0f, (gnd_speed - _wind_max));
841 0 : float tas_lim_upper = MAX(tas_lim_lower, (gnd_speed + _wind_max));
842 0 : true_airspeed = constrain_float(true_airspeed, tas_lim_lower, tas_lim_upper);
843 : } else {
844 73977 : true_airspeed = MAX(0.0f, true_airspeed);
845 : }
846 73977 : airspeed_ret = true_airspeed / get_EAS2TAS();
847 73977 : airspeed_estimate_type = AirspeedEstimateType::EKF3_SYNTHETIC;
848 73977 : return true;
849 : }
850 :
851 : #if AP_AHRS_DCM_ENABLED
852 : // fallback to DCM
853 6205 : airspeed_estimate_type = AirspeedEstimateType::DCM_SYNTHETIC;
854 6205 : return dcm.airspeed_EAS(idx, airspeed_ret);
855 : #endif
856 :
857 : return false;
858 : }
859 :
860 41686716 : bool AP_AHRS::_airspeed_TAS(float &airspeed_ret) const
861 : {
862 41686716 : switch (active_EKF_type()) {
863 : #if AP_AHRS_DCM_ENABLED
864 1986578 : case EKFType::DCM:
865 1986578 : return dcm.airspeed_TAS(airspeed_ret);
866 : #endif
867 : #if HAL_NAVEKF2_AVAILABLE
868 39700138 : case EKFType::TWO:
869 : #endif
870 : #if HAL_NAVEKF3_AVAILABLE
871 0 : case EKFType::THREE:
872 : #endif
873 : #if AP_AHRS_SIM_ENABLED
874 : case EKFType::SIM:
875 : #endif
876 : #if AP_AHRS_EXTERNAL_ENABLED
877 : case EKFType::EXTERNAL:
878 : #endif
879 39700138 : break;
880 : }
881 :
882 39700138 : if (!airspeed_EAS(airspeed_ret)) {
883 32450097 : return false;
884 : }
885 7250041 : airspeed_ret *= get_EAS2TAS();
886 7250041 : return true;
887 : }
888 :
889 : // return estimate of true airspeed vector in body frame in m/s
890 : // returns false if estimate is unavailable
891 41686716 : bool AP_AHRS::_airspeed_TAS(Vector3f &vec) const
892 : {
893 41686716 : switch (active_EKF_type()) {
894 : #if AP_AHRS_DCM_ENABLED
895 1986578 : case EKFType::DCM:
896 1986578 : break;
897 : #endif
898 : #if HAL_NAVEKF2_AVAILABLE
899 135980 : case EKFType::TWO:
900 135980 : return ekf2.EKF2.getAirSpdVec(vec);
901 : #endif
902 :
903 : #if HAL_NAVEKF3_AVAILABLE
904 36087486 : case EKFType::THREE:
905 36087486 : return ekf3.EKF3.getAirSpdVec(vec);
906 : #endif
907 :
908 : #if AP_AHRS_SIM_ENABLED
909 3410661 : case EKFType::SIM:
910 3410661 : break;
911 : #endif
912 :
913 : #if AP_AHRS_EXTERNAL_ENABLED
914 66011 : case EKFType::EXTERNAL:
915 66011 : break;
916 : #endif
917 : }
918 5463250 : return false;
919 : }
920 :
921 : // return the innovation in m/s, innovation variance in (m/s)^2 and age in msec of the last TAS measurement processed for a given sensor instance
922 : // returns false if the data is unavailable
923 178439 : bool AP_AHRS::airspeed_health_data(uint8_t instance, float &innovation, float &innovationVariance, uint32_t &age_ms) const
924 : {
925 178439 : switch (active_EKF_type()) {
926 : #if AP_AHRS_DCM_ENABLED
927 14590 : case EKFType::DCM:
928 14590 : break;
929 : #endif
930 : #if HAL_NAVEKF2_AVAILABLE
931 56 : case EKFType::TWO:
932 56 : break;
933 : #endif
934 :
935 : #if HAL_NAVEKF3_AVAILABLE
936 152265 : case EKFType::THREE:
937 152265 : return ekf3.EKF3.getAirSpdHealthData(instance, innovation, innovationVariance, age_ms);
938 : #endif
939 :
940 : #if AP_AHRS_SIM_ENABLED
941 7742 : case EKFType::SIM:
942 7742 : break;
943 : #endif
944 :
945 : #if AP_AHRS_EXTERNAL_ENABLED
946 3786 : case EKFType::EXTERNAL:
947 3786 : break;
948 : #endif
949 : }
950 26174 : return false;
951 : }
952 :
953 : // true if compass is being used
954 133406 : bool AP_AHRS::use_compass(void)
955 : {
956 : #if AP_AHRS_EXTERNAL_ENABLED
957 133406 : if (active_backend == &external) {
958 : #if AP_AHRS_DCM_ENABLED
959 : // for external we return true if DCM is using compass..
960 67351 : return dcm.use_compass();
961 : #else
962 : return false;
963 : #endif
964 : }
965 : #endif
966 :
967 66055 : return active_backend->use_compass();
968 : }
969 :
970 124943093 : AP_AHRS_Backend::Estimates *AP_AHRS::estimates_for_type(EKFType type)
971 : {
972 124943093 : switch (type) {
973 : #if AP_AHRS_DCM_ENABLED
974 41724118 : case EKFType::DCM:
975 41724118 : return &dcm_estimates;
976 : #endif
977 :
978 : #if HAL_NAVEKF2_AVAILABLE
979 389239 : case EKFType::TWO:
980 389239 : return &ekf2_estimates;
981 : #endif
982 :
983 : #if HAL_NAVEKF3_AVAILABLE
984 75874086 : case EKFType::THREE:
985 75874086 : return &ekf3_estimates;
986 : #endif
987 :
988 : #if AP_AHRS_SIM_ENABLED
989 6821512 : case EKFType::SIM:
990 6821512 : return &sim_estimates;
991 : #endif
992 :
993 : #if AP_AHRS_EXTERNAL_ENABLED
994 134138 : case EKFType::EXTERNAL:
995 134138 : return &external_estimates;
996 : #endif
997 : }
998 0 : return nullptr;
999 : }
1000 :
1001 : // set the EKF's origin location in 10e7 degrees. This should only
1002 : // be called when the EKF has no absolute position reference (i.e. GPS)
1003 : // from which to decide the origin on its own
1004 7 : bool AP_AHRS::set_origin(const Location &loc)
1005 : {
1006 7 : WITH_SEMAPHORE(_rsem);
1007 : #if HAL_NAVEKF2_AVAILABLE
1008 7 : const bool ret2 = ekf2.set_origin(loc);
1009 : #endif
1010 : #if HAL_NAVEKF3_AVAILABLE
1011 7 : const bool ret3 = ekf3.set_origin(loc);
1012 : #endif
1013 : #if AP_AHRS_EXTERNAL_ENABLED
1014 7 : const bool ret_ext = external.set_origin(loc);
1015 : #endif
1016 :
1017 : // return success if active EKF's origin was set
1018 7 : bool success = false;
1019 7 : switch (active_EKF_type()) {
1020 : #if AP_AHRS_DCM_ENABLED
1021 3 : case EKFType::DCM:
1022 3 : break;
1023 : #endif
1024 :
1025 : #if HAL_NAVEKF2_AVAILABLE
1026 0 : case EKFType::TWO:
1027 0 : success = ret2;
1028 0 : break;
1029 : #endif
1030 :
1031 : #if HAL_NAVEKF3_AVAILABLE
1032 4 : case EKFType::THREE:
1033 4 : success = ret3;
1034 4 : break;
1035 : #endif
1036 :
1037 : #if AP_AHRS_SIM_ENABLED
1038 0 : case EKFType::SIM:
1039 : // never allow origin set in SITL. The origin is set by the
1040 : // simulation backend
1041 0 : break;
1042 : #endif
1043 : #if AP_AHRS_EXTERNAL_ENABLED
1044 0 : case EKFType::EXTERNAL:
1045 0 : success = ret_ext;
1046 0 : break;
1047 : #endif
1048 : }
1049 14 : return success;
1050 7 : }
1051 :
1052 : // Record the current valid origin to parameters
1053 : // This may save the user from having to set the origin manually when using position controlled modes without GPS
1054 964 : void AP_AHRS::record_origin()
1055 : {
1056 : // Only record origin if user has enabled using the record origin option
1057 964 : if (!option_set(Options::RECORD_ORIGIN)) {
1058 962 : return;
1059 : }
1060 2 : _origin_lat.set_and_save_ifchanged(state.origin.lat * 1.0e-7);
1061 2 : _origin_lon.set_and_save_ifchanged(state.origin.lng * 1.0e-7);
1062 2 : _origin_alt.set_and_save_ifchanged(state.origin.alt * 1.0e-2);
1063 : }
1064 :
1065 : // Set the origin to the last recorded location if option bit set and not using GPS
1066 : // This is useful for position controlled modes without GPS
1067 5099682 : void AP_AHRS::use_recorded_origin_maybe()
1068 : {
1069 : // exit immediately if origin is already set or option disabled
1070 5099682 : if (state.origin_ok || !option_set(Options::USE_RECORDED_ORIGIN_FOR_NONGPS)) {
1071 5099680 : return;
1072 : }
1073 :
1074 : // never use all zero origin
1075 364395 : if (is_zero(_origin_lat.get()) && is_zero(_origin_lon.get()) && is_zero(_origin_alt.get())) {
1076 363590 : return;
1077 : }
1078 :
1079 : // don't use recorded origin if the configured EKF uses GPS for
1080 : // position — GPS will set a correct origin when it gets
1081 : // a fix. Using the recorded origin here would prevent GPS from
1082 : // setting it later (EKF origin is immutable once set).
1083 805 : if (active_estimates->configured_to_use_gps_for_pos_XY) {
1084 803 : return;
1085 : }
1086 :
1087 : // try to set origin
1088 : const Location loc {
1089 2 : int32_t(_origin_lat.get() * 1e7),
1090 4 : int32_t(_origin_lon.get() * 1e7),
1091 4 : int32_t(_origin_alt.get() * 100),
1092 : Location::AltFrame::ABSOLUTE
1093 2 : };
1094 2 : if (set_origin(loc)) {
1095 2 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "AHRS: using recorded origin:%.7f,%.7f,%.1f",
1096 : (double)_origin_lat.get(), (double)_origin_lon.get(), (double)_origin_alt.get());
1097 : }
1098 : }
1099 :
1100 : #if AP_AHRS_POSITION_RESET_ENABLED
1101 2 : bool AP_AHRS::handle_external_position_estimate(const Location &loc, float pos_accuracy, uint32_t timestamp_ms)
1102 : {
1103 : #if HAL_NAVEKF3_AVAILABLE
1104 2 : return ekf3.EKF3.setLatLng(loc, pos_accuracy, timestamp_ms);
1105 : #endif
1106 : return false;
1107 : }
1108 : #endif
1109 :
1110 : // return true if inertial navigation is active
1111 9908048 : bool AP_AHRS::have_inertial_nav(void) const
1112 : {
1113 : #if AP_AHRS_DCM_ENABLED
1114 9908048 : return active_EKF_type() != EKFType::DCM;
1115 : #endif
1116 : return true;
1117 : }
1118 :
1119 : // get velocity down in m/s. This returns get_velocity_NED.z() if available, otherwise falls back to get_vert_pos_rate_D()
1120 : // if high_vibes is true then this is equivalent to get_vert_pos_rate_D
1121 22972943 : bool AP_AHRS::get_velocity_D(float &velD, bool high_vibes) const
1122 : {
1123 22972943 : Vector3f velNED;
1124 22972943 : if (!high_vibes && get_velocity_NED(velNED)) {
1125 22957189 : velD = velNED.z;
1126 22957189 : return true;
1127 : }
1128 15754 : return get_vert_pos_rate_D(velD);
1129 : }
1130 :
1131 : // Get a derivative of the vertical position which is kinematically consistent with the vertical position is required by some control loops.
1132 : // This is different to the vertical velocity from the EKF which is not always consistent with the vertical position due to the various errors that are being corrected for.
1133 2879287 : bool AP_AHRS::get_vert_pos_rate_D(float &velocity) const
1134 : {
1135 2879287 : return active_estimates->get_vert_pos_rate_D(velocity);
1136 : }
1137 :
1138 1186861 : bool AP_AHRS::get_relative_position_NED_origin_float(Vector3f &vec) const
1139 : {
1140 1186861 : Vector3p tmp_posNED;
1141 1186861 : if (!get_relative_position_NED_origin(tmp_posNED)) {
1142 188629 : return false;
1143 : }
1144 998232 : vec = tmp_posNED.tofloat();
1145 998232 : return true;
1146 : }
1147 :
1148 : /*
1149 : return a relative ground position from home in meters
1150 : */
1151 27237 : bool AP_AHRS::get_relative_position_NED_home(Vector3f &vec) const
1152 : {
1153 27237 : Location loc;
1154 54474 : if (!_home_is_set ||
1155 27237 : !get_location(loc)) {
1156 0 : return false;
1157 : }
1158 27237 : vec = _home.get_distance_NED(loc);
1159 27237 : return true;
1160 : }
1161 :
1162 11398220 : bool AP_AHRS::get_relative_position_NE_origin_float(Vector2f &posNE) const
1163 : {
1164 11398220 : Vector2p tmp_posNE;
1165 11398220 : if (!get_relative_position_NE_origin(tmp_posNE)) {
1166 447836 : return false;
1167 : }
1168 10950384 : posNE = tmp_posNE.tofloat();
1169 10950384 : return true;
1170 : }
1171 :
1172 : /*
1173 : return a relative ground position from home in meters North/East
1174 : */
1175 267973 : bool AP_AHRS::get_relative_position_NE_home(Vector2f &posNE) const
1176 : {
1177 267973 : Location loc;
1178 533887 : if (!_home_is_set ||
1179 265914 : !get_location(loc)) {
1180 2059 : return false;
1181 : }
1182 :
1183 265914 : posNE = _home.get_distance_NE(loc);
1184 265914 : return true;
1185 : }
1186 :
1187 55259479 : bool AP_AHRS::get_relative_position_D_origin_float(float &posD) const
1188 : {
1189 : postype_t tmp_posD;
1190 55259479 : if (!get_relative_position_D_origin(tmp_posD)) {
1191 3507041 : return false;
1192 : }
1193 51752438 : posD = float(tmp_posD);
1194 51752438 : return true;
1195 : }
1196 :
1197 : /*
1198 : return relative position from home in meters
1199 : */
1200 8543040 : void AP_AHRS::get_relative_position_D_home(float &posD) const
1201 : {
1202 8543040 : if (!_home_is_set) {
1203 : // fall back to an altitude derived from barometric pressure
1204 : // differences vs a calibrated ground pressure:
1205 346133 : posD = -AP::baro().get_altitude();
1206 346133 : return;
1207 : }
1208 :
1209 8196907 : Location originLLH;
1210 : postype_t originD;
1211 16392769 : if (!get_relative_position_D_origin(originD) ||
1212 8195862 : !_get_origin(originLLH)) {
1213 : #if AP_GPS_ENABLED
1214 267126 : const auto &gps = AP::gps();
1215 267126 : if (_gps_use == GPSUse::EnableWithHeight &&
1216 0 : gps.status() >= AP_GPS_FixType::FIX_3D) {
1217 0 : posD = (_home.alt - gps.location().alt) * 0.01;
1218 0 : return;
1219 : }
1220 : #endif
1221 267126 : posD = -AP::baro().get_altitude();
1222 267126 : return;
1223 : }
1224 :
1225 7929781 : posD = originD - ((originLLH.alt - _home.alt) * 0.01f);
1226 7929781 : return;
1227 : }
1228 :
1229 : /*
1230 : canonicalise _ekf_type, forcing it to be 0, 2 or 3
1231 : type 1 has been deprecated
1232 : */
1233 41690904 : AP_AHRS::EKFType AP_AHRS::_configured_ekf_type(void) const
1234 : {
1235 41690904 : EKFType type = (EKFType)_ekf_type.get();
1236 41690904 : switch (type) {
1237 : #if AP_AHRS_SIM_ENABLED
1238 3410756 : case EKFType::SIM:
1239 3410756 : return type;
1240 : #endif
1241 : #if AP_AHRS_EXTERNAL_ENABLED
1242 67069 : case EKFType::EXTERNAL:
1243 67069 : return type;
1244 : #endif
1245 : #if HAL_NAVEKF2_AVAILABLE
1246 146417 : case EKFType::TWO:
1247 146417 : return type;
1248 : #endif
1249 : #if HAL_NAVEKF3_AVAILABLE
1250 37937043 : case EKFType::THREE:
1251 37937043 : return type;
1252 : #endif
1253 : #if AP_AHRS_DCM_ENABLED
1254 33214 : case EKFType::DCM:
1255 33214 : if (always_use_EKF()) {
1256 : #if HAL_NAVEKF2_AVAILABLE
1257 0 : return EKFType::TWO;
1258 : #elif HAL_NAVEKF3_AVAILABLE
1259 0 : return EKFType::THREE;
1260 : #endif
1261 : }
1262 33214 : return EKFType::DCM;
1263 : #endif
1264 : }
1265 : // we can get to here if the user has mis-set AHRS_EKF_TYPE - any
1266 : // value above 3 will get to here. TWO is returned here for no
1267 : // better reason than "tradition".
1268 : #if HAL_NAVEKF2_AVAILABLE
1269 96405 : return EKFType::TWO;
1270 : #elif HAL_NAVEKF3_AVAILABLE
1271 0 : return EKFType::THREE;
1272 : #elif AP_AHRS_DCM_ENABLED
1273 : return EKFType::DCM;
1274 : #else
1275 : #error "no default backend available"
1276 : #endif
1277 : }
1278 :
1279 41690904 : AP_AHRS::EKFType AP_AHRS::_active_EKF_type(void) const
1280 : {
1281 41690904 : EKFType ret = fallback_active_EKF_type();
1282 :
1283 41690904 : switch (configured_ekf_type()) {
1284 : #if AP_AHRS_DCM_ENABLED
1285 33214 : case EKFType::DCM:
1286 33214 : return EKFType::DCM;
1287 : #endif
1288 :
1289 : #if HAL_NAVEKF2_AVAILABLE
1290 242822 : case EKFType::TWO: {
1291 : // do we have an EKF2 yet?
1292 242822 : if (!ekf2.started) {
1293 102306 : return fallback_active_EKF_type();
1294 : }
1295 140516 : if (always_use_EKF()) {
1296 135211 : if (ekf2_estimates.filter_faults == 0) {
1297 135211 : ret = EKFType::TWO;
1298 : }
1299 5305 : } else if (ekf2_estimates.healthy) {
1300 5017 : ret = EKFType::TWO;
1301 : }
1302 140516 : break;
1303 : }
1304 : #endif
1305 :
1306 : #if HAL_NAVEKF3_AVAILABLE
1307 37937043 : case EKFType::THREE: {
1308 : // do we have an EKF3 yet?
1309 37937043 : if (!ekf3.started) {
1310 1227373 : return fallback_active_EKF_type();
1311 : }
1312 36709670 : if (always_use_EKF()) {
1313 28866369 : if (ekf3_estimates.filter_faults == 0) {
1314 28866369 : ret = EKFType::THREE;
1315 : }
1316 7843301 : } else if (ekf3_estimates.healthy) {
1317 7794966 : ret = EKFType::THREE;
1318 : }
1319 36709670 : break;
1320 : }
1321 : #endif
1322 :
1323 : #if AP_AHRS_SIM_ENABLED
1324 3410756 : case EKFType::SIM:
1325 3410756 : ret = EKFType::SIM;
1326 3410756 : break;
1327 : #endif
1328 : #if AP_AHRS_EXTERNAL_ENABLED
1329 67069 : case EKFType::EXTERNAL:
1330 67069 : ret = EKFType::EXTERNAL;
1331 67069 : break;
1332 : #endif
1333 : }
1334 :
1335 : #if AP_AHRS_DCM_ENABLED
1336 : // Handle fallback for fixed wing planes (including VTOL's) and ground vehicles.
1337 40328011 : if (_vehicle_class == VehicleClass::FIXED_WING ||
1338 32470491 : _vehicle_class == VehicleClass::GROUND) {
1339 8462025 : bool should_use_gps = true;
1340 8462025 : nav_filter_status filt_state {};
1341 8462025 : switch (ret) {
1342 47017 : case EKFType::DCM:
1343 : // already using DCM
1344 47017 : break;
1345 : #if HAL_NAVEKF2_AVAILABLE
1346 5017 : case EKFType::TWO:
1347 5017 : filt_state = ekf2_estimates.filter_status;
1348 5017 : should_use_gps = ekf2.EKF2.configuredToUseGPSForPosXY();
1349 5017 : break;
1350 : #endif
1351 : #if HAL_NAVEKF3_AVAILABLE
1352 7713723 : case EKFType::THREE:
1353 7713723 : filt_state = ekf3_estimates.filter_status;
1354 7713723 : should_use_gps = ekf3.EKF3.configuredToUseGPSForPosXY();
1355 7713723 : break;
1356 : #endif
1357 : #if AP_AHRS_SIM_ENABLED
1358 645420 : case EKFType::SIM:
1359 645420 : filt_state = sim_estimates.filter_status;
1360 645420 : break;
1361 : #endif
1362 : #if AP_AHRS_EXTERNAL_ENABLED
1363 50848 : case EKFType::EXTERNAL:
1364 50848 : filt_state = external_estimates.filter_status;
1365 50848 : should_use_gps = true;
1366 50848 : break;
1367 : #endif
1368 : }
1369 :
1370 : // Handle fallback for the case where the DCM or EKF is unable to provide attitude or height data.
1371 8462025 : const bool can_use_dcm = dcm.yaw_source_available() || fly_forward;
1372 : // ground vehicles only require attitude from the EKF, other vehicles also require vertical velocity and position
1373 16836589 : const bool can_use_ekf = filt_state.flags.attitude &&
1374 8374564 : ((_vehicle_class == VehicleClass::GROUND) ||
1375 7785831 : (filt_state.flags.vert_vel && filt_state.flags.vert_pos));
1376 8462025 : if (!can_use_dcm && can_use_ekf) {
1377 : // no choice - continue to use EKF
1378 1249825 : return ret;
1379 8457431 : } else if (!can_use_ekf) {
1380 : // No choice - we have to use DCM
1381 87555 : return EKFType::DCM;
1382 : }
1383 :
1384 8369876 : const bool disable_dcm_fallback = fly_forward?
1385 8369876 : option_set(Options::DISABLE_DCM_FALLBACK_FW) : option_set(Options::DISABLE_DCM_FALLBACK_VTOL);
1386 8369876 : if (disable_dcm_fallback) {
1387 : // don't fallback
1388 618800 : return ret;
1389 : }
1390 :
1391 : // Handle loss of global position when we still have a GPS fix
1392 14053457 : if (hal.util->get_soft_armed() &&
1393 12604762 : (_gps_use != GPSUse::Disable) &&
1394 6302381 : should_use_gps &&
1395 20355838 : AP::gps().status() >= AP_GPS_FixType::FIX_3D &&
1396 6296947 : (!filt_state.flags.using_gps || !filt_state.flags.horiz_pos_abs)) {
1397 : /*
1398 : If the EKF is not fusing GPS or doesn't have a 2D fix and we have a 3D GPS lock,
1399 : then plane and rover would prefer to use the GPS position from DCM unless the
1400 : fallback has been inhibited by the user.
1401 : Note: The aircraft could be dead reckoning with acceptable accuracy and rejecting a bad GPS
1402 : Note: This is a last resort fallback and makes the navigation highly vulnerable to GPS noise.
1403 : Note: When operating in a VTOL flight mode that actively controls height such as QHOVER,
1404 : the EKF gives better vertical velocity and position estimates and height control characteristics.
1405 : */
1406 3131 : return EKFType::DCM;
1407 : }
1408 :
1409 : // Handle complete loss of navigation
1410 7747945 : if (hal.util->get_soft_armed() && filt_state.flags.const_pos_mode) {
1411 : /*
1412 : Provided the EKF has been configured to use GPS, ie should_use_gps is true, then the
1413 : key difference to the case handled above is only the absence of a GPS fix which means
1414 : that DCM will not be able to navigate either so we are primarily concerned with
1415 : providing an attitude, vertical position and vertical velocity estimate.
1416 : */
1417 0 : return EKFType::DCM;
1418 : }
1419 :
1420 7747945 : if (!filt_state.flags.horiz_vel ||
1421 7744703 : (!filt_state.flags.horiz_pos_abs && !filt_state.flags.horiz_pos_rel)) {
1422 539495 : if ((!AP::compass().use_for_yaw()) &&
1423 539495 : AP::gps().status() >= AP_GPS_FixType::FIX_3D &&
1424 3750 : AP::gps().ground_speed() < 2) {
1425 : /*
1426 : special handling for non-compass mode when sitting
1427 : still. The EKF may not yet have aligned its yaw. We
1428 : accept EKF as healthy to allow arming. Once we reach
1429 : speed the EKF should get yaw alignment
1430 : */
1431 3243 : if (filt_state.flags.gps_quality_good) {
1432 320 : return ret;
1433 : }
1434 : }
1435 535425 : return EKFType::DCM;
1436 : }
1437 : }
1438 : #endif
1439 :
1440 39078186 : return ret;
1441 : }
1442 :
1443 82720877 : AP_AHRS::EKFType AP_AHRS::fallback_active_EKF_type(void) const
1444 : {
1445 : #if AP_AHRS_DCM_ENABLED
1446 82720877 : return EKFType::DCM;
1447 : #endif
1448 :
1449 : #if HAL_NAVEKF3_AVAILABLE
1450 : if (ekf3.started) {
1451 : return EKFType::THREE;
1452 : }
1453 : #endif
1454 :
1455 : #if HAL_NAVEKF2_AVAILABLE
1456 : if (ekf2.started) {
1457 : return EKFType::TWO;
1458 : }
1459 : #endif
1460 :
1461 : #if AP_AHRS_EXTERNAL_ENABLED
1462 : if (external_estimates.healthy) {
1463 : return EKFType::EXTERNAL;
1464 : }
1465 : #endif
1466 :
1467 : // so nobody is ready yet. Return something, even if it is not ready:
1468 : #if HAL_NAVEKF3_AVAILABLE
1469 : return EKFType::THREE;
1470 : #elif HAL_NAVEKF2_AVAILABLE
1471 : return EKFType::TWO;
1472 : #elif AP_AHRS_EXTERNAL_ENABLED
1473 : return EKFType::EXTERNAL;
1474 : #endif
1475 : }
1476 :
1477 : // get secondary EKF type. returns false if no secondary (i.e. only using DCM)
1478 41690904 : bool AP_AHRS::_get_secondary_EKF_type(EKFType &secondary_ekf_type) const
1479 : {
1480 :
1481 41690904 : switch (active_EKF_type()) {
1482 : #if AP_AHRS_DCM_ENABLED
1483 1990610 : case EKFType::DCM:
1484 : // EKF2, EKF3 or External is secondary
1485 : #if HAL_NAVEKF3_AVAILABLE
1486 1990610 : if ((EKFType)_ekf_type.get() == EKFType::THREE) {
1487 1849557 : secondary_ekf_type = EKFType::THREE;
1488 1849557 : return true;
1489 : }
1490 : #endif
1491 : #if HAL_NAVEKF2_AVAILABLE
1492 141053 : if ((EKFType)_ekf_type.get() == EKFType::TWO) {
1493 10437 : secondary_ekf_type = EKFType::TWO;
1494 10437 : return true;
1495 : }
1496 : #endif
1497 : #if AP_AHRS_EXTERNAL_ENABLED
1498 130616 : if ((EKFType)_ekf_type.get() == EKFType::EXTERNAL) {
1499 997 : secondary_ekf_type = EKFType::EXTERNAL;
1500 997 : return true;
1501 : }
1502 : #endif
1503 129619 : return false;
1504 : #endif
1505 : #if HAL_NAVEKF2_AVAILABLE
1506 39700294 : case EKFType::TWO:
1507 : #endif
1508 : #if HAL_NAVEKF3_AVAILABLE
1509 0 : case EKFType::THREE:
1510 : #endif
1511 : #if AP_AHRS_SIM_ENABLED
1512 : case EKFType::SIM:
1513 : #endif
1514 : #if AP_AHRS_EXTERNAL_ENABLED
1515 : case EKFType::EXTERNAL:
1516 : #endif
1517 : // DCM is secondary
1518 39700294 : secondary_ekf_type = fallback_active_EKF_type();
1519 39700294 : return true;
1520 : }
1521 :
1522 : // since there is no default case above, this is unreachable
1523 0 : return false;
1524 : }
1525 :
1526 : /*
1527 : check if the AHRS subsystem is healthy
1528 : */
1529 1167760 : bool AP_AHRS::healthy(void) const
1530 : {
1531 1167760 : if (!configured_estimates->healthy) {
1532 2869 : return false;
1533 : }
1534 :
1535 : // we must be using the configured type to be considered healthy:
1536 1164891 : if (configured_backend != active_backend) {
1537 12996 : return false;
1538 : }
1539 :
1540 1151895 : return true;
1541 : }
1542 :
1543 : // returns false if we fail arming checks, in which case the buffer will be populated with a failure message
1544 : // requires_position should be true if horizontal position configuration should be checked
1545 109571 : bool AP_AHRS::pre_arm_check(bool requires_position, char *failure_msg, uint8_t failure_msg_len) const
1546 : {
1547 109571 : bool ret = true;
1548 109571 : if (!healthy()) {
1549 : // this rather generic failure might be overwritten by
1550 : // something more specific in the "backend"
1551 7561 : hal.util->snprintf(failure_msg, failure_msg_len, "Not healthy");
1552 7561 : ret = false;
1553 : }
1554 :
1555 : #if AP_AHRS_EXTERNAL_ENABLED
1556 : // Always check external AHRS if enabled
1557 : // it is a source for IMU data even if not being used as direct AHRS replacement
1558 109571 : if (AP::externalAHRS().enabled() || (configured_ekf_type() == EKFType::EXTERNAL)) {
1559 66685 : if (!AP::externalAHRS().pre_arm_check(failure_msg, failure_msg_len)) {
1560 35 : return false;
1561 : }
1562 : }
1563 : #endif
1564 :
1565 109536 : if (!attitudes_consistent(failure_msg, failure_msg_len)) {
1566 1359 : return false;
1567 : }
1568 :
1569 : // ensure we're using the configured backend, but bypass in compass-less cases:
1570 222491 : if (_ekf_type != int8_t(configured_ekf_type()) ||
1571 114314 : (configured_ekf_type() != active_EKF_type() && AP::compass().use_for_yaw())) {
1572 6105 : hal.util->snprintf(failure_msg, failure_msg_len, "not using configured AHRS type");
1573 6105 : return false;
1574 : }
1575 :
1576 102072 : return (configured_backend->pre_arm_check(requires_position, failure_msg, failure_msg_len) && ret);
1577 : }
1578 :
1579 : // write optical flow data to EKF
1580 10128 : void AP_AHRS::writeOptFlowMeas(const uint8_t rawFlowQuality, const Vector2f &rawFlowRates, const Vector2f &rawGyroRates, const uint32_t msecFlowMeas, const Vector3f &posOffset, float heightOverride)
1581 : {
1582 : #if HAL_NAVEKF2_AVAILABLE
1583 10128 : ekf2.EKF2.writeOptFlowMeas(rawFlowQuality, rawFlowRates, rawGyroRates, msecFlowMeas, posOffset, heightOverride);
1584 : #endif
1585 : #if EK3_FEATURE_OPTFLOW_FUSION
1586 10128 : ekf3.EKF3.writeOptFlowMeas(rawFlowQuality, rawFlowRates, rawGyroRates, msecFlowMeas, posOffset, heightOverride);
1587 : #endif
1588 10128 : }
1589 :
1590 : // retrieve latest corrected optical flow samples (used for calibration)
1591 11051 : bool AP_AHRS::getOptFlowSample(uint32_t& timeStamp_ms, Vector2f& flowRate, Vector2f& bodyRate, Vector2f& losPred) const
1592 : {
1593 : #if EK3_FEATURE_OPTFLOW_FUSION
1594 11051 : return ekf3.EKF3.getOptFlowSample(timeStamp_ms, flowRate, bodyRate, losPred);
1595 : #endif
1596 : return false;
1597 : }
1598 :
1599 : // write body frame odometry measurements to the EKF
1600 3633 : void AP_AHRS::writeBodyFrameOdom(float quality, const Vector3f &delPos, const Vector3f &delAng, float delTime, uint32_t timeStamp_ms, uint16_t delay_ms, const Vector3f &posOffset)
1601 : {
1602 : #if HAL_NAVEKF3_AVAILABLE
1603 3633 : ekf3.EKF3.writeBodyFrameOdom(quality, delPos, delAng, delTime, timeStamp_ms, delay_ms, posOffset);
1604 : #endif
1605 3633 : }
1606 :
1607 : // Write position and quaternion data from an external navigation system
1608 10069 : void AP_AHRS::writeExtNavData(const Vector3f &pos, const Quaternion &quat, float posErr, float angErr, uint32_t timeStamp_ms, uint16_t delay_ms, uint32_t resetTime_ms)
1609 : {
1610 : #if HAL_NAVEKF2_AVAILABLE
1611 10069 : ekf2.EKF2.writeExtNavData(pos, quat, posErr, angErr, timeStamp_ms, delay_ms, resetTime_ms);
1612 : #endif
1613 : #if HAL_NAVEKF3_AVAILABLE
1614 10069 : ekf3.EKF3.writeExtNavData(pos, quat, posErr, angErr, timeStamp_ms, delay_ms, resetTime_ms);
1615 : #endif
1616 10069 : }
1617 :
1618 : // Writes the default equivalent airspeed and 1-sigma uncertainty in m/s to be used in forward flight if a measured airspeed is required and not available.
1619 0 : void AP_AHRS::writeDefaultAirSpeed(float airspeed, float uncertainty)
1620 : {
1621 : #if HAL_NAVEKF2_AVAILABLE
1622 0 : ekf2.EKF2.writeDefaultAirSpeed(airspeed);
1623 : #endif
1624 : #if HAL_NAVEKF3_AVAILABLE
1625 0 : ekf3.EKF3.writeDefaultAirSpeed(airspeed, uncertainty);
1626 : #endif
1627 0 : }
1628 :
1629 : // Write velocity data from an external navigation system
1630 4278 : void AP_AHRS::writeExtNavVelData(const Vector3f &vel, float err, uint32_t timeStamp_ms, uint16_t delay_ms)
1631 : {
1632 : #if HAL_NAVEKF2_AVAILABLE
1633 4278 : ekf2.EKF2.writeExtNavVelData(vel, err, timeStamp_ms, delay_ms);
1634 : #endif
1635 : #if HAL_NAVEKF3_AVAILABLE
1636 4278 : ekf3.EKF3.writeExtNavVelData(vel, err, timeStamp_ms, delay_ms);
1637 : #endif
1638 4278 : }
1639 :
1640 : // set the terrain SRTM altitude in meters above sea level
1641 : // only used by optical flow when out of rangefinder range
1642 : // Write terrain (derived from SRTM) altitude in meters above sea level
1643 1127469 : void AP_AHRS::writeTerrainAMSL(float alt_amsl_m)
1644 : {
1645 : #if EK3_FEATURE_OPTFLOW_SRTM
1646 : // return immediately if EKF origin has not been set
1647 1127469 : if (!state.origin_ok) {
1648 62480 : return;
1649 : }
1650 : // convert from amsl alt to alt above EKF origin
1651 1064989 : const float alt_above_origin_m = alt_amsl_m - (state.origin.alt * 0.01f);
1652 1064989 : ekf3.EKF3.writeTerrainData(alt_above_origin_m);
1653 : #endif
1654 : }
1655 :
1656 : // Retrieves the NED delta velocity corrected
1657 41686716 : bool AP_AHRS::_getCorrectedDeltaVelocityNED(Vector3f& ret, float& dt) const
1658 : {
1659 41686716 : if (!AP::ins().get_delta_velocity(active_estimates->primary_accel, ret, dt)) {
1660 0 : return false;
1661 : }
1662 41686716 : ret -= active_estimates->accel_bias * dt;
1663 41686716 : ret = state.dcm_matrix * get_rotation_autopilot_body_to_vehicle_body() * ret;
1664 41686716 : ret.z += GRAVITY_MSS*dt;
1665 41686716 : return true;
1666 : }
1667 :
1668 1359 : void AP_AHRS::set_failure_inconsistent_message(const char *estimator, const char *axis, float diff_rad, char *failure_msg, const uint8_t failure_msg_len) const
1669 : {
1670 1359 : hal.util->snprintf(failure_msg, failure_msg_len, "%s %s inconsistent %d deg. Wait or reboot", estimator, axis, (int)degrees(diff_rad));
1671 1359 : }
1672 :
1673 : // check all cores providing consistent attitudes for prearm checks
1674 109536 : bool AP_AHRS::attitudes_consistent(char *failure_msg, const uint8_t failure_msg_len) const
1675 : {
1676 : // get primary attitude source's attitude as quaternion
1677 109536 : Quaternion primary_quat;
1678 109536 : get_quat_body_to_ned(primary_quat);
1679 : // only check yaw if compasses are being used
1680 109536 : const bool check_yaw = AP::compass().use_for_yaw();
1681 109536 : uint8_t total_ekf_cores = 0;
1682 :
1683 : #if HAL_NAVEKF2_AVAILABLE
1684 : // check primary vs ekf2
1685 109536 : if (configured_ekf_type() == EKFType::TWO || active_EKF_type() == EKFType::TWO) {
1686 690 : for (uint8_t i = 0; i < ekf2.EKF2.activeCores(); i++) {
1687 459 : Quaternion ekf2_quat;
1688 459 : ekf2.EKF2.getQuaternionBodyToNED(i, ekf2_quat);
1689 :
1690 : // check roll and pitch difference
1691 459 : const float rp_diff_rad = primary_quat.roll_pitch_difference(ekf2_quat);
1692 459 : if (rp_diff_rad > ATTITUDE_CHECK_THRESH_ROLL_PITCH_RAD) {
1693 0 : set_failure_inconsistent_message("EKF2", "Roll/Pitch", rp_diff_rad, failure_msg, failure_msg_len);
1694 3 : return false;
1695 : }
1696 :
1697 : // check yaw difference
1698 459 : Vector3f angle_diff;
1699 459 : primary_quat.angular_difference(ekf2_quat).to_axis_angle(angle_diff);
1700 459 : const float yaw_diff = fabsf(angle_diff.z);
1701 459 : if (check_yaw && (yaw_diff > ATTITUDE_CHECK_THRESH_YAW_RAD)) {
1702 3 : set_failure_inconsistent_message("EKF2", "Yaw", yaw_diff, failure_msg, failure_msg_len);
1703 3 : return false;
1704 : }
1705 : }
1706 231 : total_ekf_cores = ekf2.EKF2.activeCores();
1707 : }
1708 : #endif
1709 :
1710 : #if HAL_NAVEKF3_AVAILABLE
1711 : // check primary vs ekf3
1712 109533 : if (configured_ekf_type() == EKFType::THREE || active_EKF_type() == EKFType::THREE) {
1713 111229 : for (uint8_t i = 0; i < ekf3.EKF3.activeCores(); i++) {
1714 73821 : Quaternion ekf3_quat;
1715 73821 : ekf3.EKF3.getQuaternionBodyToNED(i, ekf3_quat);
1716 :
1717 : // check roll and pitch difference
1718 73821 : const float rp_diff_rad = primary_quat.roll_pitch_difference(ekf3_quat);
1719 73821 : if (rp_diff_rad > ATTITUDE_CHECK_THRESH_ROLL_PITCH_RAD) {
1720 21 : set_failure_inconsistent_message("EKF3", "Roll/Pitch", rp_diff_rad, failure_msg, failure_msg_len);
1721 792 : return false;
1722 : }
1723 :
1724 : // check yaw difference
1725 73800 : Vector3f angle_diff;
1726 73800 : primary_quat.angular_difference(ekf3_quat).to_axis_angle(angle_diff);
1727 73800 : const float yaw_diff = fabsf(angle_diff.z);
1728 73800 : if (check_yaw && (yaw_diff > ATTITUDE_CHECK_THRESH_YAW_RAD)) {
1729 771 : set_failure_inconsistent_message("EKF3", "Yaw", yaw_diff, failure_msg, failure_msg_len);
1730 771 : return false;
1731 : }
1732 : }
1733 37408 : total_ekf_cores += ekf3.EKF3.activeCores();
1734 : }
1735 : #endif
1736 :
1737 : #if AP_AHRS_DCM_ENABLED
1738 : // check primary vs dcm
1739 108741 : if (!always_use_EKF() || (total_ekf_cores == 1)) {
1740 19034 : Quaternion dcm_quat;
1741 19034 : dcm_quat.from_rotation_matrix(get_DCM_rotation_body_to_ned());
1742 :
1743 : // check roll and pitch difference
1744 19034 : const float rp_diff_rad = primary_quat.roll_pitch_difference(dcm_quat);
1745 19034 : if (rp_diff_rad > ATTITUDE_CHECK_THRESH_ROLL_PITCH_RAD) {
1746 530 : set_failure_inconsistent_message("DCM", "Roll/Pitch", rp_diff_rad, failure_msg, failure_msg_len);
1747 564 : return false;
1748 : }
1749 :
1750 : // Check vs DCM yaw if this vehicle could use DCM in flight
1751 : // and if not using an external yaw source (DCM does not support external yaw sources)
1752 18504 : bool using_noncompass_for_yaw = false;
1753 : #if HAL_NAVEKF3_AVAILABLE
1754 18504 : using_noncompass_for_yaw = (configured_ekf_type() == EKFType::THREE) && ekf3.EKF3.using_noncompass_for_yaw();
1755 : #endif
1756 18504 : if (!always_use_EKF() && !using_noncompass_for_yaw) {
1757 18370 : Vector3f angle_diff;
1758 18370 : primary_quat.angular_difference(dcm_quat).to_axis_angle(angle_diff);
1759 18370 : const float yaw_diff = fabsf(angle_diff.z);
1760 18370 : if (check_yaw && (yaw_diff > ATTITUDE_CHECK_THRESH_YAW_RAD)) {
1761 34 : set_failure_inconsistent_message("DCM", "Yaw", yaw_diff, failure_msg, failure_msg_len);
1762 34 : return false;
1763 : }
1764 : }
1765 : }
1766 : #endif
1767 :
1768 108177 : return true;
1769 : }
1770 :
1771 : // Resets the baro so that it reads zero at the current height
1772 : // Resets the EKF height to zero
1773 : // Adjusts the EKf origin height so that the EKF height + origin height is the same as before
1774 3121 : void AP_AHRS::resetHeightDatum(void)
1775 : {
1776 : // support locked access functions to AHRS data
1777 3121 : WITH_SEMAPHORE(_rsem);
1778 :
1779 18726 : for (auto &backend_and_estimates : backends_and_estimates) {
1780 15605 : backend_and_estimates.backend.resetHeightDatum();
1781 : }
1782 3121 : }
1783 :
1784 : #if HAL_GCS_ENABLED
1785 : // send a EKF_STATUS_REPORT for configured EKF
1786 1117755 : void AP_AHRS::send_ekf_status_report(GCS_MAVLINK &link) const
1787 : {
1788 : // get filter status
1789 1117755 : if (!configured_estimates->filter_status_valid) {
1790 17862 : return;
1791 : }
1792 :
1793 : // get variances
1794 1112214 : if (!configured_estimates->variances_valid) {
1795 12321 : return;
1796 : }
1797 :
1798 1099893 : if (!configured_estimates->terrain_alt_variance_valid) {
1799 0 : return;
1800 : }
1801 :
1802 : // prepare flags
1803 1099893 : uint16_t flags = 0;
1804 1099893 : if (configured_estimates->filter_status.flags.attitude) {
1805 1054318 : flags |= EKF_ATTITUDE;
1806 : }
1807 1099893 : if (configured_estimates->filter_status.flags.horiz_vel) {
1808 1052819 : flags |= EKF_VELOCITY_HORIZ;
1809 : }
1810 1099893 : if (configured_estimates->filter_status.flags.vert_vel) {
1811 1054199 : flags |= EKF_VELOCITY_VERT;
1812 : }
1813 1099893 : if (configured_estimates->filter_status.flags.horiz_pos_rel) {
1814 903642 : flags |= EKF_POS_HORIZ_REL;
1815 : }
1816 1099893 : if (configured_estimates->filter_status.flags.horiz_pos_abs) {
1817 898406 : flags |= EKF_POS_HORIZ_ABS;
1818 : }
1819 1099893 : if (configured_estimates->filter_status.flags.vert_pos) {
1820 1053503 : flags |= EKF_POS_VERT_ABS;
1821 : }
1822 1099893 : if (configured_estimates->filter_status.flags.terrain_alt) {
1823 126783 : flags |= EKF_POS_VERT_AGL;
1824 : }
1825 1099893 : if (configured_estimates->filter_status.flags.const_pos_mode) {
1826 149358 : flags |= EKF_CONST_POS_MODE;
1827 : }
1828 1099893 : if (configured_estimates->filter_status.flags.pred_horiz_pos_rel) {
1829 903644 : flags |= EKF_PRED_POS_HORIZ_REL;
1830 : }
1831 1099893 : if (configured_estimates->filter_status.flags.pred_horiz_pos_abs) {
1832 898408 : flags |= EKF_PRED_POS_HORIZ_ABS;
1833 : }
1834 1099893 : if (!configured_estimates->filter_status.flags.initalized) {
1835 40580 : flags |= EKF_UNINITIALIZED;
1836 : }
1837 1099893 : if (configured_estimates->filter_status.flags.gps_glitching) {
1838 2427 : flags |= (1<<15);
1839 : }
1840 :
1841 : const mavlink_ekf_status_report_t packet{
1842 1099893 : configured_estimates->velVar,
1843 1099893 : configured_estimates->posVar,
1844 1099893 : configured_estimates->hgtVar,
1845 1099893 : fmaxf(fmaxf(configured_estimates->magVar.x, configured_estimates->magVar.y), configured_estimates->magVar.z),
1846 1099893 : configured_estimates->terrain_alt_variance,
1847 : flags,
1848 1099893 : configured_estimates->tasVar
1849 1099893 : };
1850 :
1851 : // send message
1852 1099893 : mavlink_msg_ekf_status_report_send_struct(link.get_chan(), &packet);
1853 : }
1854 : #endif // HAL_GCS_ENABLED
1855 :
1856 : // return origin for a specified EKF type
1857 50121474 : bool AP_AHRS::_get_origin(EKFType type, Location &ret) const
1858 : {
1859 50121474 : auto *backend = backend_for_type(type);
1860 50121474 : if (backend == nullptr) {
1861 : // internal error?
1862 0 : return false;
1863 : }
1864 :
1865 50121474 : return backend->get_origin(ret);
1866 : }
1867 :
1868 : /*
1869 : return origin for the configured EKF type. If we are armed and the
1870 : configured EKF type cannot return an origin then return origin for
1871 : the active EKF type (if available)
1872 :
1873 : This copes with users force arming a plane that is running on DCM as
1874 : the EKF has not fully initialised
1875 : */
1876 49882578 : bool AP_AHRS::_get_origin(Location &ret) const
1877 : {
1878 49882578 : if (_get_origin(configured_ekf_type(), ret)) {
1879 44369083 : return true;
1880 : }
1881 5513495 : if (hal.util->get_soft_armed() && _get_origin(active_EKF_type(), ret)) {
1882 134820 : return true;
1883 : }
1884 5378675 : return false;
1885 : }
1886 :
1887 6762 : bool AP_AHRS::set_home(const Location &loc)
1888 : {
1889 6762 : WITH_SEMAPHORE(_rsem);
1890 : // check location is valid
1891 6762 : if (!loc.initialised()) {
1892 0 : return false;
1893 : }
1894 6762 : if (!loc.check_latlng()) {
1895 0 : return false;
1896 : }
1897 : // home must always be global frame at the moment as .alt is
1898 : // accessed directly by the vehicles and they may not be rigorous
1899 : // in checking the frame type.
1900 6762 : Location tmp = loc;
1901 6762 : if (!tmp.change_alt_frame(Location::AltFrame::ABSOLUTE)) {
1902 0 : return false;
1903 : }
1904 :
1905 : #if !APM_BUILD_TYPE(APM_BUILD_UNKNOWN) && HAL_LOGGING_ENABLED
1906 6758 : if (!_home_is_set) {
1907 : // record home is set
1908 1007 : AP::logger().Write_Event(LogEvent::SET_HOME);
1909 : }
1910 : #endif
1911 :
1912 6762 : _home = tmp;
1913 6762 : _home_is_set = true;
1914 :
1915 : #if HAL_LOGGING_ENABLED
1916 6762 : Log_Write_Home_And_Origin();
1917 : #endif
1918 :
1919 : // send new home and ekf origin to GCS
1920 6762 : GCS_SEND_MESSAGE(MSG_HOME);
1921 6762 : GCS_SEND_MESSAGE(MSG_ORIGIN);
1922 :
1923 6762 : AP_HAL::Util::PersistentData &pd = hal.util->persistent_data;
1924 6762 : pd.home_lat = loc.lat;
1925 6762 : pd.home_lon = loc.lng;
1926 6762 : pd.home_alt_cm = loc.alt;
1927 :
1928 : #if AP_MISSION_ENABLED
1929 : // Save home to mission
1930 6762 : AP::mission().write_home_to_storage();
1931 : #endif
1932 :
1933 6762 : return true;
1934 6762 : }
1935 :
1936 : /* if this was a watchdog reset then get home from backup registers */
1937 2086 : void AP_AHRS::load_watchdog_home()
1938 : {
1939 2086 : const AP_HAL::Util::PersistentData &pd = hal.util->persistent_data;
1940 2086 : if (hal.util->was_watchdog_reset() && (pd.home_lat != 0 || pd.home_lon != 0)) {
1941 1 : _home.lat = pd.home_lat;
1942 1 : _home.lng = pd.home_lon;
1943 1 : _home.set_alt_cm(pd.home_alt_cm, Location::AltFrame::ABSOLUTE);
1944 1 : _home_is_set = true;
1945 1 : _home_locked = true;
1946 1 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Restored watchdog home");
1947 : }
1948 2086 : }
1949 :
1950 : // Set to true if the terrain underneath is stable enough to be used as a height reference
1951 : // this is not related to terrain following
1952 3800955 : void AP_AHRS::set_terrain_hgt_stable(bool stable)
1953 : {
1954 : // avoid repeatedly setting variable in NavEKF objects to prevent
1955 : // spurious event logging
1956 3800955 : switch (terrainHgtStableState) {
1957 998 : case TriState::UNKNOWN:
1958 998 : break;
1959 831167 : case TriState::True:
1960 831167 : if (stable) {
1961 830595 : return;
1962 : }
1963 572 : break;
1964 2968790 : case TriState::False:
1965 2968790 : if (!stable) {
1966 2968021 : return;
1967 : }
1968 769 : break;
1969 : }
1970 2339 : terrainHgtStableState = (TriState)stable;
1971 :
1972 : #if HAL_NAVEKF2_AVAILABLE
1973 2339 : ekf2.EKF2.setTerrainHgtStable(stable);
1974 : #endif
1975 : #if HAL_NAVEKF3_AVAILABLE
1976 2339 : ekf3.EKF3.setTerrainHgtStable(stable);
1977 : #endif
1978 : }
1979 :
1980 : // get 1-sigma position and velocity uncertainty from the EKF state error covariance matrix P
1981 13 : bool AP_AHRS::get_pos_vel_uncertainty(float &pos_horiz_m, float &pos_vert_m, float &vel_m_s) const
1982 : {
1983 13 : switch (active_EKF_type()) {
1984 : #if HAL_NAVEKF3_AVAILABLE
1985 13 : case EKFType::THREE:
1986 13 : return ekf3.EKF3.getPosVelUncertainty(pos_horiz_m, pos_vert_m, vel_m_s);
1987 : #endif
1988 0 : default:
1989 0 : return false;
1990 : }
1991 : }
1992 :
1993 : // get a source's velocity innovations. source should be from 0 to 7 (see AP_NavEKF_Source::SourceXY)
1994 : // returns true on success and results are placed in innovations and variances arguments
1995 39 : bool AP_AHRS::get_vel_innovations_and_variances_for_source(uint8_t source, Vector3f &innovations, Vector3f &variances) const
1996 : {
1997 39 : switch (configured_ekf_type()) {
1998 : #if AP_AHRS_DCM_ENABLED
1999 0 : case EKFType::DCM:
2000 : // We are not using an EKF so no data
2001 0 : return false;
2002 : #endif
2003 :
2004 : #if HAL_NAVEKF2_AVAILABLE
2005 0 : case EKFType::TWO:
2006 : // EKF2 does not support source level variances
2007 0 : return false;
2008 : #endif
2009 :
2010 : #if HAL_NAVEKF3_AVAILABLE
2011 39 : case EKFType::THREE:
2012 : // use EKF to get variance
2013 39 : return ekf3.EKF3.getVelInnovationsAndVariancesForSource((AP_NavEKF_Source::SourceXY)source, innovations, variances);
2014 : #endif
2015 :
2016 : #if AP_AHRS_SIM_ENABLED
2017 0 : case EKFType::SIM:
2018 : // SITL does not support source level variances
2019 0 : return false;
2020 : #endif
2021 :
2022 : #if AP_AHRS_EXTERNAL_ENABLED
2023 0 : case EKFType::EXTERNAL:
2024 0 : return false;
2025 : #endif
2026 : }
2027 :
2028 0 : return false;
2029 : }
2030 :
2031 : //get the index of the active airspeed sensor, wrt the primary core
2032 83375025 : uint8_t AP_AHRS::get_active_airspeed_index() const
2033 : {
2034 : #if AP_AIRSPEED_ENABLED
2035 83375025 : const auto *airspeed = AP::airspeed();
2036 83375025 : if (airspeed == nullptr) {
2037 171422 : return 0;
2038 : }
2039 :
2040 : // we only have affinity for EKF3 as of now
2041 : #if HAL_NAVEKF3_AVAILABLE
2042 83203603 : if (active_EKF_type() == EKFType::THREE) {
2043 72438414 : uint8_t ret = ekf3.EKF3.getActiveAirspeed();
2044 72438414 : if (ret != UINT8_MAX && airspeed->healthy(ret) && airspeed->use(ret)) {
2045 13486831 : return ret;
2046 : }
2047 : }
2048 : #endif
2049 :
2050 : // for the rest, let the primary airspeed sensor be used
2051 69716772 : return airspeed->get_primary();
2052 : #else
2053 :
2054 : return 0;
2055 : #endif // AP_AIRSPEED_ENABLED
2056 : }
2057 :
2058 : #if AP_AHRS_EKF_RESET_ENABLED
2059 : // request full backend reset, currently only implemented for EKF3
2060 : // returns true if the reset was performed
2061 1 : bool AP_AHRS::reset_configured_backend(void)
2062 : {
2063 : // reset EKF3 regardless of active EKF type — if we've fallen back
2064 : // to DCM due to EKF failure, that's exactly when a bootstrap reset
2065 : // is most needed to force re-convergence
2066 1 : switch (configured_ekf_type()) {
2067 : #if AP_AHRS_NAVEKF3_ENABLED
2068 1 : case EKFType::THREE:
2069 1 : return ekf3.EKF3.InitialiseFilterBootstrap();
2070 : #endif // AP_AHRS_NAVEKF3_ENABLED
2071 0 : default:
2072 0 : break;
2073 : }
2074 :
2075 0 : return false;
2076 : }
2077 : #endif // AP_AHRS_EKF_RESET_ENABLED
2078 :
2079 : // set position, velocity and yaw sources to either 0=primary, 1=secondary, 2=tertiary
2080 16 : void AP_AHRS::set_posvelyaw_source_set(AP_NavEKF_Source::SourceSetSelection source_set_idx)
2081 : {
2082 : #if HAL_NAVEKF3_AVAILABLE
2083 16 : ekf3.EKF3.setPosVelYawSourceSet((uint8_t)source_set_idx);
2084 : #endif
2085 16 : }
2086 :
2087 : //returns active source set used, 0=primary, 1=secondary, 2=tertiary
2088 0 : uint8_t AP_AHRS::get_posvelyaw_source_set() const
2089 : {
2090 : #if HAL_NAVEKF3_AVAILABLE
2091 0 : return ekf3.EKF3.get_active_source_set();
2092 : #else
2093 : return 0;
2094 : #endif
2095 : }
2096 :
2097 2461314 : void AP_AHRS::Log_Write()
2098 : {
2099 : #if HAL_NAVEKF2_AVAILABLE
2100 2461314 : ekf2.EKF2.Log_Write();
2101 : #endif
2102 : #if HAL_NAVEKF3_AVAILABLE
2103 2461314 : ekf3.EKF3.Log_Write();
2104 : #endif
2105 :
2106 2461314 : Write_AHRS2();
2107 2461314 : Write_POS();
2108 :
2109 : #if AP_AHRS_SIM_ENABLED
2110 2461314 : AP::sitl()->Log_Write_SIMSTATE();
2111 : #endif
2112 2461314 : }
2113 :
2114 : // check if non-compass sensor is providing yaw. Allows compass pre-arm checks to be bypassed
2115 563 : bool AP_AHRS::using_noncompass_for_yaw(void) const
2116 : {
2117 : #if AP_AHRS_DCM_ENABLED && HAL_NAVEKF3_AVAILABLE
2118 : // FIXME: DCM uses EKF3's answer (which is not necessarily the
2119 : // configured type)
2120 563 : if (active_estimates == &dcm_estimates) {
2121 0 : return ekf3_estimates.using_noncompass_for_yaw;
2122 : }
2123 : #endif
2124 563 : return active_estimates->using_noncompass_for_yaw;
2125 : }
2126 :
2127 : // check if external nav is providing yaw
2128 1186 : bool AP_AHRS::using_extnav_for_yaw(void) const
2129 : {
2130 : #if AP_AHRS_DCM_ENABLED && HAL_NAVEKF3_AVAILABLE
2131 : // FIXME: DCM uses EKF3's answer (which is not necessarily the
2132 : // configured type)
2133 1186 : if (active_estimates == &dcm_estimates) {
2134 154 : return ekf3_estimates.using_extnav_for_yaw;
2135 : }
2136 : #endif
2137 1032 : return active_estimates->using_extnav_for_yaw;
2138 : }
2139 :
2140 : // set and save the alt noise parameter value
2141 82 : void AP_AHRS::set_alt_measurement_noise(float noise)
2142 : {
2143 : #if HAL_NAVEKF2_AVAILABLE
2144 82 : ekf2.EKF2.set_baro_alt_noise(noise);
2145 : #endif
2146 : #if HAL_NAVEKF3_AVAILABLE
2147 82 : ekf3.EKF3.set_baro_alt_noise(noise);
2148 : #endif
2149 82 : }
2150 :
2151 : // check if non-compass sensor is providing yaw. Allows compass pre-arm checks to be bypassed
2152 479 : const EKFGSF_yaw *AP_AHRS::get_yaw_estimator(void) const
2153 : {
2154 479 : switch (active_EKF_type()) {
2155 : #if HAL_NAVEKF2_AVAILABLE
2156 0 : case EKFType::TWO:
2157 0 : return ekf2.EKF2.get_yawEstimator();
2158 : #endif
2159 : #if AP_AHRS_DCM_ENABLED
2160 0 : case EKFType::DCM:
2161 : #if HAL_NAVEKF3_AVAILABLE
2162 0 : return ekf3.EKF3.get_yawEstimator();
2163 : #else
2164 : return nullptr;
2165 : #endif
2166 : #endif
2167 : #if HAL_NAVEKF3_AVAILABLE
2168 479 : case EKFType::THREE:
2169 479 : return ekf3.EKF3.get_yawEstimator();
2170 : #endif
2171 : #if AP_AHRS_SIM_ENABLED
2172 0 : case EKFType::SIM:
2173 : #endif
2174 : #if AP_AHRS_EXTERNAL_ENABLED
2175 0 : case EKFType::EXTERNAL:
2176 : #endif
2177 0 : return nullptr;
2178 : }
2179 : // since there is no default case above, this is unreachable
2180 0 : return nullptr;
2181 : }
2182 :
2183 : // get current location estimate
2184 89554763 : bool AP_AHRS::get_location(Location &loc) const
2185 : {
2186 89554763 : loc = state.location;
2187 89554763 : return state.location_ok;
2188 : }
2189 :
2190 : // return a wind estimation vector in "wind" (m/s); returns false on failure
2191 1943939 : bool AP_AHRS::get_wind(Vector3f &wind) const
2192 : {
2193 1943939 : wind = active_estimates->wind;
2194 1943939 : return active_estimates->wind_valid;
2195 : }
2196 :
2197 : // return an airspeed estimate if available. return true
2198 : // if we have an estimate
2199 74353099 : bool AP_AHRS::airspeed_EAS(float &airspeed_ret) const
2200 : {
2201 74353099 : airspeed_ret = state.airspeed_EAS;
2202 74353099 : return state.airspeed_EAS_ok;
2203 : }
2204 :
2205 : // return an airspeed estimate if available. return true
2206 : // if we have an estimate
2207 1310707 : bool AP_AHRS::airspeed_EAS(float &airspeed_ret, AP_AHRS::AirspeedEstimateType &type) const
2208 : {
2209 1310707 : airspeed_ret = state.airspeed_EAS;
2210 1310707 : type = state.airspeed_estimate_type;
2211 1310707 : return state.airspeed_EAS_ok;
2212 : }
2213 :
2214 : // return a true airspeed estimate (navigation airspeed) if
2215 : // available. return true if we have an estimate
2216 43 : bool AP_AHRS::airspeed_TAS(float &airspeed_ret) const
2217 : {
2218 43 : airspeed_ret = state.airspeed_TAS;
2219 43 : return state.airspeed_TAS_ok;
2220 : }
2221 :
2222 : // return estimate of true airspeed vector in body frame in m/s
2223 : // returns false if estimate is unavailable
2224 770123 : bool AP_AHRS::airspeed_vector_TAS(Vector3f &vec) const
2225 : {
2226 770123 : vec = state.airspeed_TAS_vec;
2227 770123 : return state.airspeed_TAS_vec_ok;
2228 : }
2229 :
2230 : // return the quaternion defining the rotation from NED to XYZ (body) axes
2231 30861 : bool AP_AHRS::get_quaternion(Quaternion &quat) const
2232 : {
2233 30861 : quat = state.quat;
2234 30861 : return state.quat_ok;
2235 : }
2236 :
2237 : // returns the inertial navigation origin in lat/lon/alt
2238 376767905 : bool AP_AHRS::get_origin(Location &ret) const
2239 : {
2240 376767905 : ret = state.origin;
2241 376767905 : return state.origin_ok;
2242 : }
2243 :
2244 : // return a ground velocity in meters/second, North/East/Down
2245 : // order. Must only be called if have_inertial_nav() is true
2246 86101927 : bool AP_AHRS::get_velocity_NED(Vector3f &vec) const
2247 : {
2248 86101927 : vec = state.velocity_NED;
2249 86101927 : return state.velocity_NED_ok;
2250 : }
2251 :
2252 : // return location corresponding to vector relative to the
2253 : // vehicle's origin
2254 4436 : bool AP_AHRS::get_location_from_origin_offset_NED(Location &loc, const Vector3p &offset_ned) const
2255 : {
2256 4436 : if (!get_origin(loc)) {
2257 0 : return false;
2258 : }
2259 4436 : loc.offset(offset_ned);
2260 :
2261 4436 : return true;
2262 : }
2263 :
2264 : // return location corresponding to vector relative to the
2265 : // vehicle's home location
2266 0 : bool AP_AHRS::get_location_from_home_offset_NED(Location &loc, const Vector3p &offset_ned) const
2267 : {
2268 0 : if (!home_is_set()) {
2269 0 : return false;
2270 : }
2271 0 : loc = get_home();
2272 0 : loc.offset(offset_ned);
2273 :
2274 0 : return true;
2275 : }
2276 :
2277 : /*
2278 : get EAS to TAS scaling
2279 : */
2280 99679131 : float AP_AHRS::get_EAS2TAS(void) const
2281 : {
2282 99679131 : if (is_positive(state.EAS2TAS)) {
2283 99674635 : return state.EAS2TAS;
2284 : }
2285 4489 : return 1.0;
2286 : }
2287 :
2288 : // get air density / sea level density - decreases as altitude climbs
2289 67541113 : float AP_AHRS::get_air_density_ratio(void) const
2290 : {
2291 67541113 : const float eas2tas = get_EAS2TAS();
2292 67541113 : return 1.0 / sq(eas2tas);
2293 : }
2294 :
2295 : // singleton instance
2296 : AP_AHRS *AP_AHRS::_singleton;
2297 :
2298 : namespace AP {
2299 :
2300 2903180248 : AP_AHRS &ahrs()
2301 : {
2302 2903180248 : return *AP_AHRS::get_singleton();
2303 : }
2304 :
2305 : }
2306 :
2307 : #endif // AP_AHRS_ENABLED
|