LCOV - code coverage report
Current view: top level - AP_ExternalAHRS - AP_ExternalAHRS_SensAItion_Parser.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 186 217 85.7 %
Date: 2026-09-09 11:02:08 Functions: 11 12 91.7 %

          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             : #include <cassert>
      17             : #include <string.h> // Required for memchr, memmove
      18             : #include "AP_ExternalAHRS_SensAItion_Parser.h"
      19             : #include <AP_GPS/GPS_Backend.h>
      20             : #include <AP_HAL/AP_HAL.h>
      21             : #include <AP_HAL/utility/sparse-endian.h>
      22             : #include <GCS_MAVLink/GCS.h>
      23             : 
      24             : // Constructor
      25          38 : AP_ExternalAHRS_SensAItion_Parser::AP_ExternalAHRS_SensAItion_Parser(ConfigMode mode) :
      26          38 :     config_mode(mode)
      27             : {
      28          38 :     reset_parser();
      29          38 : }
      30             : 
      31       60775 : size_t AP_ExternalAHRS_SensAItion_Parser::parse_stream(const uint8_t* data, size_t data_size, Measurement& meas)
      32             : {
      33     2338789 :     for (size_t i = 0; i < data_size; i++) {
      34     2338541 :         if (parse_single_byte(data[i])) {
      35       60527 :             decode_packet(meas);
      36       60527 :             reset_parser(); // Ready for more packets in same buffer
      37       60527 :             return i + 1; // No of bytes parsed
      38             :         }
      39             :     }
      40             : 
      41         248 :     meas = Measurement(); // Uninitialized
      42         248 :     return data_size;
      43             : }
      44             : 
      45       60569 : void AP_ExternalAHRS_SensAItion_Parser::reset_parser()
      46             : {
      47       60569 :     parse_state = ParseState::WAITING_HEADER;
      48       60569 :     packet_buffer_len = 0;
      49       60569 :     target_payload_len = 0;
      50       60569 :     current_packet_id = PacketID::UNKNOWN;
      51       60569 : }
      52             : 
      53             : // Error Handler
      54           6 : void AP_ExternalAHRS_SensAItion_Parser::handle_invalid_packet()
      55             : {
      56             :     // Fallback if called with empty buffer, which should never happen
      57           6 :     if (packet_buffer_len == 0) {
      58           0 :         return;
      59             :     }
      60             : 
      61             :     // Look for a new header byte starting from index 1 to resync
      62           6 :     uint8_t *p = (uint8_t *)memchr(&packet_buffer[1], HEADER_BYTE, packet_buffer_len - 1);
      63             : 
      64           6 :     if (p == nullptr) {
      65             :         // No header found, reset completely
      66           4 :         reset_parser();
      67           4 :         return;
      68             :     }
      69             : 
      70           2 :     const size_t bytes_to_discard = p - packet_buffer;
      71           2 :     const size_t bytes_to_keep = packet_buffer_len - bytes_to_discard;
      72             : 
      73           2 :     memmove(&packet_buffer[0], p, bytes_to_keep);
      74           2 :     packet_buffer_len = bytes_to_keep;
      75             : 
      76             :     // Determine State based on Mode and remaining data
      77           2 :     if (config_mode == ConfigMode::INTERLEAVED_INS) {
      78           0 :         if (packet_buffer_len >= 2) {
      79             :             // We have Header + Potential ID. Process it immediately.
      80           0 :             uint8_t id = packet_buffer[1];
      81           0 :             switch (static_cast<PacketID>(id)) {
      82           0 :             case PacketID::IMU:
      83           0 :                 target_payload_len = PAYLOAD_SIZE_IMU;
      84           0 :                 parse_state = ParseState::COLLECTING_PAYLOAD;
      85           0 :                 current_packet_id = PacketID::IMU;
      86           0 :                 break;
      87           0 :             case PacketID::AHRS:
      88           0 :                 target_payload_len = PAYLOAD_SIZE_QUAT;
      89           0 :                 parse_state = ParseState::COLLECTING_PAYLOAD;
      90           0 :                 current_packet_id = PacketID::AHRS;
      91           0 :                 break;
      92           0 :             case PacketID::INS:
      93           0 :                 target_payload_len = PAYLOAD_SIZE_INS;
      94           0 :                 parse_state = ParseState::COLLECTING_PAYLOAD;
      95           0 :                 current_packet_id = PacketID::INS;
      96           0 :                 break;
      97           0 :             default:
      98             :                 // The "New" ID is also bad. Drop header and retry.
      99           0 :                 reset_parser();
     100           0 :                 return;
     101             :             }
     102             :         } else {
     103           0 :             parse_state = ParseState::WAITING_ID;
     104             :         }
     105             :     } else {
     106             :         // Legacy Mode
     107           2 :         target_payload_len = PAYLOAD_SIZE_IMU;
     108           2 :         parse_state = ParseState::COLLECTING_PAYLOAD;
     109             :     }
     110             : }
     111             : 
     112             : // Checksum Validator & Deep Debugger
     113       60531 : bool AP_ExternalAHRS_SensAItion_Parser::buffer_contains_valid_packet() const
     114             : {
     115       60531 :     if (packet_buffer_len < 2 || packet_buffer[0] != HEADER_BYTE) {
     116           0 :         return false;
     117             :     }
     118             : 
     119       60531 :     const uint8_t calculated = crc_xor_of_bytes(&packet_buffer[1], packet_buffer_len - 2);
     120       60531 :     const uint8_t received = packet_buffer[packet_buffer_len - 1];
     121             : 
     122       60531 :     if (calculated != received) {
     123           4 :         return false;
     124             :     }
     125             : 
     126       60527 :     return true;
     127             : }
     128             : 
     129             : // The Core State Machine
     130     2338541 : bool AP_ExternalAHRS_SensAItion_Parser::parse_single_byte(uint8_t byte)
     131             : {
     132             :     // Safety: Prevent buffer overflow
     133     2338541 :     if (packet_buffer_len >= MAX_PACKET_SIZE) {
     134           0 :         handle_invalid_packet();
     135           0 :         if (packet_buffer_len >= MAX_PACKET_SIZE) {
     136           0 :             reset_parser();
     137             :         }
     138             :     }
     139             : 
     140     2338541 :     switch (parse_state) {
     141       60577 :     case ParseState::WAITING_HEADER:
     142       60577 :         if (byte == HEADER_BYTE) {
     143       60533 :             packet_buffer_len = 0;
     144       60533 :             packet_buffer[packet_buffer_len++] = byte;
     145             : 
     146       60533 :             if (config_mode == ConfigMode::INTERLEAVED_INS) {
     147       23695 :                 parse_state = ParseState::WAITING_ID;
     148             :             } else {
     149       36838 :                 target_payload_len = PAYLOAD_SIZE_IMU;
     150       36838 :                 parse_state = ParseState::COLLECTING_PAYLOAD;
     151             :             }
     152             :         }
     153       60577 :         break;
     154             : 
     155       23695 :     case ParseState::WAITING_ID:
     156       23695 :         packet_buffer[packet_buffer_len++] = byte;
     157             : 
     158             :         switch (static_cast<PacketID>(byte)) {
     159       21520 :         case PacketID::IMU:
     160       21520 :             target_payload_len = PAYLOAD_SIZE_IMU;
     161       21520 :             current_packet_id = PacketID::IMU;
     162       21520 :             parse_state = ParseState::COLLECTING_PAYLOAD;
     163       21520 :             break;
     164             : 
     165        1077 :         case PacketID::AHRS:
     166        1077 :             target_payload_len = PAYLOAD_SIZE_QUAT;
     167        1077 :             current_packet_id = PacketID::AHRS;
     168        1077 :             parse_state = ParseState::COLLECTING_PAYLOAD;
     169        1077 :             break;
     170             : 
     171        1096 :         case PacketID::INS:
     172        1096 :             target_payload_len = PAYLOAD_SIZE_INS;
     173        1096 :             current_packet_id = PacketID::INS;
     174        1096 :             parse_state = ParseState::COLLECTING_PAYLOAD;
     175        1096 :             break;
     176             : 
     177           2 :         default:
     178           2 :             parse_errors++;
     179           2 :             handle_invalid_packet();
     180           2 :             break;
     181             :         }
     182       23695 :         break;
     183             : 
     184     2254269 :     case ParseState::COLLECTING_PAYLOAD:
     185     2254269 :         packet_buffer[packet_buffer_len++] = byte;
     186             : 
     187             :         // Calculate Expected Total Length
     188             :         // Legacy: Header(1) + Payload(N) + CRC(1)
     189             :         // Interleaved: Header(1) + ID(1) + Payload(N) + CRC(1)
     190     2254269 :         const size_t overhead = (config_mode == ConfigMode::INTERLEAVED_INS) ? 3 : 2;
     191     2254269 :         const size_t expected_total_len = target_payload_len + overhead;
     192             : 
     193     2254269 :         if (packet_buffer_len >= expected_total_len) {
     194       60531 :             if (buffer_contains_valid_packet()) {
     195       60527 :                 valid_packets++;
     196       60527 :                 return true;
     197             :             }
     198             : 
     199           4 :             parse_errors++;
     200           4 :             handle_invalid_packet();
     201           4 :             return false;
     202             :         }
     203     2193738 :         break;
     204             :     }
     205             : 
     206     2278010 :     return false;
     207             : }
     208             : 
     209           0 : bool AP_ExternalAHRS_SensAItion_Parser::validate_checksum() const
     210             : {
     211           0 :     return buffer_contains_valid_packet();
     212             : }
     213             : 
     214             : // Router
     215       60527 : void AP_ExternalAHRS_SensAItion_Parser::decode_packet(Measurement& measurement)
     216             : {
     217       60527 :     const uint8_t* payload = nullptr;
     218             : 
     219       60527 :     if (config_mode == ConfigMode::INTERLEAVED_INS) {
     220       23693 :         payload = &packet_buffer[2]; // Skip Header + ID
     221       23693 :         switch (current_packet_id) {
     222       21520 :         case PacketID::IMU:
     223       21520 :             decode_imu(payload, measurement);
     224       21520 :             break;
     225        1077 :         case PacketID::AHRS:
     226        1077 :             decode_ahrs(payload, measurement);
     227        1077 :             break;
     228        1096 :         case PacketID::INS:
     229        1096 :             decode_ins(payload, measurement);
     230        1096 :             break;
     231           0 :         case PacketID::UNKNOWN:
     232           0 :             assert(false && "decode_packet() should only be called with a valid packet ID!");
     233             :             break;
     234             :         }
     235             :     } else {
     236       36834 :         payload = &packet_buffer[1]; // Skip Header
     237       36834 :         decode_imu(payload, measurement);
     238             :     }
     239       60527 : }
     240             : 
     241             : // IMU Decoder (Packet 0)
     242       58354 : void AP_ExternalAHRS_SensAItion_Parser::decode_imu(const uint8_t* payload, Measurement& measurement)
     243             : {
     244             :     // ... [Code omitted: No changes to decoders, use your existing implementation] ...
     245             :     // Accel (Bytes 0-11): 3 x Int32 (ug)
     246       58354 :     const int32_t accel_x_ug = be32toh_ptr(&payload[0]);
     247       58354 :     const int32_t accel_y_ug = be32toh_ptr(&payload[4]);
     248       58354 :     const int32_t accel_z_ug = be32toh_ptr(&payload[8]);
     249             : 
     250             :     // Gyro (Bytes 12-23): 3 x Int32 (udeg/s)
     251       58354 :     const int32_t gyro_x_udegs = be32toh_ptr(&payload[12]);
     252       58354 :     const int32_t gyro_y_udegs = be32toh_ptr(&payload[16]);
     253       58354 :     const int32_t gyro_z_udegs = be32toh_ptr(&payload[20]);
     254             : 
     255             :     // Temp (Bytes 24-25): 1 x Int16 (Scaled)
     256       58354 :     const int16_t temp_raw = be16toh_ptr(&payload[24]);
     257             : 
     258             :     // Mag (Bytes 26-31): 3 x Int16 (mGauss)
     259       58354 :     const int16_t mag_x_mgauss = be16toh_ptr(&payload[26]);
     260       58354 :     const int16_t mag_y_mgauss = be16toh_ptr(&payload[28]);
     261       58354 :     const int16_t mag_z_mgauss = be16toh_ptr(&payload[30]);
     262             : 
     263             :     // Baro (Bytes 32-35): 1 x Int32 (0.1 Pa)
     264       58354 :     const int32_t baro_raw = be32toh_ptr(&payload[32]);
     265             : 
     266             :     // Accel: ug -> m/s^2 (Note: 1,000,000 ug = 9.81 m/s^2 roughly)
     267       58354 :     const float ug_to_mss = 1.0e-6f * GRAVITY_MSS;
     268       58354 :     measurement.acceleration_mss = Vector3f(accel_x_ug, accel_y_ug, accel_z_ug) * ug_to_mss;
     269             : 
     270             :     // Gyro: udeg/s -> rad/s (Note: 1,000,000 udeg/s = 1 deg/s = 0.017 rad/s)
     271       58354 :     const float udeg_to_rad = 1.0e-6f * DEG_TO_RAD;
     272       58354 :     measurement.angular_velocity_rads = Vector3f(gyro_x_udegs, gyro_y_udegs, gyro_z_udegs) * udeg_to_rad;
     273             : 
     274             :     // Temp: (Raw * 0.008) + 20
     275       58354 :     measurement.temperature_degc = ((float)temp_raw * 0.008f) + 20.0f;
     276             : 
     277             :     // Mag: mGauss (Pass-through, ArduPilot expects mGauss)
     278       58354 :     measurement.magnetic_field_mgauss = Vector3f(mag_x_mgauss, mag_y_mgauss, mag_z_mgauss);
     279             : 
     280             :     // Baro: 0.1 Pa -> Pascal
     281       58354 :     measurement.air_pressure_p = (float)baro_raw * 0.1f;
     282             : 
     283             :     // Metadata
     284       58354 :     measurement.type = MeasurementType::IMU;
     285       58354 :     measurement.timestamp_us = AP_HAL::micros64();
     286       58354 : }
     287             : 
     288        1077 : void AP_ExternalAHRS_SensAItion_Parser::decode_ahrs(const uint8_t* payload, Measurement& measurement)
     289             : {
     290             :     // Payload layout: W (0-3), X (4-7), Y (8-11), Z (12-15)
     291        1077 :     const int32_t quat_w_raw = be32toh_ptr(&payload[0]);
     292        1077 :     const int32_t quat_x_raw = be32toh_ptr(&payload[4]);
     293        1077 :     const int32_t quat_y_raw = be32toh_ptr(&payload[8]);
     294        1077 :     const int32_t quat_z_raw = be32toh_ptr(&payload[12]);
     295             : 
     296        1077 :     const float scale_factor = 1.0e-6f;
     297             : 
     298        2154 :     measurement.orientation = Quaternion(
     299        1077 :                                   (float)quat_w_raw * scale_factor,
     300        1077 :                                   (float)quat_x_raw * scale_factor,
     301        1077 :                                   (float)quat_y_raw * scale_factor,
     302        1077 :                                   (float)quat_z_raw * scale_factor
     303             :                               );
     304             : 
     305        1077 :     measurement.type = MeasurementType::AHRS;
     306        1077 :     measurement.timestamp_us = AP_HAL::micros64();
     307        1077 : }
     308             : 
     309        1096 : void AP_ExternalAHRS_SensAItion_Parser::decode_ins(const uint8_t* payload, Measurement& measurement)
     310             : {
     311             :     // --- 1. PARSE RAW BYTES (Big-Endian) ---
     312             : 
     313             :     // 0-3: Num Sats (G2, G1)
     314        1096 :     measurement.num_sats_gnss2 = payload[1];
     315        1096 :     measurement.num_sats_gnss1 = payload[3];
     316             : 
     317             :     // 4-7: Error Flags
     318        1096 :     measurement.error_flags = be32toh_ptr(&payload[4]);
     319             : 
     320             :     // 8: Sensor Valid
     321        1096 :     measurement.sensor_valid = payload[8];
     322             : 
     323             :     // 9-16: Lat/Lon (1e-7 deg)
     324        1096 :     const int32_t lat_raw = be32toh_ptr(&payload[9]);
     325        1096 :     const int32_t lon_raw = be32toh_ptr(&payload[13]);
     326             : 
     327             :     // 17-28: Velocity N, E, D (mm/s)
     328        1096 :     const int32_t vel_n_mm = be32toh_ptr(&payload[17]);
     329        1096 :     const int32_t vel_e_mm = be32toh_ptr(&payload[21]);
     330        1096 :     const int32_t vel_d_mm = be32toh_ptr(&payload[25]);
     331             : 
     332             :     // 29-32: Altitude relative to WGS 84 ellipsoid (mm)
     333        1096 :     const int32_t alt_raw_mm = be32toh_ptr(&payload[29]);
     334             : 
     335             :     // 33: Alignment Status
     336        1096 :     measurement.alignment_status = payload[33];
     337             : 
     338             :     // 34-37: Time of week (ms)
     339        1096 :     measurement.time_itow_ms = be32toh_ptr(&payload[34]);
     340             : 
     341             :     // 38-39: GNSS Fix
     342        1096 :     measurement.gnss2_fix = payload[38];
     343        1096 :     measurement.gnss1_fix = payload[39];
     344             : 
     345             :     // 40-44: UTC Date/Time
     346        1096 :     const uint16_t year = (uint16_t)((payload[40]<<8) | payload[41]);
     347        1096 :     const uint16_t month = (uint16_t)((payload[42]<<8) | payload[43]);
     348        1096 :     const uint8_t day = payload[44];
     349             : 
     350             :     // 45-68: Accuracy Metrics (mm or mm/s)
     351        1096 :     const int32_t acc_lat_mm = be32toh_ptr(&payload[45]);
     352        1096 :     const int32_t acc_lon_mm = be32toh_ptr(&payload[49]);
     353        1096 :     const int32_t acc_vn_mm  = be32toh_ptr(&payload[53]);
     354        1096 :     const int32_t acc_ve_mm  = be32toh_ptr(&payload[57]);
     355        1096 :     const int32_t acc_vd_mm  = be32toh_ptr(&payload[61]);
     356        1096 :     const int32_t acc_vd_pos_mm = be32toh_ptr(&payload[65]);
     357             : 
     358             :     // --- 2. POPULATE & CONVERT ---
     359        1096 :     const float mm_to_cm = 0.1f;
     360        1096 :     measurement.location = Location(lat_raw, lon_raw, alt_raw_mm * mm_to_cm, Location::AltFrame::ABSOLUTE);
     361             : 
     362        1096 :     const float mms_to_ms = 0.001f;
     363        1096 :     const float mm_to_m = 0.001f;
     364             : 
     365        1096 :     measurement.velocity_ned.x = (float)vel_n_mm * mms_to_ms;
     366        1096 :     measurement.velocity_ned.y = (float)vel_e_mm * mms_to_ms;
     367        1096 :     measurement.velocity_ned.z = (float)vel_d_mm * mms_to_ms;
     368             : 
     369             :     // Accuracy
     370        1096 :     measurement.pos_accuracy.x = (float)acc_lat_mm * mm_to_m; // Horizontal Latitude
     371        1096 :     measurement.pos_accuracy.y = (float)acc_lon_mm * mm_to_m; // Horizontal Longitude
     372        1096 :     measurement.pos_accuracy.z = (float)acc_vd_pos_mm * mm_to_m; // Vertical
     373             : 
     374        1096 :     measurement.vel_accuracy.x = (float)acc_vn_mm * mms_to_ms;
     375        1096 :     measurement.vel_accuracy.y = (float)acc_ve_mm * mms_to_ms;
     376        1096 :     measurement.vel_accuracy.z = (float)acc_vd_mm * mms_to_ms;
     377             : 
     378             :     // GPS Week Calculation (Gate check)
     379        1096 :     if (measurement.gnss1_fix >= 2) {
     380        1094 :         measurement.gps_week = calculate_gps_week(year, month, day);
     381             :     } else {
     382           2 :         measurement.gps_week = 0;
     383             :     }
     384             : 
     385        1096 :     measurement.type = MeasurementType::INS;
     386        1096 :     measurement.timestamp_us = AP_HAL::micros64();
     387             : 
     388        1096 : }
     389             : 
     390        1094 : uint16_t AP_ExternalAHRS_SensAItion_Parser::calculate_gps_week(uint16_t year, uint8_t month, uint8_t day)
     391             : {
     392             :     // Convert to BCD form (DDMMYY) - the GPS_Backend function assumes year is 20YY
     393        1094 :     const uint32_t bcd_date = year % 100U + month * 100U + day * 10000U;
     394             : 
     395        1094 :     const uint32_t bcd_time_ms = 0U;
     396             :     uint16_t gps_week;
     397             :     uint32_t gps_time_ms;
     398        1094 :     AP_GPS_Backend::BCD_to_gps_time(bcd_date, bcd_time_ms, gps_week, gps_time_ms);
     399             : 
     400        1094 :     return gps_week;
     401             : }

Generated by: LCOV version 1.14