LCOV - code coverage report
Current view: top level - AP_GPS - AP_GPS_ERB.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 37 169 21.9 %
Date: 2026-09-09 11:02:08 Functions: 1 3 33.3 %

          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             :  //
      17             :  //  Emlid Reach Binary (ERB) GPS driver for ArduPilot.
      18             :  //  ERB protocol: http://files.emlid.com/ERB.pdf
      19             : 
      20             : #include "AP_GPS.h"
      21             : #include "AP_GPS_ERB.h"
      22             : 
      23             : #if AP_GPS_ERB_ENABLED
      24             : 
      25             : #define ERB_DEBUGGING 0
      26             : 
      27             : #define STAT_FIX_VALID 0x01
      28             : 
      29             : #include <AP_HAL/AP_HAL.h>
      30             : 
      31             : extern const AP_HAL::HAL& hal;
      32             : 
      33             : #if ERB_DEBUGGING
      34             :  # define Debug(fmt, args ...)  do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)
      35             : #else
      36             :  # define Debug(fmt, args ...)
      37             : #endif
      38             : 
      39             : // Process bytes available from the stream
      40             : //
      41             : // The stream is assumed to contain only messages we recognise.  If it
      42             : // contains other messages, and those messages contain the preamble
      43             : // bytes, it is possible for this code to fail to synchronise to the
      44             : // stream immediately.  Without buffering the entire message and
      45             : // re-processing it from the top, this is unavoidable. The parser
      46             : // attempts to avoid this when possible.
      47             : //
      48             : bool
      49           0 : AP_GPS_ERB::read(void)
      50             : {
      51             :     uint8_t data;
      52             :     int16_t numc;
      53           0 :     bool parsed = false;
      54             : 
      55           0 :     numc = port->available();
      56           0 :     for (int16_t i = 0; i < numc; i++) {        // Process bytes received
      57             : 
      58             :         // read the next byte
      59           0 :         data = port->read();
      60             : #if AP_GPS_DEBUG_LOGGING_ENABLED
      61             :         log_data(&data, 1);
      62             : #endif
      63             : 
      64           0 :         reset:
      65           0 :         switch(_step) {
      66             : 
      67             :         // Message preamble detection
      68             :         //
      69           0 :         case 1:
      70           0 :             if (PREAMBLE2 == data) {
      71           0 :                 _step++;
      72           0 :                 break;
      73             :             }
      74           0 :             _step = 0;
      75             :             Debug("reset %u", __LINE__);
      76             :             FALLTHROUGH;
      77           0 :         case 0:
      78           0 :             if(PREAMBLE1 == data)
      79           0 :                 _step++;
      80           0 :             break;
      81             : 
      82             :         // Message header processing
      83             :         //
      84           0 :         case 2:
      85           0 :             _step++;
      86           0 :             _msg_id = data;
      87           0 :             _ck_b = _ck_a = data;                       // reset the checksum accumulators
      88           0 :             break;
      89           0 :         case 3:
      90           0 :             _step++;
      91           0 :             _ck_b += (_ck_a += data);                   // checksum byte
      92           0 :             _payload_length = data;                     // payload length low byte
      93           0 :             break;
      94           0 :         case 4:
      95           0 :             _step++;
      96           0 :             _ck_b += (_ck_a += data);                   // checksum byte
      97           0 :             _payload_length += (uint16_t)(data<<8);
      98           0 :             _payload_counter = 0;                       // prepare to receive payload
      99           0 :             break;
     100             : 
     101             :         // Receive message data
     102             :         //
     103           0 :         case 5:
     104           0 :             _ck_b += (_ck_a += data);                   // checksum byte
     105           0 :             if (_payload_counter < sizeof(_buffer)) {
     106           0 :                 _buffer[_payload_counter] = data;
     107             :             }
     108           0 :             if (++_payload_counter == _payload_length)
     109           0 :                 _step++;
     110           0 :             break;
     111             : 
     112             :         // Checksum and message processing
     113             :         //
     114           0 :         case 6:
     115           0 :             _step++;
     116           0 :             if (_ck_a != data) {
     117             :                 Debug("bad cka %x should be %x", data, _ck_a);
     118           0 :                 _step = 0;
     119           0 :                 goto reset;
     120             :             }
     121           0 :             break;
     122           0 :         case 7:
     123           0 :             _step = 0;
     124           0 :             if (_ck_b != data) {
     125             :                 Debug("bad ckb %x should be %x", data, _ck_b);
     126           0 :                 break;                                  // bad checksum
     127             :             }
     128             : 
     129           0 :             if (_parse_gps()) {
     130           0 :                 parsed = true;
     131             :             }
     132           0 :             break;
     133             :         }
     134             :     }
     135           0 :     return parsed;
     136             : }
     137             : 
     138             : bool
     139           0 : AP_GPS_ERB::_parse_gps(void)
     140             : {
     141           0 :     switch (_msg_id) {
     142           0 :     case MSG_VER:
     143             :         Debug("Version of ERB protocol %u.%u.%u",
     144             :               _buffer.ver.ver_high,
     145             :               _buffer.ver.ver_medium,
     146             :               _buffer.ver.ver_low);
     147           0 :         break;
     148           0 :     case MSG_POS:
     149             :         Debug("Message POS");
     150           0 :         _last_pos_time        = _buffer.pos.time;
     151           0 :         state.location.lng    = (int32_t)(_buffer.pos.longitude * (double)1e7);
     152           0 :         state.location.lat    = (int32_t)(_buffer.pos.latitude * (double)1e7);
     153           0 :         state.have_undulation = true;
     154           0 :         state.undulation = _buffer.pos.altitude_msl - _buffer.pos.altitude_ellipsoid;
     155           0 :         set_alt_amsl_cm(state, _buffer.pos.altitude_msl * 100);
     156           0 :         state.status          = next_fix;
     157           0 :         _new_position = true;
     158           0 :         state.horizontal_accuracy = _buffer.pos.horizontal_accuracy * 1.0e-3f;
     159           0 :         state.vertical_accuracy = _buffer.pos.vertical_accuracy * 1.0e-3f;
     160           0 :         state.have_horizontal_accuracy = true;
     161           0 :         state.have_vertical_accuracy = true;
     162           0 :         break;
     163           0 :     case MSG_STAT:
     164             :         Debug("Message STAT fix_status=%u fix_type=%u",
     165             :               _buffer.stat.fix_status,
     166             :               _buffer.stat.fix_type);
     167           0 :         if (_buffer.stat.fix_status & STAT_FIX_VALID) {
     168           0 :             if (_buffer.stat.fix_type == AP_GPS_ERB::FIX_FIX) {
     169           0 :                 next_fix = AP_GPS_FixType::RTK_FIXED;
     170           0 :             } else if (_buffer.stat.fix_type == AP_GPS_ERB::FIX_FLOAT) {
     171           0 :                 next_fix = AP_GPS_FixType::RTK_FLOAT;
     172           0 :             } else if (_buffer.stat.fix_type == AP_GPS_ERB::FIX_SINGLE) {
     173           0 :                 next_fix = AP_GPS_FixType::FIX_3D;
     174             :             } else {
     175           0 :                 next_fix = AP_GPS_FixType::NONE;
     176           0 :                 state.status = AP_GPS_FixType::NONE;
     177             :             }
     178             :         } else {
     179           0 :             next_fix = AP_GPS_FixType::NONE;
     180           0 :             state.status = AP_GPS_FixType::NONE;
     181             :         }
     182           0 :         state.num_sats = _buffer.stat.satellites;
     183           0 :         if (next_fix >= AP_GPS_FixType::FIX_3D) {
     184             :             // use the uart receive time to make packet timestamps more accurate
     185           0 :             set_uart_timestamp(_payload_length + sizeof(erb_header) + 2);
     186           0 :             state.last_gps_time_ms = AP_HAL::millis();
     187           0 :             state.time_week_ms    = _buffer.stat.time;
     188           0 :             state.time_week       = _buffer.stat.week;
     189             :         }
     190           0 :         break;
     191           0 :     case MSG_DOPS:
     192             :         Debug("Message DOPS");
     193           0 :         state.hdop = _buffer.dops.hDOP;
     194           0 :         state.vdop = _buffer.dops.vDOP;
     195           0 :         break;
     196           0 :     case MSG_VEL:
     197             :         Debug("Message VEL");
     198           0 :         _last_vel_time         = _buffer.vel.time;
     199           0 :         state.ground_speed     = _buffer.vel.speed_2d * 0.01f;        // m/s
     200             :         // Heading 2D deg * 100000 rescaled to deg * 100
     201           0 :         state.ground_course = wrap_360(_buffer.vel.heading_2d * 1.0e-5f);
     202           0 :         state.have_vertical_velocity = true;
     203           0 :         state.velocity.x = _buffer.vel.vel_north * 0.01f;
     204           0 :         state.velocity.y = _buffer.vel.vel_east * 0.01f;
     205           0 :         state.velocity.z = _buffer.vel.vel_down * 0.01f;
     206           0 :         state.have_speed_accuracy = true;
     207           0 :         state.speed_accuracy = _buffer.vel.speed_accuracy * 0.01f;
     208           0 :         _new_speed = true;
     209           0 :         break;
     210           0 :     case MSG_RTK:
     211             :         Debug("Message RTK");
     212           0 :         state.rtk_baseline_coords_type = RTK_BASELINE_COORDINATE_SYSTEM_NED;
     213           0 :         state.rtk_num_sats      = _buffer.rtk.base_num_sats;
     214           0 :         if (_buffer.rtk.age_cs == 0xFFFF) {
     215           0 :             state.rtk_age_ms    = 0xFFFFFFFF;
     216             :         } else {
     217           0 :             state.rtk_age_ms    = _buffer.rtk.age_cs * 10;
     218             :         }
     219           0 :         state.rtk_baseline_x_mm = _buffer.rtk.baseline_N_mm;
     220           0 :         state.rtk_baseline_y_mm = _buffer.rtk.baseline_E_mm;
     221           0 :         state.rtk_baseline_z_mm = _buffer.rtk.baseline_D_mm;
     222           0 :         state.rtk_accuracy      = _buffer.rtk.ar_ratio;
     223             : 
     224           0 :         state.rtk_week_number   = _buffer.rtk.base_week_number;
     225           0 :         state.rtk_time_week_ms  = _buffer.rtk.base_time_week_ms;
     226           0 :         break;
     227           0 :     default:
     228             :         Debug("Unexpected message 0x%02x", (unsigned)_msg_id);
     229           0 :         return false;
     230             :     }
     231             :     // we only return true when we get new position and speed data
     232             :     // this ensures we don't use stale data
     233           0 :     if (_new_position && _new_speed && _last_vel_time == _last_pos_time) {
     234           0 :         _new_speed = _new_position = false;
     235           0 :         _fix_count++;
     236           0 :         return true;
     237             :     }
     238           0 :     return false;
     239             : }
     240             : 
     241             : /*
     242             :   detect a ERB GPS. Adds one byte, and returns true if the stream
     243             :   matches a ERB
     244             :  */
     245             : bool
     246    23038517 : AP_GPS_ERB::_detect(struct ERB_detect_state &state, uint8_t data)
     247             : {
     248    23038517 : reset:
     249    23038517 :     switch (state.step) {
     250        4533 :         case 1:
     251        4533 :             if (PREAMBLE2 == data) {
     252           1 :                 state.step++;
     253           1 :                 break;
     254             :             }
     255        4532 :             state.step = 0;
     256             :             FALLTHROUGH;
     257    23038414 :         case 0:
     258    23038414 :             if (PREAMBLE1 == data)
     259        4538 :                 state.step++;
     260    23038414 :             break;
     261           1 :         case 2:
     262           1 :             state.step++;
     263           1 :             state.ck_b = state.ck_a = data;
     264           1 :             break;
     265           1 :         case 3:
     266           1 :             state.step++;
     267           1 :             state.ck_b += (state.ck_a += data);
     268           1 :             state.payload_length = data;
     269           1 :             break;
     270           1 :         case 4:
     271           1 :             state.step++;
     272           1 :             state.ck_b += (state.ck_a += data);
     273           1 :             state.payload_counter = 0;
     274           1 :             break;
     275          98 :         case 5:
     276          98 :             state.ck_b += (state.ck_a += data);
     277          98 :             if (++state.payload_counter == state.payload_length)
     278           1 :                 state.step++;
     279          98 :             break;
     280           1 :         case 6:
     281           1 :             state.step++;
     282           1 :             if (state.ck_a != data) {
     283           1 :                 state.step = 0;
     284           1 :                 goto reset;
     285             :             }
     286           0 :             break;
     287           0 :         case 7:
     288           0 :             state.step = 0;
     289           0 :             if (state.ck_b == data) {
     290           0 :                 return true;
     291             :             } else {
     292           0 :                 goto reset;
     293             :             }
     294             :     }
     295    23038516 :     return false;
     296             : }
     297             : #endif

Generated by: LCOV version 1.14