Initial commit
This commit is contained in:
542
Custom_NMEA_Sentences.md
Normal file
542
Custom_NMEA_Sentences.md
Normal file
@@ -0,0 +1,542 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user