Skip to content

Commit

Permalink
Merge pull request #63 from semuconsulting/RC-1.0.25
Browse files Browse the repository at this point in the history
Rc 1.0.25
  • Loading branch information
semuadmin committed Apr 26, 2024
2 parents 466134e + 5af087c commit 665efc6
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 41 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": "${workspaceFolderBasename}",
"distname": "${workspaceFolderBasename}",
"moduleversion": "1.0.24"
"moduleversion": "1.0.25"
}
7 changes: 7 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# pygnssutils Release Notes

### RELEASE 1.0.25

ENHANCEMENTS:

1. Add SPARTN payload decrypt option to gnssnmqqclient.
1. Minor internal streamlining.

### RELEASE 1.0.24

FIXES:
Expand Down
64 changes: 48 additions & 16 deletions examples/gnssmqttclient_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
Illustration of GNSSMQTTClient class.
Reads selected topics from MQTT server and outputs raw data to log file.
Reads selected topics from MQTT server and outputs data to terminal or log file.
Expects user's Client ID to be set up in environment variable MQTTCLIENTID.
Expects user's *.crt and *.pem files to be placed in user's home directory.
- ClientID can be provided as keyword argument or set in environment variable MQTTCLIENTID.
- SPARTN decryption key can be provided as keyword argument or set in environment variable MQTTKEY.
- Expects user's *.crt and *.pem files to be placed in user's home directory.
Usage:
python3 gnssmqttclient_example.py clientid="your-client-id" outfile="spartn_ip.log" decode=1 decryptkey=yourkey decryptbasedate=yourbasedate
Created on 5 Jun 2023
Expand All @@ -15,17 +20,33 @@
:license: BSD 3-Clause
"""

from time import sleep
from threading import Event
from datetime import datetime
from os import getenv, path
from pathlib import Path
from pygnssutils import GNSSMQTTClient, SPARTN_PPSERVER, OUTPORT_SPARTN
from sys import argv
from threading import Event
from time import sleep

from pygnssutils import OUTPORT_SPARTN, SPARTN_PPSERVER, GNSSMQTTClient

clientid = getenv("MQTTCLIENTID") # or hard code here

try:
with open("spartn_ip.log", "wb") as outfile:
kwargs = {
def main(**kwargs):
"""
Main routine.
"""

clientid = kwargs.get("clientid", getenv("MQTTCLIENTID", default="enter-client-id"))
outfile = kwargs.get("outfile", None)
decode = kwargs.get("decode", 0)
decryptkey = kwargs.get("decryptkey", getenv("MQTTKEY", default=None))
decryptbasedate = kwargs.get("decryptbasedate", datetime.now())
stream = None

if outfile is not None:
stream = open(outfile, "wb")

try:
settings = {
"server": SPARTN_PPSERVER, # Thingstream MQTT server
"port": OUTPORT_SPARTN, # 8883
"clientid": clientid,
Expand All @@ -35,16 +56,27 @@
"topic_ip": 1, # SPARTN correction data (SPARTN OCB, HPAC & GAD messages)
"topic_mga": 0, # Assist Now ephemera data (UBX MGA-EPH-* messages)
"topic_key": 0, # SPARTN decryption keys (UBX RXM_SPARTNKEY messages)
"output": outfile,
"decode": decode,
"decryptkey": decryptkey,
"decryptbasedate": decryptbasedate,
"output": stream,
"errevent": Event(),
}
with GNSSMQTTClient(None, **kwargs) as gsc:
streaming = gsc.start(**kwargs)
with GNSSMQTTClient(None, **settings) as gsc:
streaming = gsc.start(**settings)
while (
streaming and not kwargs["errevent"].is_set()
streaming and not settings["errevent"].is_set()
): # run until error or user presses CTRL-C
sleep(3)
sleep(3)

except (KeyboardInterrupt, TimeoutError):
gsc.stop()
except (KeyboardInterrupt, TimeoutError):
gsc.stop()
finally:
if outfile is not None:
stream.close()


if __name__ == "__main__":

main(**dict(arg.split("=") for arg in argv[1:]))
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "pygnssutils"
authors = [{ name = "semuadmin", email = "[email protected]" }]
maintainers = [{ name = "semuadmin", email = "[email protected]" }]
description = "GNSS Command Line Utilities"
version = "1.0.24"
version = "1.0.25"
license = { file = "LICENSE" }
readme = "README.md"
requires-python = ">=3.8"
Expand Down Expand Up @@ -37,7 +37,7 @@ dependencies = [
"certifi>=2024.0.0",
"paho-mqtt>=1.6.0",
"pyserial>=3.5",
"pyspartn>=0.2.1",
"pyspartn>=0.3.2",
"pyubx2>=1.2.39",
]

Expand Down
2 changes: 1 addition & 1 deletion src/pygnssutils/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
:license: BSD 3-Clause
"""

__version__ = "1.0.24"
__version__ = "1.0.25"
9 changes: 9 additions & 0 deletions src/pygnssutils/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,12 @@
(36.30, 128.20): "kr",
(39.20, -096.60): "us",
}

