Skip to content

Commit db5a71d

Browse files
authored
Merge pull request #243 from semuconsulting/RC-1.6.5
RC 1.6.5
2 parents dc5a0e3 + f1e0b60 commit db5a71d

29 files changed

Lines changed: 543 additions & 450 deletions

.vscode/settings.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
"telemetry.feedback.enabled": false,
2222
"python.analysis.addHoverSummaries": false,
2323
"python-envs.defaultEnvManager": "ms-python.python:venv",
24-
"python-envs.pythonProjects": [],
2524
"python.venvPath": "${userHome}/pygpsclient/bin/python3",
2625
"python.systemPath": "/usr/local/bin/python3.14",
2726
"python.defaultInterpreterPath": "${userHome}/pygpsclient/bin/python3",

README.md

Lines changed: 37 additions & 31 deletions
Large diffs are not rendered by default.

RELEASE_NOTES.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# PyGPSClient Release Notes
22

3+
### RELEASE 1.6.5
4+
5+
FIXES:
6+
1. Fix custom map import spurious validation error.
7+
8+
ENHANCEMENTS:
9+
1. Add preset description entry field to Configuration Command Recorder import facility.
10+
1. Make TTY Presets dialog fully resizeable.
11+
1. Update NMEA config panel to allow preset commands to be edited or entered manually before sending.
12+
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).
13+
314
### RELEASE 1.6.4
415

516
FIXES:
@@ -8,7 +19,7 @@ FIXES:
819

920
ENHANCEMENTS:
1021
1. Improve Base Station receiver configuration handling in the NTRIP Caster/Socket Server dialog.
11-
1. Add connected device descriptor to status bar (e.g. "u-blox ZED-F9P", "Unicore UM981S", "Septentrio mosaic-X5" or "Quectel LG290AG03"). **FYI** This is based on a series of query messages (*one for each enabled protocol*) sent approximately 3 seconds after the connection is started (*you may see 'unknown protocol' warnings in response to some of these messages; these can be ignored)*. Failing this, a generic descriptor is displayed on receipt of a message protocol unique to a particular manufacturer (*e.g. "u-blox" on receipt of a UBX message, "Unicore" on receipt of a UNI message, etc.*). Note that some (mainly older) devices may not return a meaningful descriptor, in which case "N/A" will be displayed.
22+
1. Add connected device descriptor to status bar (e.g. "u-blox ZED-F9P", "Unicore UM981S", "Septentrio mosaic-X5" or "Quectel LG290AG03"). **FYI** This is based on a poll of hardware information messages (*one for each enabled protocol*) sent approximately 3 seconds after the connection is started (*you may see 'unknown protocol' warnings in response to some of these messages; these can be ignored)*. Failing this, a generic descriptor is displayed on receipt of a message protocol unique to a particular manufacturer (*e.g. "u-blox" on receipt of a UBX message, "Unicore" on receipt of a UNI message, etc.*). Note that some (mainly older) devices may not return a meaningful descriptor, in which case "N/A" will be displayed. Note also that some receivers will not output hardware information or other status messages at low baud rates (< 38,400)
1223
1. Minor updates to serial port configuration panel (additional timeout values).
1324
1. Mininum pynmeagps version updated to v1.1.1.
1425

images/nmeaconfig_widget.png

20.9 KB
Loading

images/tty_dialog.png

308 KB
Loading

images/ubxconfig_widget.png

-42.2 KB
Loading

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ dependencies = [
5454
"Pillow>=9.0.0",
5555
"pygnssutils>=1.1.22",
5656
"pyunigps>=0.2.0",
57-
"pynmeagps>=1.1.1",
57+
"pynmeagps>=1.1.2",
5858
]
5959

6060
[project.scripts]

