Path: blob/master/libraries/AP_ExternalAHRS/AP_ExternalAHRS_GSOF.cpp
13808 views
/*1This program is free software: you can redistribute it and/or modify2it under the terms of the GNU General Public License as published by3the Free Software Foundation, either version 3 of the License, or4(at your option) any later version.5This program is distributed in the hope that it will be useful,6but WITHOUT ANY WARRANTY; without even the implied warranty of7MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the8GNU General Public License for more details.9You should have received a copy of the GNU General Public License10along with this program. If not, see <http://www.gnu.org/licenses/>.11*/1213// Usage in SITL with hardware for debugging:14// $ sim_vehicle.py -v Plane --console --map -DG15// param set AHRS_EKF_TYPE 1116// param set EAHRS_TYPE 617// For EAHRS as a GPS:18// param set GPS1_TYPE 2119// Configure GSOF 1, 49,50,70 on UDP port 44444, "UDP Mode" and "UDP Broadcast Transmit"20// Consider setting EK3_SRC1_YAW to 2 on the bench...2122// Usage with NET parameters and ethernet in SITL with hardware:23// param set NET_ENABLE 124// param set NET_P1_TYPE 225// # Set up AHRS input26// param set NET_P1_PROTOCOL 3627// param set SIM_GPS1_TYPE 028// param set NET_P1_PORT 4444429// param set EAHRS_TYPE 630//31// Usage with serial in SITL with hardware:32// $ sim_vehicle.py -v Plane -A "--serial3=uart:/dev/ttyUSB0" --console --map -DG33// param set SERIAL3_PROTOCOL 3634// param set SERIAL3_BAUD 11520035// param set EAHRS_TYPE 636// # ensure NET_* if off if you were using ethernet.37// # To enable the EAHRS to provide GPS:38// param set GPS2_TYPE 213940// On most hardware, you must enable EAHRS:41// ./waf configure --board Pixhawk6X --enable-AHRS_EXT4243// GPS ride-along with EARHS as the 2nd GPS44// param set GPS_AUTO_SWITCH 045// param set GPS2_TYPE 2146// param set GPS_PRIMARY 04748#define AP_MATH_ALLOW_DOUBLE_FUNCTIONS 14950#include "AP_ExternalAHRS_config.h"5152#if AP_EXTERNAL_AHRS_GSOF_ENABLED5354#include "AP_ExternalAHRS_GSOF.h"55#include <AP_Baro/AP_Baro.h>56#include <AP_Compass/AP_Compass.h>57#include <AP_GPS/AP_GPS.h>58#include <AP_HAL/utility/sparse-endian.h>59#include <AP_InertialSensor/AP_InertialSensor.h>60#include <GCS_MAVLink/GCS.h>61#include <AP_Logger/AP_Logger.h>62#include <AP_BoardConfig/AP_BoardConfig.h>63#include <AP_HAL/utility/Socket.h>6465static const char* LOG_FMT = "%s ExternalAHRS: %s";6667extern const AP_HAL::HAL &hal;6869AP_ExternalAHRS_GSOF::AP_ExternalAHRS_GSOF(AP_ExternalAHRS *_frontend,70AP_ExternalAHRS::state_t &_state): AP_ExternalAHRS_backend(_frontend, _state)71{72auto &sm = AP::serialmanager();73uart = sm.find_serial(AP_SerialManager::SerialProtocol_AHRS, 0);74if (uart == nullptr) {75GCS_SEND_TEXT(MAV_SEVERITY_ERROR, LOG_FMT, get_name(), "no UART");76return;77}78baudrate = sm.find_baudrate(AP_SerialManager::SerialProtocol_AHRS, 0);79port_num = sm.find_portnum(AP_SerialManager::SerialProtocol_AHRS, 0);8081if (!hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_ExternalAHRS_GSOF::update_thread, void), "AHRS", 2048, AP_HAL::Scheduler::PRIORITY_SPI, 0)) {82AP_BoardConfig::allocation_error("GSOF ExternalAHRS failed to allocate ExternalAHRS update thread");83}8485// Offer GPS even through it's a tightly coupled EKF.86set_default_sensors(uint16_t(AP_ExternalAHRS::AvailableSensor::GPS));87}8889// get serial port number for the uart90int8_t AP_ExternalAHRS_GSOF::get_port(void) const91{92if (uart == nullptr) {93return -1;94}95return port_num;96};9798void AP_ExternalAHRS_GSOF::update_thread(void)99{100// TODO configure receiver to output expected data.101102#if AP_EXTERNALAHRS_GSOF_DEBUG_ENABLED103auto last_debug = AP_HAL::millis();104uint32_t pps = 0;105#endif // AP_EXTERNALAHRS_GSOF_DEBUG_ENABLED106107uart->begin(baudrate);108109while (true) {110const auto available = uart->available();111if (available == 0) {112hal.scheduler->delay_microseconds(100);113continue;114}115uint8_t c;116if (!uart->read(c)) {117hal.scheduler->delay_microseconds(100);118continue;119}120121AP_GSOF::MsgTypes parsed;122const auto parse_res = parse(c, parsed);123if (parse_res != PARSED_GSOF_DATA) {124continue;125}126127auto const now = AP_HAL::millis();128#if AP_EXTERNALAHRS_GSOF_DEBUG_ENABLED129pps++;130131if (now - last_debug > 1000) {132GCS_SEND_TEXT(MAV_SEVERITY_INFO,133"GSOF PPS: %lu, ins %u",134static_cast<unsigned long>(pps),135static_cast<uint16_t>(ins_full_nav.gnss_status));136last_debug = now;137pps = 0;138}139#endif // AP_EXTERNALAHRS_GSOF_DEBUG_ENABLED140if (parsed.get(AP_GSOF::POS_TIME)) {141last_pos_time_ms = now;142143gps_data.satellites_in_view = pos_time.num_sats;144}145146if (parsed.get(AP_GSOF::INS_FULL_NAV)) {147last_ins_full_nav_ms = now;148149// The Trimble PX-1 is a tightly coupled GNSS-INS.150// Although generally we don't want to couple EKF's151// by populating EKF data from a GPS into ArduPilot's GPS152// which then goes to AP's EKF, this is the only approach.153// Extensive flight testing indicates good performance,154// and proper handling of failure of the PX-1 INS results155// in proper ignoring of the GPS data.156gps_data.gps_week = ins_full_nav.gps_week;157gps_data.ms_tow = ins_full_nav.gps_time_ms;158gps_data.ned_vel_north = ins_full_nav.vel_n;159gps_data.ned_vel_east = ins_full_nav.vel_e;160gps_data.ned_vel_down = ins_full_nav.vel_d;161switch(ins_full_nav.gnss_status) {162case AP_GSOF::GnssStatus::FIX_NOT_AVAILABLE:163gps_data.fix_type = AP_GPS_FixType::NONE;164break;165case AP_GSOF::GnssStatus::GNSS_SPS_MODE:166gps_data.fix_type = AP_GPS_FixType::FIX_3D;167break;168case AP_GSOF::GnssStatus::DGPS_SPS_MODE:169gps_data.fix_type = AP_GPS_FixType::DGPS;170break;171case AP_GSOF::GnssStatus::GNSS_PPS_MODE:172gps_data.fix_type = AP_GPS_FixType::PPP;173break;174case AP_GSOF::GnssStatus::FIXED_RTK_MODE:175gps_data.fix_type = AP_GPS_FixType::RTK_FIXED;176break;177case AP_GSOF::GnssStatus::FLOAT_RTK_MODE:178gps_data.fix_type = AP_GPS_FixType::RTK_FLOAT;179break;180case AP_GSOF::GnssStatus::DR_MODE:181gps_data.fix_type = AP_GPS_FixType::NONE;182break;183}184if (ins_full_nav.gnss_status != AP_GSOF::GnssStatus::FIX_NOT_AVAILABLE) {185// If fix is unavailable, the lat/lon may be zero, so don't post the data.186// To reproduce this condition, disconnect the GPS antenna and reboot the receiver.187post_filter();188}189}190if (parsed.get(AP_GSOF::INS_RMS)) {191last_ins_rms_ms = now;192193gps_data.horizontal_pos_accuracy = Vector2d(ins_rms.pos_rms_n, ins_rms.pos_rms_e).length();194gps_data.vertical_pos_accuracy = ins_rms.pos_rms_d;195gps_data.horizontal_vel_accuracy = Vector2d(ins_rms.vel_rms_n, ins_rms.vel_rms_e).length();196}197if (parsed.get(AP_GSOF::LLH_MSL)) {198last_llh_msl_ms = now;199200gps_data.longitude = static_cast<int32_t>(llh_msl.longitude * 1E7);201gps_data.latitude = static_cast<int32_t>(llh_msl.latitude * 1E7);202gps_data.msl_altitude = static_cast<int32_t>(llh_msl.altitude_msl * 1E2);203}204205if (parsed.get(AP_GSOF::LLH_MSL) && parsed.get(AP_GSOF::INS_FULL_NAV)) {206undulation = ins_full_nav.altitude - llh_msl.altitude_msl;207}208209AP_GSOF::MsgTypes expected_gps;210expected_gps.set(AP_GSOF::POS_TIME);211expected_gps.set(AP_GSOF::INS_FULL_NAV);212expected_gps.set(AP_GSOF::INS_RMS);213expected_gps.set(AP_GSOF::LLH_MSL);214uint8_t instance;215if (AP::gps().get_first_external_instance(instance) && parsed == expected_gps) {216AP::gps().handle_external(gps_data, instance);217}218check_initialise_state();219}220}221222void AP_ExternalAHRS_GSOF::check_initialise_state(void)223{224const bool new_init_state = initialised();225// Only send the message after fully booted up, otherwise it gets dropped.226if (!last_init_state && new_init_state && AP_HAL::millis() > 5000) {227GCS_SEND_TEXT(MAV_SEVERITY_INFO, LOG_FMT, get_name(), "initialised.");228last_init_state = new_init_state;229}230}231232void AP_ExternalAHRS_GSOF::post_filter() const233{234WITH_SEMAPHORE(state.sem);235state.velocity = Vector3f{ins_full_nav.vel_n, ins_full_nav.vel_e, ins_full_nav.vel_d};236state.have_velocity = true;237238state.location = Location(239ins_full_nav.latitude * 1E7,240ins_full_nav.longitude * 1E7,241(ins_full_nav.altitude - undulation) * 1E2,242Location::AltFrame::ABSOLUTE);243state.have_location = true;244245state.quat.from_euler(246radians(ins_full_nav.roll_deg),247radians(ins_full_nav.pitch_deg),248radians(ins_full_nav.heading_deg));249state.have_quaternion = true;250251#if CONFIG_HAL_BOARD == HAL_BOARD_SITL252if (!state.location.initialised()) {253AP_HAL::panic("Uninitialized location.");254}255#endif256257if (!state.have_origin && filter_healthy()) {258state.origin = state.location;259state.have_origin = true;260}261262state.last_location_update_us = AP_HAL::micros();263}264265// Get model/type name266const char* AP_ExternalAHRS_GSOF::get_name() const267{268return "GSOF";269}270271bool AP_ExternalAHRS_GSOF::healthy(void) const272{273return times_healthy() && filter_healthy();274}275276bool AP_ExternalAHRS_GSOF::initialised(void) const277{278return last_pos_time_ms != 0 && last_ins_full_nav_ms != 0 && last_ins_rms_ms != 0 && last_llh_msl_ms != 0;279}280281bool AP_ExternalAHRS_GSOF::pre_arm_check(char *failure_msg, uint8_t failure_msg_len) const282{283if (!initialised()) {284hal.util->snprintf(failure_msg, failure_msg_len, LOG_FMT, get_name(), "not initialised");285return false;286}287if (!times_healthy()) {288hal.util->snprintf(failure_msg, failure_msg_len, LOG_FMT, get_name(), "data is stale");289return false;290}291if (!filter_healthy()) {292hal.util->snprintf(failure_msg, failure_msg_len, LOG_FMT, get_name(), "filter is unhealthy");293return false;294}295if (!healthy()) {296hal.util->snprintf(failure_msg, failure_msg_len, LOG_FMT, get_name(), "unhealthy");297return false;298}299300return true;301}302303void AP_ExternalAHRS_GSOF::get_filter_status(nav_filter_status &status) const304{305memset(&status, 0, sizeof(status));306status.flags.initalized = initialised();307}308309bool AP_ExternalAHRS_GSOF::get_variances(float &velVar, float &posVar, float &hgtVar, Vector3f &magVar, float &tasVar) const310{311velVar = Vector3<float>(312ins_rms.vel_rms_n,313ins_rms.vel_rms_e,314ins_rms.vel_rms_d).length() * vel_gate_scale;315posVar = Vector2<float>(316ins_rms.pos_rms_n,317ins_rms.pos_rms_e).length() * pos_gate_scale;318hgtVar = ins_rms.pos_rms_d * hgt_gate_scale;319tasVar = 0;320return true;321}322323bool AP_ExternalAHRS_GSOF::times_healthy() const324{325auto const now = AP_HAL::millis();326327auto const TIMES_FOS = 2.0;328329// All messages must be at minimum 5Hz to pass the AP_GPS 4hz rate as logged in dataflash GPA.Delta logs.330331// 5Hz = 200mS.332auto const GSOF_1_EXPECTED_DELAY_MS = 200;333auto const pos_time_healthy = now - last_pos_time_ms <= TIMES_FOS * GSOF_1_EXPECTED_DELAY_MS;334335// 50Hz = 20mS.336auto const GSOF_49_EXPECTED_DELAY_MS = 20;337auto const ins_full_nav_healthy = now - last_ins_full_nav_ms <= TIMES_FOS * GSOF_49_EXPECTED_DELAY_MS;338339// 5Hz = 200mS.340auto const GSOF_50_EXPECTED_DELAY_MS = 200;341auto const ins_rms_healthy = now - last_ins_rms_ms < TIMES_FOS * GSOF_50_EXPECTED_DELAY_MS;342343// 5Hz = 200mS.344auto const GSOF_70_EXPECTED_DELAY_MS = 200;345auto const llh_msl_healthy = now - last_llh_msl_ms < TIMES_FOS * GSOF_70_EXPECTED_DELAY_MS;346347#if CONFIG_HAL_BOARD == HAL_BOARD_SITL348if (!pos_time_healthy) {349GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "%s: GSOF pos time delayed by %f ms", get_name(), float(now - last_pos_time_ms));350}351if (!ins_full_nav_healthy) {352GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "%s: INS Full nav delayed by %f ms", get_name(), float(now - last_ins_full_nav_ms));353}354if (!ins_rms_healthy) {355GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "%s: INS rms delayed by %f ms", get_name(), float(now - last_ins_rms_ms));356}357if (!llh_msl_healthy) {358GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "%s: LLH MSL delayed by %f ms", get_name(), float(now - last_llh_msl_ms));359}360#endif // CONFIG_HAL_BOARD == HAL_BOARD_SITL361362return pos_time_healthy && ins_full_nav_healthy && ins_rms_healthy && llh_msl_healthy;363}364365bool AP_ExternalAHRS_GSOF::filter_healthy() const366{367// TODO get the right threshold from Trimble for arming vs in flight.368// Fow now, assume an aligned IMU is sufficient for flight.369auto const imu_alignment_healthy = (370ins_rms.imu_alignment_status == ImuAlignmentStatus::DEGRADED ||371ins_rms.imu_alignment_status == ImuAlignmentStatus::ALIGNED ||372ins_rms.imu_alignment_status == ImuAlignmentStatus::FULL_NAV373);374375auto const gnss_healthy = ins_rms.gnss_status != GnssStatus::FIX_NOT_AVAILABLE;376377// TODO check RMS errors.378return imu_alignment_healthy && gnss_healthy;379}380381#endif // AP_EXTERNAL_AHRS_GSOF_ENABLED382383384