pygnssutils 1.2.2__tar.gz → 1.2.4__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.
Files changed (48) hide show
  1. {pygnssutils-1.2.2/src/pygnssutils.egg-info → pygnssutils-1.2.4}/PKG-INFO +8 -10
  2. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/README.md +4 -6
  3. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/pyproject.toml +3 -3
  4. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/_version.py +1 -1
  5. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/globals.py +1 -1
  6. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/rawnav.py +150 -72
  7. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/rinex_conv.py +23 -2
  8. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/rinex_conv_cli.py +36 -1
  9. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/rinex_conv_met.py +2 -2
  10. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/rinex_conv_nav.py +265 -138
  11. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/rinex_conv_obs.py +15 -10
  12. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/rinex_globals.py +19 -12
  13. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/rinex_helpers.py +96 -34
  14. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/rinex_subframes_bds.py +18 -12
  15. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/rinex_subframes_gal.py +4 -3
  16. pygnssutils-1.2.4/src/pygnssutils/rinex_subframes_glo.py +136 -0
  17. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/rinex_subframes_gps.py +11 -8
  18. {pygnssutils-1.2.2 → pygnssutils-1.2.4/src/pygnssutils.egg-info}/PKG-INFO +8 -10
  19. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils.egg-info/SOURCES.txt +2 -0
  20. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils.egg-info/requires.txt +3 -3
  21. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/tests/test_rinex.py +82 -41
  22. pygnssutils-1.2.4/tests/test_rinex_defs.py +148 -0
  23. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/LICENSE +0 -0
  24. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/setup.cfg +0 -0
  25. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/__init__.py +0 -0
  26. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/exceptions.py +0 -0
  27. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/gnssmqttclient.py +0 -0
  28. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/gnssmqttclient_cli.py +0 -0
  29. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/gnssntripclient.py +0 -0
  30. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/gnssntripclient_cli.py +0 -0
  31. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/gnssreader.py +0 -0
  32. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/gnssserver.py +0 -0
  33. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/gnssserver_cli.py +0 -0
  34. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/gnssstreamer.py +0 -0
  35. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/gnssstreamer_cli.py +0 -0
  36. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/helpers.py +0 -0
  37. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/mqttmessage.py +0 -0
  38. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils/socket_server.py +0 -0
  39. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils.egg-info/dependency_links.txt +0 -0
  40. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils.egg-info/entry_points.txt +0 -0
  41. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/src/pygnssutils.egg-info/top_level.txt +0 -0
  42. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/tests/test_cli.py +0 -0
  43. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/tests/test_gnssstreamer.py +0 -0
  44. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/tests/test_rawnav.py +0 -0
  45. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/tests/test_socketwrapper.py +0 -0
  46. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/tests/test_sourcetable.py +0 -0
  47. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/tests/test_static.py +0 -0
  48. {pygnssutils-1.2.2 → pygnssutils-1.2.4}/tests/test_stream.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygnssutils
3
- Version: 1.2.2
3
+ Version: 1.2.4
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>
@@ -34,11 +34,11 @@ Requires-Dist: certifi>=2025.0.0
34
34
  Requires-Dist: paho-mqtt>=2.1.0
35
35
  Requires-Dist: pyserial>=3.5
36
36
  Requires-Dist: pyspartn>=1.0.8
37
- Requires-Dist: pyubx2>=1.3.0
38
- Requires-Dist: pynmeagps>=1.1.4
37
+ Requires-Dist: pyubx2>=1.3.3
38
+ Requires-Dist: pynmeagps>=1.1.5
39
39
  Requires-Dist: pysbf2>=1.0.4
40
40
  Requires-Dist: pyubxutils>=1.0.6
41
- Requires-Dist: pyqgc>=0.2.2
41
+ Requires-Dist: pyqgc>=1.0.0
42
42
  Requires-Dist: pyunigps>=1.0.0
43
43
  Dynamic: license-file
44
44
 
@@ -81,7 +81,7 @@ designated output stream.
81
81
  a simple SPARTN IP (MQTT) Client which receives SPARTN correction data from an SPARTN IP location service and (optionally) sends this to a
82
82
  designated output stream.
83
83
  1. `SocketServer` class based on the native Python `ThreadingTCPServer`. Capable of operating in two modes - Socket Server or NTRIP Caster. Provides two alternate client request handler classes - `ClientHandler` (HTTP) or `ClientHandlerTLS` (HTTPS).
