543 lines
16 KiB
Markdown
543 lines
16 KiB
Markdown
# Custom NMEA Sentences Reference
|
|
|
|
## Overview
|
|
|
|
This document describes the custom NMEA-format sentences (GNPOS and GNDEV) output by the H11hw GNSS RTK device. These proprietary sentences provide comprehensive positioning data and device information in a standardized NMEA format.
|
|
|
|
**NMEA Format Structure:**
|
|
```
|
|
$<sentence>*<checksum>\r\n
|
|
```
|
|
- `$` - Start delimiter
|
|
- `<sentence>` - Sentence identifier and data fields (comma-separated)
|
|
- `*` - Checksum delimiter
|
|
- `<checksum>` - Two-digit hexadecimal XOR checksum
|
|
- `\r\n` - End delimiter (CR+LF)
|
|
|
|
---
|
|
|
|
## 1. GNPOS - Position Data Sentence
|
|
|
|
### Description
|
|
|
|
The GNPOS sentence provides comprehensive real-time positioning information including coordinates, accuracy metrics, satellite status, and system information.
|
|
|
|
### Output Frequency
|
|
|
|
- **Default:** Every 800ms (1.25 Hz)
|
|
- **Configurable:** Via `AT+BT_OUT=SET` command
|
|
|
|
### Format
|
|
|
|
```
|
|
$GNPOS,<lat>,<lon>,<alt>,<altCorr>,<status>,<hdop>,<hrms>,<vrms>,<satUsed>,<satView>,<speed>,<heading>,<battV>,<battPct>,<ntripFlag>,<rtcmSize>,<age>,<timestamp>,<tiltAngle>*<checksum>\r\n
|
|
```
|
|
|
|
### Field Definitions
|
|
|
|
| Field | Index | Type | Unit | Description | Example |
|
|
|-------|-------|------|------|-------------|---------|
|
|
| Sentence ID | 0 | String | - | Fixed identifier | GNPOS |
|
|
| lat | 1 | Float | degrees | Latitude (signed, 9 decimals) | 31.140518542 |
|
|
| lon | 2 | Float | degrees | Longitude (signed, 9 decimals) | 121.284018564 |
|
|
| alt | 3 | Float | meters | Altitude above sea level (3 decimals) | 45.123 |
|
|
| altCorr | 4 | Float | meters | Corrected altitude (3 decimals) | 45.456 |
|
|
| status | 5 | Integer | - | Positioning status (see table below) | 4 |
|
|
| hdop | 6 | Float | - | Horizontal Dilution of Precision (2 decimals) | 0.85 |
|
|
| hrms | 7 | Float | meters | Horizontal RMS error (3 decimals) | 0.012 |
|
|
| vrms | 8 | Float | meters | Vertical RMS error (3 decimals) | 0.018 |
|
|
| satUsed | 9 | Integer | - | Number of satellites used in solution | 18 |
|
|
| satView | 10 | Integer | - | Number of satellites visible | 24 |
|
|
| speed | 11 | Float | km/h | Ground speed (3 decimals) | 12.345 |
|
|
| heading | 12 | Float | degrees | Heading/course over ground (2 decimals) | 135.67 |
|
|
| battV | 13 | Float | volts | Battery voltage (2 decimals) | 4.15 |
|
|
| battPct | 14 | Integer | % | Battery percentage (0-100) | 85 |
|
|
| ntripFlag | 15 | Integer | - | NTRIP connection status (0=disconnected, 1=connected) | 1 |
|
|
| rtcmSize | 16 | Integer | bytes | RTCM data size received | 1024 |
|
|
| age | 17 | Float | seconds | Differential correction age (1 decimal) | 1.2 |
|
|
| timestamp | 18 | Integer | seconds | System timestamp (Unix time) | 1714953600 |
|
|
| tiltAngle | 19 | Float | degrees | Device tilt angle (1 decimal) | 2.5 |
|
|
|
|
### Positioning Status Values
|
|
|
|
| Value | Status | Description |
|
|
|-------|--------|-------------|
|
|
| 0 | No Fix | No valid position |
|
|
| 1 | Single Point | Autonomous GPS/GNSS positioning |
|
|
| 2 | DGPS | Differential GPS (SBAS corrected) |
|
|
| 4 | RTK Fixed | RTK fixed solution (cm-level accuracy) |
|
|
| 5 | RTK Float | RTK float solution (dm-level accuracy) |
|
|
|
|
### Example Output
|
|
|
|
```
|
|
$GNPOS,31.140518542,121.284018564,45.123,45.456,4,0.85,0.012,0.018,18,24,12.345,135.67,4.15,85,1,1024,1.2,1714953600,2.5*5A\r\n
|
|
```
|
|
|
|
### Parsing Example (Python)
|
|
|
|
```python
|
|
def parse_gnpos(sentence):
|
|
# Remove $, checksum, and whitespace
|
|
data = sentence.strip().split('*')[0].lstrip('$')
|
|
fields = data.split(',')
|
|
|
|
if fields[0] != 'GNPOS' or len(fields) != 20:
|
|
return None
|
|
|
|
return {
|
|
'latitude': float(fields[1]),
|
|
'longitude': float(fields[2]),
|
|
'altitude': float(fields[3]),
|
|
'altitude_corrected': float(fields[4]),
|
|
'status': int(fields[5]),
|
|
'hdop': float(fields[6]),
|
|
'hrms': float(fields[7]),
|
|
'vrms': float(fields[8]),
|
|
'satellites_used': int(fields[9]),
|
|
'satellites_visible': int(fields[10]),
|
|
'speed_kmh': float(fields[11]),
|
|
'heading': float(fields[12]),
|
|
'battery_voltage': float(fields[13]),
|
|
'battery_percent': int(fields[14]),
|
|
'ntrip_connected': bool(int(fields[15])),
|
|
'rtcm_size': int(fields[16]),
|
|
'correction_age': float(fields[17]),
|
|
'timestamp': int(fields[18]),
|
|
'tilt_angle': float(fields[19])
|
|
}
|
|
```
|
|
|
|
### Parsing Example (C)
|
|
|
|
```c
|
|
typedef struct {
|
|
double latitude;
|
|
double longitude;
|
|
float altitude;
|
|
float altitude_corrected;
|
|
int status;
|
|
float hdop;
|
|
float hrms;
|
|
float vrms;
|
|
int satellites_used;
|
|
int satellites_visible;
|
|
float speed_kmh;
|
|
float heading;
|
|
float battery_voltage;
|
|
int battery_percent;
|
|
int ntrip_connected;
|
|
int rtcm_size;
|
|
float correction_age;
|
|
long long timestamp;
|
|
float tilt_angle;
|
|
} gnpos_data_t;
|
|
|
|
int parse_gnpos(const char* sentence, gnpos_data_t* data) {
|
|
if (strncmp(sentence, "$GNPOS,", 7) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
int result = sscanf(sentence,
|
|
"$GNPOS,%lf,%lf,%f,%f,%d,%f,%f,%f,%d,%d,%f,%f,%f,%d,%d,%d,%f,%lld,%f*",
|
|
&data->latitude,
|
|
&data->longitude,
|
|
&data->altitude,
|
|
&data->altitude_corrected,
|
|
&data->status,
|
|
&data->hdop,
|
|
&data->hrms,
|
|
&data->vrms,
|
|
&data->satellites_used,
|
|
&data->satellites_visible,
|
|
&data->speed_kmh,
|
|
&data->heading,
|
|
&data->battery_voltage,
|
|
&data->battery_percent,
|
|
&data->ntrip_connected,
|
|
&data->rtcm_size,
|
|
&data->correction_age,
|
|
&data->timestamp,
|
|
&data->tilt_angle
|
|
);
|
|
|
|
return (result == 19) ? 0 : -1;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 2. GNDEV - Device Information Sentence
|
|
|
|
### Description
|
|
|
|
The GNDEV sentence provides device identification and cellular module information. This sentence is useful for device management, inventory tracking, and remote diagnostics.
|
|
|
|
### Output Frequency
|
|
|
|
- **Bluetooth disconnected:** Every 5 seconds
|
|
- **Bluetooth connected (first 10 seconds):** Every 800ms (fast sync)
|
|
- **Bluetooth connected (after 10 seconds):** Every 5 seconds
|
|
|
|
### Format
|
|
|
|
```
|
|
$GNDEV,<sn>,<pcb_version>,<fw_version>,<imei>,<imsi>,<iccid>*<checksum>\r\n
|
|
```
|
|
|
|
### Field Definitions
|
|
|
|
| Field | Index | Type | Length | Description | Example |
|
|
|-------|-------|------|--------|-------------|---------|
|
|
| Sentence ID | 0 | String | - | Fixed identifier | GNDEV |
|
|
| sn | 1 | String | Variable | Device serial number (unique ID) | H11-20240507-001 |
|
|
| pcb_version | 2 | String | Variable | PCB hardware version | V1.2 |
|
|
| fw_version | 3 | String | Variable | Firmware version (x.x.x format) | 1.2.37 |
|
|
| imei | 4 | String | 15 | 4G module IMEI (International Mobile Equipment Identity) | 866123456789012 |
|
|
| imsi | 5 | String | 15 | SIM card IMSI (International Mobile Subscriber Identity) | 460012345678901 |
|
|
| iccid | 6 | String | 20 | SIM card ICCID (Integrated Circuit Card ID) | 89860123456789012345 |
|
|
|
|
### Example Output
|
|
|
|
```
|
|
$GNDEV,H11-20240507-001,V1.2,1.2.37,866123456789012,460012345678901,89860123456789012345*3F\r\n
|
|
```
|
|
|
|
### Parsing Example (Python)
|
|
|
|
```python
|
|
def parse_gndev(sentence):
|
|
# Remove $, checksum, and whitespace
|
|
data = sentence.strip().split('*')[0].lstrip('$')
|
|
fields = data.split(',')
|
|
|
|
if fields[0] != 'GNDEV' or len(fields) != 7:
|
|
return None
|
|
|
|
return {
|
|
'serial_number': fields[1],
|
|
'pcb_version': fields[2],
|
|
'firmware_version': fields[3],
|
|
'imei': fields[4],
|
|
'imsi': fields[5],
|
|
'iccid': fields[6]
|
|
}
|
|
```
|
|
|
|
### Parsing Example (C)
|
|
|
|
```c
|
|
typedef struct {
|
|
char serial_number[32];
|
|
char pcb_version[16];
|
|
char firmware_version[16];
|
|
char imei[16];
|
|
char imsi[16];
|
|
char iccid[32];
|
|
} gndev_data_t;
|
|
|
|
int parse_gndev(const char* sentence, gndev_data_t* data) {
|
|
if (strncmp(sentence, "$GNDEV,", 7) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
int result = sscanf(sentence,
|
|
"$GNDEV,%31[^,],%15[^,],%15[^,],%15[^,],%15[^,],%31[^*]*",
|
|
data->serial_number,
|
|
data->pcb_version,
|
|
data->firmware_version,
|
|
data->imei,
|
|
data->imsi,
|
|
data->iccid
|
|
);
|
|
|
|
return (result == 6) ? 0 : -1;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Checksum Calculation
|
|
|
|
### Algorithm
|
|
|
|
The checksum is calculated as the XOR of all characters between `$` and `*` (exclusive).
|
|
|
|
### Example (C)
|
|
|
|
```c
|
|
uint8_t calculate_nmea_checksum(const char* sentence) {
|
|
uint8_t checksum = 0;
|
|
const char* ptr = sentence;
|
|
|
|
// Skip '$' if present
|
|
if (*ptr == '$') ptr++;
|
|
|
|
// XOR all characters until '*' or end
|
|
while (*ptr && *ptr != '*') {
|
|
checksum ^= *ptr;
|
|
ptr++;
|
|
}
|
|
|
|
return checksum;
|
|
}
|
|
|
|
// Verify checksum
|
|
int verify_nmea_checksum(const char* sentence) {
|
|
const char* asterisk = strchr(sentence, '*');
|
|
if (!asterisk) return 0;
|
|
|
|
uint8_t calculated = calculate_nmea_checksum(sentence);
|
|
uint8_t received = 0;
|
|
sscanf(asterisk + 1, "%02X", &received);
|
|
|
|
return (calculated == received);
|
|
}
|
|
```
|
|
|
|
### Example (Python)
|
|
|
|
```python
|
|
def calculate_nmea_checksum(sentence):
|
|
# Remove $ and everything after *
|
|
data = sentence.lstrip('$').split('*')[0]
|
|
checksum = 0
|
|
for char in data:
|
|
checksum ^= ord(char)
|
|
return checksum
|
|
|
|
def verify_nmea_checksum(sentence):
|
|
if '*' not in sentence:
|
|
return False
|
|
data, checksum_str = sentence.split('*')
|
|
calculated = calculate_nmea_checksum(data)
|
|
received = int(checksum_str[:2], 16)
|
|
return calculated == received
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Integration Guide
|
|
|
|
### Enabling Custom Sentences
|
|
|
|
Use the `AT+BT_OUT=SET` command to enable GNPOS and GNDEV output:
|
|
|
|
```
|
|
AT+BT_OUT=SET,1,0,1,1,0,0,0,0,0,0\r\n
|
|
```
|
|
|
|
This enables:
|
|
- Custom mode (`type=1`)
|
|
- GNPOS sentence (`gnpos=1`)
|
|
- GNDEV sentence (`gndev=1`)
|
|
- Disables JSON and standard NMEA sentences
|
|
|
|
### Complete Data Stream Example
|
|
|
|
```
|
|
$GNDEV,H11-20240507-001,V1.2,1.2.37,866123456789012,460012345678901,89860123456789012345*3F\r\n
|
|
$GNPOS,31.140518542,121.284018564,45.123,45.456,4,0.85,0.012,0.018,18,24,12.345,135.67,4.15,85,1,1024,1.2,1714953600,2.5*5A\r\n
|
|
$GNPOS,31.140518543,121.284018565,45.124,45.457,4,0.85,0.012,0.018,18,24,12.346,135.68,4.15,85,1,1024,1.2,1714953601,2.5*5B\r\n
|
|
$GNPOS,31.140518544,121.284018566,45.125,45.458,4,0.85,0.012,0.018,18,24,12.347,135.69,4.15,85,1,1024,1.2,1714953602,2.5*5C\r\n
|
|
$GNDEV,H11-20240507-001,V1.2,1.2.37,866123456789012,460012345678901,89860123456789012345*3F\r\n
|
|
```
|
|
|
|
### Bluetooth Receiver Implementation
|
|
|
|
```python
|
|
import serial
|
|
import time
|
|
|
|
class H11RTKReceiver:
|
|
def __init__(self, port, baudrate=115200):
|
|
self.serial = serial.Serial(port, baudrate, timeout=1)
|
|
self.gnpos_callback = None
|
|
self.gndev_callback = None
|
|
|
|
def set_gnpos_callback(self, callback):
|
|
self.gnpos_callback = callback
|
|
|
|
def set_gndev_callback(self, callback):
|
|
self.gndev_callback = callback
|
|
|
|
def read_loop(self):
|
|
buffer = ""
|
|
while True:
|
|
data = self.serial.read(256).decode('utf-8', errors='ignore')
|
|
buffer += data
|
|
|
|
while '\n' in buffer:
|
|
line, buffer = buffer.split('\n', 1)
|
|
line = line.strip()
|
|
|
|
if line.startswith('$GNPOS,'):
|
|
if verify_nmea_checksum(line):
|
|
gnpos_data = parse_gnpos(line)
|
|
if self.gnpos_callback and gnpos_data:
|
|
self.gnpos_callback(gnpos_data)
|
|
|
|
elif line.startswith('$GNDEV,'):
|
|
if verify_nmea_checksum(line):
|
|
gndev_data = parse_gndev(line)
|
|
if self.gndev_callback and gndev_data:
|
|
self.gndev_callback(gndev_data)
|
|
|
|
# Usage example
|
|
def on_position_update(data):
|
|
print(f"Position: {data['latitude']:.9f}, {data['longitude']:.9f}")
|
|
print(f"Status: {data['status']}, Satellites: {data['satellites_used']}")
|
|
print(f"Accuracy: H={data['hrms']:.3f}m, V={data['vrms']:.3f}m")
|
|
|
|
def on_device_info(data):
|
|
print(f"Device: {data['serial_number']}")
|
|
print(f"Firmware: {data['firmware_version']}")
|
|
print(f"IMEI: {data['imei']}")
|
|
|
|
receiver = H11RTKReceiver('/dev/rfcomm0')
|
|
receiver.set_gnpos_callback(on_position_update)
|
|
receiver.set_gndev_callback(on_device_info)
|
|
receiver.read_loop()
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Data Quality Indicators
|
|
|
|
### RTK Solution Quality
|
|
|
|
| Status | HRMS Range | VRMS Range | Typical Accuracy |
|
|
|--------|------------|------------|------------------|
|
|
| RTK Fixed (4) | < 0.02m | < 0.03m | 1-2 cm horizontal, 2-3 cm vertical |
|
|
| RTK Float (5) | 0.1-0.5m | 0.2-1.0m | 10-50 cm horizontal, 20-100 cm vertical |
|
|
| DGPS (2) | 0.5-2.0m | 1.0-3.0m | 0.5-2 m horizontal, 1-3 m vertical |
|
|
| Single (1) | 2.0-10.0m | 3.0-15.0m | 2-10 m horizontal, 3-15 m vertical |
|
|
|
|
### HDOP Quality Assessment
|
|
|
|
| HDOP Value | Quality | Description |
|
|
|------------|---------|-------------|
|
|
| < 1.0 | Excellent | Ideal satellite geometry |
|
|
| 1.0 - 2.0 | Good | Acceptable for RTK |
|
|
| 2.0 - 5.0 | Moderate | Usable but degraded |
|
|
| > 5.0 | Poor | Unreliable positioning |
|
|
|
|
### Correction Age Guidelines
|
|
|
|
| Age (seconds) | RTK Quality | Recommendation |
|
|
|---------------|-------------|----------------|
|
|
| < 3.0 | Excellent | Optimal RTK performance |
|
|
| 3.0 - 10.0 | Good | Acceptable for most applications |
|
|
| 10.0 - 30.0 | Degraded | Consider reconnecting |
|
|
| > 30.0 | Poor | RTK solution unreliable |
|
|
|
|
---
|
|
|
|
## 6. Troubleshooting
|
|
|
|
### No GNPOS/GNDEV Output
|
|
|
|
**Possible Causes:**
|
|
1. Bluetooth output not configured
|
|
2. Custom mode disabled
|
|
3. Bluetooth connection lost
|
|
|
|
**Solutions:**
|
|
```
|
|
AT+BT_OUT=GET\r\n # Check current configuration
|
|
AT+BT_OUT=SET,1,0,1,1,0,0,0,0,0,0\r\n # Enable custom sentences
|
|
```
|
|
|
|
### Invalid Checksum
|
|
|
|
**Possible Causes:**
|
|
1. Data corruption during transmission
|
|
2. Bluetooth interference
|
|
3. Buffer overflow
|
|
|
|
**Solutions:**
|
|
- Verify checksum calculation algorithm
|
|
- Check Bluetooth signal strength
|
|
- Increase receive buffer size
|
|
|
|
### Incorrect Data Values
|
|
|
|
**Possible Causes:**
|
|
1. GNSS not initialized
|
|
2. No satellite fix
|
|
3. Antenna disconnected
|
|
|
|
**Solutions:**
|
|
- Check `status` field (should be > 0)
|
|
- Verify `satView` and `satUsed` (should be > 4)
|
|
- Check antenna connection
|
|
|
|
---
|
|
|
|
## 7. Best Practices
|
|
|
|
### Data Processing
|
|
|
|
1. **Always verify checksums** before parsing data
|
|
2. **Check positioning status** before using coordinates
|
|
3. **Monitor correction age** for RTK applications
|
|
4. **Validate satellite count** (minimum 4 for 3D fix)
|
|
5. **Check HDOP values** for solution quality
|
|
|
|
### Performance Optimization
|
|
|
|
1. **Buffer management:** Use circular buffers for continuous data streams
|
|
2. **Parsing efficiency:** Pre-compile regex patterns or use fixed-format parsing
|
|
3. **Callback design:** Keep callbacks lightweight, defer heavy processing
|
|
4. **Error handling:** Implement timeout and retry mechanisms
|
|
|
|
### Application Design
|
|
|
|
1. **Position filtering:** Apply Kalman filtering for smooth trajectories
|
|
2. **Status monitoring:** Track RTK solution quality over time
|
|
3. **Battery management:** Monitor voltage and percentage for low-power warnings
|
|
4. **Connection health:** Track NTRIP status and correction age
|
|
|
|
---
|
|
|
|
## Appendix A: Complete Message Examples
|
|
|
|
### RTK Fixed Solution (High Quality)
|
|
|
|
```
|
|
$GNPOS,31.140518542,121.284018564,45.123,45.456,4,0.85,0.012,0.018,18,24,0.000,0.00,4.15,85,1,1024,1.2,1714953600,0.5*XX\r\n
|
|
```
|
|
- Status: 4 (RTK Fixed)
|
|
- HRMS: 0.012m (1.2cm horizontal accuracy)
|
|
- VRMS: 0.018m (1.8cm vertical accuracy)
|
|
- 18 satellites used, 24 visible
|
|
- NTRIP connected, 1.2s correction age
|
|
|
|
### RTK Float Solution (Medium Quality)
|
|
|
|
```
|
|
$GNPOS,31.140518542,121.284018564,45.123,45.456,5,1.20,0.250,0.450,15,22,5.234,45.30,4.10,80,1,1024,3.5,1714953600,1.2*XX\r\n
|
|
```
|
|
- Status: 5 (RTK Float)
|
|
- HRMS: 0.250m (25cm horizontal accuracy)
|
|
- VRMS: 0.450m (45cm vertical accuracy)
|
|
- 15 satellites used, 22 visible
|
|
- NTRIP connected, 3.5s correction age
|
|
|
|
### Single Point Solution (Low Quality)
|
|
|
|
```
|
|
$GNPOS,31.140518542,121.284018564,45.123,45.456,1,2.50,3.500,5.200,8,15,12.345,135.67,3.95,65,0,0,0.0,1714953600,2.5*XX\r\n
|
|
```
|
|
- Status: 1 (Single Point)
|
|
- HRMS: 3.500m (3.5m horizontal accuracy)
|
|
- VRMS: 5.200m (5.2m vertical accuracy)
|
|
- 8 satellites used, 15 visible
|
|
- NTRIP disconnected
|
|
|
|
---
|
|
|
|
**Document Version:** 1.0
|
|
**Firmware Version:** 1.2.37
|
|
**Last Updated:** 2026-05-07
|