Skip to content

Commit

Permalink
Merge pull request #65 from semuconsulting/RELEASE-CANDIDATE-1.1.4
Browse files Browse the repository at this point in the history
Update 1300 1302 handling
  • Loading branch information
semuadmin authored Jan 8, 2025
2 parents ec33ec6 + 91092e0 commit 7d859f5
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 31 deletions.
6 changes: 6 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# pyrtcm Release Notes

### RELEASE 1.1.4

1. Refine handling of string attributes (e.g. DF140, DF563, DF566).
1. Add optional 'parsed' argument to RTCMReader - 1 = return raw and parsed data, 0 = return only raw data (parsed will be None)
2. Temporarily suppress 1302 test cases **NB:** sample 1302 messages from euref-ip.net:2101/EUREF01 appear to be truncated (to 59 bytes), causing a `ValueError - negative shift count` exception; this mountpoint also causes the BNC 2.13.1 NTRIP client to bomb every time, so possibly an issue with the source implementation or documentation???

### RELEASE 1.1.3

1. Update RTCM message definitions - messages 1300-1305 added.
Expand Down
12 changes: 12 additions & 0 deletions examples/gnssntripclient.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
server=euref-ip.net
port=2101
mountpoint=EUREF01
https=0
ntripversion=2.0
datatype=RTCM
ntripuser=semuadmin
ntrippassword=Hmp2e8Vktx
ggainterval=-1
clioutput=1
output=ntripdata.log
verbosity=3
2 changes: 1 addition & 1 deletion src/pyrtcm/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
:license: BSD 3-Clause
"""

__version__ = "1.1.3"
__version__ = "1.1.4"
21 changes: 15 additions & 6 deletions src/pyrtcm/rtcmmessage.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
from pyrtcm.rtcmtypes_core import (
CELPRN,
CELSIG,
CHA,
INT,
INTS,
NA,
NCELL,
NHARMCOEFFC,
Expand All @@ -24,6 +27,7 @@
RTCM_DATA_FIELDS,
RTCM_HDR,
RTCM_MSGIDS,
STR,
)
from pyrtcm.rtcmtypes_get import RTCM_PAYLOADS_GET
from pyrtcm.rtcmtypes_get_igs import RTCM_PAYLOADS_GET_IGS
Expand Down Expand Up @@ -216,22 +220,27 @@ def _set_attribute_single(
else:
# done inline for performance reasons...
bits = self._payloadi >> (self._payblen - offset - asiz) & ((1 << asiz) - 1)
msb = 1 << asiz - 1 if atyp in ("SNT", "INT") else 0
if atyp == "SNT": # int, MSB indicates sign
msb = 1 << asiz - 1 if atyp in (INTS, INT) else 0
if atyp == INTS: # int, MSB indicates sign
val = bits & msb - 1
if bits & msb:
val *= -1
elif atyp == CHA:
val = chr(bits)
else: # all other types
val = bits
if atyp == "INT" and bits & msb: # 2's compliment -ve int
if atyp == INT and bits & msb: # 2's compliment -ve int
val -= 1 << asiz
if atyp in ("CHA", "UTF"): # ASCII or UTF-8 character
val = chr(val)
if atyp == STR:
val = "" if val == 0 else chr(bits)
else:
if ares not in (0, 1): # apply any scaling factor
val *= ares

setattr(self, anami, val)
if atyp == STR: # concatenated string
setattr(self, anam, getattr(self, anam, "") + val)
else:
setattr(self, anami, val)
offset += asiz

# add special attributes to keep track of
Expand Down
17 changes: 12 additions & 5 deletions src/pyrtcm/rtcmreader.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def __init__(
quitonerror: int = ERR_LOG,
labelmsm: int = 1,
bufsize: int = 4096,
parsed: bool = True,
errorhandler: object = None,
encoding: int = ENCODE_NONE,
): # pylint: disable=too-many-arguments
Expand All @@ -69,6 +70,8 @@ def __init__(
ERR_RAISE (2) = (re)raise (1)
:param int labelmsm: MSM NSAT and NCELL attribute label (1 = RINEX, 2 = freq)
:param int bufsize: socket recv buffer size (4096)
:param bool parsed: 1 = return raw and parsed data, 0 = return only raw data \
(parsed = None) (1)
:param object errorhandler: error handling object or function (None)
:param int encoding: encoding for socket stream \
(0 = none, 1 = chunk, 2 = gzip, 4 = compress, 8 = deflate (can be OR'd)) (0)
Expand All @@ -83,6 +86,7 @@ def __init__(
self._errorhandler = errorhandler
self._validate = validate
self._labelmsm = labelmsm
self._parsed = parsed
self._logger = getLogger(__name__)

def __iter__(self):
Expand Down Expand Up @@ -209,11 +213,14 @@ def _parse_rtcm3(self, hdr: bytes) -> tuple:
payload = self._read_bytes(size)
crc = self._read_bytes(3)
raw_data = hdr + hdr3 + payload + crc
parsed_data = self.parse(
raw_data,
validate=self._validate,
labelmsm=self._labelmsm,
)
if self._parsed:
parsed_data = self.parse(
raw_data,
validate=self._validate,
labelmsm=self._labelmsm,
)
else:
parsed_data = None
return (raw_data, parsed_data)

def _read_bytes(self, size: int) -> bytes:
Expand Down
10 changes: 5 additions & 5 deletions src/pyrtcm/rtcmtypes_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,10 @@
BIT = "BIT" # bitfield
BITX = "BITX" # variable bitfield
CHA = "CHA" # characters, ISO 8859-1 (not limited to ASCII)
STR = "STR" # concatenated UTF-8 string
INT = "INT" # 2’s complement integer
UINT = "UINT" # unsigned integer
INTS = "SNT" # sign-magnitude integer
UTF = "UTF" # Unicode UTF-8 Code Unit
PRN = "PRN" # Derived satellite PRN
CELPRN = "CPR" # Derived cell PRN
CELSIG = "CSG" # Derived cell Signal ID
Expand Down Expand Up @@ -329,7 +329,7 @@
"DF137": (BIT, 1, 1, "GPS Fit Interval"),
"DF138": (UINT, 7, 1, "Number of Characters to Follow"),
"DF139": (UINT, 8, 1, "Number of UTF-8 Code Units"),
"DF140": (UTF, 8, 0, "UTF-8 Character Code Units"),
"DF140": (STR, 8, 0, "UTF-8 Character Code Units"),
"DF141": (BIT, 1, 0, "Reference-Station Indicator"),
"DF142": (BIT, 1, 0, "Single Receiver Oscillator Indicator"),
"DF143": (UINT, 5, 0, "Source Name Counter"),
Expand Down Expand Up @@ -668,14 +668,14 @@
"DF560": (INT, 17, 0.0000004, "dot R3 Rate of Change of Rotation about Z"),
"DF561": (INT, 14, 0.0000002, "dot dS Rate of Change of Scale Correction"),
"DF562": (UINT, 5, 0, "Service CRS Name Counter"),
"DF563": (CHA, 8, 0, "Service CRS Name"),
"DF563": (STR, 8, 0, "Service CRS Name"),
"DF564": (UINT, 16, 0.01, "Coordinate Epoch CE"),
"DF565": (UINT, 5, 0, "RTCM CRS Name Counter"),
"DF566": (CHA, 8, 0, "RTCM CRS Name"),
"DF566": (STR, 8, 0, "RTCM CRS Name"),
"DF567": (BIT, 1, 0, "Anchor - Global/Plate Fixed Indicator"),
"DF568": (UINT, 3, 1, "Number of Database Links"),
"DF569": (UINT, 5, 0, "Database Link Counter"),
"DF570": (CHA, 8, 0, "Database Link"),
"DF570": (STR, 8, 0, "Database Link Name"),
"DF571": (UINT, 20, 1, "Beidou Residuals Epoch Time TOW"),
"DF572": (UINT, 5, 0, "Beidou Number of Satellite Signals Processed"),
"DF573": (UINT, 20, 1, "Galileo Residuals Epoch Time TOW"),
Expand Down
8 changes: 5 additions & 3 deletions src/pyrtcm/rtcmtypes_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -1272,13 +1272,15 @@
),
"DF567": "Anchor - Global/Plate Fixed Indicator",
"DF149": "Plate Number",
"DF568": "Number of Database Links I",
"DF568": "Number of Database Links",
"group-DF568": (
"DF568",
{
"DF569": "Database Link Counter N",
"DF569": "Database Link Counter",
# TODO check correct payload definition
# 1302 messages from EUREF01 appear to be truncated?
"group-DF569": (
"DF569",
"DF569+1",
{
"DF570": "Database Link",
},
Expand Down
Binary file added tests/pygpsdata-NTRIP-1300-1302.log
Binary file not shown.
Binary file removed tests/pygpsdata-NTRIP-1300.log
Binary file not shown.
36 changes: 25 additions & 11 deletions tests/test_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ def testntrip2log(
"<RTCM(1013, DF002=1013, DF003=0, DF051=60382, DF052=59727, DF053=0, DF054=18)>",
"<RTCM(1019, DF002=1019, DF009=2, DF076=257, DF077=0, DF078=1, DF079=-1.559783413540572e-10, DF071=185, DF081=324000, DF082=0.0, DF083=6.139089236967266e-12, DF084=-0.00047086644917726517, DF085=185, DF086=-117.28125, DF087=1.339799382549245e-09, DF088=0.6883564381860197, DF089=-5.889683961868286e-06, DF090=0.016119434614665806, DF091=8.553266525268555e-06, DF092=5153.713861465454, DF093=324000, DF094=2.421438694000244e-07, DF095=-0.944771918002516, DF096=1.6763806343078613e-08, DF097=0.3080678000114858, DF098=210.3125, DF099=-0.3891187282279134, DF100=-2.476781446603127e-09, DF101=-1.7695128917694092e-08, DF102=0, DF103=0, DF137=0)>",
"<RTCM(1020, DF002=1020, DF038=9, DF040=5, DF104=1, DF105=1, DF106=1, DF107=2492, DF108=0, DF109=1, DF110=79, DF111=-2.059713363647461, DF112=19637.81884765625, DF113=0.0, DF114=0.8449039459228516, DF115=33.10888671875, DF116=-1.862645149230957e-09, DF117=-2.4976272583007812, DF118=-16217.08740234375, DF119=2.7939677238464355e-09, DF120=1, DF121=2, DF122=3, DF123=0, DF124=-188052, DF125=-4, DF126=0, DF127=1, DF128=5, DF129=73, DF130=1, DF131=1, DF132=73, DF133=-3, DF134=8, DF135=8, DF136=0, DF001_7=0)>",
"<RTCM(1029, DF002=1029, DF003=0, DF051=60382, DF052=59727, DF138=7, DF139=7, DF140_01=U, DF140_02=n, DF140_03=k, DF140_04=n, DF140_05=o, DF140_06=w, DF140_07=n)>",
"<RTCM(1029, DF002=1029, DF003=0, DF051=60382, DF052=59727, DF138=7, DF139=7, DF140=Unknown)>",
"<RTCM(1033, DF002=1033, DF003=0, DF029=20, DF030_01=S, DF030_02=E, DF030_03=P, DF030_04=C, DF030_05=H, DF030_06=O, DF030_07=K, DF030_08=E, DF030_09=_, DF030_10=B, DF030_11=3, DF030_12=E, DF030_13=6, DF030_14= , DF030_15= , DF030_16= , DF030_17=S, DF030_18=P, DF030_19=K, DF030_20=E, DF031=0, DF032=4, DF033_01=5, DF033_02=8, DF033_03=5, DF033_04=6, DF227=12, DF228_01=S, DF228_02=E, DF228_03=P, DF228_04=T, DF228_05= , DF228_06=P, DF228_07=O, DF228_08=L, DF228_09=A, DF228_10=R, DF228_11=X, DF228_12=5, DF229=5, DF230_01=5, DF230_02=., DF230_03=5, DF230_04=., DF230_05=0, DF231=7, DF232_01=3, DF232_02=0, DF232_03=7, DF232_04=5, DF232_05=0, DF232_06=2, DF232_07=4)>",
"<RTCM(1042, DF002=1042, DF488=12, DF489=949, DF490=0, DF491=-1.3505996321327984e-10, DF492=3, DF493=316800, DF494=-1.3552527156068805e-19, DF495=-7.778666599733697e-12, DF496=-0.00021217693574726582, DF497=2, DF498=-102.984375, DF499=1.1275460565229878e-09, DF500=-0.11344346264377236, DF501=-5.0924718379974365e-06, DF502=0.001100340741686523, DF503=4.862435162067413e-06, DF504=5282.629014968872, DF505=316800, DF506=4.0978193283081055e-08, DF507=0.9092594981193542, DF508=-1.862645149230957e-08, DF509=0.31285916129127145, DF510=274.09375, DF511=-0.4671555492095649, DF512=-2.2137101041153073e-09, DF513=2.4000000000000004, DF514=0.4, DF515=0)>",
"<RTCM(1045, DF002=1045, DF252=3, DF289=1281, DF290=22, DF291=107, DF292=-3.115019353572279e-11, DF293=318000, DF294=0.0, DF295=-2.6716406864579767e-12, DF296=-0.00010003114584833384, DF297=-40.125, DF298=1.1664269550237805e-09, DF299=-0.5413645040243864, DF300=-1.8794089555740356e-06, DF301=0.00022546376567333937, DF302=4.287809133529663e-06, DF303=5440.592414855957, DF304=318000, DF305=-3.166496753692627e-08, DF306=-0.24508476676419377, DF307=-3.166496753692627e-08, DF308=0.3057721094228327, DF309=247.90625, DF310=-0.08484991453588009, DF311=-1.8743548935162835e-09, DF312=3.026798367500305e-09, DF314=0, DF315=0, DF001_7=0)>",
Expand Down Expand Up @@ -286,24 +286,38 @@ def testigsssr4076(
self.assertEqual(f"{parsed}", EXPECTED_RESULTS[i])
i += 1

def test1300(
def test13001302(
self,
): # test 1300 messages using log from NTRIP caster products.igs-ip.net, mountpoint SIRGAS200001
): # test 1300 / 1302 messages using log from NTRIP caster euref-ip.net, mountpoint EUREF01
# TODO 1302 message payloads from this data log appears to be truncated (59 bytes) NB: these messages also bomb the BNC 2.13.1 NTRIP client
EXPECTED_RESULTS = [
"<RTCM(1300, DF002=1300, DF562=31, DF563_01=I, DF563_02=G, DF563_03=S, DF563_04=2, DF563_05=0, DF563_06=\x00, DF563_07=\x00, DF563_08=\x00, DF563_09=\x00, DF563_10=\x00, DF563_11=\x00, DF563_12=\x00, DF563_13=\x00, DF563_14=\x00, DF563_15=\x00, DF563_16=\x00, DF563_17=\x00, DF563_18=\x00, DF563_19=\x00, DF563_20=\x00, DF563_21=\x00, DF563_22=\x00, DF563_23=\x00, DF563_24=\x00, DF563_25=\x00, DF563_26=\x00, DF563_27=\x00, DF563_28=\x00, DF563_29=\x00, DF563_30=\x00, DF563_31=\x00, DF564=0.0)>",
"<RTCM(1300, DF002=1300, DF562=31, DF563_01=I, DF563_02=G, DF563_03=S, DF563_04=2, DF563_05=0, DF563_06=\x00, DF563_07=\x00, DF563_08=\x00, DF563_09=\x00, DF563_10=\x00, DF563_11=\x00, DF563_12=\x00, DF563_13=\x00, DF563_14=\x00, DF563_15=\x00, DF563_16=\x00, DF563_17=\x00, DF563_18=\x00, DF563_19=\x00, DF563_20=\x00, DF563_21=\x00, DF563_22=\x00, DF563_23=\x00, DF563_24=\x00, DF563_25=\x00, DF563_26=\x00, DF563_27=\x00, DF563_28=\x00, DF563_29=\x00, DF563_30=\x00, DF563_31=\x00, DF564=0.0)>",
"<RTCM(1300, DF002=1300, DF562=31, DF563=ETRF2000, DF564=115.0)>",
# "<RTCM(1302, DF002=1302, DF565=31, DF566=ETRF2000(2015), DF567=1, DF149=7, DF568=6, DF569_01=9, DF570_01=*, DF569_02=13, DF570_02=<, DF569_03=23, DF570_03=$, DF569_04=21, DF570_04=4, DF569_05=30, DF570_05=\, DF569_06=18, DF570_06=T)>",
"<RTCM(1300, DF002=1300, DF562=31, DF563=ETRF2000, DF564=115.0)>",
# "<RTCM(1302, DF002=1302, DF565=31, DF566=ETRF2000(2015), DF567=1, DF149=7, DF568=6, DF569_01=9, DF570_01=*, DF569_02=13, DF570_02=<, DF569_03=23, DF570_03=$, DF569_04=21, DF570_04=4, DF569_05=30, DF570_05=\, DF569_06=18, DF570_06=T)>",
"<RTCM(1300, DF002=1300, DF562=31, DF563=ETRF2000, DF564=115.0)>",
# "<RTCM(1302, DF002=1302, DF565=31, DF566=ETRF2000(2015), DF567=1, DF149=7, DF568=6, DF569_01=9, DF570_01=*, DF569_02=13, DF570_02=<, DF569_03=23, DF570_03=$, DF569_04=21, DF570_04=4, DF569_05=30, DF570_05=\, DF569_06=18, DF570_06=T)>",
"<RTCM(1300, DF002=1300, DF562=31, DF563=ETRF2000, DF564=115.0)>",
# "<RTCM(1302, DF002=1302, DF565=31, DF566=ETRF2000(2015), DF567=1, DF149=7, DF568=6, DF569_01=9, DF570_01=*, DF569_02=13, DF570_02=<, DF569_03=23, DF570_03=$, DF569_04=21, DF570_04=4, DF569_05=30, DF570_05=\, DF569_06=18, DF570_06=T)>",
"<RTCM(1300, DF002=1300, DF562=31, DF563=ETRF2000, DF564=115.0)>",
# "<RTCM(1302, DF002=1302, DF565=31, DF566=ETRF2000(2015), DF567=1, DF149=7, DF568=6, DF569_01=9, DF570_01=*, DF569_02=13, DF570_02=<, DF569_03=23, DF570_03=$, DF569_04=21, DF570_04=4, DF569_05=30, DF570_05=\, DF569_06=18, DF570_06=T)>",
"<RTCM(1300, DF002=1300, DF562=31, DF563=ETRF2000, DF564=115.0)>",
# "<RTCM(1302, DF002=1302, DF565=31, DF566=ETRF2000(2015), DF567=1, DF149=7, DF568=6, DF569_01=9, DF570_01=*, DF569_02=13, DF570_02=<, DF569_03=23, DF570_03=$, DF569_04=21, DF570_04=4, DF569_05=30, DF570_05=\, DF569_06=18, DF570_06=T)>",
]
dirname = os.path.dirname(__file__)
with open(os.path.join(dirname, "pygpsdata-NTRIP-1300.log"), "rb") as stream:
with open(
os.path.join(dirname, "pygpsdata-NTRIP-1300-1302.log"), "rb"
) as stream:
i = 0
raw = 0
rtr = RTCMReader(stream, labelmsm=True)
rtr = RTCMReader(stream, labelmsm=True, quitonerror=ERR_LOG)
for raw, parsed in rtr:
if raw is not None:
# print(f'"{parsed}",')
self.assertEqual(f"{parsed}", EXPECTED_RESULTS[i])
i += 1
self.assertEqual(i, 2)
if parsed.identity in ("1300"): # , "1302"):
print(f'"{parsed}",')
# self.assertEqual(f"{parsed}", EXPECTED_RESULTS[i])
i += 1
self.assertEqual(i, 6)

def testSerialize(self): # test serialize()
payload = self._raw1005[3:-3]
Expand Down

0 comments on commit 7d859f5

Please sign in to comment.