QNMEA.dll Decompilation
Raw technical findings from reverse engineering QGNSS V1.8 using Ghidra. QGNSS is the Windows-only configuration tool provided by Quectel for their GNSS module families. These findings informed the design of the lc29h Linux replacement.
Binary Overview
Section titled “Binary Overview”| Component | Description |
|---|---|
| QGNSS.exe | Qt5 C++ application, 15,013 functions |
| QNMEA.dll | NMEA parser library, 669 exported functions |
| Runtime | Qt 5.x (QIODevice, QSettings, QByteArray, QString) |
QNMEA.dll Parser Architecture
Section titled “QNMEA.dll Parser Architecture”The DLL is a parser-only library — it contains no command construction logic. All command building happens in QGNSS.exe itself.
Class Hierarchy
Section titled “Class Hierarchy”classDiagram
NMEA_Base <|-- QGNSS_NMEA_Parse
NMEA_Base <|-- NMEA_DT
NMEA_Base <|-- Async_Read_Log
NMEA_Base <|-- QLOG_Data_Center
class QGNSS_NMEA_Parse {
Main parser
Dispatches by sentence ID
}
class NMEA_DT {
Data types
Field converters
}
class Async_Read_Log {
Async read logging
}
class QLOG_Data_Center {
Data logging/collection
}
Streaming State Machine
Section titled “Streaming State Machine”The parser uses a leftover-buffer pattern for handling partial NMEA sentences across read boundaries:
- New data arrives via an async read callback
- Data is appended to a persistent leftover buffer
- The buffer is scanned for
$(sentence start) and\r\n(sentence end) - Complete sentences between these markers are extracted and dispatched by sentence ID lookup
- Partial data remaining after the last complete sentence is retained for the next read cycle
This is the standard approach for stream-oriented NMEA parsing over serial or TCP connections — the same pattern used in the lc29h Python CLI’s parser.
Memory-Mapped Lookup Tables
Section titled “Memory-Mapped Lookup Tables”Talker ID table at virtual address 0x18001f8a0:
- 24 entries, each 4 bytes (2-character ASCII ID + 2 bytes padding)
- Maps talker prefixes (
GP,GL,GA,GB,BD,QZ,GN,GQ, etc.) to internal constellation enums
Sentence ID table at virtual address 0x18001f7b8:
- 7 entries mapping 3-character sentence types to enum values
- Mapping:
RMC=0,VTG=1,GGA=2,GSA=3,GSV=4,GNS=5,GLL=6
Protocol Constants
Section titled “Protocol Constants”Extracted from initialized data sections and function prologues:
TALKERID_LEN = 2SENTENCEID_LEN = 3CHECKSUM_LEN = 2RF_LEN = 2 (length of \r\n terminator)MIN_NMEA_LEN = 12MAX_NMEA_LEN = 82NMEASTARTC = '$' (0x24)NMEAENDC = '*' (0x2a)NMEADOT = ',' (0x2c, field separator)NMEARF = '\r\n'MAX_NMEA_LEN=82 is the NMEA 0183 standard maximum sentence length including $ and \r\n. These constants match the specification exactly.
QGNSS.exe Command Construction
Section titled “QGNSS.exe Command Construction”Checksum Functions
Section titled “Checksum Functions”Two checksum implementations were identified:
Serial transmission variant (FUN_14024abd0):
- Input:
QByteArray(raw bytes of sentence body) - Operation: XOR all bytes
- Output: Appends
*XX\r\nto the input - Used when writing commands to the serial port via
QIODevice::write
Display/logging variant (FUN_14024adc0):
- Input:
QString(Unicode sentence body) - Operation: XOR all character code points
- Output: Appends
*XXonly (no CRLF terminator) - Used for command echo in the UI console
Both compute the standard NMEA XOR checksum. The only difference is framing: serial output includes \r\n, display output does not.
Command Build Pattern
Section titled “Command Build Pattern”Command construction follows a consistent pattern:
sprintf(orQString::arg) builds the sentence body (e.g.,PAIR066,1,1,1,1,1)- The serial-variant checksum function appends
*XX\r\n QIODevice::writesends the complete framed command to the serial port
No intermediate validation or escaping. Commands are assembled from format strings with parameters interpolated directly.
PAIR Command Catalog
Section titled “PAIR Command Catalog”Commands found in the QGNSS binary, organized by the UI page where they appeared:
System and Restart
Section titled “System and Restart”| Command | Description | Variants |
|---|---|---|
| PAIR003 | Cold start (clear all) | AA, DA |
| PAIR004 | Hot start | AA, DA |
| PAIR005 | Warm start | AA, DA |
| PAIR006 | Cold start (keep config) | AA, DA |
| PAIR007 | Factory reset | AA, DA |
| PAIR021 | Query firmware version | All |
Constellation Configuration
Section titled “Constellation Configuration”| Command | Description |
|---|---|
| PAIR066 | Set active constellations |
PAIR066 uses positional boolean fields:
$PAIR066,gps,glo,gal,bds,qzss*CSEach field is 0 (disabled) or 1 (enabled). Example enabling GPS, Galileo, and BeiDou only:
$PAIR066,1,0,1,1,0*CSAGNSS and EPO
Section titled “AGNSS and EPO”| Command | Description |
|---|---|
| PAIR470 | Query EPO status |
| PAIR471 | Inject EPO data segment (20 hex fields) |
| PAIR472 | Erase EPO data |
| PAIR590 | Time assist injection |
| PAIR600 | Position assist injection |
| PAIR864 | Trigger EPO file download |
PAIR471 segments use 20 hex-encoded fields per command. The full EPO file is chunked and sent as sequential PAIR471 commands:
$PAIR471,field0,field1,...,field19*CSPAIR590 injects time assistance:
$PAIR590,year,month,day,hour,minute,second*CSPAIR600 uses hardcoded uncertainty values in the QGNSS binary, though the protocol specification allows them to be set explicitly.
PQTM Command Catalog
Section titled “PQTM Command Catalog”Quectel proprietary commands found in UI construction code:
Module Information
Section titled “Module Information”| Command | Description |
|---|---|
| PQTMVERNO | Query firmware/hardware/SDK version |
Port and Message Configuration
Section titled “Port and Message Configuration”| Command | Description |
|---|---|
| PQTMCFGPORT | Configure port baud rate and protocol |
| PQTMCFGNMEAMSG | Configure per-message NMEA output |
| PQTMCFGMSGRATE | Set output rate for any message type |
Navigation Tuning
Section titled “Navigation Tuning”| Command | Description |
|---|---|
| PQTMCFGEAMASK | Set satellite elevation mask angle |
| PQTMCFGCLAMPING | Enable/disable static speed clamping |
| PQTMCFGODO | Enable/disable odometer |
| PQTMRESETODO | Reset odometer |
| PQTMCFGGEOFENCE | Configure geofence circles |
Dead Reckoning (BA/CA)
Section titled “Dead Reckoning (BA/CA)”| Command | Description |
|---|---|
| PQTMCFGDRMODE | Set DR mode (GNSS-only, DR-only, auto) |
| PQTMCFGFWD | Enable/disable UART forwarding |
| PQTMCFGWHEELTICK | Configure wheel tick input |
| PQTMCFGEINSMSG | Enable/disable extended INS messages |
CAN Bus (BA only)
Section titled “CAN Bus (BA only)”| Command | Description |
|---|---|
| PQTMCFGCAN | Configure CAN bus interface |
| PQTMCFGCANFILTER | Configure CAN message filters |
| PQTMCFGVEHDBC | Configure vehicle DBC for DR |
System Control
Section titled “System Control”| Command | Description |
|---|---|
| PQTMSAVEPAR | Save parameters to flash |
| PQTMRESTOREPAR | Restore factory defaults |
| PQTMGNSSSUSPEND | Suspend GNSS engine |
| PQTMGNSSRESUME | Resume GNSS engine |
| PQTMGNSSRESTART | Restart GNSS engine |
| PQTMFWUPGRADE | Enter firmware upgrade mode |
NTRIP Implementation
Section titled “NTRIP Implementation”The NTRIP client in QGNSS was implemented directly over TCP sockets (not via Qt’s HTTP classes):
- HTTP request templates were hardcoded as string literals in the binary
- Basic authentication used standard base64 encoding
- User-Agent was
"NTRIP QGNSS" - Connection settings were persisted via
QSettingsunder theNtrip/registry prefix:Ntrip/IP,Ntrip/Port,Ntrip/Username,Ntrip/Password,Ntrip/Mountpoint
- Auto-reconnect on disconnect with no backoff logic
AGNSS File Handling
Section titled “AGNSS File Handling”EPO File Paths
Section titled “EPO File Paths”Extracted from string constants in the QGNSS binary:
| Filename | Contents |
|---|---|
QGPS.DAT | GPS extended ephemeris |
QG_R.DAT | GLONASS extended ephemeris |
QGA.DAT | Galileo extended ephemeris |
QBD2.DAT | BeiDou extended ephemeris |
Download source: wpepodownload.mediatek.com
EPO Binary Protocol
Section titled “EPO Binary Protocol”The EPO injection protocol (used with PAIR471) wraps data in a binary frame:
+----------+----------+---------+---------+----------+----------+| Preamble | MsgID | Length | Payload | Checksum | Tail || 2 bytes | 2 bytes | 2 bytes | N bytes | 1 byte | 2 bytes |+----------+----------+---------+---------+----------+----------+| 0x04 0x24| | LE16 | | XOR | 0xAA 0x44|- Preamble: Fixed bytes
0x04 0x24 - MsgID: 2-byte message identifier
- Length: 2-byte little-endian payload length
- Payload: EPO data for the segment
- Checksum: Single-byte XOR checksum
- Tail marker:
0xAA 0x44
The host reads .DAT files, splits them into segments fitting the PAIR471 field limit, wraps each in the binary frame, hex-encodes the fields, and sends them sequentially.
ModelInfo.json Structure
Section titled “ModelInfo.json Structure”QGNSS ships with a ModelInfo.json that declares per-model capabilities:
| Field | Type | Description |
|---|---|---|
name | string | Model identifier (e.g., LC29HAA) |
isNMEA_CMD | bool | Supports NMEA command protocol |
Full | string | Full cold start command (empty = not supported) |
Warm | string | Warm start command |
Hot | string | Hot start command |
Cold | string | Cold start command |
AGNSS | bool | Supports AGNSS online (EPO injection) |
AGNSS_OL | bool | Supports AGNSS offline (EASY prediction) |
DR | bool | Dead reckoning flag |
NTRIP | bool | NTRIP client support enabled in QGNSS |
TTFF | bool | TTFF measurement support |
baudRate | string | Default baud rate |
configuration | bool | Advanced configuration page enabled |
See Also
Section titled “See Also”- Firmware Internals — Round 2 analysis: FlashUpdater, QXWZ SDK, and bootloader
- PAIR Commands Reference — Full command documentation
- PQTM Commands Reference — Quectel proprietary commands