diff --git a/README.md b/README.md
index b83d5273..6307b3e7 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@
[Mapquest API Key](#mapquestapi) |
[User-defined Presets](#userdefined) |
[CLI Utilities](#cli) |
-[Troubleshooting](#troubleshoot) |
+[Troubleshooting & Known Issues](#troubleshoot) |
[License](#license) |
[Author Information](#author)
@@ -59,7 +59,7 @@ This is an independent project and we have no affiliation whatsoever with any GN
The PyGPSClient home page is at [PyGPSClient](https://github.com/semuconsulting/PyGPSClient).
-Contributions from human beings welcome - please refer to [CONTRIBUTING.MD](https://github.com/semuconsulting/PyGPSClient/blob/master/CONTRIBUTING.md).
+Contributions **_from human beings_** welcome - please refer to [CONTRIBUTING.MD](https://github.com/semuconsulting/PyGPSClient/blob/master/CONTRIBUTING.md).
For [Bug reports](https://github.com/semuconsulting/PyGPSClient/blob/master/.github/ISSUE_TEMPLATE/bug_report.md), please use the template provided. For feature requests and general queries and advice, post a message to one of the [PyGPSClient Discussions](https://github.com/semuconsulting/PyGPSClient/discussions) channels in the first instance.
@@ -91,7 +91,8 @@ To install into a virtual environment (*which may be necessary if you have an [`
python3 -m venv pygpsclient
source pygpsclient/bin/activate # (or .\pygpsclient\Scripts\activate on Windows)
python3 -m pip install --upgrade pygpsclient
-deactivate
+pygpsclient
+deactivate # to deactivate virtual environment when finished
```
Quick [installation shell scripts](https://github.com/semuconsulting/PyGPSClient/blob/master/INSTALLATION.md#script) are available for Linux and MacOS platforms.
@@ -149,6 +150,7 @@ For more comprehensive installation instructions, please refer to [INSTALLATION.
gnssstreamer --port /dev/ttyACM0 --baudrate 115200 --timeout 3 --format 2 --clioutput 1 --output pygpsdata.log --verbosity 2
```
+ Datalogs will include both receiver output and (_if the relevant protocol is enabled in settings_) any incoming NTRIP RTK data stream.
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.
@@ -508,17 +510,19 @@ The `pygnssutils` and `pyubxutils` libraries which underpin many of the function
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).
---
-## Troubleshooting
+## Troubleshooting and Known Issues
+
+1. There is a known issue with PyGPSClient GUI refreshes becoming progressively slower on certain platforms if the app is left unattended (_i.e. no user interaction_) for an extended period - typically 30 minutes or more. The issue is more pronounced on low-end SBC platforms like the Raspberry Pi. **Underlying processing (including message parsing and datalogging) is unaffected**, and the GUI can generally be 'woken up' within a few seconds via a simple user interaction e.g. resizing the main panel. The root cause of this issue is under investigation, but as a workaround, users can try a) increasing the `guiupdateinterval_f` setting in the json configuration file, or b) hiding some or all user-selectable widgets until needed.
-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. **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 GUI 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.
+3. 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 raw Ephemerides or Observation data*). Try increasing the baudrate in the first instance.
-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.
+4. 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.
-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.
+5. 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.
-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.
+6. 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. For this reason, application updates via the About..Update button are disabled on Homebrew environments; use the CLI `python3 -m pip install --upgrade pygpsclient` command instead.
---
## License
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 93030812..e4a02d4c 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,5 +1,16 @@
# PyGPSClient Release Notes
+### RELEASE 1.7.3
+
+FIXES:
+
+1. Enhancement for unattended UI lag issue.
+1. Remove redundant CLI arguments relating to disused SPARTN client.
+
+CHANGES:
+
+1. When datalogging is enabled, any incoming NTRIP RTK stream (RTCM3 or SPARTN) will _only_ be logged if its protocol is enabled in settings; otherwise logging will be restricted to receiver output.
+
### RELEASE 1.7.2
FIXES:
diff --git a/pyproject.toml b/pyproject.toml
index a5fcb78a..0c6fa897 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -52,7 +52,7 @@ classifiers = [
dependencies = [
"requests>=2.34.0",
"Pillow>=12.0.0",
- "pygnssutils>=1.2.6",
+ "pygnssutils>=1.2.7",
"pyunigps>=1.0.0",
"pynmeagps>=1.1.5",
"pyubx2>=1.3.5",
diff --git a/src/pygpsclient/__main__.py b/src/pygpsclient/__main__.py
index b243c121..f62134db 100644
--- a/src/pygpsclient/__main__.py
+++ b/src/pygpsclient/__main__.py
@@ -22,12 +22,7 @@
from pygpsclient._version import __version__ as VERSION
from pygpsclient.app import App
-from pygpsclient.globals import (
- APPNAME,
- CONFIGFILE,
- SPARTN_BASEDATE_CURRENT,
- SPARTN_BASEDATE_DATASTREAM,
-)
+from pygpsclient.globals import APPNAME, CONFIGFILE
from pygpsclient.strings import EPILOG
@@ -52,32 +47,11 @@ def main():
help="User-defined GNSS receiver port",
default=SUPPRESS,
)
- ap.add_argument(
- "-S",
- "--spartnport",
- help="User-defined SPARTN receiver port",
- default=SUPPRESS,
- )
ap.add_argument(
"--mqapikey",
help="MapQuest API Key",
default=SUPPRESS,
)
- ap.add_argument(
- "--mqttclientid",
- help="MQTT Client ID",
- default=SUPPRESS,
- )
- ap.add_argument(
- "--mqttclientregion",
- help="MQTT Client Region",
- default=SUPPRESS,
- )
- ap.add_argument(
- "--mqttclientmode",
- help="MQTT Client Mode (0 - IP, 1 - L-Band)",
- default=SUPPRESS,
- )
ap.add_argument(
"--ntripcasteruser",
help="NTRIP Caster authentication user",
@@ -88,26 +62,14 @@ def main():
help="NTRIP Caster authentication password",
default=SUPPRESS,
)
- ap.add_argument(
- "--spartnkey",
- help="SPARTN message decryption key",
- default=SUPPRESS,
- )
- ap.add_argument(
- "--spartnbasedate",
- help=f"SPARTN message decryption timetag ({SPARTN_BASEDATE_CURRENT} = \
- current datetime, {SPARTN_BASEDATE_DATASTREAM} = use timetags from data stream)",
- type=int,
- default=SUPPRESS,
- )
ap.add_argument(
"--tlspempath",
- help="Fully qualified path to TLS PEM (private key/certificate) file",
+ help="Fully qualified path to TLS PEM (private key/certificate) file used by socket server",
default=SUPPRESS,
)
ap.add_argument(
"--tlscrtpath",
- help="Fully qualified path to TLS CRT (certificate) file",
+ help="Fully qualified path to TLS CRT (certificate) file used by socket client",
default=SUPPRESS,
)
ap.add_argument(
diff --git a/src/pygpsclient/_version.py b/src/pygpsclient/_version.py
index 24f75b4c..d1a925ac 100644
--- a/src/pygpsclient/_version.py
+++ b/src/pygpsclient/_version.py
@@ -8,4 +8,4 @@
:license: BSD 3-Clause
"""
-__version__ = "1.7.2"
+__version__ = "1.7.3"
diff --git a/src/pygpsclient/app.py b/src/pygpsclient/app.py
index 96c31b1b..7cf9ec67 100644
--- a/src/pygpsclient/app.py
+++ b/src/pygpsclient/app.py
@@ -3,20 +3,18 @@
PyGPSClient - Main tkinter application class.
-Essentially the 'Model' in a nominal MVC (Model-View-Controller)
+Essentially the 'Controller' in a nominal MVC (Model-View-Controller)
architecture.
-- Loads configuration from json file (if available)
-- Instantiates all frames, widgets, and protocol handlers.
+- Instantiates all frames and widgets via subclasses ('View').
+- Instantiates all protocol handlers.
+- Maintains central dictionary of current key navigation data as
+ `gnss_status`, for use by user-selectable widgets ('Model').
- Maintains state of all user-selectable widgets.
- Maintains state of all Toplevel dialogs.
-- Maintains state of all threaded protocol handler processes.
+- Maintains state of all threaded protocol handler and server processes.
- Maintains state of serial and RTK connections.
-- Handles event-driven data processing of navigation data placed on
- input message queue by stream handler and assigns to appropriate
- protocol handler.
-- Maintains central dictionary of current key navigation data as
- `gnss_status`, for use by user-selectable widgets.
+- Handles configuration load, save and update.
Global logging configuration is defined in __main__.py. To enable module
logging, this and other subsidiary modules can use:
@@ -42,11 +40,11 @@
from queue import Empty, Queue
from subprocess import CalledProcessError, run
from sys import executable
-from threading import Thread
+from threading import Lock, Thread
from tkinter import EW, NSEW, NW, Frame, PhotoImage, Tk, Toplevel, font
from types import NoneType
-from pygnssutils import GNSSMQTTClient, GNSSNTRIPClient
+from pygnssutils import GNSSNTRIPClient
from pygnssutils.gnssreader import (
NMEA_PROTOCOL,
POLL,
@@ -74,8 +72,6 @@
CMDINITDELAY,
CMDPAUSE,
CONFIGFILE,
- CONNECTED_SPARTNIP,
- CONNECTED_SPARTNLB,
DISCONNECTED,
ERRCOL,
FRAME,
@@ -85,13 +81,11 @@
ICON_APP128,
INFOCOL,
MAINSCALE,
- MQTT_PROTOCOL,
NOPORTS,
NTRIP_EVENT,
OKCOL,
RTCMSTR,
SOCKSERVER_MAX_CLIENTS,
- SPARTN_EVENT,
SPARTN_PROTOCOL,
STATUS_PRIORITY,
STATUS_TIMEOUT,
@@ -177,7 +171,6 @@ def __init__(self, **kwargs):
super().__init__()
# load config from json file
- self.refresh_widget_timer = None
self.status_msg_timer = None
self._deferredmsg = None
self.widget_state = WidgetState() # widget state
@@ -202,11 +195,12 @@ def __init__(self, **kwargs):
self._server_status = -1 # socket server status -1 = inactive
self.gnss_outqueue = Queue() # messages to GNSS receiver
self.ntrip_inqueue = Queue() # messages from NTRIP source
- self.spartn_inqueue = Queue() # messages from SPARTN correction rcvr
- self.spartn_outqueue = Queue() # messages to SPARTN correction rcvr
self.socket_inqueue = Queue() # message from socket
self.socket_outqueue = Queue() # message to socket
self.console_outqueue = Queue() # message to console
+ self.gnssstatus_lock = Lock() # thread lock for GNSS status data
+ self.datalog_lock = Lock() # thread lock for datalog file
+ self.gpx_lock = Lock() # thread lock for gpx file
self.dialog_state = DialogState() # dialog state
self.gnss_status = GNSSStatus() # holds latest GNSS readings
self.stream_handler = StreamHandler(self)
@@ -219,7 +213,6 @@ def __init__(self, **kwargs):
self.rtcm_handler = RTCM3Handler(self)
self.tty_handler = TTYHandler(self)
self.ntrip_handler = GNSSNTRIPClient(self)
- self.spartn_handler = GNSSMQTTClient(self)
self.sqlite_handler = SqliteHandler(self)
self.frm_settings = None
self._conn_status = DISCONNECTED
@@ -232,6 +225,9 @@ def __init__(self, **kwargs):
self.recording = False # RecordDialog status
self.recording_type = 0 # 0 = TTY ONLY, 1 = UBX/NMEA
self.ntriprtcmstr = RTCMSTR
+ self._gui_refresh_int = int(
+ self.configuration.get("guiupdateinterval_f") * 1000
+ )
# open database if database recording enabled
dbpath = self.configuration.get("databasepath_s")
@@ -370,7 +366,6 @@ def _attach_events(self):
self.bind(GNSS_TIMEOUT_EVENT, self.on_gnss_timeout)
self.bind(GNSS_ERR_EVENT, self.on_stream_error)
self.bind(NTRIP_EVENT, self.on_ntrip_read)
- self.bind(SPARTN_EVENT, self.on_spartn_read)
self.bind_all("", self.on_exit)
self.bind_all("", self.on_killswitch)
# also bound in check_updates
@@ -573,7 +568,8 @@ def update_widgets(self):
def refresh_widgets(self):
"""
TIMER PROCESS WHILE CONNECTED
- Refresh visible widgets.
+
+ Refresh visible widgets with latest GNSSStatus data.
"""
self.frm_banner.update_frame()
@@ -583,16 +579,14 @@ def refresh_widgets(self):
if hasattr(frm, "update_frame") and wdgdata[VISIBLE]:
frm.update_frame()
self.update()
+ self.update_idletasks()
# update database if enabled (must be done in main App thread)
if self.configuration.get("database_b"):
self.sqlite_handler.load_data()
if self.conn_status != DISCONNECTED or self.rtk_conn_status != DISCONNECTED:
- update_interval = int(self.configuration.get("guiupdateinterval_f") * 1000)
- self.refresh_widget_timer = self.after(
- update_interval, self.refresh_widgets
- )
+ self.after(self._gui_refresh_int, self.refresh_widgets)
def start_dialog(self, dlg: str):
"""
@@ -800,51 +794,42 @@ def on_ntrip_read(self, event): # pylint: disable=unused-argument
:param event event: read event
"""
+ inmask = False
try:
- raw_data, parsed_data = self.ntrip_inqueue.get(False)
- if (
- raw_data is not None
- and parsed_data is not None
- and isinstance(raw_data, bytes)
- ):
- if isinstance(parsed_data, RTCMMessage):
- self.send_to_device(raw_data)
- if self.protocol_mask & RTCM3_PROTOCOL:
- self.console_outqueue.put((raw_data, parsed_data, "NTRIP>>"))
- elif isinstance(parsed_data, SPARTNMessage):
- self.send_to_device(raw_data)
- if self.protocol_mask & SPARTN_PROTOCOL:
- self.console_outqueue.put((raw_data, parsed_data, "NTRIP>>"))
- elif isinstance(parsed_data, NMEAMessage):
- if self.protocol_mask & NMEA_PROTOCOL:
- self.console_outqueue.put((raw_data, parsed_data, "NTRIP<<"))
- self.ntrip_inqueue.task_done()
- except Empty:
- pass
- except (SerialException, SerialTimeoutException) as err:
- self.set_status_label(f"Error sending to device {err}", ERRCOL)
-
- def on_spartn_read(self, event): # pylint: disable=unused-argument
- """
- EVENT TRIGGERED
- Action on <> event - data available on SPARTN queue.
-
- :param event event: read event
- """
-
- try:
- raw_data, parsed_data = self.spartn_inqueue.get(False)
- if raw_data is not None and parsed_data is not None:
- self.send_to_device(raw_data)
- if self._rtk_conn_status == CONNECTED_SPARTNLB:
- source = "LBAND>>"
- elif self._rtk_conn_status == CONNECTED_SPARTNIP:
- source = "MQTT>>"
- else:
- source = "OTHER>>"
- self.console_outqueue.put((raw_data, parsed_data, source))
- self.spartn_inqueue.task_done()
-
+ while True:
+ raw_data, parsed_data = self.ntrip_inqueue.get(False)
+ if (
+ raw_data is not None
+ and parsed_data is not None
+ and isinstance(raw_data, bytes)
+ ):
+ inmask = False
+ if isinstance(parsed_data, RTCMMessage):
+ self.send_to_device(raw_data)
+ if self.protocol_mask & RTCM3_PROTOCOL:
+ self.console_outqueue.put(
+ (raw_data, parsed_data, "NTRIP>>")
+ )
+ inmask = True
+ elif isinstance(parsed_data, SPARTNMessage):
+ self.send_to_device(raw_data)
+ if self.protocol_mask & SPARTN_PROTOCOL:
+ self.console_outqueue.put(
+ (raw_data, parsed_data, "NTRIP>>")
+ )
+ inmask = True
+ elif isinstance(parsed_data, NMEAMessage):
+ if self.protocol_mask & NMEA_PROTOCOL:
+ self.console_outqueue.put(
+ (raw_data, parsed_data, "NTRIP<<")
+ )
+
+ # update log file if enabled
+ if self.configuration.get("datalog_b") and inmask:
+ with self.datalog_lock:
+ self.file_handler.write_logfile(raw_data, parsed_data)
+
+ self.ntrip_inqueue.task_done()
except Empty:
pass
except (SerialException, SerialTimeoutException) as err:
@@ -1165,7 +1150,6 @@ def protocol_mask(self) -> int:
+ (cfg.get("qgcprot_b") * QGC_PROTOCOL) # 16
+ (cfg.get("uniprot_b") * UNI_PROTOCOL) # 32
+ (cfg.get("spartnprot_b") * SPARTN_PROTOCOL) # 256
- + (cfg.get("mqttprot_b") * MQTT_PROTOCOL) # 512
+ (cfg.get("ttyprot_b") * TTY_PROTOCOL) # 1024
)
return mask
diff --git a/src/pygpsclient/file_handler.py b/src/pygpsclient/file_handler.py
index 62f6d40e..374f6e97 100644
--- a/src/pygpsclient/file_handler.py
+++ b/src/pygpsclient/file_handler.py
@@ -44,7 +44,6 @@
from pygpsclient.strings import CONFIGTITLE, GITHUB_URL, SAVETITLE
DEFEXT = ("all files", "*.*")
-FLUSHINT = 100 # flush log file every 100 updates
class FileHandler:
@@ -280,10 +279,7 @@ def write_logfile(self, raw_data, parsed_data):
datum = (str(datum) + "\r").encode("utf-8")
try:
self._logfile.write(datum)
- self._flushcount += 1
- if self._flushcount >= FLUSHINT:
- self._logfile.flush()
- self._flushcount = 0
+ self._logfile.flush()
self._logsize += len(datum)
except ValueError:
pass
diff --git a/src/pygpsclient/globals.py b/src/pygpsclient/globals.py
index a9ed6ae1..024efe9d 100644
--- a/src/pygpsclient/globals.py
+++ b/src/pygpsclient/globals.py
@@ -242,7 +242,6 @@
SPARTN_DEFAULT_KEY = "abcd1234abcd1234abcd1234abcd1234"
SPARTN_EOF_EVENT = "<>"
SPARTN_ERR_EVENT = "<>"
-SPARTN_EVENT = "<>"
SPARTN_KEYLEN = 16
SPARTN_OUTPORT = 8883
SPARTN_PPREGIONS = ("eu", "us", "jp", "kr", "au")
diff --git a/src/pygpsclient/recorder_dialog.py b/src/pygpsclient/recorder_dialog.py
index ce71b593..294682d9 100644
--- a/src/pygpsclient/recorder_dialog.py
+++ b/src/pygpsclient/recorder_dialog.py
@@ -19,7 +19,6 @@
# pylint: disable=unused-argument
from datetime import datetime
-from threading import Event, Thread
from time import sleep
from tkinter import (
CENTER,
@@ -77,7 +76,8 @@
from pygpsclient.toplevel_dialog import ToplevelDialog
CFG = b"\x06"
-FLASH = 0.7
+FLASH = 1000
+FLASHCOLS = (("white", ERRCOL), (ERRCOL, "white"))
MSG = b"\x01"
PLAY = 1
PRT = b"\x00"
@@ -120,11 +120,11 @@ def __init__(self, app: Tk, *args, **kwargs):
self._importdesc = StringVar()
self._rec_status = STOP
self._configfile = None
- self._stop_event = Event()
self._bg = self.cget("bg") # default background color
self._configfile = None
self._configpath = None
self._save_to_preset = False
+ self._flash = 0
self._body()
self._do_layout()
@@ -481,15 +481,8 @@ def _on_record(self):
if self._rec_status == STOP:
self._rec_status = RECORD
self.__app.recording = True
- # start flashing record label...
- self._stop_event.clear()
- Thread(
- target=self._flash_record,
- daemon=True,
- args=(self._stop_event,),
- ).start()
+ self._flash_record()
elif self._rec_status == RECORD:
- self._stop_event.set()
self._rec_status = STOP
self.__app.recording = False
@@ -625,24 +618,22 @@ def update_count(self):
self._lbl_memory["text"] = len(self.__app.recorded_commands)
- def _flash_record(self, stop: Event):
+ def _flash_record(self):
"""
- THREADED
Flash record indicator for conspicuity.
"""
try:
- cols = [("white", ERRCOL), (ERRCOL, "white")]
- i = 0
- while not stop.is_set():
- i = not i
+ if self._rec_status == RECORD:
+ self._flash = not self._flash
self._lbl_activity["text"] = "RECORDING"
- self._lbl_activity["fg"] = cols[i][0]
- self._lbl_activity["bg"] = cols[i][1]
- sleep(FLASH)
- self._lbl_activity["text"] = ""
- self._lbl_activity["fg"] = FGCOL
- self._lbl_activity["bg"] = BGCOL
+ self._lbl_activity["fg"] = FLASHCOLS[self._flash][0]
+ self._lbl_activity["bg"] = FLASHCOLS[self._flash][1]
+ self.after(FLASH, self._flash_record)
+ else:
+ self._lbl_activity["text"] = ""
+ self._lbl_activity["fg"] = FGCOL
+ self._lbl_activity["bg"] = BGCOL
except TclError: # if dialog closed without stopping recording
pass
diff --git a/src/pygpsclient/rinex_dialog.py b/src/pygpsclient/rinex_dialog.py
index 08b0efe8..71657a99 100644
--- a/src/pygpsclient/rinex_dialog.py
+++ b/src/pygpsclient/rinex_dialog.py
@@ -696,7 +696,7 @@ def _reset(self):
for chk in (
self._rinexobs,
self._rinexnav,
- self._rinexmet,
+ # self._rinexmet,
self._rxgps,
self._rxglonass,
self._rxgalileo,
@@ -706,6 +706,7 @@ def _reset(self):
self._rxnavic,
):
chk.set(1)
+ self._rinexmet.set(0)
self._obssource.set(UBLOX)
self._navsource.set(UBLOX)
self._metsource.set(NMEA)
diff --git a/src/pygpsclient/stream_handler.py b/src/pygpsclient/stream_handler.py
index 1d1b6d6b..4e75a8d9 100644
--- a/src/pygpsclient/stream_handler.py
+++ b/src/pygpsclient/stream_handler.py
@@ -3,21 +3,14 @@
StreamHandler class for PyGPSClient application.
-This handles all the serial stream i/o. It uses the pyubx2.UBXReader
-class to read and parse incoming data from the receiver. It places
-this data on an input message queue and generates a <>
-which triggers the main App class to process the data.
+This handles all the serial stream i/o.
-It also reads any command and poll messages placed on an output
-message queue and sends these to the receiver.
-
-The StreamHandler class is used by two PyGPSClient 'caller' objects:
-
-- SettingsFrame - i/o with the main GNSS receiver.
-- SpartnLbandDialog - i/o with a SPARTN L-Band receiver when SPARTN Client active.
-
-The caller object can implement a 'status_label = ()' method to
-display any status messages output by StreamHandler.
+- uses the pygnssutils.GNSSReader class to read and parse incoming data \
+ from the receiver.
+- updates the GNSSStatus object with parsed GNSS data. GNSSStatus is used \
+ by the various user-selectable widgets to display current status.
+- reads any command and poll messages placed on an output message queue \
+ and sends these to the receiver.
Created on 16 Sep 2020
@@ -26,8 +19,6 @@ class to read and parse incoming data from the receiver. It places
:license: BSD 3-Clause
"""
-# pylint: disable=fixme
-
import logging
import ssl
from datetime import datetime, timedelta
@@ -482,48 +473,55 @@ def _process_message(
tty = self.__app.configuration.get("ttyprot_b")
console = self.__app.widget_state.state[WDGCONSOLE][VISIBLE]
- if isinstance(parsed_data, NMEAMessage) and protfilter & NMEA_PROTOCOL:
- self.__app.nmea_handler.process_data(raw_data, parsed_data)
- msgprot = NMEA_PROTOCOL
- elif isinstance(parsed_data, UBXMessage) and protfilter & UBX_PROTOCOL:
- self.__app.ubx_handler.process_data(raw_data, parsed_data)
- msgprot = UBX_PROTOCOL
- elif isinstance(parsed_data, RTCMMessage) and protfilter & RTCM3_PROTOCOL:
- self.__app.rtcm_handler.process_data(raw_data, parsed_data)
- msgprot = RTCM3_PROTOCOL
- elif isinstance(parsed_data, SBFMessage) and protfilter & SBF_PROTOCOL:
- self.__app.sbf_handler.process_data(raw_data, parsed_data)
- msgprot = SBF_PROTOCOL
- elif isinstance(parsed_data, QGCMessage) and protfilter & QGC_PROTOCOL:
- self.__app.qgc_handler.process_data(raw_data, parsed_data)
- msgprot = QGC_PROTOCOL
- elif isinstance(parsed_data, UNIMessage) and protfilter & UNI_PROTOCOL:
- self.__app.uni_handler.process_data(raw_data, parsed_data)
- msgprot = UNI_PROTOCOL
- elif isinstance(parsed_data, SPARTNMessage) and protfilter & SPARTN_PROTOCOL:
- msgprot = SPARTN_PROTOCOL
- elif isinstance(parsed_data, GNSSMessage):
- msgprot = GNSS_PROTOCOL
- elif isinstance(parsed_data, str):
- if tty:
- msgprot = TTY_PROTOCOL
- self.__app.tty_handler.process_data(raw_data, parsed_data)
- else:
- msgprot = -1
- marker = WARNING
-
- # update consoledata if console is visible and protocol not filtered
+ with self.__app.gnssstatus_lock:
+ if isinstance(parsed_data, NMEAMessage) and protfilter & NMEA_PROTOCOL:
+ self.__app.nmea_handler.process_data(raw_data, parsed_data)
+ msgprot = NMEA_PROTOCOL
+ elif isinstance(parsed_data, UBXMessage) and protfilter & UBX_PROTOCOL:
+ self.__app.ubx_handler.process_data(raw_data, parsed_data)
+ msgprot = UBX_PROTOCOL
+ elif isinstance(parsed_data, RTCMMessage) and protfilter & RTCM3_PROTOCOL:
+ self.__app.rtcm_handler.process_data(raw_data, parsed_data)
+ msgprot = RTCM3_PROTOCOL
+ elif isinstance(parsed_data, SBFMessage) and protfilter & SBF_PROTOCOL:
+ self.__app.sbf_handler.process_data(raw_data, parsed_data)
+ msgprot = SBF_PROTOCOL
+ elif isinstance(parsed_data, QGCMessage) and protfilter & QGC_PROTOCOL:
+ self.__app.qgc_handler.process_data(raw_data, parsed_data)
+ msgprot = QGC_PROTOCOL
+ elif isinstance(parsed_data, UNIMessage) and protfilter & UNI_PROTOCOL:
+ self.__app.uni_handler.process_data(raw_data, parsed_data)
+ msgprot = UNI_PROTOCOL
+ elif (
+ isinstance(parsed_data, SPARTNMessage) and protfilter & SPARTN_PROTOCOL
+ ):
+ msgprot = SPARTN_PROTOCOL
+ elif isinstance(parsed_data, GNSSMessage):
+ msgprot = GNSS_PROTOCOL
+ elif isinstance(parsed_data, str):
+ if tty:
+ msgprot = TTY_PROTOCOL
+ self.__app.tty_handler.process_data(raw_data, parsed_data)
+ else:
+ msgprot = -1
+ marker = WARNING
+
+ # if console is visible and protocol not filtered, place raw and parsed
+ # data on console input queue
if console and msgprot:
self.__app.console_outqueue.put((raw_data, parsed_data, marker))
- # if socket server is running and has clients, output raw data to socket
+ # if socket server is running and has clients, place raw data on socket
+ # output queue
if self.__app.server_status > 0:
self.__app.socket_outqueue.put(raw_data)
# update log file if enabled
if self.__app.configuration.get("datalog_b"):
- self.__app.file_handler.write_logfile(raw_data, parsed_data)
+ with self.__app.datalog_lock:
+ self.__app.file_handler.write_logfile(raw_data, parsed_data)
# update GPX track file if enabled
if self.__app.configuration.get("recordtrack_b"):
- self.__app.file_handler.update_gpx_track()
+ with self.__app.gpx_lock:
+ self.__app.file_handler.update_gpx_track()
diff --git a/src/pygpsclient/strings.py b/src/pygpsclient/strings.py
index e86805f7..6c605ac0 100644
--- a/src/pygpsclient/strings.py
+++ b/src/pygpsclient/strings.py
@@ -230,7 +230,7 @@
DLGTNMEA = "NMEA Configuration"
DLGTNTRIP = "NTRIP Configuration"
DLGTRECORD = "Configuration Command Recorder"
-DLGTRINEX = "RINEX Conversion (EXPERIMENTAL)"
+DLGTRINEX = "RINEX Conversion (BETA)"
DLGTSERVER = "Server Configuration"
DLGTSETTINGS = "Settings"
DLGTTTY = "TTY Configuration"