cssrlib 1.2.1__tar.gz → 1.3.2__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 (49) hide show
  1. {cssrlib-1.2.1/src/cssrlib.egg-info → cssrlib-1.3.2}/PKG-INFO +2 -1
  2. {cssrlib-1.2.1 → cssrlib-1.3.2}/pyproject.toml +2 -1
  3. {cssrlib-1.2.1 → cssrlib-1.3.2}/setup.cfg +1 -1
  4. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/__init__.py +2 -0
  5. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/cssr_bds.py +21 -13
  6. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/cssr_has.py +18 -16
  7. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/cssr_mdc.py +64 -41
  8. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/cssr_pvs.py +16 -11
  9. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/cssrlib.py +808 -105
  10. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/dgps.py +41 -22
  11. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/ephemeris.py +96 -60
  12. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/ewss.py +34 -30
  13. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/gnss.py +589 -333
  14. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/mlambda.py +174 -100
  15. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/osnma.py +44 -39
  16. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/peph.py +219 -117
  17. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/plot.py +41 -16
  18. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/pntpos.py +66 -34
  19. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/ppp.py +97 -117
  20. cssrlib-1.3.2/src/cssrlib/ppprtk.py +23 -0
  21. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/pppssr.py +315 -177
  22. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/qznma.py +49 -32
  23. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/rawnav.py +97 -100
  24. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/rinex.py +52 -59
  25. cssrlib-1.3.2/src/cssrlib/rtcm.py +4974 -0
  26. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/rtk.py +6 -10
  27. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/sbas.py +213 -106
  28. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/test/test_eph.py +1 -1
  29. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/tlesim.py +4 -6
  30. cssrlib-1.3.2/src/cssrlib/utils.py +258 -0
  31. {cssrlib-1.2.1 → cssrlib-1.3.2/src/cssrlib.egg-info}/PKG-INFO +2 -1
  32. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib.egg-info/SOURCES.txt +1 -0
  33. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib.egg-info/requires.txt +1 -0
  34. cssrlib-1.2.1/src/cssrlib/ppprtk.py +0 -24
  35. cssrlib-1.2.1/src/cssrlib/rtcm.py +0 -2797
  36. {cssrlib-1.2.1 → cssrlib-1.3.2}/LICENSE +0 -0
  37. {cssrlib-1.2.1 → cssrlib-1.3.2}/MANIFEST.in +0 -0
  38. {cssrlib-1.2.1 → cssrlib-1.3.2}/README.md +0 -0
  39. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/data/30340780.21q +0 -0
  40. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/data/3034078M1.21O +0 -0
  41. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/data/SEPT078M.21P +0 -0
  42. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/data/SEPT078M1.21O +0 -0
  43. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/test/test_gnss.py +0 -0
  44. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/test/test_peph.py +0 -0
  45. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/test/test_rnx.py +0 -0
  46. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib/test/test_rtk.py +0 -0
  47. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib.egg-info/dependency_links.txt +0 -0
  48. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib.egg-info/not-zip-safe +0 -0
  49. {cssrlib-1.2.1 → cssrlib-1.3.2}/src/cssrlib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cssrlib
3
- Version: 1.2.1
3
+ Version: 1.3.2
4
4
  Summary: Python Toolkit for PPP/PPP-RTK positioning
5
5
  Home-page: https://github.com/hirokawa/cssrlib
6
6
  Author: Rui Hirokawa
@@ -16,6 +16,7 @@ Requires-Dist: notebook
16
16
  Requires-Dist: numpy
17
17
  Requires-Dist: matplotlib
18
18
  Requires-Dist: cartopy
19
+ Requires-Dist: cryptography
19
20
  Dynamic: license-file
20
21
 
21
22
  CSSRlib - Toolkit for PPP-RTK/RTK in Python using Compact SSR
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "cssrlib"
10
- version = "1.2.1"
10
+ version = "1.3.2"
11
11
  description = "Python Toolkit for PPP/PPP-RTK positioning"
