Skip to content

Commit cee2104

Browse files
committed
- updated to support newest edition of draft SC134 messages.
1 parent ede5a67 commit cee2104

File tree

1 file changed

+149
-86
lines changed

1 file changed

+149
-86
lines changed

src/cssrlib/rtcm.py

Lines changed: 149 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ class Integrity():
146146
sig_k_t = [0.0, 0.04, 0.07, 0.10, 0.20, 0.25, 0.30, 0.40, 0.50, 0.70,
147147
1, 2, 5, 7, 10, -1]
148148

149+
# GNSS Signal Modulation Mask Table 8-45
150+
mod_t = ['BPSK(1)', 'BPSK(5)', 'BPSK(10)', 'BOC(1,1)', 'CBOC(6,1,1/11)',
151+
'AltBOC(15,10)', '', '']
152+
149153
# Validity Period DFi065
150154
vp_tbl = [1, 2, 5, 10, 15, 30, 60, 120, 240,
151155
300, 600, 900, 1800, 3600, 7200, 10800]
@@ -1219,13 +1223,14 @@ def out_log_ssr_orb(self, sys, flg_vel=True):
12191223

12201224
def out_log(self, obs=None, eph=None, geph=None, seph=None):
12211225
""" output ssr message to log file """
1226+
sys = -1
12221227
if self.is_ssrtype(self.msgtype, True):
12231228
sys = self.get_ssr_sys(self.msgtype)
12241229
inet = self.inet
12251230
self.fh.write("{:4d}\t{:s}\n".format(self.msgtype,
12261231
time2str(self.time)))
12271232

1228-
if self.subtype not in [sRTCM.SSR_META, sRTCM.SSR_GRID]:
1233+
if sys > 0 and self.subtype not in [sRTCM.SSR_META, sRTCM.SSR_GRID]:
12291234
self.fh.write(f" Update Interval: {self.udint_t[self.uint]}[s]")
12301235

12311236
self.fh.write(f" MultiMsg: {self.mi}\n")
@@ -1452,6 +1457,61 @@ def out_log(self, obs=None, eph=None, geph=None, seph=None):
14521457
self.fh.write("\n")
14531458
self.fh.flush()
14541459

