Path: blob/master/libraries/AP_ExternalAHRS/AP_ExternalAHRS_SensAItion_Parser.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*/1415#include <cassert>16#include <string.h> // Required for memchr, memmove17#include "AP_ExternalAHRS_SensAItion_Parser.h"18#include <AP_GPS/GPS_Backend.h>19#include <AP_HAL/AP_HAL.h>20#include <AP_HAL/utility/sparse-endian.h>21#include <GCS_MAVLink/GCS.h>2223// Constructor24AP_ExternalAHRS_SensAItion_Parser::AP_ExternalAHRS_SensAItion_Parser(ConfigMode mode) :25config_mode(mode)26{27reset_parser();28}2930size_t AP_ExternalAHRS_SensAItion_Parser::parse_stream(const uint8_t* data, size_t data_size, Measurement& meas)31{32for (size_t i = 0; i < data_size; i++) {33if (parse_single_byte(data[i])) {34decode_packet(meas);35reset_parser(); // Ready for more packets in same buffer36return i + 1; // No of bytes parsed37}38}3940meas = Measurement(); // Uninitialized41return data_size;42}4344void AP_ExternalAHRS_SensAItion_Parser::reset_parser()45{46parse_state = ParseState::WAITING_HEADER;47packet_buffer_len = 0;48target_payload_len = 0;49current_packet_id = PacketID::UNKNOWN;50}5152// Error Handler53void AP_ExternalAHRS_SensAItion_Parser::handle_invalid_packet()54{55// Fallback if called with empty buffer, which should never happen56if (packet_buffer_len == 0) {57return;58}5960// Look for a new header byte starting from index 1 to resync61uint8_t *p = (uint8_t *)memchr(&packet_buffer[1], HEADER_BYTE, packet_buffer_len - 1);6263if (p == nullptr) {64// No header found, reset completely65reset_parser();66return;67}6869const size_t bytes_to_discard = p - packet_buffer;70const size_t bytes_to_keep = packet_buffer_len - bytes_to_discard;7172memmove(&packet_buffer[0], p, bytes_to_keep);73packet_buffer_len = bytes_to_keep;7475// Determine State based on Mode and remaining data76if (config_mode == ConfigMode::INTERLEAVED_INS) {77if (packet_buffer_len >= 2) {78// We have Header + Potential ID. Process it immediately.79uint8_t id = packet_buffer[1];80switch (static_cast<PacketID>(id)) {81case PacketID::IMU:82target_payload_len = PAYLOAD_SIZE_IMU;83parse_state = ParseState::COLLECTING_PAYLOAD;84current_packet_id = PacketID::IMU;85break;86case PacketID::AHRS:87target_payload_len = PAYLOAD_SIZE_QUAT;88parse_state = ParseState::COLLECTING_PAYLOAD;89current_packet_id = PacketID::AHRS;90break;91case PacketID::INS:92target_payload_len = PAYLOAD_SIZE_INS;93parse_state = ParseState::COLLECTING_PAYLOAD;94current_packet_id = PacketID::INS;95break;96default:97// The "New" ID is also bad. Drop header and retry.98reset_parser();99return;100}101} else {102parse_state = ParseState::WAITING_ID;103}104} else {105// Legacy Mode106target_payload_len = PAYLOAD_SIZE_IMU;107parse_state = ParseState::COLLECTING_PAYLOAD;108}109}110111// Checksum Validator & Deep Debugger112bool AP_ExternalAHRS_SensAItion_Parser::buffer_contains_valid_packet() const113{114if (packet_buffer_len < 2 || packet_buffer[0] != HEADER_BYTE) {115return false;116}117118const uint8_t calculated = crc_xor_of_bytes(&packet_buffer[1], packet_buffer_len - 2);119const uint8_t received = packet_buffer[packet_buffer_len - 1];120121if (calculated != received) {122return false;123}124125return true;126}127128// The Core State Machine129bool AP_ExternalAHRS_SensAItion_Parser::parse_single_byte(uint8_t byte)130{131// Safety: Prevent buffer overflow132if (packet_buffer_len >= MAX_PACKET_SIZE) {133handle_invalid_packet();134if (packet_buffer_len >= MAX_PACKET_SIZE) {135reset_parser();136}137}138139switch (parse_state) {140case ParseState::WAITING_HEADER:141if (byte == HEADER_BYTE) {142packet_buffer_len = 0;143packet_buffer[packet_buffer_len++] = byte;144145if (config_mode == ConfigMode::INTERLEAVED_INS) {146parse_state = ParseState::WAITING_ID;147} else {148target_payload_len = PAYLOAD_SIZE_IMU;149parse_state = ParseState::COLLECTING_PAYLOAD;150}151}152break;153154case ParseState::WAITING_ID:155packet_buffer[packet_buffer_len++] = byte;156157switch (static_cast<PacketID>(byte)) {158case PacketID::IMU:159target_payload_len = PAYLOAD_SIZE_IMU;160current_packet_id = PacketID::IMU;161parse_state = ParseState::COLLECTING_PAYLOAD;162break;163164case PacketID::AHRS:165target_payload_len = PAYLOAD_SIZE_QUAT;166current_packet_id = PacketID::AHRS;167parse_state = ParseState::COLLECTING_PAYLOAD;168break;169170case PacketID::INS:171target_payload_len = PAYLOAD_SIZE_INS;172current_packet_id = PacketID::INS;173parse_state = ParseState::COLLECTING_PAYLOAD;174break;175176default:177parse_errors++;178handle_invalid_packet();179break;180}181break;182183case ParseState::COLLECTING_PAYLOAD:184packet_buffer[packet_buffer_len++] = byte;185186// Calculate Expected Total Length187// Legacy: Header(1) + Payload(N) + CRC(1)188// Interleaved: Header(1) + ID(1) + Payload(N) + CRC(1)189const size_t overhead = (config_mode == ConfigMode::INTERLEAVED_INS) ? 3 : 2;190const size_t expected_total_len = target_payload_len + overhead;191192if (packet_buffer_len >= expected_total_len) {193if (buffer_contains_valid_packet()) {194valid_packets++;195return true;196}197198parse_errors++;199handle_invalid_packet();200return false;201}202break;203}204205return false;206}207208bool AP_ExternalAHRS_SensAItion_Parser::validate_checksum() const209{210return buffer_contains_valid_packet();211}212213// Router214void AP_ExternalAHRS_SensAItion_Parser::decode_packet(Measurement& measurement)215{216const uint8_t* payload = nullptr;217218if (config_mode == ConfigMode::INTERLEAVED_INS) {219payload = &packet_buffer[2]; // Skip Header + ID220switch (current_packet_id) {221case PacketID::IMU:222decode_imu(payload, measurement);223break;224case PacketID::AHRS:225decode_ahrs(payload, measurement);226break;227case PacketID::INS:228decode_ins(payload, measurement);229break;230case PacketID::UNKNOWN:231assert(false && "decode_packet() should only be called with a valid packet ID!");232break;233}234} else {235payload = &packet_buffer[1]; // Skip Header236decode_imu(payload, measurement);237}238}239240// IMU Decoder (Packet 0)241void AP_ExternalAHRS_SensAItion_Parser::decode_imu(const uint8_t* payload, Measurement& measurement)242{243// ... [Code omitted: No changes to decoders, use your existing implementation] ...244// Accel (Bytes 0-11): 3 x Int32 (ug)245const int32_t accel_x_ug = be32toh_ptr(&payload[0]);246const int32_t accel_y_ug = be32toh_ptr(&payload[4]);247const int32_t accel_z_ug = be32toh_ptr(&payload[8]);248249// Gyro (Bytes 12-23): 3 x Int32 (udeg/s)250const int32_t gyro_x_udegs = be32toh_ptr(&payload[12]);251const int32_t gyro_y_udegs = be32toh_ptr(&payload[16]);252const int32_t gyro_z_udegs = be32toh_ptr(&payload[20]);253254// Temp (Bytes 24-25): 1 x Int16 (Scaled)255const int16_t temp_raw = be16toh_ptr(&payload[24]);256257// Mag (Bytes 26-31): 3 x Int16 (mGauss)258const int16_t mag_x_mgauss = be16toh_ptr(&payload[26]);259const int16_t mag_y_mgauss = be16toh_ptr(&payload[28]);260const int16_t mag_z_mgauss = be16toh_ptr(&payload[30]);261262// Baro (Bytes 32-35): 1 x Int32 (0.1 Pa)263const int32_t baro_raw = be32toh_ptr(&payload[32]);264265// Accel: ug -> m/s^2 (Note: 1,000,000 ug = 9.81 m/s^2 roughly)266const float ug_to_mss = 1.0e-6f * GRAVITY_MSS;267measurement.acceleration_mss = Vector3f(accel_x_ug, accel_y_ug, accel_z_ug) * ug_to_mss;268269// Gyro: udeg/s -> rad/s (Note: 1,000,000 udeg/s = 1 deg/s = 0.017 rad/s)270const float udeg_to_rad = 1.0e-6f * DEG_TO_RAD;271measurement.angular_velocity_rads = Vector3f(gyro_x_udegs, gyro_y_udegs, gyro_z_udegs) * udeg_to_rad;272273// Temp: (Raw * 0.008) + 20274measurement.temperature_degc = ((float)temp_raw * 0.008f) + 20.0f;275276// Mag: mGauss (Pass-through, ArduPilot expects mGauss)277measurement.magnetic_field_mgauss = Vector3f(mag_x_mgauss, mag_y_mgauss, mag_z_mgauss);278279// Baro: 0.1 Pa -> Pascal280measurement.air_pressure_p = (float)baro_raw * 0.1f;281282// Metadata283measurement.type = MeasurementType::IMU;284measurement.timestamp_us = AP_HAL::micros64();285}286287void AP_ExternalAHRS_SensAItion_Parser::decode_ahrs(const uint8_t* payload, Measurement& measurement)288{289// Payload layout: W (0-3), X (4-7), Y (8-11), Z (12-15)290const int32_t quat_w_raw = be32toh_ptr(&payload[0]);291const int32_t quat_x_raw = be32toh_ptr(&payload[4]);292const int32_t quat_y_raw = be32toh_ptr(&payload[8]);293const int32_t quat_z_raw = be32toh_ptr(&payload[12]);294295const float scale_factor = 1.0e-6f;296297measurement.orientation = Quaternion(298(float)quat_w_raw * scale_factor,299(float)quat_x_raw * scale_factor,300(float)quat_y_raw * scale_factor,301(float)quat_z_raw * scale_factor302);303304measurement.type = MeasurementType::AHRS;305measurement.timestamp_us = AP_HAL::micros64();306}307308void AP_ExternalAHRS_SensAItion_Parser::decode_ins(const uint8_t* payload, Measurement& measurement)309{310// --- 1. PARSE RAW BYTES (Big-Endian) ---311312// 0-3: Num Sats (G2, G1)313measurement.num_sats_gnss2 = payload[1];314measurement.num_sats_gnss1 = payload[3];315316// 4-7: Error Flags317measurement.error_flags = be32toh_ptr(&payload[4]);318319// 8: Sensor Valid320measurement.sensor_valid = payload[8];321322// 9-16: Lat/Lon (1e-7 deg)323const int32_t lat_raw = be32toh_ptr(&payload[9]);324const int32_t lon_raw = be32toh_ptr(&payload[13]);325326// 17-28: Velocity N, E, D (mm/s)327const int32_t vel_n_mm = be32toh_ptr(&payload[17]);328const int32_t vel_e_mm = be32toh_ptr(&payload[21]);329const int32_t vel_d_mm = be32toh_ptr(&payload[25]);330331// 29-32: Altitude relative to WGS 84 ellipsoid (mm)332const int32_t alt_raw_mm = be32toh_ptr(&payload[29]);333334// 33: Alignment Status335measurement.alignment_status = payload[33];336337// 34-37: Time of week (ms)338measurement.time_itow_ms = be32toh_ptr(&payload[34]);339340// 38-39: GNSS Fix341measurement.gnss2_fix = payload[38];342measurement.gnss1_fix = payload[39];343344// 40-44: UTC Date/Time345const uint16_t year = (uint16_t)((payload[40]<<8) | payload[41]);346const uint16_t month = (uint16_t)((payload[42]<<8) | payload[43]);347const uint8_t day = payload[44];348349// 45-68: Accuracy Metrics (mm or mm/s)350const int32_t acc_lat_mm = be32toh_ptr(&payload[45]);351const int32_t acc_lon_mm = be32toh_ptr(&payload[49]);352const int32_t acc_vn_mm = be32toh_ptr(&payload[53]);353const int32_t acc_ve_mm = be32toh_ptr(&payload[57]);354const int32_t acc_vd_mm = be32toh_ptr(&payload[61]);355const int32_t acc_vd_pos_mm = be32toh_ptr(&payload[65]);356357// --- 2. POPULATE & CONVERT ---358const float mm_to_cm = 0.1f;359measurement.location = Location(lat_raw, lon_raw, alt_raw_mm * mm_to_cm, Location::AltFrame::ABSOLUTE);360361const float mms_to_ms = 0.001f;362const float mm_to_m = 0.001f;363364measurement.velocity_ned.x = (float)vel_n_mm * mms_to_ms;365measurement.velocity_ned.y = (float)vel_e_mm * mms_to_ms;366measurement.velocity_ned.z = (float)vel_d_mm * mms_to_ms;367368// Accuracy369measurement.pos_accuracy.x = (float)acc_lat_mm * mm_to_m; // Horizontal Latitude370measurement.pos_accuracy.y = (float)acc_lon_mm * mm_to_m; // Horizontal Longitude371measurement.pos_accuracy.z = (float)acc_vd_pos_mm * mm_to_m; // Vertical372373measurement.vel_accuracy.x = (float)acc_vn_mm * mms_to_ms;374measurement.vel_accuracy.y = (float)acc_ve_mm * mms_to_ms;375measurement.vel_accuracy.z = (float)acc_vd_mm * mms_to_ms;376377// GPS Week Calculation (Gate check)378if (measurement.gnss1_fix >= 2) {379measurement.gps_week = calculate_gps_week(year, month, day);380} else {381measurement.gps_week = 0;382}383384measurement.type = MeasurementType::INS;385measurement.timestamp_us = AP_HAL::micros64();386387}388389uint16_t AP_ExternalAHRS_SensAItion_Parser::calculate_gps_week(uint16_t year, uint8_t month, uint8_t day)390{391// Convert to BCD form (DDMMYY) - the GPS_Backend function assumes year is 20YY392const uint32_t bcd_date = year % 100U + month * 100U + day * 10000U;393394const uint32_t bcd_time_ms = 0U;395uint16_t gps_week;396uint32_t gps_time_ms;397AP_GPS_Backend::BCD_to_gps_time(bcd_date, bcd_time_ms, gps_week, gps_time_ms);398399return gps_week;400}401402403