LCOV - code coverage report
Current view: top level - AP_GPS - AP_GPS_NOVA.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 130 153 85.0 %
Date: 2026-09-09 11:02:08 Functions: 4 4 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             : 
       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             : //  Novatel/Tersus/ComNav GPS driver for ArduPilot.
      17             : //  Code by Michael Oborne
      18             : //  Derived from https://hexagondownloads.blob.core.windows.net/public/Novatel/assets/Documents/Manuals/om-20000129/om-20000129.pdf
      19             : 
      20             : #include "AP_GPS.h"
      21             : #include "AP_GPS_NOVA.h"
      22             : #include <AP_Logger/AP_Logger.h>
      23             : 
      24             : #if AP_GPS_NOVA_ENABLED
      25             : 
      26             : extern const AP_HAL::HAL& hal;
      27             : 
      28             : #define NOVA_DEBUGGING 0
      29             : 
      30             : #if NOVA_DEBUGGING
      31             : #include <cstdio>
      32             :  # define Debug(fmt, args ...)                  \
      33             : do {                                            \
      34             :     printf("%s:%d: " fmt "\n",     \
      35             :                         __FUNCTION__, __LINE__, \
      36             :                         ## args);               \
      37             :     hal.scheduler->delay(1);                    \
      38             : } while(0)
      39             : #else
      40             :  # define Debug(fmt, args ...)
      41             : #endif
      42             : 
      43           4 : AP_GPS_NOVA::AP_GPS_NOVA(AP_GPS &_gps,
      44             :                          AP_GPS::Params &_params,
      45             :                          AP_GPS::GPS_State &_state,
      46           4 :                          AP_HAL::UARTDriver *_port) :
      47           4 :     AP_GPS_Backend(_gps, _params, _state, _port)
      48             : {
      49           4 :     nova_msg.nova_state = nova_msg_parser::PREAMBLE1;
      50             : 
      51           4 :     nova_msg.header.data[0] = NOVA_PREAMBLE1;
      52           4 :     nova_msg.header.data[1] = NOVA_PREAMBLE2;
      53           4 :     nova_msg.header.data[2] = NOVA_PREAMBLE3;
      54             : 
      55           4 :     if (gps._auto_config != AP_GPS::GPS_AUTO_CONFIG_DISABLE) {
      56           4 :         const char *init_str = _initialisation_blob[0];
      57           4 :         const char *init_str1 = _initialisation_blob[1];
      58             : 
      59           4 :         port->write((const uint8_t*)init_str, strlen(init_str));
      60           4 :         port->write((const uint8_t*)init_str1, strlen(init_str1));
      61             :     }
      62           4 : }
      63             : 
      64             : const char* const AP_GPS_NOVA::_initialisation_blob[4] {
      65             :     "\r\n\r\nunlogall\r\n", // cleanup enviroment
      66             :     "log bestposb ontime 0.2 0 nohold\r\n",
      67             :     "log bestvelb ontime 0.2 0 nohold\r\n",
      68             :     "log psrdopb ontime 0.2 0 nohold\r\n",
      69             : };
      70             : 
      71             : // Process all bytes available from the stream
      72             : //
      73             : bool
      74        1190 : AP_GPS_NOVA::read(void)
      75             : {
      76        1190 :     if (gps._auto_config != AP_GPS::GPS_AUTO_CONFIG_DISABLE) {
      77        1190 :         const uint32_t now = AP_HAL::millis();
      78             : 
      79        1190 :         if (_init_blob_index < (sizeof(_initialisation_blob) / sizeof(_initialisation_blob[0]))) {
      80         136 :             const char *init_str = _initialisation_blob[_init_blob_index];
      81             : 
      82         136 :             if (now > _init_blob_time) {
      83          16 :                 port->write((const uint8_t*)init_str, strlen(init_str));
      84          16 :                 _init_blob_time = now + 200;
      85          16 :                 _init_blob_index++;
      86             :             }
      87             :         }
      88             :     }
      89             : 
      90        1190 :     bool ret = false;
      91       14671 :     for (uint16_t i=0; i<8192; i++) {
      92             :         uint8_t temp;
      93       14671 :         if (!port->read(temp)) {
      94        1190 :             break;
      95             :         }
      96             : #if AP_GPS_DEBUG_LOGGING_ENABLED
      97             :         log_data(&temp, 1);
      98             : #endif
      99       13481 :         ret |= parse(temp);
     100             :     }
     101             :     
     102        1190 :     return ret;
     103             : }
     104             : 
     105             : bool
     106       13481 : AP_GPS_NOVA::parse(uint8_t temp)
     107             : {
     108       13481 :     switch (nova_msg.nova_state)
     109             :     {
     110        1157 :         default:
     111             :         case nova_msg_parser::PREAMBLE1:
     112        1157 :             if (temp == NOVA_PREAMBLE1) {
     113         156 :                 nova_msg.nova_state = nova_msg_parser::PREAMBLE2;
     114             :             }
     115        1157 :             nova_msg.read = 0;
     116        1157 :             break;
     117         156 :         case nova_msg_parser::PREAMBLE2:
     118         156 :             if (temp == NOVA_PREAMBLE2) {
     119         156 :                 nova_msg.nova_state = nova_msg_parser::PREAMBLE3;
     120             :             } else {
     121           0 :                 nova_msg.nova_state = nova_msg_parser::PREAMBLE1;
     122             :             }
     123         156 :             break;
     124         156 :         case nova_msg_parser::PREAMBLE3:
     125         156 :             if (temp == NOVA_PREAMBLE3) {
     126         156 :                 nova_msg.nova_state = nova_msg_parser::HEADERLENGTH;
     127             :             } else {
     128           0 :                 nova_msg.nova_state = nova_msg_parser::PREAMBLE1;
     129             :             }
     130         156 :             break;
     131         156 :         case nova_msg_parser::HEADERLENGTH:
     132             :             Debug("NOVA HEADERLENGTH\n");
     133         156 :             nova_msg.header.data[3] = temp;
     134         156 :             nova_msg.header.nova_headeru.headerlength = temp;
     135         156 :             nova_msg.nova_state = nova_msg_parser::HEADERDATA;
     136         156 :             nova_msg.read = 4;
     137         156 :             break;
     138        3744 :         case nova_msg_parser::HEADERDATA:
     139        3744 :             if (nova_msg.read >= sizeof(nova_msg.header.data)) {
     140             :                 Debug("parse header overflow length=%u\n", (unsigned)nova_msg.read);
     141           0 :                 nova_msg.nova_state = nova_msg_parser::PREAMBLE1;
     142           0 :                 break;
     143             :             }
     144        3744 :             nova_msg.header.data[nova_msg.read] = temp;
     145        3744 :             nova_msg.read++;
     146        3744 :             if (nova_msg.read >= nova_msg.header.nova_headeru.headerlength) {
     147         156 :                 nova_msg.nova_state = nova_msg_parser::DATA;
     148             :             }
     149        3744 :             break;
     150        7488 :         case nova_msg_parser::DATA:
     151        7488 :             if (nova_msg.read >= sizeof(nova_msg.data)) {
     152             :                 Debug("parse data overflow length=%u msglength=%u\n", (unsigned)nova_msg.read,nova_msg.header.nova_headeru.messagelength);
     153           0 :                 nova_msg.nova_state = nova_msg_parser::PREAMBLE1;
     154           0 :                 break;
     155             :             }
     156        7488 :             nova_msg.data.bytes[nova_msg.read - nova_msg.header.nova_headeru.headerlength] = temp;
     157        7488 :             nova_msg.read++;
     158        7488 :             if (nova_msg.read >= (nova_msg.header.nova_headeru.messagelength + nova_msg.header.nova_headeru.headerlength)) {
     159             :                 Debug("NOVA DATA exit\n");
     160         156 :                 nova_msg.nova_state = nova_msg_parser::CRC1;
     161             :             }
     162        7488 :             break;
     163         156 :         case nova_msg_parser::CRC1:
     164         156 :             nova_msg.crc = (uint32_t) (temp << 0);
     165         156 :             nova_msg.nova_state = nova_msg_parser::CRC2;
     166         156 :             break;
     167         156 :         case nova_msg_parser::CRC2:
     168         156 :             nova_msg.crc += (uint32_t) (temp << 8);
     169         156 :             nova_msg.nova_state = nova_msg_parser::CRC3;
     170         156 :             break;
     171         156 :         case nova_msg_parser::CRC3:
     172         156 :             nova_msg.crc += (uint32_t) (temp << 16);
     173         156 :             nova_msg.nova_state = nova_msg_parser::CRC4;
     174         156 :             break;
     175         156 :         case nova_msg_parser::CRC4:
     176         156 :             nova_msg.crc += (uint32_t) (temp << 24);
     177         156 :             nova_msg.nova_state = nova_msg_parser::PREAMBLE1;
     178             : 
     179         156 :             uint32_t crc = crc_crc32((uint32_t)0, (uint8_t *)&nova_msg.header.data, (uint32_t)nova_msg.header.nova_headeru.headerlength);
     180         156 :             crc = crc_crc32(crc, (uint8_t *)&nova_msg.data, (uint32_t)nova_msg.header.nova_headeru.messagelength);
     181             : 
     182         156 :             if (nova_msg.crc == crc) {
     183         156 :                 return process_message();
     184             :             } else {
     185             :                 Debug("crc failed");
     186           0 :                 crc_error_counter++;
     187             :             }
     188           0 :             break;
     189             :     }
     190             : 
     191       13325 :     return false;
     192             : }
     193             : 
     194             : bool
     195         156 : AP_GPS_NOVA::process_message(void)
     196             : {
     197         156 :     const uint16_t messageid = nova_msg.header.nova_headeru.messageid;
     198             : 
     199             :     Debug("NOVA process_message messid=%u\n",messageid);
     200             : 
     201         156 :     check_new_itow(nova_msg.header.nova_headeru.tow, nova_msg.header.nova_headeru.messagelength + nova_msg.header.nova_headeru.headerlength);
     202             :  
     203         156 :     switch (messageid) {
     204          52 :     case NOVA_BESTPOS: {  // bestpos
     205          52 :         const bestpos &bestposu = nova_msg.data.bestposu;
     206             : 
     207          52 :         state.time_week = nova_msg.header.nova_headeru.week;
     208          52 :         state.time_week_ms = (uint32_t) nova_msg.header.nova_headeru.tow;
     209          52 :         state.last_gps_time_ms = AP_HAL::millis();
     210             : 
     211          52 :         state.location.lat = (int32_t) (bestposu.lat * (double)1e7);
     212          52 :         state.location.lng = (int32_t) (bestposu.lng * (double)1e7);
     213          52 :         state.have_undulation = true;
     214          52 :         state.undulation = -bestposu.undulation;
     215          52 :         set_alt_amsl_cm(state, bestposu.hgt * 100);
     216             : 
     217          52 :         state.num_sats = bestposu.svsused;
     218             : 
     219          52 :         state.horizontal_accuracy =  norm(bestposu.latsdev, bestposu.lngsdev);
     220          52 :         state.vertical_accuracy = (float) bestposu.hgtsdev;
     221          52 :         state.have_horizontal_accuracy = true;
     222          52 :         state.have_vertical_accuracy = true;
     223          52 :         state.rtk_age_ms = bestposu.diffage * 1000;
     224          52 :         state.rtk_num_sats = bestposu.svsused;
     225             : 
     226          52 :         if (bestposu.solstat == 0) // have a solution
     227             :         {
     228          52 :             switch (bestposu.postype)
     229             :             {
     230           0 :                 case 16:
     231           0 :                     state.status = AP_GPS_FixType::FIX_3D;
     232           0 :                     break;
     233           0 :                 case 17: // psrdiff
     234             :                 case 18: // waas
     235             :                 case 20: // omnistar
     236             :                 case 68: // ppp_converg
     237             :                 case 69: // ppp
     238           0 :                     state.status = AP_GPS_FixType::DGPS;
     239           0 :                     break;
     240          52 :                 case 32: // l1 float
     241             :                 case 33: // iono float
     242             :                 case 34: // narrow float
     243          52 :                     state.status = AP_GPS_FixType::RTK_FLOAT;
     244          52 :                     break;
     245           0 :                 case 48: // l1 int
     246             :                 case 50: // narrow int
     247           0 :                     state.status = AP_GPS_FixType::RTK_FIXED;
     248           0 :                     break;
     249           0 :                 case 0: // NONE
     250             :                 case 1: // FIXEDPOS
     251             :                 case 2: // FIXEDHEIGHT
     252             :                 default:
     253           0 :                     state.status = AP_GPS_FixType::NONE;
     254           0 :                     break;
     255             :             }
     256             :         } else {
     257           0 :             state.status = AP_GPS_FixType::NONE;
     258             :         }
     259             :         
     260          52 :         _new_position = true;
     261          52 :         break;
     262             :     }
     263          52 :     case NOVA_BESTVEL: {  // bestvel
     264          52 :         const bestvel &bestvelu = nova_msg.data.bestvelu;
     265             : 
     266          52 :         state.ground_speed = (float) bestvelu.horspd;
     267          52 :         state.ground_course = (float) bestvelu.trkgnd;
     268          52 :         fill_3d_velocity();
     269          52 :         state.velocity.z = -(float) bestvelu.vertspd;
     270          52 :         state.have_vertical_velocity = true;
     271             :         
     272          52 :         _last_vel_time = (uint32_t) nova_msg.header.nova_headeru.tow;
     273          52 :         _new_speed = true;
     274          52 :         break;
     275             :     }
     276          52 :     case NOVA_PSRDOP: {  // psrdop
     277          52 :         const psrdop &psrdopu = nova_msg.data.psrdopu;
     278             : 
     279          52 :         state.hdop = (uint16_t) (psrdopu.hdop*100);
     280          52 :         state.vdop = (uint16_t) (psrdopu.htdop*100);
     281          52 :         return false;
     282             :     }
     283           0 :     default:
     284           0 :         break;
     285             :     }
     286             : 
     287             :     // ensure out position and velocity stay insync
     288         104 :     if (_new_position && _new_speed && _last_vel_time == state.time_week_ms) {
     289          52 :         _new_speed = _new_position = false;
     290             :         
     291          52 :         return true;
     292             :     }
     293             :     
     294          52 :     return false;
     295             : }
     296             : 
     297             : #endif

Generated by: LCOV version 1.14