Changeset 11127 in ntrip for trunk/BNC/tests/tools
- Timestamp:
- Oct 24, 2026, 11:15:22 PM (3 weeks ago)
- Location:
- trunk/BNC/tests/tools
- Files:
-
- 1 added
- 3 edited
-
bncraw.py (modified) (4 diffs)
-
fakecaster.py (modified) (7 diffs)
-
ssl_check.py (added)
-
ssr_harness.cpp (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/BNC/tests/tools/bncraw.py
r11069 r11127 9 9 info <raw> [<raw> ...] streams, byte counts, time span, RTCM3 message types 10 10 slice <raw> <out> [options] cut a time window / subset of stations 11 crs <raw> decode RTCM 1300/1301/1302 (CRS messages) 11 12 """ 12 13 … … 110 111 return "4076_%03d" % sub 111 112 return str(t) 113 114 115 class BitReader: 116 def __init__(self, data): 117 self.data = data 118 self.pos = 0 119 120 def u(self, n): 121 v = 0 122 for _ in range(n): 123 if self.pos >= 8 * len(self.data): 124 raise ValueError("message too short") 125 v = (v << 1) | ((self.data[self.pos // 8] >> (7 - self.pos % 8)) & 1) 126 self.pos += 1 127 return v 128 129 def s(self, n): 130 v = self.u(n) 131 return v - (1 << n) if v >> (n - 1) else v 132 133 def text(self): 134 n = self.u(5) 135 return bytes(self.u(8) for _ in range(n)).decode("latin-1") 136 137 def left(self): 138 return 8 * len(self.data) - self.pos 139 140 141 def decode_crs(p): 142 """One line per 1300/1301/1302 message (RTCM 10403.4, DF562-DF587).""" 143 r = BitReader(p) 144 t = r.u(12) 145 if t == 1300: 146 out = "1300 name %r CE %.2f" % (r.text(), r.u(16) / 100.0) 147 elif t == 1301: 148 src, tgt = r.text(), r.text() 149 out = "1301 source %r target %r sys %d util %d mjd %d par" % ( 150 src, tgt, r.u(8), r.u(10), r.u(16) + 44244) 151 for n in (23, 23, 23, 32, 32, 32, 25, 17, 17, 17, 17, 17, 17, 14): 152 out += " %d" % r.s(n) 153 else: 154 out = "1302 name %r anchor %d plate %d links" % (r.text(), r.u(1), r.u(5)) 155 out += " %r" % [r.text() for _ in range(r.u(3))] 156 if r.left() >= 8: # more than the padding to the byte boundary 157 out += " TRAILING %d bits" % r.left() 158 return out 112 159 113 160 … … 166 213 167 214 215 def cmd_crs(args): 216 """Distinct CRS message contents per stream, with their number.""" 217 stats = collections.OrderedDict() 218 framers = {} 219 for t, sta, fmt, data in read_chunks(args.input): 220 for p in framers.setdefault(sta, Rtcm3Framer()).feed(data): 221 if msg_type(p) in ("1300", "1301", "1302"): 222 try: 223 line = decode_crs(p) 224 except ValueError as e: 225 line = "%s %s" % (msg_type(p), e) 226 key = (sta, line) 227 stats[key] = stats.get(key, 0) + 1 228 for (sta, line), n in stats.items(): 229 print("%s %4dx %s" % (sta, n, line)) 230 231 168 232 def main(): 169 233 ap = argparse.ArgumentParser(description=__doc__, … … 183 247 p.set_defaults(func=cmd_slice) 184 248 249 p = sub.add_parser("crs", help="decode CRS messages 1300/1301/1302") 250 p.add_argument("input") 251 p.set_defaults(func=cmd_crs) 252 185 253 args = ap.parse_args() 186 254 args.func(args) -
trunk/BNC/tests/tools/fakecaster.py
r11074 r11127 11 11 12 12 fakecaster.py --port 21011 --out upload.raw --sta CLOCK --time 2026-10-01T09:30:00 13 14 With --tls-cert/--tls-key the caster speaks TLS (Ntrip 2s); --tls-client-ca 15 additionally requires a client certificate signed by that CA. With --serve 16 a GET request (BNC as Ntrip client) is answered with the RTCM bytes of the 17 given raw file. Failed TLS handshakes are reported on stdout. 13 18 """ 14 19 15 20 import argparse 21 import os 16 22 import socket 23 import ssl 17 24 import sys 18 25 import threading 26 27 sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) 19 28 20 29 … … 45 54 self.head = "%s %s %s" % (time, sta, fmt) 46 55 self.total = 0 56 self.handshakes = 0 57 self.served = 0 47 58 48 59 def write(self, data): … … 56 67 57 68 58 def serve(conn, store, timeout): 69 def serve(conn, store, timeout, tls=None, served=None): 59 70 conn.settimeout(timeout) 60 71 buf = b"" 61 72 try: 73 if tls: 74 try: 75 conn = tls.wrap_socket(conn, server_side=True) 76 except (ssl.SSLError, OSError) as e: 77 print("tls handshake failed: %s" % e, flush=True) 78 return 79 store.handshakes += 1 62 80 while b"\r\n\r\n" not in buf: 63 81 d = conn.recv(4096) … … 67 85 header, _, pending = buf.partition(b"\r\n\r\n") 68 86 chunked = b"chunked" in header.lower() 87 if header.startswith(b"GET") and served is not None: 88 conn.sendall(b"HTTP/1.1 200 OK\r\nNtrip-Version: Ntrip/2.0\r\n" 89 b"Content-Type: gnss/data\r\nConnection: close\r\n\r\n" + served) 90 store.served += len(served) 91 return 69 92 if header.startswith(b"SOURCE"): 70 93 conn.sendall(b"ICY 200 OK\r\n\r\n") … … 100 123 ap.add_argument("--timeout", type=float, default=600.0, 101 124 help="stop after this many seconds without a new connection or data") 125 ap.add_argument("--tls-cert", help="server certificate (PEM): TLS") 126 ap.add_argument("--tls-key", help="server private key (PEM)") 127 ap.add_argument("--tls-client-ca", help="require a client certificate of this CA") 128 ap.add_argument("--serve", help="raw file whose RTCM bytes are sent on GET") 102 129 args = ap.parse_args() 130 131 tls = None 132 if args.tls_cert: 133 tls = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) 134 tls.load_cert_chain(args.tls_cert, args.tls_key) 135 if args.tls_client_ca: 136 tls.verify_mode = ssl.CERT_REQUIRED 137 tls.load_verify_locations(args.tls_client_ca) 138 served = None 139 if args.serve: 140 from bncraw import read_chunks # noqa: E402 141 served = b"".join(d for t, sta, fmt, d in read_chunks(args.serve)) 103 142 104 143 store = Store(args.out, args.sta, args.format, args.time) … … 116 155 except socket.timeout: 117 156 break 118 t = threading.Thread(target=serve, args=(conn, store, args.timeout), daemon=True) 157 t = threading.Thread(target=serve, args=(conn, store, args.timeout, tls, served), 158 daemon=True) 119 159 t.start() 120 160 threads.append(t) … … 122 162 t.join(args.timeout) 123 163 print("received %d bytes in %d connection(s)" % (store.total, len(threads)), flush=True) 164 if tls: 165 print("%d TLS handshake(s) completed" % store.handshakes, flush=True) 166 if served is not None: 167 print("served %d bytes" % store.served, flush=True) 124 168 return 0 125 169 -
trunk/BNC/tests/tools/ssr_harness.cpp
r11125 r11127 6 6 // struct is a separate heap block here, so a write beyond a struct is 7 7 // reported by AddressSanitizer instead of silently corrupting the next member. 8 // 9 // Every decoded VTEC (all formats) and metadata message (new RTCM-SSR) is 10 // encoded again with the Make* function of the same class and decoded once 11 // more; the result must be identical (exit code 1 otherwise). 8 12 // 9 13 // Usage: ssr_harness <file> (BNC raw file or plain RTCM3 byte stream) … … 33 37 } 34 38 39 struct t_target { 40 t_target() : co(new SsrCorr::ClockOrbit()), cb(new SsrCorr::CodeBias()), 41 v(new SsrCorr::VTEC()), pb(new SsrCorr::PhaseBias()), 42 md(new SsrCorr::MetaData()), a(new SsrCorr::Antenna()) {} 43 ~t_target() {delete co; delete cb; delete v; delete pb; delete md; delete a;} 44 int decode(SsrCorr* ssr, const unsigned char* frame, size_t size) { 45 int bytesused = 0; 46 return ssr->GetSSR(co, cb, v, pb, md, a, (const char*)frame, size, &bytesused); 47 } 48 SsrCorr::ClockOrbit* co; 49 SsrCorr::CodeBias* cb; 50 SsrCorr::VTEC* v; 51 SsrCorr::PhaseBias* pb; 52 SsrCorr::MetaData* md; 53 SsrCorr::Antenna* a; 54 }; 55 56 static bool sameVTEC(const SsrCorr::VTEC* v1, const SsrCorr::VTEC* v2) { 57 if (v1->EpochTime != v2->EpochTime || v1->NumLayers != v2->NumLayers || 58 v1->Quality != v2->Quality) { 59 return false; 60 } 61 for (unsigned l = 0; l < v1->NumLayers; l++) { 62 const SsrCorr::VTEC::IonoLayers& l1 = v1->Layers[l]; 63 const SsrCorr::VTEC::IonoLayers& l2 = v2->Layers[l]; 64 if (l1.Height != l2.Height || l1.Degree != l2.Degree || l1.Order != l2.Order) { 65 return false; 66 } 67 for (unsigned d = 0; d <= l1.Degree; d++) { 68 for (unsigned o = 0; o <= l1.Order; o++) { 69 if (l1.Cosinus[d][o] != l2.Cosinus[d][o] || l1.Sinus[d][o] != l2.Sinus[d][o]) { 70 return false; 71 } 72 } 73 } 74 } 75 return true; 76 } 77 78 static bool sameMetaData(const SsrCorr::MetaData* m1, const SsrCorr::MetaData* m2) { 79 if (m1->SSRIOD != m2->SSRIOD || m1->SSRProviderID != m2->SSRProviderID || 80 m1->SSRSolutionID != m2->SSRSolutionID || m1->NumEntries != m2->NumEntries) { 81 return false; 82 } 83 for (unsigned i = 0; i < m1->NumEntries; i++) { 84 if (memcmp(&m1->Entries[i], &m2->Entries[i], sizeof(m1->Entries[i])) != 0) { 85 return false; 86 } 87 } 88 return true; 89 } 90 91 static int nFailed = 0; 92 93 // Decode, print a summary, encode the result again and compare 35 94 static void decode(const char* name, SsrCorr* ssr, const unsigned char* frame, size_t size) { 36 SsrCorr::ClockOrbit* co = new SsrCorr::ClockOrbit(); 37 SsrCorr::CodeBias* cb = new SsrCorr::CodeBias(); 38 SsrCorr::VTEC* v = new SsrCorr::VTEC(); 39 SsrCorr::PhaseBias* pb = new SsrCorr::PhaseBias(); 40 SsrCorr::MetaData* md = new SsrCorr::MetaData(); 41 SsrCorr::Antenna* a = new SsrCorr::Antenna(); 42 int bytesused = 0; 43 44 int irc = ssr->GetSSR(co, cb, v, pb, md, a, (const char*)frame, size, &bytesused); 95 t_target t1; 96 int irc = t1.decode(ssr, frame, size); 45 97 printf(" %-8s irc %3d", name, irc); 46 if (irc >= 0 && v->NumLayers) { 98 if (irc >= 0 && t1.v->NumLayers) { 47 99 double sum = 0.0; 48 100 printf(" VTEC"); 49 for (unsigned l = 0; l < v->NumLayers; l++) { 50 const SsrCorr::VTEC::IonoLayers& layer = v->Layers[l]; 101 for (unsigned l = 0; l < t1.v->NumLayers; l++) { 102 const SsrCorr::VTEC::IonoLayers& layer = t1.v->Layers[l]; 51 103 printf(" %u/%u", layer.Degree, layer.Order); 52 104 for (unsigned d = 0; d <= layer.Degree; d++) { … … 57 109 } 58 110 printf(" sum(C-S) %.3f", sum); 111 112 char buffer[CLOCKORBIT_BUFFERSIZE]; 113 size_t len = ssr->MakeVTEC(t1.v, 0, buffer, sizeof(buffer)); 114 t_target t2; 115 bool ok = len > 0 && t2.decode(ssr, (const unsigned char*)buffer, len) >= 0 && sameVTEC(t1.v, t2.v); 116 printf(" re-encoded: %s", ok ? "identical" : "DIFFERENT"); 117 nFailed += !ok; 59 118 } 60 if (irc >= 0 && md->NumEntries) { 61 printf(" META %u entries, additional data", md->NumEntries); 62 for (unsigned i = 0; i < md->NumEntries; i++) { 63 if (md->Entries[i].additionalParameterIndicator) { 64 printf(" %u:0x%x", md->Entries[i].additionalParameterNumBits, 65 md->Entries[i].additionalParameterData); 119 if (irc >= 0 && t1.md->NumEntries) { 120 printf(" META %u entries, additional data", t1.md->NumEntries); 121 for (unsigned i = 0; i < t1.md->NumEntries; i++) { 122 if (t1.md->Entries[i].additionalParameterIndicator) { 123 printf(" %u:0x%x", t1.md->Entries[i].additionalParameterNumBits, 124 t1.md->Entries[i].additionalParameterData); 66 125 } 126 } 127 SsrCorrRtcmNew* rtcmNew = dynamic_cast<SsrCorrRtcmNew*>(ssr); 128 if (rtcmNew) { 129 char buffer[CLOCKORBIT_BUFFERSIZE]; 130 size_t len = rtcmNew->MakeMetaData(t1.md, buffer, sizeof(buffer)); 131 t_target t2; 132 bool ok = len > 0 && t2.decode(ssr, (const unsigned char*)buffer, len) >= 0 && sameMetaData(t1.md, t2.md); 133 printf(" re-encoded: %s", ok ? "identical" : "DIFFERENT"); 134 nFailed += !ok; 67 135 } 68 136 } 69 137 printf("\n"); 70 71 delete co; delete cb; delete v; delete pb; delete md; delete a;72 138 } 73 139 … … 111 177 ii += len + 6; 112 178 } 179 if (nFailed) { 180 printf("%d decoded messages differ after re-encoding\n", nFailed); 181 return 1; 182 } 113 183 return 0; 114 184 }
Note:
See TracChangeset
for help on using the changeset viewer.