src/pygpsclient/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88
:license: BSD 3-Clause
99
"""
1010

11-
__version__ = "1.6.4"
11+
__version__ = "1.6.5"

src/pygpsclient/dialog_state.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def __init__(self):
9797
DLGTTTY: {
9898
CLASS: TTYPresetDialog,
9999
DLG: None,
100-
RESIZE: False,
100+
RESIZE: True,
101101
},
102102
DLGTRECORD: {
103103
CLASS: RecorderDialog,

src/pygpsclient/dynamic_config_frame.py

Lines changed: 51 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
END,
2424
EW,
2525
LEFT,
26+
NE,
2627
NSEW,
2728
NW,
2829
VERTICAL,
@@ -40,7 +41,6 @@
4041
W,
4142
)
4243

43-
from PIL import Image, ImageTk
4444
from pynmeagps import (
4545
NMEA_MSGIDS_PROP,
4646
NMEA_PAYLOADS_POLL_PROP,
@@ -63,12 +63,6 @@
6363

6464
from pygpsclient.globals import (
6565
ERRCOL,
66-
ICON_CONFIRMED,
67-
ICON_PENDING,
68-
ICON_REDRAW,
69-
ICON_SEND,
70-
ICON_UNKNOWN,
71-
ICON_WARNING,
7266
INFOCOL,
7367
NMEA_CFGOTHER,
7468
OKCOL,
@@ -98,6 +92,8 @@
9892
)
9993
# alternative POLL dictionary names for where POLL command
10094
# doesn't correspond to SET (fudge for Quectel)
95+
# e.g. a PAIR864 (set baud rate) command corresponds to PAIR865 (poll baud rate)
96+
# (for PAIR commands, the poll is typically one digit higher than the set)
10197
ALT_POLL_NAMES = {
10298
"AIR050": "AIR051",
10399
"AIR058": "AIR059",
@@ -111,8 +107,8 @@
111107
"AIR100": "AIR101",
112108
"AIR104": "AIR105",
113109
"AIR400": "AIR401",
114-
"AIR410": "AIR421",
115-
"AIR420": "AIR411",
110+
"AIR410": "AIR411",
111+
"AIR420": "AIR421",
116112
"AIR432": "AIR433",
117113
"AIR434": "AIR435",
118114
"AIR436": "AIR437",
@@ -173,12 +169,6 @@ def __init__(self, app: Frame, parent: Frame, *args, **kwargs):
173169

174170
super().__init__(parent.container, *args, **kwargs)
175171

176-
self._img_send = ImageTk.PhotoImage(Image.open(ICON_SEND))
177-
self._img_pending = ImageTk.PhotoImage(Image.open(ICON_PENDING))
178-
self._img_confirmed = ImageTk.PhotoImage(Image.open(ICON_CONFIRMED))
179-
self._img_warn = ImageTk.PhotoImage(Image.open(ICON_WARNING))
180-
self._img_unknown = ImageTk.PhotoImage(Image.open(ICON_UNKNOWN))
181-
self._img_refresh = ImageTk.PhotoImage(Image.open(ICON_REDRAW))
182172
self._cfg_id = "" # identity of selected CFG command
183173
self._cfg_atts = {} # this holds the attributes of the selected CFG command
184174
self._expected_response = None
@@ -210,22 +200,22 @@ def _body(self):
210200
self, orient=VERTICAL, command=self._lbx_cfg_cmd.yview
211201
)
212202
self._lbx_cfg_cmd.config(yscrollcommand=self._scr_cfg_cmd.set)
213-
self._lbl_send_command = Label(self, image=self._img_pending)
203+
self._lbl_send_command = Label(self, image=self.__container.img_none)
214204
self._btn_send_command = Button(
215205
self,
216-
image=self._img_send,
206+
image=self.__container.img_send,
217207
width=50,
218208
command=self._on_set_cfg,
219209
font=self.__app.font_md,
220210
)
221211
self._btn_refresh = Button(
222212
self,
223-
image=self._img_refresh,
224-
width=50,
213+
image=self.__container.img_redraw,
214+
width=40,
225215
command=self._on_refresh,
226216
font=self.__app.font_md,
227217
)
228-
self._lbl_command = Label(self, text="", width=30, anchor=W)
218+
self._lbl_command = Label(self, text="", anchor=W)
229219
self._frm_container = Frame(self)
230220
self._can_container = Canvas(self._frm_container)
231221
self._frm_attrs = Frame(self._can_container)
@@ -246,31 +236,29 @@ def _do_layout(self):
246236
Layout widgets.
247237
"""
248238

249-
self._lbl_cfg_dyn.grid(column=0, row=0, columnspan=4, padx=3, sticky=EW)
250-
self._lbx_cfg_cmd.grid(
251-
column=0, row=1, columnspan=2, rowspan=6, padx=3, pady=3, sticky=EW
252-
)
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)
239+
self._lbl_cfg_dyn.grid(column=0, row=0, columnspan=3, sticky=EW)
240+
self._lbx_cfg_cmd.grid(column=0, row=1, columnspan=1, sticky=EW)
241+
self._scr_cfg_cmd.grid(column=1, row=1, sticky=(N, S, W))
242+
self._btn_send_command.grid(column=2, row=1, ipadx=3, ipady=3, sticky=NE)
243+
self._lbl_send_command.grid(column=3, row=1, ipadx=3, ipady=3, sticky=NE)
244+
self._btn_refresh.grid(column=4, row=1, ipadx=3, ipady=3, sticky=NE)
245+
self._lbl_command.grid(column=0, row=2, columnspan=5, sticky=EW)
258246
self._frm_container.grid(
259-
column=0, row=8, columnspan=4, rowspan=15, padx=3, sticky=NSEW
247+
column=0,
248+
row=3,
249+
columnspan=5,
250+
rowspan=1,
251+
sticky=NSEW,
260252
)
261253
self._can_container.grid(
262-
column=0, row=0, columnspan=3, rowspan=15, padx=3, sticky=NSEW
254+
column=0,
255+
row=0,
256+
columnspan=5,
257+
rowspan=1,
258+
sticky=NSEW,
263259
)
264-
self._scr_container_ver.grid(column=3, row=0, rowspan=15, sticky=(N, S, E))
265-
self._scr_container_hor.grid(
266-
column=0, row=15, columnspan=4, rowspan=15, sticky=EW
267-
)
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)
260+
self._scr_container_ver.grid(column=5, row=0, sticky=(N, S, W))
261+
self._scr_container_hor.grid(column=0, row=2, columnspan=5, sticky=EW)
274262
self.option_add("*Font", self.__app.font_sm)
275263

276264
def _attach_events(self):
@@ -297,7 +285,7 @@ def reset(self):
297285
self._lbx_cfg_cmd.insert(i, cmd)
298286

299287
self._clear_widgets()
300-
self._lbl_send_command.config(image=self._img_unknown)
288+
self._lbl_send_command.config(image=self.__container.img_unknown)
301289

