Skip to content

Commit 3c62195

Browse files
authored
Merge pull request #255 from semuconsulting/RC-1.7.0
Rc 1.7.0
2 parents 2f27f6a + 6cdbd08 commit 3c62195

33 files changed

Lines changed: 279 additions & 76 deletions

README.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ PyGPSClient is a free, open-source, multi-platform graphical GNSS/GPS testing, d
2626
* Provides [NTRIP client](#ntripconfig) facilities for both RTCM3 and SPARTN NTRIP services.
2727
* Can serve as an [NTRIP base station](#basestation) with an RTK-compatible receiver (e.g. u-blox ZED-F9P/ZED-X20P, Quectel LG/LC Series, Septentrio Mosaic Series or Unicore UM9** Series).
2828
* Supports GNSS (*and related*) device configuration via proprietary UBX, NMEA and ASCII TTY protocols, including most u-blox, Quectel, Septentrio, Unicore and Feyman GNSS devices.
29-
* **New in v1.6.7** - Experimental support for [RINEX conversion](#rinex) of raw observation, navigation and meteorology data.
29+
* **New in version >= 1.6.7** - Experimental support for [RINEX conversion](#rinex) of raw observation, navigation and meteorology data.
3030
* Can be installed using the standard `pip` Python package manager - see [installation instructions](#installation) below.
3131

3232
This is an independent project and we have no affiliation whatsoever with any GNSS manufacturer or distributor.
@@ -183,7 +183,7 @@ For more comprehensive installation instructions, please refer to [INSTALLATION.
183183
---
184184
| Widget | To show or hide the various widgets, go to Menu..View and click on the relevant hide/show option. |
185185
|---------------------------|---------------------------------------------------------------------------------------------------|
186-
|![banner widget](https://github.com/semuconsulting/PyGPSClient/blob/master/images/banner_widget.png?raw=true)| Expandable banner showing key navigation status information based on messages received from receiver. To expand or collapse the banner or serial port configuration widgets, click the ![expand icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-arrow-80-16.png?raw=true)/![expand icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-triangle-1-16.png?raw=true) buttons. Ordinarily 'track:' represents heading of motion (aka course over ground), but the field will display 'hdg:' where static heading (yaw) is available. **NB**: some fields (e.g. hdop/vdop, hacc/vacc) are only available from proprietary NMEA or UBX messages and may not be output by default. The minimum messages required to populate all available fields are: NMEA: GGA, GSA, GSV, RMC, UBX00 (proprietary); UBX: NAV-DOP, NAV-PVT, NAV-SAT; UNI: BESTNAV, SATSINFO, STADOP. |
186+
|![banner widget](https://github.com/semuconsulting/PyGPSClient/blob/master/images/banner_widget.png?raw=true)| Expandable banner showing key navigation status information based on messages received from receiver. To expand or collapse the banner or serial port configuration widgets, click the ![expand icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-arrow-80-16.png?raw=true)/![expand icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-triangle-1-16.png?raw=true) buttons. Double-click the "hae" label or value to toggle between hae (height above ellipsoid) and sep (separation) values. **NB**: some fields (e.g. hdop/vdop, hacc/vacc) are only available from proprietary NMEA or UBX messages and may not be output by default. The minimum messages required to populate all available fields are: NMEA: GGA, GSA, GSV, RMC, UBX00 (proprietary); UBX: NAV-DOP, NAV-PVT, NAV-SAT; UNI: BESTNAV, SATSINFO, STADOP. |
187187
|![console widget](https://github.com/semuconsulting/PyGPSClient/blob/master/images/console_widget.png?raw=true)| Configurable serial console widget showing incoming GNSS data streams in either parsed, binary or tabular hexadecimal formats. Double-right-click to copy contents of console to the clipboard. The scroll behaviour and number of lines retained in the console can be configured via the settings panel. Supports user-configurable color tagging of selected strings for easy identification. Color tags are loaded from the `"colortag_b":` value (`0` = disable, `1` = enable) and `"colortags_l":` list (`[string, color]` pairs) in your json configuration file (see example provided). If color is set to "HALT", streaming will halt on any match and a warning displayed. NB: color tagging does impose a small performance overhead - turning it off will improve console response times at very high transaction rates.|
188188
|![skyview widget](https://github.com/semuconsulting/PyGPSClient/blob/master/images/skyview_widget.png?raw=true)| Skyview widget showing current satellite visibility and position (elevation / azimuth). Satellite icon borders are colour-coded to distinguish between different GNSS constellations. For consistency between NMEA and UBX data sources, will display GLONASS NMEA SVID (65-96) rather than slot (1-24). |
189189
|![levelsview widget](https://github.com/semuconsulting/PyGPSClient/blob/master/images/graphview_widget.png?raw=true)| Levels view widget showing current satellite carrier-to-noise (C/No) levels for each GNSS constellation. Double-click to toggle legend. Double-right-click to toggle levels where C/No = 0 dbHz. |
@@ -322,7 +322,7 @@ The NTRIP Configuration utility allows users to receive and process NTRIP RTK Co
322322
1. For NTRIP services which require client position data via NMEA GGA sentences, select the appropriate sentence transmission interval in seconds. The default is 'None' (no GGA sentences sent). A value of 10 or 60 seconds is typical.
323323
1. If GGA sentence transmission is enabled, GGA sentences can either be populated from live navigation data (*assuming a receiver is connected and outputting valid position data*) or from fixed reference settings entered in the NTRIP configuration panel (latitude, longitude, elevation and geoid separation - all four reference settings must be provided).
324324
1. To connect to the NTRIP server, click ![connect icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-media-control-48-24.png?raw=true). To disconnect, click ![disconnect icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-media-control-50-24.png?raw=true).
325-
1. If NTRIP data is being successfully received, the banner '**corr:**' status indicator should change to 'YES' and indicate the age and reference station of the correction data (where available) ![dgps status](https://github.com/semuconsulting/PyGPSClient/blob/master/images/dgps_status.png?raw=true). Note that DGPS status is typically maintained for up to 60 seconds after loss of correction signal.
325+
1. If NTRIP data is being successfully received, the banner '**corr:**' status indicator should change to '' and indicate the age and reference station of the correction data (where available) ![dgps status](https://github.com/semuconsulting/PyGPSClient/blob/master/images/dgps_status.png?raw=true). Note that CORR status is typically maintained for up to 60 seconds after loss of correction signal.
326326
1. Some NTRIP services may output RTCM3 or SPARTN correction messages at a high rate, flooding the GUI console display. To suppress these messages in the console, de-select the 'RTCM' or'SPARTN' options in 'Protocols Shown' - the RTCM3 or SPARTN messages will continue to be processed in the background.
327327

328328
Below is a illustrative NTRIP DGPS data log, showing:
@@ -428,9 +428,7 @@ The RINEX Conversion Dialog supports the conversion of raw observation, navigati
428428

429429
**Pre-Requisites:**
430430

431-
1. A previously-saved binary datalog containing raw observation (UBX RXM-RAWX), navigation (UBX RXM-SFRBX¹) and/or meteorology (NMEA) data or RTCM3 ephemerides (1019, 1020, 1041-1046) messages. A suitable datalog can be recorded using PyGPSClient's [binary datalogging](#datalog) facility. **NB**: The file should contain at least 15-30 minutes of continuous data.
432-
433-
¹ Currently only GPS LNAV/CNAV, Galileo FNAV/INAV and Beidou D1/D2 data is supported by the [pygnssutils RINEX NAV conversion utility](https://github.com/semuconsulting/pygnssutils#rinexconvert). This will be enhanced in future releases.
431+
1. A previously-saved binary datalog containing raw observation (UBX RXM-RAWX), navigation (UBX RXM-SFRBX) and/or meteorology (NMEA) data or RTCM3 ephemerides (1019, 1020, 1041-1046) messages. A suitable datalog can be recorded using PyGPSClient's [binary datalogging](#datalog) facility. **NB**: The file should contain at least 15-30 minutes of continuous data.
434432

435433
**Instructions:**
436434

RELEASE_NOTES.md

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

3+
### RELEASE 1.7.0
4+
5+
1. Can now double-click to toggle between 'hae' (height above ellipsoid) and 'sep' (separation) values in banner panel.
6+
1. Minimum pyubx2 version updated to 1.3.3 (adds new firmware configuration database items for u-blox X20 HPG 2.10).
7+
1. Default log level amended to '0' (ERROR) rather than '-1' (CRITICAL). Any logged exception tracebacks will now appear in terminal logs by default.
8+
1. Updates to RINEX conversion dialog for pygnssutil>=1.2.5 (incorporates various bug fixes to RINEX conversion routines, but remains an experimental facility).
9+
310
### RELEASE 1.6.10
411

512
1. Minor enhancements to UBX Config Dialog confirmation signalling.

images/dgps_status.png

-5.45 KB
Loading

pyproject.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ classifiers = [
5050
]
5151

5252
dependencies = [
53-
"requests>=2.28.0",
54-
"Pillow>=9.0.0",
55-
"pygnssutils>=1.2.2",
53+
"requests>=2.34.0",
54+
"Pillow>=12.0.0",
55+
"pygnssutils>=1.2.5",
5656
"pyunigps>=1.0.0",
57-
"pynmeagps>=1.1.4",
58-
"pyubx2>=1.3.1",
57+
"pynmeagps>=1.1.5",
58+
"pyubx2>=1.3.3",
5959
]
6060

6161
[project.scripts]

src/pygpsclient/__main__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def main():
129129
VERBOSITY_DEBUG,
130130
VERBOSITY_CRITICAL,
131131
],
132-
default=VERBOSITY_CRITICAL,
132+
default=VERBOSITY_LOW,
133133
)
134134
ap.add_argument(
135135
"--logtofile",
@@ -139,7 +139,7 @@ def main():
139139
kwargs = vars(ap.parse_args())
140140

141141
# set up global logging configuration
142-
verbosity = int(kwargs.pop("verbosity", VERBOSITY_CRITICAL))
142+
verbosity = int(kwargs.pop("verbosity", VERBOSITY_LOW))
143143
logtofile = kwargs.pop("logtofile", "")
144144
logger = getLogger(APPNAME) # "pygpsclient"
145145
logger_utils = getLogger("pygnssutils")

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.10"
11+
__version__ = "1.7.0"

src/pygpsclient/about_dialog.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from PIL import Image, ImageTk
1919

2020
from pygpsclient.globals import (
21+
CLICK_CURSOR,
2122
ERRCOL,
2223
ICON_APP128,
2324
ICON_GITHUB,
@@ -112,7 +113,7 @@ def _body(self):
112113
self._frm_body,
113114
text="",
114115
width=16,
115-
cursor="hand2",
116+
cursor=CLICK_CURSOR,
116117
)
117118
self._chk_checkupdate = Checkbutton(
118119
self._frm_body,
@@ -122,18 +123,18 @@ def _body(self):
122123
self._lbl_sponsoricon = Label(
123124
self._frm_body,
124125
image=self._img_sponsor,
125-
cursor="hand2",
126+
cursor=CLICK_CURSOR,
126127
)
127128
self._lbl_github = Label(
128129
self._frm_body,
129130
text=GITHUB_URL,
130131
fg=INFOCOL,
131-
cursor="hand2",
132+
cursor=CLICK_CURSOR,
132133
)
133134
self._lbl_copyright = Label(
134135
self._frm_body,
135136
text=COPYRIGHT,
136-
cursor="hand2",
137+
cursor=CLICK_CURSOR,
137138
)
138139

139140
def _do_layout(self):

src/pygpsclient/app.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@
9999
SOCKSERVER_MAX_CLIENTS,
100100
SPARTN_EVENT,
101101
SPARTN_PROTOCOL,
102-
STATUSPRIORITY,
102+
STATUS_PRIORITY,
103+
STATUS_TIMEOUT,
103104
TTY_PROTOCOL,
104105
UNDO,
105106
)
@@ -225,7 +226,9 @@ def __init__(self, master, **kwargs):
225226
self.frm_settings = None
226227
self._conn_status = DISCONNECTED
227228
self._rtk_conn_status = DISCONNECTED
228-
self._last_gui_update = datetime.now()
229+
now = datetime.now()
230+
self._last_gui_update = now
231+
self._last_status_update = now
229232
self._socket_thread = None
230233
self._socket_server = None
231234
self.consoledata = []
@@ -969,7 +972,8 @@ def process_data(self, raw_data: bytes, parsed_data: object, marker: str = ""):
969972
self.consoledata.append((raw_data, parsed_data, marker))
970973

971974
# periodically update widgets if visible
972-
if datetime.now() > self._last_gui_update + timedelta(
975+
now = datetime.now()
976+
if now > self._last_gui_update + timedelta(
973977
seconds=self.configuration.get("guiupdateinterval_f")
974978
):
975979
self._refresh_widgets()
@@ -978,6 +982,10 @@ def process_data(self, raw_data: bytes, parsed_data: object, marker: str = ""):
978982
self.sqlite_handler.load_data()
979983
self._last_gui_update = datetime.now()
980984

985+
# remove stale status messages
986+
if now > self._last_status_update + timedelta(seconds=STATUS_TIMEOUT):
987+
self.status_label = ("", INFOCOL)
988+
981989
# update GPX track file if enabled
982990
if self.configuration.get("recordtrack_b"):
983991
self.file_handler.update_gpx_track()
@@ -1165,7 +1173,7 @@ def status_label(self, message: str | tuple[str, str]):
11651173
"""
11661174

11671175
def priority(col):
1168-
return STATUSPRIORITY.get(col, 0)
1176+
return STATUS_PRIORITY.get(col, 0)
11691177

11701178
if isinstance(message, tuple):
11711179
message, color = message
@@ -1180,6 +1188,7 @@ def priority(col):
11801188
color = INFOCOL if color == "blue" else color
11811189
self.status_label.config(text=message, fg=color)
11821190
self.status_label.update()
1191+
self._last_status_update = datetime.now()
11831192
else: # defer message until frm_status is instantiated
11841193
if isinstance(self._deferredmsg, tuple):
11851194
defpty = priority(self._deferredmsg[1])

src/pygpsclient/banner_frame.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
from pygpsclient.globals import (
2121
BGCOL,
22+
CLICK_CURSOR,
2223
CONNECTED,
2324
CONNECTED_FILE,
2425
CONNECTED_NTRIP,
@@ -94,6 +95,7 @@ def __init__(self, app: Frame, parent: Frame, *args, **kwargs):
9495
self._img_ntrip = ImageTk.PhotoImage(Image.open(ICON_NTRIPCONFIG))
9596
self._img_spartn = ImageTk.PhotoImage(Image.open(ICON_SPARTNCONFIG))
9697
self._img_blank = ImageTk.PhotoImage(Image.open(ICON_BLANK))
98+
self._sep = False
9799

98100
super().__init__(parent, *args, **kwargs)
99101

@@ -143,12 +145,18 @@ def _body(self):
143145
height=22,
144146
command=self._toggle_advanced,
145147
image=self._img_expand,
148+
cursor=CLICK_CURSOR,
146149
)
147150
self._lbl_lhmsl = Label(
148151
self._frm_advanced, text="hmsl:", bg=BGCOL, fg=FGCOL, anchor=N
149152
)
150153
self._lbl_lhae = Label(
151-
self._frm_advanced, text="hae:", bg=BGCOL, fg=FGCOL, anchor=N
154+
self._frm_advanced,
155+
text="hae:",
156+
bg=BGCOL,
157+
fg=FGCOL,
158+
anchor=N,
159+
cursor=CLICK_CURSOR,
152160
)
153161
self._lbl_lspd = Label(
154162
self._frm_advanced, text="speed:", bg=BGCOL, fg=FGCOL, anchor=N
@@ -195,7 +203,12 @@ def _body(self):
195203
self._frm_advanced, bg=BGCOL, fg="orange", width=13, anchor=W
196204
)
197205
self._lbl_hae = Label(
198-
self._frm_advanced, bg=BGCOL, fg="orange", width=13, anchor=W
206+
self._frm_advanced,
207+
bg=BGCOL,
208+
fg="orange",
209+
width=13,
210+
anchor=W,
211+
cursor=CLICK_CURSOR,
199212
)
200213
self._lbl_spd = Label(
201214
self._frm_advanced, bg=BGCOL, fg="deepskyblue", width=12, anchor=W
@@ -283,6 +296,8 @@ def _attach_events(self):
283296
"""
284297

285298
self.bind("<Configure>", self._on_resize)
299+
self._lbl_lhae.bind("<Double-Button-1>", self._on_sep)
300+
self._lbl_hae.bind("<Double-Button-1>", self._on_sep)
286301

287302
def _toggle_advanced(self):
288303
"""
@@ -396,7 +411,8 @@ def _update_pos(self, pos_format, units):
396411
self._lbl_llat.config(text="lat:")
397412
self._lbl_llon.config(text="lon:")
398413
self._lbl_lhmsl.config(text="hmsl:")
399-
self._lbl_lhae.config(text="hae:")
414+
lhae = "sep:" if self._sep else "hae:"
415+
self._lbl_lhae.config(text=lhae)
400416
alt_u = "ft" if units in (UI, UIK) else "m"
401417

402418
try:
@@ -423,7 +439,8 @@ def _update_pos(self, pos_format, units):
423439
self._lbl_lat.config(text=f"{lat:{deg_f}}")
424440
self._lbl_lon.config(text=f"{lon:{deg_f}}")
425441
self._lbl_hmsl.config(text=f"{alt:.4f} {alt_u}")
426-
self._lbl_hae.config(text=f"{hae:.4f} {alt_u}")
442+
haev = hae - alt if self._sep else hae
443+
self._lbl_hae.config(text=f"{haev:.4f} {alt_u}")
427444
except (TypeError, ValueError):
428445
self._lbl_lat.config(text=NA)
429446
self._lbl_lon.config(text=NA)
@@ -623,9 +640,18 @@ def _set_fontsize(self):
623640
):
624641
ctl.config(font=scale_font(self.width, FONTBASE - 4, FONTSCALE)[0])
625642

643+
def _on_sep(self, event): # pylint: disable=unused-argument
644+
"""
645+
Toggle hae/sep display.
646+
647+
:param event event: mouse click event
648+
"""
649+
650+
self._sep = not self._sep
651+
626652
def _on_resize(self, event): # pylint: disable=unused-argument
627653
"""
628-
Resize frame
654+
Resize frame.
629655
630656
:param event event: resize event
631657
"""

src/pygpsclient/dynamic_config_frame.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
)
6363

6464
from pygpsclient.globals import (
65+
CLICK_CURSOR,
6566
ERRCOL,
6667
INFOCOL,
6768
NMEA_CFGOTHER,
@@ -207,13 +208,15 @@ def _body(self):
207208
width=50,
208209
command=self._on_set_cfg,
209210
font=self.__app.font_md,
211+
cursor=CLICK_CURSOR,
210212
)
211213
self._btn_refresh = Button(
212214
self,
213215
image=self.__container.img_redraw,
214216
width=40,
215217
command=self._on_refresh,
216218
font=self.__app.font_md,
219+
cursor=CLICK_CURSOR,
217220
)
218221
self._lbl_command = Label(self, text="", anchor=W)
219222
self._frm_container = Frame(self)

0 commit comments

Comments
 (0)