Skip to content

Commit

Permalink
Merge pull request #108 from semuconsulting/RELEASE-CANDIDATE-1.4.8
Browse files Browse the repository at this point in the history
Release candidate 1.4.8
  • Loading branch information
semuadmin authored Jan 18, 2024
2 parents 3a3a5a4 + 0d7c9e6 commit c9a4117
Show file tree
Hide file tree
Showing 11 changed files with 90 additions and 77 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"editor.formatOnSave": true,
"modulename": "pygpsclient",
"distname": "pygpsclient",
"moduleversion": "1.4.7",
"moduleversion": "1.4.8",
}
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ Below is a illustrative SPARTN DGPS data log, showing:

The Socket Server / NTRIP Caster facility is capable of operating in either of two modes;
1. SOCKET SERVER - an open, unauthenticated TCP socket server available to any socket client including, for example, another instance of PyGPSClient or the [`gnssdump` CLI utility](https://github.com/semuconsulting/pygnssutils#gnssdump). In this mode it will broadcast the host's currently connected GNSS data stream (NMEA, UBX, RTCM3). The default port is 50012.
2. NTRIP CASTER - a simple implementation of an authenticated NTRIP caster available to any NTRIP client including, for example, the PyGPSClient NTRIP Client facility or the [`gnssntripclient` CLI utility](https://github.com/semuconsulting/pygnssutils#gnssntripclient). Login credentials for the NTRIP caster are set via the `"ntripclientuser_s":` and `"ntripclientpassword_s":` settings in the *.json confirmation file (they can also be set via PyGPSClient command line arguments `--ntripuser`, `--ntrippassword`, or by setting environment variables `PYGPSCLIENT_USER`, `PYGPSCLIENT_PASSWORD`). Default settings are as follows: bind address: 0.0.0.0, port: 2101, mountpoint: pygnssutils, user: anon, password: password.
2. NTRIP CASTER - a simple implementation of an authenticated NTRIP caster available to any NTRIP client including, for example, the PyGPSClient NTRIP Client facility or the [`gnssntripclient` CLI utility](https://github.com/semuconsulting/pygnssutils#gnssntripclient). Login credentials for the NTRIP caster are set via the `"ntripcasteruser_s":` and `"ntripcasterpassword_s":` settings in the *.json confirmation file (they can also be set via PyGPSClient command line arguments `--ntripcasteruser`, `--ntripcasterpassword`, or by setting environment variables `NTRIPCASTER_USER`, `NTRIPCASTER_PASSWORD`). Default settings are as follows: bind address: 0.0.0.0, port: 2101, mountpoint: pygnssutils, user: anon, password: password.

By default, the server/caster binds to the host address '0.0.0.0' (IPv4) or '::' (IPv6) i.e. all available IP addresses on the host machine. This can be overridden via the settings panel or a host environment variable `PYGPSCLIENT_BINDADDRESS`. A label on the settings panel indicates the number of connected clients, and the server/caster status is indicated in the topmost banner: running with no clients: ![transmit icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-noclient-10-24.png?raw=true), running with clients: ![transmit icon](https://github.com/semuconsulting/PyGPSClient/blob/master/src/pygpsclient/resources/iconmonstr-transmit-10-24.png?raw=true).

Expand Down Expand Up @@ -389,7 +389,7 @@ To run the application, if the Python 3 scripts (bin) directory is in your PATH,
pygpsclient
```

`pygpsclient` also accepts optional command line arguments for a variety of configurable parameters. Type the following for help:
`pygpsclient` also accepts optional command line arguments for a variety of configurable parameters. These will override any saved configuration file settings. Type the following for help:
```shell
pygpsclient -h
```
Expand Down
11 changes: 9 additions & 2 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# PyGPSClient Release Notes

### RELEASE CANDIDATE v1.4.7
### RELEASE CANDIDATE v1.4.8

FIXES:

1. Fix parsing of command line arguments (NB: command line arguments and/or environment variables will now take temporary precedence over saved config file settings)
1. Fix 'unhashable type' error when first displaying mountpoint data in NTRIP client panel

### RELEASE v1.4.7

ENHANCEMENTS:

Expand Down Expand Up @@ -374,7 +381,7 @@ ENHANCEMENTS:

1. **New BETA Socket / NTRIP Server feature**. Capable of operating in two modes - either (a) as an open, unauthenticated TCP socket server, or (b) as an authenticated NTRIP server.
3. In open socket server mode, the output socket stream can be accessed by any TCP socket client capable of parsing raw GNSS data, including another instance of PyGPSClient or `gnssdump` (the CLI utility installed with `pyubx2`) running on another machine (*assuming the traffic is permitted through any firewalls*).
4. In NTRIP server mode, the socket stream can be accessed by any authenticated NTRIP client. The sourcetable contains a single entry corresponding to the PyGPSClient host. The server authentication credentials are set via two environment variables `PYGPSCLIENT_USER` and `PYGPSCLIENT_PASSWORD`.
4. In NTRIP server mode, the socket stream can be accessed by any authenticated NTRIP client. The sourcetable contains a single entry corresponding to the PyGPSClient host. The server authentication credentials are set via two environment variables `NTRIPCASTER_USER` and `NTRIPCASTER_PASSWORD`.
5. In either mode, the maximum number of clients is arbitrarily limited to 5. A label on the settings panel indicates the number of connected clients - this turns red when the maximum has been reached.
6. The socket host address is `0.0.0.0` (i.e. binds to all available IP addresses on the host machine). The socket port defaults to `50010` but is configurable via the settings panel (`2101` is the convention for NTRIP servers but is not mandated).
7. The default configuration for the socket server is set in `globals.py` as `SOCKSERVER_HOST`, `SOCKSERVER_PORT` and `SOCKSERVER_MAX_CLIENTS`.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "pygpsclient"
authors = [{ name = "semuadmin", email = "[email protected]" }]
maintainers = [{ name = "semuadmin", email = "[email protected]" }]
description = "GNSS Diagnostic and UBX Configuration GUI Application"
version = "1.4.7"
version = "1.4.8"
license = { file = "LICENSE" }
keywords = [
"PyGPSClient",
Expand Down
22 changes: 14 additions & 8 deletions src/pygpsclient/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@

from pygpsclient._version import __version__ as VERSION
from pygpsclient.app import App
from pygpsclient.globals import CONFIGFILE, EPILOG
from pygpsclient.globals import (
CONFIGFILE,
DEFAULT_PASSWORD,
DEFAULT_REGION,
DEFAULT_USER,
EPILOG,
)


def main():
Expand All @@ -24,14 +30,14 @@ def main():
arp = ArgumentParser(
epilog=EPILOG,
formatter_class=ArgumentDefaultsHelpFormatter,
description="Config file will override other command line arguments",
description="Command line arguments will override configuration file",
)
arp.add_argument("-V", "--version", action="version", version="%(prog)s " + VERSION)
arp.add_argument(
"-C",
"--config",
required=False,
help="Fully-qualified path to config file",
help="Fully-qualified path to configuration file",
default=CONFIGFILE,
)
arp.add_argument(
Expand Down Expand Up @@ -64,7 +70,7 @@ def main():
"--mqttclientregion",
required=False,
help="MQTT Client Region",
default=getenv("MQTTCLIENTREGION", "eu"),
default=getenv("MQTTCLIENTREGION", DEFAULT_REGION),
)
arp.add_argument(
"--mqttclientmode",
Expand All @@ -73,16 +79,16 @@ def main():
default=getenv("MQTTCLIENTMODE", "0"),
)
arp.add_argument(
"--ntripuser",
"--ntripcasteruser",
required=False,
help="NTRIP Caster authentication user",
default=getenv("PYGPSCLIENT_USER", "anon"),
default=getenv("NTRIPCASTER_USER", DEFAULT_USER),
)
arp.add_argument(
"--ntrippassword",
"--ntripcasterpassword",
required=False,
help="NTRIP Caster authentication password",
default=getenv("PYGPSCLIENT_PASSWORD", "password"),
default=getenv("NTRIPCASTER_PASSWORD", DEFAULT_PASSWORD),
)

kwargs = vars(arp.parse_args())
Expand Down
2 changes: 1 addition & 1 deletion src/pygpsclient/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
:license: BSD 3-Clause
"""

__version__ = "1.4.7"
__version__ = "1.4.8"
71 changes: 45 additions & 26 deletions src/pygpsclient/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
CLASS,
CONFIGFILE,
CONNECTED,
DEFAULT_PASSWORD,
DEFAULT_REGION,
DEFAULT_USER,
DISCONNECTED,
DLG,
DLGTNTRIP,
Expand Down Expand Up @@ -124,23 +127,23 @@ def __init__(self, master, *args, **kwargs): # pylint: disable=too-many-stateme
# user-defined serial port can be passed as environment variable
# or command line keyword argument
self._configfile = kwargs.pop("config", CONFIGFILE)
self.user_port = kwargs.pop("userport", getenv("PYGPSCLIENT_USERPORT", ""))
self.spartn_user_port = kwargs.pop(
user_port = kwargs.pop("userport", getenv("PYGPSCLIENT_USERPORT", ""))
spartn_user_port = kwargs.pop(
"spartnport", getenv("PYGPSCLIENT_SPARTNPORT", "")
)
self.mqapikey = kwargs.pop("mqapikey", getenv("MQAPIKEY", ""))
self.mqttclientid = kwargs.pop("mqttclientid", getenv("MQTTCLIENTID", ""))
self.mqttclientregion = kwargs.pop(
"mqttclientregion", getenv("MQTTCLIENTREGION", "eu")
mqapikey = kwargs.pop("mqapikey", getenv("MQAPIKEY", ""))
mqttclientid = kwargs.pop("mqttclientid", getenv("MQTTCLIENTID", ""))
mqttclientregion = kwargs.pop(
"mqttclientregion", getenv("MQTTCLIENTREGION", DEFAULT_REGION)
)
self.mqttclientmode = int(
mqttclientmode = int(
kwargs.pop("mqttclientmode", getenv("MQTTCLIENTMODE", "0"))
)
self.ntripcaster_user = kwargs.pop(
"ntripuser", getenv("PYGPSCLIENT_USER", "anon")
ntripcaster_user = kwargs.pop(
"ntripcasteruser", getenv("NTRIPCASTER_USER", DEFAULT_USER)
)
self.ntripcaster_password = kwargs.pop(
"ntrippassword", getenv("PYGPSCLIENT_USER", "password")
ntripcaster_password = kwargs.pop(
"ntripcasterpassword", getenv("NTRIPCASTER_PASSWORD", DEFAULT_PASSWORD)
)

Frame.__init__(self, self.__master, *args, **kwargs)
Expand Down Expand Up @@ -168,7 +171,6 @@ def __init__(self, master, *args, **kwargs): # pylint: disable=too-many-stateme
self.rtcm_handler = RTCM3Handler(self)
self.ntrip_handler = GNSSNTRIPClient(self, verbosity=0)
self.spartn_handler = GNSSMQTTClient(self, verbosity=0)
self.saved_config = {}
self._conn_status = DISCONNECTED
self._rtk_conn_status = DISCONNECTED
self._socket_thread = None
Expand All @@ -179,13 +181,30 @@ def __init__(self, master, *args, **kwargs): # pylint: disable=too-many-stateme
# Load configuration from file if it exists
self._colortags = []
self._ubxpresets = []
self.saved_config = {}
(_, config, configerr) = self.file_handler.load_config(self._configfile)
if configerr == "": # load succeeded
self.saved_config = config
# update configs needed to instantiate widgets and protocol handlers
self.update_widgets()
else: # load failed - invalid json or attribute types
self.saved_config = {}

# temporarily override saved config with any command line arguments / env variables
if user_port != "":
self.saved_config["userport_s"] = user_port
if spartn_user_port != "":
self.saved_config["spartnport_s"] = spartn_user_port
if mqapikey != "":
self.saved_config["mqapikey_s"] = mqapikey
if mqttclientid != "":
self.saved_config["mqttclientid_s"] = mqttclientid
if mqttclientregion != DEFAULT_REGION:
self.saved_config["mqttclientregion_s"] = mqttclientregion
if mqttclientmode != 0:
self.saved_config["mqttclientmode_n"] = mqttclientmode
if ntripcaster_user != DEFAULT_USER:
self.saved_config["ntripcasteruser_s"] = ntripcaster_user
if ntripcaster_password != DEFAULT_PASSWORD:
self.saved_config["ntripcasterpassword_s"] = ntripcaster_password

# update NTRIP and SPARTN client handlers with initial config
self.update_NTRIP_handler()
Expand Down Expand Up @@ -497,10 +516,10 @@ def update_NTRIP_handler(self):
"ntripclientversion_s", "2.0"
)
ntripsettings["ntripuser"] = self.saved_config.get(
"ntripclientuser_s", "anon"
"ntripclientuser_s", DEFAULT_USER
)
ntripsettings["ntrippassword"] = self.saved_config.get(
"ntripclientpassword_s", "password"
"ntripclientpassword_s", DEFAULT_PASSWORD
)
ntripsettings["ggainterval"] = self.saved_config.get(
"ntripclientggainterval_n", -1
Expand Down Expand Up @@ -530,15 +549,11 @@ def update_SPARTN_handler(self):
spartnsettings["port"] = self.saved_config.get(
"mqttclientport_n", SPARTN_OUTPORT
)
spartnsettings["clientid"] = self.saved_config.get(
"mqttclientid_s", self.mqttclientid
)
spartnsettings["clientid"] = self.saved_config.get("mqttclientid_s", "")
spartnsettings["region"] = self.saved_config.get(
"mgttclientregion_s", self.mqttclientregion
)
spartnsettings["mode"] = self.saved_config.get(
"mgttclientmode_n", self.mqttclientmode
"mgttclientregion_s", DEFAULT_REGION
)
spartnsettings["mode"] = self.saved_config.get("mgttclientmode_n", 0)
spartnsettings["topic_ip"] = self.saved_config.get("mgttclienttopicip_b", 1)
spartnsettings["topic_mga"] = self.saved_config.get(
"mgttclienttopicmga_b", 1
Expand All @@ -548,11 +563,11 @@ def update_SPARTN_handler(self):
)
spartnsettings["tlscrt"] = self.saved_config.get(
"mgttclienttlscrt_s",
path.join(Path.home(), f"device-{self.mqttclientid}-pp-cert.crt"),
path.join(Path.home(), "device-mqttclientid-pp-cert.crt"),
)
spartnsettings["tlskey"] = self.saved_config.get(
"mgttclienttlskey_s",
path.join(Path.home(), f"device-{self.mqttclientid}-pp-key.pem"),
path.join(Path.home(), "device-mqttclientid-pp-key.pem"),
)
spartnsettings["output"] = self.spartn_inqueue
self.spartn_handler.settings = spartnsettings
Expand Down Expand Up @@ -756,7 +771,11 @@ def on_ntrip_read(self, event): # pylint: disable=unused-argument

try:
raw_data, parsed_data = self.ntrip_inqueue.get(False)
if raw_data is not None and parsed_data is not None:
if (
raw_data is not None
and parsed_data is not None
and isinstance(raw_data, bytes)
):
if protocol(raw_data) == RTCM3_PROTOCOL:
if self.conn_status == CONNECTED:
self.gnss_outqueue.put(raw_data)
Expand Down
3 changes: 3 additions & 0 deletions src/pygpsclient/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,11 @@
CRLF = b"\x0d\x0a"
DDD = "DD.D"
DEFAULT_BUFSIZE = 4096
DEFAULT_PASSWORD = "password" # nosec
DEFAULT_PORT = 50010
DEFAULT_REGION = "eu"
DEFAULT_SERVER = "localhost"
DEFAULT_USER = "anon"
DIRNAME = path.dirname(__file__)
DISCONNECTED = 0
DLG = "dlg"
Expand Down
14 changes: 5 additions & 9 deletions src/pygpsclient/scatter_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
PLANAR = "Planar"
HAVERSINE = "Great Circle"
SQRT2 = 0.7071067811865476
PNTCOL = "orange"


class ScatterViewFrame(Frame):
Expand All @@ -50,7 +51,6 @@ def __init__(self, app, *args, **kwargs):

def_w, def_h = WIDGETU2

self.dot_col = "orange"
self.width = kwargs.get("width", def_w)
self.height = kwargs.get("height", def_h)
self.fg_col = FGCOL
Expand Down Expand Up @@ -148,17 +148,15 @@ def _draw_mean(self, lbl_font: font):
height = lbl_font.metrics("linespace")
lat = f"Lat {self.mean.lat:14.10f}"
lon = f"Lon {self.mean.lon:15.10f}"
self.canvas.create_text(5, 10, text=lat, fill=PNTCOL, font=lbl_font, anchor="w")
self.canvas.create_text(
5, 10, text=lat, fill=self.fg_col, font=lbl_font, anchor="w"
)
self.canvas.create_text(
5, 10 + height, text=lon, fill=self.fg_col, font=lbl_font, anchor="w"
5, 10 + height, text=lon, fill=PNTCOL, font=lbl_font, anchor="w"
)
self.canvas.create_text(
5,
10 + height * 2,
text=self._calc,
fill=self.fg_col,
fill=PNTCOL,
font=lbl_font,
anchor="w",
)
Expand Down Expand Up @@ -220,9 +218,7 @@ def draw_point(self, center: Point, position: Point):
center_y = self.height / 2
pt_x = center_x + pos_x
pt_y = center_y - pos_y
self.canvas.create_circle(
pt_x, pt_y, 2, fill=self.dot_col, outline=self.dot_col
)
self.canvas.create_circle(pt_x, pt_y, 2, fill=PNTCOL, outline=PNTCOL)

def _ave_pos(self):
"""
Expand Down
14 changes: 4 additions & 10 deletions src/pygpsclient/serverconfig_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
from pyubx2 import UBXMessage, llh2ecef

from pygpsclient.globals import (
DEFAULT_PASSWORD,
DEFAULT_USER,
DISCONNECTED,
ICON_CONTRACT,
ICON_EXPAND,
Expand Down Expand Up @@ -406,18 +408,10 @@ def reset(self):
self.fixedlon.set(self._saved_config.get("ntripcasterfixedlon_f", 0))
self.fixedalt.set(self._saved_config.get("ntripcasterfixedalt_f", 0))
self.user.set(
self._saved_config.get(
"ntripcasteruser_s",
self._saved_config.get("ntripcasteruser", self.__app.ntripcaster_user),
)
self._saved_config.get("ntripcasteruser_s", DEFAULT_USER),
)
self.password.set(
self._saved_config.get(
"ntripcasterpassword_s",
self._saved_config.get(
"ntripcasterpassword", self.__app.ntripcaster_password
),
)
self._saved_config.get("ntripcasterpassword_s", DEFAULT_PASSWORD),
)
self.clients = 0
self._sock_port_temp = self.sock_port.get()
Expand Down
22 changes: 5 additions & 17 deletions src/pygpsclient/settings_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -882,27 +882,15 @@ def config(self) -> dict:
),
),
# Manually edited config settings
# (cater for older config file element names without suffices)
"mapupdateinterval_n": self.__app.saved_config.get(
"mapupdateinterval_n",
self.__app.saved_config.get(
"mapupdateinterval", MAP_UPDATE_INTERVAL
),
"mapupdateinterval_n", MAP_UPDATE_INTERVAL
),
"defaultport_s": self.__app.saved_config.get(
"defaultport_s",
self.__app.saved_config.get("defaultport", RCVR_CONNECTION),
),
"mqapikey_s": self.__app.saved_config.get(
"mqapikey_s",
self.__app.saved_config.get("mqapikey", self.__app.mqapikey),
),
"colortags_l": self.__app.saved_config.get(
"colortags_l", self.__app.saved_config.get("colortags", [])
),
"ubxpresets_l": self.__app.saved_config.get(
"ubxpresets_l", self.__app.saved_config.get("ubxpresets", [])
"defaultport_s", RCVR_CONNECTION
),
"mqapikey_s": self.__app.saved_config.get("mqapikey_s", ""),
"colortags_l": self.__app.saved_config.get("colortags_l", []),
"ubxpresets_l": self.__app.saved_config.get("ubxpresets_l", []),
}
return config
except (KeyError, ValueError, TypeError, TclError) as err:
Expand Down

0 comments on commit c9a4117

Please sign in to comment.