84
- 1. `RinexConverter` class and its associated [`pyrinexconv`](#rinexconvert) CLI utility. This implements a binary GNSS data log file to RINEX text file conversion facility. **NB: this initial ALPHA release has limited functionality.**
84
+ 1. `RinexConverter` class and its associated [`pyrinexconv`](#rinexconvert) CLI utility. This implements a binary GNSS data log file to RINEX text file conversion facility. **NB: RINEX conversion is currently an experimental work in progress (*contributions and feedback welcome*)**
85
85
 
86
86
  The pygnssutils homepage is located at [https://github.com/semuconsulting/pygnssutils](https://github.com/semuconsulting/pygnssutils).
87
87
 
@@ -543,17 +543,15 @@ 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: This ALPHA preview release is limited to the following experimental functionality:**
546
+ **NB: RINEX conversion is an experimental work in progress (*contributions and feedback welcome*). The current ALPHA 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. **Currently supports GPS LNAV & CNAV, GAL FNAV & INAV, BDS D1**, but the underlying `RinexConverterNavigation` class is readily extensible.
550
+ 1. Convert binary RXM-SFRBX (navigation subframe) data from u-blox receivers to RINEX Navigation file format. **Currently supports GPS LNAV/CNAV, GAL FNAV/INAV, BDS D1/D2 & GLO L1OF**, but the underlying `RinexConverterNavigation` class will be enhanced in future releases.
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
 
554
- A Graphical User Interface for this utility will be added to [PyGPSClient](https://github.com/semuconsulting/PyGPSClient).
555
-
556
- The intention behind this preview release is to **gauge the wider appetite for further development** of a cross-platform Python RINEX conversion utility and enhance functionality in a future release, as and when time permits. **CONTRIBUTORS WELCOME**.
554
+ A Graphical User Interface for this utility has been added to [PyGPSClient](https://github.com/semuconsulting/PyGPSClient).
557
555
 
558
556
  Assuming the u-blox receiver is already configured to output raw observation (UBX-RXM-RAWX) and navigation subframe (UBX-RXM-SFRBX) data, a suitable input log file can be created using the pygnssutils `gnssstreamer` CLI utility e.g. ...
559
557
 
@@ -37,7 +37,7 @@ designated output stream.
37
37
  a simple SPARTN IP (MQTT) Client which receives SPARTN correction data from an SPARTN IP location service and (optionally) sends this to a
38
38
  designated output stream.
39
39
  1. `SocketServer` class based on the native Python `ThreadingTCPServer`. Capable of operating in two modes - Socket Server or NTRIP Caster. Provides two alternate client request handler classes - `ClientHandler` (HTTP) or `ClientHandlerTLS` (HTTPS).
40
- 1. `RinexConverter` class and its associated [`pyrinexconv`](#rinexconvert) CLI utility. This implements a binary GNSS data log file to RINEX text file conversion facility. **NB: this initial ALPHA release has limited functionality.**
40
+ 1. `RinexConverter` class and its associated [`pyrinexconv`](#rinexconvert) CLI utility. This implements a binary GNSS data log file to RINEX text file conversion facility. **NB: RINEX conversion is currently an experimental work in progress (*contributions and feedback welcome*)**
41
41
 
42
42
  The pygnssutils homepage is located at [https://github.com/semuconsulting/pygnssutils](https://github.com/semuconsulting/pygnssutils).
43
43
 
@@ -499,17 +499,15 @@ 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: This ALPHA preview release is limited to the following experimental functionality:**
502
+ **NB: RINEX conversion is an experimental work in progress (*contributions and feedback welcome*). The current ALPHA 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. **Currently supports GPS LNAV & CNAV, GAL FNAV & INAV, BDS D1**, but the underlying `RinexConverterNavigation` class is readily extensible.
506
+ 1. Convert binary RXM-SFRBX (navigation subframe) data from u-blox receivers to RINEX Navigation file format. **Currently supports GPS LNAV/CNAV, GAL FNAV/INAV, BDS D1/D2 & GLO L1OF**, but the underlying `RinexConverterNavigation` class will be enhanced in future releases.
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
 
510
- A Graphical User Interface for this utility will be added to [PyGPSClient](https://github.com/semuconsulting/PyGPSClient).
511
-
512
- The intention behind this preview release is to **gauge the wider appetite for further development** of a cross-platform Python RINEX conversion utility and enhance functionality in a future release, as and when time permits. **CONTRIBUTORS WELCOME**.
510
+ A Graphical User Interface for this utility has been added to [PyGPSClient](https://github.com/semuconsulting/PyGPSClient).
513
511
 
514
512
  Assuming the u-blox receiver is already configured to output raw observation (UBX-RXM-RAWX) and navigation subframe (UBX-RXM-SFRBX) data, a suitable input log file can be created using the pygnssutils `gnssstreamer` CLI utility e.g. ...
515
513
 
@@ -38,11 +38,11 @@ dependencies = [
38
38
  "paho-mqtt>=2.1.0",
39
39
  "pyserial>=3.5",
40
40
  "pyspartn>=1.0.8",
41
- "pyubx2>=1.3.0",
42
- "pynmeagps>=1.1.4",
41
+ "pyubx2>=1.3.3",
42
+ "pynmeagps>=1.1.5",
43
43
  "pysbf2>=1.0.4",
44
44
  "pyubxutils>=1.0.6",
45
- "pyqgc>=0.2.2",
45
+ "pyqgc>=1.0.0",
46
46
  "pyunigps>=1.0.0",
47
47
  ]
48
48
 
@@ -8,4 +8,4 @@ Created on 2 Oct 2020
8
8
  :license: BSD 3-Clause
9
9
  """
10
10
 
11
- __version__ = "1.2.2"
11
+ __version__ = "1.2.4"
@@ -121,7 +121,7 @@ LOGLIMIT = 10485760 # max size of logfile in bytes
121
121
  NOGGA = -1
122
122
  """No GGA sentence to be sent (for NTRIP caster)"""
123
123
  EPILOG = (
124
- "© 2022 semuadmin (Steve Smith) BSD 3-Clause license"
124
+ "© 2022-2026 semuadmin (Steve Smith) BSD 3-Clause license"
125
125
  " - https://github.com/semuconsulting/pygnssutils/"
126
126
  )
127
127
  """CLI argument parser epilog"""
@@ -47,11 +47,11 @@ navigation data:
47
47
 
48
48
  https://www.u-blox.com/sites/default/files/ZED-F9P_IntegrationManual_UBX-18010802.pdf
49
49
 
50
- NB: Alpha Support currently limited to:
50
+ NB: Alpha release currently implements:
51
51
  - GPS LNAV, CNAV
52
52
  - GAL FNAV, INAV
53
- - BDS D1
54
- ... pending transcription of other GNSS ICDs.
53
+ - BDS D1, D2
54
+ - GLO L1OF
55
55
 
56
56
  Created on 20 Apr 2026
57
57
 
@@ -60,7 +60,7 @@ Created on 20 Apr 2026
60
60
  :license: BSD 3-Clause
61
61
  """
62
62
 
63
- # pylint: disable=fixme, unused-argument, unused-variable, too-many-arguments, too-many-positional-arguments
63
+ # pylint: disable=unused-argument, unused-variable, too-many-arguments, too-many-positional-arguments
64
64
 
65
65
  import struct
66
66
  from logging import getLogger
@@ -79,8 +79,8 @@ from pygnssutils.rinex_globals import (
79
79
  QZS,
80
80
  SBA,
81
81
  UBXRINEXGNSS,
82
- UBXRINEXOBSCODE,
83
82
  )
83
+ from pygnssutils.rinex_helpers import get_obscode_ubx
84
84
 
85
85
  IS1 = "_is1"
86
86
  """IS1 field name suffix (between MSB and ISB)"""
@@ -120,7 +120,7 @@ class RawNav:
120
120
  self,
121
121
  gnss: Literal["G", "R", "E", "C", "J", "S", "I"],
122
122
  svid: int,
123
- sigid: str,
123
+ sigcode: str,
124
124
  **kwargs, # pylint: disable=unused-argument
125
125
  ):
126
126
  """
@@ -128,17 +128,18 @@ class RawNav:
128
128
 
129
129
  :param Literal["G","R","E","C","J","S","I"] gnss: GNSS code
130
130
  :param int svid: RINEX SV id e.g. 14
131
- :param str sigid: RINEX signal id e.g. "1C"
131
+ :param str sigcode: RINEX signal code e.g. "1C"
132
132
  :param dict kwargs: optional keyword arguments
133
133
  """
134
134
 
135
135
  self._logger = getLogger(__name__)
136
136
  self._gnss = gnss
137
137
  self._svid = svid
138
- self._sigid = sigid
139
- self.wn = -1
140
- self.toc = -1
141
- self.tow = -1
138
+ self._sigcode = sigcode
139
+ if gnss != "R":
140
+ self.wn = -1
141
+ self.toc = -1
142
+ self.tow = -1
142
143
  self._subframeacq = 0
143
144
  self._msb = {}
144
145
  self._isb = {}
@@ -290,7 +291,7 @@ class RawNav:
290
291
 
291
292
  stg = (
292
293
  f"<RAWNAV({self.identity}, gnss={self._gnss}, svid={self._svid}, "
293
- f"sigid={self._sigid}, sfracq={self._subframeacq}, "
294
+ f"sigid={self._sigcode}, sfracq={self._subframeacq}, "
294
295
  )
295
296
  for i, att in enumerate(self.__dict__):
296
297
  if att[0] != "_": # only show public attributes
@@ -310,7 +311,7 @@ class RawNav:
310
311
  :rtype: str
311
312
  """
312
313
 
313
- return f"{self._gnss}{self._svid:02d}{self._sigid}"
314
+ return f"{self._gnss}{self._svid:02d}{self._sigcode}"
314
315
 
315
316
  @property
316
317
  def gnss(self) -> str:
@@ -343,18 +344,18 @@ class RawNav:
343
344
  :rtype: str
344
345
  """
345
346
 
346
- return f"{self._gnss}{self._svid:02d}"
347
+ return f"{self._gnss}{self._svid:>2}" # no leading zero
347
348
 
348
349
  @property
349
- def sigid(self) -> str:
350
+ def sigcode(self) -> str:
350
351
  """
351
- Getter for signal id in RINEX format.
352
+ Getter for signal code in RINEX format.
352
353
 
353
- :return: signal id
354
+ :return: signal code e.g. '1C'
354
355
  :rtype: str
355
356
  """
356
357
 
357
- return self._sigid
358
+ return self._sigcode
358
359
 
359
360
  @property
360
361
  def subframeacq(self) -> int:
@@ -411,37 +412,37 @@ class RawNavReader:
411
412
  gnss = UBXRINEXGNSS[data.gnssId]
412
413
  svid = data.svId
413
414
  numw = data.numWords
414
- sigid = UBXRINEXOBSCODE[(data.gnssId, data.sigId)]
415
+ sigcode = get_obscode_ubx(data.gnssId, data.sigId)
415
416
  except KeyError as err:
416
417
  raise RINEXProcessingError(
417
418
  f"Unrecognised GNSS or Signal code: {data.gnssId=}, {data.sigId=}"
418
419
  ) from err
419
420
 
420
421
  if gnss == GPS:
421
- sfrdata = self._process_rxm_sfrbx_gps(gnss, svid, sigid, numw, data)
422
+ sfrdata = self._process_rxm_sfrbx_gps(gnss, svid, sigcode, numw, data)
422
423
  elif gnss == GAL:
423
- sfrdata = self._process_rxm_sfrbx_gal(gnss, svid, sigid, numw, data)
424
+ sfrdata = self._process_rxm_sfrbx_gal(gnss, svid, sigcode, numw, data)
424
425
  elif gnss == BDS:
425
- sfrdata = self._process_rxm_sfrbx_bds(gnss, svid, sigid, numw, data)
426
+ sfrdata = self._process_rxm_sfrbx_bds(gnss, svid, sigcode, numw, data)
426
427
  elif gnss == GLO:
427
- sfrdata = self._process_rxm_sfrbx_glo(gnss, svid, sigid, numw, data)
428
+ sfrdata = self._process_rxm_sfrbx_glo(gnss, svid, sigcode, numw, data)
428
429
  elif gnss == SBA:
429
- sfrdata = self._process_rxm_sfrbx_sba(gnss, svid, sigid, numw, data)
430
+ sfrdata = self._process_rxm_sfrbx_sba(gnss, svid, sigcode, numw, data)
430
431
  elif gnss == QZS:
431
- sfrdata = self._process_rxm_sfrbx_qzs(gnss, svid, sigid, numw, data)
432
+ sfrdata = self._process_rxm_sfrbx_qzs(gnss, svid, sigcode, numw, data)
432
433
  elif gnss == IRN:
433
- sfrdata = self._process_rxm_sfrbx_irn(gnss, svid, sigid, numw, data)
434
+ sfrdata = self._process_rxm_sfrbx_irn(gnss, svid, sigcode, numw, data)
434
435
  return sfrdata
435
436
 
436
437
  def _process_rxm_sfrbx_gps(
437
- self, gnss: str, svid: int, sigid: str, numw: int, data: UBXMessage
438
+ self, gnss: str, svid: int, sigcode: str, numw: int, data: UBXMessage
438
439
  ) -> dict[str, str | int | float | NoneType]:
439
440
  """
440
441
  Reassemble GPS subframe from individual UBX RXM-SFRBX dwrds.
441
442
 
442
443
  :param str gnss: RINEX gnss code
443
444
  :param int svid: SV
444
- :param str sigid: RINEX sigid
445
+ :param str sigcode: RINEX sig code e.g. '1C'
445
446
  :param UBXMessage data: parsed UBX RXM-SFRBX message
446
447
  :return: dict of subframe attributes
447
448
  :rtype: dict[str, str | int | float | NoneType]
@@ -450,21 +451,19 @@ class RawNavReader:
450
451
 
451
452
  subframe = 0
452
453
  subframeid = 0
453
- # dataid = 0
454
454
  subframepageid = 0
455
455
 
456
456
  # for GPS LNAV, subframe = 10 * 30 bits, with each 32-bit dwrd padded with 2 bits at end
457
- if sigid == "1C": # GPS LNAV
457
+ if sigcode == "1C": # GPS LNAV
458
458
  for i in range(numw):
459
459
  wrd = getattr(data, f"dwrd_{i+1:02d}") & 0xFFFFFFFC >> 2
460
460
  subframe += wrd << (30 * (numw - 1 - i))
461
461
  subframeid = (subframe >> 248) & 0b111
462
- # dataid = subframe >> 234 & 0b11
463
462
  if subframeid in (4, 5):
464
463
  subframepageid = subframe >> 232 & 0b111111
465
464
 
466
465
  # for GPS CNAV, subframe = 3 * 100 bits, final 20 bits of 320 bit dwrd is padding
467
- elif sigid in ("2L", "2S", "5I", "5Q"): # GPS CNAV
466
+ elif sigcode in ("2L", "2S", "5I", "5Q"): # GPS CNAV
468
467
  for i in range(numw):
469
468
  wrd = getattr(data, f"dwrd_{i+1:02d}")
470
469
  subframe += wrd << (32 * (numw - 1 - i))
@@ -477,22 +476,21 @@ class RawNavReader:
477
476
  return {
478
477
  "gnss": gnss,
479
478
  "svid": svid,
480
- "sigid": sigid,
481
- # "dataid": dataid,
479
+ "sigcode": sigcode,
482
480
  "subframeid": subframeid,
483
481
  "subframepageid": subframepageid,
484
482
  "subframe": subframe,
485
483
  }
486
484
 
487
485
  def _process_rxm_sfrbx_gal(
488
- self, gnss: str, svid: int, sigid: str, numw: int, data: UBXMessage
486
+ self, gnss: str, svid: int, sigcode: str, numw: int, data: UBXMessage
489
487
  ) -> dict[str, str | int | float | NoneType]:
490
488
  """
491
489
  Reassemble GALILEO subframe from individual UBX RXM-SFRBX dwrds.
492
490
 
493
491
  :param str gnss: RINEX gnss code
494
492
  :param int svid: SV
495
- :param str sigid: RINEX sigid
493
+ :param str sigcode: RINEX sigid e.g. '5I'
496
494
  :param UBXMessage data: parsed UBX RXM-SFRBX message
497
495
  :return: dict of subframe attributes
498
496
  :rtype: dict[str, str | int | float | NoneType]
@@ -505,7 +503,7 @@ class RawNavReader:
505
503
 
506
504
  # for GAL FNAV, subframe = 244 bits,
507
505
  # 8 * 32 bit dwrds with 12 bits padding at end
508
- if sigid == "5I": # GAL FNAV
506
+ if sigcode == "5I": # GAL FNAV
509
507
  for i in range(numw):
510
508
  wrd = getattr(data, f"dwrd_{i+1:02d}")
511
509
  subframe += wrd << (32 * (numw - 1 - i))
@@ -517,7 +515,7 @@ class RawNavReader:
517
515
  # for GAL INAV, subframe = 256 bits, 8 * 32 bit dwrds,
518
516
  # with word data separated into 112 msb and 16 lsb
519
517
  # (see GAL_INAV_SUBFRAME)
520
- elif sigid in ("1B", "7I"): # GAL INAV
518
+ elif sigcode in ("1B", "7I"): # GAL INAV
521
519
  supsubframe = 0
522
520
  for i in range(numw):
523
521
  wrd = getattr(data, f"dwrd_{i+1:02d}")
@@ -532,21 +530,21 @@ class RawNavReader:
532
530
  return {
533
531
  "gnss": gnss,
534
532
  "svid": svid,
535
- "sigid": sigid,
533
+ "sigcode": sigcode,
536
534
  "subframeid": subframeid,
537
535
  "subframepageid": subframepageid,
538
536
  "subframe": subframe,
539
537
  }
540
538
 
541
539
  def _process_rxm_sfrbx_bds(
542
- self, gnss: str, svid: int, sigid: str, numw: int, data: UBXMessage
540
+ self, gnss: str, svid: int, sigcode: str, numw: int, data: UBXMessage
543
541
  ) -> dict[str, str | int | float | NoneType]:
544
542
  """
545
543
  Reassemble BEIDOU subframe from individual UBX RXM-SFRBX dwrds.
546
544
 
547
545
  :param str gnss: RINEX gnss code
548
546
  :param int svid: SV
549
- :param str sigid: RINEX sigid
547
+ :param str sigcode: RINEX sig code e.g. '2I'
550
548
  :param UBXMessage data: parsed UBX RXM-SFRBX message
551
549
  :return: dict of subframe attributes
552
550
  :rtype: dict[str, str | int | float | NoneType]
@@ -558,7 +556,7 @@ class RawNavReader:
558
556
  subframepageid = 0
559
557
  d1d2 = 0
560
558
 
561
- if sigid in ("2I", "6I", "7I"): # BDS D1/D2
559
+ if sigcode in ("2I", "6I", "7I"): # BDS D1/D2
562
560
  if data.sigId in (1, 3, 10): # D2
563
561
  d1d2 = 2
564
562
  else: # D1
@@ -572,16 +570,16 @@ class RawNavReader:
572
570
  elif d1d2 == 2 and subframeid in (1,):
573
571
  subframepageid = subframe >> 254 & 0b1111
574
572
 
575
- elif sigid == "1D": # BDS CNV1
573
+ elif sigcode == "1D": # BDS CNV1
576
574
  pass # TODO
577
575
 
578
- elif sigid == "5D": # BDS CNV2
576
+ elif sigcode == "5D": # BDS CNV2
579
577
  pass # TODO
580
578
 
581
579
  return {
582
580
  "gnss": gnss,
583
581
  "svid": svid,
584
- "sigid": sigid,
582
+ "sigcode": sigcode,
585
583
  "d1d2": d1d2,
586
584
  "subframeid": subframeid,
587
585
  "subframepageid": subframepageid,
@@ -589,14 +587,14 @@ class RawNavReader:
589
587
  }
590
588
 
591
589
  def _process_rxm_sfrbx_glo(
592
- self, gnss: str, svid: int, sigid: str, numw: int, data: UBXMessage
590
+ self, gnss: str, svid: int, sigcode: str, numw: int, data: UBXMessage
593
591
  ) -> dict[str, str | int | float | NoneType]:
594
592
  """
595
593
  Reassemble GLONASS subframe from individual UBX RXM-SFRBX dwrds.
596
594
 
597
595
  :param str gnss: RINEX gnss code
598
596
  :param int svid: SV
599
- :param str sigid: RINEX sigid
597
+ :param str sigcode: RINEX sigid e.g. '1C'
600
598
  :param UBXMessage data: parsed UBX RXM-SFRBX message
601
599
  :return: dict of subframe attributes
602
600
  :rtype: dict[str, str | int | float | NoneType]
@@ -606,35 +604,38 @@ class RawNavReader:
606
604
  subframe = 0
607
605
  subframeid = 0
608
606
  subframepageid = 0
607
+ freqid = data.freqId
609
608
 
610
- # for GLO, subframe = 85 bits, 3 * 32 bit dwrds, plus
611
- # a receiver-generated 4th 32 dwrd containing subframe and page ids
612
- if sigid in ("1C", "2C"): # GLO L1,L2
609
+ # for GLO, subframe = 85 bits,
610
+ # 3 * 32 bit dwrds with 11 bits padding at end,
611
+ # plus a receiver-generated 4th dwrd containing superframe and frame ids
612
+ if sigcode in ("1C",): # GLO L1OF
613
613
  for i in range(numw):
614
614
  wrd = getattr(data, f"dwrd_{i+1:02d}")
615
615
  subframe += wrd << (32 * (numw - 1 - i))
616
- subframepageid = (subframe >> 16) & 0xFFFF
617
- subframeid = subframe & 0b11111111
618
- subframe = (subframe >> 43) & 0x1FFFFFFFFFFFFFFFFFFFFF # strip 4th dwrd
616
+ # strip 4th dwrd, leaving 85 bit subframe
617
+ subframe = (subframe >> 43) & 0x1FFFFFFFFFFFFFFFFFFFFF # 2**85-1
618
+ subframeid = (subframe >> 80) & 0b01111
619
619
 
620
620
  return {
621
621
  "gnss": gnss,
622
622
  "svid": svid,
623
- "sigid": sigid,
623
+ "sigcode": sigcode,
624
624
  "subframeid": subframeid,
625
625
  "subframepageid": subframepageid,
626
626
  "subframe": subframe,
627
+ "freqid": freqid,
627
628
  }
628
629
 
629
630
  def _process_rxm_sfrbx_sba(
630
- self, gnss: str, svid: int, sigid: str, numw: int, data: UBXMessage
631
+ self, gnss: str, svid: int, sigcode: str, numw: int, data: UBXMessage
631
632
  ) -> dict[str, str | int | float | NoneType]:
632
633
  """
633
634
  Reassemble SBAS subframe from individual UBX RXM-SFRBX dwrds.
634
635
 
635
636
  :param str gnss: RINEX gnss code
636
637
  :param int svid: SV
637
- :param str sigid: RINEX sigid
638
+ :param str sigcode: RINEX sigcode e.g. '1C'
638
639
  :param UBXMessage data: parsed UBX RXM-SFRBX message
639
640
  :return: dict of subframe attributes
640
641
  :rtype: dict[str, str | int | float | NoneType]
@@ -642,22 +643,39 @@ class RawNavReader:
642
643
  """
643
644
 
644
645
  subframe = 0
645
- tow = 0
646
646
  subframeid = 0
647
647
  subframepageid = 0
648
- sfrdata = {}
649
- # TODO
650
- return sfrdata
648
+ svid -= 100 # adjust SV ID range
649
+
650
+ # for SBAS, subframe = 250 bits,
651
+ # 8 * 32 bit dwrds with 6 bits padding at end
652
+ if sigcode == "1C": # SBAS L1C/A
653
+ for i in range(numw):
654
+ wrd = getattr(data, f"dwrd_{i+1:02d}")
655
+ subframe += wrd << (32 * (numw - 1 - i))
656
+ subframe = (
657
+ subframe >> 12
658
+ ) & 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF # (2**250 - 1)
659
+ subframeid = (subframe >> 236) & 0b111111
660
+
661
+ return {
662
+ "gnss": gnss,
663
+ "svid": svid,
664
+ "sigcode": sigcode,
665
+ "subframeid": subframeid,
666
+ "subframepageid": subframepageid,
667
+ "subframe": subframe,
668
+ }
651
669
 
652
670
  def _process_rxm_sfrbx_qzs(
653
- self, gnss: str, svid: int, sigid: str, numw: int, data: UBXMessage
671
+ self, gnss: str, svid: int, sigcode: str, numw: int, data: UBXMessage
654
672
  ) -> dict[str, str | int | float | NoneType]:
655
673
  """
656
674
  Reassemble QZSS subframe from individual UBX RXM-SFRBX dwrds.
657
675
 
658
676
  :param str gnss: RINEX gnss code
659
677
  :param int svid: SV
660
- :param str sigid: RINEX sigid
678
+ :param str sigcode: RINEX sigcode e.g. '1C'
661
679
  :param UBXMessage data: parsed UBX RXM-SFRBX message
662
680
  :return: dict of subframe attributes
663
681
  :rtype: dict[str, str | int | float | NoneType]
@@ -665,22 +683,65 @@ class RawNavReader:
665
683
  """
666
684
 
667
685
  subframe = 0
668
- tow = 0
669
686
  subframeid = 0
670
687
  subframepageid = 0
671
- sfrdata = {}
672
- # TODO
673
- return sfrdata
688
+ svid -= 192 # adjust SV ID range
689
+
690
+ # for QZSS L1C/A, subframe = 10 * 30 bits, with each 32-bit dwrd padded with 2 bits at end
691
+ # same as GPS LNAV
692
+ if sigcode == "1C": # QZSS L1C/A
693
+ for i in range(numw):
694
+ wrd = getattr(data, f"dwrd_{i+1:02d}") & 0xFFFFFFFC >> 2
695
+ subframe += wrd << (30 * (numw - 1 - i))
696
+ subframeid = (subframe >> 248) & 0b111
697
+ if subframeid in (4, 5):
698
+ subframepageid = subframe >> 232 & 0b111111
699
+
700
+ # for QZSS L1S, subframe = 250 bits, 8 * 32 bit dwrds with 6 bits padding at end
701
+ # same as SBAS L1C/A
702
+ if sigcode == "1Z": # QZSS L1S
703
+ for i in range(numw):
704
+ wrd = getattr(data, f"dwrd_{i+1:02d}")
705
+ subframe += wrd << (32 * (numw - 1 - i))
706
+ subframe = (
707
+ subframe >> 12
708
+ ) & 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF # (2**250 - 1)
709
+ subframeid = (subframe >> 236) & 0b111111
710
+
711
+ # for QZSS L2C, L5I, subframe = 3 * 100 bits, final 20 bits of 320 bit dwrd is padding
712
+ # same as GPS CNAV
713
+ elif sigcode in (
714
+ "2L",
715
+ "2S",
716
+ "5I",
717
+ ): # QZSS L2C, L5I
718
+ for i in range(numw):
719
+ wrd = getattr(data, f"dwrd_{i+1:02d}")
720
+ subframe += wrd << (32 * (numw - 1 - i))
721
+ subframe = (
722
+ (subframe >> 20)
723
+ & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
724
+ ) # (2**300 - 1)
725
+ subframeid = (subframe >> 280) & 0b111111
726
+
727
+ return {
728
+ "gnss": gnss,
729
+ "svid": svid,
730
+ "sigcode": sigcode,
731
+ "subframeid": subframeid,
732
+ "subframepageid": subframepageid,
733
+ "subframe": subframe,
734
+ }
674
735
 
675
736
  def _process_rxm_sfrbx_irn(
676
- self, gnss: str, svid: int, sigid: str, numw: int, data: UBXMessage
737
+ self, gnss: str, svid: int, sigcode: str, numw: int, data: UBXMessage
677
738
  ) -> dict[str, str | int | float | NoneType]:
678
739
  """
679
740
  Reassemble IRNSS (NAVIC) subframe from individual UBX RXM-SFRBX dwrds.
680
741
 
681
742
  :param str gnss: RINEX gnss code
682
743
  :param int svid: SV
683
- :param str sigid: RINEX sigid
744
+ :param str sigcode: RINEX sigcode e.g. '5A'
684
745
  :param UBXMessage data: parsed UBX RXM-SFRBX message
685
746
  :return: dict of subframe attributes
686
747
  :rtype: dict[str, str | int | float | NoneType]
@@ -688,9 +749,26 @@ class RawNavReader:
688
749
  """
689
750
 
690
751
  subframe = 0
691
- tow = 0
692
752
  subframeid = 0
693
753
  subframepageid = 0
694
- sfrdata = {}
695
- # TODO
696
- return sfrdata
754
+
755
+ # for IRN L5A, subframe = 3 * 100 bits, final 20 bits of 320 bit dwrd is padding
756
+ # same as GPS CNAV
757
+ if sigcode in ("5A",): # IRN L5A
758
+ for i in range(numw):
759
+ wrd = getattr(data, f"dwrd_{i+1:02d}")
760
+ subframe += wrd << (32 * (numw - 1 - i))
761
+ subframe = (
762
+ (subframe >> 20)
763
+ & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
764
+ ) # (2**300 - 1)
765
+ subframeid = (subframe >> 280) & 0b111111
766
+
767
+ return {
768
+ "gnss": gnss,
769
+ "svid": svid,
770
+ "sigcode": sigcode,
771
+ "subframeid": subframeid,
772
+ "subframepageid": subframepageid,
773
+ "subframe": subframe,
774
+ }