12
12
  authors = [
13
13
  {name="Rui Hirokawa", email="rui.hirokawa@gmail.com"},
@@ -23,4 +23,5 @@ dependencies = [
23
23
  "numpy",
24
24
  "matplotlib",
25
25
  "cartopy",
26
+ "cryptography",
26
27
  ]
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = cssrlib
3
- version = 1.2.1
3
+ version = 1.3.2
4
4
  author = Rui Hirokawa
5
5
  author_email = rui.hirokawa@gmail.com
6
6
  description = Python Toolkit for PPP/PPP-RTK positioning
@@ -1,3 +1,5 @@
1
+ """CSSRLIB package for GNSS SSR and PPP/PPP-RTK processing."""
2
+
1
3
 
2
4
  from cssrlib.gnss import (gpst2time,epoch2time,ecef2pos,
3
5
  prn2sat)
@@ -1,7 +1,6 @@
1
- """
2
- BDS PPP correction data decoder
1
+ """BDS PPP correction data decoder.
3
2
 
4
- [1] BeiDou Navigation Satellite System Signal In Space Interface Control Document
3
+ [1] BeiDou Navigation Satellite System Signal In Space ICD
5
4
  Precise Point Positioning Service Signal PPP-B2b (Version 1.0), 2020
6
5
  """
7
6
 
@@ -12,7 +11,10 @@ from cssrlib.gnss import bdt2time, bdt2gpst, uGNSS, uSIG, uTYP, rSigRnx
12
11
 
13
12
 
14
13
  class cssr_bds(cssr):
14
+ """Class to decode BDS PPP (B2b) Compact SSR messages."""
15
+
15
16
  def __init__(self, foutname=None):
17
+ """Initialize BDS PPP SSR decoder."""
16
18
  super().__init__(foutname)
17
19
  self.MAXNET = 1
18
20
  self.cssrmode = sCSSRTYPE.BDS_PPP
@@ -23,6 +25,7 @@ class cssr_bds(cssr):
23
25
  self.cb_scl = 0.017
24
26
 
25
27
  def ssig2rsig(self, sys: sGNSS, utyp: uTYP, ssig):
28
+ """Convert SSR signal index to RINEX signal code."""
26
29
  gps_tbl = {
27
30
  0: uSIG.L1C,
28
31
  1: uSIG.L1P,
@@ -72,13 +75,14 @@ class cssr_bds(cssr):
72
75
  return rSigRnx(sys, utyp, usig_tbl[ssig])
73
76
 
74
77
  def sval(self, u, n, scl):
75
- """ calculate signed value based on n-bit int, lsb """
78
+ """Calculate signed value based on n-bit int, lsb."""
76
79
  invalid = -2**(n-1)
77
80
  dnu = -(2**(n-1)-1) # this value seems to be invalid
78
81
  y = np.nan if u == invalid or u == dnu else u*scl
79
82
  return y
80
83
 
81
84
  def slot2prn(self, slot):
85
+ """Convert BDS B2b satellite slot index to system and PRN."""
82
86
  prn = 0
83
87
  sys = uGNSS.NONE
84
88
  if slot >= 1 and slot <= 63:
@@ -96,6 +100,7 @@ class cssr_bds(cssr):
96
100
  return sys, prn
97
101
 
98
102
  def decode_head(self, msg, i, st=-1):
103
+ """Decode BDS PPP message header."""
99
104
  self.tod, _, iodssr = bs.unpack_from('u17u4u2', msg, i)
100
105
  i += 23
101
106
 
@@ -107,10 +112,11 @@ class cssr_bds(cssr):
107
112
  if self.week >= 0:
108
113
  self.time = bdt2gpst(bdt2time(self.week, self.tow))
109
114
 
110
- head = {'uint': 0, 'mi': 0, 'iodssr': iodssr}
115
+ head = {'udi': 0, 'mi': 0, 'iodssr': iodssr}
111
116
  return head, i
112
117
 
113
118
  def add_gnss(self, mask, blen, gnss):
119
+ """Add decoded GNSS satellite mask entries."""
114
120
  prn, nsat = self.decode_mask(mask, blen)
115
121
  self.nsat_g[gnss] = nsat
116
122
  self.nsat_n += nsat
@@ -124,7 +130,7 @@ class cssr_bds(cssr):
124
130
  self.gnss_n.append(gnss)
125
131
 
126
132
  def decode_cssr_mask(self, msg, i):
127
- """decode MT1 Mask message """
133
+ """Decode MT1 Mask message."""
128
134
  head, i = self.decode_head(msg, i, sCSSR.MASK)
129
135
 
130
136
  self.iodp = bs.unpack_from('u4', msg, i)[0]
@@ -179,6 +185,7 @@ class cssr_bds(cssr):
179
185
  return i
180
186
 
181
187
  def decode_cssr_orb_sat(self, msg, i, inet, sat_n):
188
+ """Decode satellite orbit correction for BDS PPP."""
182
189
  slot, iodn, iodc = bs.unpack_from('u9u10u3', msg, i)
183
190
  i += 22
184
191
 
@@ -216,7 +223,7 @@ class cssr_bds(cssr):
216
223
  return i
217
224
 
218
225
  def decode_cssr_orb(self, msg, i, inet=0):
219
- """decode MT2 orbit + URA message """
226
+ """Decode MT2 orbit + URA message."""
220
227
  head, i = self.decode_head(msg, i)
221
228
  sat_n = np.array(self.sat_n)
222
229
 
@@ -233,7 +240,7 @@ class cssr_bds(cssr):
233
240
  return i
234
241
 
235
242
  def decode_cssr_cbias(self, msg, i, inet=0):
236
- """decode MT3 Code Bias Correction message """
243
+ """Decode MT3 Code Bias Correction message."""
237
244
  head, i = self.decode_head(msg, i)
238
245
  nsat = self.nsat_n
239
246
  self.flg_net = False
@@ -270,7 +277,7 @@ class cssr_bds(cssr):
270
277
  return i
271
278
 
272
279
  def decode_cssr_clk_sat(self, msg, i, inet, sat):
273
- """ decode clock correction for satellite """
280
+ """Decode clock correction for satellite."""
274
281
  iodc, dclk = bs.unpack_from('u3s{:d}'.format(self.dclk_blen), msg, i)
275
282
  i += 3+self.dclk_blen
276
283
 
@@ -285,7 +292,7 @@ class cssr_bds(cssr):
285
292
  return i
286
293
 
287
294
  def decode_cssr_clk(self, msg, i, inet=0):
288
- """decode MT4 Clock Correction message """
295
+ """Decode MT4 Clock Correction message."""
289
296
  head, i = self.decode_head(msg, i)
290
297
  if self.iodssr != head['iodssr']:
291
298
  return -1
@@ -311,7 +318,7 @@ class cssr_bds(cssr):
311
318
  return i
312
319
 
313
320
  def decode_cssr_ura(self, msg, i):
314
- """decode MT5 URA message """
321
+ """Decode MT5 URA message."""
315
322
  head, i = self.decode_head(msg, i)
316
323
  if self.iodssr != head['iodssr']:
317
324
  return -1
@@ -332,7 +339,7 @@ class cssr_bds(cssr):
332
339
  return i
333
340
 
334
341
  def decode_cssr_comb1(self, msg, i, inet=0):
335
- """decode MT6 combined message #1 """
342
+ """Decode MT6 combined message #1."""
336
343
  numc, numo = bs.unpack_from('u5u3', msg, i)
337
344
  sat_n = np.array(self.sat_n)
338
345
 
@@ -354,7 +361,7 @@ class cssr_bds(cssr):
354
361
  return i
355
362
 
356
363
  def decode_cssr_comb2(self, msg, i, inet=0):
357
- """decode MT7 combined message #2 """
364
+ """Decode MT7 combined message #2."""
358
365
  numc, numo = bs.unpack_from('u5u3', msg, i)
359
366
  sat_n = np.array(self.sat_n)
360
367
 
@@ -377,6 +384,7 @@ class cssr_bds(cssr):
377
384
  return i
378
385
 
379
386
  def decode_cssr(self, msg, i=0):
387
+ """Decode BDS PPP B2b message."""
380
388
  mt = bs.unpack_from('u6', msg, i)[0]
381
389
  i += 6
382
390
 
@@ -1,5 +1,4 @@
1
- """
2
- Galileo HAS correction data decoder
1
+ """Galileo HAS correction data decoder.
3
2
 
4
3
  [1] Galileo High Accuracy Service Signal-in-Space
5
4
  Interface Control Document (HAS SIS ICD), Issue 1.0, May 2022
@@ -15,7 +14,10 @@ from binascii import unhexlify
15
14
 
16
15
 
17
16
  class cssr_has(cssr):
17
+ """Class to decode Galileo HAS Compact SSR messages."""
18
+
18
19
  def __init__(self, foutname=None):
20
+ """Initialize Galileo HAS SSR decoder."""
19
21
  super().__init__(foutname)
20
22
  self.MAXNET = 1
21
23
  self.cssrmode = sCSSRTYPE.GAL_HAS_SIS
@@ -29,13 +31,14 @@ class cssr_has(cssr):
29
31
  self.pb_scl = 0.01 # cycles
30
32
 
31
33
  def sval(self, u, n, scl):
32
- """ calculate signed value based on n-bit int, lsb """
34
+ """Calculate signed value based on n-bit int, lsb."""
33
35
  invalid = -2**(n-1)
34
36
  dnu = 2**(n-1)-1
35
37
  y = np.nan if u == invalid or u == dnu else u*scl
36
38
  return y
37
39
 
38
40
  def decode_cssr_clk_sub(self, msg, i=0):
41
+ """Decode Galileo HAS clock subset correction message."""
39
42
  head, i = self.decode_head(msg, i)
40
43
  self.flg_net = False
41
44
  if self.iodssr != head['iodssr']:
@@ -58,12 +61,11 @@ class cssr_has(cssr):
58
61
  return i
59
62
 
60
63
  def decode_head(self, msg, i, st=-1):
61
- """ decode header part of HAS messages """
62
-
64
+ """Decode header part of HAS messages."""
63
65
  if st == sCSSR.MASK:
64
- ui = 0
66
+ udi = 0
65
67
  else:
66
- ui = bs.unpack_from('u4', msg, i)[0]
68
+ udi = bs.unpack_from('u4', msg, i)[0]
67
69
  i += 4
68
70
 
69
71
  if self.tow0 >= 0:
@@ -71,13 +73,12 @@ class cssr_has(cssr):
71
73
  if self.week >= 0:
72
74
  self.time = gpst2time(self.week, self.tow)
73
75
 
74
- head = {'uint': ui, 'mi': 0, 'iodssr': self.iodssr}
76
+ head = {'udi': udi, 'mi': 0, 'iodssr': self.iodssr}
75
77
  return head, i
76
78
 
77
79
  def decode_cssr(self, msg, i=0):
78
- """ decode HAS messages """
79
-
80
- # Galileo HAS-SIS only defines MT=1, MT=4073 is CSSR message used in the
80
+ """Decode HAS messages."""
81
+ # Galileo HAS-SIS only defines MT=1; MT=4073 is CSSR used in the
81
82
  # Galileo HAS test dataset
82
83
  #
83
84
  if self.msgtype != 1 and self.msgtype != 4073:
@@ -131,9 +132,10 @@ class cssr_has(cssr):
131
132
 
132
133
 
133
134
  class cnav_msg():
134
- """ class to handle Galileo E6 (CNAV) message """
135
+ """Class to handle Galileo E6 (CNAV) message."""
135
136
 
136
137
  def __init__(self):
138
+ """Initialize Galileo C/NAV HAS message decoder."""
137
139
  self.mid_decoded = []
138
140
  self.mid_ = -1
139
141
  self.ms_ = -1
@@ -148,10 +150,11 @@ class cnav_msg():
148
150
  self.monlevel = 0
149
151
 
150
152
  def load_gmat(self, file_gm):
153
+ """Load Reed-Solomon generator matrix for Galileo HAS decoding."""
151
154
  self.gMat = np.genfromtxt(file_gm, dtype="u1", delimiter=",")
152
155
 
153
156
  def decode_has_header(self, buff, i):
154
- """ decode header of HAS pages (obsoleted) """
157
+ """Decode header of HAS pages (obsoleted)."""
155
158
  if bs.unpack_from('u24', buff, i)[0] == 0xaf3bc3:
156
159
  return 0, 0, 0, 0, 0
157
160
 
@@ -160,7 +163,7 @@ class cnav_msg():
160
163
  return hass, mt, mid, ms, pid
161
164
 
162
165
  def decode_has_page(self, idx, has_pages, gMat, ms):
163
- """ HPVRS decoding for RS(255,32,224) """
166
+ """Decode HPVRS Reed-Solomon RS(255,32,224) page."""
164
167
  HASmsg = bytes()
165
168
  k = len(idx)
166
169
  if k >= ms:
@@ -172,8 +175,7 @@ class cnav_msg():
172
175
  return HASmsg
173
176
 
174
177
  def decode_cnav(self, tow, vi):
175
- """ decode Galileo CNAV message """
176
-
178
+ """Decode Galileo CNAV message."""
177
179
  HASmsg = None
178
180
 
179
181
  for vn in vi:
@@ -1,9 +1,8 @@
1
- """
2
- QZSS MADOCA-PPP correction data decoder
1
+ """QZSS MADOCA-PPP correction data decoder.
3
2
 
4
3
  [1] Quasi-Zenith Satellite System Interface Specification Multi-GNSS
5
4
  Advanced Orbit and Clock Augmentation - Precise Point Positioning
6
- (IS-QZSS-MDC-004), 2025
5
+ (IS-QZSS-MDC-005draft), October, 2026
7
6
 
8
7
  """
9
8
 
@@ -11,10 +10,14 @@ import numpy as np
11
10
  import bitstruct as bs
12
11
  from cssrlib.cssrlib import cssr, sCSSRTYPE, sCSSR, local_corr, sCType
13
12
  from cssrlib.gnss import gpst2time, time2str, uGNSS, prn2sat, rCST, sat2id
13
+ from cssrlib.gnss import pos2ecef, gtime_t
14
14
 
15
15
 
16
16
  class areaInfo():
17
+ """Class to store MADOCA-PPP service area information."""
18
+
17
19
  def __init__(self, sid, latr, lonr, p1=0, p2=0):
20
+ """Initialize MADOCA-PPP area information."""
18
21
  self.sid = sid
19
22
  self.latr = latr
20
23
  self.lonr = lonr
@@ -26,7 +29,10 @@ class areaInfo():
26
29
 
27
30
 
28
31
  class ionoCorr():
32
+ """Class to store MADOCA-PPP regional ionospheric corrections."""
33
+
29
34
  def __init__(self):
35
+ """Initialize MADOCA-PPP ionospheric correction data."""
30
36
  self.t0 = None
31
37
  self.qi = []
32
38
  self.iodssr = 0
@@ -36,7 +42,10 @@ class ionoCorr():
36
42
 
37
43
 
38
44
  class cssr_mdc(cssr):
45
+ """Class to decode QZSS MADOCA-PPP L6D/L6E messages."""
46
+
39
47
  def __init__(self, foutname=None):
48
+ """Initialize MADOCA-PPP L6 message decoder."""
40
49
  super().__init__(foutname)
41
50
 
42
51
  self.cssrmode = sCSSRTYPE.QZS_MADOCA
@@ -44,6 +53,8 @@ class cssr_mdc(cssr):
44
53
 
45
54
  self.pnt = {}
46
55
  self.ci = {}
56
+ self.reg_alrt = {}
57
+ self.reg_iod = {}
47
58
 
48
59
  self.area = -1
49
60
  self.reg = -1
@@ -51,6 +62,10 @@ class cssr_mdc(cssr):
51
62
  self.narea_t = {1: 8, 2: 16, 3: 5, 4: 1, 5: 8}
52
63
  self.MAXNET = np.sum(list(self.narea_t.values()))
53
64
 
65
+ self.tmax = {sCType.CLOCK: 60.0, sCType.ORBIT: 60.0,
66
+ sCType.CBIAS: 60.0, sCType.PBIAS: 60.0,
67
+ sCType.TROP: 120.0, sCType.STEC: 300.0}
68
+
54
69
  for inet in range(self.MAXNET+1):
55
70
  self.lc.append(local_corr())
56
71
  self.lc[inet].inet = inet
@@ -60,35 +75,40 @@ class cssr_mdc(cssr):
60
75
  self.lc[inet].t0 = {}
61
76
 
62
77
  def find_grid_index(self, pos):
63
- """ find region/area """
64
-
78
+ """Find region/area."""
65
79
  latd, lond = np.rad2deg(pos[0:2])
80
+ rr = pos2ecef(pos)
66
81
  self.reg = self.area = -1
82
+ min_dist = 1e5
67
83
 
68
84
  for reg in self.pnt.keys():
85
+ if self.reg_alrt.get(reg, 0) != 0:
86
+ continue
69
87
  for area in self.pnt[reg].keys():
70
88
  p = self.pnt[reg][area]
89
+ ref_ecef = pos2ecef(
90
+ np.array([p.latr*rCST.D2R, p.lonr*rCST.D2R, 0.0]))
91
+ dist = np.linalg.norm(rr - ref_ecef)*1e-3 # [km]
92
+ if dist > min_dist:
93
+ continue
71
94
  if p.sid == 0: # rectangle shape:
72
95
  if p.latr-p.lats <= latd and latd <= p.latr+p.lats and \
73
96
  p.lonr-p.lons <= lond and lond <= p.lonr+p.lons:
74
97
  self.reg = reg
75
98
  self.area = area
76
- break
99
+ min_dist = dist
77
100
  elif p.sid == 1: # circle
78
- dn = (latd - p.latr)*rCST.D2R*rCST.RE_WGS84
79
- de = (lond - p.lonr)*rCST.D2R * \
80
- rCST.RE_WGS84*np.cos(p.lonr*rCST.D2R)
81
- r = np.sqrt(dn**2+de**2)
82
- if r <= p.rng:
101
+ if dist <= p.rng:
83
102
  self.reg = reg
84
103
  self.area = area
85
- break
104
+ min_dist = dist
86
105
 
87
106
  inet = self.get_inet(self.reg, self.area)
107
+ self.inet = inet
88
108
  return inet
89
109
 
90
110
  def get_dpos(self, pos):
91
- """ calculate position offset from reference """
111
+ """Calculate position offset from reference."""
92
112
  if self.reg < 0 or self.area < 0:
93
113
  print("get_dpos: region, area not defined.")
94
114
  return 0, 0
@@ -101,7 +121,7 @@ class cssr_mdc(cssr):
101
121
  return dlat, dlon
102
122
 
103
123
  def get_stec(self, dlat=0.0, dlon=0.0):
104
- """ calculate STEC correction by interporation """
124
+ """Calculate STEC correction by interporation."""
105
125
  if self.inet < 0:
106
126
  print("get_stec: region, area not defined.")
107
127
  return np.zeros(self.nsat_n)
@@ -118,14 +138,19 @@ class cssr_mdc(cssr):
118
138
  return stec
119
139
 
120
140
  def decode_mdc_stec_area(self, buff, i=0):
121
- """ decoder for MT1 - STEC Coverage Message """
141
+ """Decode MT1 STEC coverage message."""
122
142
  tow, uid, mi, iodssr = bs.unpack_from('u20u4u1u4', buff, i)
123
143
  i += 29
144
+ self.tow = tow
124
145
  self.tow0 = tow//3600*3600
146
+ if self.week >= 0:
147
+ self.time = gpst2time(self.week, self.tow)
125
148
  reg, alrt, len_, narea = bs.unpack_from('u8u1u16u5', buff, i)
126
149
  i += 30
127
150
  self.reg = reg
128
151
  self.alrt = alrt
152
+ self.reg_alrt[reg] = alrt
153
+ self.reg_iod[reg] = iodssr
129
154
 
130
155
  if reg not in self.pnt:
131
156
  self.pnt[reg] = {}
@@ -152,8 +177,7 @@ class cssr_mdc(cssr):
152
177
  return i
153
178
 
154
179
  def get_inet(self, reg, area):
155
- """ region, area to inet conversion """
156
-
180
+ """Convert region and area ID to network index."""
157
181
  if reg < 0 or area < 0:
158
182
  return -1
159
183
 
@@ -168,9 +192,17 @@ class cssr_mdc(cssr):
168
192
  return inet
169
193
 
170
194
  def decode_mdc_stec_corr(self, buff, i=0):
171
- """ decoder for MT2 - STEC Correction Message """
195
+ """Decode MT2 STEC correction message."""
172
196
  dtow, uid, mi, iodssr = bs.unpack_from('u12u4u1u4', buff, i)
173
197
  i += 21
198
+ if self.tow0 >= 0:
199
+ tow = self.tow0 + dtow
200
+ if self.tow >= 0 and tow < self.tow - 1800:
201
+ tow += 3600
202
+ self.tow = tow
203
+ t0 = gpst2time(self.week, self.tow) if self.week >= 0 else gtime_t()
204
+ if self.week >= 0:
205
+ self.time = t0
174
206
  reg, area, stype_ = bs.unpack_from('u8u5u2', buff, i)
175
207
  i += 15
176
208
 
@@ -184,7 +216,6 @@ class cssr_mdc(cssr):
184
216
 
185
217
  inet = self.get_inet(reg, area)
186
218
 
187
- self.lc[inet].nsat_n = np.sum(nsat)
188
219
  ci = {}
189
220
  qi = {}
190
221
  stype = {}
@@ -193,39 +224,29 @@ class cssr_mdc(cssr):
193
224
  for gnss in range(5):
194
225
  sys = sys_t[gnss]
195
226
  for k in range(nsat[gnss]):
196
- c01 = c10 = c11 = c02 = c20 = 0.0
197
-
198
- prn, qi_, c00 = bs.unpack_from('u6u6s14', buff, i)
199
- i += 26
227
+ prn, cls_, val = bs.unpack_from('u6u3u3', buff, i)
228
+ i += 12
200
229
 
201
230
  if sys == uGNSS.QZS:
202
231
  prn += 192
203
232
  sat = prn2sat(sys, prn)
204
- qi[sat] = qi_
233
+ ci_sat, i = self.decode_cssr_stec_coeff(buff, stype_, i)
234
+ if np.isnan(ci_sat).any():
235
+ continue
236
+ qi[sat] = self.quality_idx(cls_, val)
205
237
  stype[sat] = stype_
206
-
207
- if stype_ > 0:
208
- c01, c10 = bs.unpack_from('s12s12', buff, i)
209
- i += 24
210
- if stype_ > 1:
211
- c11 = bs.unpack_from('s10', buff, i)[0]
212
- i += 10
213
- if stype_ > 2:
214
- c02, c20 = bs.unpack_from('s8s8', buff, i)
215
- i += 16
216
-
217
238
  sat_ += [sat]
218
- ci[sat] = np.array(
219
- [c00*0.05, c01*0.02, c10*0.02, c11*0.02,
220
- c02*5e-3, c20*5e-3])
239
+ ci[sat] = ci_sat
240
+ self.set_t0(inet, sat, sCType.STEC, t0)
221
241
  j += 1
222
242
 
243
+ self.lc[inet].nsat_n = len(sat_)
244
+ self.lc[inet].flg_stec = 2
223
245
  self.lc[inet].stype = stype
224
246
  self.lc[inet].sat_n = sat_
225
247
  self.lc[inet].ci = ci
226
248
  self.lc[inet].stec_quality = qi
227
249
 
228
- t0 = gpst2time(self.week, self.tow0+dtow)
229
250
  self.set_t0(inet, 0, sCType.STEC, t0)
230
251
  self.lc[inet].cstat |= (1 << sCType.STEC)
231
252
 
@@ -236,7 +257,7 @@ class cssr_mdc(cssr):
236
257
  def out_log(self):
237
258
  # if self.msgtype not in (1, 2):
238
259
  # return super(cssr_mdc, self).out_log()
239
-
260
+ """Write decoded MADOCA-PPP STEC messages to log file."""
240
261
  sz_t = [1, 3, 4, 6]
241
262
 
242
263
  if self.time == -1:
@@ -276,9 +297,11 @@ class cssr_mdc(cssr):
276
297
  self.fh.flush()
277
298
 
278
299
  def decode_cssr(self, msg, i=0):
279
- """decode Compact SSR message with MADOCA-PPP extension """
300
+ """Decode Compact SSR message with MADOCA-PPP extension."""
280
301
  df = {'msgtype': 4073}
281
302
  while df['msgtype'] in [1, 2, 4073]:
303
+ if len(msg)*8 < i + 16:
304
+ return 0
282
305
  df = bs.unpack_from_dict('u12u4', ['msgtype', 'subtype'], msg, i)
283
306
  i += 16
284
307
  if df['msgtype'] not in [1, 2, 4073]:
@@ -1,11 +1,10 @@
1
- """
2
- PVS (PPP via SouthPAN) correction data decoder
1
+ """PVS (PPP via SouthPAN) correction data decoder.
3
2
 
4
3
  [1] Service Definition Document for Open Services, SBAS-STN-0001,
5
- Revision 02, December 2022
4
+ Revision 04, April 2026
6
5
 
7
6
  [2] Service Definition Document for Data Access Services, SBAS-STN-0002,
8
- Revision 01, November 2023
7
+ Revision 02, April 2026
9
8
 
10
9
  For use of SSR corrections see section A 3.9.2 in
11
10
 
@@ -24,7 +23,7 @@ from binascii import unhexlify
24
23
 
25
24
 
26
25
  def decode_sinca_line(line):
27
- """ SINCA (SISNeT compression algorithm) decoder """
26
+ """Decode SINCA (SISNeT compression algorithm) line."""
28
27
  if line[0:4] != '*MSG':
29
28
  return None
30
29
  v = line.split(',')
@@ -47,7 +46,10 @@ def decode_sinca_line(line):
47
46
 
48
47
 
49
48
  class cssr_pvs(cssr):
49
+ """Class to decode SouthPAN PVS PPP messages."""
50
+
50
51
  def __init__(self, foutname=None):
52
+ """Initialize PVS PPP decoder."""
51
53
  super().__init__(foutname)
52
54
  self.MAXNET = 1
53
55
  self.cssrmode = sCSSRTYPE.PVS_PPP
@@ -67,6 +69,7 @@ class cssr_pvs(cssr):
67
69
  self.nav_mode = {uGNSS.GPS: 0, uGNSS.GAL: 1}
68
70
 
69
71
  def slot2sat(self, slot):
72
+ """Convert satellite slot index to satellite number."""
70
73
  sat = 0
71
74
  if slot >= 1 and slot <= 32:
72
75
  prn = slot
@@ -86,14 +89,16 @@ class cssr_pvs(cssr):
86
89
  return sat
87
90
 
88
91
  def adjust_time_week(self, time, time0):
92
+ """Adjust time-of-week for week rollover."""
89
93
  dt = timediff(time, time0)
90
- if dt > rCST.DAY_SEC/2:
91
- time = timeadd(time, -rCST.DAY_SEC)
92
- elif dt < -rCST.DAY_SEC/2:
93
- time = timeadd(time, rCST.DAY_SEC)
94
+ if dt > rCST.HALFWEEK_SEC:
95
+ time = timeadd(time, -rCST.WEEK_SEC)
96
+ elif dt < -rCST.HALFWEEK_SEC:
97
+ time = timeadd(time, rCST.WEEK_SEC)
94
98
  return time
95
99
 
96
100
  def check_validity(self, time):
101
+ """Check validity time of PVS SSR corrections."""
97
102
  for sat in self.sat_n:
98
103
  if timediff(time, self.lc[0].t0[sat][sCType.CLOCK]) > \
99
104
  self.tmax[sCType.CLOCK]:
@@ -104,7 +109,7 @@ class cssr_pvs(cssr):
104
109
  self.lc[0].dorb[sat] = np.zeros(3)
105
110
 
106
111
  def decode_cssr_orb(self, msg, i, inet=0):
107
- """ Types 32 clock-ephemeris correction and covariance matrix """
112
+ """Decode Type 32 clock-ephemeris correction and covariance matrix."""
108
113
  slot, iodn = bs.unpack_from('u8u10', msg, i)
109
114
  i += 18
110
115
  dx, dy, dz, db = bs.unpack_from('s11s11s11s12', msg, i)
@@ -158,7 +163,7 @@ class cssr_pvs(cssr):
158
163
  return i
159
164
 
160
165
  def decode_cssr(self, msg, i=0):
161
-
166
+ """Decode PVS PPP DFMC SBAS message."""
162
167
  _, mt = bs.unpack_from('u4u6', msg, i)
163
168
  i += 10
164
169