HTTPCODES = {
200: "OK",
400: "Bad Request",
401: "Unauthorized",
403: "Forbidden",
404: "Not Found",
405: "Method Not Allowed",
}
36 changes: 35 additions & 1 deletion src/pygnssutils/gnssmqttclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ def __init__(self, app=None, **kwargs):
"topic_key": 1,
"tlscrt": path.join(Path.home(), f"device-{clientid}-pp-cert.crt"),
"tlskey": path.join(Path.home(), f"device-{clientid}-pp-key.pem"),
"decode": 0,
"decryptkey": getenv("MQTTKEY", default=None),
"decryptbasedate": datetime.now(),
"output": None,
}

Expand Down Expand Up @@ -171,6 +174,9 @@ def start(self, **kwargs) -> int:
"topic_key",
"tlscrt",
"tlskey",
"decode",
"decryptkey",
"decryptbasedate",
"output",
]:
if kwarg in kwargs:
Expand Down Expand Up @@ -251,6 +257,9 @@ def _run(
"output": settings["output"],
"topics": topics,
"app": app,
"decode": settings["decode"],
"key": settings["decryptkey"],
"basedate": settings["decryptbasedate"],
}

try:
Expand Down Expand Up @@ -392,7 +401,12 @@ def do_write(userdata: dict, raw: bytes, parsed: object):
parsed = MQTTMessage(msg.topic, msg.payload)
do_write(userdata, msg.payload, parsed)
else: # SPARTN protocol message
spr = SPARTNReader(BytesIO(msg.payload))
spr = SPARTNReader(
BytesIO(msg.payload),
decode=userdata["decode"],
key=userdata["key"],
basedate=userdata["basedate"],
)
try:
for raw, parsed in spr:
do_write(userdata, raw, parsed)
Expand Down Expand Up @@ -554,6 +568,26 @@ def main():
help="Fully-qualified path to TLS key (*.pem)",
default=path.join(Path.home(), f"device-{clientid}-pp-key.pem"),
)
ap.add_argument(
"--decode",
required=False,
help="Decode payload?",
type=int,
choices=[0, 1],
default=0,
)
ap.add_argument(
"--decryptkey",
required=False,
help="Decryption key for encrypted payloads",
default=getenv("MQTTKEY", default=None),
)
ap.add_argument(
"--decryptbasedate",
required=False,
help="Decryption basedate for encrypted payloads",
default=datetime.now(),
)
ap.add_argument(
"--output",
required=False,
Expand Down
26 changes: 6 additions & 20 deletions src/pygnssutils/socket_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from threading import Event, Thread

from pygnssutils._version import __version__ as VERSION
from pygnssutils.globals import CONNECTED, DISCONNECTED
from pygnssutils.globals import CONNECTED, DISCONNECTED, HTTPCODES
from pygnssutils.helpers import ipprot2int

# from pygpsclient import version as PYGPSVERSION
Expand Down Expand Up @@ -280,17 +280,12 @@ def handle(self):
if self.server.ntripmode: # NTRIP server mode
self.data = self.request.recv(BUFSIZE)
resptype, resp = self._process_ntrip_request(self.data)
if resptype == SRT: # sourcetable request
self.wfile.write(resp)
self.wfile.flush()
elif resptype == RTCM: # RTCM3 data request
self.wfile.write(resp)
if resptype is not None:
self.wfile.write(resp) # send HTTP response
self.wfile.flush()
if resptype == RTCM: # RTCM3 data request
while True: # send continuous RTCM data stream
self._write_from_mq()
elif resptype == BAD: # unauthorised
self.wfile.write(resp)
self.wfile.flush()
else:
break

Expand Down Expand Up @@ -345,7 +340,7 @@ def _process_ntrip_request(self, data: bytes) -> bytes:
if validmp: # respond by opening RTCM3 stream
http = self._format_data()
return RTCM, bytes(http, "UTF-8")
return None
return None, None

def _format_sourcetable(self) -> str:
"""
Expand Down Expand Up @@ -405,20 +400,11 @@ def _format_http_header(self, code: int = 200) -> str:
:rtype: str
"""

codes = {
200: "OK",
400: "Bad Request",
401: "Unauthorized",
403: "Forbidden",
404: "Not Found",
405: "Method Not Allowed",
}

dat = datetime.now(timezone.utc)
server_date = dat.strftime("%d %b %Y")
http_date = dat.strftime("%a, %d %b %Y %H:%M:%S %Z")
header = (
f"HTTP/1.1 {code} {codes[code]}\r\n"
f"HTTP/1.1 {code} {HTTPCODES[code]}\r\n"
+ "Ntrip-Version: Ntrip/2.0\r\n"
+ "Ntrip-Flags: \r\n"
+ f"Server: pygnssutils_NTRIP_Caster_{VERSION}/of:{server_date}\r\n"
Expand Down

0 comments on commit 665efc6

Please sign in to comment.