diff --git a/INSTALLATION.md b/INSTALLATION.md index 180c37f4..bfbb6c36 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -63,7 +63,7 @@ In the following, `python3` & `pip` refer to the Python 3 executables. You may n - Graphical User Interface (window system / manager) - Python >= 3.10⁴ -- Tk (tkinter) >= 8.6⁵ (*tkinter is a commonly used library for developing Graphical User Interfaces (GUI) in Python*) +- Tk (tkinter) >= 8.6⁵⁶ (*tkinter is a commonly used library for developing Graphical User Interfaces (GUI) in Python*) - Screen resolution >= 640 x 480 (VGA); Ideally 1920 x 1080, though at lower screen resolutions (<= 1024 width), top level dialogs will be resizable and scrollable. **NB** It is highly recommended to use the latest official [Python.org](https://www.python.org/downloads/) installation package for your platform, rather than any pre-installed version. @@ -81,18 +81,18 @@ Normally installs without any additional steps. ### MacOS 13 or later -⁵ The version of Python supplied with some older Apple MacOS platforms includes a [deprecated version of tkinter](https://www.python.org/download/mac/tcltk/) (8.5). Use an official [Python.org](https://www.python.org/downloads/macos) installation package instead. +**NB:** PyGPSClient does ***NOT*** normally require Homebrew or MacPorts to be installed on MacOS (either Intel or Apple Silicon). The Python organisation provides serviceable [64-bit universal MacOS installation packages](https://www.python.org/downloads/macos/) for all current and legacy versions of Python, including release candidates. Note, however, the following caveats: -**NB:** Python does ***NOT*** normally require Homebrew or MacPorts to be installed on MacOS. The Python organisation provides serviceable [64-bit universal installation packages](https://www.python.org/downloads/macos/) for all current and legacy versions of Python, including release candidates. +⁵ The version of Python supplied with some older Apple MacOS platforms includes a [deprecated version of tkinter](https://www.python.org/download/mac/tcltk/) (8.5). Use an official [Python for MacOS](https://www.python.org/downloads/macos) installation package instead. -However, if you wish to install Python using [Homebrew](https://brew.sh/) to take advantage of certain non-default configurations (*e.g. support for sqlite3 extensions*), use the `python-tk` formula rather than `python`, e.g. +⁶ The version of tkinter supplied with the very latest official versions of [Python for MacOS](https://www.python.org/downloads/macos) (`>= 3.14.5`), and with Homebrew formula `python-tk >= 3.12`, include a new version of tkinter (9.0). There appear to be some performance and compatibility issues with this version on MacOS Sonoma and Tahoe which may render the PyGPSClient UI somewhat sluggish or unresponsive. Until this is resolved, MacOS users are recommended to use official Python <= 3.14.4 or Homebrew formula `python-tk = 3.11`. + +If you wish to install Python using [Homebrew](https://brew.sh/) to take advantage of certain non-default configurations (*e.g. support for sqlite3 extensions*), use the `python-tk` formula rather than `python`, e.g. ```shell -brew install python-tk@3.13 libspatialite +brew install python-tk@3.11 libspatialite ``` -Note also that the Homebrew formulae for python-tk>=3.12 include the latest tkinter 9.0 (rather than 8.6). There are known compatibility issues between tkinter 9.0 and other Python packages (*e.g. ImageTk*) on some platform configurations, which may result in PyGPSClient being unable to load. If you encounter these issues, consider using `brew install python-tk@3.11` or an official [Python.org](https://www.python.org/downloads/macos) installation package instead. - Note that on MacOS, serial ports may appear as `/dev/tty*` *or* `/dev/cu*`. To understand the differences between the two, see [here](https://www.codegenes.net/blog/what-s-the-difference-between-dev-tty-and-dev-cu-on-macos/). ### Linux (including Raspberry Pi OS) @@ -128,7 +128,7 @@ Add your user to this group using usermod (*you will need to log out and in agai sudo usermod -aG dialout myuser ``` -For Debian-based platforms, the group is normally `dialout` or `tty`; on Arch-based platforms it is normally `uucp`. +For Debian-based platforms, the group is normally `dialout`; on Arch-based platforms it is normally `uucp`. Other than this, no special privileges are required. diff --git a/README.md b/README.md index 12b57370..412a51a2 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,8 @@ For more comprehensive installation instructions, please refer to [INSTALLATION. 1. By default, the Settings panel is displayed to the right of the main application window. It can be hidden or shown via Menu..View..Hide/Show Settings. The panel can also be 'undocked' from the main application window via Menu..View..Undock Settings and - if [non-transient](#transient) (`transient_dialog_b: 0`) - minimized independently of the main window. Exiting the undocked dialog, or selecting Menu..View..Dock Settings, will 'dock' the panel. 2. Protocols Shown - Select which message protocols to display in the console; NMEA, UBX (*u-blox binary*), SBF (*Septentrio binary*), UNI (*Unicore binary*), QGC (*Quectel binary*), RTCM3, SPARTN or TTY (*terminal*). NB: this only changes the *displayed* protocols - to change the actual protocols output by the receiver, use the relevant configuration command(s). - - **NB:** Serial connection must be stopped before changing to or from TTY (terminal) protocol. Enabling TTY (terminal) mode will disable all other protocols. + - **NB:** Serial connection must be stopped before changing to or from TTY (terminal) protocol or "Meta Only" mode. Enabling TTY (terminal) mode will disable all other protocols. + - If "Meta Only" is enabled, the underlying `GNSSReader` parser will *only* parse basic metadata from each incoming raw message (protocol, identity, length). This mode is vastly quicker (≈ 100x) than full parsing, but individual data attributes *will no longer be available* and PyGPSClient's widgets other than console will *not* be updated. 3. To connect to a GNSS receiver via USB or UART port, select the device from the listbox, set the appropriate serial connection parameters and click ![connect icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/usbport-1-24.png?raw=true). The application will endeavour to pre-select a recognised GNSS/GPS device but this is platform and device dependent. Press the ![refresh](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-refresh-6-16.png?raw=true) button to refresh the list of connected devices at any point. - `Rate bps` (baud rate) is typically the only setting that might need adjusting, but tweaking the `timeout` setting may improve performance on certain platforms. @@ -145,6 +146,12 @@ For more comprehensive installation instructions, please refer to [INSTALLATION. #### Datalogging, GPX Track Recording and Database 17. DataLogging - Turn Data logging in the selected format (Binary, Parsed, Hex Tabular, Hex String, Parsed+Hex Tabular) on or off. On first selection, you will be prompted to select the directory into which timestamped log files are saved. Log files are cycled when a maximum size is reached (default is 10 MB, manually configurable via `logsize_n` setting). + + **NB**: For extended datalogging (> 2 hours or more) it is **strongly** recommended to use an unattended (e.g. CLI) tool like [GNSSStreamer](https://github.com/semuconsulting/pygnssutils#gnssstreamer) rather than an attended GUI tool like PyGPSClient (*especially if you're running 'headless'*), e.g.: + ```shell + gnssstreamer --port /dev/ttyACM0 --baudrate 115200 --timeout 3 --format 2 --clioutput 1 --output pygpsdata.log --verbosity 2 + ``` + 18. GPX Track - Turn track recording (in GPX format) on or off. On first selection, you will be prompted to select the directory into which timestamped GPX track files are saved. See also [GPX Track Viewer](#gpxviewer). 19. Database - Turn spatialite database recording (*where available*) on or off. On first selection, you will be prompted to select the directory into which the `pygpsclient.sqlite` database is saved. *Note that, when first created, the database's spatial metadata may take up to a minute or so to initialise*. - Database logging is dependent on your Python environment supporting the requisite [sqlite3 `mod_spatialite` extension](https://www.gaia-gis.it/fossil/libspatialite/index) - see [INSTALLATION.md](https://github.com/semuconsulting/PyGPSClient/blob/master/INSTALLATION.md#prereqs) for further details. If not supported, the option will be greyed out. Check the Menu..Help..About dialog for an indication of the current spatialite support status - `no-ext` means the spatialite extension is not supported; `no-ms` means spatialite *is* supported but the necessary `mod_spatialite` extension module cannot be found in the PATH; a numeric version number like `3.51.2` indicates spatialite is fully supported. @@ -153,31 +160,33 @@ For more comprehensive installation instructions, please refer to [INSTALLATION. #### Pop-up Configuration Dialogs -20. [UBX Configuration Dialog](#ubxconfig), with the ability to send a variety of UBX CFG configuration commands to any u-blox GNSS device. This includes the facility to add **user-defined commands or command sequences** - see instructions under [user-defined presets](#userdefined) below. To display the UBX Configuration Dialog (*only functional when connected to a UBX GNSS device via serial port*), click +20. [UBX Configuration Dialog](#ubxconfig), with the ability to send a variety of UBX CFG configuration commands to any modern (Generation 9 or later e.g. ZED-F9P, ZED-X20P) u-blox GNSS device. This includes the facility to add **user-defined commands or command sequences** - see instructions under [user-defined presets](#userdefined) below. To display the UBX Configuration Dialog (*only functional when connected to a modern UBX GNSS device via serial port*), click ![gear icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-gear-2-24-ubx.png?raw=true), or go to Menu..Options..UBX Configuration Dialog. -21. [NMEA Configuration Dialog](#nmeaconfig), with the ability to send a variety of NMEA configuration commands to compatible GNSS devices (e.g. Quectel LG or LC Series). This includes the facility to add **user-defined commands or command sequences** - see instructions under [user-defined presets](#userdefined) below. To display the NMEA Configuration Dialog (*only functional when connected to a compatible GNSS device via serial port*), click ![gear icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-gear-2-24-nmea.png?raw=true), or go to Menu..Options..NMEA Configuration Dialog. -22. [TTY Config Dialog](#ttycommands), with the ability to send a variety of TTY (ASCII) configuration commands to compatible GNSS and related devices (e.g. Septentrio Mosaic Series, Unicore UM9** Series). This includes the facility to add **user-defined commands or command sequences** - see instructions under [user-defined presets](#userdefined) below. To display the TTY Configuration Dialog (*only functional when connected to a compatible GNSS device via serial port*), click +21. [UBX Legacy Configuration Dialog](#ubxconfig), with the ability to send a variety of UBX CFG configuration commands to any legacy (Generation 8 or earlier e.g. NEO-M7P, NEO-M8P) u-blox GNSS device. To display the UBX Legacy Configuration Dialog (*only functional when connected to a legacy UBX GNSS device via serial port*), click +![gear icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-gear-2-24-ubxlegacy.png?raw=true), or go to Menu..Options..UBX Configuration Dialog. +22. [NMEA Configuration Dialog](#nmeaconfig), with the ability to send a variety of NMEA configuration commands to compatible GNSS devices (e.g. Quectel LG or LC Series). This includes the facility to add **user-defined commands or command sequences** - see instructions under [user-defined presets](#userdefined) below. To display the NMEA Configuration Dialog (*only functional when connected to a compatible GNSS device via serial port*), click ![gear icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-gear-2-24-nmea.png?raw=true), or go to Menu..Options..NMEA Configuration Dialog. +23. [TTY Config Dialog](#ttycommands), with the ability to send a variety of TTY (ASCII) configuration commands to compatible GNSS and related devices (e.g. Septentrio Mosaic Series, Unicore UM9** Series). This includes the facility to add **user-defined commands or command sequences** - see instructions under [user-defined presets](#userdefined) below. To display the TTY Configuration Dialog (*only functional when connected to a compatible GNSS device via serial port*), click ![gear icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-gear-2-24-tty.png?raw=true), or go to Menu..Options..TTY Configuration. -23. [NTRIP Client](#ntripconfig) facility with the ability to connect to a specified NTRIP caster, parse the incoming RTCM3 or SPARTN data and feed this data to a compatible GNSS receiver (*requires an Internet connection and access to an NTRIP caster and local mountpoint*). To display the NTRIP Client Configuration Dialog, click +24. [NTRIP Client](#ntripconfig) facility with the ability to connect to a specified NTRIP caster, parse the incoming RTCM3 or SPARTN data and feed this data to a compatible GNSS receiver (*requires an Internet connection and access to an NTRIP caster and local mountpoint*). To display the NTRIP Client Configuration Dialog, click ![ntrip icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-antenna-4-24.png?raw=true), or go to Menu..Options..NTRIP Configuration Dialog. -24. [Server Config](#socketserver) facility with the ability to act as an NTRIP caster (mountpoint = `pygnssutils`) or generic socket server. To display the Server Configuration Dialog, click +25. [Server Config](#socketserver) facility with the ability to act as an NTRIP caster (mountpoint = `pygnssutils`) or generic socket server. To display the Server Configuration Dialog, click ![server icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-transmit-10-24.png?raw=true), or go to Menu..Options..Server Configuration Dialog. -25. [GPX Track Viewer](#gpxviewer) facility with the ability to map GPX files containing track, route or waypoint data and show elevation and speed profiles and other metadata. To display the GPX Track viewer, go to Menu..Options..GPX Track Viewer. -26. [RINEX Conversion](#rinex) facility which supports conversion of previously-saved binary datalogs to RINEX observation and navigation format. To display the RINEX Conversion dialog, go to Menu..Options..RINEX Conversion. -27. [Import Custom Map](#custommap) facility which allows the user to import geo-referenced images for use as background maps. To display the Import Custom Map dialog, go to Menu..Options..Import Custom Map. -28. [Configuration Command Recorder](#recorder) facility which allows the user to record, save, load, import (*as a preset*) and replay UBX, NMEA or TTY configuration commands sent to a receiver. To display the Command Record Facility dialog, go to Menu..Options..Configuration Command Recorder. -29. [SPARTN Client](#spartnconfig) facility with the ability to configure an IP or L-Band SPARTN Correction source and SPARTN-compatible GNSS receiver (e.g. ZED-F9P) and pass the incoming correction data to the GNSS receiver (*requires an Internet connection and access to a SPARTN location service*). To display the SPARTN Client Configuration Dialog, go to Menu..Options..SPARTN Configuration Dialog. +26. [GPX Track Viewer](#gpxviewer) facility with the ability to map GPX files containing track, route or waypoint data and show elevation and speed profiles and other metadata. To display the GPX Track viewer, go to Menu..Options..GPX Track Viewer. +27. [RINEX Conversion](#rinex) facility which supports conversion of previously-saved binary datalogs to RINEX observation and navigation format. To display the RINEX Conversion dialog, go to Menu..Options..RINEX Conversion. +28. [Import Custom Map](#custommap) facility which allows the user to import geo-referenced images for use as background maps. To display the Import Custom Map dialog, go to Menu..Options..Import Custom Map. +29. [Configuration Command Recorder](#recorder) facility which allows the user to record, save, load, import (*as a preset*) and replay UBX, NMEA or TTY configuration commands sent to a receiver. To display the Command Record Facility dialog, go to Menu..Options..Configuration Command Recorder. +30. [SPARTN Client](#spartnconfig) facility with the ability to configure an IP or L-Band SPARTN Correction source and SPARTN-compatible GNSS receiver (e.g. ZED-F9P) and pass the incoming correction data to the GNSS receiver (*requires an Internet connection and access to a SPARTN location service*). To display the SPARTN Client Configuration Dialog, go to Menu..Options..SPARTN Configuration Dialog. #### GUI refresh rate setting -30. PyGPSClient processes all incoming GNSS data in 'real time' but, by default, the GUI is only refreshed every 0.5 seconds. The refresh rate can be manually configured via the `guiupdateinterval_f` setting in the json configuration file. **NB:** PyGPSClient may become unresponsive on slower platforms (e.g. Raspberry Pi) at high message rates if the GUI update interval is less than 0.1 seconds, though lower intervals (<= 0.1 secs) can be accommodated on more powerful platforms. +31. PyGPSClient processes all incoming GNSS data in 'real time' but, by default, the GUI is only refreshed every 0.5 seconds. The refresh rate can be manually configured via the `guiupdateinterval_f` setting in the json configuration file. **NB:** PyGPSClient may become unresponsive on slower platforms (e.g. Raspberry Pi) at high message rates if the GUI update interval is less than 0.1 seconds, though lower intervals (<= 0.1 secs) can be accommodated on more powerful platforms. #### Toplevel ('pop-up') dialog setting -31. The behaviour of Toplevel ('pop-up') dialogs will depend on the screen resolution and 'transient' setting. If the width or height of a Toplevel dialog exceeds the screen resolution, the dialog will be displayed in a scrollable, resizeable window. Otherwise, the dialog is displayed as a fixed, non-resizeable panel. +32. The behaviour of Toplevel ('pop-up') dialogs will depend on the screen resolution and 'transient' setting. If the width or height of a Toplevel dialog exceeds the screen resolution, the dialog will be displayed in a scrollable, resizeable window. Otherwise, the dialog is displayed as a fixed, non-resizeable panel. - A boolean configuration setting `transient_dialog_b` governs whether Toplevel dialogs are 'transient' (i.e. always on top of main application dialog) or not. Changing this setting to `0` allows Toplevel dialogs to be minimised independently of the main application window, but be mindful that some dialogs may end up hidden behind others e.g. "Open file/folder" dialogs. **If a file open button appears unresponsive, check that the "Open file/folder" panel isn't already open but obscured**. - If you're accessing the desktop via a VNC session (e.g. to a headless Raspberry Pi) it is recommended to keep the setting at the default `1`, as VNC may not recognise keystrokes on overlaid non-transient windows. - - A boolean configuration setting `resizeable_dialog_b` governs whether *all* Toplevel dialogs are resizeable, irrespective of the default setting in `DialogState`. + - A boolean configuration setting `resizeable_dialog_b` governs whether *all* Toplevel dialogs are resizeable, irrespective of the default setting in `DialogState`. Setting this to '1' provides a workaround for issues with some scaled Linux Wayland displays. #### User-selectable widgets --- @@ -185,8 +194,8 @@ For more comprehensive installation instructions, please refer to [INSTALLATION. |---------------------------|---------------------------------------------------------------------------------------------------| |![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. | |![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.| -|![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). | -|![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. | +|![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). A manually-edited boolean configuration parameter `unusedsat_b` determines whether or not to include satellites where CNo = 0; default = 0. | +|![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.| |![signalsview widget](https://github.com/semuconsulting/PyGPSClient/blob/master/images/signalsview_widget.png?raw=true)| Signals view widget showing current svid/signal carrier-to-noise (C/No) level and (where applicable) correction source for each GNSS svid/signal received (*GNSS receiver must be capable of outputting UBX NAV-SIG messages*). Signal identifiers are in RINEX format e.g. `L1_C/A`, `E5_aQ`, etc. Double-click to toggle legend. Double-right-click to toggle signals where C/No = 0 dbHz. | |![world map](https://github.com/semuconsulting/PyGPSClient/blob/master/images/staticmap.png?raw=true)| Map widget with various modes of display - select from "map" / "sat" (online) or "world" / "custom" (offline). Select zoom level 1 - 20. Double-click the zoom level label to reset the zoom to 10. Double-right-click the zoom label to maximise zoom to 20. Tick Track to show track (track will only be recorded while this box is checked). Double-Right-click will clear the map. Map Type = 'world': a static offline Mercator world map showing current global location. |![online map](https://github.com/semuconsulting/PyGPSClient/blob/master/images/webmap_widget.png?raw=true)| Map Type = 'map', 'sat' or 'hyb' (hybrid): Dynamic, online web map or satellite image via MapQuest API (*requires an Internet connection and free [Mapquest API Key](#mapquestapi)*). By default, the web map will automatically refresh every 60 seconds (*indicated by a small timer icon at the top left*). The default refresh rate can be amended by changing the `"mapupdateinterval_n":` value in your json configuration file, but **NB** the facility is not intended to be used for real-time navigation. Double-click anywhere in the map to immediately refresh. | @@ -195,7 +204,7 @@ For more comprehensive installation instructions, please refer to [INSTALLATION. |![spectrum widget](https://github.com/semuconsulting/PyGPSClient/blob/master/images/spectrum_widget.png?raw=true)| Spectrum widget showing a spectrum analysis chart (*GNSS receiver must be capable of outputting UBX MON-SPAN messages*). Clicking anywhere in the spectrum chart will display the frequency and decibel reading at that point. Double-clicking anywhere in the chart will toggle the GNSS frequency band markers (L1, G2, etc.) on or off. Right-click anywhere in the chart to capture a snapshot of the spectrum data, which will then be superimposed on the live data (*this can, for example, be used to compare reception with different antenna configurations*). Double-right-click to clear snapshot. **NB:** Some receivers (e.g. NEO-F10N) will not output the requisite MON-SPAN messages unless the port baud rate is at least 57,600. | |![sysmon widget](https://github.com/semuconsulting/PyGPSClient/blob/master/images/sysmon_widget.png?raw=true)| System Monitor widget showing device cpu, memory and I/O utilisation (*GNSS receiver must be capable of outputting UBX MON-SYS/MON-COMMS or SBF ReceiverStatus messages*). Tick checkbox to toggle between actual (cumulative) I/O stats and pending I/O. Primarily intended for u-blox modules, but can display limited system information for other devices. | |![scatterplot widget](https://github.com/semuconsulting/PyGPSClient/blob/master/images/scatterplot_widget.png?raw=true)| Scatterplot widget showing variability in position reporting over time. (Optional) Enter fixed reference position. Select Average to center plot on dynamic average position (*displayed at top left*), or Fixed to center on fixed reference position (*if entered*). Check Autorange to set plot range automatically. Set the update interval (e.g. 4 = every 4th navigation solution). Use the range slider or mouse wheel to adjust plot range. Right-click to set fixed reference point to the current mouse cursor position. Double-click to clear the existing data. | -|![rover widget](https://github.com/semuconsulting/PyGPSClient/blob/master/images/rover_widget.png?raw=true) | Rover widget plots the relative 2D position, track and status information for the roving receiver in a fixed or moving base / rover RTK configuration. Can also display relative position of NTRIP mountpoint and receiver in a static RTK configuration. Double-click to clear existing plot. | +|![rover widget](https://github.com/semuconsulting/PyGPSClient/blob/master/images/rover_widget.png?raw=true) | Relative Position (formerly 'Rover') widget plots relative 2D bearing, distance, track and status information for the following configurations: (i) NAV-RELPOSNED - position of roving receiver relative to a fixed or moving RTK base (including an NTRIP mountpoint); (ii) RTCM 1005/6 - position of roving receiver relative to a fixed RTK NTRIP antenna reference point (ARP); (iii) NAV-DAHEADING - static heading ('yaw') from a dual-antenna receiver e.g. u-blox ZED-X20D. Double-click to clear existing plot. | |![chart view](https://github.com/semuconsulting/PyGPSClient/blob/master/images/chart_widget.png?raw=true) | Chart widget acts as a multi-channel "plotter", allowing the user to plot a series of named numeric data attributes from any parsed GNSS data source, with configurable y (value) and x (time) axes. By default, the number of channels is set to 4, but this can be manually edited by the user via the json configuration file setting `chartsettings_d["numchn_n"]`. For each channel, user can select: (*optional*) identity of message source e.g. `NAV-PVT`; attribute name e.g. `hAcc`; scaling factor (divisor) e.g. 1000; y axis range e.g. 0 - 5. Wildcards are available for attribute groups - "\*" (average of group values), "+" (maximum of group values), "-" (minimum of group values) e.g. `cno*` will plot the average `cno` value for a group of satellites. Double-click to clear the existing data. Double-right-click to save the current chart data to the clipboard in CSV format, which can be directly pasted into a spreadsheet application. | |![attitude widget](https://github.com/semuconsulting/PyGPSClient/blob/master/images/attitude_widget.png?raw=true) | Attitude Monitor widget (*formerly "IMU Monitor"*) showing current orientation/attitude (roll, pitch, yaw *aka 'static heading'*) and status from a variety of IMU, Dead Reckoning, Dual Antenna or other 2D/3D attitude message sources. Select range in degrees (from ±1 to ±180 degrees). | @@ -519,22 +528,24 @@ For further details, refer to the `pygnssutils` homepage at [https://github.com/ --- ## Troubleshooting -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. +1. **NB:** The latest version of Python for MacOS (>=3.14.5) comes with a new version of tkinter (9.0). There appear to be fairly serious performance issues with this version on MacOS Tahoe which render the PyGPSClient UI somewhat sluggish. For the time being, it is recommended that users use >=3.14.4. This issue does *not* affect other operating systems or Python apps not using tkinter. + +2. 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. -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. +3. 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. -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. +4. 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. -4. Some Homebrew-installed Python environments on MacOS can give rise to critical segmentation errors (*illegal memory access*) when shell subprocesses are invoked, due to the way permissions are implemented. This may, for example, affect About..Update functionality; the workaround is to update via a standard CLI `pip install --upgrade` command. +5. Some Homebrew-installed Python environments on MacOS can give rise to critical segmentation errors (*illegal memory access*) when shell subprocesses are invoked, due to the way permissions are implemented. This may, for example, affect About..Update functionality; the workaround is to update via a standard CLI `pip install --upgrade` command. -5. Installing the optional `cryptography` package on some 32-bit Linux platforms (e.g. Raspberry Pi OS 32) may require [Rust compiler support](https://www.rust-lang.org/tools/install) and some [additional build dependencies](https://cryptography.io/en/latest/installation/) (see [pyspartn cryptography installation notes](https://github.com/semuconsulting/pyspartn/tree/main/cryptography_installation#readme)): +6. Installing the optional `cryptography` package on some 32-bit Linux platforms (e.g. Raspberry Pi OS 32) may require [Rust compiler support](https://www.rust-lang.org/tools/install) and some [additional build dependencies](https://cryptography.io/en/latest/installation/) (see [pyspartn cryptography installation notes](https://github.com/semuconsulting/pyspartn/tree/main/cryptography_installation#readme)): ```shell curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh sudo apt-get install build-essential libssl-dev libffi-dev python3-dev pkg-config ``` -6. As of October 2025, u-blox have [discontinued their PointPerfect SPARTN L-Band and MQTT services](https://portal.u-blox.com/s/question/0D5Oj00000uB53GKAS/suspension-of-european-pointperfect-lband-spartn-service). As a result, PyGPSClient's [SPARTN Configuration](#spartnconfig) panel is largely redundant and is disabled by default in version>=1.5.17, though it can be re-enabled by manually setting the `lband_enabled_b` configuration setting to 1. +7. As of October 2025, u-blox have [discontinued their PointPerfect SPARTN L-Band and MQTT services](https://portal.u-blox.com/s/question/0D5Oj00000uB53GKAS/suspension-of-european-pointperfect-lband-spartn-service). As a result, PyGPSClient's [SPARTN Configuration](#spartnconfig) panel is largely redundant and is disabled by default in version>=1.5.17, though it can be re-enabled by manually setting the `lband_enabled_b` configuration setting to 1. --- ## License diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 18ce089b..aa436c10 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,21 @@ # PyGPSClient Release Notes +### RELEASE 1.7.1 + +FIXES: + +1. Fix 'memory leak' issue (socket server message queue filling up when server not active). +1. Internal refactoring and performance enhancements to improve long-term UI responsiveness and stability. +1. Fix u-blox preset configuration validation which was preventing some valid preset commands. + +ENHANCEMENTS: + +1. Split UBX Configuration panel into two separate panels - one for modern (Gen9+) receivers and one for legacy (Gen8-) receivers. The intention is to make the panels easier to use on lower-resolution or scaled displays. +1. Display UBX NAV-DAHEADING data from dual-antenna receivers such as the u-blox ZED-X20D in the "Rover Plot" widget. +1. Update portid designation in System Monitor I/O widget (X20 assigns different MON-COMMS.portids to F9P). +1. Add satellite filter entry field to RINEX conversion dialog (requires pygnssutils>=1.2.6). +1. "Meta Only" parsing mode added. When enabled, incoming raw data will *only* be parsed for basic metadata (protocol, identity, length) rather than individual data attributes. This mode is significantly quicker than full parsing, but individual data attributes *will no longer be available* and PyGPSClient's widgets other than the console will *not* be updated. Utilises the new [GNSSReader](https://github.com/semuconsulting/pygnssutils#gnssreader) `parsing=2` argument. Requires pygnssutils>=1.2.6. + ### RELEASE 1.7.0 1. Can now double-click to toggle between 'hae' (height above ellipsoid) and 'sep' (separation) values in banner panel. diff --git a/docs/pygpsclient.rst b/docs/pygpsclient.rst index 7e32acb9..9da9c236 100644 --- a/docs/pygpsclient.rst +++ b/docs/pygpsclient.rst @@ -508,6 +508,14 @@ pygpsclient.ubx\_handler module :undoc-members: :show-inheritance: +pygpsclient.ubx\_legacy\_config\_dialog module +---------------------------------------------- + +.. automodule:: pygpsclient.ubx_legacy_config_dialog + :members: + :undoc-members: + :show-inheritance: + pygpsclient.ubx\_msgrate\_frame module -------------------------------------- diff --git a/images/rover_widget.png b/images/rover_widget.png index 904f105c..2d6a1301 100644 Binary files a/images/rover_widget.png and b/images/rover_widget.png differ diff --git a/pygpsclient.json b/pygpsclient.json index cfa52fc5..28a61a1c 100644 --- a/pygpsclient.json +++ b/pygpsclient.json @@ -47,8 +47,8 @@ "gpxmapzoom_n": 10, "gpxtype_s": "track", "showtrack_b": 0, - "legend_b": 1, - "unusedsat_b": 0, + "legend_b": true, + "unusedsat_b": false, "datalog_b": 0, "logformat_s": "Binary", "logpath_s": "", diff --git a/pyproject.toml b/pyproject.toml index 776f1d65..0585cf6a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,10 +52,10 @@ classifiers = [ dependencies = [ "requests>=2.34.0", "Pillow>=12.0.0", - "pygnssutils>=1.2.5", + "pygnssutils>=1.2.5", # TODO requires >=1.2.6 when available "pyunigps>=1.0.0", "pynmeagps>=1.1.5", - "pyubx2>=1.3.3", + "pyubx2>=1.3.5", ] [project.scripts] diff --git a/src/pygpsclient/__main__.py b/src/pygpsclient/__main__.py index fc951336..b243c121 100644 --- a/src/pygpsclient/__main__.py +++ b/src/pygpsclient/__main__.py @@ -10,7 +10,6 @@ from argparse import SUPPRESS, ArgumentDefaultsHelpFormatter, ArgumentParser from logging import getLogger -from tkinter import Tk from pygnssutils import ( VERBOSITY_CRITICAL, @@ -118,8 +117,8 @@ def main(): f"{VERBOSITY_CRITICAL} = critical, " f"{VERBOSITY_LOW} = low (error), " f"{VERBOSITY_MEDIUM} = medium (warning), " - f"{VERBOSITY_HIGH} = high (info), {VERBOSITY_DEBUG} = debug, " - f"default = {VERBOSITY_CRITICAL}" + f"{VERBOSITY_HIGH} = high (info), " + f"{VERBOSITY_DEBUG} = debug" ), type=int, choices=[ @@ -129,7 +128,7 @@ def main(): VERBOSITY_DEBUG, VERBOSITY_CRITICAL, ], - default=VERBOSITY_LOW, + default=VERBOSITY_CRITICAL, ) ap.add_argument( "--logtofile", @@ -139,7 +138,7 @@ def main(): kwargs = vars(ap.parse_args()) # set up global logging configuration - verbosity = int(kwargs.pop("verbosity", VERBOSITY_LOW)) + verbosity = int(kwargs.pop("verbosity", VERBOSITY_CRITICAL)) logtofile = kwargs.pop("logtofile", "") logger = getLogger(APPNAME) # "pygpsclient" logger_utils = getLogger("pygnssutils") @@ -147,10 +146,10 @@ def main(): for logr in (logger, logger_utils, logger_pyubx2): set_logging(logr, verbosity, logtofile) - root = Tk() - App(root, **kwargs) - root.mainloop() + app = App(**kwargs) + app.mainloop() if __name__ == "__main__": + main() diff --git a/src/pygpsclient/_version.py b/src/pygpsclient/_version.py index 32122df4..07f89dde 100644 --- a/src/pygpsclient/_version.py +++ b/src/pygpsclient/_version.py @@ -8,4 +8,4 @@ :license: BSD 3-Clause """ -__version__ = "1.7.0" +__version__ = "1.7.1" diff --git a/src/pygpsclient/about_dialog.py b/src/pygpsclient/about_dialog.py index 6a764161..4e74ee2f 100644 --- a/src/pygpsclient/about_dialog.py +++ b/src/pygpsclient/about_dialog.py @@ -55,11 +55,10 @@ def __init__(self, app, *args, **kwargs): # pylint: disable=unused-argument """ Initialise Toplevel dialog - :param Frame app: reference to main tkinter application + :param Tk app: reference to main tkinter application """ self.__app = app # Reference to main application class - self.__master = self.__app.appmaster # Reference to root class (Tk) self.logger = logging.getLogger(__name__) self._img_icon = ImageTk.PhotoImage(Image.open(ICON_APP128).resize((64, 64))) self._img_github = ImageTk.PhotoImage(Image.open(ICON_GITHUB).resize((32, 32))) @@ -190,7 +189,7 @@ def _on_github(self, *args, **kwargs): # pylint: disable=unused-argument """ if brew_installed(): - self.status_label = (BREWWARN, INFOCOL) + self.set_status_label(BREWWARN, INFOCOL) return open_new_tab(GITHUB_URL) @@ -202,7 +201,7 @@ def _on_sponsor(self, *args, **kwargs): # pylint: disable=unused-argument """ if brew_installed(): - self.status_label = (BREWWARN, INFOCOL) + self.set_status_label(BREWWARN, INFOCOL) return open_new_tab(SPONSOR_URL) @@ -214,7 +213,7 @@ def _on_license(self, *args, **kwargs): # pylint: disable=unused-argument """ if brew_installed(): - self.status_label = (BREWWARN, INFOCOL) + self.set_status_label(BREWWARN, INFOCOL) return open_new_tab(LICENSE_URL) @@ -225,7 +224,7 @@ def _check_for_update(self, *args, **kwargs): # pylint: disable=unused-argument Check for updates. """ - self.status_label = ("Checking for updates...", INFOCOL) + self.set_status_label("Checking for updates...", INFOCOL) versions = check_for_updates() for i, (nam, current, latest) in enumerate(versions): txt = f"{nam}: {current}" @@ -238,14 +237,14 @@ def _check_for_update(self, *args, **kwargs): # pylint: disable=unused-argument else: txt += f" - Latest version is {latest}" col = ERRCOL - self._lbl_lib_versions[i].config(text=txt, fg=col) - + self._lbl_lib_versions[i]["text"] = txt + self._lbl_lib_versions[i]["fg"] = col updates = [nam for (nam, current, latest) in versions if latest != current] if len(updates) > 0: - self.status_label = ("Updates available", OKCOL) + self.set_status_label("Updates available", OKCOL) self._set_update_btn_mode(True) else: - self.status_label = ("No updates available", INFOCOL) + self.set_status_label("No updates available", INFOCOL) def _do_update(self, *args, **kwargs): # pylint: disable=unused-argument """ @@ -253,15 +252,15 @@ def _do_update(self, *args, **kwargs): # pylint: disable=unused-argument """ if brew_installed(): - self.status_label = (BREWUPDATE, INFOCOL) + self.set_status_label(BREWUPDATE, INFOCOL) return - self.status_label = (UPDATEINPROG, INFOCOL) + self.set_status_label(UPDATEINPROG, INFOCOL) rc = self.__app.do_app_update() if rc: - self.status_label = (UPDATERESTART, OKCOL) + self.set_status_label(UPDATERESTART, OKCOL) else: - self.status_label = (UPDATEERR.format(err=rc), ERRCOL) + self.set_status_label(UPDATEERR.format(err=rc), ERRCOL) self._set_update_btn_mode(False) def _set_update_btn_mode(self, update: bool): @@ -272,9 +271,11 @@ def _set_update_btn_mode(self, update: bool): """ if update: - self._btn_checkupdate.config(text="UPDATE", fg=OKCOL) + self._btn_checkupdate["text"] = "UPDATE" + self._btn_checkupdate["fg"] = OKCOL self._btn_checkupdate.bind("