Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/libraries/AP_ExternalAHRS/AP_ExternalAHRS_InertialLabs.cpp
Views: 1798
/*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 serial connected InertialLabs INS16*/1718#include "AP_ExternalAHRS_config.h"1920#if AP_EXTERNAL_AHRS_INERTIALLABS_ENABLED2122#include "AP_ExternalAHRS_InertialLabs.h"23#include <AP_Math/AP_Math.h>24#include <AP_Math/crc.h>25#include <AP_Baro/AP_Baro.h>26#include <AP_Compass/AP_Compass.h>27#include <AP_GPS/AP_GPS.h>28#include <AP_Airspeed/AP_Airspeed.h>29#include <AP_InertialSensor/AP_InertialSensor.h>30#include <GCS_MAVLink/GCS.h>31#include <AP_Logger/AP_Logger.h>32#include <AP_SerialManager/AP_SerialManager.h>33#include <AP_HAL/utility/sparse-endian.h>34#include <AP_Common/Bitmask.h>35#include <AP_Vehicle/AP_Vehicle_Type.h>3637extern const AP_HAL::HAL &hal;3839// unit status bits40#define ILABS_UNIT_STATUS_ALIGNMENT_FAIL 0x000141#define ILABS_UNIT_STATUS_OPERATION_FAIL 0x000242#define ILABS_UNIT_STATUS_GYRO_FAIL 0x000443#define ILABS_UNIT_STATUS_ACCEL_FAIL 0x000844#define ILABS_UNIT_STATUS_MAG_FAIL 0x001045#define ILABS_UNIT_STATUS_ELECTRONICS_FAIL 0x002046#define ILABS_UNIT_STATUS_GNSS_FAIL 0x004047#define ILABS_UNIT_STATUS_RUNTIME_CAL 0x008048#define ILABS_UNIT_STATUS_VOLTAGE_LOW 0x010049#define ILABS_UNIT_STATUS_VOLTAGE_HIGH 0x020050#define ILABS_UNIT_STATUS_X_RATE_HIGH 0x040051#define ILABS_UNIT_STATUS_Y_RATE_HIGH 0x080052#define ILABS_UNIT_STATUS_Z_RATE_HIGH 0x100053#define ILABS_UNIT_STATUS_MAG_FIELD_HIGH 0x200054#define ILABS_UNIT_STATUS_TEMP_RANGE_ERR 0x400055#define ILABS_UNIT_STATUS_RUNTIME_CAL2 0x80005657// unit status2 bits58#define ILABS_UNIT_STATUS2_ACCEL_X_HIGH 0x000159#define ILABS_UNIT_STATUS2_ACCEL_Y_HIGH 0x000260#define ILABS_UNIT_STATUS2_ACCEL_Z_HIGH 0x000461#define ILABS_UNIT_STATUS2_BARO_FAIL 0x000862#define ILABS_UNIT_STATUS2_DIFF_PRESS_FAIL 0x001063#define ILABS_UNIT_STATUS2_MAGCAL_2D_ACT 0x002064#define ILABS_UNIT_STATUS2_MAGCAL_3D_ACT 0x004065#define ILABS_UNIT_STATUS2_GNSS_FUSION_OFF 0x008066#define ILABS_UNIT_STATUS2_DIFF_PRESS_FUSION_OFF 0x010067#define ILABS_UNIT_STATUS2_MAG_FUSION_OFF 0x020068#define ILABS_UNIT_STATUS2_GNSS_POS_VALID 0x04006970// air data status bits71#define ILABS_AIRDATA_INIT_FAIL 0x000172#define ILABS_AIRDATA_DIFF_PRESS_INIT_FAIL 0x000273#define ILABS_AIRDATA_STATIC_PRESS_FAIL 0x000474#define ILABS_AIRDATA_DIFF_PRESS_FAIL 0x000875#define ILABS_AIRDATA_STATIC_PRESS_RANGE_ERR 0x001076#define ILABS_AIRDATA_DIFF_PRESS_RANGE_ERR 0x002077#define ILABS_AIRDATA_PRESS_ALT_FAIL 0x010078#define ILABS_AIRDATA_AIRSPEED_FAIL 0x020079#define ILABS_AIRDATA_BELOW_THRESHOLD 0x0400808182// constructor83AP_ExternalAHRS_InertialLabs::AP_ExternalAHRS_InertialLabs(AP_ExternalAHRS *_frontend,84AP_ExternalAHRS::state_t &_state) :85AP_ExternalAHRS_backend(_frontend, _state)86{87auto &sm = AP::serialmanager();88uart = sm.find_serial(AP_SerialManager::SerialProtocol_AHRS, 0);89if (!uart) {90GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "InertialLabs ExternalAHRS no UART");91return;92}93baudrate = sm.find_baudrate(AP_SerialManager::SerialProtocol_AHRS, 0);94port_num = sm.find_portnum(AP_SerialManager::SerialProtocol_AHRS, 0);9596// don't offer IMU by default, at 200Hz it is too slow for many aircraft97set_default_sensors(uint16_t(AP_ExternalAHRS::AvailableSensor::GPS) |98uint16_t(AP_ExternalAHRS::AvailableSensor::BARO) |99uint16_t(AP_ExternalAHRS::AvailableSensor::COMPASS));100101if (!hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_ExternalAHRS_InertialLabs::update_thread, void), "ILabs", 2048, AP_HAL::Scheduler::PRIORITY_SPI, 0)) {102AP_HAL::panic("InertialLabs Failed to start ExternalAHRS update thread");103}104GCS_SEND_TEXT(MAV_SEVERITY_INFO, "InertialLabs ExternalAHRS initialised");105}106107/*108re-sync buffer on parse failure109*/110void AP_ExternalAHRS_InertialLabs::re_sync(void)111{112if (buffer_ofs > 3) {113/*114look for the 2 byte header and try to sync to that115*/116const uint16_t header = 0x55AA;117const uint8_t *p = (const uint8_t *)memmem(&buffer[1], buffer_ofs-3, &header, sizeof(header));118if (p != nullptr) {119const uint16_t n = p - &buffer[0];120memmove(&buffer[0], p, buffer_ofs - n);121buffer_ofs -= n;122} else {123buffer_ofs = 0;124}125} else {126buffer_ofs = 0;127}128}129130// macro for checking we don't run past end of message buffer131#define CHECK_SIZE(d) need_re_sync = (message_ofs + (msg_len=sizeof(d)) > buffer_end); if (need_re_sync) break132133// lookup a message in the msg_types bitmask to see if we received it in this packet134#define GOT_MSG(mtype) msg_types.get(unsigned(MessageType::mtype))135136/*137check header is valid138*/139bool AP_ExternalAHRS_InertialLabs::check_header(const ILabsHeader *h) const140{141return h->magic == 0x55AA &&142h->msg_type == 1 &&143h->msg_id == 0x95 &&144h->msg_len <= sizeof(buffer)-2;145}146147/*148check the UART for more data149returns true if we have consumed potentially valid bytes150*/151bool AP_ExternalAHRS_InertialLabs::check_uart()152{153WITH_SEMAPHORE(state.sem);154155if (!setup_complete) {156return false;157}158// ensure we own the uart159uart->begin(0);160uint32_t n = uart->available();161if (n == 0) {162return false;163}164if (n + buffer_ofs > sizeof(buffer)) {165n = sizeof(buffer) - buffer_ofs;166}167const ILabsHeader *h = (ILabsHeader *)&buffer[0];168169if (buffer_ofs < sizeof(ILabsHeader)) {170n = MIN(n, sizeof(ILabsHeader)-buffer_ofs);171} else {172if (!check_header(h)) {173re_sync();174return false;175}176if (buffer_ofs > h->msg_len+8) {177re_sync();178return false;179}180n = MIN(n, uint32_t(h->msg_len + 2 - buffer_ofs));181}182183const ssize_t nread = uart->read(&buffer[buffer_ofs], n);184if (nread != ssize_t(n)) {185re_sync();186return false;187}188buffer_ofs += n;189190if (buffer_ofs < sizeof(ILabsHeader)) {191return true;192}193194if (!check_header(h)) {195re_sync();196return false;197}198199if (buffer_ofs < h->msg_len+2) {200/*201see if we can read the rest immediately202*/203const uint16_t needed = h->msg_len+2 - buffer_ofs;204if (uart->available() < needed) {205// need more data206return true;207}208const ssize_t nread2 = uart->read(&buffer[buffer_ofs], needed);209if (nread2 != needed) {210re_sync();211return false;212}213buffer_ofs += nread2;214}215216// check checksum217const uint16_t crc1 = crc_sum_of_bytes_16(&buffer[2], buffer_ofs-4);218const uint16_t crc2 = le16toh_ptr(&buffer[buffer_ofs-2]);219if (crc1 != crc2) {220re_sync();221return false;222}223224const uint8_t *buffer_end = &buffer[buffer_ofs];225const uint16_t payload_size = h->msg_len - 6;226const uint8_t *payload = &buffer[6];227if (payload_size < 3) {228re_sync();229return false;230}231const uint8_t num_messages = payload[0];232if (num_messages == 0 ||233num_messages > payload_size-1) {234re_sync();235return false;236}237const uint8_t *message_ofs = &payload[num_messages+1];238bool need_re_sync = false;239240// bitmask for what types we get241Bitmask<256> msg_types;242uint32_t now_ms = AP_HAL::millis();243244for (uint8_t i=0; i<num_messages; i++) {245if (message_ofs >= buffer_end) {246re_sync();247return false;248}249MessageType mtype = (MessageType)payload[1+i];250ILabsData &u = *(ILabsData*)message_ofs;251uint8_t msg_len = 0;252253msg_types.set(unsigned(mtype));254255switch (mtype) {256case MessageType::GPS_INS_TIME_MS: {257// this is the GPS tow timestamp in ms for when the IMU data was sampled258CHECK_SIZE(u.gnss_time_ms);259gps_data.ms_tow = u.gnss_time_ms;260break;261}262case MessageType::GPS_WEEK: {263CHECK_SIZE(u.gnss_week);264gps_data.gps_week = u.gnss_week;265break;266}267case MessageType::ACCEL_DATA_HR: {268CHECK_SIZE(u.accel_data_hr);269// should use 9.8106 instead of GRAVITY_MSS-constant in accordance with the device-documentation270ins_data.accel = u.accel_data_hr.tofloat().rfu_to_frd()*9.8106f*1.0e-6; // m/s^2271break;272}273case MessageType::GYRO_DATA_HR: {274CHECK_SIZE(u.gyro_data_hr);275ins_data.gyro = u.gyro_data_hr.tofloat().rfu_to_frd()*DEG_TO_RAD*1.0e-5; // rad/s276break;277}278case MessageType::BARO_DATA: {279CHECK_SIZE(u.baro_data);280baro_data.pressure_pa = u.baro_data.pressure_pa2*2; // Pa281state2.baro_alt = u.baro_data.baro_alt*0.01; // m282break;283}284case MessageType::MAG_DATA: {285CHECK_SIZE(u.mag_data);286mag_data.field = u.mag_data.tofloat().rfu_to_frd()*(10*NTESLA_TO_MGAUSS); // milligauss287break;288}289case MessageType::ORIENTATION_ANGLES: {290CHECK_SIZE(u.orientation_angles);291state.quat.from_euler(radians(u.orientation_angles.roll*0.01),292radians(u.orientation_angles.pitch*0.01),293radians(u.orientation_angles.yaw*0.01));294state.have_quaternion = true;295if (last_att_ms == 0) {296GCS_SEND_TEXT(MAV_SEVERITY_INFO, "InertialLabs: got link");297}298last_att_ms = now_ms;299break;300}301case MessageType::VELOCITIES: {302CHECK_SIZE(u.velocity);303state.velocity = u.velocity.tofloat().rfu_to_frd()*0.01;304gps_data.ned_vel_north = state.velocity.x; // m/s305gps_data.ned_vel_east = state.velocity.y; // m/s306gps_data.ned_vel_down = state.velocity.z; // m/s307state.have_velocity = true;308last_vel_ms = now_ms;309break;310}311case MessageType::POSITION: {312CHECK_SIZE(u.position);313state.location.lat = u.position.lat; // deg*1.0e7314state.location.lng = u.position.lon; // deg*1.0e7315state.location.alt = u.position.alt; // m*100316317gps_data.latitude = u.position.lat; // deg*1.0e7318gps_data.longitude = u.position.lon; // deg*1.0e7319gps_data.msl_altitude = u.position.alt; // m*100320321state.have_location = true;322state.last_location_update_us = AP_HAL::micros();323last_pos_ms = now_ms;324break;325}326case MessageType::KF_VEL_COVARIANCE: {327CHECK_SIZE(u.kf_vel_covariance);328state2.kf_vel_covariance = u.kf_vel_covariance.tofloat().rfu_to_frd(); // mm/s329break;330}331case MessageType::KF_POS_COVARIANCE: {332CHECK_SIZE(u.kf_pos_covariance);333state2.kf_pos_covariance = u.kf_pos_covariance.tofloat(); // mm334break;335}336case MessageType::UNIT_STATUS: {337CHECK_SIZE(u.unit_status);338state2.unit_status = u.unit_status;339break;340}341case MessageType::GNSS_EXTENDED_INFO: {342CHECK_SIZE(u.gnss_extended_info);343gps_data.fix_type = AP_GPS_FixType(u.gnss_extended_info.fix_type+1);344gnss_data.spoof_status = u.gnss_extended_info.spoofing_status;345break;346}347case MessageType::NUM_SATS: {348CHECK_SIZE(u.num_sats);349gps_data.satellites_in_view = u.num_sats;350break;351}352case MessageType::GNSS_POSITION: {353CHECK_SIZE(u.gnss_position);354gnss_data.lat = u.gnss_position.lat; // deg*1.0e7355gnss_data.lng = u.gnss_position.lon; // deg*1.0e7356gnss_data.alt = u.gnss_position.alt; // mm357break;358}359case MessageType::GNSS_VEL_TRACK: {360CHECK_SIZE(u.gnss_vel_track);361gnss_data.hor_speed = u.gnss_vel_track.hor_speed*0.01; // m/s362gnss_data.ver_speed = u.gnss_vel_track.ver_speed*0.01; // m/s363gnss_data.track_over_ground = u.gnss_vel_track.track_over_ground*0.01; // deg364break;365}366case MessageType::GNSS_POS_TIMESTAMP: {367CHECK_SIZE(u.gnss_pos_timestamp);368gnss_data.pos_timestamp = u.gnss_pos_timestamp;369break;370}371case MessageType::GNSS_INFO_SHORT: {372CHECK_SIZE(u.gnss_info_short);373gnss_data.info_short = u.gnss_info_short;374break;375}376case MessageType::GNSS_NEW_DATA: {377CHECK_SIZE(u.gnss_new_data);378gnss_data.new_data = u.gnss_new_data;379break;380}381case MessageType::GNSS_JAM_STATUS: {382CHECK_SIZE(u.gnss_jam_status);383gnss_data.jam_status = u.gnss_jam_status;384break;385}386case MessageType::DIFFERENTIAL_PRESSURE: {387CHECK_SIZE(u.differential_pressure);388airspeed_data.differential_pressure = u.differential_pressure*1.0e-4*100; // 100: mbar to Pa389break;390}391case MessageType::TRUE_AIRSPEED: {392CHECK_SIZE(u.true_airspeed);393state2.true_airspeed = u.true_airspeed*0.01; // m/s394break;395}396case MessageType::WIND_SPEED: {397CHECK_SIZE(u.wind_speed);398state2.wind_speed = u.wind_speed.tofloat().rfu_to_frd()*0.01; // m/s399break;400}401case MessageType::AIR_DATA_STATUS: {402CHECK_SIZE(u.air_data_status);403state2.air_data_status = u.air_data_status;404break;405}406case MessageType::SUPPLY_VOLTAGE: {407CHECK_SIZE(u.supply_voltage);408state2.supply_voltage = u.supply_voltage*0.01; // V409break;410}411case MessageType::TEMPERATURE: {412CHECK_SIZE(u.temperature);413// assume same temperature for baro and airspeed414baro_data.temperature = u.temperature*0.1; // degC415airspeed_data.temperature = u.temperature*0.1; // degC416ins_data.temperature = u.temperature*0.1;417break;418}419case MessageType::UNIT_STATUS2: {420CHECK_SIZE(u.unit_status2);421state2.unit_status2 = u.unit_status2;422break;423}424case MessageType::GNSS_ANGLES: {425CHECK_SIZE(u.gnss_angles);426gnss_data.heading = u.gnss_angles.heading*0.01; // deg427gnss_data.pitch = u.gnss_angles.pitch*0.01; // deg428break;429}430case MessageType::GNSS_ANGLE_POS_TYPE: {431CHECK_SIZE(u.gnss_angle_pos_type);432gnss_data.angle_pos_type = u.gnss_angle_pos_type;433break;434}435case MessageType::GNSS_HEADING_TIMESTAMP: {436CHECK_SIZE(u.gnss_heading_timestamp);437gnss_data.heading_timestamp = u.gnss_heading_timestamp;438break;439}440case MessageType::GNSS_DOP: {441CHECK_SIZE(u.gnss_dop);442gnss_data.gdop = u.gnss_dop.gdop*0.1;443gnss_data.pdop = u.gnss_dop.pdop*0.1;444gnss_data.tdop = u.gnss_dop.tdop*0.1;445446gps_data.hdop = u.gnss_dop.hdop*0.1;447gps_data.vdop = u.gnss_dop.vdop*0.1;448break;449}450case MessageType::INS_SOLUTION_STATUS: {451CHECK_SIZE(u.ins_sol_status);452state2.ins_sol_status = u.ins_sol_status;453break;454}455}456457if (msg_len == 0) {458// got an unknown message459GCS_SEND_TEXT(MAV_SEVERITY_INFO, "InertialLabs: unknown msg 0x%02x", unsigned(mtype));460buffer_ofs = 0;461return false;462}463message_ofs += msg_len;464465if (msg_len == 0 || need_re_sync) {466re_sync();467return false;468}469}470471if (h->msg_len != message_ofs-buffer) {472// we had stray bytes at the end of the message473re_sync();474return false;475}476477if (GOT_MSG(ACCEL_DATA_HR) &&478GOT_MSG(GYRO_DATA_HR)) {479AP::ins().handle_external(ins_data);480state.accel = ins_data.accel;481state.gyro = ins_data.gyro;482483#if HAL_LOGGING_ENABLED484uint64_t now_us = AP_HAL::micros64();485486// @LoggerMessage: ILB1487// @Description: InertialLabs AHRS data1488// @Field: TimeUS: Time since system startup489// @Field: GMS: GPS INS time (round)490// @Field: GyrX: Gyro X491// @Field: GyrY: Gyro Y492// @Field: GyrZ: Gyro z493// @Field: AccX: Accelerometer X494// @Field: AccY: Accelerometer Y495// @Field: AccZ: Accelerometer Z496497AP::logger().WriteStreaming("ILB1", "TimeUS,GMS,GyrX,GyrY,GyrZ,AccX,AccY,AccZ",498"s-kkkooo",499"F-------",500"QIffffff",501now_us, gps_data.ms_tow,502ins_data.gyro.x, ins_data.gyro.y, ins_data.gyro.z,503ins_data.accel.x, ins_data.accel.y, ins_data.accel.z);504#endif // HAL_LOGGING_ENABLED505}506507if (GOT_MSG(GPS_INS_TIME_MS) &&508GOT_MSG(NUM_SATS) &&509GOT_MSG(GNSS_POSITION) &&510GOT_MSG(GNSS_NEW_DATA) &&511GOT_MSG(GNSS_EXTENDED_INFO) &&512gnss_data.new_data != 0) {513uint8_t instance;514if (AP::gps().get_first_external_instance(instance)) {515AP::gps().handle_external(gps_data, instance);516}517if (gps_data.satellites_in_view > 3) {518if (last_gps_ms == 0) {519GCS_SEND_TEXT(MAV_SEVERITY_INFO, "InertialLabs: got GPS lock");520if (!state.have_origin) {521state.origin = Location{522gps_data.latitude,523gps_data.longitude,524gps_data.msl_altitude,525Location::AltFrame::ABSOLUTE};526state.have_origin = true;527}528}529last_gps_ms = now_ms;530}531532#if HAL_LOGGING_ENABLED533uint64_t now_us = AP_HAL::micros64();534535// @LoggerMessage: ILB4536// @Description: InertialLabs AHRS data4537// @Field: TimeUS: Time since system startup538// @Field: GMS: GNSS Position timestamp539// @Field: GWk: GPS Week540// @Field: NSat: Number of satellites541// @Field: NewGPS: Indicator of new update of GPS data542// @Field: Lat: GNSS Latitude543// @Field: Lng: GNSS Longitude544// @Field: Alt: GNSS Altitude545// @Field: GCrs: GNSS Track over ground546// @Field: Spd: GNSS Horizontal speed547// @Field: VZ: GNSS Vertical speed548549AP::logger().WriteStreaming("ILB4", "TimeUS,GMS,GWk,NSat,NewGPS,Lat,Lng,Alt,GCrs,Spd,VZ",550"s----DUmhnn",551"F----------",552"QIHBBffffff",553now_us, gnss_data.pos_timestamp, gps_data.gps_week,554gps_data.satellites_in_view, gnss_data.new_data,555gnss_data.lat*1.0e-7, gnss_data.lng*1.0e-7, gnss_data.alt*0.01,556gnss_data.track_over_ground, gnss_data.hor_speed, gnss_data.ver_speed557);558559// @LoggerMessage: ILB5560// @Description: InertialLabs AHRS data5561// @Field: TimeUS: Time since system startup562// @Field: GMS: GNSS Position timestamp563// @Field: FType: fix type564// @Field: GSS: GNSS spoofing status565// @Field: GJS: GNSS jamming status566// @Field: GI1: GNSS Info1567// @Field: GI2: GNSS Info2568// @Field: GAPS: GNSS Angles position type569570AP::logger().WriteStreaming("ILB5", "TimeUS,GMS,FType,GSS,GJS,GI1,GI2,GAPS",571"s-------",572"F-------",573"QIBBBBBB",574now_us, gnss_data.pos_timestamp, gps_data.fix_type,575gnss_data.spoof_status, gnss_data.jam_status,576gnss_data.info_short.info1, gnss_data.info_short.info2,577gnss_data.angle_pos_type);578579// @LoggerMessage: ILB6580// @Description: InertialLabs AHRS data6581// @Field: TimeUS: Time since system startup582// @Field: GMS: GNSS Position timestamp583// @Field: GpsHTS: GNSS Heading timestamp584// @Field: GpsYaw: GNSS Heading585// @Field: GpsPitch: GNSS Pitch586// @Field: GDOP: GNSS GDOP587// @Field: PDOP: GNSS PDOP588// @Field: HDOP: GNSS HDOP589// @Field: VDOP: GNSS VDOP590// @Field: TDOP: GNSS TDOP591592AP::logger().WriteStreaming("ILB6", "TimeUS,GMS,GpsHTS,GpsYaw,GpsPitch,GDOP,PDOP,HDOP,VDOP,TDOP",593"s--hd-----",594"F---------",595"QIIfffffff",596now_us, gnss_data.pos_timestamp, gnss_data.heading_timestamp,597gnss_data.heading, gnss_data.pitch, gnss_data.gdop, gnss_data.pdop,598gps_data.hdop, gps_data.vdop, gnss_data.tdop);599#endif // HAL_LOGGING_ENABLED600}601602#if AP_BARO_EXTERNALAHRS_ENABLED603if (GOT_MSG(BARO_DATA) &&604GOT_MSG(TEMPERATURE)) {605AP::baro().handle_external(baro_data);606607#if HAL_LOGGING_ENABLED608uint64_t now_us = AP_HAL::micros64();609610// @LoggerMessage: ILB3611// @Description: InertialLabs AHRS data3612// @Field: TimeUS: Time since system startup613// @Field: GMS: GPS INS time (round)614// @Field: Press: Static pressure615// @Field: Diff: Differential pressure616// @Field: Temp: Temperature617// @Field: Alt: Baro altitude618// @Field: TAS: true airspeed619// @Field: VWN: Wind velocity north620// @Field: VWE: Wind velocity east621// @Field: VWD: Wind velocity down622// @Field: ADU: Air Data Unit status623624AP::logger().WriteStreaming("ILB3", "TimeUS,GMS,Press,Diff,Temp,Alt,TAS,VWN,VWE,VWD,ADU",625"s-PPOmnnnn-",626"F----------",627"QIffffffffH",628now_us, gps_data.ms_tow,629baro_data.pressure_pa, airspeed_data.differential_pressure, baro_data.temperature,630state2.baro_alt, state2.true_airspeed,631state2.wind_speed.x, state2.wind_speed.y, state2.wind_speed.z,632state2.air_data_status);633#endif // HAL_LOGGING_ENABLED634}635#endif // AP_BARO_EXTERNALAHRS_ENABLED636637#if AP_COMPASS_EXTERNALAHRS_ENABLED638if (GOT_MSG(MAG_DATA)) {639AP::compass().handle_external(mag_data);640641#if HAL_LOGGING_ENABLED642uint64_t now_us = AP_HAL::micros64();643644// @LoggerMessage: ILB2645// @Description: InertialLabs AHRS data2646// @Field: TimeUS: Time since system startup647// @Field: GMS: GPS INS time (round)648// @Field: MagX: Magnetometer X649// @Field: MagY: Magnetometer Y650// @Field: MagZ: Magnetometer Z651652AP::logger().WriteStreaming("ILB2", "TimeUS,GMS,MagX,MagY,MagZ",653"s----",654"F----",655"QIfff",656now_us, gps_data.ms_tow,657mag_data.field.x, mag_data.field.y, mag_data.field.z);658#endif // HAL_LOGGING_ENABLED659}660#endif // AP_COMPASS_EXTERNALAHRS_ENABLED661662#if AP_AIRSPEED_EXTERNAL_ENABLED && (APM_BUILD_COPTER_OR_HELI || APM_BUILD_TYPE(APM_BUILD_ArduPlane))663// only on plane and copter as others do not link AP_Airspeed664if (GOT_MSG(DIFFERENTIAL_PRESSURE) &&665GOT_MSG(TEMPERATURE)) {666auto *arsp = AP::airspeed();667if (arsp != nullptr) {668arsp->handle_external(airspeed_data);669}670}671672#endif // AP_AIRSPEED_EXTERNAL_ENABLED673674buffer_ofs = 0;675676if (GOT_MSG(POSITION) &&677GOT_MSG(ORIENTATION_ANGLES) &&678GOT_MSG(VELOCITIES)) {679680float roll, pitch, yaw_deg;681state.quat.to_euler(roll, pitch, yaw_deg);682683yaw_deg = fmodf(degrees(yaw_deg), 360.0f);684if (yaw_deg < 0.0f) {685yaw_deg += 360.0f;686}687688#if HAL_LOGGING_ENABLED689uint64_t now_us = AP_HAL::micros64();690691// @LoggerMessage: ILB7692// @Description: InertialLabs AHRS data7693// @Field: TimeUS: Time since system startup694// @Field: GMS: GPS INS time (round)695// @Field: Roll: euler roll696// @Field: Pitch: euler pitch697// @Field: Yaw: euler yaw698// @Field: VN: velocity north699// @Field: VE: velocity east700// @Field: VD: velocity down701// @Field: Lat: latitude702// @Field: Lng: longitude703// @Field: Alt: altitude MSL704// @Field: USW: USW1705// @Field: USW2: USW2706// @Field: Vdc: Supply voltage707708AP::logger().WriteStreaming("ILB7", "TimeUS,GMS,Roll,Pitch,Yaw,VN,VE,VD,Lat,Lng,Alt,USW,USW2,Vdc",709"s-dddnnnDUm--v",710"F-------------",711"QIfffffffffHHf",712now_us, gps_data.ms_tow,713degrees(roll), degrees(pitch), yaw_deg,714state.velocity.x, state.velocity.y, state.velocity.z,715state.location.lat*1.0e-7, state.location.lng*1.0e-7, state.location.alt*0.01,716state2.unit_status, state2.unit_status2,717state2.supply_voltage);718719// @LoggerMessage: ILB8720// @Description: InertialLabs AHRS data8721// @Field: TimeUS: Time since system startup722// @Field: GMS: GPS INS time (round)723// @Field: PVN: position variance north724// @Field: PVE: position variance east725// @Field: PVD: position variance down726// @Field: VVN: velocity variance north727// @Field: VVE: velocity variance east728// @Field: VVD: velocity variance down729730AP::logger().WriteStreaming("ILB8", "TimeUS,GMS,PVN,PVE,PVD,VVN,VVE,VVD",731"s-mmmnnn",732"F-------",733"QIffffff",734now_us, gps_data.ms_tow,735state2.kf_pos_covariance.x, state2.kf_pos_covariance.y, state2.kf_pos_covariance.z,736state2.kf_vel_covariance.x, state2.kf_vel_covariance.y, state2.kf_vel_covariance.z);737#endif // HAL_LOGGING_ENABLED738}739740const uint32_t dt_critical_usw = 10000;741uint32_t now_usw = AP_HAL::millis();742743// InertialLabs critical messages to GCS (sending messages once every 10 seconds)744if ((last_unit_status != state2.unit_status) ||745(last_unit_status2 != state2.unit_status2) ||746(last_air_data_status != state2.air_data_status) ||747(now_usw - last_critical_msg_ms > dt_critical_usw)) {748749// Critical USW message750if (state2.unit_status & ILABS_UNIT_STATUS_ALIGNMENT_FAIL) {751GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "ILAB: Unsuccessful initial alignment");752}753if (state2.unit_status & ILABS_UNIT_STATUS_OPERATION_FAIL) {754GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "ILAB: IMU data are incorrect");755}756757if (state2.unit_status & ILABS_UNIT_STATUS_GYRO_FAIL) {758GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "ILAB: Gyros failure");759}760761if (state2.unit_status & ILABS_UNIT_STATUS_ACCEL_FAIL) {762GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "ILAB: Accelerometers failure");763}764765if (state2.unit_status & ILABS_UNIT_STATUS_MAG_FAIL) {766GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "ILAB: Magnetometers failure");767}768769if (state2.unit_status & ILABS_UNIT_STATUS_ELECTRONICS_FAIL) {770GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "ILAB: Electronics failure");771}772773if (state2.unit_status & ILABS_UNIT_STATUS_GNSS_FAIL) {774GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "ILAB: GNSS receiver failure");775}776777// Critical USW2 message778if (state2.unit_status2 & ILABS_UNIT_STATUS2_BARO_FAIL) {779GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "ILAB: Baro altimeter failure");780}781782if (state2.unit_status2 & ILABS_UNIT_STATUS2_DIFF_PRESS_FAIL) {783GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "ILAB: Diff. pressure sensor failure");784}785786// Critical ADU message787if (state2.air_data_status & ILABS_AIRDATA_INIT_FAIL) {788GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "ILAB: Static pressure sensor unsuccessful initialization");789}790791if (state2.air_data_status & ILABS_AIRDATA_DIFF_PRESS_INIT_FAIL) {792GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "ILAB: Diff. pressure sensor unsuccessful initialization");793}794795if (state2.air_data_status & ILABS_AIRDATA_STATIC_PRESS_FAIL) {796GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "ILAB: Static pressure sensor failure is detect");797}798799if (state2.air_data_status & ILABS_AIRDATA_DIFF_PRESS_FAIL) {800GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "ILAB: Diff. pressure sensor failure is detect");801}802803last_critical_msg_ms = AP_HAL::millis();804}805806if (last_unit_status != state2.unit_status) {807if (state2.unit_status & ILABS_UNIT_STATUS_RUNTIME_CAL) {808GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: On-the-fly calibration is in progress");809}810811if (state2.unit_status & ILABS_UNIT_STATUS_VOLTAGE_LOW) {812GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: Low input voltage");813} else {814if (last_unit_status & ILABS_UNIT_STATUS_VOLTAGE_LOW) {815GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Input voltage is in range");816}817}818819if (state2.unit_status & ILABS_UNIT_STATUS_VOLTAGE_HIGH) {820GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: High input voltage");821} else {822if (last_unit_status & ILABS_UNIT_STATUS_VOLTAGE_HIGH) {823GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Input voltage is in range");824}825}826827if (state2.unit_status & ILABS_UNIT_STATUS_X_RATE_HIGH) {828GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: Y-axis angular rate is exceeded");829} else {830if (last_unit_status & ILABS_UNIT_STATUS_X_RATE_HIGH) {831GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Y-axis angular rate is in range");832}833}834835if (state2.unit_status & ILABS_UNIT_STATUS_Y_RATE_HIGH) {836GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: X-axis angular rate is exceeded");837} else {838if (last_unit_status & ILABS_UNIT_STATUS_Y_RATE_HIGH) {839GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: X-axis angular rate is in range");840}841}842843if (state2.unit_status & ILABS_UNIT_STATUS_Z_RATE_HIGH) {844GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: Z-axis angular rate is exceeded");845} else {846if (last_unit_status & ILABS_UNIT_STATUS_Z_RATE_HIGH) {847GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Z-axis angular rate is in range");848}849}850851if (state2.unit_status & ILABS_UNIT_STATUS_MAG_FIELD_HIGH) {852GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: Large total magnetic field");853} else {854if (last_unit_status & ILABS_UNIT_STATUS_MAG_FIELD_HIGH) {855GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Total magnetic field is in range");856}857}858859if (state2.unit_status & ILABS_UNIT_STATUS_TEMP_RANGE_ERR) {860GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: Temperature is out of range");861} else {862if (last_unit_status & ILABS_UNIT_STATUS_TEMP_RANGE_ERR) {863GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Temperature is in range");864}865}866867if (state2.unit_status & ILABS_UNIT_STATUS_RUNTIME_CAL2) {868GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: On-the-fly calibration successful");869}870871last_unit_status = state2.unit_status;872}873874// InertialLabs INS Unit Status Word 2 (USW2) messages to GCS875if (last_unit_status2 != state2.unit_status2) {876if (state2.unit_status2 & ILABS_UNIT_STATUS2_ACCEL_X_HIGH) {877GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: Y-acceleration is out of range");878} else {879if (last_unit_status2 & ILABS_UNIT_STATUS2_ACCEL_X_HIGH) {880GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Y-acceleration is in range");881}882}883884if (state2.unit_status2 & ILABS_UNIT_STATUS2_ACCEL_Y_HIGH) {885GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: X-acceleration is out of range");886} else {887if (last_unit_status2 & ILABS_UNIT_STATUS2_ACCEL_Y_HIGH) {888GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: X-acceleration is in range");889}890}891892if (state2.unit_status2 & ILABS_UNIT_STATUS2_ACCEL_Z_HIGH) {893GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: Z-acceleration is out of range");894} else {895if (last_unit_status2 & ILABS_UNIT_STATUS2_ACCEL_Z_HIGH) {896GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Z-acceleration is in range");897}898}899900if (state2.unit_status2 & ILABS_UNIT_STATUS2_MAGCAL_2D_ACT) {901GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Automatic 2D calibration is in progress");902}903904if (state2.unit_status2 & ILABS_UNIT_STATUS2_MAGCAL_3D_ACT) {905GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Automatic 3D calibration is in progress");906}907908if (state2.unit_status2 & ILABS_UNIT_STATUS2_GNSS_FUSION_OFF) {909GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: GNSS input switched off");910} else {911if (last_unit_status2 & ILABS_UNIT_STATUS2_GNSS_FUSION_OFF) {912GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: GNSS input switched on");913}914}915916if (state2.unit_status2 & ILABS_UNIT_STATUS2_DIFF_PRESS_FUSION_OFF) {917GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Diff. pressure input switched off");918} else {919if (last_unit_status2 & ILABS_UNIT_STATUS2_DIFF_PRESS_FUSION_OFF) {920GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Diff. pressure input switched on");921}922}923924if (state2.unit_status2 & ILABS_UNIT_STATUS2_MAG_FUSION_OFF) {925GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Magnetometer input switched off");926} else {927if (last_unit_status2 & ILABS_UNIT_STATUS2_MAG_FUSION_OFF) {928GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Magnetometer input switched on");929}930}931932if (state2.unit_status2 & ILABS_UNIT_STATUS2_GNSS_POS_VALID) {933GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: Incorrect GNSS position");934} else {935if (last_unit_status2 & ILABS_UNIT_STATUS2_GNSS_POS_VALID) {936GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: GNSS position is correct");937}938}939940last_unit_status2 = state2.unit_status2;941}942943// InertialLabs INS Air Data Unit (ADU) status messages to GCS944if (last_air_data_status != state2.air_data_status) {945if (state2.air_data_status & ILABS_AIRDATA_STATIC_PRESS_RANGE_ERR) {946GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: Static pressure is out of range");947} else {948if (last_air_data_status & ILABS_AIRDATA_STATIC_PRESS_RANGE_ERR) {949GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Static pressure is in range");950}951}952953if (state2.air_data_status & ILABS_AIRDATA_DIFF_PRESS_RANGE_ERR) {954GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: Diff. pressure is out of range");955} else {956if (last_air_data_status & ILABS_AIRDATA_DIFF_PRESS_RANGE_ERR) {957GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Diff. pressure is in range");958}959}960961if (state2.air_data_status & ILABS_AIRDATA_PRESS_ALT_FAIL) {962GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: Pressure altitude is incorrect");963} else {964if (last_air_data_status & ILABS_AIRDATA_PRESS_ALT_FAIL) {965GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Pressure altitude is correct");966}967}968969if (state2.air_data_status & ILABS_AIRDATA_AIRSPEED_FAIL) {970GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: Air speed is incorrect");971} else {972if (last_air_data_status & ILABS_AIRDATA_AIRSPEED_FAIL) {973GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Air speed is correct");974}975}976977if (state2.air_data_status & ILABS_AIRDATA_AIRSPEED_FAIL) {978GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: Air speed is below the threshold");979} else {980if (last_air_data_status & ILABS_AIRDATA_AIRSPEED_FAIL) {981GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: Air speed is above the threshold");982}983}984985last_air_data_status = state2.air_data_status;986}987988// InertialLabs INS spoofing detection messages to GCS989if (last_spoof_status != gnss_data.spoof_status) {990if ((last_spoof_status == 2 || last_spoof_status == 3) && (gnss_data.spoof_status == 1)) {991GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: GNSS no spoofing");992}993994if (last_spoof_status == 2) {995GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: GNSS spoofing indicated");996}997998if (last_spoof_status == 3) {999GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: GNSS multiple spoofing indicated");1000}10011002last_spoof_status = gnss_data.spoof_status;1003}10041005// InertialLabs INS jamming detection messages to GCS1006if (last_jam_status != gnss_data.jam_status) {1007if ((last_jam_status == 2 || last_jam_status == 3) && (gnss_data.jam_status == 1)) {1008GCS_SEND_TEXT(MAV_SEVERITY_INFO, "ILAB: GNSS no jamming");1009}10101011if (gnss_data.jam_status == 3) {1012GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "ILAB: GNSS jamming indicated and no fix");1013}10141015last_jam_status = gnss_data.jam_status;1016}10171018return true;1019}10201021void AP_ExternalAHRS_InertialLabs::update_thread()1022{1023// Open port in the thread1024uart->begin(baudrate, 1024, 512);10251026/*1027we assume the user has already configured the device1028*/10291030setup_complete = true;1031while (true) {1032if (!check_uart()) {1033hal.scheduler->delay_microseconds(250);1034}1035}1036}10371038// get serial port number for the uart1039int8_t AP_ExternalAHRS_InertialLabs::get_port(void) const1040{1041if (!uart) {1042return -1;1043}1044return port_num;1045};10461047// accessors for AP_AHRS1048bool AP_ExternalAHRS_InertialLabs::healthy(void) const1049{1050WITH_SEMAPHORE(state.sem);1051return AP_HAL::millis() - last_att_ms < 100;1052}10531054bool AP_ExternalAHRS_InertialLabs::initialised(void) const1055{1056if (!setup_complete) {1057return false;1058}1059return true;1060}10611062bool AP_ExternalAHRS_InertialLabs::pre_arm_check(char *failure_msg, uint8_t failure_msg_len) const1063{1064if (!setup_complete) {1065hal.util->snprintf(failure_msg, failure_msg_len, "InertialLabs setup failed");1066return false;1067}1068if (!healthy()) {1069hal.util->snprintf(failure_msg, failure_msg_len, "InertialLabs unhealthy");1070return false;1071}1072WITH_SEMAPHORE(state.sem);1073uint32_t now = AP_HAL::millis();1074if (now - last_att_ms > 10 ||1075now - last_pos_ms > 10 ||1076now - last_vel_ms > 10) {1077hal.util->snprintf(failure_msg, failure_msg_len, "InertialLabs not up to date");1078return false;1079}1080return true;1081}10821083/*1084get filter status. We don't know the meaning of the status bits yet,1085so assume all OK if we have GPS lock1086*/1087void AP_ExternalAHRS_InertialLabs::get_filter_status(nav_filter_status &status) const1088{1089WITH_SEMAPHORE(state.sem);1090uint32_t now = AP_HAL::millis();1091const uint32_t dt_limit = 200;1092const uint32_t dt_limit_gps = 500;1093memset(&status, 0, sizeof(status));1094const bool init_ok = (state2.unit_status & (ILABS_UNIT_STATUS_ALIGNMENT_FAIL|ILABS_UNIT_STATUS_OPERATION_FAIL))==0;1095status.flags.initalized = init_ok;1096status.flags.attitude = init_ok && (now - last_att_ms < dt_limit) && init_ok;1097status.flags.vert_vel = init_ok && (now - last_vel_ms < dt_limit);1098status.flags.vert_pos = init_ok && (now - last_pos_ms < dt_limit);1099status.flags.horiz_vel = status.flags.vert_vel;1100status.flags.horiz_pos_abs = status.flags.vert_pos;1101status.flags.horiz_pos_rel = status.flags.horiz_pos_abs;1102status.flags.pred_horiz_pos_rel = status.flags.horiz_pos_abs;1103status.flags.pred_horiz_pos_abs = status.flags.horiz_pos_abs;1104status.flags.using_gps = (now - last_gps_ms < dt_limit_gps) &&1105((state2.unit_status & ILABS_UNIT_STATUS_GNSS_FAIL) | (state2.unit_status2 & ILABS_UNIT_STATUS2_GNSS_FUSION_OFF)) == 0;1106status.flags.gps_quality_good = (now - last_gps_ms < dt_limit_gps) &&1107(state2.unit_status2 & ILABS_UNIT_STATUS2_GNSS_POS_VALID) != 0 &&1108(state2.unit_status & ILABS_UNIT_STATUS_GNSS_FAIL) == 0;1109status.flags.rejecting_airspeed = (state2.air_data_status & ILABS_AIRDATA_AIRSPEED_FAIL);1110}11111112// get variances1113bool AP_ExternalAHRS_InertialLabs::get_variances(float &velVar, float &posVar, float &hgtVar, Vector3f &magVar, float &tasVar) const1114{1115velVar = state2.kf_vel_covariance.length() * vel_gate_scale;1116posVar = state2.kf_pos_covariance.xy().length() * pos_gate_scale;1117hgtVar = state2.kf_pos_covariance.z * hgt_gate_scale;1118tasVar = 0;1119return true;1120}11211122#endif // AP_EXTERNAL_AHRS_INERTIALLABS_ENABLED1123112411251126