Skip to content

Commit

Permalink
Merge pull request #56 from semuconsulting/RC-1.0.20
Browse files Browse the repository at this point in the history
RC 1.0.20
  • Loading branch information
semuadmin authored May 8, 2024
2 parents f47dee5 + 3908b36 commit 8120e62
Show file tree
Hide file tree
Showing 15 changed files with 733 additions and 210 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ src/pyrtcm.egg-*
/tests/__pycache__
*.pyc
/examples/rtcmsandpit.py
/examples/temp*.py
/examples/temp*.*
/examples/*.log
/.pytest_cache/
pylint_report.txt
Expand Down
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.19"
"moduleversion": "1.0.20"
}
30 changes: 17 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ Example - Serial input:
```python
from serial import Serial
from pyrtcm import RTCMReader
stream = Serial('/dev/tty.usbmodem14101', 9600, timeout=3)
rtr = RTCMReader(stream)
(raw_data, parsed_data) = rtr.read()
print(parsed_data)
with Serial('/dev/tty.usbmodem14101', 9600, timeout=3) as stream:
rtr = RTCMReader(stream)
raw_data, parsed_data = rtr.read()
print(parsed_data)
```
```
<RTCM(1077, DF002=1077, DF003=0, GNSSEpoch=204137001, DF393=1, DF409=0, DF001_7=0, DF411=0, DF412=0, DF417=0, DF418=0, DF394=760738918298550272, NSat=10, DF395=1073807360, NSig=2, DF396=1044459, DF397_01(005)=75, DF397_02(007)=75, DF397_03(009)=81, ..., DF404_19(030,1C)=0.0, DF404_20(030,2L)=0.0)>,
Expand All @@ -111,21 +111,21 @@ print(parsed_data)
Example - File input (using iterator).
```python
from pyrtcm import RTCMReader
stream = open('rtcmdata.log', 'rb')
rtr = RTCMReader(stream)
for (raw_data, parsed_data) in rtr:
print(parsed_data)
with open('rtcmdata.log', 'rb') as stream:
rtr = RTCMReader(stream)
for raw_data, parsed_data in rtr:
print(parsed_data)
```

Example - Socket input (using iterator):
```python
import socket
from pyrtcm import RTCMReader
stream = socket.socket(socket.AF_INET, socket.SOCK_STREAM):
stream.connect(("localhost", 50007))
rtr = RTCMReader(stream)
for (raw_data, parsed_data) in rtr:
print(parsed_data)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as stream:
stream.connect(("localhost", 50007))
rtr = RTCMReader(stream)
for raw_data, parsed_data in rtr:
print(parsed_data)
```

---
Expand Down Expand Up @@ -199,6 +199,10 @@ print(msmarray)
[[0.00014309026300907135, 0.00014193402603268623, 341, 45.0, 0, -0.9231], [0.00014183297753334045, 0.00014339853078126907, 341, 38.0, 0, -0.9194], ... etc.]
```

The following dedicated helper methods are available to parse selected RTCM3 message types into a series of iterable data arrays:
- `parse_msm` - for MSM message types (e.g. 1077, 1125, etc.).
- `parse_4076_201` - for 4076_201 SSR (harmonic coefficients) message types.

---
## <a name="generating">Generating</a>

Expand Down
8 changes: 8 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# pyrtcm Release Notes

### RELEASE 1.0.20

ENHANCEMENTS

1. Add `parse_msm` helper method to parse RTCM3 MSM message type into series of iterable data arrays.
1. Add `parse_4076_201` helper method to parse RTCM3 4076_201 SSR message types into series of iterable data arrays.
1. Internal streamlining of conditional group parsing & updated docstrings - no functional changes.

### RELEASE 1.0.19

ENHANCEMENTS
Expand Down
51 changes: 4 additions & 47 deletions examples/hmc_4076_201.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,51 +14,7 @@

from sys import argv

from pyrtcm import RTCMReader, RTCMMessage

COEFFS = {
0: ("IDF039", "Cosine"),
1: ("IDF040", "Sine"),
}


def process_message(parsed: RTCMMessage):
"""
Process individual 4076_201 message.
:param RTCMMessage parsed: parsed 4076_201 message
"""

layers = parsed.IDF035 + 1 # number of ionospheric layers
hmc = {}
# for each ionospheric layer
for lyr in range(layers):
lyrheight = getattr(parsed, f"IDF036_{lyr+1:02d}")
hmc[lyr] = {}
# for each coefficient (cosine & sine)
for field, coeff in COEFFS.values():
hmc[lyr][coeff] = []
i = 0
eof = False
# for each coefficient value
while not eof:
try:
hmc[lyr][coeff].append(
getattr(parsed, f"{field}_{lyr+1:02d}_{i+1:02d}")
)
i += 1
except AttributeError:
eof = True

print(
f"\nLayer {lyr+1} {lyrheight} km -",
f"Harmonic Coefficients {coeff} ({len(hmc[lyr][coeff])}):",
)
for i, hc in enumerate(hmc[lyr][coeff]):
print(f"{i+1:03d}: {hc}")

print("\nEntire contents of hmc array:")
print(hmc)
from pyrtcm import RTCMReader, parse_4076_201


def main(**kwargs):
Expand All @@ -73,8 +29,9 @@ def main(**kwargs):
with open(infile, "rb") as infile:
rtr = RTCMReader(infile)
for _, parsed in rtr:
if parsed.identity == "4076_201":
process_message(parsed)
coeffs = parse_4076_201(parsed)
if coeffs is not None:
print(coeffs)


if __name__ == "__main__":
Expand Down
60 changes: 3 additions & 57 deletions examples/msmparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,62 +38,10 @@

from sys import argv

from pyrtcm import ATT_NCELL # list of all sat attribute names
from pyrtcm import ATT_NSAT # list of all cell attribute names
from pyrtcm import RTCM_MSGIDS, RTCMMessage, RTCMReader, cell2prn, sat2prn

# map of msg identity to GNSS name, epoch attribute name
GNSSMAP = {
"107": ("GPS", "DF004"),
"108": ("GLONASS", "DF034"),
"109": ("GALILEO", "DF248"),
"110": ("SBAS", "DF004"),
"111": ("QZSS", "DF428"),
"112": ("BEIDOU", "DF427"),
"113": ("NAVIC", "DF546"),
}


def process_msm(msg: RTCMMessage) -> tuple:
"""
Process individual MSM message.
:return: tuple of (metadata, sat data array, cell data array)
:rtype: tuple
"""

satmap = sat2prn(msg) # maps indices to satellite PRNs
cellmap = cell2prn(msg) # maps indices to cells (satellite PRN, signal ID)
meta = {}
gmap = GNSSMAP[msg.identity[0:3]]
meta["identity"] = msg.identity
meta["gnss"] = gmap[0]
meta["station"] = msg.DF003
meta["epoch"] = getattr(msg, gmap[1])
meta["sats"] = msg.NSat
meta["cells"] = msg.NCell
msmsats = []
for i in range(1, msg.NSat + 1): # iterate through satellites
sats = {}
sats["PRN"] = satmap[i]
for attr in ATT_NSAT:
if hasattr(msg, f"{attr}_{i:02d}"):
sats[attr] = getattr(msg, f"{attr}_{i:02d}")
msmsats.append(sats)
msmcells = []
for i in range(1, msg.NCell + 1): # iterate through cells (satellite/signal)
cells = {}
cells["PRN"], cells["SIGNAL"] = cellmap[i]
for attr in ATT_NCELL:
if hasattr(msg, f"{attr}_{i:02d}"):
cells[attr] = getattr(msg, f"{attr}_{i:02d}")
msmcells.append(cells)

return (meta, msmsats, msmcells)
from pyrtcm import RTCMReader, parse_msm


def main(**kwargs):

"""
Main routine.
Expand All @@ -107,11 +55,9 @@ def main(**kwargs):
for _, parsed in rtr:
if parsed is not None:
try:
if "MSM" in RTCM_MSGIDS[parsed.identity]:
# print(parsed)
msmarray = process_msm(parsed)
msmarray = parse_msm(parsed)
if msmarray is not None:
print(msmarray)

# to then iterate through a specific data item,
# e.g. the satellite DF398 (rough range) value:
for sat in msmarray[1]: # satellite data array
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 = "pyrtcm"
authors = [{ name = "semuadmin", email = "[email protected]" }]
maintainers = [{ name = "semuadmin", email = "[email protected]" }]
description = "RTCM3 protocol parser"
version = "1.0.19"
version = "1.0.20"
license = { file = "LICENSE" }
readme = "README.md"
requires-python = ">=3.8"
Expand Down
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.0.19"
__version__ = "1.0.20"
85 changes: 80 additions & 5 deletions src/pyrtcm/rtcmhelpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@
:license: BSD 3-Clause
"""

# pylint: disable=invalid-name

from datetime import datetime, timedelta

from pyrtcm.exceptions import RTCMTypeError
from pyrtcm.rtcmtables import PRNSIGMAP
from pyrtcm.rtcmtypes_core import RTCM_DATA_FIELDS
from pyrtcm.rtcmtypes_core import ATT_NCELL, ATT_NSAT, COEFFS, GNSSMAP, RTCM_DATA_FIELDS


def att2idx(att: str) -> int:
Expand Down Expand Up @@ -102,14 +100,14 @@ def calc_crc24q(message: bytes) -> int:
"""

POLY = 0x1864CFB
poly = 0x1864CFB
crc = 0
for octet in message:
crc ^= octet << 16
for _ in range(8):
crc <<= 1
if crc & 0x1000000:
crc ^= POLY
crc ^= poly
return crc & 0xFFFFFF


Expand Down Expand Up @@ -356,3 +354,80 @@ def escapeall(val: bytes) -> str:
"""

return "b'{}'".format("".join(f"\\x{b:02x}" for b in val))


def parse_msm(msg: object) -> tuple:
"""
Parse individual MSM message into iterable data arrays.
:param RTCMMessage msg: RTCM MSM Message
:return: tuple of (metadata, sat data array, cell data array)
:rtype: tuple
"""

if not msg.ismsm:
return None

satmap = sat2prn(msg) # maps indices to satellite PRNs
cellmap = cell2prn(msg) # maps indices to cells (satellite PRN, signal ID)
meta = {}
gmap = GNSSMAP[msg.identity[0:3]]
meta["identity"] = msg.identity
meta["gnss"] = gmap[0]
meta["station"] = msg.DF003
meta["epoch"] = getattr(msg, gmap[1])
meta["sats"] = msg.NSat
meta["cells"] = msg.NCell
msmsats = []
for i in range(1, msg.NSat + 1): # iterate through satellites
sats = {}
sats["PRN"] = satmap[i]
for attr in ATT_NSAT:
if hasattr(msg, f"{attr}_{i:02d}"):
sats[attr] = getattr(msg, f"{attr}_{i:02d}")
msmsats.append(sats)
msmcells = []
for i in range(1, msg.NCell + 1): # iterate through cells (satellite/signal)
cells = {}
cells["PRN"], cells["SIGNAL"] = cellmap[i]
for attr in ATT_NCELL:
if hasattr(msg, f"{attr}_{i:02d}"):
cells[attr] = getattr(msg, f"{attr}_{i:02d}")
msmcells.append(cells)

return (meta, msmsats, msmcells)


def parse_4076_201(msg: object):
"""
Parse individual 4076_201 message into iterable data arrays.
:param RTCMMessage parsed: parsed 4076_201 message
:return: dict of {metadata, [cosine coefficients], [sine coefficients]} for each layer
:rtype: dict
"""

if msg.identity != "4076_201":
return None

hmc = {}
# for each ionospheric layer
for lyr in range(msg.IDF035 + 1): # number of ionospheric layers
hmc[lyr] = {}
hmc[lyr]["Layer Height"] = getattr(msg, f"IDF036_{lyr+1:02d}")
# for each coefficient (cosine & sine)
for field, coeff in COEFFS.values():
hmc[lyr][coeff] = []
i = 0
eof = False
# for each coefficient value
while not eof:
try:
hmc[lyr][coeff].append(
getattr(msg, f"{field}_{lyr+1:02d}_{i+1:02d}")
)
i += 1
except AttributeError:
eof = True

return hmc
Loading

0 comments on commit 8120e62

Please sign in to comment.