pygnssutils 1.2.4__tar.gz → 1.2.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pygnssutils-1.2.4/src/pygnssutils.egg-info → pygnssutils-1.2.5}/PKG-INFO +4 -4
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/README.md +3 -3
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/_version.py +1 -1
- pygnssutils-1.2.5/src/pygnssutils/rawnav.py +340 -0
- pygnssutils-1.2.4/src/pygnssutils/rawnav.py → pygnssutils-1.2.5/src/pygnssutils/rawnav_reader.py +68 -393
- pygnssutils-1.2.4/src/pygnssutils/rinex_subframes_bds.py → pygnssutils-1.2.5/src/pygnssutils/rawnav_subframes_bds.py +7 -7
- pygnssutils-1.2.4/src/pygnssutils/rinex_subframes_gal.py → pygnssutils-1.2.5/src/pygnssutils/rawnav_subframes_gal.py +4 -2
- pygnssutils-1.2.4/src/pygnssutils/rinex_subframes_glo.py → pygnssutils-1.2.5/src/pygnssutils/rawnav_subframes_glo.py +9 -2
- pygnssutils-1.2.4/src/pygnssutils/rinex_subframes_gps.py → pygnssutils-1.2.5/src/pygnssutils/rawnav_subframes_gps.py +7 -17
- pygnssutils-1.2.5/src/pygnssutils/rawnav_subframes_irn.py +171 -0
- pygnssutils-1.2.5/src/pygnssutils/rawnav_subframes_qzs.py +518 -0
- pygnssutils-1.2.5/src/pygnssutils/rawnav_subframes_sba.py +144 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/rinex_conv.py +2 -4
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/rinex_conv_met.py +0 -4
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/rinex_conv_nav.py +406 -24
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/rinex_conv_obs.py +2 -6
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/rinex_globals.py +80 -27
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/rinex_helpers.py +16 -32
- {pygnssutils-1.2.4 → pygnssutils-1.2.5/src/pygnssutils.egg-info}/PKG-INFO +4 -4
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils.egg-info/SOURCES.txt +9 -4
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/tests/test_rawnav.py +1 -1
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/tests/test_rinex.py +23 -99
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/tests/test_rinex_defs.py +70 -6
- pygnssutils-1.2.5/tests/test_rinex_parse.py +491 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/LICENSE +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/pyproject.toml +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/setup.cfg +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/__init__.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/exceptions.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/globals.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/gnssmqttclient.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/gnssmqttclient_cli.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/gnssntripclient.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/gnssntripclient_cli.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/gnssreader.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/gnssserver.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/gnssserver_cli.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/gnssstreamer.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/gnssstreamer_cli.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/helpers.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/mqttmessage.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/rinex_conv_cli.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils/socket_server.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils.egg-info/dependency_links.txt +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils.egg-info/entry_points.txt +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils.egg-info/requires.txt +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/src/pygnssutils.egg-info/top_level.txt +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/tests/test_cli.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/tests/test_gnssstreamer.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/tests/test_socketwrapper.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/tests/test_sourcetable.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/tests/test_static.py +0 -0
- {pygnssutils-1.2.4 → pygnssutils-1.2.5}/tests/test_stream.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pygnssutils
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.5
|
|
4
4
|
Summary: GNSS Command Line Utilities
|
|
5
5
|
Author-email: Steve Smith <semuadmin@semuconsulting.com>
|
|
6
6
|
Maintainer-email: Steve Smith <semuadmin@semuconsulting.com>
|
|
@@ -328,7 +328,7 @@ Streaming terminated, 93 messages processed with 0 errors.
|
|
|
328
328
|
|
|
329
329
|
### <a name="gnssstreamerservice">Installing gnssstreamer as a systemd service on Linux</a>
|
|
330
330
|
|
|
331
|
-
This example should work for most Linux distributions running `systemd` and `python3>=3.
|
|
331
|
+
This example should work for most Linux distributions running `systemd` and `python3>=3.10`, including Raspberry Pi OS (*substitute `dnf` or `pacman` for `apt` as necessary*).
|
|
332
332
|
|
|
333
333
|
The example `gnssstreamer.conf` file will set up `gnssstreamer` as a multi-client TCP socket server accessible on `hostip:50012` (*check TCP port 50012 is allowed through any active firewall*).
|
|
334
334
|
|
|
@@ -543,11 +543,11 @@ class pygnssutils.rinex_conv.RinexConvertor(app, rinex_version, rinex_type, gnss
|
|
|
543
543
|
|
|
544
544
|
A command line utility and Python class `RinexConverter` to convert binary GNSS data logs to RINEX text file format.
|
|
545
545
|
|
|
546
|
-
**NB: RINEX conversion is an experimental work in progress (*contributions and feedback welcome*). The current
|
|
546
|
+
**NB: RINEX conversion is an experimental work in progress (*contributions and feedback welcome*). The current release implements the following functionality:**
|
|
547
547
|
|
|
548
548
|
1. RINEX versions 3.05 and 4.02.
|
|
549
549
|
1. Convert binary UBX RXM-RAW or RXM-RAWX (raw observation) data from u-blox receivers (e.g. ZED-F9P) to RINEX Observation file format.
|
|
550
|
-
1. Convert binary RXM-SFRBX (navigation subframe) data from u-blox receivers to RINEX Navigation file format.
|
|
550
|
+
1. Convert binary RXM-SFRBX (navigation subframe) data from u-blox receivers to RINEX Navigation file format.
|
|
551
551
|
1. Convert RTCM3 Ephemerides messages (1019, 1020, 1041-1046) from any source (including NTRIP caster or RTK base station receiver) to RINEX Navigation file format.
|
|
552
552
|
1. Convert NMEA MWD (wind speed and direction) and XDR (temperature and pressure) sensor data to RINEX Meteorology file format.
|
|
553
553
|
|
|
@@ -284,7 +284,7 @@ Streaming terminated, 93 messages processed with 0 errors.
|
|
|
284
284
|
|
|
285
285
|
### <a name="gnssstreamerservice">Installing gnssstreamer as a systemd service on Linux</a>
|
|
286
286
|
|
|
287
|
-
This example should work for most Linux distributions running `systemd` and `python3>=3.
|
|
287
|
+
This example should work for most Linux distributions running `systemd` and `python3>=3.10`, including Raspberry Pi OS (*substitute `dnf` or `pacman` for `apt` as necessary*).
|
|
288
288
|
|
|
289
289
|
The example `gnssstreamer.conf` file will set up `gnssstreamer` as a multi-client TCP socket server accessible on `hostip:50012` (*check TCP port 50012 is allowed through any active firewall*).
|
|
290
290
|
|
|
@@ -499,11 +499,11 @@ class pygnssutils.rinex_conv.RinexConvertor(app, rinex_version, rinex_type, gnss
|
|
|
499
499
|
|
|
500
500
|
A command line utility and Python class `RinexConverter` to convert binary GNSS data logs to RINEX text file format.
|
|
501
501
|
|
|
502
|
-
**NB: RINEX conversion is an experimental work in progress (*contributions and feedback welcome*). The current
|
|
502
|
+
**NB: RINEX conversion is an experimental work in progress (*contributions and feedback welcome*). The current release implements the following functionality:**
|
|
503
503
|
|
|
504
504
|
1. RINEX versions 3.05 and 4.02.
|
|
505
505
|
1. Convert binary UBX RXM-RAW or RXM-RAWX (raw observation) data from u-blox receivers (e.g. ZED-F9P) to RINEX Observation file format.
|
|
506
|
-
1. Convert binary RXM-SFRBX (navigation subframe) data from u-blox receivers to RINEX Navigation file format.
|
|
506
|
+
1. Convert binary RXM-SFRBX (navigation subframe) data from u-blox receivers to RINEX Navigation file format.
|
|
507
507
|
1. Convert RTCM3 Ephemerides messages (1019, 1020, 1041-1046) from any source (including NTRIP caster or RTK base station receiver) to RINEX Navigation file format.
|
|
508
508
|
1. Convert NMEA MWD (wind speed and direction) and XDR (temperature and pressure) sensor data to RINEX Meteorology file format.
|
|
509
509
|
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"""
|
|
2
|
+
rawnav.py
|
|
3
|
+
|
|
4
|
+
The RawNav class parses and stores the individual attributes
|
|
5
|
+
(ephemerides, ionospheric & clock corrections, etc.) of one
|
|
6
|
+
or more raw GNSS NAV subframes.
|
|
7
|
+
|
|
8
|
+
Once a RawNav object is instantiated, the `parse` function can
|
|
9
|
+
be invoked repeatedly to collate data from separate sequential
|
|
10
|
+
subframes e.g. for GPS LNAV, subframe 1 contains clock corrections,
|
|
11
|
+
subframes 2 & 3 contain ephemerides and subframe 4 page 18 contains
|
|
12
|
+
ionospheric corrections.
|
|
13
|
+
|
|
14
|
+
An `subframeacq` bitfield signifies which subframe/page IDs have
|
|
15
|
+
been acquired, and hence whether or not the RawNav frame contains
|
|
16
|
+
sufficient information to be converted to a NAV record e.g. as a
|
|
17
|
+
precursor to RINEX conversion.
|
|
18
|
+
|
|
19
|
+
The objective is to handle any GNSS subframe format for which:
|
|
20
|
+
|
|
21
|
+
- the complete, unpadded subframe is represented as an unsigned little-endian
|
|
22
|
+
integer.
|
|
23
|
+
- the subframe definition dictionary has been transcribed from the relevant
|
|
24
|
+
GNSS ICD (Interface Control Document) with standardized ascii
|
|
25
|
+
field names e.g. `omegadot`, `sqrta`, `cus`, `tauc`, etc.
|
|
26
|
+
|
|
27
|
+
Format of subframe definition dictionary::
|
|
28
|
+
|
|
29
|
+
dict[field_name, tuple[offset, length, encoding, scaling]
|
|
30
|
+
|
|
31
|
+
where offset and length are in bits (see, for example,
|
|
32
|
+
`rawnav_subframes_gps.py`).
|
|
33
|
+
|
|
34
|
+
MSB, ISB (intermediate bits) and LSB field names MUST be suffixed "_msb",
|
|
35
|
+
"_isb" and "_lsb" respectively - the `parse` function will automatically
|
|
36
|
+
combine them.
|
|
37
|
+
|
|
38
|
+
Created on 20 Apr 2026
|
|
39
|
+
|
|
40
|
+
:author: semuadmin (Steve Smith)
|
|
41
|
+
:copyright: semuadmin © 2026
|
|
42
|
+
:license: BSD 3-Clause
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
# pylint: disable=too-many-arguments, too-many-positional-arguments
|
|
46
|
+
|
|
47
|
+
import struct
|
|
48
|
+
from logging import getLogger
|
|
49
|
+
from typing import Literal
|
|
50
|
+
|
|
51
|
+
from pygnssutils.exceptions import RINEXProcessingError
|
|
52
|
+
from pygnssutils.rinex_globals import GLO
|
|
53
|
+
from pygnssutils.rinex_helpers import get_svcode
|
|
54
|
+
|
|
55
|
+
IS1 = "_is1"
|
|
56
|
+
"""IS1 field name suffix (between MSB and ISB)"""
|
|
57
|
+
ISB = "_isb"
|
|
58
|
+
"""ISB field name suffix (between MSB/IS1 and LSB)"""
|
|
59
|
+
LSB = "_lsb"
|
|
60
|
+
"""LSB field name suffix"""
|
|
61
|
+
MSB = "_msb"
|
|
62
|
+
"""MSB field name suffix"""
|
|
63
|
+
TOC = "toc"
|
|
64
|
+
"""TOC (time of clock) field name - used to establish epoch"""
|
|
65
|
+
TOW = "tow"
|
|
66
|
+
"""HOW TOW field name"""
|
|
67
|
+
SID = "sid"
|
|
68
|
+
"""subframe id field name"""
|
|
69
|
+
SPID = "spid"
|
|
70
|
+
"""subframe page id field name"""
|
|
71
|
+
WN = "wn"
|
|
72
|
+
"""WN (week number) field name - used to establish epoch"""
|
|
73
|
+
|
|
74
|
+
PREAMBLE = "_preamble"
|
|
75
|
+
VALPREAMBLE = "_valid_preamble"
|
|
76
|
+
D = "D" # IEEE 754 64-bit double float
|
|
77
|
+
F = "F" # IEEE 754 32-bit float
|
|
78
|
+
S = "S" # 2's complement signed integer
|
|
79
|
+
U = "U" # unsiged integer
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class RawNav:
|
|
83
|
+
"""
|
|
84
|
+
Raw Navigation Class.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
# pylint: disable=too-many-instance-attributes
|
|
88
|
+
|
|
89
|
+
def __init__(
|
|
90
|
+
self,
|
|
91
|
+
gnss: Literal["G", "R", "E", "C", "J", "S", "I"],
|
|
92
|
+
svid: int,
|
|
93
|
+
sigcode: str,
|
|
94
|
+
**kwargs, # pylint: disable=unused-argument
|
|
95
|
+
):
|
|
96
|
+
"""
|
|
97
|
+
Constructor.
|
|
98
|
+
|
|
99
|
+
:param Literal["G","R","E","C","J","S","I"] gnss: GNSS code
|
|
100
|
+
:param int svid: RINEX SV id e.g. 14
|
|
101
|
+
:param str sigcode: RINEX signal code e.g. "1C"
|
|
102
|
+
:param dict kwargs: optional keyword arguments
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
self._logger = getLogger(__name__)
|
|
106
|
+
self._gnss = gnss
|
|
107
|
+
self._svid = svid
|
|
108
|
+
self._sigcode = sigcode
|
|
109
|
+
if gnss != GLO:
|
|
110
|
+
self.wn = -1
|
|
111
|
+
self.toc = -1
|
|
112
|
+
self.tow = -1
|
|
113
|
+
self._subframeacq = 0
|
|
114
|
+
self._msb = {}
|
|
115
|
+
self._isb = {}
|
|
116
|
+
self._is1 = {}
|
|
117
|
+
self._lsb = {}
|
|
118
|
+
self._firsttoc = 999999999
|
|
119
|
+
self._lasttoc = 0
|
|
120
|
+
self._firstwn = 999999999
|
|
121
|
+
self._lastwn = 0
|
|
122
|
+
|
|
123
|
+
def parse(
|
|
124
|
+
self,
|
|
125
|
+
data: int,
|
|
126
|
+
subframedef: dict[str, tuple[int, int, str, int]],
|
|
127
|
+
subframeacq: int,
|
|
128
|
+
sequence: bool = True,
|
|
129
|
+
):
|
|
130
|
+
"""
|
|
131
|
+
Parse raw subframe data into its constituent attributes.
|
|
132
|
+
|
|
133
|
+
:param int data: raw, unpadded input data
|
|
134
|
+
:param dict[str, tuple[int, int, str, int]] subframedef: subframe \
|
|
135
|
+
definition dictionary (from GNSS ICD)
|
|
136
|
+
:param int subframeacq: subframe acquisition bitmask
|
|
137
|
+
:param bool sequence: process subframe as part of a contiguous sequence (True)
|
|
138
|
+
:raises: RINEXProcessingError
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
|
|
143
|
+
# get exemplary preamble value if one is available
|
|
144
|
+
valpre = subframedef.pop(VALPREAMBLE, 0)
|
|
145
|
+
|
|
146
|
+
# get total subframe length in bits
|
|
147
|
+
offset, bitlen, _, _ = list(subframedef.values())[-1]
|
|
148
|
+
sfrlen = offset + bitlen
|
|
149
|
+
|
|
150
|
+
# parse each attribute in subframe, combining MSB, ISB and
|
|
151
|
+
# LSB fields where appropriate
|
|
152
|
+
for att, (offset, length, encoding, scaling) in subframedef.items():
|
|
153
|
+
|
|
154
|
+
if att[0:1] == "_": # ignore non-data attributes
|
|
155
|
+
continue
|
|
156
|
+
bits = data >> (sfrlen - offset - length) & (2**length - 1)
|
|
157
|
+
|
|
158
|
+
# validate preamble if an exemplary value is available
|
|
159
|
+
if valpre and att == PREAMBLE and bits != valpre:
|
|
160
|
+
raise RINEXProcessingError(
|
|
161
|
+
f"Invalid preamble - expected 0b{valpre:b}, got 0b{bits:b}"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# recombine MSB, IS1, ISB and LSB bits
|
|
165
|
+
if att[-4:].lower() == MSB: # most significant bits
|
|
166
|
+
self._msb[att] = (bits, length, encoding, scaling)
|
|
167
|
+
continue
|
|
168
|
+
if att[-4:].lower() == IS1: # intermediate bits 1 (BDS)
|
|
169
|
+
self._is1[att] = (bits, length, encoding, scaling)
|
|
170
|
+
continue
|
|
171
|
+
if att[-4:].lower() == ISB: # intermediate bits (BDS)
|
|
172
|
+
self._isb[att] = (bits, length, encoding, scaling)
|
|
173
|
+
continue
|
|
174
|
+
if att[-4:].lower() == LSB: # least significant bits
|
|
175
|
+
attns = att[:-4]
|
|
176
|
+
msbbits, msblen, _, _ = self._msb.pop(f"{attns}{MSB}", (0, 0, 0, 0))
|
|
177
|
+
is1bits, is1len, _, _ = self._is1.pop(f"{attns}{IS1}", (0, 0, 0, 0))
|
|
178
|
+
isbbits, isblen, _, _ = self._isb.pop(f"{attns}{ISB}", (0, 0, 0, 0))
|
|
179
|
+
bits = (
|
|
180
|
+
(msbbits << (is1len + isblen + length))
|
|
181
|
+
+ (is1bits << (isblen + length))
|
|
182
|
+
+ (isbbits << length)
|
|
183
|
+
+ bits
|
|
184
|
+
)
|
|
185
|
+
if msblen: # if combining with msb...
|
|
186
|
+
att = attns # strip "_lsb" suffix
|
|
187
|
+
length += msblen + is1len + isblen
|
|
188
|
+
|
|
189
|
+
val = self._bits2val(bits, length, encoding, scaling)
|
|
190
|
+
setattr(self, att, val)
|
|
191
|
+
|
|
192
|
+
if att in (SID, SPID): # update subframe acquisition status
|
|
193
|
+
self._subframeacq |= subframeacq
|
|
194
|
+
|
|
195
|
+
if not sequence:
|
|
196
|
+
self._store_orphaned_msb()
|
|
197
|
+
|
|
198
|
+
except (ValueError, TypeError, KeyError) as err:
|
|
199
|
+
raise RINEXProcessingError(
|
|
200
|
+
"Invalid subframe definition dictionary."
|
|
201
|
+
) from err
|
|
202
|
+
|
|
203
|
+
def _store_orphaned_msb(self):
|
|
204
|
+
"""
|
|
205
|
+
If not processing sequentially, store any 'orphaned' MSB/IS1 now rather
|
|
206
|
+
than waiting for associated ISB/LSB from next subframe in sequence.
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
for msb in (self._msb, self._is1):
|
|
210
|
+
for att, (bits, length, encoding, scaling) in msb.items():
|
|
211
|
+
val = self._bits2val(bits, length, encoding, scaling)
|
|
212
|
+
setattr(self, att, val)
|
|
213
|
+
msb = {}
|
|
214
|
+
|
|
215
|
+
def _bits2val(
|
|
216
|
+
self, vali: int, length: int, encoding: str, scaling: int
|
|
217
|
+
) -> int | float:
|
|
218
|
+
"""
|
|
219
|
+
Convert encoded bits to value.
|
|
220
|
+
|
|
221
|
+
:param int vali: value as raw integer
|
|
222
|
+
:param int length: length in bits
|
|
223
|
+
:param str encoding: bit encoding e.g. U, S, N, F
|
|
224
|
+
:param int scaling: scaling factor (0 = no scaling)
|
|
225
|
+
:return: decoded value
|
|
226
|
+
:rtype: int | float
|
|
227
|
+
:raises: RINEXProcessingError
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
val = vali
|
|
231
|
+
if encoding == "U": # unsigned integer
|
|
232
|
+
pass
|
|
233
|
+
elif encoding == "S": # 2's complement signed integer
|
|
234
|
+
if vali >= (1 << (length - 1)):
|
|
235
|
+
val = vali - (1 << length)
|
|
236
|
+
elif encoding == "F": # IEEE 754 32 bit floating point
|
|
237
|
+
valb = int.to_bytes(vali, 4, "little")
|
|
238
|
+
val = struct.unpack("<f", valb)[0]
|
|
239
|
+
elif encoding == "D": # IEEE 754 64 bit double floating point
|
|
240
|
+
valb = int.to_bytes(vali, 8, "little")
|
|
241
|
+
val = struct.unpack("<d", valb)[0]
|
|
242
|
+
else:
|
|
243
|
+
raise RINEXProcessingError(f"Unknown attribute type {encoding}")
|
|
244
|
+
if scaling not in (0, 1):
|
|
245
|
+
val *= scaling
|
|
246
|
+
return val
|
|
247
|
+
|
|
248
|
+
def __str__(self) -> str:
|
|
249
|
+
"""
|
|
250
|
+
Human readable representation.
|
|
251
|
+
|
|
252
|
+
:return: human readable representation
|
|
253
|
+
:rtype: str
|
|
254
|
+
"""
|
|
255
|
+
|
|
256
|
+
stg = (
|
|
257
|
+
f"<RAWNAV({self.identity}, gnss={self._gnss}, svid={self._svid}, "
|
|
258
|
+
f"sigid={self._sigcode}, sfracq={self._subframeacq}, "
|
|
259
|
+
)
|
|
260
|
+
for i, att in enumerate(self.__dict__):
|
|
261
|
+
if att[0] != "_": # only show public attributes
|
|
262
|
+
val = self.__dict__[att]
|
|
263
|
+
stg += att + "=" + str(val)
|
|
264
|
+
if i < len(self.__dict__) - 1:
|
|
265
|
+
stg += ", "
|
|
266
|
+
stg += ")>"
|
|
267
|
+
return stg
|
|
268
|
+
|
|
269
|
+
@property
|
|
270
|
+
def identity(self) -> str:
|
|
271
|
+
"""
|
|
272
|
+
Getter for identity.
|
|
273
|
+
|
|
274
|
+
:return: identity
|
|
275
|
+
:rtype: str
|
|
276
|
+
"""
|
|
277
|
+
|
|
278
|
+
svcode = get_svcode(self._gnss, self._svid, leadzero=True)
|
|
279
|
+
return f"{svcode}{self._sigcode}"
|
|
280
|
+
|
|
281
|
+
@property
|
|
282
|
+
def gnss(self) -> str:
|
|
283
|
+
"""
|
|
284
|
+
Getter for GNSS code e.g. "G"
|
|
285
|
+
|
|
286
|
+
:return: gnss
|
|
287
|
+
:rtype: str
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
return self._gnss
|
|
291
|
+
|
|
292
|
+
@property
|
|
293
|
+
def svid(self) -> int:
|
|
294
|
+
"""
|
|
295
|
+
Getter for SV id e.g. 14
|
|
296
|
+
|
|
297
|
+
:return: svid
|
|
298
|
+
:rtype: int
|
|
299
|
+
"""
|
|
300
|
+
|
|
301
|
+
return self._svid
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def svcode(self) -> str:
|
|
305
|
+
"""
|
|
306
|
+
Getter for SV code (gnss & prn).
|
|
307
|
+
|
|
308
|
+
:return: svcode e.g. "G14"
|
|
309
|
+
:rtype: str
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
return get_svcode(self._gnss, self._svid, leadzero=False)
|
|
313
|
+
|
|
314
|
+
@property
|
|
315
|
+
def sigcode(self) -> str:
|
|
316
|
+
"""
|
|
317
|
+
Getter for signal code in RINEX format.
|
|
318
|
+
|
|
319
|
+
:return: signal code e.g. '1C'
|
|
320
|
+
:rtype: str
|
|
321
|
+
"""
|
|
322
|
+
|
|
323
|
+
return self._sigcode
|
|
324
|
+
|
|
325
|
+
@property
|
|
326
|
+
def subframeacq(self) -> int:
|
|
327
|
+
"""
|
|
328
|
+
Getter for subframe acquisition status.
|
|
329
|
+
|
|
330
|
+
Bitfield signifying which subframe IDs have been acquired:
|
|
331
|
+
|
|
332
|
+
- subframeacq & 0b001 => page 1
|
|
333
|
+
- subframeacq & 0b010 => page 2,
|
|
334
|
+
- subframeacq & 0b100 => page 3, etc.
|
|
335
|
+
|
|
336
|
+
:return: subframe acquisition status.
|
|
337
|
+
:rtype: int
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
return self._subframeacq
|