LCOV - code coverage report
Current view: top level - AP_ExternalAHRS - MicroStrain_common.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 165 198 83.3 %
Date: 2026-09-09 11:02:08 Functions: 9 9 100.0 %

          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             :    This program is distributed in the hope that it will be useful,
       7             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       8             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       9             :    GNU General Public License for more details.
      10             :    You should have received a copy of the GNU General Public License
      11             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      12             :  */
      13             : /*
      14             :   support for MicroStrain MIP parsing
      15             :  */
      16             : 
      17             : #define AP_MATH_ALLOW_DOUBLE_FUNCTIONS 1
      18             : 
      19             : #include "AP_ExternalAHRS_config.h"
      20             : #include <AP_GPS/AP_GPS.h>
      21             : 
      22             : #if AP_MICROSTRAIN_ENABLED
      23             : 
      24             : #include "MicroStrain_common.h"
      25             : #include <AP_HAL/utility/sparse-endian.h>
      26             : 
      27             : // https://s3.amazonaws.com/files.microstrain.com/GQ7+User+Manual/external_content/dcp/Data/sensor_data/sensor_data_links.htm
      28             : enum class INSPacketField {
      29             :     ACCEL = 0x04,
      30             :     GYRO = 0x05,
      31             :     QUAT = 0x0A,
      32             :     MAG = 0x06,
      33             :     PRESSURE = 0x17,
      34             : };
      35             : 
      36             : // https://s3.amazonaws.com/files.microstrain.com/GQ7+User+Manual/external_content/dcp/Data/gnss_recv_1/gnss_recv_1_links.htm
      37             : enum class GNSSPacketField {
      38             :     LLH_POSITION = 0x03,
      39             :     NED_VELOCITY = 0x05,
      40             :     DOP_DATA = 0x07,
      41             :     FIX_INFO = 0x0B,
      42             :     // https://s3.amazonaws.com/files.microstrain.com/GQ7+User+Manual/external_content/dcp/Data/shared_data/data/mip_field_shared_gps_timestamp.htm
      43             :     GPS_TIMESTAMP = 0xD3,
      44             : };
      45             : 
      46             : // https://s3.amazonaws.com/files.microstrain.com/GQ7+User+Manual/external_content/dcp/Data/gnss_recv_1/data/mip_field_gnss_fix_info.htm
      47             : enum class GNSSFixType {
      48             :     FIX_3D = 0x00,
      49             :     FIX_2D = 0x01,
      50             :     TIME_ONLY = 0x02,
      51             :     NONE = 0x03,
      52             :     INVALID = 0x04,
      53             :     FIX_RTK_FLOAT = 0x05,
      54             :     FIX_RTK_FIXED = 0x06,
      55             : };
      56             : 
      57             : // https://s3.amazonaws.com/files.microstrain.com/GQ7+User+Manual/external_content/dcp/Data/filter_data/filter_data_links.htm
      58             : enum class FilterPacketField {
      59             :     FILTER_STATUS = 0x10,
      60             :     LLH_POSITION = 0x01,
      61             :     NED_VELOCITY = 0x02,
      62             :     // https://s3.amazonaws.com/files.microstrain.com/GQ7+User+Manual/external_content/dcp/Data/filter_data/data/mip_field_filter_attitude_quaternion.htm
      63             :     ATTITUDE_QUAT = 0x03,
      64             :     // https://s3.amazonaws.com/files.microstrain.com/GQ7+User+Manual/external_content/dcp/Data/0x82/data/0x08.htm
      65             :     LLH_POSITION_UNCERTAINTY = 0x08,
      66             :     // https://s3.amazonaws.com/files.microstrain.com/GQ7+User+Manual/external_content/dcp/Data/0x82/data/0x09.htm
      67             :     NED_VELOCITY_UNCERTAINTY = 0x09,
      68             :     // https://s3.amazonaws.com/files.microstrain.com/GQ7+User+Manual/external_content/dcp/Data/shared_data/data/mip_field_shared_gps_timestamp.htm
      69             :     GPS_TIMESTAMP = 0xD3,
      70             : };
      71             : 
      72     2674108 : bool AP_MicroStrain::handle_byte(const uint8_t b, DescriptorSet& descriptor)
      73             : {
      74     2674108 :     switch (message_in.state) {
      75       33141 :         case ParseState::WaitingFor_SyncOne:
      76       33141 :             if (b == SYNC_ONE) {
      77       33141 :                 message_in.packet.header[0] = b;
      78       33141 :                 message_in.state = ParseState::WaitingFor_SyncTwo;
      79             :             }
      80       33141 :             break;
      81       33141 :         case ParseState::WaitingFor_SyncTwo:
      82       33141 :             if (b == SYNC_TWO) {
      83       33141 :                 message_in.packet.header[1] = b;
      84       33141 :                 message_in.state = ParseState::WaitingFor_Descriptor;
      85             :             } else {
      86           0 :                 message_in.state = ParseState::WaitingFor_SyncOne;
      87             :             }
      88       33141 :             break;
      89       33141 :         case ParseState::WaitingFor_Descriptor:
      90       33141 :             message_in.packet.descriptor_set(b);
      91       33141 :             message_in.state = ParseState::WaitingFor_PayloadLength;
      92       33141 :             break;
      93       33141 :         case ParseState::WaitingFor_PayloadLength:
      94       33141 :             message_in.packet.payload_length(b);
      95       33141 :             message_in.state = ParseState::WaitingFor_Data;
      96       33141 :             message_in.index = 0;
      97       33141 :             break;
      98     2475262 :         case ParseState::WaitingFor_Data:
      99     2475262 :             message_in.packet.payload[message_in.index++] = b;
     100     2475262 :             if (message_in.index >= message_in.packet.payload_length()) {
     101       33141 :                 message_in.state = ParseState::WaitingFor_Checksum;
     102       33141 :                 message_in.index = 0;
     103             :             }
     104     2475262 :             break;
     105       66282 :         case ParseState::WaitingFor_Checksum:
     106       66282 :             message_in.packet.checksum[message_in.index++] = b;
     107       66282 :             if (message_in.index >= 2) {
     108       33141 :                 message_in.state = ParseState::WaitingFor_SyncOne;
     109       33141 :                 message_in.index = 0;
     110             : 
     111       33141 :                 if (valid_packet(message_in.packet)) {
     112       33141 :                     descriptor = handle_packet(message_in.packet);
     113       33141 :                     return true;
     114             :                 }
     115             :             }
     116       33141 :             break;
     117             :         }
     118     2640967 :     return false;
     119             : }
     120             : 
     121       33141 : bool AP_MicroStrain::valid_packet(const MicroStrain_Packet & packet)
     122             : {
     123       33141 :     uint8_t checksum_one = 0;
     124       33141 :     uint8_t checksum_two = 0;
     125             : 
     126      165705 :     for (int i = 0; i < 4; i++) {
     127      132564 :         checksum_one += packet.header[i];
     128      132564 :         checksum_two += checksum_one;
     129             :     }
     130             : 
     131     2508403 :     for (int i = 0; i < packet.payload_length(); i++) {
     132     2475262 :         checksum_one += packet.payload[i];
     133     2475262 :         checksum_two += checksum_one;
     134             :     }
     135             : 
     136       33141 :     return packet.checksum[0] == checksum_one && packet.checksum[1] == checksum_two;
     137             : }
     138             : 
     139       33141 : AP_MicroStrain::DescriptorSet AP_MicroStrain::handle_packet(const MicroStrain_Packet& packet)
     140             : {
     141       33141 :     const DescriptorSet descriptor = packet.descriptor_set();
     142       33141 :     switch (descriptor) {
     143       15627 :     case DescriptorSet::IMUData:
     144       15627 :         handle_imu(packet);
     145       15627 :         break;
     146       15627 :     case DescriptorSet::FilterData:
     147       15627 :         handle_filter(packet);
     148       15627 :         break;
     149           0 :     case DescriptorSet::BaseCommand:
     150             :     case DescriptorSet::DMCommand:
     151             :     case DescriptorSet::SystemCommand:
     152           0 :         break;
     153        1887 :     case DescriptorSet::GNSSData:
     154             :     case DescriptorSet::GNSSRecv1:
     155             :     case DescriptorSet::GNSSRecv2:
     156        1887 :         handle_gnss(packet);
     157        1887 :         break;
     158             :     }
     159       33141 :     return descriptor;
     160             : }
     161             : 
     162             : 
     163       15627 : void AP_MicroStrain::handle_imu(const MicroStrain_Packet& packet)
     164             : {
     165       15627 :     last_imu_pkt = AP_HAL::millis();
     166             : 
     167             :     // Iterate through fields of varying lengths in INS packet
     168       93762 :     for (uint8_t i = 0; i < packet.payload_length(); i +=  packet.payload[i]) {
     169       78135 :         switch ((INSPacketField) packet.payload[i+1]) {
     170             :         // Scaled Ambient Pressure
     171       15627 :         case INSPacketField::PRESSURE: {
     172       15627 :             imu_data.pressure = be32tofloat_ptr(packet.payload, i+2) * 100; // Convert millibar to pascals
     173       15627 :             break;
     174             :         }
     175             :         // Scaled Magnetometer Vector
     176       15627 :         case INSPacketField::MAG: {
     177       15627 :             imu_data.mag = populate_vector3f(packet.payload, i+2) * 1000; // Convert gauss to milligauss
     178       15627 :             break;
     179             :         }
     180             :         // Scaled Accelerometer Vector
     181       15627 :         case INSPacketField::ACCEL: {
     182       15627 :             imu_data.accel = populate_vector3f(packet.payload, i+2) * GRAVITY_MSS; // Convert g's to m/s^2
     183       15627 :             break;
     184             :         }
     185             :         // Scaled Gyro Vector
     186       15627 :         case INSPacketField::GYRO: {
     187       15627 :             imu_data.gyro = populate_vector3f(packet.payload, i+2);
     188       15627 :             break;
     189             :         }
     190             :         // Quaternion
     191       15627 :         case INSPacketField::QUAT: {
     192       15627 :             imu_data.quat = populate_quaternion(packet.payload, i+2);
     193       15627 :             break;
     194             :         }
     195             :         }
     196             :     }
     197       15627 : }
     198             : 
     199             : 
     200        1887 : void AP_MicroStrain::handle_gnss(const MicroStrain_Packet &packet)
     201             : {
     202        1887 :     last_gps_pkt = AP_HAL::millis();
     203             :     uint8_t gnss_instance;
     204        1887 :     const DescriptorSet descriptor  = DescriptorSet(packet.descriptor_set());
     205        1887 :     if (!get_gnss_instance(descriptor, gnss_instance)) {
     206           0 :         return;
     207             :     }
     208             : 
     209             :     // Iterate through fields of varying lengths in GNSS packet
     210       11322 :     for (uint8_t i = 0; i < packet.payload_length(); i += packet.payload[i]) {
     211        9435 :         switch ((GNSSPacketField) packet.payload[i+1]) {
     212        1887 :         case GNSSPacketField::GPS_TIMESTAMP: {
     213        1887 :             gnss_data[gnss_instance].tow_ms = double_to_uint32(be64todouble_ptr(packet.payload, i+2) * 1000); // Convert seconds to ms
     214        1887 :             gnss_data[gnss_instance].week = be16toh_ptr(&packet.payload[i+10]);
     215        1887 :             break;
     216             :         }
     217        1887 :         case GNSSPacketField::FIX_INFO: {
     218        1887 :             switch ((GNSSFixType) packet.payload[i+2]) {
     219           0 :             case (GNSSFixType::FIX_RTK_FLOAT): {
     220           0 :                 gnss_data[gnss_instance].fix_type = AP_GPS_FixType::RTK_FLOAT;
     221           0 :                 break;
     222             :             }
     223           0 :             case (GNSSFixType::FIX_RTK_FIXED): {
     224           0 :                 gnss_data[gnss_instance].fix_type = AP_GPS_FixType::RTK_FIXED;
     225           0 :                 break;
     226             :             }
     227        1887 :             case (GNSSFixType::FIX_3D): {
     228        1887 :                 gnss_data[gnss_instance].fix_type = AP_GPS_FixType::FIX_3D;
     229        1887 :                 break;
     230             :             }
     231           0 :             case (GNSSFixType::FIX_2D): {
     232           0 :                 gnss_data[gnss_instance].fix_type = AP_GPS_FixType::FIX_2D;
     233           0 :                 break;
     234             :             }
     235           0 :             case (GNSSFixType::TIME_ONLY):
     236             :             case (GNSSFixType::NONE): {
     237           0 :                 gnss_data[gnss_instance].fix_type = AP_GPS_FixType::NONE;
     238           0 :                 break;
     239             :             }
     240           0 :             default:
     241             :             case (GNSSFixType::INVALID): {
     242           0 :                 gnss_data[gnss_instance].fix_type = AP_GPS_FixType::NO_GPS;
     243           0 :                 break;
     244             :             }
     245             :             }
     246             : 
     247        1887 :             gnss_data[gnss_instance].satellites = packet.payload[i+3];
     248        1887 :             break;
     249             :         }
     250        1887 :         case GNSSPacketField::LLH_POSITION: {
     251        1887 :             gnss_data[gnss_instance].lat = be64todouble_ptr(packet.payload, i+2) * 1.0e7; // Decimal degrees to degrees
     252        1887 :             gnss_data[gnss_instance].lon = be64todouble_ptr(packet.payload, i+10) * 1.0e7;
     253        1887 :             gnss_data[gnss_instance].msl_altitude = be64todouble_ptr(packet.payload, i+26) * 1.0e2; // Meters to cm
     254        1887 :             gnss_data[gnss_instance].horizontal_position_accuracy = be32tofloat_ptr(packet.payload, i+34);
     255        1887 :             gnss_data[gnss_instance].vertical_position_accuracy = be32tofloat_ptr(packet.payload, i+38);
     256        1887 :             break;
     257             :         }
     258        1887 :         case GNSSPacketField::DOP_DATA: {
     259        1887 :             gnss_data[gnss_instance].hdop = be32tofloat_ptr(packet.payload, i+10);
     260        1887 :             gnss_data[gnss_instance].vdop = be32tofloat_ptr(packet.payload, i+14);
     261        1887 :             break;
     262             :         }
     263        1887 :         case GNSSPacketField::NED_VELOCITY: {
     264        1887 :             gnss_data[gnss_instance].ned_velocity_north = be32tofloat_ptr(packet.payload, i+2);
     265        1887 :             gnss_data[gnss_instance].ned_velocity_east = be32tofloat_ptr(packet.payload, i+6);
     266        1887 :             gnss_data[gnss_instance].ned_velocity_down = be32tofloat_ptr(packet.payload, i+10);
     267        1887 :             gnss_data[gnss_instance].speed_accuracy = be32tofloat_ptr(packet.payload, i+26);
     268        1887 :             break;
     269             :         }
     270             :         }
     271             :     }
     272             : }
     273             : 
     274       15627 : void AP_MicroStrain::handle_filter(const MicroStrain_Packet &packet)
     275             : {
     276       15627 :     last_filter_pkt = AP_HAL::millis();
     277             : 
     278             :     // Iterate through fields of varying lengths in filter packet
     279       86117 :     for (uint8_t i = 0; i < packet.payload_length(); i += packet.payload[i]) {
     280       70490 :         switch ((FilterPacketField) packet.payload[i+1]) {
     281       15627 :         case FilterPacketField::GPS_TIMESTAMP: {
     282       15627 :             filter_data.tow_ms = be64todouble_ptr(packet.payload, i+2) * 1000; // Convert seconds to ms
     283       15627 :             filter_data.week = be16toh_ptr(&packet.payload[i+10]);
     284       15627 :             break;
     285             :         }
     286       15627 :         case FilterPacketField::LLH_POSITION: {
     287       15627 :             filter_data.lat = be64todouble_ptr(packet.payload, i+2) * 1.0e7; // Decimal degrees to degrees
     288       15627 :             filter_data.lon = be64todouble_ptr(packet.payload, i+10) * 1.0e7;
     289       15627 :             filter_data.hae_altitude = be64todouble_ptr(packet.payload, i+26) * 1.0e2; // Meters to cm
     290       15627 :             break;
     291             :         }
     292       15627 :         case FilterPacketField::NED_VELOCITY: {
     293       15627 :             filter_data.ned_velocity_north = be32tofloat_ptr(packet.payload, i+2);
     294       15627 :             filter_data.ned_velocity_east = be32tofloat_ptr(packet.payload, i+6);
     295       15627 :             filter_data.ned_velocity_down = be32tofloat_ptr(packet.payload, i+10);
     296       15627 :             break;
     297             :         }
     298        7982 :         case FilterPacketField::ATTITUDE_QUAT: {
     299        7982 :             filter_data.attitude_quat = populate_quaternion(packet.payload, i+2);
     300        7982 :             filter_data.attitude_quat.normalize();
     301        7982 :             break;
     302             :         }
     303           0 :         case FilterPacketField::LLH_POSITION_UNCERTAINTY: {
     304           0 :             const float north_pos_acc = be32tofloat_ptr(packet.payload, i+2);
     305           0 :             const float east_pos_acc = be32tofloat_ptr(packet.payload, i+6);
     306           0 :             const float down_pos_acc = be32tofloat_ptr(packet.payload, i+10);
     307           0 :             filter_data.ned_position_uncertainty = Vector3f(
     308             :                 north_pos_acc,
     309             :                 east_pos_acc,
     310             :                 down_pos_acc
     311             :             );
     312           0 :             break;
     313             :         }
     314           0 :         case FilterPacketField::NED_VELOCITY_UNCERTAINTY: {
     315           0 :             const float north_vel_uncertainty = be32tofloat_ptr(packet.payload, i+2);
     316           0 :             const float east_vel_uncertainty = be32tofloat_ptr(packet.payload, i+6);
     317           0 :             const float down_vel_uncertainty = be32tofloat_ptr(packet.payload, i+10);
     318           0 :             filter_data.ned_velocity_uncertainty = Vector3f(
     319             :                 north_vel_uncertainty,
     320             :                 east_vel_uncertainty,
     321             :                 down_vel_uncertainty
     322             :             );
     323           0 :             break;
     324             :         }
     325       15627 :         case FilterPacketField::FILTER_STATUS: {
     326       15627 :             filter_status.state = be16toh_ptr(&packet.payload[i+2]);
     327       15627 :             filter_status.mode = be16toh_ptr(&packet.payload[i+4]);
     328       15627 :             filter_status.flags = be16toh_ptr(&packet.payload[i+6]);
     329       15627 :             break;
     330             :         }
     331             :         }
     332             :     }
     333       15627 : }
     334             : 
     335       46881 : Vector3f AP_MicroStrain::populate_vector3f(const uint8_t *data, uint8_t offset)
     336             : {
     337       93762 :     return Vector3f {
     338             :         be32tofloat_ptr(data, offset),
     339       46881 :         be32tofloat_ptr(data, offset+4),
     340       46881 :         be32tofloat_ptr(data, offset+8)
     341       46881 :     };
     342             : }
     343             : 
     344       23609 : Quaternion AP_MicroStrain::populate_quaternion(const uint8_t *data, uint8_t offset)
     345             : {
     346             :     // https://github.com/clemense/quaternion-conventions
     347             :     // AP follows W + Xi + Yj + Zk format.
     348             :     // Microstrain follows the same
     349       47218 :     return Quaternion {
     350             :         be32tofloat_ptr(data, offset),
     351       23609 :         be32tofloat_ptr(data, offset+4),
     352       23609 :         be32tofloat_ptr(data, offset+8),
     353       23609 :         be32tofloat_ptr(data, offset+12)
     354       23609 :     };
     355             : }
     356             : 
     357        1887 : bool AP_MicroStrain::get_gnss_instance(const DescriptorSet& descriptor, uint8_t& instance){
     358        1887 :     bool success = false;
     359             :     
     360        1887 :     switch(descriptor) {
     361        1249 :     case DescriptorSet::GNSSData:
     362             :     case DescriptorSet::GNSSRecv1:
     363        1249 :         instance = 0;
     364        1249 :         success = true;
     365        1249 :         break;
     366         638 :     case DescriptorSet::GNSSRecv2:
     367         638 :         instance = 1;
     368         638 :         success = true;
     369         638 :         break;
     370           0 :     default:
     371           0 :         break;
     372             :     }
     373        1887 :     return success;
     374             : }
     375             : 
     376             : 
     377             : 
     378             : #endif // AP_MICROSTRAIN_ENABLED

Generated by: LCOV version 1.14