1460+
if self.msgtype == 54: # RTCM SC134 integrity messages
1461+
self.fh.write(f" Multiple Message Sequence :{self.integ.seq:}\n")
1462+
self.fh.write(" Message Type: {:4d}-{:-2d}\n".
1463+
format(self.msgtype, self.subtype))
1464+
if self.subtype == 9: # MT54-9 VMAP
1465+
self.fh.write(f" TOF (s): {self.integ.tow:9.3f}\n")
1466+
self.fh.write(f" Number of Area Points: {self.integ.narea:}\n")
1467+
self.fh.write(f" Number of Azimuth Slices: {
1468+
self.integ.naz:}\n")
1469+
self.fh.write(" Area Points (lat, long, alt):\n")
1470+
for k in range(self.integ.narea):
1471+
self.fh.write(" {:2d}\t{:12.9f}\t{:12.9f}\t{:4.0f}\n".format
1472+
(k+1, self.integ.pos[k, 0], self.integ.pos[k, 1],
1473+
self.integ.pos[k, 2]))
1474+
1475+
self.fh.write(" Visibility mask (az, el):\n")
1476+
for k in range(self.integ.naz):
1477+
self.fh.write(" {:3.0f}\t{:3.0f}\n".format
1478+
(self.integ.azel[k][0], self.integ.azel[k][1]))
1479+
elif self.subtype == 10:
1480+
self.fh.write(f" TOF (s): {self.integ.tow:9.3f}\n")
1481+
self.fh.write(f" Number of Area Points: {self.integ.narea:}\n")
1482+
self.fh.write(f" Multipath Model ID: {self.integ.mm_id:}\n")
1483+
self.fh.write(" Boundary Points (lat, long, alt):\n")
1484+
for k in range(self.integ.narea):
1485+
self.fh.write(" {:2d}\t{:12.9f}\t{:12.9f}\t{:4.0f}\n".format
1486+
(k+1, self.integ.pos[k, 0], self.integ.pos[k, 1],
1487+
self.integ.pos[k, 2]))
1488+
1489+
if self.integ.mm_id == 0:
1490+
1491+
self.fh.write(
1492+
" Components (Prob, Exp, stdev):\n")
1493+
for k in range(self.integ.narea):
1494+
for j in range(self.integ.np[k]):
1495+
self.fh.write(" {:2d}\t{:1d}\t{:6.4f}\t{:7.2f}\t{:7.2f}\n".format
1496+
(k+1, j+1, self.integ.mm_param[k][j][0],
1497+
self.integ.mm_param[k][j][1],
1498+
self.integ.mm_param[k][j][2]))
1499+
elif self.integ.mm_id in [1, 2]:
1500+
n = 3 if self.integ.mm_id == 1 else 4
1501+
self.fh.write(
1502+
" Multipath Parameters (Modulation,a,b,c(,d)) :\n")
1503+
for k in range(self.integ.narea):
1504+
for j in range(self.integ.np[k]):
1505+
s = int(self.integ.mm_param[k][j][0])
1506+
self.fh.write(" {:2d}\t{:1d}\t{:10s}".format
1507+
(k+1, j+1, self.integ.mod_t[s]))
1508+
for i in range(n):
1509+
self.fh.write("\t{:7.4f}".format
1510+
(self.integ.mm_param[k][j][i+1]))
1511+
self.fh.write("\n")
1512+
1513+
self.fh.flush()
1514+
14551515
if eph is not None:
14561516
sys, prn = sat2prn(eph.sat)
14571517
self.fh.write(" {:20s}{:6d} ({:s})\n".format("PRN:", prn,
@@ -3026,22 +3086,22 @@ def decode_integrity_cnr_acg(self, msg, i):
30263086
def decode_integrity_vmap(self, msg, i):
30273087
""" RTCM SC-134 Satellite Visibility Map Message (MT9) """
30283088

3029-
if self.test_mode: # for test
3030-
wg, st, rev = bs.unpack_from('u4u8u8', msg, i)
3031-
i += 20
3032-
self.subtype = st
3033-
self.ver = rev
3034-
self.wg = wg-1
3035-
30363089
# GPS Epoch Time (TOW) DFi008
30373090
# number of area points DFi201
30383091
# Number of Azimuth Slices DFi205
30393092
# Message Continuation Flag DFi021
3040-
tow, narea, naz, mcf = bs.unpack_from('u30u8u6u1', msg, i)
3093+
3094+
tow, narea, naz, mi = bs.unpack_from('u30u8u6u1', msg, i)
30413095
i += 45
3042-
self.integ.pos = np.zeros((narea, 3))
3096+
# Multiple Message Sequence Number DFi079
3097+
self.integ.seq = bs.unpack_from('u5', msg, i)[0]
3098+
i += 5
30433099

3044-
tow *= 1e-3
3100+
self.integ.tow = tow*1e-3
3101+
self.integ.narea = narea
3102+
self.integ.naz = naz
3103+
self.mi = mi
3104+
self.integ.pos = np.zeros((narea, 3))
30453105

30463106
for k in range(narea):
30473107
# Area Point - Lat DFi202
@@ -3052,71 +3112,59 @@ def decode_integrity_vmap(self, msg, i):
30523112

30533113
self.integ.pos[k, :] = [lat*1.1e-8, lon*1.1e-8, alt]
30543114

3055-
self.integ.azel = np.zeros((naz, 2))
3115+
self.integ.azel = np.zeros((naz, 2))
30563116

3057-
# Azimuth DFi206
3058-
# Elevation Mask DFi208
3059-
az = 0
3060-
for k in range(naz):
3061-
daz, mask_el = bs.unpack_from('u9u7', msg, i)
3062-
i += 16
3063-
az += daz
3064-
self.integ.azel[k, :] = [az, mask_el]
3117+
# Azimuth DFi206
3118+
# Elevation Mask DFi208
3119+
az = 0
3120+
for k in range(naz):
3121+
daz, mask_el = bs.unpack_from('u9u7', msg, i)
3122+
i += 16
3123+
az += daz
3124+
self.integ.azel[k, :] = [az, mask_el]
30653125

30663126
return i
30673127

30683128
def decode_integrity_mmap(self, msg, i):
30693129
""" RTCM SC-134 Multipath Map Message (MT10) """
30703130

3071-
if self.test_mode: # for test
3072-
wg, st, rev = bs.unpack_from('u4u8u8', msg, i)
3073-
i += 20
3074-
self.subtype = st
3075-
self.ver = rev
3076-
self.wg = wg-1
3077-
3078-
# GPS Epoch Time (TOW) DFi008
3079-
# number of area points DFi201
3080-
# multipath model ID DFi209: 0:GMM,1:MBM,2:JM
3081-
tow, narea, mm_id, mcf = bs.unpack_from(
3082-
'u30u8u3u1', msg, i)
3083-
i += 42
3084-
tow *= 1e-3
3085-
self.integ.pos = np.zeros((narea, 3))
3086-
self.integ.mm_id = mm_id
3087-
3088-
if mm_id == 0:
3131+
# GPS Epoch Time (TOW) DFi008
3132+
# number of area points DFi201
3133+
# multipath model ID DFi209: 0:GMM,1:MBM,2:JM
3134+
tow, narea, mm_id, self.mi, self.integ.seq = bs.unpack_from(
3135+
'u30u8u3u1u5', msg, i)
3136+
i += 47
3137+
self.integ.tow = tow*1e-3
3138+
self.integ.narea = narea
3139+
self.integ.pos = np.zeros((narea, 3))
3140+
self.integ.mm_id = mm_id
30893141

3090-
for k in range(narea):
3091-
# Area Point - Lat DFi202
3092-
# Area Point - Lon DFi203
3093-
# Area Point - Height DFi204
3094-
lat, lon, alt = bs.unpack_from('s34s35s14', msg, i)
3095-
i += 83
3142+
sigmask = np.zeros(narea, dtype=np.int32)
30963143

3097-
self.integ.pos[k, :] = [lat*1.1e-8, lon*1.1e-8, alt]
3144+
self.integ.np = np.zeros(narea, dtype=np.int32)
3145+
self.integ.mm_param = {}
3146+
n = 4 if mm_id == 1 else 5
30983147

3099-
elif mm_id == 1 or mm_id == 2:
3100-
sigmask = np.array(narea, dtype=np.int32)
3101-
for k in range(narea):
3102-
# GNSS Signal Modulation Mask - DFi214
3103-
# Area Point - Lat DFi202
3104-
# Area Point - Lon DFi203
3105-
# Area Point - Height DFi204
3106-
sigmask[k], lat, lon, alt = bs.unpack_from(
3107-
'u8s34s35s14', msg, i)
3108-
i += 91
3109-
3110-
self.integ.pos[k, :] = [lat*1.1e-8, lon*1.1e-8, alt]
3148+
for k in range(narea):
3149+
if mm_id in [1, 2]:
3150+
sigmask[k] = bs.unpack_from('u8', msg, i)[0]
3151+
i += 8
3152+
# Area Point - Lat DFi202
3153+
# Area Point - Lon DFi203
3154+
# Area Point - Height DFi204
3155+
lat, lon, alt = bs.unpack_from('s34s35s14', msg, i)
3156+
i += 83
3157+
self.integ.pos[k, :] = [lat*1.1e-8, lon*1.1e-8, alt]
3158+
# i += 26 # ?
31113159

31123160
if mm_id == 0:
31133161
# Number of GMM components DFi210
31143162
ngmm = bs.unpack_from('u2', msg, i)[0]
31153163
i += 2
3164+
self.integ.np[k] = ngmm
3165+
prm = np.zeros((ngmm, 3))
31163166

3117-
self.integ.mm_param = np.zeros((ngmm, 3))
3118-
3119-
for k in range(ngmm):
3167+
for j in range(ngmm):
31203168
# GMM component probability DFi211
31213169
# GMM component expectation DFi212
31223170
# GMM component standard deviation DFi213
@@ -3126,33 +3174,34 @@ def decode_integrity_mmap(self, msg, i):
31263174
prob *= 0.0625
31273175
exp = self.integ.mu_k_t[exp_]
31283176
std = self.integ.sig_k_t[std_]
3129-
self.integ.mm_param[k, :] = [prob, exp, std]
3177+
prm[j, :] = [prob, exp, std]
3178+
3179+
self.integ.mm_param[k] = prm
31303180

31313181
elif mm_id == 1 or mm_id == 2:
3132-
self.integ.mm_param = {}
3133-
n = 3 if mm_id == 1 else 4
3134-
3135-
for k in range(narea):
3136-
sig_t, nsig = self.decode_mask(sigmask[k], 8, ofst=0)
3137-
prm = np.zeros((nsig, n))
3138-
3139-
for j in range(nsig):
3140-
# Multipath parameter a DFi215
3141-
# Multipath parameter b DFi216
3142-
# Multipath parameter c DFi217
3143-
a, b, c = bs.unpack_from('u4s5s5', msg, i)
3144-
i += 14
3182+
# mm_id=1: Mats Brenner Model Data
3183+
sig_t, nsig = self.decode_mask(sigmask[k], 8, ofst=0)
3184+
self.integ.np[k] = nsig
3185+
prm = np.zeros((nsig, n))
3186+
3187+
for j in range(nsig):
3188+
# Multipath parameter a DFi215
3189+
# Multipath parameter b DFi216
3190+
# Multipath parameter c DFi217
3191+
a, b, c = bs.unpack_from('u4s5s5', msg, i)
3192+
i += 14
31453193

3146-
a = self.integ.sig_k_t[a]
3147-
prm[j, 0:3] = [a, b*0.25, c*0.0625]
3194+
prm[j, 0] = sig_t[j]
3195+
a = self.integ.sig_k_t[a]
3196+
prm[j, 1:4] = [a, b*0.25, c*0.0625]
31483197

3149-
if mm_id == 2:
3150-
# Multipath parameter d DFi218
3151-
d = bs.unpack_from('u8', msg, i)[0]
3152-
i += 8
3153-
prm[j, 3] = d*0.3515625
3198+
if mm_id == 2:
3199+
# Multipath parameter d DFi218
3200+
d = bs.unpack_from('u8', msg, i)[0]
3201+
i += 8
3202+
prm[j, 4] = d*0.3515625
31543203

3155-
self.integ.mm_param[k] = prm
3204+
self.integ.mm_param[k] = prm
31563205

31573206
return i
31583207

@@ -3201,6 +3250,19 @@ def decode_integrity_ssr(self, msg, i):
32013250
self.integ.iod_sys = iod_sys
32023251
self.integ.flag = flag_t
32033252

3253+
def decode_sc134_test_header(self, msg, i):
3254+
# MT51-56 has subtype to extent message type space
3255+
# DFi028 uint8 as in Table 9-35
3256+
3257+
# Message Type DF002 uint12
3258+
# Working Group DFi020 uint4
3259+
# Sub-Message DFi028 uint8
3260+
# Message Revision DFi00x uint4 => uint8?
3261+
wg, self.subtype, self.ver = bs.unpack_from('u4u8u8', msg, i)
3262+
self.wg = wg-1
3263+
i += 20
3264+
return i
3265+
32043266
def decode(self, msg, subtype=None, scanmode=False):
32053267
""" decode RTCM messages """
32063268
i = 24
@@ -3370,12 +3432,13 @@ def decode(self, msg, subtype=None, scanmode=False):
33703432
self.subtype = sRTCM.INTEG_SSR_TROP
33713433
self.decode_integrity_ssr(msg, i)
33723434
elif self.msgtype == 54: # SSR integrity test msg
3435+
i = self.decode_sc134_test_header(msg, i)
33733436
if self.subtype == 9:
33743437
self.decode_integrity_vmap(msg, i)
3375-
elif self.subtype == 10:
3376-
self.decode_integrity_mmap(msg, i)
3377-
else:
3378-
self.subtype = -1
3438+
elif self.subtype == 10:
3439+
self.decode_integrity_mmap(msg, i)
3440+
else:
3441+
self.subtype = -1
33793442

33803443
if self.monlevel > 0 and self.fh is not None:
33813444
self.out_log(obs, eph, geph, seph)

0 commit comments

Comments
 (0)