Skip to content

Commit

Permalink
Merge pull request #176 from semuconsulting/RC-1.2.50
Browse files Browse the repository at this point in the history
Rc 1.2.50
  • Loading branch information
semuadmin authored Jan 22, 2025
2 parents ec4ee76 + ceb0fb5 commit 7309774
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 47 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.2.49",
"moduleversion": "1.2.50",
}
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pyubx2
[Graphical Client](#gui) |
[Author & License](#author)

`pyubx2` is an original Python 3 parser for the UBX ©, NMEA 0183 © and RTCM3 © protocols. UBX is a proprietary binary protocol implemented on u-blox ™ GNSS/GPS receiver modules.
`pyubx2` is an original Python 3 parser for the UBX © protocol. UBX is a proprietary binary protocol implemented on u-blox ™ GNSS receiver modules. `pyubx2` can also parse NMEA 0183 © and RTCM3 © protocols via the underlying [`pynmeagps`](https://github.com/semuconsulting/pynmeagps) and [`pyrtcm`](https://github.com/semuconsulting/pyrtcm) packages from the same author - hence it covers all the protocols that u-blox GNSS receivers are capable of outputting.

The `pyubx2` homepage is located at [https://github.com/semuconsulting/pyubx2](https://github.com/semuconsulting/pyubx2).

Expand Down Expand Up @@ -49,9 +49,7 @@ Contributions welcome - please refer to [CONTRIBUTING.MD](https://github.com/sem
[![PyPI version](https://img.shields.io/pypi/v/pyubx2.svg?style=flat)](https://pypi.org/project/pyubx2/)
![PyPI downloads](https://img.shields.io/pypi/dm/pyubx2.svg?style=flat)

`pyubx2` is compatible with Python 3.9 - 3.13 and has no third-party library dependencies.

In the following, `python3` & `pip` refer to the Python 3 executables. You may need to substitute `python` for `python3`, depending on your particular environment (*on Windows it's generally `python`*).
`pyubx2` is compatible with Python 3.9 - 3.13. In the following, `python3` & `pip` refer to the Python 3 executables. You may need to substitute `python` for `python3`, depending on your particular environment (*on Windows it's generally `python`*).

The recommended way to install the latest version of `pyubx2` is with [pip](http://pypi.python.org/pypi/pip/):

Expand Down
13 changes: 12 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
# pyubx2 Release Notes

### RELEASE 1.2.50

FIXES:

1. Fix typos in AID-ALPSRV message definitions - thanks to @wheirman for contribution.
1. Add alternate AID-ALPSRV Request and Send GET message types.

ENHANCEMENTS:

1. Minor internal streamlining of helper methods.

### RELEASE 1.2.49

ENHANCEMENTS:

1. Enhance pyubx2.config_set() exception handling - addresses #173.
1. Enhance val2bytes() helper method exception handling - addresses #173.

### RELEASE 1.2.48

Expand Down
9 changes: 6 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[build-system]
requires = ["setuptools>=66.0.0", "wheel"]
build-backend = "setuptools.build_meta:__legacy__"
build-backend = "setuptools.build_meta"

[project]
name = "pyubx2"
dynamic = ["version"]
authors = [{ name = "semuadmin", email = "[email protected]" }]
maintainers = [{ name = "semuadmin", email = "[email protected]" }]
description = "UBX protocol parser and generator"
version = "1.2.49"
license = { file = "LICENSE" }
readme = "README.md"
requires-python = ">=3.9"
Expand All @@ -33,7 +33,7 @@ classifiers = [
"Topic :: Scientific/Engineering :: GIS",
]

dependencies = ["pynmeagps >= 1.0.43", "pyrtcm >= 1.1.2"]
dependencies = ["pynmeagps >= 1.0.44", "pyrtcm >= 1.1.4"]

[project.urls]
homepage = "https://github.com/semuconsulting/pyubx2"
Expand All @@ -54,6 +54,9 @@ test = [
"sphinx-rtd-theme",
]

[tool.setuptools.dynamic]
version = { attr = "pyubx2._version.__version__" }

[tool.black]
target-version = ['py39']

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

__version__ = "1.2.49"
__version__ = "1.2.50"
47 changes: 16 additions & 31 deletions src/pyubx2/ubxhelpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,27 +285,18 @@ def val2bytes(val, att: str) -> bytes:
except KeyError as err:
raise ube.UBXTypeError(f"Unknown attribute type {att}") from err

atts = attsiz(att)
if atttyp(att) == "X": # byte
valb = val
elif atttyp(att) == "C": # char
if isinstance(val, str):
valb = val.encode("utf-8", "backslashreplace")
else: # byte
valb = val
elif atttyp(att) in ("E", "L", "U"): # unsigned integer
valb = val.to_bytes(atts, byteorder="little", signed=False)
valb = val.encode("utf-8", "backslashreplace") if isinstance(val, str) else val
elif atttyp(att) in ("E", "I", "L", "U"): # integer
valb = val.to_bytes(attsiz(att), byteorder="little", signed=atttyp(att) == "I")
elif atttyp(att) == "R": # floating point
valb = struct.pack("<f" if attsiz(att) == 4 else "<d", float(val))
elif atttyp(att) == "A": # array of unsigned integers
atts = attsiz(att)
valb = b""
for i in range(atts):
for i in range(attsiz(att)):
valb += val[i].to_bytes(1, byteorder="little", signed=False)
elif atttyp(att) == "I": # signed integer
valb = val.to_bytes(atts, byteorder="little", signed=True)
elif att == ubt.R4: # single precision floating point
valb = struct.pack("<f", float(val))
elif att == ubt.R8: # double precision floating point
valb = struct.pack("<d", float(val))
return valb


Expand All @@ -325,19 +316,14 @@ def bytes2val(valb: bytes, att: str) -> object:
val = valb.decode("utf-8", "backslashreplace")
elif atttyp(att) in ("X", "C"):
val = valb
elif atttyp(att) in ("E", "L", "U"): # unsigned integer
val = int.from_bytes(valb, "little", signed=False)
elif atttyp(att) in ("E", "I", "L", "U"): # integer
val = int.from_bytes(valb, byteorder="little", signed=atttyp(att) == "I")
elif atttyp(att) == "R": # floating point
val = struct.unpack("<f" if attsiz(att) == 4 else "<d", valb)[0]
elif atttyp(att) == "A": # array of unsigned integers
atts = attsiz(att)
val = []
for i in range(atts):
for i in range(attsiz(att)):
val.append(valb[i])
elif atttyp(att) == "I": # signed integer
val = int.from_bytes(valb, "little", signed=True)
elif att == ubt.R4: # single precision floating point
val = struct.unpack("<f", valb)[0]
elif att == ubt.R8: # double precision floating point
val = struct.unpack("<d", valb)[0]
else:
raise ube.UBXTypeError(f"Unknown attribute type {att}")
return val
Expand Down Expand Up @@ -380,9 +366,7 @@ def msgclass2bytes(msgclass: int, msgid: int) -> bytes:
"""

msgclass = val2bytes(msgclass, ubt.U1)
msgid = val2bytes(msgid, ubt.U1)
return (msgclass, msgid)
return (val2bytes(msgclass, ubt.U1), val2bytes(msgid, ubt.U1))


def msgstr2bytes(msgclass: str, msgid: str) -> bytes:
Expand All @@ -398,9 +382,10 @@ def msgstr2bytes(msgclass: str, msgid: str) -> bytes:
"""

try:
clsid = key_from_val(ubt.UBX_CLASSES, msgclass)
msgid = key_from_val(ubt.UBX_MSGIDS, msgid)[1:2]
return (clsid, msgid)
return (
key_from_val(ubt.UBX_CLASSES, msgclass),
key_from_val(ubt.UBX_MSGIDS, msgid)[1:2],
)
except KeyError as err:
raise ube.UBXMessageError(
f"Undefined message, class {msgclass}, id {msgid}"
Expand Down
19 changes: 16 additions & 3 deletions src/pyubx2/ubxtypes_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,16 +153,29 @@
"reserved3": U1,
"reserved4": U1,
},
"ALP-ALPSRV": {
"AID-ALPSRV-REQ": { # ALP client requests AlmanacPlus data from server
"idSize": U1,
"type": U1,
"type": U1, # must not be 0xFF
"ofs": U2,
"size": U2,
"fileId": U2,
"dataSize": U2,
"id1": U1,
"id2": U1,
"id3": U1,
"id3": U4,
},
"AID-ALPSRV-SEND": { # ALP client sends AlmanacPlus data to server
"idSize": U1,
"type": U1, # must be 0xFF
"ofs": U2,
"size": U2,
"fileId": U2,
"group": (
"size",
{
"data": U2,
},
),
},
# ********************************************************************
# Configuration Input Messages: i.e. Set Dynamic Model, Set DOP Mask, Set Baud Rate, etc..
Expand Down
4 changes: 2 additions & 2 deletions src/pyubx2/ubxtypes_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
},
),
},
"ALP-ALPSRV": {
"AID-ALPSRV": { # ALP server sends AlmanacPlus data to client
"idSize": U1,
"type": U1,
"ofs": U2,
Expand All @@ -62,7 +62,7 @@
"dataSize": U2,
"id1": U1,
"id2": U1,
"id3": U1,
"id3": U4,
"group": (
"dataSize",
{
Expand Down
25 changes: 25 additions & 0 deletions src/pyubx2/ubxvariants.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,30 @@ def get_secsig_dict(**kwargs) -> dict:
return UBX_PAYLOADS_GET["SEC-SIG-V2"]


def get_alpsrv_dict(**kwargs) -> dict:
"""
Select appropriate AID-ALPSRV GET payload definition by checking
value of 'type' attribute (2nd byte of payload).
:param kwargs: optional payload key/value pairs
:return: dictionary representing payload definition
:rtype: dict
"""

if "type" in kwargs:
typ = val2bytes(kwargs["type"], U1)
elif "payload" in kwargs:
typ = kwargs["payload"][1:2]
else:
raise UBXMessageError(
"AID-ALPSRV GET message definitions must include type or payload keyword"
)
if typ == b"\xff":
return UBX_PAYLOADS_GET["AID-ALPSRV-SEND"]
return UBX_PAYLOADS_GET["AID-ALPSRV-REQ"]


VARIANTS = {
POLL: {b"\x06\x31": get_cfgtp5_dict}, # CFG-TP5
SET: {
Expand All @@ -304,6 +328,7 @@ def get_secsig_dict(**kwargs) -> dict:
b"\x06\x06": get_cfgdat_dict, # CFG-DAT
},
GET: {
b"\x0b\x32": get_alpsrv_dict, # AID-ALPSRV
b"\x13\x21": get_mga_dict, # MGA FLASH
b"\x13\x60": get_mga_dict, # MGA ACK NAK
b"\x02\x72": get_rxmpmp_dict, # RXM-PMP
Expand Down
58 changes: 57 additions & 1 deletion tests/test_specialcases.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
@author: semuadmin
"""

# pylint: disable=line-too-long, invalid-name, missing-docstring, no-member

import unittest

from pyubx2 import UBXMessage, UBXReader, SET, GET
from pyubx2 import UBXMessage, UBXReader, SET, GET, UBXMessageError
import pyubx2.ubxtypes_configdb as ubxcdb


Expand Down Expand Up @@ -379,6 +380,61 @@ def testUnknownID(self): # test for known class, unknown id
# print(msg)
self.assertEqual(str(msg), EXPECTED_RESULT)

def testAIDALPSRVREQ(self):
EXPECTED_RESULT = "<UBX(AID-ALPSRV, idSize=60, type=253, ofs=23, size=24, fileId=12, dataSize=6, id1=1, id2=2, id3=3)>"
msg = UBXMessage(
"AID",
"AID-ALPSRV",
GET,
idSize=0x3C,
type=0xFD,
ofs=23,
size=24,
fileId=12,
dataSize=6,
id1=1,
id2=2,
id3=3,
)
# print(msg)
self.assertEqual(str(msg), EXPECTED_RESULT)

def testAIDALPSRVSEND(self):
EXPECTED_RESULT = "<UBX(AID-ALPSRV, idSize=60, type=255, ofs=23, size=6, fileId=12, data_01=1, data_02=2, data_03=3, data_04=0, data_05=0, data_06=0)>"
msg = UBXMessage(
"AID",
"AID-ALPSRV",
GET,
idSize=0x3C,
type=0xFF,
ofs=23,
size=6,
fileId=12,
data_01=1,
data_02=2,
data_03=3,
)
# print(msg)
self.assertEqual(str(msg), EXPECTED_RESULT)

def testAIDALPSRVNOTYPE(self):
with self.assertRaisesRegex(
UBXMessageError,
"AID-ALPSRV GET message definitions must include type or payload keyword",
):
msg = UBXMessage(
"AID",
"AID-ALPSRV",
GET,
idSize=0x3C,
ofs=23,
size=6,
fileId=12,
data_01=1,
data_02=2,
data_03=3,
)


if __name__ == "__main__":
# import sys;sys.argv = ['', 'Test.testName']
Expand Down

0 comments on commit 7309774

Please sign in to comment.