pygnssutils 1.2.2__tar.gz → 1.2.3__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 (47) hide show
  1. {pygnssutils-1.2.2/src/pygnssutils.egg-info → pygnssutils-1.2.3}/PKG-INFO +4 -6
  2. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/README.md +2 -4
  3. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/pyproject.toml +1 -1
  4. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/_version.py +1 -1
  5. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/globals.py +1 -1
  6. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/rawnav.py +135 -61
  7. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/rinex_conv.py +23 -2
  8. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/rinex_conv_cli.py +36 -1
  9. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/rinex_conv_met.py +2 -2
  10. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/rinex_conv_nav.py +146 -121
  11. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/rinex_conv_obs.py +15 -10
  12. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/rinex_globals.py +14 -12
  13. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/rinex_helpers.py +40 -33
  14. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/rinex_subframes_bds.py +18 -12
  15. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/rinex_subframes_gal.py +4 -3
  16. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/rinex_subframes_gps.py +11 -8
  17. {pygnssutils-1.2.2 → pygnssutils-1.2.3/src/pygnssutils.egg-info}/PKG-INFO +4 -6
  18. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils.egg-info/SOURCES.txt +1 -0
  19. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils.egg-info/requires.txt +1 -1
  20. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/tests/test_rinex.py +21 -1
  21. pygnssutils-1.2.3/tests/test_rinex_defs.py +136 -0
  22. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/LICENSE +0 -0
  23. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/setup.cfg +0 -0
  24. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/__init__.py +0 -0
  25. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/exceptions.py +0 -0
  26. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/gnssmqttclient.py +0 -0
  27. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/gnssmqttclient_cli.py +0 -0
  28. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/gnssntripclient.py +0 -0
  29. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/gnssntripclient_cli.py +0 -0
  30. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/gnssreader.py +0 -0
  31. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/gnssserver.py +0 -0
  32. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/gnssserver_cli.py +0 -0
  33. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/gnssstreamer.py +0 -0
  34. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/gnssstreamer_cli.py +0 -0
  35. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/helpers.py +0 -0
  36. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/mqttmessage.py +0 -0
  37. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils/socket_server.py +0 -0
  38. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils.egg-info/dependency_links.txt +0 -0
  39. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils.egg-info/entry_points.txt +0 -0
  40. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/src/pygnssutils.egg-info/top_level.txt +0 -0
  41. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/tests/test_cli.py +0 -0
  42. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/tests/test_gnssstreamer.py +0 -0
  43. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/tests/test_rawnav.py +0 -0
  44. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/tests/test_socketwrapper.py +0 -0
  45. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/tests/test_sourcetable.py +0 -0
  46. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/tests/test_static.py +0 -0
  47. {pygnssutils-1.2.2 → pygnssutils-1.2.3}/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.3
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,7 +34,7 @@ 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
37
+ Requires-Dist: pyubx2>=1.3.3
38
38
  Requires-Dist: pynmeagps>=1.1.4
39
39
  Requires-Dist: pysbf2>=1.0.4
40
40
  Requires-Dist: pyubxutils>=1.0.6
