From 526a359844c5ceaa2710b73fb73a73fa498c4ad1 Mon Sep 17 00:00:00 2001
From: semuadmin <28569967+semuadmin@users.noreply.github.com>
Date: Thu, 8 Feb 2024 11:38:52 +0000
Subject: [PATCH 1/6] add val2sphp helper method
---
.vscode/settings.json | 2 +-
RELEASE_NOTES.md | 8 +++++++-
examples/f9p_basestation.py | 40 +++++++++++++++----------------------
pyproject.toml | 4 ++--
src/pyubx2/_version.py | 2 +-
src/pyubx2/ubxhelpers.py | 22 +++++++++++++++++++-
tests/test_static.py | 38 +++++++++++++++++++++--------------
7 files changed, 71 insertions(+), 45 deletions(-)
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 01f803a..dd60674 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -4,5 +4,5 @@
"editor.formatOnSave": true,
"modulename": "${workspaceFolderBasename}",
"distname": "${workspaceFolderBasename}",
- "moduleversion": "1.2.37",
+ "moduleversion": "1.2.38",
}
\ No newline at end of file
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 1e872ce..a54eba4 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,6 +1,12 @@
# pyubx2 Release Notes
-### RELEASE CANDIDATE 1.2.37
+### RELEASE CANDIDATE 1.2.38
+
+CHANGES:
+
+1. Add val2sphp helper method.
+
+### RELEASE 1.2.37
CHANGES:
diff --git a/examples/f9p_basestation.py b/examples/f9p_basestation.py
index c1b0c95..4977856 100644
--- a/examples/f9p_basestation.py
+++ b/examples/f9p_basestation.py
@@ -17,13 +17,13 @@
:license: BSD 3-Clause
"""
-from math import trunc
from serial import Serial
-from pyubx2 import UBXMessage
+
+from pyubx2 import UBXMessage, val2sphp
TMODE_SVIN = 1
TMODE_FIXED = 2
-
+SHOW_PRESET = True # hide or show PyGPSClient preset string
def send_msg(serial_out: Serial, ubx: UBXMessage):
"""
@@ -105,17 +105,8 @@ def config_fixed(acc_limit: int, lat: float, lon: float, height: float) -> UBXMe
layers = 1
transaction = 0
acc_limit = int(round(acc_limit / 0.1, 0))
-
- # separate standard and high precision parts of lat / lon
- # and apply scaling factors
- lat_7dp = trunc(lat * 1e7) / 1e7
- lat_hp = lat - lat_7dp
- lat = int(round(lat_7dp / 1e-7, 0))
- lat_hp = int(round(lat_hp / 1e-9, 0))
- lon_7dp = trunc(lon * 1e7) / 1e7
- lon_hp = lon - lon_7dp
- lon = int(round(lon_7dp / 1e-7, 0))
- lon_hp = int(round(lon_hp / 1e-9, 0))
+ lats, lath = val2sphp(lat)
+ lons, lonh = val2sphp(lon)
height = int(height)
cfg_data = [
@@ -124,10 +115,10 @@ def config_fixed(acc_limit: int, lat: float, lon: float, height: float) -> UBXMe
("CFG_TMODE_FIXED_POS_ACC", acc_limit),
("CFG_TMODE_HEIGHT_HP", 0),
("CFG_TMODE_HEIGHT", height),
- ("CFG_TMODE_LAT", lat),
- ("CFG_TMODE_LAT_HP", lat_hp),
- ("CFG_TMODE_LON", lon),
- ("CFG_TMODE_LON_HP", lon_hp),
+ ("CFG_TMODE_LAT", lats),
+ ("CFG_TMODE_LAT_HP", lath),
+ ("CFG_TMODE_LON", lons),
+ ("CFG_TMODE_LON_HP", lonh),
]
ubx = UBXMessage.config_set(layers, transaction, cfg_data)
@@ -147,21 +138,22 @@ def config_fixed(acc_limit: int, lat: float, lon: float, height: float) -> UBXMe
PORT_TYPE = "USB" # choose from "USB", "UART1", "UART2"
BAUD = 38400
TIMEOUT = 5
- SHOW_PRESET = True # hide or show PyGPSClient preset string
- TMODE = TMODE_SVIN # "TMODE_SVIN" or 1 = Survey-In, "TMODE_FIXED" or 2 = Fixed
+
+ TMODE = TMODE_FIXED # "TMODE_SVIN" or 1 = Survey-In, "TMODE_FIXED" or 2 = Fixed
ACC_LIMIT = 200 # accuracy in mm
- # only used if TMODE = 1 ...
+ # only used if TMODE = SVIN ...
SVIN_MIN_DUR = 90 # seconds
- # only used if TMODE = 2 ...
- ARP_LAT = 37.012345678
- ARP_LON = -115.012345678
+ # only used if TMODE = FIXED ...
+ ARP_LAT = 12.123456789
+ ARP_LON = -115.987654321
ARP_HEIGHT = 137000 # cm
print(f"Configuring receiver on {PORT} @ {BAUD:,} baud.\n")
with Serial(PORT, BAUD, timeout=TIMEOUT) as stream:
+
# configure RTCM3 outputs
msg = config_rtcm(PORT_TYPE)
send_msg(stream, msg)
diff --git a/pyproject.toml b/pyproject.toml
index 6442c63..3970fa3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,7 +7,7 @@ name = "pyubx2"
authors = [{ name = "semuadmin", email = "semuadmin@semuconsulting.com" }]
maintainers = [{ name = "semuadmin", email = "semuadmin@semuconsulting.com" }]
description = "UBX protocol parser and generator"
-version = "1.2.37"
+version = "1.2.38"
license = { file = "LICENSE" }
readme = "README.md"
requires-python = ">=3.8"
@@ -87,7 +87,7 @@ disable = """
[tool.pytest.ini_options]
minversion = "7.0"
-addopts = "--cov --cov-report term-missing --cov-fail-under 95"
+addopts = "--cov --cov-report html --cov-fail-under 95"
pythonpath = ["src"]
testpaths = ["tests"]
diff --git a/src/pyubx2/_version.py b/src/pyubx2/_version.py
index 6fb0f67..cdf05ff 100644
--- a/src/pyubx2/_version.py
+++ b/src/pyubx2/_version.py
@@ -8,4 +8,4 @@
:license: BSD 3-Clause
"""
-__version__ = "1.2.37"
+__version__ = "1.2.38"
diff --git a/src/pyubx2/ubxhelpers.py b/src/pyubx2/ubxhelpers.py
index bddaa2c..8f6a9aa 100644
--- a/src/pyubx2/ubxhelpers.py
+++ b/src/pyubx2/ubxhelpers.py
@@ -13,7 +13,7 @@
import struct
from datetime import datetime, timedelta
-from math import cos, pi, sin
+from math import cos, pi, sin, trunc
from pynmeagps.nmeatypes_core import NMEA_HDR
@@ -498,3 +498,23 @@ def escapeall(val: bytes) -> str:
"""
return "b'{}'".format("".join(f"\\x{b:02x}" for b in val))
+
+
+def val2sphp(val: float, scale: float = 1e-7) -> tuple:
+ """
+ Convert a float value into separate standard and high precisions components,
+ multiplied by a scaling factor to render them as integers, as required by some
+ CFG and NAV messages.
+
+ e.g. 48.123456789 becomes (481234567, 89)
+
+ :param float val: value as float
+ :param float scale: scaling factor e.g. 1e-7
+ :return: tuple of (standard precision, high precision)
+ :rtype: tuple
+ """
+
+ val = val / scale
+ val_sp = trunc(val)
+ val_hp = round((val - val_sp) * 100)
+ return val_sp, val_hp
diff --git a/tests/test_static.py b/tests/test_static.py
index 02fae30..1d48d1e 100644
--- a/tests/test_static.py
+++ b/tests/test_static.py
@@ -11,30 +11,30 @@
import os
import unittest
-import unittest
-from pyubx2 import UBXMessage, UBXReader, UBX_CLASSES, POLL
import pyubx2.ubxtypes_core as ubt
+from pyubx2 import POLL, UBX_CLASSES, UBXMessage, UBXReader
from pyubx2.ubxhelpers import (
+ att2idx,
+ att2name,
+ bytes2val,
calc_checksum,
- isvalid_checksum,
- key_from_val,
+ cel2cart,
+ cfgkey2name,
+ cfgname2key,
+ dop2str,
+ escapeall,
get_bits,
- itow2utc,
gnss2str,
- dop2str,
gpsfix2str,
+ hextable,
+ isvalid_checksum,
+ itow2utc,
+ key_from_val,
msgstr2bytes,
- val2bytes,
- bytes2val,
- cfgkey2name,
- cfgname2key,
protocol,
- hextable,
- att2idx,
- att2name,
- cel2cart,
- escapeall,
+ val2bytes,
+ val2sphp,
)
@@ -276,6 +276,14 @@ def testescapeall(self):
print(res)
self.assertEqual(res, EXPECTED_RESULT)
+ def testval2sphp(self):
+ res = val2sphp(100.123456789)
+ self.assertEqual(res, (1001234567,89))
+ res = val2sphp(-13.987654321)
+ self.assertEqual(res, (-139876543,-21))
+ res = val2sphp(5.9876543)
+ self.assertEqual(res, (59876543,0))
+
if __name__ == "__main__":
# import sys;sys.argv = ['', 'Test.testName']
From 6084e5e53d92dcbe1f438a4ddfb9a5dd6cd676dc Mon Sep 17 00:00:00 2001
From: semuadmin <28569967+semuadmin@users.noreply.github.com>
Date: Fri, 9 Feb 2024 04:23:07 +0000
Subject: [PATCH 2/6] add utc2itow helper
---
RELEASE_NOTES.md | 3 ++-
src/pyubx2/ubxhelpers.py | 25 ++++++++++++++++++++++---
tests/test_static.py | 8 +++++++-
3 files changed, 31 insertions(+), 5 deletions(-)
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index a54eba4..f19e430 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -4,7 +4,8 @@
CHANGES:
-1. Add val2sphp helper method.
+1. Add val2sphp helper method to convert high precision (9dp) coordinate to separate standard and high precision components, as required by some CFG and NAV messages.
+1. Add utc2itow helper method to convert utc datetime to GPS week number and time of week.
### RELEASE 1.2.37
diff --git a/src/pyubx2/ubxhelpers.py b/src/pyubx2/ubxhelpers.py
index 8f6a9aa..9956799 100644
--- a/src/pyubx2/ubxhelpers.py
+++ b/src/pyubx2/ubxhelpers.py
@@ -22,6 +22,10 @@
import pyubx2.ubxtypes_core as ubt
from pyubx2.ubxtypes_core import GNSSLIST, UBX_HDR
+EPOCH0 = datetime(1980, 1, 6) # EPOCH start date
+LEAPOFFSET = 18 # leap year offset in seconds, valid as from 1/1/2017
+SIW = 604800 # seconds in week = 3600*24*7
+
def att2idx(att: str) -> int:
"""
@@ -123,18 +127,33 @@ def attsiz(att: str) -> int:
def itow2utc(itow: int) -> datetime.time:
"""
Convert GPS Time Of Week to UTC time
- (UTC = GPS - 18 seconds; correct as from 1/1/2017).
- :param int itow: GPS Time Of Week
+ :param int itow: GPS Time Of Week in milliseconds
:return: UTC time hh.mm.ss
:rtype: datetime.time
"""
- utc = datetime(1980, 1, 6) + timedelta(seconds=(itow / 1000) - 18)
+ utc = EPOCH0 + timedelta(seconds=(itow / 1000) - LEAPOFFSET)
return utc.time()
+def utc2itow(utc: datetime) -> tuple:
+ """
+ Convert UTC datetime to GPS Week Number, Time Of Week
+
+ :param datetime utc: datetime
+ :return: GPS Week Number, Time of Week in milliseconds
+ :rtype: tuple
+
+ """
+
+ wno = int((utc - EPOCH0).total_seconds() / SIW)
+ sow = EPOCH0 + timedelta(seconds=wno * SIW)
+ itow = int(((utc - sow).total_seconds() + LEAPOFFSET) * 1000)
+ return wno, itow
+
+
def gpsfix2str(fix: int) -> str:
"""
Convert GPS fix integer to descriptive string.
diff --git a/tests/test_static.py b/tests/test_static.py
index 1d48d1e..7696ea1 100644
--- a/tests/test_static.py
+++ b/tests/test_static.py
@@ -11,7 +11,7 @@
import os
import unittest
-
+from datetime import datetime
import pyubx2.ubxtypes_core as ubt
from pyubx2 import POLL, UBX_CLASSES, UBXMessage, UBXReader
from pyubx2.ubxhelpers import (
@@ -33,6 +33,7 @@
key_from_val,
msgstr2bytes,
protocol,
+ utc2itow,
val2bytes,
val2sphp,
)
@@ -162,6 +163,11 @@ def testitow2utc(self):
res = str(itow2utc(387092000))
self.assertEqual(res, "11:31:14")
+ def testutc2itow(self):
+ dt = datetime(2024,2,8,11,31,14)
+ res = utc2itow(dt)
+ self.assertEqual(res, (2300, 387092000))
+
def testgnss2str(self):
GNSS = {
0: "GPS",
From fb56ac370a38ec9ec2b986036c8580b450c801e6 Mon Sep 17 00:00:00 2001
From: semuadmin <28569967+semuadmin@users.noreply.github.com>
Date: Fri, 9 Feb 2024 09:35:46 +0000
Subject: [PATCH 3/6] simplify some helpers
---
src/pyubx2/ubxhelpers.py | 20 ++++++--------------
src/pyubx2/ubxtypes_core.py | 11 -----------
src/pyubx2/ubxtypes_decodes.py | 31 +++++++++++++++++++++----------
tests/test_static.py | 4 ++--
4 files changed, 29 insertions(+), 37 deletions(-)
diff --git a/src/pyubx2/ubxhelpers.py b/src/pyubx2/ubxhelpers.py
index 9956799..e20a318 100644
--- a/src/pyubx2/ubxhelpers.py
+++ b/src/pyubx2/ubxhelpers.py
@@ -20,7 +20,8 @@
import pyubx2.exceptions as ube
import pyubx2.ubxtypes_configdb as ubcdb
import pyubx2.ubxtypes_core as ubt
-from pyubx2.ubxtypes_core import GNSSLIST, UBX_HDR
+from pyubx2.ubxtypes_core import UBX_HDR
+from pyubx2.ubxtypes_decodes import FIXTYPE, GNSSLIST
EPOCH0 = datetime(1980, 1, 6) # EPOCH start date
LEAPOFFSET = 18 # leap year offset in seconds, valid as from 1/1/2017
@@ -164,19 +165,10 @@ def gpsfix2str(fix: int) -> str:
"""
- if fix == 5:
- fixs = "TIME ONLY"
- elif fix == 4:
- fixs = "GPS + DR"
- elif fix == 3:
- fixs = "3D"
- elif fix == 2:
- fixs = "2D"
- elif fix == 1:
- fixs = "DR"
- else:
- fixs = "NO FIX"
- return fixs
+ try:
+ return FIXTYPE[fix]
+ except KeyError:
+ return str(fix)
def dop2str(dop: float) -> str:
diff --git a/src/pyubx2/ubxtypes_core.py b/src/pyubx2/ubxtypes_core.py
index cdd532a..4ceb9e9 100644
--- a/src/pyubx2/ubxtypes_core.py
+++ b/src/pyubx2/ubxtypes_core.py
@@ -21,17 +21,6 @@
ERR_LOG = 1
ERR_IGNORE = 0
-GNSSLIST = {
- 0: "GPS",
- 1: "SBAS",
- 2: "Galileo",
- 3: "BeiDou",
- 4: "IMES",
- 5: "QZSS",
- 6: "GLONASS",
- 7: "NAVIC",
-}
-
# scaling factor constants
SCAL9 = 1e-9 # 0.000000001
SCAL8 = 1e-8 # 0.00000001
diff --git a/src/pyubx2/ubxtypes_decodes.py b/src/pyubx2/ubxtypes_decodes.py
index a8665c7..ef88a1b 100644
--- a/src/pyubx2/ubxtypes_decodes.py
+++ b/src/pyubx2/ubxtypes_decodes.py
@@ -8,6 +8,17 @@
:author: semuadmin
"""
+GNSSLIST = {
+ 0: "GPS",
+ 1: "SBAS",
+ 2: "Galileo",
+ 3: "BeiDou",
+ 4: "IMES",
+ 5: "QZSS",
+ 6: "GLONASS",
+ 7: "NAVIC",
+}
+
# UBX-CFG-DGNSS
DGNSMODE = {
2: "RTK float",
@@ -266,12 +277,12 @@
# UBX-NAV-PVT
GPSFIX = FIXTYPE = {
- 0: "no fix",
- 1: "dead reckoning only",
- 2: "2D fix",
- 3: "3D fix",
- 4: "GPS + dead reckoning combined",
- 5: "Time only fix",
+ 0: "NO FIX",
+ 1: "DR",
+ 2: "2D",
+ 3: "3D",
+ 4: "GPS + DR",
+ 5: "TIME ONLY",
}
PSMSTATE = {
0: "PSM is not active",
@@ -282,9 +293,9 @@
5: "Inactive",
}
CARRSOLN = {
- 0: "no RTK",
- 1: "RTK float",
- 2: "RTK fixed",
+ 0: "NO RTK",
+ 1: "RTK FLOAT",
+ 2: "RTK FIXED",
}
# UBX-NAV-SAT
@@ -331,7 +342,7 @@
# UBX-NAV-SIG
CORRSOURCE = {
- 0: "none",
+ 0: "NONE",
1: "SBAS",
2: "BeiDou",
3: "RTCM2",
diff --git a/tests/test_static.py b/tests/test_static.py
index 7696ea1..8ab2e9d 100644
--- a/tests/test_static.py
+++ b/tests/test_static.py
@@ -185,8 +185,8 @@ def testgnss2str(self):
self.assertEqual(res, GNSS[i])
def testgps2str(self):
- fixs = ["NO FIX", "DR", "2D", "3D", "GPS + DR", "TIME ONLY"]
- for i, fix in enumerate(range(0, 6)):
+ fixs = ["NO FIX", "DR", "2D", "3D", "GPS + DR", "TIME ONLY", "6"]
+ for i, fix in enumerate(range(0, 7)):
res = gpsfix2str(fix)
self.assertEqual(res, fixs[i])
From 8da5894464b3487599cc352d7071aecf32b8ffdf Mon Sep 17 00:00:00 2001
From: semuadmin <28569967+semuadmin@users.noreply.github.com>
Date: Wed, 14 Feb 2024 10:43:44 +0000
Subject: [PATCH 4/6] add getinputmode helper
---
RELEASE_NOTES.md | 1 +
src/pyubx2/ubxhelpers.py | 29 ++++++++++++++++++++++++++++-
tests/test_static.py | 14 +++++++++++++-
3 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index f19e430..b60dad3 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -6,6 +6,7 @@ CHANGES:
1. Add val2sphp helper method to convert high precision (9dp) coordinate to separate standard and high precision components, as required by some CFG and NAV messages.
1. Add utc2itow helper method to convert utc datetime to GPS week number and time of week.
+1. Add getinputmode helper to determinate mode of input UBX message (SET or POLL)
### RELEASE 1.2.37
diff --git a/src/pyubx2/ubxhelpers.py b/src/pyubx2/ubxhelpers.py
index e20a318..205c380 100644
--- a/src/pyubx2/ubxhelpers.py
+++ b/src/pyubx2/ubxhelpers.py
@@ -20,7 +20,7 @@
import pyubx2.exceptions as ube
import pyubx2.ubxtypes_configdb as ubcdb
import pyubx2.ubxtypes_core as ubt
-from pyubx2.ubxtypes_core import UBX_HDR
+from pyubx2.ubxtypes_core import POLL, SET, UBX_HDR
from pyubx2.ubxtypes_decodes import FIXTYPE, GNSSLIST
EPOCH0 = datetime(1980, 1, 6) # EPOCH start date
@@ -529,3 +529,30 @@ def val2sphp(val: float, scale: float = 1e-7) -> tuple:
val_sp = trunc(val)
val_hp = round((val - val_sp) * 100)
return val_sp, val_hp
+
+
+def getinputmode(data: bytes) -> int:
+ """
+ Return input message mode (SET or POLL).
+
+ :param bytes data: raw UBX input message
+ :return: message mode (1 = SET, 2 = POLL)
+ :rtype: int
+ """
+
+ if (
+ len(data) == 8
+ or data[2:4] == b"\x06\x8b" # CFG-VALGET
+ or (
+ data[2:4]
+ in (
+ b"\x06\x01",
+ b"\x06\x02",
+ b"\x06\x03",
+ b"\x06\x31",
+ ) # CFG-INF, CFG-MSG, CFG-PRT, CFG-TP5
+ and len(data) <= 10
+ )
+ ):
+ return POLL
+ return SET
diff --git a/tests/test_static.py b/tests/test_static.py
index 8ab2e9d..712d3b7 100644
--- a/tests/test_static.py
+++ b/tests/test_static.py
@@ -13,7 +13,7 @@
import unittest
from datetime import datetime
import pyubx2.ubxtypes_core as ubt
-from pyubx2 import POLL, UBX_CLASSES, UBXMessage, UBXReader
+from pyubx2 import SET, POLL, UBX_CLASSES, UBXMessage, UBXReader
from pyubx2.ubxhelpers import (
att2idx,
att2name,
@@ -25,6 +25,7 @@
dop2str,
escapeall,
get_bits,
+ getinputmode,
gnss2str,
gpsfix2str,
hextable,
@@ -290,6 +291,17 @@ def testval2sphp(self):
res = val2sphp(5.9876543)
self.assertEqual(res, (59876543,0))
+ def testgetinputmode(self):
+ res = getinputmode(UBXMessage("CFG","CFG-ODO", POLL).serialize())
+ self.assertEqual(res, POLL)
+ res = getinputmode(UBXMessage.config_poll(0,0,["CFG_UART1_BAUDRATE", 0x40530001]).serialize())
+ self.assertEqual(res, POLL)
+ res = getinputmode(UBXMessage.config_set(0,0,[("CFG_UART1_BAUDRATE", 9600), (0x40530001, 115200)]).serialize())
+ self.assertEqual(res, SET)
+ res = getinputmode(UBXMessage.config_del(0,0,["CFG_UART1_BAUDRATE", 0x40530001]).serialize())
+ self.assertEqual(res, SET)
+ res = getinputmode(UBXMessage("CFG","CFG-INF", POLL, protocolID=1).serialize())
+ self.assertEqual(res, POLL)
if __name__ == "__main__":
# import sys;sys.argv = ['', 'Test.testName']
From 26ef9d3dfd4d357e0778adc3286cfccede1d548b Mon Sep 17 00:00:00 2001
From: semuadmin <28569967+semuadmin@users.noreply.github.com>
Date: Wed, 14 Feb 2024 11:48:43 +0000
Subject: [PATCH 5/6] add auto-detection of SET/POLL mode
---
README.md | 3 ++-
RELEASE_NOTES.md | 2 +-
src/pyubx2/ubxmessage.py | 4 ++--
src/pyubx2/ubxreader.py | 24 +++++++++++++++++-------
src/pyubx2/ubxtypes_core.py | 1 +
tests/test_exceptions.py | 12 ++++++------
tests/test_parse.py | 16 +++++++++++++++-
tests/test_static.py | 2 ++
8 files changed, 46 insertions(+), 18 deletions(-)
diff --git a/README.md b/README.md
index 2774c22..1226145 100644
--- a/README.md
+++ b/README.md
@@ -95,7 +95,8 @@ conda install -c conda-forge pyubx2
| POLL (0x02) | query input *to* the receiver | `ubxtypes_poll.py` |
If you're simply streaming and/or parsing the *output* of a UBX receiver, the mode is implicitly GET. If you want to create
-or parse an *input* (command or query) message, you must set the mode parameter to SET or POLL.
+or parse an *input* (command or query) message, you must set the mode parameter to SET or POLL. If the parser mode is set to
+0x03 (SETPOLL), `pyubx2` will automatically determine the applicable input mode (SET or POLL) based on the message payload.
---
## Reading (Streaming)
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index b60dad3..e6e298a 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -6,7 +6,7 @@ CHANGES:
1. Add val2sphp helper method to convert high precision (9dp) coordinate to separate standard and high precision components, as required by some CFG and NAV messages.
1. Add utc2itow helper method to convert utc datetime to GPS week number and time of week.
-1. Add getinputmode helper to determinate mode of input UBX message (SET or POLL)
+1. Add getinputmode helper to determinate mode of input UBX message (SET or POLL). Add new UBXReader msgmode of SETPOLL (0x03), which will automatically determine input mode.
### RELEASE 1.2.37
diff --git a/src/pyubx2/ubxmessage.py b/src/pyubx2/ubxmessage.py
index 9b01c85..45c96f9 100644
--- a/src/pyubx2/ubxmessage.py
+++ b/src/pyubx2/ubxmessage.py
@@ -74,8 +74,8 @@ def __init__(
self._parsebf = parsebitfield # parsing bitfields Y/N?
self._scaling = scaling # apply scale factors Y/N?
- if msgmode not in (0, 1, 2):
- raise ube.UBXMessageError(f"Invalid msgmode {msgmode} - must be 0, 1 or 2.")
+ if msgmode not in (ubt.GET, ubt.SET, ubt.POLL):
+ raise ube.UBXMessageError(f"Invalid msgmode {msgmode} - must be 0, 1 or 2")
# accommodate different formats of msgClass and msgID
if isinstance(ubxClass, str) and isinstance(
diff --git a/src/pyubx2/ubxreader.py b/src/pyubx2/ubxreader.py
index 345a124..f49dd35 100644
--- a/src/pyubx2/ubxreader.py
+++ b/src/pyubx2/ubxreader.py
@@ -9,7 +9,9 @@
'protfilter' governs which protocols (NMEA, UBX or RTCM3) are processed
'quitonerror' governs how errors are handled
-'msgmode' indicates the type of UBX datastream (input GET, output SET, query POLL)
+'msgmode' indicates the type of UBX datastream (output GET, input SET, query POLL)
+
+If msgmode set to SETPOLL, input mode will be automatically detected by parser.
Created on 2 Oct 2020
@@ -32,14 +34,17 @@
UBXTypeError,
)
from pyubx2.socket_stream import SocketStream
-from pyubx2.ubxhelpers import bytes2val, calc_checksum, val2bytes
+from pyubx2.ubxhelpers import bytes2val, calc_checksum, getinputmode, val2bytes
from pyubx2.ubxmessage import UBXMessage
from pyubx2.ubxtypes_core import (
ERR_LOG,
ERR_RAISE,
GET,
NMEA_PROTOCOL,
+ POLL,
RTCM3_PROTOCOL,
+ SET,
+ SETPOLL,
U2,
UBX_HDR,
UBX_PROTOCOL,
@@ -69,7 +74,7 @@ def __init__(
"""Constructor.
:param datastream stream: input data stream
- :param int msgmode: 0=GET, 1=SET, 2=POLL (0)
+ :param int msgmode: 0=GET, 1=SET, 2=POLL, 3=SETPOLL (0)
:param int validate: 0 = ignore invalid checksum, 1 = validate checksum (1)
:param int protfilter: protocol filter 1 = NMEA, 2 = UBX, 4 = RTCM3 (3)
:param int quitonerror: 0 = ignore errors, 1 = log continue, 2 = (re)raise (1)
@@ -97,9 +102,9 @@ def __init__(
self._msgmode = msgmode
self._parsing = parsing
- if self._msgmode not in (0, 1, 2):
+ if self._msgmode not in (GET, SET, POLL, SETPOLL):
raise UBXStreamError(
- f"Invalid stream mode {self._msgmode} - must be 0, 1 or 2"
+ f"Invalid stream mode {self._msgmode} - must be 0, 1, 2 or 3"
)
def __iter__(self):
@@ -374,8 +379,10 @@ def parse(
"""
# pylint: disable=too-many-arguments
- if msgmode not in (0, 1, 2):
- raise UBXParseError(f"Invalid message mode {msgmode} - must be 0, 1 or 2")
+ if msgmode not in (GET, SET, POLL, SETPOLL):
+ raise UBXParseError(
+ f"Invalid message mode {msgmode} - must be 0, 1, 2 or 3"
+ )
lenm = len(message)
hdr = message[0:2]
@@ -410,6 +417,9 @@ def parse(
(f"Message checksum {ckm}" f" invalid - should be {ckv}")
)
try:
+ # if input message (SET or POLL), determine mode automatically
+ if msgmode == SETPOLL:
+ msgmode = getinputmode(message) # returns SET or POLL
if payload is None:
return UBXMessage(clsid, msgid, msgmode)
return UBXMessage(
diff --git a/src/pyubx2/ubxtypes_core.py b/src/pyubx2/ubxtypes_core.py
index 4ceb9e9..99d2a10 100644
--- a/src/pyubx2/ubxtypes_core.py
+++ b/src/pyubx2/ubxtypes_core.py
@@ -12,6 +12,7 @@
GET = 0
SET = 1
POLL = 2
+SETPOLL = 3
VALNONE = 0
VALCKSUM = 1
NMEA_PROTOCOL = 1
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
index efb762c..35a505e 100644
--- a/tests/test_exceptions.py
+++ b/tests/test_exceptions.py
@@ -44,9 +44,9 @@ def tearDown(self):
pass
def testInvMode(self): # test invalid mode
- EXPECTED_ERROR = "Invalid msgmode 3 - must be 0, 1 or 2"
+ EXPECTED_ERROR = "Invalid msgmode 4 - must be 0, 1 or 2"
with self.assertRaisesRegex(UBXMessageError, EXPECTED_ERROR):
- UBXMessage("CFG", "CFG-MSG", 3, msgClass=240, msgID=5)
+ UBXMessage("CFG", "CFG-MSG", 4, msgClass=240, msgID=5)
def testAckCkT(self): # bad checksum
EXPECTED_ERROR = "Message checksum (.*) invalid - should be (.*)"
@@ -320,10 +320,10 @@ def testFill_CFGVALGET1(
UBXMessage("CFG", "CFG-VALGET", GET, version=0, layer=0)
def testParseMode(self): # test invalid parse message mode
- EXPECTED_ERROR = "Invalid message mode 3 - must be 0, 1 or 2"
+ EXPECTED_ERROR = "Invalid message mode 4 - must be 0, 1, 2 or 3"
with self.assertRaisesRegex(UBXParseError, EXPECTED_ERROR):
UBXReader.parse(
- b"\xb5b\x05\x01\x02\x00\x06\x01\x0f\x38", validate=VALCKSUM, msgmode=3
+ b"\xb5b\x05\x01\x02\x00\x06\x01\x0f\x38", validate=VALCKSUM, msgmode=4
)
def testParseMode2(self): # test parser with incorrect mode for input message
@@ -332,9 +332,9 @@ def testParseMode2(self): # test parser with incorrect mode for input message
UBXReader.parse(self.mga_ini, validate=VALCKSUM, quitonerror=ERR_RAISE)
def testStreamMode(self): # test invalid stream message mode
- EXPECTED_ERROR = "Invalid stream mode 3 - must be 0, 1 or 2"
+ EXPECTED_ERROR = "Invalid stream mode 4 - must be 0, 1, 2 or 3"
with self.assertRaisesRegex(UBXStreamError, EXPECTED_ERROR):
- UBXReader(None, validate=VALCKSUM, msgmode=3)
+ UBXReader(None, validate=VALCKSUM, msgmode=4)
def testVal2Bytes(self): # test invalid attribute type
EXPECTED_ERROR = "Unknown attribute type Z001"
diff --git a/tests/test_parse.py b/tests/test_parse.py
index 831c4db..37cd6a2 100644
--- a/tests/test_parse.py
+++ b/tests/test_parse.py
@@ -11,7 +11,7 @@
import unittest
-from pyubx2 import UBXMessage, UBXReader, VALCKSUM, VALNONE, SET
+from pyubx2 import UBXMessage, UBXReader, VALCKSUM, VALNONE, SET, SETPOLL
class ParseTest(unittest.TestCase):
@@ -270,6 +270,20 @@ def testMGAINI2(self): # test parser of MGA-INI input message with args
"",
)
+ def testSETPOLL(self): # test auto detection of SET or POLL mode
+ msg = UBXMessage.config_poll(0,0,["CFG_UART1_BAUDRATE", 0x40530001]).serialize()
+ res = UBXReader.parse(msg, msgmode=SETPOLL)
+ self.assertEqual(
+ str(res),
+ "",
+ )
+ msg = UBXMessage.config_set(0,0,[("CFG_UART1_BAUDRATE", 9600), (0x40530001, 38400)]).serialize()
+ res = UBXReader.parse(msg, msgmode=SETPOLL)
+ self.assertEqual(
+ str(res),
+ "",
+ )
+
def testESFSTATUS(self): # test parser of ESF-STATUS message
res = UBXReader.parse(self.esf_status)
# print(res)
diff --git a/tests/test_static.py b/tests/test_static.py
index 712d3b7..cd54705 100644
--- a/tests/test_static.py
+++ b/tests/test_static.py
@@ -302,6 +302,8 @@ def testgetinputmode(self):
self.assertEqual(res, SET)
res = getinputmode(UBXMessage("CFG","CFG-INF", POLL, protocolID=1).serialize())
self.assertEqual(res, POLL)
+ res = getinputmode(UBXMessage("CFG","CFG-INF", SET, protocolID=1, infMsgMask_01=1,infMsgMask_02=1).serialize())
+ self.assertEqual(res, SET)
if __name__ == "__main__":
# import sys;sys.argv = ['', 'Test.testName']
From 5b632445d6ce7092ebc781ac274246d4966c0cf5 Mon Sep 17 00:00:00 2001
From: semuadmin <28569967+semuadmin@users.noreply.github.com>
Date: Wed, 14 Feb 2024 11:55:00 +0000
Subject: [PATCH 6/6] update readme
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 1226145..bc8316c 100644
--- a/README.md
+++ b/README.md
@@ -117,7 +117,7 @@ The constructor accepts the following optional keyword arguments:
* `quitonerror`: 0 = ignore errors, 1 = log errors and continue (default), 2 = (re)raise errors and terminate
* `validate`: VALCKSUM (0x01) = validate checksum (default), VALNONE (0x00) = ignore invalid checksum or length
* `parsebitfield`: 1 = parse bitfields ('X' type properties) as individual bit flags, where defined (default), 0 = leave bitfields as byte sequences
-* `msgmode`: 0 = GET (default), 1 = SET, 2 = POLL
+* `msgmode`: 0 = GET (default), 1 = SET, 2 = POLL, 3 = SETPOLL (automatically determine SET or POLL input mode)
Example - Serial input. This example will output both UBX and NMEA messages:
```python
@@ -160,7 +160,7 @@ The `parse()` method accepts the following optional keyword arguments:
* `validate`: VALCKSUM (0x01) = validate checksum (default), VALNONE (0x00) = ignore invalid checksum or length
* `parsebitfield`: 1 = parse bitfields as individual bit flags, where defined (default), 0 = leave bitfields as byte sequences
-* `msgmode`: 0 = GET (default), 1 = SET, 2 = POLL
+* `msgmode`: 0 = GET (default), 1 = SET, 2 = POLL, 3 = SETPOLL (automatically determine SET or POLL input mode)
Example - output (GET) message:
```python