Skip to content

Commit 8d9cb41

Browse files
committed
update NMEA PAIR response handling
update NMEA PAIR poll handling update NMEA preset panel update NMEA preset panel update config panels grid
1 parent 4a3eca6 commit 8d9cb41

18 files changed

Lines changed: 317 additions & 206 deletions

README.md

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
[Mapquest API Key](#mapquestapi) |
1515
[User-defined Presets](#userdefined) |
1616
[CLI Utilities](#cli) |
17-
[Known Issues](#knownissues) |
17+
[Troubleshooting](#troubleshoot) |
1818
[License](#license) |
1919
[Author Information](#author)
2020

@@ -220,7 +220,7 @@ The UBX Configuration Dialog currently provides the following UBX configuration
220220
1. Message Rate panel (CFG-MSG) sets message rates per port for UBX and NMEA messages (*legacy protocols only*). Message rate is relative to navigation solution frequency e.g. a message rate of '4' means 'every 4th navigation solution' (higher = less frequent).
221221
1. Configuration Interface widget (CFG-VALSET, CFG-VALDEL and CFG-VALGET) queries and sets configuration for *modern protocols only*.
222222
1. UBX Legacy Command configuration panel providing structured updates for a range of legacy CFG-* configuration commands (*legacy protocols only*). Note: 'X' (byte) type attributes can be entered as integers or hexadecimal strings e.g. 522125312 or 0x1f1f0000. Once a command is selected, the configuration is polled and the current values displayed. The user can then amend these values as required and send the updated configuration. Some polls require input arguments (e.g. portID) - these are highlighted and will be set at default values initially (e.g. portID = 0), but can be amended by the user and re-polled using the ![refresh](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-refresh-lined-24.png?raw=true) button.
223-
1. Preset Commands widget supports a variety of user-defined UBX commands and queries - see [user defined presets](#userdefined).
223+
1. Preset Commands widget supports a variety of user-defined UBX commands and queries - see [user-defined presets](#userdefined).
224224

225225
An icon to the right of each 'SEND'
226226
![send icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-arrow-12-24.png?raw=true) button indicates the confirmation status of the configuration command;
@@ -236,25 +236,26 @@ warning ![warning icon](https://github.com/semuconsulting/PyGPSClient/blob/maste
236236
**Pre-Requisites:**
237237

238238
- Receiver capable of being configured via proprietary NMEA sentences, connected to the workstation via USB or UART port.
239-
- The facility includes support for a wide range of Quectel LG and LC series receivers via PQTM*, PSTM* and PAIR* sentences¹. Additional types may be supported in the underlying NMEA parser library [pynmeagps](https://github.com/semuconsulting/pynmeagps) in later releases (*contributions welcome*).
239+
- The facility includes support for a wide range of Quectel LG and LC series receivers via `PQTM*`, `PSTM*` and `PAIR*` sentences¹ ². Additional types may be supported in the underlying NMEA parser library [pynmeagps](https://github.com/semuconsulting/pynmeagps) in later releases (*contributions welcome*).
240240

241-
¹ Note that Quectel message support depends on the *specific model variant* and firmware version, and several models (e.g. LG290 and LC29) exist in a wide range of variants. Refer to the GNSS Protocol Guide for your *specific* variant.
241+
¹ Note that Quectel receivers implement a bewildering array of different configuration protocols, based on a mixture of proprietary NMEA `PQTM*`, `PSTM*` and `PAIR*` message types. Implementation depends on the *specific model variant and firmware version*, and several models (e.g. LG290 and LC29) exist in a wide range of variants. Refer to the GNSS Protocol Guide for your *specific* variant for details on the available configuration commands.
242+
243+
² Note that several Quectel configuration commands require a Hot Restart (PQTMHOT) *or* Save (PQTMSAVEPAR or PAIR513) and Reset (PQTMSRR) before taking effect, including for example PQTMCFGCNST (Enable/Disable Constellations), PQTMCFGFIX (Configure Fix Rate), PQTMCFGSAT (Configure Satellite Masks), PQTMCFGSIGNAL (Configure Signal Masks), PAIR050 (Set Fix Rate) and PAIR864 (Set Baud Rate). Devices (e.g. some LC variants) that don't implement a software reset command may have to be physically disconnected and reconnected.
242244

243245
**Instructions:**
244246

245247
The NMEA Configuration Dialog currently provides the following NMEA configuration panels:
246248
1. Version panel shows current device hardware/firmware versions (*Double-left-click to refresh*).
247249
1. Dynamic configuration panel providing structured updates for supported receivers e.g. Quectel LGSERIES via PQTM* sentences, or LCSERIES via PAIR* sentences. Once a command is selected, the configuration is polled and the current values displayed. The user can then amend these values as required and send the updated configuration. Some polls require input arguments (e.g. portid or msgname) - these are highlighted and will be set at default values initially (e.g. portid = 1), but can be amended by the user and re-polled using the ![refresh](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-refresh-lined-24.png?raw=true) button.
248250
1. Preset Commands widget supports a variety of user-defined NMEA commands and queries - see [user defined presets](#userdefined).
251+
1. Preset commands, once selected, can be edited or overwritten in the 'Commands' field before sending, but commands must observe the format `<talker>; <message id>; <payload as comma-separated string>; <msgmode>` (e.g. `P; QTMCFGUART; W,115200; 1` - see [user-defined presets](#userdefined)).
249252

250253
An icon to the right of each 'SEND'
251254
![send icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-arrow-12-24.png?raw=true) button indicates the confirmation status of the configuration command;
252255
(pending i.e. awaiting confirmation ![pending icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-time-6-24.png?raw=true),
253256
confirmed ![confirmed icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-check-mark-8-24.png?raw=true) or
254257
warning ![warning icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-warning-1-24.png?raw=true)).
255258

256-
**NB:** Several Quectel LG and LC series commands require a Hot Restart (PQTMHOT) before taking effect, including PQTMCFGCNST (Enable/Disable Constellations), PQTMCFGFIX (Configure Fix Rate), PQTMCFGSAT (Configure Satellite Masks) and PQTMCFGSIGNAL (Configure Signal Masks). This is a Quectel protocol constraint, not a PyGPSClient issue.
257-
258259
---
259260
## <a name="ttycommands">TTY Configuration Facilities</a>
260261

@@ -442,17 +443,21 @@ that this is a User variable rather than a System/Global variable.
442443
---
443444
## <a name="userdefined">User Defined Presets</a>
444445

445-
The UBX, NMEA and TTY Configuration Dialogs include the facility to send user-defined configuration messages or message sequences to a compatible receiver. These can be set up by adding appropriate comma- or semicolon-delimited message descriptions and payload definitions to the `"ubxpresets_l"`, `"nmeapresets_l"` or `"ttypresets_l"` settings in your json configuration file (see [example provided](https://github.com/semuconsulting/PyGPSClient/blob/master/pygpsclient.json#L189)). The message definition comprises a free-format text description (*avoid embedded commas or semi-colons*) followed by one or more pyubx2 (UBX), pynmeagps (NMEA) or tty (ASCII) message constructors, e,g.
446+
The UBX, NMEA and TTY Configuration Dialogs include the facility to send user-defined configuration messages or message sequences to a compatible receiver. These can be set up as follows:
447+
448+
1. By manually adding appropriate comma- or semicolon-delimited message descriptions and payload definitions to the `"ubxpresets_l"`, `"nmeapresets_l"` or `"ttypresets_l"` sections of your json configuration file (see [example provided](https://github.com/semuconsulting/PyGPSClient/blob/master/pygpsclient.json#L189)). The message definition comprises a free-format text description (*avoid embedded commas or semi-colons*) followed by one or more pyubx2 (UBX), pynmeagps (NMEA) or tty (ASCII) message constructors, e.g.
446449

447-
- UBX - `<description>, <message class>, <message id>, <payload as hexadecimal string>, <msgmode>`
448-
- NMEA - `<description>; <talker>; <message id>; <payload as comma-separated string>; <msgmode>`
449-
- TTY - `<description>; <tty command>`
450+
- UBX - `<description>, [<message class>, <message id>, <payload as hexadecimal string>, <msgmode>, ...]`
451+
- NMEA - `<description>; [<talker>; <message id>; <payload as comma-separated string>; <msgmode>; ...]`
452+
- TTY - `<description>; [<tty command>; ...]`
453+
454+
2. By using the [Configuration Command Load/Save/Record](#recorder) facility to record commands as they are sent to the receiver, and automatically import these recorded commands into the relevant `"...presets_l"` section of the json configuration file.
450455

451456
If the command description contains the term `CONFIRM`, a pop-up confirmation box will appear before the command is actioned.
452457

453-
When PyGPSClient is first started, these settings are pre-populated with an initial set of preset commands, which can be saved to a \*.json configuration file and then manually removed, amended or supplemented in accordance with the user's preferences. To reinstate this initial set at a later date, insert the line `"INIT_PRESETS",` at the top of the relevant `"ubxpresets_l"`, `"nmeapresets_l"` or `"ttypresets_l"` configuration setting.
458+
When PyGPSClient is first started, the preset command sections are pre-populated in-memory with an initial set of preset commands, which can be saved to a json configuration file and then manually edited in accordance with the user's preferences. To reinstate this initial set at a later date, insert the line `"INIT_PRESETS"` at the top of the relevant `"ubxpresets_l"`, `"nmeapresets_l"` or `"ttypresets_l"` configuration section.
454459

455-
The `pygpsclient.ubx2preset()`, `pygpsclient.nmea2preset()` and `pygpsclient.tty2preset()` helper functions may be used to convert a `UBXMessage`, `NMEAMessage` or ASCII text object into a preset string suitable for copying and pasting into the `"ubxpresents_l":`, `"nmeapresets_l":` or `"ttypresets_l":` JSON configuration sections:
460+
The `pygpsclient.ubx2preset()`, `pygpsclient.nmea2preset()` and `pygpsclient.tty2preset()` helper functions may be used to convert a `UBXMessage`, `NMEAMessage` or ASCII text object into a string suitable for copying and pasting into the `"ubxpresets_l":`, `"nmeapresets_l":` or `"ttypresets_l":` configuration file sections:
456461

457462
```python
458463
from pygpsclient import ubx2preset, nmea2preset, tty2preset
@@ -476,8 +481,6 @@ IM19 System reset CONFIRM; AT+SYSTEM_RESET
476481

477482
Multiple commands can be concatenated on a single line. Illustrative examples are shown in the sample [pygpsclient.json](https://github.com/semuconsulting/PyGPSClient/blob/master/pygpsclient.json#L189) file.
478483

479-
The [Configuration Command Load/Save/Record facility](#configuration-command-loadsaverecord-facility) can also be used to import recorded configuration command sequences into the presets section of the json configuration file.
480-
481484
---
482485
## <a name="cli">Command Line Utilities</a>
483486

@@ -486,11 +489,11 @@ The `pygnssutils` and `pyubxutils` libraries which underpin many of the function
486489
For further details, refer to the `pygnssutils` homepage at [https://github.com/semuconsulting/pygnssutils](https://github.com/semuconsulting/pygnssutils) or `pyubxutils` homepage at [https://github.com/semuconsulting/pyubxutils](https://github.com/semuconsulting/pyubxutils).
487490

488491
---
489-
## <a name="knownissues">Known Issues</a>
492+
## <a name="troubleshoot">Troubleshooting</a>
490493

491494
1. If you encounter persistent `WARNING>>Error parsing data stream Serial stream terminated unexpectedly` messages in the console, this may be indicative of insufficient serial port bandwidth (baudrate or timeout) for the current output message cohort (*particularly if this includes Ephemera or Observation data*). Try increasing the baudrate in the first instance.
492495

493-
2. Most budget USB-UART adapters (e.g. FT232, CH345, CP2102) have a bandwidth limit of around 3MB/s and may not work reliably above 115200 baud, even if the receiver supports higher baud rates. If you're using an adapter and notice significant message corruption, try reducing the baud rate to a maximum 115200.
496+
2. Most [budget USB-UART adapters](https://www.amazon.co.uk/DSD-TECH-adapter-FT232RL-Compatible/dp/B07BBPX8B8?ref_=ast_sto_dp) (e.g. FT232, CH345, CP2102, *including those embedded on development boards*) have a bandwidth limit of around 3Mbps (≈ 375000 baud) and may not work reliably above 230600 baud, even if the receiver supports higher baud rates. If you're using an adapter and notice significant message corruption (e.g. frequent `WARNING>>..invalid checksum` messages), try reducing the baud rate to a maximum 230600.
494497

495498
3. Some Linux Wayland platforms appear to require Toplevel dialog windows to be non-transient (`transient_dialog_b: 0`) for the window 'maximise' icon to work properly.
496499

RELEASE_NOTES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
1. Add preset description entry field to Configuration Command Recorder import facility.
66
1. Make TTY Presets dialog fully resizeable.
7+
1. Update NMEA config panel to allow preset commands to be edited or entered manually before sending.
8+
1. Update NMEA config panel to recognise correct PAIR responses for some Quectel set and poll commands (e.g. a PAIR864 Set baud rate command corresponds to a PAIR865 poll response).
79

810
### RELEASE 1.6.4
911

images/nmeaconfig_widget.png

54.7 KB
Loading

src/pygpsclient/dynamic_config_frame.py

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@
9898
)
9999
# alternative POLL dictionary names for where POLL command
100100
# doesn't correspond to SET (fudge for Quectel)
101+
# e.g. a PAIR864 (set baud rate) command corresponds to PAIR865 (poll baud rate)
102+
# (for PAIR commands, the poll is typically one digit higher than the set)
101103
ALT_POLL_NAMES = {
102104
"AIR050": "AIR051",
103105
"AIR058": "AIR059",
@@ -111,8 +113,8 @@
111113
"AIR100": "AIR101",
112114
"AIR104": "AIR105",
113115
"AIR400": "AIR401",
114-
"AIR410": "AIR421",
115-
"AIR420": "AIR411",
116+
"AIR410": "AIR411",
117+
"AIR420": "AIR421",
116118
"AIR432": "AIR433",
117119
"AIR434": "AIR435",
118120
"AIR436": "AIR437",
@@ -225,7 +227,7 @@ def _body(self):
225227
command=self._on_refresh,
226228
font=self.__app.font_md,
227229
)
228-
self._lbl_command = Label(self, text="", width=30, anchor=W)
230+
self._lbl_command = Label(self, text="", anchor=W)
229231
self._frm_container = Frame(self)
230232
self._can_container = Canvas(self._frm_container)
231233
self._frm_attrs = Frame(self._can_container)
@@ -246,31 +248,45 @@ def _do_layout(self):
246248
Layout widgets.
247249
"""
248250

249-
self._lbl_cfg_dyn.grid(column=0, row=0, columnspan=4, padx=3, sticky=EW)
251+
self._lbl_cfg_dyn.grid(column=0, row=0, columnspan=3, padx=3, pady=3, sticky=EW)
250252
self._lbx_cfg_cmd.grid(
251-
column=0, row=1, columnspan=2, rowspan=6, padx=3, pady=3, sticky=EW
253+
column=0, row=1, columnspan=1, rowspan=10, padx=3, pady=3, sticky=EW
254+
)
255+
self._scr_cfg_cmd.grid(column=1, row=1, rowspan=10, sticky=(N, S, W))
256+
self._btn_send_command.grid(
257+
column=2, row=1, ipadx=3, ipady=3, padx=3, pady=3, sticky=W
258+
)
259+
self._lbl_send_command.grid(
260+
column=2, row=2, ipadx=3, ipady=3, padx=3, pady=3, sticky=W
261+
)
262+
self._btn_refresh.grid(
263+
column=2, row=3, ipadx=3, ipady=3, padx=3, pady=3, sticky=W
264+
)
265+
self._lbl_command.grid(
266+
column=0, row=11, columnspan=3, padx=3, pady=3, sticky=EW
252267
)
253-
self._scr_cfg_cmd.grid(column=1, row=1, rowspan=6, sticky=(N, S, E))
254-
self._btn_send_command.grid(column=3, row=1, ipadx=3, ipady=3, sticky=W)
255-
self._lbl_send_command.grid(column=3, row=2, ipadx=3, ipady=3, sticky=W)
256-
self._btn_refresh.grid(column=3, row=3, ipadx=3, ipady=3, sticky=W)
257-
self._lbl_command.grid(column=0, row=7, columnspan=4, padx=3, sticky=EW)
258268
self._frm_container.grid(
259-
column=0, row=8, columnspan=4, rowspan=15, padx=3, sticky=NSEW
269+
column=0,
270+
row=12,
271+
columnspan=3,
272+
rowspan=15,
273+
padx=3,
274+
pady=3,
275+
sticky=NSEW,
260276
)
261277
self._can_container.grid(
262-
column=0, row=0, columnspan=3, rowspan=15, padx=3, sticky=NSEW
278+
column=0,
279+
row=0,
280+
columnspan=3,
281+
rowspan=15,
282+
padx=3,
283+
pady=3,
284+
sticky=NSEW,
263285
)
264-
self._scr_container_ver.grid(column=3, row=0, rowspan=15, sticky=(N, S, E))
286+
self._scr_container_ver.grid(column=3, row=0, rowspan=15, sticky=(N, S, W))
265287
self._scr_container_hor.grid(
266-
column=0, row=15, columnspan=4, rowspan=15, sticky=EW
288+
column=0, row=15, columnspan=3, rowspan=15, sticky=EW
267289
)
268-
269-
cols, rows = self.grid_size()
270-
for i in range(cols):
271-
self.grid_columnconfigure(i, weight=1)
272-
for i in range(rows):
273-
self.grid_rowconfigure(i, weight=1)
274290
self.option_add("*Font", self.__app.font_sm)
275291

276292
def _attach_events(self):
@@ -406,7 +422,7 @@ def _do_poll_cfg(self, *args, **kwargs): # pylint: disable=unused-argument
406422
return
407423

408424
msg = penddlg = pendcfg = None
409-
# use alternate names for some NMEA PAIR/PQTM POLL commands
425+
# use alternate name for some NMEA PAIR/PQTM POLL commands
410426
cfg_id = ALT_POLL_NAMES.get(self._cfg_id, self._cfg_id)
411427
# set any POLL arguments to specified or default values
412428
# e.g. portid = "1", msgname="RMC"
@@ -477,23 +493,26 @@ def _do_poll_args(self, cfg_id: str) -> dict:
477493
pass
478494
return args
479495

480-
def update_status(self, msg: object):
496+
def update_status(self, msg: NMEAMessage | UBXMessage):
481497
"""
482498
UBXHandler or NMEAHandler module has received expected command response
483499
and forwarded it to this module, entry widgets are pre-populated with
484500
current configuration values and confirmation status is updated.
485501
486-
:param object msg: UBXMessage or NMEAMessage response
502+
:param NMEAMessage | UBXMessage msg: UBXMessage or NMEAMessage response
487503
"""
488504

505+
# self.logger.debug(f"{msg.identity=}")
489506
ok = False
490-
# strip off any variant suffix from cfg_id
491-
# e.g. "QTMCFGUART_CURR" -> "PQTMCFGUART"
492-
cfg_id = (
493-
"P" + self._cfg_id.rsplit("_", 1)[0]
494-
if self._protocol == NMEA
495-
else self._cfg_id
496-
)
507+
if self._protocol == NMEA:
508+
# use alternate name for some NMEA PAIR/PQTM POLL commands
509+
# e.g. a PAIR864 (set baud rate) command corresponds to PAIR865 (poll baud rate)
510+
cfg_id = ALT_POLL_NAMES.get(self._cfg_id, self._cfg_id)
511+
# strip off any variant suffix from cfg_id
512+
# e.g. "QTMCFGUART_CURR" -> "PQTMCFGUART"
513+
cfg_id = "P" + cfg_id.rsplit("_", 1)[0]
514+
else:
515+
cfg_id = self._cfg_id
497516

498517
# if this message identity matches an expected response
499518
if msg.identity in (cfg_id, ACK, NAK):
@@ -530,13 +549,13 @@ def _clear_widgets(self):
530549
wdg.destroy()
531550
wdg = None
532551
Label(self._frm_attrs, text="Attribute", width=12, anchor=W).grid(
533-
column=0, row=0, padx=3, sticky=(W)
552+
column=0, row=0, padx=3, sticky=W
534553
)
535554
Label(self._frm_attrs, text="Value", width=20, anchor=W).grid(
536-
column=1, row=0, padx=3, sticky=(W)
555+
column=1, row=0, padx=3, sticky=W
537556
)
538557
Label(self._frm_attrs, text="Type", width=5, anchor=W).grid(
539-
column=2, row=0, padx=3, sticky=(W)
558+
column=2, row=0, padx=3, sticky=W
540559
)
541560

542561
def _add_widgets(self, pdict: dict, row: int, index: int) -> int:

src/pygpsclient/globals.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@
271271
VALDMY = 8
272272
VALLEN = 9
273273
VALBOOL = 10
274+
VALREGEX = 11
275+
VALCUSTOM = 12
274276
WAYPOINT = "waypoint"
275277
WIDGETU1 = (200, 200) # small widget size
276278
WIDGETU2 = (300, 200) # medium widget size

0 commit comments

Comments
 (0)