Path: blob/master/libraries/AP_ExternalAHRS/AP_ExternalAHRS_SensAItion.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.56This program is distributed in the hope that it will be useful,7but WITHOUT ANY WARRANTY; without even the implied warranty of8MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9GNU General Public License for more details.1011You should have received a copy of the GNU General Public License12along with this program. If not, see <http://www.gnu.org/licenses/>.13*/14/*15Support for SensAItion serial connected INS and IMU16Implements SensAItion protocol with ArduPilot-specific adaptations17*/1819#include "AP_ExternalAHRS_SensAItion.h"2021#if AP_EXTERNAL_AHRS_SENSAITION_ENABLED2223#include <float.h>24#include <math.h>2526#include <AP_SerialManager/AP_SerialManager.h>27#include <GCS_MAVLink/GCS.h>28#include <AP_HAL/AP_HAL.h>29#include <AP_GPS/AP_GPS.h>30#include <AP_Baro/AP_Baro.h>31#include <AP_Compass/AP_Compass.h>32#include <AP_InertialSensor/AP_InertialSensor.h>3334namespace35{36const float MINIMUM_INTERESTING_BAROMETER_CHANGE_p = 1.0f;37const float MINIMUM_INTERESTING_TEMP_CHANGE_degc = 0.1f;38const uint32_t BARO_UPDATE_TIMEOUT_ms = 100U;39}4041extern const AP_HAL::HAL &hal;4243AP_ExternalAHRS_SensAItion::AP_ExternalAHRS_SensAItion(AP_ExternalAHRS *_frontend, AP_ExternalAHRS::state_t &_state) :44AP_ExternalAHRS_backend(_frontend, _state),45parser(AP_ExternalAHRS_SensAItion_Parser::ConfigMode::IMU)46{47ins_mode_enabled = option_is_set(AP_ExternalAHRS::OPTIONS::SENSAITION_INS);4849auto mode = ins_mode_enabled ?50AP_ExternalAHRS_SensAItion_Parser::ConfigMode::INTERLEAVED_INS :51AP_ExternalAHRS_SensAItion_Parser::ConfigMode::IMU;5253if (ins_mode_enabled) {54parser = AP_ExternalAHRS_SensAItion_Parser(mode);55}5657auto &sm = AP::serialmanager();58uart = sm.find_serial(AP_SerialManager::SerialProtocol_AHRS, 0);59baudrate = sm.find_baudrate(AP_SerialManager::SerialProtocol_AHRS, 0);60port_num = sm.find_portnum(AP_SerialManager::SerialProtocol_AHRS, 0);61if (!uart || baudrate == 0 || port_num == -1) {62GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "KEBNI: Serial Port Not Found!");63return;64}6566if (ins_mode_enabled) {67set_default_sensors(uint16_t(AP_ExternalAHRS::AvailableSensor::IMU) |68uint16_t(AP_ExternalAHRS::AvailableSensor::GPS) |69uint16_t(AP_ExternalAHRS::AvailableSensor::BARO) |70uint16_t(AP_ExternalAHRS::AvailableSensor::COMPASS));71} else {72set_default_sensors(uint16_t(AP_ExternalAHRS::AvailableSensor::IMU));73}7475if (!hal.scheduler->thread_create(76FUNCTOR_BIND_MEMBER(&AP_ExternalAHRS_SensAItion::update_thread, void),77"AHRS_SensAItion", 2048, AP_HAL::Scheduler::PRIORITY_SPI, 0)) {78GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "KEBNI: Failed to create thread!");79}80}8182int8_t AP_ExternalAHRS_SensAItion::get_port() const83{84return uart ? port_num : -1;85}8687const char* AP_ExternalAHRS_SensAItion::get_name() const88{89return "Kebni SensAItion";90}9192bool AP_ExternalAHRS_SensAItion::healthy() const93{94const uint32_t now_ms = AP_HAL::millis();9596WITH_SEMAPHORE(driver_state.semaphore);9798if ((now_ms - driver_state.last_imu_pkt_ms) > 160) {99// IMU is required at high rate for health100return false;101}102103if (ins_mode_enabled) {104if ((now_ms - driver_state.last_ins_pkt_ms) > 400) {105// INS packets must also have high enough rate106return false;107}108109if (!(driver_state.last_sensor_valid & 0x01)) {110// IMU not available111return false;112}113114if (driver_state.last_gnss1_fix < 3) {115// No 3D satellite fix116return false;117}118}119120return true;121}122123bool AP_ExternalAHRS_SensAItion::initialised() const124{125return setup_complete;126}127128bool AP_ExternalAHRS_SensAItion::pre_arm_check(char *failure_msg, uint8_t failure_msg_len) const129{130if (!healthy()) {131hal.util->snprintf(failure_msg, failure_msg_len, "SensAItion Unhealthy");132return false;133}134135if (ins_mode_enabled) {136uint8_t last_alignment_status;137{138WITH_SEMAPHORE(driver_state.semaphore);139last_alignment_status = driver_state.last_alignment_status;140}141if (last_alignment_status != 1) {142hal.util->snprintf(failure_msg, failure_msg_len, "SensAItion Aligning");143return false;144}145}146147return true;148}149150void AP_ExternalAHRS_SensAItion::get_filter_status(nav_filter_status &status) const151{152memset(&status, 0, sizeof(status));153154status.flags.initalized = initialised();155156if (healthy()) {157WITH_SEMAPHORE(driver_state.semaphore);158if (ins_mode_enabled && driver_state.last_alignment_status == 1) {159status.flags.attitude = true;160status.flags.horiz_pos_abs = true;161status.flags.vert_pos = true;162status.flags.horiz_vel = true;163status.flags.vert_vel = true;164status.flags.using_gps = true;165status.flags.horiz_pos_rel = true;166status.flags.pred_horiz_pos_abs = true;167status.flags.pred_horiz_pos_rel = true;168}169}170}171172bool AP_ExternalAHRS_SensAItion::get_variances(float &velVar, float &posVar, float &hgtVar, Vector3f &magVar, float &tasVar) const173{174WITH_SEMAPHORE(driver_state.semaphore);175if (ins_mode_enabled && driver_state.last_alignment_status == 1) {176posVar = driver_state.last_h_pos_quality * pos_gate_scale;177velVar = driver_state.last_vel_quality * vel_gate_scale;178hgtVar = driver_state.last_v_pos_quality * hgt_gate_scale;179tasVar = 0; //not used180return true;181}182183return false;184}185186uint8_t AP_ExternalAHRS_SensAItion::num_gps_sensors() const187{188return ins_mode_enabled ? 1 : 0;189}190191// ---------------------------------------------------------------------------192// THREAD & PROFILER193// ---------------------------------------------------------------------------194void AP_ExternalAHRS_SensAItion::update_thread()195{196while (true) {197if (!check_uart()) {198hal.scheduler->delay_microseconds(500);199}200}201}202203bool AP_ExternalAHRS_SensAItion::check_uart()204{205WITH_SEMAPHORE(sem_handle);206207if (!uart) {208return false;209}210211if (!setup_complete) {212uart->begin(baudrate);213setup_complete = true;214GCS_SEND_TEXT(MAV_SEVERITY_INFO, "KEBNI: INIT. Mode:%d Baud:%u",215(int)ins_mode_enabled, (unsigned)baudrate);216}217218const auto nread = uart->read(buffer, sizeof(buffer));219if (nread <= 0) {220return false;221}222223const uint32_t now_ms = AP_HAL::millis();224225AP_ExternalAHRS_SensAItion_Parser::Measurement meas;226bool found_measurement = false;227ssize_t parsed_bytes = 0;228while (parsed_bytes < nread) {229const size_t max_bytes_to_parse = nread - parsed_bytes;230parsed_bytes += parser.parse_stream(&buffer[parsed_bytes], max_bytes_to_parse, meas);231232switch (meas.type) {233case AP_ExternalAHRS_SensAItion_Parser::MeasurementType::UNINITIALIZED:234// The parser is done parsing the whole buffer, but didn't find any235// more complete message. (We might be in the middle of a message236// that will be finished later.)237break;238case AP_ExternalAHRS_SensAItion_Parser::MeasurementType::IMU:239handle_imu(meas, now_ms);240found_measurement = true;241break;242case AP_ExternalAHRS_SensAItion_Parser::MeasurementType::AHRS:243handle_ahrs(meas, now_ms);244found_measurement = true;245break;246case AP_ExternalAHRS_SensAItion_Parser::MeasurementType::INS:247handle_ins(meas, now_ms);248found_measurement = true;249break;250}251}252253return found_measurement;254}255256void AP_ExternalAHRS_SensAItion::handle_imu(const AP_ExternalAHRS_SensAItion_Parser::Measurement& meas, uint32_t now_ms)257{258{259WITH_SEMAPHORE(state.sem);260state.accel = meas.acceleration_mss;261state.gyro = meas.angular_velocity_rads;262}263264// INS265ins.accel = meas.acceleration_mss;266ins.gyro = meas.angular_velocity_rads;267ins.temperature = meas.temperature_degc;268AP::ins().handle_external(ins);269270#if AP_COMPASS_EXTERNALAHRS_ENABLED271// COMPASS272mag.field = meas.magnetic_field_mgauss;273AP::compass().handle_external(mag);274#endif275276#if AP_BARO_EXTERNALAHRS_ENABLED277bool baro_updated = false;278#endif279280{281WITH_SEMAPHORE(driver_state.semaphore);282driver_state.last_imu_pkt_ms = now_ms;283284#if AP_BARO_EXTERNALAHRS_ENABLED285// BARO286// ArduPlane has an internal check that triggers an error if there are too many barometer287// readings with the same value. At high sampling rates, we triggered that check because288// the barometer is internally sampled at a lower rate. To avoid that, we only update the value289// if it changes OR a certain minimum time has passed.290const bool pressure_changed = fabsf(baro.pressure_pa - meas.air_pressure_p) > MINIMUM_INTERESTING_BAROMETER_CHANGE_p;291const bool temp_changed = fabsf(baro.temperature - meas.temperature_degc) > MINIMUM_INTERESTING_TEMP_CHANGE_degc;292const bool timeout = now_ms > driver_state.last_baro_update_ms + BARO_UPDATE_TIMEOUT_ms;293294if (pressure_changed || temp_changed || timeout) {295driver_state.last_baro_update_ms = now_ms;296baro.instance = 0;297baro.pressure_pa = meas.air_pressure_p;298baro.temperature = meas.temperature_degc;299baro_updated = true;300}301#endif302303}304305#if AP_BARO_EXTERNALAHRS_ENABLED306// Do the update after releasing the semaphore, for speed307if (baro_updated) {308AP::baro().handle_external(baro);309}310#endif311}312313void AP_ExternalAHRS_SensAItion::handle_ahrs(const AP_ExternalAHRS_SensAItion_Parser::Measurement& meas, uint32_t now_ms)314{315{316WITH_SEMAPHORE(driver_state.semaphore);317driver_state.last_quat_pkt_ms = now_ms;318}319320{321WITH_SEMAPHORE(state.sem);322state.quat = meas.orientation;323state.have_quaternion = true;324}325}326327void AP_ExternalAHRS_SensAItion::handle_ins(const AP_ExternalAHRS_SensAItion_Parser::Measurement& meas, uint32_t now_ms)328{329// STATE330{331WITH_SEMAPHORE(state.sem);332state.location = Location(333meas.location.lat,334meas.location.lng,335meas.location.alt,336Location::AltFrame::ABSOLUTE337);338state.velocity = meas.velocity_ned;339state.have_location = true;340state.have_velocity = true;341state.last_location_update_us = AP_HAL::micros();342343if (!state.have_origin && meas.alignment_status) {344state.origin = Location(345meas.location.lat,346meas.location.lng,347meas.location.alt,348Location::AltFrame::ABSOLUTE349);350state.have_origin = true;351GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "KEBNI: Origin Set.");352}353}354355{356WITH_SEMAPHORE(driver_state.semaphore);357358// Local data359driver_state.last_ins_pkt_ms = now_ms;360driver_state.last_alignment_status = meas.alignment_status;361driver_state.last_sensor_valid = meas.sensor_valid;362driver_state.last_gnss1_fix = meas.gnss1_fix;363driver_state.last_gnss2_fix = meas.gnss2_fix;364driver_state.last_error_flags = meas.error_flags;365driver_state.last_h_pos_quality = meas.pos_accuracy.xy().length();366driver_state.last_v_pos_quality = meas.pos_accuracy.z;367driver_state.last_vel_quality = meas.vel_accuracy.length();368369// GPS370gps.horizontal_pos_accuracy = driver_state.last_h_pos_quality;371gps.vertical_pos_accuracy = driver_state.last_v_pos_quality;372}373374// GPS - continued375gps.gps_week = meas.gps_week;376gps.ms_tow = meas.time_itow_ms;377gps.fix_type = AP_GPS_FixType(meas.gnss1_fix);378gps.satellites_in_view = meas.num_sats_gnss1;379gps.horizontal_vel_accuracy = meas.vel_accuracy.xy().length();380gps.latitude = meas.location.lat;381gps.longitude = meas.location.lng;382383// Note: SensAItion reports altitude relative to WGS84, not MSL.384// But we expect the user to reset the altitude to 0 at start,385// so the absolute reference should not matter.386gps.msl_altitude = meas.location.alt;387gps.ned_vel_north = meas.velocity_ned.x;388gps.ned_vel_east = meas.velocity_ned.y;389gps.ned_vel_down = meas.velocity_ned.z;390391// 3. Estimate DOPs (Unitless) using assumed UERE of 3.0m392// This answers "What is HDOP/VDOP?"393const float ASSUMED_UERE = 3.0f;394395float est_hdop = gps.horizontal_pos_accuracy / ASSUMED_UERE;396float est_vdop = gps.vertical_pos_accuracy / ASSUMED_UERE;397398// 4. Sanity Clamping (DOP cannot be 0, and rarely < 0.6)399if (est_hdop < 0.7f) {400est_hdop = 0.7f;401}402if (est_vdop < 0.7f) {403est_vdop = 0.7f;404}405gps.hdop = est_hdop;406gps.vdop = est_vdop;407408// Handle409uint8_t instance;410if (AP::gps().get_first_external_instance(instance)) {411AP::gps().handle_external(gps, instance);412}413}414415#endif // AP_EXTERNAL_AHRS_SENSAITION_ENABLED416417418