302290
def _setscroll(self, event): # pylint: disable=unused-argument
303291
"""
@@ -379,7 +367,7 @@ def _on_set_cfg(self, *args, **kwargs): # pylint: disable=unused-argument
379367

380368
# send message, update status and await response
381369
self.__container.send_command(msg)
382-
self._lbl_send_command.config(image=self._img_pending)
370+
self._lbl_send_command.config(image=self.__container.img_pending)
383371
self.__container.status_label = f"P{self._cfg_id} SET message sent"
384372
for msgid in pendcfg:
385373
self.__container.set_pending(msgid, penddlg)
@@ -406,7 +394,7 @@ def _do_poll_cfg(self, *args, **kwargs): # pylint: disable=unused-argument
406394
return
407395

408396
msg = penddlg = pendcfg = None
409-
# use alternate names for some NMEA PAIR/PQTM POLL commands
397+
# use alternate name for some NMEA PAIR/PQTM POLL commands
410398
cfg_id = ALT_POLL_NAMES.get(self._cfg_id, self._cfg_id)
411399
# set any POLL arguments to specified or default values
412400
# e.g. portid = "1", msgname="RMC"
@@ -428,13 +416,13 @@ def _do_poll_cfg(self, *args, **kwargs): # pylint: disable=unused-argument
428416
if msg is not None:
429417
self.__container.send_command(msg)
430418
self.__container.status_label = f"{cp}{cfg_id} POLL message sent"
431-
self._lbl_send_command.config(image=self._img_pending)
419+
self._lbl_send_command.config(image=self.__container.img_pending)
432420
for msgid in pendcfg:
433421
self.__container.set_pending(msgid, penddlg)
434422
self._expected_response = POLL
435423
else: # CFG cannot be POLLed
436424
self.__container.status_label = f"{cp}{cfg_id} No POLL available"
437-
self._lbl_send_command.config(image=self._img_unknown)
425+
self._lbl_send_command.config(image=self.__container.img_unknown)
438426

439427
def _do_poll_args(self, cfg_id: str) -> dict:
440428
"""
@@ -477,23 +465,26 @@ def _do_poll_args(self, cfg_id: str) -> dict:
477465
pass
478466
return args
479467

480-
def update_status(self, msg: object):
468+
def update_status(self, msg: NMEAMessage | UBXMessage):
481469
"""
482470
UBXHandler or NMEAHandler module has received expected command response
483471
and forwarded it to this module, entry widgets are pre-populated with
484472
current configuration values and confirmation status is updated.
485473
486-
:param object msg: UBXMessage or NMEAMessage response
474+
:param NMEAMessage | UBXMessage msg: UBXMessage or NMEAMessage response
487475
"""
488476

477+
# self.logger.debug(f"{msg.identity=}")
489478
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-
)
479+
if self._protocol == NMEA:
480+
# use alternate name for some NMEA PAIR/PQTM POLL commands
481+
# e.g. a PAIR864 (set baud rate) command corresponds to PAIR865 (poll baud rate)
482+
cfg_id = ALT_POLL_NAMES.get(self._cfg_id, self._cfg_id)
483+
# strip off any variant suffix from cfg_id
484+
# e.g. "QTMCFGUART_CURR" -> "PQTMCFGUART"
485+
cfg_id = "P" + cfg_id.rsplit("_", 1)[0]
486+
else:
487+
cfg_id = self._cfg_id
497488

498489
# if this message identity matches an expected response
499490
if msg.identity in (cfg_id, ACK, NAK):
@@ -513,10 +504,10 @@ def update_status(self, msg: object):
513504
f"{cfg_id} message acknowledged",
514505
OKCOL,
515506
)
516-
self._lbl_send_command.config(image=self._img_confirmed)
507+
self._lbl_send_command.config(image=self.__container.img_confirmed)
517508
else:
518509
self.__container.status_label = (f"{cfg_id} message rejected", ERRCOL)
519-
self._lbl_send_command.config(image=self._img_warn)
510+
self._lbl_send_command.config(image=self.__container.img_warn)
520511
self.update()
521512

522513
def _clear_widgets(self):
@@ -530,13 +521,13 @@ def _clear_widgets(self):
530521
wdg.destroy()
531522
wdg = None
532523
Label(self._frm_attrs, text="Attribute", width=12, anchor=W).grid(
533-
column=0, row=0, padx=3, sticky=(W)
524+
column=0, row=0, padx=3, sticky=W
534525
)
535526
Label(self._frm_attrs, text="Value", width=20, anchor=W).grid(
536-
column=1, row=0, padx=3, sticky=(W)
527+
column=1, row=0, padx=3, sticky=W
537528
)
538529
Label(self._frm_attrs, text="Type", width=5, anchor=W).grid(
539-
column=2, row=0, padx=3, sticky=(W)
530+
column=2, row=0, padx=3, sticky=W
540531
)
541532

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

0 commit comments

Comments
 (0)