@@ -543,18 +543,16 @@ 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: 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**, 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
554
  A Graphical User Interface for this utility will be added to [PyGPSClient](https://github.com/semuconsulting/PyGPSClient).
555
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**.
557
-
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
 
560
558
  ```
@@ -499,18 +499,16 @@ 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: 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**, 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
510
  A Graphical User Interface for this utility will be added to [PyGPSClient](https://github.com/semuconsulting/PyGPSClient).
511
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**.
513
-
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
 
516
514
  ```
@@ -38,7 +38,7 @@ dependencies = [
38
38
  "paho-mqtt>=2.1.0",
39
39
  "pyserial>=3.5",
40
40
  "pyspartn>=1.0.8",
41
- "pyubx2>=1.3.0",
41
+ "pyubx2>=1.3.3",
42
42
  "pynmeagps>=1.1.4",
43
43
  "pysbf2>=1.0.4",
44
44
  "pyubxutils>=1.0.6",
@@ -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.3"
@@ -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"""
@@ -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,14 +128,14 @@ 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
138
+ self._sigcode = sigcode
139
139
  self.wn = -1
140
140
  self.toc = -1
141
141
  self.tow = -1
@@ -290,7 +290,7 @@ class RawNav:
290
290
 
291
291
  stg = (
292
292
  f"<RAWNAV({self.identity}, gnss={self._gnss}, svid={self._svid}, "
293
- f"sigid={self._sigid}, sfracq={self._subframeacq}, "
293
+ f"sigid={self._sigcode}, sfracq={self._subframeacq}, "
294
294
  )
295
295
  for i, att in enumerate(self.__dict__):
296
296
  if att[0] != "_": # only show public attributes
@@ -310,7 +310,7 @@ class RawNav:
310
310
  :rtype: str
311
311
  """
312
312
 
313
- return f"{self._gnss}{self._svid:02d}{self._sigid}"
313
+ return f"{self._gnss}{self._svid:02d}{self._sigcode}"
314
314
 
315
315
  @property
316
316
  def gnss(self) -> str:
@@ -343,18 +343,18 @@ class RawNav:
343
343
  :rtype: str
344
344
  """
345
345
 
346
- return f"{self._gnss}{self._svid:02d}"
346
+ return f"{self._gnss}{self._svid:>2}" # no leading zero
347
347
 
348
348
  @property
349
- def sigid(self) -> str:
349
+ def sigcode(self) -> str:
350
350
  """
351
- Getter for signal id in RINEX format.
351
+ Getter for signal code in RINEX format.
352
352
 
353
- :return: signal id
353
+ :return: signal code e.g. '1C'
354
354
  :rtype: str
355
355
  """
356
356
 
357
- return self._sigid
357
+ return self._sigcode
358
358
 
359
359
  @property
360
360
  def subframeacq(self) -> int:
@@ -411,37 +411,37 @@ class RawNavReader:
411
411
  gnss = UBXRINEXGNSS[data.gnssId]
412
412
  svid = data.svId
413
413
  numw = data.numWords
414
- sigid = UBXRINEXOBSCODE[(data.gnssId, data.sigId)]
414
+ sigcode = get_obscode_ubx(data.gnssId, data.sigId)
415
415
  except KeyError as err:
416
416
  raise RINEXProcessingError(
417
417
  f"Unrecognised GNSS or Signal code: {data.gnssId=}, {data.sigId=}"
418
418
  ) from err
419
419
 
420
420
  if gnss == GPS:
421
- sfrdata = self._process_rxm_sfrbx_gps(gnss, svid, sigid, numw, data)
421
+ sfrdata = self._process_rxm_sfrbx_gps(gnss, svid, sigcode, numw, data)
422
422
  elif gnss == GAL:
423
- sfrdata = self._process_rxm_sfrbx_gal(gnss, svid, sigid, numw, data)
423
+ sfrdata = self._process_rxm_sfrbx_gal(gnss, svid, sigcode, numw, data)
424
424
  elif gnss == BDS:
425
- sfrdata = self._process_rxm_sfrbx_bds(gnss, svid, sigid, numw, data)
425
+ sfrdata = self._process_rxm_sfrbx_bds(gnss, svid, sigcode, numw, data)
426
426
  elif gnss == GLO:
427
- sfrdata = self._process_rxm_sfrbx_glo(gnss, svid, sigid, numw, data)
427
+ sfrdata = self._process_rxm_sfrbx_glo(gnss, svid, sigcode, numw, data)
428
428
  elif gnss == SBA:
429
- sfrdata = self._process_rxm_sfrbx_sba(gnss, svid, sigid, numw, data)
429
+ sfrdata = self._process_rxm_sfrbx_sba(gnss, svid, sigcode, numw, data)
430
430
  elif gnss == QZS:
431
- sfrdata = self._process_rxm_sfrbx_qzs(gnss, svid, sigid, numw, data)
431
+ sfrdata = self._process_rxm_sfrbx_qzs(gnss, svid, sigcode, numw, data)
432
432
  elif gnss == IRN:
433
- sfrdata = self._process_rxm_sfrbx_irn(gnss, svid, sigid, numw, data)
433
+ sfrdata = self._process_rxm_sfrbx_irn(gnss, svid, sigcode, numw, data)
434
434
  return sfrdata
435
435
 
436
436
  def _process_rxm_sfrbx_gps(
437
- self, gnss: str, svid: int, sigid: str, numw: int, data: UBXMessage
437
+ self, gnss: str, svid: int, sigcode: str, numw: int, data: UBXMessage
438
438
  ) -> dict[str, str | int | float | NoneType]:
439
439
  """
440
440
  Reassemble GPS subframe from individual UBX RXM-SFRBX dwrds.
441
441
 
442
442
  :param str gnss: RINEX gnss code
443
443
  :param int svid: SV
444
- :param str sigid: RINEX sigid
444
+ :param str sigcode: RINEX sig code e.g. '1C'
445
445
  :param UBXMessage data: parsed UBX RXM-SFRBX message
446
446
  :return: dict of subframe attributes
447
447
  :rtype: dict[str, str | int | float | NoneType]
@@ -450,21 +450,19 @@ class RawNavReader:
450
450
 
451
451
  subframe = 0
452
452
  subframeid = 0
453
- # dataid = 0
454
453
  subframepageid = 0
455
454
 
456
455
  # for GPS LNAV, subframe = 10 * 30 bits, with each 32-bit dwrd padded with 2 bits at end
457
- if sigid == "1C": # GPS LNAV
456
+ if sigcode == "1C": # GPS LNAV
458
457
  for i in range(numw):
459
458
  wrd = getattr(data, f"dwrd_{i+1:02d}") & 0xFFFFFFFC >> 2
460
459
  subframe += wrd << (30 * (numw - 1 - i))
461
460
  subframeid = (subframe >> 248) & 0b111
462
- # dataid = subframe >> 234 & 0b11
463
461
  if subframeid in (4, 5):
464
462
  subframepageid = subframe >> 232 & 0b111111
465
463
 
466
464
  # 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
465
+ elif sigcode in ("2L", "2S", "5I", "5Q"): # GPS CNAV
468
466
  for i in range(numw):
469
467
  wrd = getattr(data, f"dwrd_{i+1:02d}")
470
468
  subframe += wrd << (32 * (numw - 1 - i))
@@ -477,22 +475,21 @@ class RawNavReader:
477
475
  return {
478
476
  "gnss": gnss,
479
477
  "svid": svid,
480
- "sigid": sigid,
481
- # "dataid": dataid,
478
+ "sigcode": sigcode,
482
479
  "subframeid": subframeid,
483
480
  "subframepageid": subframepageid,
484
481
  "subframe": subframe,
485
482
  }
486
483
 
487
484
  def _process_rxm_sfrbx_gal(
488
- self, gnss: str, svid: int, sigid: str, numw: int, data: UBXMessage
485
+ self, gnss: str, svid: int, sigcode: str, numw: int, data: UBXMessage
489
486
  ) -> dict[str, str | int | float | NoneType]:
490
487
  """
491
488
  Reassemble GALILEO subframe from individual UBX RXM-SFRBX dwrds.
492
489
 
493
490
  :param str gnss: RINEX gnss code
494
491
  :param int svid: SV
495
- :param str sigid: RINEX sigid
492
+ :param str sigcode: RINEX sigid e.g. '5I'
496
493
  :param UBXMessage data: parsed UBX RXM-SFRBX message
497
494
  :return: dict of subframe attributes
498
495
  :rtype: dict[str, str | int | float | NoneType]
@@ -505,7 +502,7 @@ class RawNavReader:
505
502
 
506
503
  # for GAL FNAV, subframe = 244 bits,
507
504
  # 8 * 32 bit dwrds with 12 bits padding at end
508
- if sigid == "5I": # GAL FNAV
505
+ if sigcode == "5I": # GAL FNAV
509
506
  for i in range(numw):
510
507
  wrd = getattr(data, f"dwrd_{i+1:02d}")
511
508
  subframe += wrd << (32 * (numw - 1 - i))
@@ -517,7 +514,7 @@ class RawNavReader:
517
514
  # for GAL INAV, subframe = 256 bits, 8 * 32 bit dwrds,
518
515
  # with word data separated into 112 msb and 16 lsb
519
516
  # (see GAL_INAV_SUBFRAME)
520
- elif sigid in ("1B", "7I"): # GAL INAV
517
+ elif sigcode in ("1B", "7I"): # GAL INAV
521
518
  supsubframe = 0
522
519
  for i in range(numw):
523
520
  wrd = getattr(data, f"dwrd_{i+1:02d}")
@@ -532,21 +529,21 @@ class RawNavReader:
532
529
  return {
533
530
  "gnss": gnss,
534
531
  "svid": svid,
535
- "sigid": sigid,
532
+ "sigcode": sigcode,
536
533
  "subframeid": subframeid,
537
534
  "subframepageid": subframepageid,
538
535
  "subframe": subframe,
539
536
  }
540
537
 
541
538
  def _process_rxm_sfrbx_bds(
542
- self, gnss: str, svid: int, sigid: str, numw: int, data: UBXMessage
539
+ self, gnss: str, svid: int, sigcode: str, numw: int, data: UBXMessage
543
540
  ) -> dict[str, str | int | float | NoneType]:
544
541
  """
545
542
  Reassemble BEIDOU subframe from individual UBX RXM-SFRBX dwrds.
546
543
 
547
544
  :param str gnss: RINEX gnss code
548
545
  :param int svid: SV
549
- :param str sigid: RINEX sigid
546
+ :param str sigcode: RINEX sig code e.g. '2I'
550
547
  :param UBXMessage data: parsed UBX RXM-SFRBX message
551
548
  :return: dict of subframe attributes
552
549
  :rtype: dict[str, str | int | float | NoneType]
@@ -558,7 +555,7 @@ class RawNavReader:
558
555
  subframepageid = 0
559
556
  d1d2 = 0
560
557
 
561
- if sigid in ("2I", "6I", "7I"): # BDS D1/D2
558
+ if sigcode in ("2I", "6I", "7I"): # BDS D1/D2
562
559
  if data.sigId in (1, 3, 10): # D2
563
560
  d1d2 = 2
564
561
  else: # D1
@@ -572,16 +569,16 @@ class RawNavReader:
572
569
  elif d1d2 == 2 and subframeid in (1,):
573
570
  subframepageid = subframe >> 254 & 0b1111
574
571
 
575
- elif sigid == "1D": # BDS CNV1
572
+ elif sigcode == "1D": # BDS CNV1
576
573
  pass # TODO
577
574
 
578
- elif sigid == "5D": # BDS CNV2
575
+ elif sigcode == "5D": # BDS CNV2
579
576
  pass # TODO
580
577
 
581
578
  return {
582
579
  "gnss": gnss,
583
580
  "svid": svid,
584
- "sigid": sigid,
581
+ "sigcode": sigcode,
585
582
  "d1d2": d1d2,
586
583
  "subframeid": subframeid,
587
584
  "subframepageid": subframepageid,
@@ -589,14 +586,14 @@ class RawNavReader:
589
586
  }
590
587
 
591
588
  def _process_rxm_sfrbx_glo(
592
- self, gnss: str, svid: int, sigid: str, numw: int, data: UBXMessage
589
+ self, gnss: str, svid: int, sigcode: str, numw: int, data: UBXMessage
593
590
  ) -> dict[str, str | int | float | NoneType]:
594
591
  """
595
592
  Reassemble GLONASS subframe from individual UBX RXM-SFRBX dwrds.
596
593
 
597
594
  :param str gnss: RINEX gnss code
598
595
  :param int svid: SV
599
- :param str sigid: RINEX sigid
596
+ :param str sigcode: RINEX sigid e.g. '1C'
600
597
  :param UBXMessage data: parsed UBX RXM-SFRBX message
601
598
  :return: dict of subframe attributes
602
599
  :rtype: dict[str, str | int | float | NoneType]
@@ -609,7 +606,7 @@ class RawNavReader:
609
606
 
610
607
  # for GLO, subframe = 85 bits, 3 * 32 bit dwrds, plus
611
608
  # a receiver-generated 4th 32 dwrd containing subframe and page ids
612
- if sigid in ("1C", "2C"): # GLO L1,L2
609
+ if sigcode in ("1C", "2C"): # GLO L1,L2
613
610
  for i in range(numw):
614
611
  wrd = getattr(data, f"dwrd_{i+1:02d}")
615
612
  subframe += wrd << (32 * (numw - 1 - i))
@@ -620,21 +617,21 @@ class RawNavReader:
620
617
  return {
621
618
  "gnss": gnss,
622
619
  "svid": svid,
623
- "sigid": sigid,
620
+ "sigcode": sigcode,
624
621
  "subframeid": subframeid,
625
622
  "subframepageid": subframepageid,
626
623
  "subframe": subframe,
627
624
  }
628
625
 
629
626
  def _process_rxm_sfrbx_sba(
630
- self, gnss: str, svid: int, sigid: str, numw: int, data: UBXMessage
627
+ self, gnss: str, svid: int, sigcode: str, numw: int, data: UBXMessage
631
628
  ) -> dict[str, str | int | float | NoneType]:
632
629
  """
633
630
  Reassemble SBAS subframe from individual UBX RXM-SFRBX dwrds.
634
631
 
635
632
  :param str gnss: RINEX gnss code
636
633
  :param int svid: SV
637
- :param str sigid: RINEX sigid
634
+ :param str sigcode: RINEX sigcode e.g. '1C'
638
635
  :param UBXMessage data: parsed UBX RXM-SFRBX message
639
636
  :return: dict of subframe attributes
640
637
  :rtype: dict[str, str | int | float | NoneType]
@@ -642,22 +639,39 @@ class RawNavReader:
642
639
  """
643
640
 
644
641
  subframe = 0
645
- tow = 0
646
642
  subframeid = 0
647
643
  subframepageid = 0
648
- sfrdata = {}
649
- # TODO
650
- return sfrdata
644
+ svid -= 100 # adjust SV ID range
645
+
646
+ # for SBAS, subframe = 250 bits,
647
+ # 8 * 32 bit dwrds with 6 bits padding at end
648
+ if sigcode == "1C": # SBAS L1C/A
649
+ for i in range(numw):
650
+ wrd = getattr(data, f"dwrd_{i+1:02d}")
651
+ subframe += wrd << (32 * (numw - 1 - i))
652
+ subframe = (
653
+ subframe >> 12
654
+ ) & 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF # (2**250 - 1)
655
+ subframeid = (subframe >> 236) & 0b111111
656
+
657
+ return {
658
+ "gnss": gnss,
659
+ "svid": svid,
660
+ "sigcode": sigcode,
661
+ "subframeid": subframeid,
662
+ "subframepageid": subframepageid,
663
+ "subframe": subframe,
664
+ }
651
665
 
652
666
  def _process_rxm_sfrbx_qzs(
653
- self, gnss: str, svid: int, sigid: str, numw: int, data: UBXMessage
667
+ self, gnss: str, svid: int, sigcode: str, numw: int, data: UBXMessage
654
668
  ) -> dict[str, str | int | float | NoneType]:
655
669
  """
656
670
  Reassemble QZSS subframe from individual UBX RXM-SFRBX dwrds.
657
671
 
658
672
  :param str gnss: RINEX gnss code
659
673
  :param int svid: SV
660
- :param str sigid: RINEX sigid
674
+ :param str sigcode: RINEX sigcode e.g. '1C'
661
675
  :param UBXMessage data: parsed UBX RXM-SFRBX message
662
676
  :return: dict of subframe attributes
663
677
  :rtype: dict[str, str | int | float | NoneType]
@@ -665,22 +679,65 @@ class RawNavReader:
665
679
  """
666
680
 
667
681
  subframe = 0
668
- tow = 0
669
682
  subframeid = 0
670
683
  subframepageid = 0
671
- sfrdata = {}
672
- # TODO
673
- return sfrdata
684
+ svid -= 192 # adjust SV ID range
685
+
686
+ # for QZSS L1C/A, subframe = 10 * 30 bits, with each 32-bit dwrd padded with 2 bits at end
687
+ # same as GPS LNAV
688
+ if sigcode == "1C": # QZSS L1C/A
689
+ for i in range(numw):
690
+ wrd = getattr(data, f"dwrd_{i+1:02d}") & 0xFFFFFFFC >> 2
691
+ subframe += wrd << (30 * (numw - 1 - i))
692
+ subframeid = (subframe >> 248) & 0b111
693
+ if subframeid in (4, 5):
694
+ subframepageid = subframe >> 232 & 0b111111
695
+
696
+ # for QZSS L1S, subframe = 250 bits, 8 * 32 bit dwrds with 6 bits padding at end
697
+ # same as SBAS L1C/A
698
+ if sigcode == "1Z": # QZSS L1S
699
+ for i in range(numw):
700
+ wrd = getattr(data, f"dwrd_{i+1:02d}")
701
+ subframe += wrd << (32 * (numw - 1 - i))
702
+ subframe = (
703
+ subframe >> 12
704
+ ) & 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF # (2**250 - 1)
705
+ subframeid = (subframe >> 236) & 0b111111
706
+
707
+ # for QZSS L2C, L5I, subframe = 3 * 100 bits, final 20 bits of 320 bit dwrd is padding
708
+ # same as GPS CNAV
709
+ elif sigcode in (
710
+ "2L",
711
+ "2S",
712
+ "5I",
713
+ ): # QZSS L2C, L5I
714
+ for i in range(numw):
715
+ wrd = getattr(data, f"dwrd_{i+1:02d}")
716
+ subframe += wrd << (32 * (numw - 1 - i))
717
+ subframe = (
718
+ (subframe >> 20)
719
+ & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
720
+ ) # (2**300 - 1)
721
+ subframeid = (subframe >> 280) & 0b111111
722
+
723
+ return {
724
+ "gnss": gnss,
725
+ "svid": svid,
726
+ "sigcode": sigcode,
727
+ "subframeid": subframeid,
728
+ "subframepageid": subframepageid,
729
+ "subframe": subframe,
730
+ }
674
731
 
675
732
  def _process_rxm_sfrbx_irn(
676
- self, gnss: str, svid: int, sigid: str, numw: int, data: UBXMessage
733
+ self, gnss: str, svid: int, sigcode: str, numw: int, data: UBXMessage
677
734
  ) -> dict[str, str | int | float | NoneType]:
678
735
  """
679
736
  Reassemble IRNSS (NAVIC) subframe from individual UBX RXM-SFRBX dwrds.
680
737
 
681
738
  :param str gnss: RINEX gnss code
682
739
  :param int svid: SV
683
- :param str sigid: RINEX sigid
740
+ :param str sigcode: RINEX sigcode e.g. '5A'
684
741
  :param UBXMessage data: parsed UBX RXM-SFRBX message
685
742
  :return: dict of subframe attributes
686
743
  :rtype: dict[str, str | int | float | NoneType]
@@ -688,9 +745,26 @@ class RawNavReader:
688
745
  """
689
746
 
690
747
  subframe = 0
691
- tow = 0
692
748
  subframeid = 0
693
749
  subframepageid = 0
694
- sfrdata = {}
695
- # TODO
696
- return sfrdata
750
+
751
+ # for IRN L5A, subframe = 3 * 100 bits, final 20 bits of 320 bit dwrd is padding
752
+ # same as GPS CNAV
753
+ if sigcode in ("5A",): # IRN L5A
754
+ for i in range(numw):
755
+ wrd = getattr(data, f"dwrd_{i+1:02d}")
756
+ subframe += wrd << (32 * (numw - 1 - i))
757
+ subframe = (
758
+ (subframe >> 20)
759
+ & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
760
+ ) # (2**300 - 1)
761
+ subframeid = (subframe >> 280) & 0b111111
762
+
763
+ return {
764
+ "gnss": gnss,
765
+ "svid": svid,
766
+ "sigcode": sigcode,
767
+ "subframeid": subframeid,
768
+ "subframepageid": subframepageid,
769
+ "subframe": subframe,
770
+ }
@@ -89,11 +89,15 @@ class RinexConverter:
89
89
  rinex_types: list[str],
90
90
  gnssfilter: list[str],
91
91
  obsfilter: list[str],
92
+ timecorr: bool,
93
+ ionocorr: bool,
94
+ eopcorr: bool,
92
95
  datasource: list[str],
93
96
  starttime: datetime | str,
94
97
  minobs: int,
95
98
  marker: list[str],
96
99
  antenna: list[str],
100
+ antennahed: list[float | str],
97
101
  receiver: list[str],
98
102
  observer: str,
99
103
  comments: list[str],
@@ -112,6 +116,9 @@ class RinexConverter:
112
116
  (or None for all) e.g. [GPS,GAL]
113
117
  :param list[str] obsfilter: List of observation codes to process \
114
118
  (or None for all) e.g. ["1C","2B"]
119
+ :param bool timecorr: Include time (clock) corrections
120
+ :param bool ionocorr: Include ionospheric corrections
121
+ :param bool eopcorr: Include earth orientation corrections
115
122
  :param list[str] datasource: List of datasources for each rinex \
116
123
  type e.g. ["R","R","R"]
117
124
  :param datetime | str starttime: Approximate start time of RTCM3 (e.g. NTRIP) data \
@@ -120,6 +127,7 @@ class RinexConverter:
120
127
  :param int minobs: Minimum observations per observation type (0)
121
128
  :param list[str] marker: marker details (name, number, type)
122
129
  :param list[str] antenna: antenna details (number, type)
130
+ :param list[float | str] antennahed: antenna delta H,E,D
123
131
  :param list[str] | receiver: receiver details (number, type, version)
124
132
  :param str observer: observer details
125
133
  :param list[str] comments: user comments
@@ -137,6 +145,9 @@ class RinexConverter:
137
145
  self._rinex_types = ALLOBS if rinex_types == [""] else rinex_types
138
146
  self._gnssfilter = ALLGNSS if gnssfilter == [""] else gnssfilter
139
147
  self._obsfilter = obsfilter
148
+ self._timecorrflag = timecorr
149
+ self._ionocorrflag = ionocorr
150
+ self._eopcorrflag = eopcorr
140
151
  while len(datasource) < 3: # OBS, NAV, MET
141
152
  datasource.append("R")
142
153
  self._datasource = ["R", "R", "R"] if datasource == [""] else datasource
@@ -149,6 +160,7 @@ class RinexConverter:
149
160
  self._minobs = minobs
150
161
  self._marker = marker
151
162
  self._antenna = antenna
163
+ self._antennahed = antennahed
152
164
  self._receiver = receiver
153
165
  self._observer = observer
154
166
  self.user_comments = comments
@@ -163,6 +175,7 @@ class RinexConverter:
163
175
  self._station = kwargs.get("station", "")
164
176
  self.verbosity = int(verbosity)
165
177
  self.logtofile = logtofile
178
+ self._logfile = ""
166
179
 
167
180
  self._outputs = {}
168
181
  self._tot = 0
@@ -179,10 +192,14 @@ class RinexConverter:
179
192
  rinex_version=self._rinex_version,
180
193
  gnssfilter=self._gnssfilter,
181
194
  obsfilter=self._obsfilter,
195
+ timecorr=self._timecorrflag,
196
+ ionocorr=self._ionocorrflag,
197
+ eopcorr=self._eopcorrflag,
182
198
  datasource=self._datasource[{OBS: 0, NAV: 1, MET: 2}[rt]],
183
199
  minobs=self._minobs,
184
200
  marker=self._marker,
185
201
  antenna=self._antenna,
202
+ antennahed=self._antennahed,
186
203
  receiver=self._receiver,
187
204
  observer=self._observer,
188
205
  **kwargs,
@@ -210,6 +227,7 @@ class RinexConverter:
210
227
  if isinstance(infile, str):
211
228
  infile = Path(infile)
212
229
 
230
+ self._logfile = infile
213
231
  outputpath = infile.parent
214
232
 
215
233
  # check total number of raw messages in file
@@ -308,8 +326,8 @@ class RinexConverter:
308
326
  RINEXProcessingError,
309
327
  TypeError,
310
328
  ValueError,
311
- ) as err:
312
- self.logger.error(f"Processing error {err}")
329
+ ):
330
+ self.logger.exception(f"RINEX NAV Conversion error")
313
331
  res = RINEX_ERROR
314
332
  except KeyboardInterrupt:
315
333
  self.logger.warning("Terminated by user")
@@ -346,9 +364,12 @@ class RinexConverter:
346
364
  :rtype: str
347
365
  """
348
366
 
367
+ datasource = self._datasource[{"O": 0, "N": 1, "M": 2}[rinextype]]
349
368
  hdr = (
350
369
  format_version(self._rinex_version, rinextype, self._gnssfilter)
351
370
  + format_runby()
371
+ + format_comments(f"log: {self._logfile}")
372
+ + format_comments(f"format: {datasource}")
352
373
  + format_comments(self.user_comments)
353
374
  )
354
375
  if self._rinex_version >= RINEX4: