LCOV - code coverage report
Current view: top level - AP_ExternalAHRS - AP_ExternalAHRS.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 157 177 88.7 %
Date: 2026-09-09 11:02:08 Functions: 20 21 95.2 %

          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             :   support for serial connected AHRS systems
      17             :  */
      18             : 
      19             : #include "AP_ExternalAHRS_config.h"
      20             : 
      21             : #if AP_EXTERNAL_AHRS_ENABLED
      22             : 
      23             : #include "AP_ExternalAHRS.h"
      24             : #include "AP_ExternalAHRS_backend.h"
      25             : #include "AP_ExternalAHRS_Aeron_plx.h"
      26             : #include "AP_ExternalAHRS_VectorNav.h"
      27             : #include "AP_ExternalAHRS_MicroStrain5.h"
      28             : #include "AP_ExternalAHRS_MicroStrain7.h"
      29             : #include "AP_ExternalAHRS_InertialLabs.h"
      30             : #include "AP_ExternalAHRS_SBG.h"
      31             : #include "AP_ExternalAHRS_GSOF.h"
      32             : #include "AP_ExternalAHRS_SensAItion.h"
      33             : 
      34             : #include <GCS_MAVLink/GCS.h>
      35             : #include <AP_AHRS/AP_AHRS.h>
      36             : #include <AP_GPS/AP_GPS.h>
      37             : #include <AP_Logger/AP_Logger.h>
      38             : 
      39             : extern const AP_HAL::HAL &hal;
      40             : 
      41             : AP_ExternalAHRS *AP_ExternalAHRS::_singleton;
      42             : 
      43             : // constructor
      44        2098 : AP_ExternalAHRS::AP_ExternalAHRS()
      45             : {
      46        2098 :     AP_Param::setup_object_defaults(this, var_info);
      47        2098 :     _singleton = this;
      48        2098 :     if (rate.get() < 50) {
      49             :         // min 50Hz
      50           0 :         rate.set(50);
      51             :     }
      52        2098 : }
      53             : 
      54             : #ifndef HAL_EXTERNAL_AHRS_DEFAULT
      55             : #define HAL_EXTERNAL_AHRS_DEFAULT 0
      56             : #endif
      57             : 
      58             : 
      59             : // table of user settable parameters
      60             : const AP_Param::GroupInfo AP_ExternalAHRS::var_info[] = {
      61             : 
      62             :     // @Param: _TYPE
      63             :     // @DisplayName: AHRS type
      64             :     // @Description: Type of AHRS device
      65             :     // @Values: 0:None,1:VectorNav,2:MicroStrain5,5:InertialLabs,6:Trimble GSOF,7:MicroStrain7,8:SBG,10:Aeron,11:SensAItion
      66             :     // @User: Standard
      67             :     AP_GROUPINFO_FLAGS("_TYPE", 1, AP_ExternalAHRS, devtype, HAL_EXTERNAL_AHRS_DEFAULT, AP_PARAM_FLAG_ENABLE),
      68             : 
      69             :     // @Param: _RATE
      70             :     // @DisplayName: AHRS data rate
      71             :     // @Description: Requested rate for AHRS device
      72             :     // @Units: Hz
      73             :     // @User: Standard
      74             :     AP_GROUPINFO("_RATE", 2, AP_ExternalAHRS, rate, 50),
      75             : 
      76             :     // @Param: _OPTIONS
      77             :     // @DisplayName: External AHRS options
      78             :     // @Description: External AHRS options bitmask
      79             :     // @Bitmask: 0:Vector Nav use uncompensated values for accel gyro and mag, 1:SBG uses EKF as GNSS, 2:SensAItion used as AHRS
      80             :     // @User: Standard
      81             :     AP_GROUPINFO("_OPTIONS", 3, AP_ExternalAHRS, options, 0),
      82             : 
      83             :     // @Param: _SENSORS
      84             :     // @DisplayName: External AHRS sensors
      85             :     // @Description: External AHRS sensors bitmask
      86             :     // @Bitmask: 0:GPS,1:IMU,2:Baro,3:Compass
      87             :     // @User: Advanced
      88             :     AP_GROUPINFO("_SENSORS", 4, AP_ExternalAHRS, sensors, 0xF),
      89             : 
      90             :     // @Param: _LOG_RATE
      91             :     // @DisplayName: AHRS logging rate
      92             :     // @Description: Logging rate for EAHRS devices
      93             :     // @Units: Hz
      94             :     // @User: Standard
      95             :     AP_GROUPINFO("_LOG_RATE", 5, AP_ExternalAHRS, log_rate, 10),
      96             : 
      97             :     AP_GROUPEND
      98             : };
      99             : 
     100             : 
     101        2094 : void AP_ExternalAHRS::init(void)
     102             : {
     103        2094 :     if (rate.get() < 50) {
     104             :         // min 50Hz
     105           0 :         rate.set(50);
     106             :     }
     107             : 
     108        2094 :     switch (DevType(devtype)) {
     109        2032 :     case DevType::None:
     110             :         // nothing to do
     111        2032 :         return;
     112             : 
     113             : #if AP_EXTERNAL_AHRS_VECTORNAV_ENABLED
     114          12 :     case DevType::VecNav:
     115          12 :         backend = NEW_NOTHROW AP_ExternalAHRS_VectorNav(this, state);
     116          12 :         return;
     117             : #endif
     118             : 
     119             : #if AP_EXTERNAL_AHRS_MICROSTRAIN5_ENABLED
     120          11 :     case DevType::MicroStrain5:
     121          11 :         backend = NEW_NOTHROW AP_ExternalAHRS_MicroStrain5(this, state);
     122          11 :         return;
     123             : #endif
     124             : 
     125             : #if AP_EXTERNAL_AHRS_GSOF_ENABLED
     126           0 :     case DevType::GSOF:
     127           0 :         backend = NEW_NOTHROW AP_ExternalAHRS_GSOF(this, state);
     128           0 :         return;
     129             : #endif
     130             : 
     131             : #if AP_EXTERNAL_AHRS_MICROSTRAIN7_ENABLED
     132          14 :     case DevType::MicroStrain7:
     133          14 :         backend = NEW_NOTHROW AP_ExternalAHRS_MicroStrain7(this, state);
     134          14 :         return;
     135             : #endif
     136             : 
     137             : #if AP_EXTERNAL_AHRS_INERTIALLABS_ENABLED
     138          11 :     case DevType::InertialLabs:
     139          11 :         backend = NEW_NOTHROW AP_ExternalAHRS_InertialLabs(this, state);
     140          11 :         return;
     141             : #endif
     142             : #if AP_EXTERNAL_AHRS_SENSAITION_ENABLED
     143           3 :     case DevType::SensAItion:
     144           3 :         backend = NEW_NOTHROW AP_ExternalAHRS_SensAItion(this, state);
     145           3 :         return;
     146             : #endif
     147             : 
     148             : #if AP_EXTERNAL_AHRS_SBG_ENABLED
     149           0 :     case DevType::SBG:
     150           0 :         backend = NEW_NOTHROW AP_ExternalAHRS_SBG(this, state);
     151           0 :         return;
     152             : #endif  // AP_EXTERNAL_AHRS_SBG_ENABLED
     153             : 
     154             : #if AP_EXTERNAL_AHRS_AERON_PLX_ENABLED
     155          11 :     case DevType::Aeron:
     156          11 :         backend = NEW_NOTHROW AP_ExternalAHRS_Aeron_plx(this, state);
     157          11 :         return;
     158             : #endif  // AP_EXTERNAL_AHRS_AERON_PLX_ENABLED
     159             : 
     160             :     }
     161             : 
     162           0 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Unsupported ExternalAHRS type %u", unsigned(devtype));
     163             : }
     164             : 
     165    42359120 : bool AP_ExternalAHRS::enabled() const
     166             : {
     167    42359120 :     return DevType(devtype) != DevType::None;
     168             : }
     169             : 
     170             : // get serial port number for the uart, or -1 if not applicable
     171        7290 : int8_t AP_ExternalAHRS::get_port(AvailableSensor sensor) const
     172             : {
     173        7290 :     if (!backend || !has_sensor(sensor)) {
     174        7101 :         return -1;
     175             :     }
     176         189 :     return backend->get_port();
     177             : };
     178             : 
     179             : // accessors for AP_AHRS
     180    41686716 : bool AP_ExternalAHRS::healthy(void) const
     181             : {
     182    41686716 :     return backend && backend->healthy();
     183             : }
     184             : 
     185    41686716 : bool AP_ExternalAHRS::initialised(void) const
     186             : {
     187    41686716 :     return backend && backend->initialised();
     188             : }
     189             : 
     190    41686716 : bool AP_ExternalAHRS::get_quaternion(Quaternion &quat)
     191             : {
     192    41686716 :     if (state.have_quaternion) {
     193       59493 :         WITH_SEMAPHORE(state.sem);
     194       59493 :         quat = state.quat;
     195       59493 :         return true;
     196       59493 :     }
     197    41627223 :     return false;
     198             : }
     199             : 
     200      236255 : bool AP_ExternalAHRS::get_origin(Location &loc)
     201             : {
     202      236255 :     if (state.have_origin) {
     203      230917 :         WITH_SEMAPHORE(state.sem);
     204             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
     205      230917 :         if (!state.origin.initialised()) {
     206           0 :             AP_HAL::panic("Uninitialized origin in AP_ExternalAHRS.");
     207             :         }
     208             : #endif
     209      230917 :         loc = state.origin;
     210      230917 :         return true;
     211      230917 :     }
     212        5338 :     return false;
     213             : }
     214             : 
     215         842 : bool AP_ExternalAHRS::set_origin(const Location &loc)
     216             : {
     217         842 :     WITH_SEMAPHORE(state.sem);
     218         842 :     if (state.have_origin) {
     219           7 :         return false;
     220             :     }
     221         835 :     state.origin = loc;
     222         835 :     state.have_origin = true;
     223         835 :     return true;
     224         842 : }
     225             : 
     226       59493 : bool AP_ExternalAHRS::get_location(Location &loc)
     227             : {
     228       59493 :     if (!state.have_location) {
     229           0 :         return false;
     230             :     }
     231       59493 :     WITH_SEMAPHORE(state.sem);
     232       59493 :     loc = state.location;
     233             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
     234       59493 :     if (!loc.initialised()) {
     235           0 :         AP_HAL::panic("Uninitialized location in AP_ExternalAHRS.");
     236             :     }
     237             : #endif
     238             : 
     239       59493 :     if (state.last_location_update_us != 0 &&
     240       59493 :         state.have_velocity) {
     241             :         // extrapolate position based on velocity to cope with slow backends
     242       59493 :         const float dt = (AP_HAL::micros() - state.last_location_update_us)*1.0e-6;
     243       59493 :         if (dt < 1) {
     244             :             // only extrapolate for 1s max
     245       59493 :             Vector3p ofs = state.velocity.topostype();
     246       59493 :             ofs *= dt;
     247       59493 :             loc.offset(ofs);
     248             :         }
     249             :     }
     250             : 
     251       59493 :     return true;
     252       59493 : }
     253             : 
     254       59493 : Vector2f AP_ExternalAHRS::get_groundspeed_vector()
     255             : {
     256       59493 :     WITH_SEMAPHORE(state.sem);
     257       59493 :     Vector2f vec{state.velocity.x, state.velocity.y};
     258      118986 :     return vec;
     259       59493 : }
     260             : 
     261       59493 : bool AP_ExternalAHRS::get_velocity_NED(Vector3f &vel)
     262             : {
     263       59493 :     if (!state.have_velocity) {
     264           0 :         return false;
     265             :     }
     266       59493 :     WITH_SEMAPHORE(state.sem);
     267       59493 :     vel = state.velocity;
     268       59493 :     return true;
     269       59493 : }
     270             : 
     271       59493 : bool AP_ExternalAHRS::get_speed_down(float &speedD)
     272             : {
     273       59493 :     if (!state.have_velocity) {
     274           0 :         return false;
     275             :     }
     276       59493 :     WITH_SEMAPHORE(state.sem);
     277       59493 :     speedD = state.velocity.z;
     278       59493 :     return true;
     279       59493 : }
     280             : 
     281      133284 : bool AP_ExternalAHRS::pre_arm_check(char *failure_msg, uint8_t failure_msg_len) const
     282             : {
     283      133284 :     if (backend == nullptr) {
     284           2 :         hal.util->snprintf(failure_msg, failure_msg_len, "ExternalAHRS: Invalid backend");
     285           2 :         return false;
     286             :     }
     287      133282 :     if (!backend->pre_arm_check(failure_msg, failure_msg_len)) {
     288           0 :         return false;
     289             :     }
     290             :     // Verify the user has configured the GPS to accept EAHRS data.
     291      133282 :     if (has_sensor(AvailableSensor::GPS)) {
     292         295 :         const auto eahrs_gps_sensors = backend->num_gps_sensors();
     293             : 
     294         295 :         const auto &gps = AP::gps();
     295         295 :         uint8_t n_configured_eahrs_gps = 0;
     296        1180 :         for (uint8_t i = 0; i < GPS_MAX_INSTANCES; ++i) {
     297         885 :             const auto gps_type = gps.get_type(i);
     298         885 :             if (gps_type == AP_GPS::GPS_TYPE_EXTERNAL_AHRS) {
     299         270 :                 n_configured_eahrs_gps++;
     300             :             }
     301             :         }
     302             : 
     303             :         // Once AP supports at least 3 GPS's, change to == and remove the second condition.
     304             :         // At that point, enforce that all GPS's in EAHRS can report to AP_GPS.
     305         295 :         if (n_configured_eahrs_gps < 1 && eahrs_gps_sensors >= 1) {
     306          25 :             hal.util->snprintf(failure_msg, failure_msg_len, "ExternalAHRS: Incorrect number of GPS sensors configured for EAHRS");
     307          25 :             return false;
     308             :         }
     309             :     }
     310      133257 :     AP_AHRS &ahrs = AP::ahrs();
     311      133257 :     if (ahrs.configured_ekf_type() == AP_AHRS::EKFType::EXTERNAL) {
     312             :         // when using EAHRS as the EKF source, we must have a valid position origin
     313      133206 :         if (!state.have_origin) {
     314           8 :             hal.util->snprintf(failure_msg, failure_msg_len, "ExternalAHRS: No origin");
     315           8 :             return false;
     316             :         }
     317             :     }
     318      133249 :     return true;
     319             : }
     320             : 
     321             : /*
     322             :   get filter status
     323             :  */
     324       73219 : void AP_ExternalAHRS::get_filter_status(nav_filter_status &status) const
     325             : {
     326       73219 :     status = {};
     327       73219 :     if (backend) {
     328       72971 :         backend->get_filter_status(status);
     329             :     }
     330       73219 : }
     331             : 
     332             : /*
     333             :   get estimated variances, return false if not implemented
     334             :  */
     335       59493 : bool AP_ExternalAHRS::get_variances(float &velVar, float &posVar, float &hgtVar, Vector3f &magVar, float &tasVar) const
     336             : {
     337       59493 :     if (backend != nullptr) {
     338       59493 :         return backend->get_variances(velVar, posVar, hgtVar, magVar, tasVar);
     339             :     }
     340           0 :     return false;
     341             : }
     342             : 
     343       59493 : bool AP_ExternalAHRS::get_gyro(Vector3f &gyro)
     344             : {
     345       59493 :     WITH_SEMAPHORE(state.sem);
     346       59493 :     if (!has_sensor(AvailableSensor::IMU)) {
     347       38868 :         return false;
     348             :     }
     349       20625 :     gyro = state.gyro;
     350       20625 :     return true;
     351       59493 : }
     352             : 
     353       59493 : bool AP_ExternalAHRS::get_accel(Vector3f &accel)
     354             : {
     355       59493 :     WITH_SEMAPHORE(state.sem);
     356       59493 :     if (!has_sensor(AvailableSensor::IMU)) {
     357       38868 :         return false;
     358             :     }
     359       20625 :     accel = state.accel;
     360       20625 :     return true;
     361       59493 : }
     362             : 
     363    42249549 : void AP_ExternalAHRS::update(void)
     364             : {
     365    42249549 :     if (backend) {
     366      637981 :         backend->update();
     367             :     }
     368             : 
     369    42249549 :     WITH_SEMAPHORE(state.sem);
     370             : #if HAL_LOGGING_ENABLED
     371    42249549 :     const uint32_t now_ms = AP_HAL::millis();
     372    42249549 :     if (enabled() && log_rate.get() > 0 && now_ms - last_log_ms >= uint32_t(1000U/log_rate.get())) {
     373       13726 :         last_log_ms = now_ms;
     374             : 
     375             :         // @LoggerMessage: EAHR
     376             :         // @Description: External AHRS data
     377             :         // @Field: TimeUS: Time since system startup
     378             :         // @Field: Roll: euler roll
     379             :         // @Field: Pitch: euler pitch
     380             :         // @Field: Yaw: euler yaw
     381             :         // @Field: VN: velocity north
     382             :         // @Field: VE: velocity east
     383             :         // @Field: VD: velocity down
     384             :         // @Field: Lat: latitude
     385             :         // @Field: Lon: longitude
     386             :         // @Field: Alt: altitude AMSL
     387             :         // @Field: Flg: nav status flags
     388             : 
     389             :         float roll, pitch, yaw;
     390       13726 :         state.quat.to_euler(roll, pitch, yaw);
     391       13726 :         nav_filter_status filterStatus {};
     392       13726 :         get_filter_status(filterStatus);
     393             : 
     394       27452 :         AP::logger().WriteStreaming("EAHR", "TimeUS,Roll,Pitch,Yaw,VN,VE,VD,Lat,Lon,Alt,Flg",
     395             :                                     "sdddnnnDUm-",
     396             :                                     "F000000GG0-",
     397             :                                     "QffffffLLfI",
     398             :                                     AP_HAL::micros64(),
     399       13726 :                                     degrees(roll), degrees(pitch), degrees(yaw),
     400       13726 :                                     state.velocity.x, state.velocity.y, state.velocity.z,
     401       13726 :                                     state.location.lat, state.location.lng, state.location.alt*0.01,
     402             :                                     filterStatus.value);
     403             : 
     404             :         // @LoggerMessage: EAHV
     405             :         // @Description: External AHRS variances
     406             :         // @Field: TimeUS: Time since system startup
     407             :         // @Field: Vel: velocity variance
     408             :         // @Field: Pos: position variance
     409             :         // @Field: Hgt: height variance
     410             :         // @Field: MagX: magnetic variance, X
     411             :         // @Field: MagY: magnetic variance, Y
     412             :         // @Field: MagZ: magnetic variance, Z
     413             :         // @Field: TAS: true airspeed variance
     414             : 
     415             :         float velVar, posVar, hgtVar, tasVar;
     416       13726 :         Vector3f magVar;
     417       13726 :         if (backend != nullptr && backend->get_variances(velVar, posVar, hgtVar, magVar, tasVar)) {
     418       23370 :             AP::logger().WriteStreaming("EAHV", "TimeUS,Vel,Pos,Hgt,MagX,MagY,MagZ,TAS",
     419             :                                         "Qfffffff",
     420             :                                         AP_HAL::micros64(),
     421             :                                         velVar, posVar, hgtVar,
     422       11685 :                                         magVar.x, magVar.y, magVar.z,
     423             :                                         tasVar);
     424             :         }
     425             :     }
     426             : #endif  // HAL_LOGGING_ENABLED
     427    42249549 : }
     428             : 
     429             : // Get model/type name
     430           0 : const char* AP_ExternalAHRS::get_name() const
     431             : {
     432           0 :     if (backend) {
     433           0 :         return backend->get_name();
     434             :     }
     435           0 :     return nullptr;
     436             : }
     437             : 
     438             : namespace AP {
     439             : 
     440    84741575 : AP_ExternalAHRS &externalAHRS()
     441             : {
     442    84741575 :     return *AP_ExternalAHRS::get_singleton();
     443             : }
     444             : 
     445             : };
     446             : 
     447             : #endif  // AP_EXTERNAL_AHRS_ENABLED
     448             : 

Generated by: LCOV version 1.14