You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
645 lines
22 KiB
645 lines
22 KiB
/* |
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
// |
|
// NMEA parser, adapted by Michael Smith from TinyGPS v9: |
|
// |
|
// TinyGPS - a small GPS library for Arduino providing basic NMEA parsing |
|
// Copyright (C) 2008-9 Mikal Hart |
|
// All rights reserved. |
|
// |
|
|
|
/// @file AP_GPS_NMEA.cpp |
|
/// @brief NMEA protocol parser |
|
/// |
|
/// This is a lightweight NMEA parser, derived originally from the |
|
/// TinyGPS parser by Mikal Hart. |
|
/// |
|
|
|
#include <AP_Common/AP_Common.h> |
|
|
|
#include <ctype.h> |
|
#include <stdint.h> |
|
#include <stdlib.h> |
|
|
|
#include "AP_GPS_NMEA.h" |
|
|
|
#include <AP_Logger/AP_Logger.h> |
|
#include <GCS_MAVLink/GCS.h> |
|
extern const AP_HAL::HAL& hal; |
|
|
|
// optionally log all NMEA data for debug purposes |
|
// #define NMEA_LOG_PATH "nmea.log" |
|
|
|
#ifdef NMEA_LOG_PATH |
|
#include <stdio.h> |
|
#endif |
|
|
|
// Convenience macros ////////////////////////////////////////////////////////// |
|
// |
|
#define DIGIT_TO_VAL(_x) (_x - '0') |
|
#define hexdigit(x) ((x)>9?'A'+((x)-10):'0'+(x)) |
|
|
|
bool AP_GPS_NMEA::read(void) |
|
{ |
|
int16_t numc; |
|
bool parsed = false; |
|
|
|
numc = port->available(); |
|
while (numc--) { |
|
char c = port->read(); |
|
#ifdef NMEA_LOG_PATH |
|
static FILE *logf = nullptr; |
|
if (logf == nullptr) { |
|
logf = fopen(NMEA_LOG_PATH, "wb"); |
|
} |
|
if (logf != nullptr) { |
|
::fwrite(&c, 1, 1, logf); |
|
} |
|
#endif |
|
if (_decode(c)) { |
|
parsed = true; |
|
} |
|
} |
|
return parsed; |
|
} |
|
|
|
bool AP_GPS_NMEA::_decode(char c) |
|
{ |
|
bool valid_sentence = false; |
|
|
|
_sentence_length++; |
|
|
|
switch (c) { |
|
case ',': // term terminators |
|
case ';': // term terminators |
|
_parity ^= c; |
|
|
|
markCRC = aulCrcTable[(markCRC ^ c) & 0xff] ^ (markCRC >> 8); |
|
FALLTHROUGH; |
|
case '\r': |
|
case '\n': |
|
case '*': |
|
if (_term_offset < sizeof(_term)) { |
|
_term[_term_offset] = 0; |
|
valid_sentence = _term_complete(); |
|
} |
|
++_term_number; |
|
_term_offset = 0; |
|
_is_checksum_term = c == '*'; |
|
return valid_sentence; |
|
|
|
case '$': // sentence begin |
|
case '#': // sentence begin |
|
_term_number = _term_offset = 0; |
|
_parity = 0; |
|
markCRC = 0; |
|
_sentence_type = _GPS_SENTENCE_OTHER; |
|
_is_checksum_term = false; |
|
_gps_data_good = false; |
|
_sentence_length = 1; |
|
return valid_sentence; |
|
} |
|
|
|
// ordinary characters |
|
if (_term_offset < sizeof(_term) - 1) |
|
_term[_term_offset++] = c; |
|
if (!_is_checksum_term){ |
|
_parity ^= c; |
|
|
|
markCRC = aulCrcTable[(markCRC ^ c) & 0xff] ^ (markCRC >> 8); |
|
} |
|
|
|
return valid_sentence; |
|
} |
|
|
|
int32_t AP_GPS_NMEA::_parse_decimal_100(const char *p) |
|
{ |
|
char *endptr = nullptr; |
|
long ret = 100 * strtol(p, &endptr, 10); |
|
int sign = ret < 0 ? -1 : 1; |
|
|
|
if (ret >= (long)INT32_MAX) { |
|
return INT32_MAX; |
|
} |
|
if (ret <= (long)INT32_MIN) { |
|
return INT32_MIN; |
|
} |
|
if (endptr == nullptr || *endptr != '.') { |
|
return ret; |
|
} |
|
|
|
if (isdigit(endptr[1])) { |
|
ret += sign * 10 * DIGIT_TO_VAL(endptr[1]); |
|
if (isdigit(endptr[2])) { |
|
ret += sign * DIGIT_TO_VAL(endptr[2]); |
|
if (isdigit(endptr[3])) { |
|
ret += sign * (DIGIT_TO_VAL(endptr[3]) >= 5); |
|
} |
|
} |
|
} |
|
return ret; |
|
} |
|
|
|
/* |
|
parse a NMEA latitude/longitude degree value. The result is in degrees*1e7 |
|
*/ |
|
uint32_t AP_GPS_NMEA::_parse_degrees() |
|
{ |
|
char *p, *q; |
|
uint8_t deg = 0, min = 0; |
|
float frac_min = 0; |
|
int32_t ret = 0; |
|
|
|
// scan for decimal point or end of field |
|
for (p = _term; *p && isdigit(*p); p++) |
|
; |
|
q = _term; |
|
|
|
// convert degrees |
|
while ((p - q) > 2 && *q) { |
|
if (deg) |
|
deg *= 10; |
|
deg += DIGIT_TO_VAL(*q++); |
|
} |
|
|
|
// convert minutes |
|
while (p > q && *q) { |
|
if (min) |
|
min *= 10; |
|
min += DIGIT_TO_VAL(*q++); |
|
} |
|
|
|
// convert fractional minutes |
|
if (*p == '.') { |
|
q = p + 1; |
|
float frac_scale = 0.1f; |
|
while (*q && isdigit(*q)) { |
|
frac_min += DIGIT_TO_VAL(*q) * frac_scale; |
|
q++; |
|
frac_scale *= 0.1f; |
|
} |
|
} |
|
ret = (deg * (int32_t)10000000UL); |
|
ret += (min * (int32_t)10000000UL / 60); |
|
ret += (int32_t) (frac_min * (1.0e7f / 60.0f)); |
|
return ret; |
|
} |
|
|
|
/* |
|
see if we have a new set of NMEA messages |
|
*/ |
|
bool AP_GPS_NMEA::_have_new_message() |
|
{ |
|
if (_last_RMC_ms == 0 || |
|
_last_GGA_ms == 0) { |
|
return false; |
|
} |
|
uint32_t now = AP_HAL::millis(); |
|
if (now - _last_RMC_ms > 150 || |
|
now - _last_GGA_ms > 150) { |
|
return false; |
|
} |
|
if (_last_VTG_ms != 0 && |
|
now - _last_VTG_ms > 150) { |
|
return false; |
|
} |
|
// prevent these messages being used again |
|
if (_last_VTG_ms != 0) { |
|
_last_VTG_ms = 1; |
|
} |
|
|
|
if (now - _last_HDT_ms > 300) { |
|
// we have lost GPS yaw |
|
state.have_gps_yaw = false; |
|
} |
|
|
|
_last_GGA_ms = 1; |
|
_last_RMC_ms = 1; |
|
return true; |
|
} |
|
|
|
// Processes a just-completed term |
|
// Returns true if new sentence has just passed checksum test and is validated |
|
bool AP_GPS_NMEA::_term_complete() |
|
{ |
|
// handle the last term in a message |
|
if (_is_checksum_term) { |
|
uint8_t nibble_high = 0; |
|
uint8_t nibble_low = 0; |
|
if (!hex_to_uint8(_term[0], nibble_high) || !hex_to_uint8(_term[1], nibble_low)) { |
|
return false; |
|
} |
|
const uint8_t checksum = (nibble_high << 4u) | nibble_low; |
|
if(_gps_data_good && (_sentence_type == _GPS_SENTENCE_MARK || _sentence_type == _GPS_SENTENCE_EVENT )){ |
|
if(get_new_mark){ |
|
const uint8_t mark_crc = markCRC >> 24; |
|
|
|
// gcs().send_text(MAV_SEVERITY_INFO, "week: %d, %f,crc:0x%02x--0x%02x--0x%02x",gps_mark.week,gps_mark.second,checksum,_parity,mark_crc); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "llh: %f , %f , %f\n",gps_mark.lat,gps_mark.lon,gps_mark.hgt); |
|
if(checksum == mark_crc){ |
|
switch (_sentence_type) { |
|
case _GPS_SENTENCE_MARK: |
|
case _GPS_SENTENCE_EVENT: |
|
if(get_new_mark){ |
|
gcs().send_text(MAV_SEVERITY_INFO, "%d, %.4f, %f , %f , %f\n",gps_mark.week,gps_mark.second,gps_mark.lat,gps_mark.lon,gps_mark.hgt); |
|
get_new_mark = false; |
|
AP_Logger *logger = AP_Logger::get_singleton(); |
|
|
|
if ( logger!=nullptr ) |
|
{ |
|
Vector3d llh = Vector3d(gps_mark.lat,gps_mark.lon, gps_mark.hgt); |
|
Vector3f sigma = Vector3f(gps_mark.lat_sigma,gps_mark.lon_sigma, gps_mark.hgt_sigma); |
|
AP::logger().Write_RTK_Mark_Pos( |
|
gps_mark.week, |
|
gps_mark.second, |
|
llh, |
|
gps_mark.undulation, |
|
sigma, |
|
gps_mark.diff_age, |
|
gps_mark.sol_age |
|
); |
|
} |
|
} |
|
} |
|
|
|
return _have_new_message(); |
|
}else{ |
|
|
|
gcs().send_text(MAV_SEVERITY_INFO, "crc err:0x%02x--0x%02x--0x%02x",checksum,_parity,mark_crc); |
|
} |
|
get_new_mark = false; |
|
} |
|
} |
|
if (checksum == _parity ) { |
|
if (_gps_data_good) { |
|
uint32_t now = AP_HAL::millis(); |
|
switch (_sentence_type) { |
|
case _GPS_SENTENCE_RMC: |
|
_last_RMC_ms = now; |
|
//time = _new_time; |
|
//date = _new_date; |
|
state.location.lat = _new_latitude; |
|
state.location.lng = _new_longitude; |
|
state.ground_speed = _new_speed*0.01f; |
|
state.ground_course = wrap_360(_new_course*0.01f); |
|
make_gps_time(_new_date, _new_time * 10); |
|
set_uart_timestamp(_sentence_length); |
|
state.last_gps_time_ms = now; |
|
fill_3d_velocity(); |
|
break; |
|
case _GPS_SENTENCE_GGA: |
|
_last_GGA_ms = now; |
|
state.location.alt = _new_altitude; |
|
state.location.lat = _new_latitude; |
|
state.location.lng = _new_longitude; |
|
state.num_sats = _new_satellite_count; |
|
state.hdop = _new_hdop; |
|
switch(_new_quality_indicator) { |
|
case 0: // Fix not available or invalid |
|
state.status = AP_GPS::NO_FIX; |
|
break; |
|
case 1: // GPS SPS Mode, fix valid |
|
state.status = AP_GPS::GPS_OK_FIX_3D; |
|
break; |
|
case 2: // Differential GPS, SPS Mode, fix valid |
|
state.status = AP_GPS::GPS_OK_FIX_3D_DGPS; |
|
break; |
|
case 3: // GPS PPS Mode, fix valid |
|
state.status = AP_GPS::GPS_OK_FIX_3D; |
|
break; |
|
case 4: // Real Time Kinematic. System used in RTK mode with fixed integers |
|
state.status = AP_GPS::GPS_OK_FIX_3D_RTK_FIXED; |
|
break; |
|
case 5: // Float RTK. Satellite system used in RTK mode, floating integers |
|
state.status = AP_GPS::GPS_OK_FIX_3D_RTK_FLOAT; |
|
break; |
|
case 6: // Estimated (dead reckoning) Mode |
|
state.status = AP_GPS::NO_FIX; |
|
break; |
|
default://to maintain compatibility with MAV_GPS_INPUT and others |
|
state.status = AP_GPS::GPS_OK_FIX_3D; |
|
break; |
|
} |
|
break; |
|
case _GPS_SENTENCE_VTG: |
|
_last_VTG_ms = now; |
|
state.ground_speed = _new_speed*0.01f; |
|
state.ground_course = wrap_360(_new_course*0.01f); |
|
fill_3d_velocity(); |
|
// VTG has no fix indicator, can't change fix status |
|
break; |
|
case _GPS_SENTENCE_HDT: |
|
_last_HDT_ms = now; |
|
state.gps_yaw = wrap_360(_new_gps_yaw*0.01f); |
|
state.have_gps_yaw = true; |
|
break; |
|
// case _GPS_SENTENCE_MARK: |
|
// case _GPS_SENTENCE_EVENT: |
|
// if(get_new_mark){ |
|
// gcs().send_text(MAV_SEVERITY_INFO, "week: %d, %f",gps_mark.week,gps_mark.second); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "llh: %f , %f , %f\n",gps_mark.lat,gps_mark.lon,gps_mark.hgt); |
|
// get_new_mark = false; |
|
// } |
|
// break; |
|
} |
|
} else { |
|
switch (_sentence_type) { |
|
case _GPS_SENTENCE_RMC: |
|
case _GPS_SENTENCE_GGA: |
|
// Only these sentences give us information about |
|
// fix status. |
|
state.status = AP_GPS::NO_FIX; |
|
} |
|
} |
|
// see if we got a good message |
|
return _have_new_message(); |
|
} |
|
// we got a bad message, ignore it |
|
return false; |
|
} |
|
|
|
// the first term determines the sentence type |
|
if (_term_number == 0) { |
|
/* |
|
The first two letters of the NMEA term are the talker |
|
ID. The most common is 'GP' but there are a bunch of others |
|
that are valid. We accept any two characters here. |
|
*/ |
|
if (_term[0] < 'A' || _term[0] > 'Z' || |
|
_term[1] < 'A' || _term[1] > 'Z') { |
|
_sentence_type = _GPS_SENTENCE_OTHER; |
|
return false; |
|
} |
|
const char *term_type = &_term[2]; |
|
if (strcmp(term_type, "RMC") == 0) { |
|
_sentence_type = _GPS_SENTENCE_RMC; |
|
// gcs().send_text(MAV_SEVERITY_INFO, "get: %s\n",term_type); |
|
} else if (strcmp(term_type, "GGA") == 0) { |
|
_sentence_type = _GPS_SENTENCE_GGA; |
|
// gcs().send_text(MAV_SEVERITY_INFO, "get: %s\n",term_type); |
|
} else if (strcmp(term_type, "HDT") == 0) { |
|
_sentence_type = _GPS_SENTENCE_HDT; |
|
// HDT doesn't have a data qualifier |
|
_gps_data_good = true; |
|
} else if (strcmp(term_type, "VTG") == 0) { |
|
_sentence_type = _GPS_SENTENCE_VTG; |
|
// VTG may not contain a data qualifier, presume the solution is good |
|
// unless it tells us otherwise. |
|
_gps_data_good = true; |
|
// gcs().send_text(MAV_SEVERITY_INFO, "get: %s\n",term_type); |
|
} else if (strcmp(term_type, "ENTALLA") == 0) { |
|
_sentence_type = _GPS_SENTENCE_EVENT; |
|
// VTG may not contain a data qualifier, presume the solution is good |
|
// unless it tells us otherwise. |
|
_gps_data_good = true; |
|
// gcs().send_text(MAV_SEVERITY_INFO, "get: %s",term_type); |
|
} else if (strcmp(term_type, "RKPOSA") == 0) { |
|
_sentence_type = _GPS_SENTENCE_MARK; |
|
// VTG may not contain a data qualifier, presume the solution is good |
|
// unless it tells us otherwise. |
|
|
|
// gcs().send_text(MAV_SEVERITY_INFO, "get: %s",term_type); |
|
_gps_data_good = true; |
|
} else { |
|
_sentence_type = _GPS_SENTENCE_OTHER; |
|
} |
|
return false; |
|
} |
|
|
|
// 32 = RMC, 64 = GGA, 96 = VTG, 128 = HDT |
|
if (_sentence_type != _GPS_SENTENCE_OTHER && _term[0]) { |
|
switch (_sentence_type + _term_number) { |
|
// operational status |
|
// |
|
case _GPS_SENTENCE_RMC + 2: // validity (RMC) |
|
_gps_data_good = _term[0] == 'A'; |
|
break; |
|
case _GPS_SENTENCE_GGA + 6: // Fix data (GGA) |
|
_gps_data_good = _term[0] > '0'; |
|
_new_quality_indicator = _term[0] - '0'; |
|
break; |
|
case _GPS_SENTENCE_VTG + 9: // validity (VTG) (we may not see this field) |
|
_gps_data_good = _term[0] != 'N'; |
|
break; |
|
case _GPS_SENTENCE_GGA + 7: // satellite count (GGA) |
|
_new_satellite_count = atol(_term); |
|
break; |
|
case _GPS_SENTENCE_GGA + 8: // HDOP (GGA) |
|
_new_hdop = (uint16_t)_parse_decimal_100(_term); |
|
break; |
|
|
|
// time and date |
|
// |
|
case _GPS_SENTENCE_RMC + 1: // Time (RMC) |
|
case _GPS_SENTENCE_GGA + 1: // Time (GGA) |
|
_new_time = _parse_decimal_100(_term); |
|
break; |
|
case _GPS_SENTENCE_RMC + 9: // Date (GPRMC) |
|
_new_date = atol(_term); |
|
break; |
|
|
|
// location |
|
// |
|
case _GPS_SENTENCE_RMC + 3: // Latitude |
|
case _GPS_SENTENCE_GGA + 2: |
|
_new_latitude = _parse_degrees(); |
|
break; |
|
case _GPS_SENTENCE_RMC + 4: // N/S |
|
case _GPS_SENTENCE_GGA + 3: |
|
if (_term[0] == 'S') |
|
_new_latitude = -_new_latitude; |
|
break; |
|
case _GPS_SENTENCE_RMC + 5: // Longitude |
|
case _GPS_SENTENCE_GGA + 4: |
|
_new_longitude = _parse_degrees(); |
|
break; |
|
case _GPS_SENTENCE_RMC + 6: // E/W |
|
case _GPS_SENTENCE_GGA + 5: |
|
if (_term[0] == 'W') |
|
_new_longitude = -_new_longitude; |
|
break; |
|
case _GPS_SENTENCE_GGA + 9: // Altitude (GPGGA) |
|
_new_altitude = _parse_decimal_100(_term); |
|
break; |
|
|
|
// course and speed |
|
// |
|
case _GPS_SENTENCE_RMC + 7: // Speed (GPRMC) |
|
case _GPS_SENTENCE_VTG + 5: // Speed (VTG) |
|
_new_speed = (_parse_decimal_100(_term) * 514) / 1000; // knots-> m/sec, approximiates * 0.514 |
|
break; |
|
case _GPS_SENTENCE_HDT + 1: // Course (HDT) |
|
_new_gps_yaw = _parse_decimal_100(_term); |
|
break; |
|
case _GPS_SENTENCE_RMC + 8: // Course (GPRMC) |
|
case _GPS_SENTENCE_VTG + 1: // Course (VTG) |
|
_new_course = _parse_decimal_100(_term); |
|
break; |
|
} |
|
} |
|
|
|
#if 1 |
|
static uint32_t cnt; |
|
cnt++; |
|
// if ((_sentence_type == _GPS_SENTENCE_MARK || _sentence_type == _GPS_SENTENCE_EVENT )) |
|
// gcs().send_text(MAV_SEVERITY_INFO, "%d,t:%d, n:%d,%d: %s",cnt,_sentence_type,_term_number,_term[0],_term); |
|
// rtk event mark decode |
|
if ((_sentence_type == _GPS_SENTENCE_MARK || _sentence_type == _GPS_SENTENCE_EVENT )) { |
|
switch (_sentence_type + _term_number) { |
|
// operational status |
|
// |
|
case _GPS_SENTENCE_MARK + 5: // week |
|
case _GPS_SENTENCE_EVENT + 5: // week |
|
gps_mark.week = atol(_term); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "%s:%d",_term,gps_mark.week); |
|
break; |
|
case _GPS_SENTENCE_MARK + 6: // second |
|
case _GPS_SENTENCE_EVENT + 6: // second |
|
gps_mark.second = atof(_term); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "%s:%f",_term,gps_mark.second); |
|
break; |
|
case _GPS_SENTENCE_MARK + 12: // lat |
|
case _GPS_SENTENCE_EVENT + 22: // lat |
|
gps_mark.lat = atof(_term); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "%s:%f",_term,gps_mark.lat); |
|
break; |
|
case _GPS_SENTENCE_MARK + 13: // lon |
|
case _GPS_SENTENCE_EVENT + 23: |
|
gps_mark.lon = atof(_term); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "%s:%f",_term,gps_mark.lon); |
|
break; |
|
case _GPS_SENTENCE_MARK + 14: // hgt |
|
case _GPS_SENTENCE_EVENT + 24: |
|
gps_mark.hgt = atof(_term); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "%s:%f",_term,gps_mark.lon); |
|
break; |
|
case _GPS_SENTENCE_MARK + 15: // undulation |
|
case _GPS_SENTENCE_EVENT + 25: |
|
gps_mark.undulation = atof(_term); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "%d,%s",_term_number,_term); |
|
break; |
|
case _GPS_SENTENCE_MARK + 17: // lat_sigma |
|
case _GPS_SENTENCE_EVENT + 27: |
|
gps_mark.lat_sigma = atof(_term); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "%d,%s",_term_number,_term); |
|
break; |
|
case _GPS_SENTENCE_MARK + 18: // lon_sigma |
|
case _GPS_SENTENCE_EVENT + 28: |
|
gps_mark.lon_sigma = atof(_term); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "%d,%s",_term_number,_term); |
|
break; |
|
case _GPS_SENTENCE_MARK + 19: // hgt_sigma |
|
case _GPS_SENTENCE_EVENT + 29: |
|
gps_mark.hgt_sigma = atof(_term); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "%d,%s",_term_number,_term); |
|
break; |
|
case _GPS_SENTENCE_MARK + 21: // diff_age |
|
case _GPS_SENTENCE_EVENT + 31: |
|
gps_mark.diff_age = atof(_term); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "%d,%s",_term_number,_term); |
|
break; |
|
case _GPS_SENTENCE_MARK + 22: // sol_age |
|
case _GPS_SENTENCE_EVENT + 32: |
|
gps_mark.sol_age = atof(_term); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "%d,%s",_term_number,_term); |
|
break; |
|
case _GPS_SENTENCE_MARK + 23: // SVs |
|
case _GPS_SENTENCE_EVENT + 33: |
|
gps_mark.SVs = atol(_term); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "%d,%s",_term_number,_term); |
|
case _GPS_SENTENCE_MARK + 24: // solnSVs |
|
case _GPS_SENTENCE_EVENT + 34: |
|
gps_mark.solnSVs = atol(_term); |
|
// gcs().send_text(MAV_SEVERITY_INFO, "--- %d,%s \n",_term_number,_term); |
|
get_new_mark = true; |
|
|
|
break; |
|
} |
|
} |
|
#endif |
|
return false; |
|
} |
|
|
|
bool |
|
AP_GPS_NMEA::_send_message( void *msg, uint16_t size) |
|
{ |
|
if (port->txspace() < size) { |
|
return false; |
|
} |
|
|
|
port->write((const uint8_t *)msg, size); |
|
return true; |
|
} |
|
|
|
|
|
const char AP_GPS_NMEA::_initialisation_blob[] = AP_GPS_NMEA_HEMISPHERE_INIT_STRING; |
|
|
|
void AP_GPS_NMEA::send_init_blob(uint8_t instance, AP_GPS &gps) |
|
{ |
|
gps.send_blob_start(instance, _initialisation_blob, sizeof(_initialisation_blob)); |
|
} |
|
|
|
/* |
|
detect a NMEA GPS. Adds one byte, and returns true if the stream |
|
matches a NMEA string |
|
*/ |
|
bool |
|
AP_GPS_NMEA::_detect(struct NMEA_detect_state &state, uint8_t data) |
|
{ |
|
switch (state.step) { |
|
case 0: |
|
state.ck = 0; |
|
if ('$' == data) { |
|
state.step++; |
|
} |
|
break; |
|
case 1: |
|
if ('*' == data) { |
|
state.step++; |
|
} else { |
|
state.ck ^= data; |
|
} |
|
break; |
|
case 2: |
|
if (hexdigit(state.ck>>4) == data) { |
|
state.step++; |
|
} else { |
|
state.step = 0; |
|
} |
|
break; |
|
case 3: |
|
if (hexdigit(state.ck&0xF) == data) { |
|
state.step = 0; |
|
return true; |
|
} |
|
state.step = 0; |
|
break; |
|
} |
|
return false; |
|
} |
|
|
|
|
|
// Calculate and return the CRC for usA binary buffer |
|
// unsigned long CalculateCRC32(u_char *szBuf, int iSize) |
|
// { |
|
// int iIndex; |
|
// u_long ulCRC = 0; |
|
// for (iIndex=0; iIndex<iSize; iIndex++) |
|
// { |
|
// ulCRC = aulCrcTable[(ulCRC ^ szBuf[iIndex]) & 0xff] ^ (ulCRC >> 8); |
|
// } |
|
// return ulCRC; |
|
// }
|