1use crate::error::{Error, Result};
2use crate::rtcm::bits::{BitReader, BitWriter};
3use crate::rtcm::crc::crc24q_bits;
4
5pub type SbasMessageType = u8;
6
7const FRAMED_LEN: usize = 32;
8const BODY_LEN: usize = 29;
9const HEADER_BITS: usize = 14;
10const DATA_BITS: usize = 212;
11const BODY_BITS: usize = HEADER_BITS + DATA_BITS;
12const CRC_BITS: usize = 24;
13const FRAMED_BITS: usize = BODY_BITS + CRC_BITS;
14const PREAMBLES: [u8; 3] = [0x53, 0x9A, 0xC6];
15
16#[derive(Clone, Debug, PartialEq, Eq, Default)]
17pub struct SpareBits(pub Vec<(u64, u8)>);
18
19impl SpareBits {
20 pub fn new() -> Self {
21 Self(Vec::new())
22 }
23
24 pub fn push(&mut self, value: u64, width: u8) {
25 self.0.push((value, width));
26 }
27}
28
29#[derive(Clone, Debug, PartialEq, Eq)]
30pub enum SbasMessage {
31 DoNotUse(SbasDoNotUse),
32 PrnMask(SbasPrnMask),
33 FastCorrections(SbasFastCorrections),
34 Integrity(SbasIntegrity),
35 FastDegradation(SbasFastDegradation),
36 GeoNav(SbasGeoNav),
37 NetworkTime(SbasNetworkTime),
38 GeoAlmanac(SbasGeoAlmanac),
39 IgpMask(SbasIgpMask),
40 MixedCorrections(SbasMixedCorrections),
41 LongTermCorrections(SbasLongTermCorrections),
42 IonoDelays(SbasIonoDelays),
43 Unsupported(SbasUnsupported),
44}
45
46#[derive(Clone, Debug, PartialEq, Eq)]
47pub struct SbasDoNotUse {
48 pub preamble: u8,
49 pub data: Vec<u8>,
50}
51
52#[derive(Clone, Debug, PartialEq, Eq)]
53pub struct SbasPrnMask {
54 pub preamble: u8,
55 pub iodp: u8,
56 pub mask: [bool; 210],
57 pub reserved: SpareBits,
58}
59
60#[derive(Clone, Debug, PartialEq, Eq)]
61pub struct SbasFastCorrections {
62 pub preamble: u8,
63 pub message_type: SbasMessageType,
64 pub iodf: u8,
65 pub iodp: u8,
66 pub prc: [i16; 13],
67 pub udrei: [u8; 13],
68 pub reserved: SpareBits,
69}
70
71#[derive(Clone, Debug, PartialEq, Eq)]
72pub struct SbasIntegrity {
73 pub preamble: u8,
74 pub iodf: [u8; 4],
75 pub udrei: [u8; 51],
76 pub reserved: SpareBits,
77}
78
79#[derive(Clone, Debug, PartialEq, Eq)]
80pub struct SbasFastDegradation {
81 pub preamble: u8,
82 pub system_latency_s: u8,
83 pub iodp: u8,
84 pub ai: [u8; 51],
85 pub reserved: SpareBits,
86}
87
88#[derive(Clone, Debug, PartialEq, Eq)]
89pub struct SbasGeoNav {
90 pub preamble: u8,
91 pub time_of_day_s: u16,
92 pub ura: u8,
93 pub x_m: i32,
94 pub y_m: i32,
95 pub z_m: i32,
96 pub x_rate_m_s: i32,
97 pub y_rate_m_s: i32,
98 pub z_rate_m_s: i32,
99 pub x_accel_m_s2: i16,
100 pub y_accel_m_s2: i16,
101 pub z_accel_m_s2: i16,
102 pub a_gf0_s: i16,
103 pub a_gf1_s_s: i16,
104 pub reserved: SpareBits,
105}
106
107#[derive(Clone, Debug, PartialEq, Eq)]
108pub struct SbasNetworkTime {
109 pub preamble: u8,
110 pub data: Vec<u8>,
111}
112
113#[derive(Clone, Debug, PartialEq, Eq)]
114pub struct SbasGeoAlmanac {
115 pub preamble: u8,
116 pub data: Vec<u8>,
117}
118
119#[derive(Clone, Debug, PartialEq, Eq)]
120pub struct SbasMixedCorrections {
121 pub preamble: u8,
122 pub fast: SbasMixedFastCorrections,
123 pub long_term: SbasLongTermHalf,
124}
125
126#[derive(Clone, Debug, PartialEq, Eq)]
127pub struct SbasMixedFastCorrections {
128 pub iodf: u8,
129 pub iodp: u8,
130 pub block_id: u8,
131 pub prc: [i16; 6],
132 pub udrei: [u8; 6],
133 pub reserved: SpareBits,
134}
135
136#[derive(Clone, Debug, PartialEq, Eq)]
137pub struct SbasLongTermCorrections {
138 pub preamble: u8,
139 pub halves: [SbasLongTermHalf; 2],
140}
141
142#[derive(Clone, Debug, PartialEq, Eq)]
143pub struct SbasLongTermHalf {
144 pub velocity_code: bool,
145 pub iodp: u8,
146 pub records: Vec<SbasLongTermRecord>,
147 pub reserved: SpareBits,
148}
149
150#[derive(Clone, Debug, PartialEq, Eq)]
151pub struct SbasLongTermRecord {
152 pub monitored_index: u8,
153 pub iode: u8,
154 pub delta_x: i32,
155 pub delta_y: i32,
156 pub delta_z: i32,
157 pub delta_x_rate: i32,
158 pub delta_y_rate: i32,
159 pub delta_z_rate: i32,
160 pub delta_a_f0: i32,
161 pub delta_a_f1: i32,
162 pub time_of_day_s: Option<u32>,
163}
164
165#[derive(Clone, Debug, PartialEq, Eq)]
166pub struct SbasIgpMask {
167 pub preamble: u8,
168 pub band_number: u8,
169 pub iodi: u8,
170 pub mask: [bool; 201],
171 pub reserved: SpareBits,
172}
173
174#[derive(Clone, Debug, PartialEq, Eq)]
175pub struct SbasIonoDelays {
176 pub preamble: u8,
177 pub band_number: u8,
178 pub block_id: u8,
179 pub iodi: u8,
180 pub entries: [SbasIgpDelay; 15],
181 pub reserved: SpareBits,
182}
183
184#[derive(Clone, Debug, PartialEq, Eq, Default)]
185pub struct SbasIgpDelay {
186 pub vertical_delay: u16,
187 pub givei: u8,
188}
189
190#[derive(Clone, Debug, PartialEq, Eq)]
191pub struct SbasUnsupported {
192 pub preamble: u8,
193 pub message_type: SbasMessageType,
194 pub data: Vec<u8>,
195}
196
197#[derive(Clone, Copy, Debug, PartialEq, Eq)]
198pub enum SbasWireForm {
199 Framed250,
200 Body226,
201}
202
203#[derive(Clone, Debug, PartialEq, Eq)]
204pub struct SbasBlock {
205 pub form: SbasWireForm,
206 pub message: SbasMessage,
207}
208
209struct CountedBitWriter {
210 writer: BitWriter,
211 nbits: usize,
212}
213
214impl CountedBitWriter {
215 fn new() -> Self {
216 Self {
217 writer: BitWriter::new(),
218 nbits: 0,
219 }
220 }
221
222 fn push_u(&mut self, value: u64, width: usize) {
223 self.writer.push_u(value, width);
224 self.nbits += width;
225 }
226
227 fn push_i(&mut self, value: i64, width: usize) {
228 self.writer.push_i(value, width);
229 self.nbits += width;
230 }
231
232 fn push_flag(&mut self, value: bool) {
233 self.writer.push_flag(value);
234 self.nbits += 1;
235 }
236
237 fn push_bits_from(&mut self, bytes: &[u8], nbits: usize) {
238 for bit_pos in 0..nbits {
239 self.push_u(u64::from(bit_at(bytes, bit_pos)), 1);
240 }
241 }
242
243 fn pad_to(&mut self, nbits: usize) {
244 while self.nbits < nbits {
245 self.push_u(0, 1);
246 }
247 }
248
249 fn into_bytes(self) -> Vec<u8> {
250 self.writer.into_bytes()
251 }
252}
253
254impl SbasBlock {
255 pub fn decode(bytes: &[u8], form: SbasWireForm) -> Result<Self> {
256 match form {
257 SbasWireForm::Framed250 => {
258 if bytes.len() != FRAMED_LEN {
259 return Err(parse_error("SBAS framed block must be 32 bytes"));
260 }
261 let got = bits_as_u32(bytes, BODY_BITS, CRC_BITS);
262 let want = crc24q_bits(bytes, BODY_BITS);
263 if got != want {
264 return Err(parse_error("SBAS CRC mismatch"));
265 }
266 }
267 SbasWireForm::Body226 => {
268 if bytes.len() != BODY_LEN {
269 return Err(parse_error("SBAS body block must be 29 bytes"));
270 }
271 }
272 }
273
274 let mut reader = BitReader::new(bytes);
275 let preamble = reader.u(8)? as u8;
276 if !PREAMBLES.contains(&preamble) {
277 return Err(parse_error("SBAS preamble not recognized"));
278 }
279 let message_type = reader.u(6)? as u8;
280 let data = read_bits_as_bytes(&mut reader, DATA_BITS)?;
281 let message = decode_message(preamble, message_type, &data)?;
282 Ok(Self { form, message })
283 }
284
285 pub fn encode(&self) -> Vec<u8> {
286 let mut body = CountedBitWriter::new();
287 body.push_u(u64::from(self.message.preamble()), 8);
288 body.push_u(u64::from(self.message.message_type()), 6);
289 let data = self.message.encode_data();
290 body.push_bits_from(&data, DATA_BITS);
291 body.pad_to(BODY_BITS);
292 let body_bytes = body.into_bytes();
293
294 match self.form {
295 SbasWireForm::Body226 => body_bytes,
296 SbasWireForm::Framed250 => {
297 let crc = crc24q_bits(&body_bytes, BODY_BITS);
298 let mut framed = CountedBitWriter::new();
299 framed.push_bits_from(&body_bytes, BODY_BITS);
300 framed.push_u(u64::from(crc), CRC_BITS);
301 framed.pad_to(FRAMED_BITS);
302 framed.into_bytes()
303 }
304 }
305 }
306}
307
308impl SbasMessage {
309 pub fn message_type(&self) -> SbasMessageType {
310 match self {
311 Self::DoNotUse(_) => 0,
312 Self::PrnMask(_) => 1,
313 Self::FastCorrections(m) => m.message_type,
314 Self::Integrity(_) => 6,
315 Self::FastDegradation(_) => 7,
316 Self::GeoNav(_) => 9,
317 Self::NetworkTime(_) => 12,
318 Self::GeoAlmanac(_) => 17,
319 Self::IgpMask(_) => 18,
320 Self::MixedCorrections(_) => 24,
321 Self::LongTermCorrections(_) => 25,
322 Self::IonoDelays(_) => 26,
323 Self::Unsupported(m) => m.message_type,
324 }
325 }
326
327 fn preamble(&self) -> u8 {
328 match self {
329 Self::DoNotUse(m) => m.preamble,
330 Self::PrnMask(m) => m.preamble,
331 Self::FastCorrections(m) => m.preamble,
332 Self::Integrity(m) => m.preamble,
333 Self::FastDegradation(m) => m.preamble,
334 Self::GeoNav(m) => m.preamble,
335 Self::NetworkTime(m) => m.preamble,
336 Self::GeoAlmanac(m) => m.preamble,
337 Self::IgpMask(m) => m.preamble,
338 Self::MixedCorrections(m) => m.preamble,
339 Self::LongTermCorrections(m) => m.preamble,
340 Self::IonoDelays(m) => m.preamble,
341 Self::Unsupported(m) => m.preamble,
342 }
343 }
344
345 fn encode_data(&self) -> Vec<u8> {
346 match self {
347 Self::DoNotUse(m) => data_from_raw(&m.data),
348 Self::PrnMask(m) => encode_prn_mask(m),
349 Self::FastCorrections(m) => encode_fast(m),
350 Self::Integrity(m) => encode_integrity(m),
351 Self::FastDegradation(m) => encode_fast_degradation(m),
352 Self::GeoNav(m) => encode_geo_nav(m),
353 Self::NetworkTime(m) => data_from_raw(&m.data),
354 Self::GeoAlmanac(m) => data_from_raw(&m.data),
355 Self::IgpMask(m) => encode_igp_mask(m),
356 Self::MixedCorrections(m) => encode_mixed(m),
357 Self::LongTermCorrections(m) => encode_long_term(m),
358 Self::IonoDelays(m) => encode_iono_delays(m),
359 Self::Unsupported(m) => data_from_raw(&m.data),
360 }
361 }
362}
363
364pub(crate) fn is_phase_a_message(mt: SbasMessageType) -> bool {
365 matches!(mt, 0..=7 | 9 | 12 | 17 | 18 | 24 | 25 | 26)
366}
367
368fn decode_message(preamble: u8, message_type: u8, data: &[u8]) -> Result<SbasMessage> {
369 if !is_phase_a_message(message_type) {
370 return Ok(SbasMessage::Unsupported(SbasUnsupported {
371 preamble,
372 message_type,
373 data: data_from_raw(data),
374 }));
375 }
376 match message_type {
377 0 => Ok(SbasMessage::DoNotUse(SbasDoNotUse {
378 preamble,
379 data: data_from_raw(data),
380 })),
381 1 => decode_prn_mask(preamble, data).map(SbasMessage::PrnMask),
382 2..=5 => decode_fast(preamble, message_type, data).map(SbasMessage::FastCorrections),
383 6 => decode_integrity(preamble, data).map(SbasMessage::Integrity),
384 7 => decode_fast_degradation(preamble, data).map(SbasMessage::FastDegradation),
385 9 => decode_geo_nav(preamble, data).map(SbasMessage::GeoNav),
386 12 => Ok(SbasMessage::NetworkTime(SbasNetworkTime {
387 preamble,
388 data: data_from_raw(data),
389 })),
390 17 => Ok(SbasMessage::GeoAlmanac(SbasGeoAlmanac {
391 preamble,
392 data: data_from_raw(data),
393 })),
394 18 => decode_igp_mask(preamble, data).map(SbasMessage::IgpMask),
395 24 => decode_mixed(preamble, data).map(SbasMessage::MixedCorrections),
396 25 => decode_long_term(preamble, data).map(SbasMessage::LongTermCorrections),
397 26 => decode_iono_delays(preamble, data).map(SbasMessage::IonoDelays),
398 _ => unreachable!("phase A classification and decode match are out of sync"),
399 }
400}
401
402fn decode_prn_mask(preamble: u8, data: &[u8]) -> Result<SbasPrnMask> {
403 let mut r = BitReader::new(data);
404 let mut mask = [false; 210];
405 for bit in &mut mask {
406 *bit = r.flag()?;
407 }
408 let iodp = r.u(2)? as u8;
409 Ok(SbasPrnMask {
410 preamble,
411 iodp,
412 mask,
413 reserved: SpareBits::new(),
414 })
415}
416
417fn encode_prn_mask(m: &SbasPrnMask) -> Vec<u8> {
418 let mut w = CountedBitWriter::new();
419 for bit in m.mask {
420 w.push_flag(bit);
421 }
422 w.push_u(u64::from(m.iodp), 2);
423 w.pad_to(DATA_BITS);
424 w.into_bytes()
425}
426
427fn decode_fast(preamble: u8, message_type: u8, data: &[u8]) -> Result<SbasFastCorrections> {
428 let mut r = BitReader::new(data);
429 let iodf = r.u(2)? as u8;
430 let iodp = r.u(2)? as u8;
431 let mut prc = [0i16; 13];
432 for value in &mut prc {
433 *value = r.i(12)? as i16;
434 }
435 let mut udrei = [0u8; 13];
436 for value in &mut udrei {
437 *value = r.u(4)? as u8;
438 }
439 Ok(SbasFastCorrections {
440 preamble,
441 message_type,
442 iodf,
443 iodp,
444 prc,
445 udrei,
446 reserved: SpareBits::new(),
447 })
448}
449
450fn encode_fast(m: &SbasFastCorrections) -> Vec<u8> {
451 let mut w = CountedBitWriter::new();
452 w.push_u(u64::from(m.iodf), 2);
453 w.push_u(u64::from(m.iodp), 2);
454 for value in m.prc {
455 w.push_i(i64::from(value), 12);
456 }
457 for value in m.udrei {
458 w.push_u(u64::from(value), 4);
459 }
460 w.pad_to(DATA_BITS);
461 w.into_bytes()
462}
463
464fn decode_integrity(preamble: u8, data: &[u8]) -> Result<SbasIntegrity> {
465 let mut r = BitReader::new(data);
466 let mut iodf = [0u8; 4];
467 for value in &mut iodf {
468 *value = r.u(2)? as u8;
469 }
470 let mut udrei = [0u8; 51];
471 for value in &mut udrei {
472 *value = r.u(4)? as u8;
473 }
474 Ok(SbasIntegrity {
475 preamble,
476 iodf,
477 udrei,
478 reserved: SpareBits::new(),
479 })
480}
481
482fn encode_integrity(m: &SbasIntegrity) -> Vec<u8> {
483 let mut w = CountedBitWriter::new();
484 for value in m.iodf {
485 w.push_u(u64::from(value), 2);
486 }
487 for value in m.udrei {
488 w.push_u(u64::from(value), 4);
489 }
490 w.pad_to(DATA_BITS);
491 w.into_bytes()
492}
493
494fn decode_fast_degradation(preamble: u8, data: &[u8]) -> Result<SbasFastDegradation> {
495 let mut r = BitReader::new(data);
496 let system_latency_s = r.u(4)? as u8;
497 let iodp = r.u(2)? as u8;
498 let mut ai = [0u8; 51];
499 for value in &mut ai {
500 *value = r.u(4)? as u8;
501 }
502 let mut reserved = SpareBits::new();
503 if r.remaining_bits() >= 2 {
504 reserved.push(r.u(2)?, 2);
505 }
506 Ok(SbasFastDegradation {
507 preamble,
508 system_latency_s,
509 iodp,
510 ai,
511 reserved,
512 })
513}
514
515fn encode_fast_degradation(m: &SbasFastDegradation) -> Vec<u8> {
516 let mut w = CountedBitWriter::new();
517 w.push_u(u64::from(m.system_latency_s), 4);
518 w.push_u(u64::from(m.iodp), 2);
519 for value in m.ai {
520 w.push_u(u64::from(value), 4);
521 }
522 push_spares(&mut w, &m.reserved);
523 w.pad_to(DATA_BITS);
524 w.into_bytes()
525}
526
527fn decode_geo_nav(preamble: u8, data: &[u8]) -> Result<SbasGeoNav> {
528 let mut r = BitReader::new(data);
529 let mut reserved = SpareBits::new();
530 reserved.push(r.u(8)?, 8);
531 let time_of_day_s = r.u(13)? as u16;
532 let ura = r.u(4)? as u8;
533 let x_m = r.i(30)? as i32;
534 let y_m = r.i(30)? as i32;
535 let z_m = r.i(25)? as i32;
536 let x_rate_m_s = r.i(17)? as i32;
537 let y_rate_m_s = r.i(17)? as i32;
538 let z_rate_m_s = r.i(18)? as i32;
539 let x_accel_m_s2 = r.i(10)? as i16;
540 let y_accel_m_s2 = r.i(10)? as i16;
541 let z_accel_m_s2 = r.i(10)? as i16;
542 let a_gf0_s = r.i(12)? as i16;
543 let a_gf1_s_s = r.i(8)? as i16;
544 Ok(SbasGeoNav {
545 preamble,
546 time_of_day_s,
547 ura,
548 x_m,
549 y_m,
550 z_m,
551 x_rate_m_s,
552 y_rate_m_s,
553 z_rate_m_s,
554 x_accel_m_s2,
555 y_accel_m_s2,
556 z_accel_m_s2,
557 a_gf0_s,
558 a_gf1_s_s,
559 reserved,
560 })
561}
562
563fn encode_geo_nav(m: &SbasGeoNav) -> Vec<u8> {
564 let mut w = CountedBitWriter::new();
565 let spare_start = push_prefix_spare(&mut w, &m.reserved, 8);
566 w.push_u(u64::from(m.time_of_day_s), 13);
567 w.push_u(u64::from(m.ura), 4);
568 w.push_i(i64::from(m.x_m), 30);
569 w.push_i(i64::from(m.y_m), 30);
570 w.push_i(i64::from(m.z_m), 25);
571 w.push_i(i64::from(m.x_rate_m_s), 17);
572 w.push_i(i64::from(m.y_rate_m_s), 17);
573 w.push_i(i64::from(m.z_rate_m_s), 18);
574 w.push_i(i64::from(m.x_accel_m_s2), 10);
575 w.push_i(i64::from(m.y_accel_m_s2), 10);
576 w.push_i(i64::from(m.z_accel_m_s2), 10);
577 w.push_i(i64::from(m.a_gf0_s), 12);
578 w.push_i(i64::from(m.a_gf1_s_s), 8);
579 push_spares_from(&mut w, &m.reserved, spare_start);
580 w.pad_to(DATA_BITS);
581 w.into_bytes()
582}
583
584fn decode_igp_mask(preamble: u8, data: &[u8]) -> Result<SbasIgpMask> {
585 let mut r = BitReader::new(data);
586 let mut reserved = SpareBits::new();
587 reserved.push(r.u(4)?, 4);
588 let band_number = r.u(4)? as u8;
589 let iodi = r.u(2)? as u8;
590 let mut mask = [false; 201];
591 for bit in &mut mask {
592 *bit = r.flag()?;
593 }
594 if r.remaining_bits() >= 1 {
595 reserved.push(r.u(1)?, 1);
596 }
597 Ok(SbasIgpMask {
598 preamble,
599 band_number,
600 iodi,
601 mask,
602 reserved,
603 })
604}
605
606fn encode_igp_mask(m: &SbasIgpMask) -> Vec<u8> {
607 let mut w = CountedBitWriter::new();
608 let spare_start = push_prefix_spare(&mut w, &m.reserved, 4);
609 w.push_u(u64::from(m.band_number), 4);
610 w.push_u(u64::from(m.iodi), 2);
611 for bit in m.mask {
612 w.push_flag(bit);
613 }
614 push_spares_from(&mut w, &m.reserved, spare_start);
615 w.pad_to(DATA_BITS);
616 w.into_bytes()
617}
618
619fn decode_mixed(preamble: u8, data: &[u8]) -> Result<SbasMixedCorrections> {
620 let mut r = BitReader::new(data);
621 let mut prc = [0i16; 6];
622 for value in &mut prc {
623 *value = r.i(12)? as i16;
624 }
625 let mut udrei = [0u8; 6];
626 for value in &mut udrei {
627 *value = r.u(4)? as u8;
628 }
629 let iodp = r.u(2)? as u8;
630 let block_id = r.u(2)? as u8;
631 let iodf = r.u(2)? as u8;
632 let mut reserved = SpareBits::new();
633 reserved.push(r.u(4)?, 4);
634 let long_raw = read_bits_as_bytes(&mut r, 106)?;
635 let long_term = decode_long_half(&long_raw)?;
636 Ok(SbasMixedCorrections {
637 preamble,
638 fast: SbasMixedFastCorrections {
639 iodf,
640 iodp,
641 block_id,
642 prc,
643 udrei,
644 reserved,
645 },
646 long_term,
647 })
648}
649
650fn encode_mixed(m: &SbasMixedCorrections) -> Vec<u8> {
651 let mut w = CountedBitWriter::new();
652 for value in m.fast.prc {
653 w.push_i(i64::from(value), 12);
654 }
655 for value in m.fast.udrei {
656 w.push_u(u64::from(value), 4);
657 }
658 w.push_u(u64::from(m.fast.iodp), 2);
659 w.push_u(u64::from(m.fast.block_id), 2);
660 w.push_u(u64::from(m.fast.iodf), 2);
661 push_spares(&mut w, &m.fast.reserved);
662 w.pad_to(106);
663 let long = encode_long_half(&m.long_term);
664 w.push_bits_from(&long, 106);
665 w.pad_to(DATA_BITS);
666 w.into_bytes()
667}
668
669fn decode_long_term(preamble: u8, data: &[u8]) -> Result<SbasLongTermCorrections> {
670 let mut r = BitReader::new(data);
671 let first = read_bits_as_bytes(&mut r, 106)?;
672 let second = read_bits_as_bytes(&mut r, 106)?;
673 Ok(SbasLongTermCorrections {
674 preamble,
675 halves: [decode_long_half(&first)?, decode_long_half(&second)?],
676 })
677}
678
679fn encode_long_term(m: &SbasLongTermCorrections) -> Vec<u8> {
680 let mut w = CountedBitWriter::new();
681 for half in &m.halves {
682 let encoded = encode_long_half(half);
683 w.push_bits_from(&encoded, 106);
684 }
685 w.pad_to(DATA_BITS);
686 w.into_bytes()
687}
688
689fn decode_long_half(raw: &[u8]) -> Result<SbasLongTermHalf> {
690 let mut r = BitReader::new(raw);
691 let velocity_code = r.flag()?;
692 let mut records = Vec::new();
693 let mut reserved = SpareBits::new();
694 if velocity_code {
695 records.push(SbasLongTermRecord {
696 monitored_index: r.u(6)? as u8,
697 iode: r.u(8)? as u8,
698 delta_x: r.i(11)? as i32,
699 delta_y: r.i(11)? as i32,
700 delta_z: r.i(11)? as i32,
701 delta_a_f0: r.i(11)? as i32,
702 delta_x_rate: r.i(8)? as i32,
703 delta_y_rate: r.i(8)? as i32,
704 delta_z_rate: r.i(8)? as i32,
705 delta_a_f1: r.i(8)? as i32,
706 time_of_day_s: Some(r.u(13)? as u32),
707 });
708 let iodp = r.u(2)? as u8;
709 Ok(SbasLongTermHalf {
710 velocity_code,
711 iodp,
712 records,
713 reserved,
714 })
715 } else {
716 for _ in 0..2 {
717 records.push(SbasLongTermRecord {
718 monitored_index: r.u(6)? as u8,
719 iode: r.u(8)? as u8,
720 delta_x: r.i(9)? as i32,
721 delta_y: r.i(9)? as i32,
722 delta_z: r.i(9)? as i32,
723 delta_x_rate: 0,
724 delta_y_rate: 0,
725 delta_z_rate: 0,
726 delta_a_f0: r.i(10)? as i32,
727 delta_a_f1: 0,
728 time_of_day_s: None,
729 });
730 }
731 let iodp = r.u(2)? as u8;
732 if r.remaining_bits() >= 1 {
733 reserved.push(r.u(1)?, 1);
734 }
735 Ok(SbasLongTermHalf {
736 velocity_code,
737 iodp,
738 records,
739 reserved,
740 })
741 }
742}
743
744fn encode_long_half(m: &SbasLongTermHalf) -> Vec<u8> {
745 let mut w = CountedBitWriter::new();
746 w.push_flag(m.velocity_code);
747 if m.velocity_code {
748 if let Some(record) = m.records.first() {
749 push_long_record_with_velocity(&mut w, record);
750 } else {
751 push_long_record_with_velocity(&mut w, &zero_long_record());
752 }
753 w.push_u(u64::from(m.iodp), 2);
754 } else {
755 for idx in 0..2 {
756 if let Some(record) = m.records.get(idx) {
757 push_long_record_without_velocity(&mut w, record);
758 } else {
759 push_long_record_without_velocity(&mut w, &zero_long_record());
760 }
761 }
762 w.push_u(u64::from(m.iodp), 2);
763 push_spares(&mut w, &m.reserved);
764 }
765 w.pad_to(106);
766 w.into_bytes()
767}
768
769fn push_long_record_without_velocity(w: &mut CountedBitWriter, record: &SbasLongTermRecord) {
770 w.push_u(u64::from(record.monitored_index), 6);
771 w.push_u(u64::from(record.iode), 8);
772 w.push_i(i64::from(record.delta_x), 9);
773 w.push_i(i64::from(record.delta_y), 9);
774 w.push_i(i64::from(record.delta_z), 9);
775 w.push_i(i64::from(record.delta_a_f0), 10);
776}
777
778fn push_long_record_with_velocity(w: &mut CountedBitWriter, record: &SbasLongTermRecord) {
779 w.push_u(u64::from(record.monitored_index), 6);
780 w.push_u(u64::from(record.iode), 8);
781 w.push_i(i64::from(record.delta_x), 11);
782 w.push_i(i64::from(record.delta_y), 11);
783 w.push_i(i64::from(record.delta_z), 11);
784 w.push_i(i64::from(record.delta_a_f0), 11);
785 w.push_i(i64::from(record.delta_x_rate), 8);
786 w.push_i(i64::from(record.delta_y_rate), 8);
787 w.push_i(i64::from(record.delta_z_rate), 8);
788 w.push_i(i64::from(record.delta_a_f1), 8);
789 w.push_u(u64::from(record.time_of_day_s.unwrap_or(0)), 13);
790}
791
792fn zero_long_record() -> SbasLongTermRecord {
793 SbasLongTermRecord {
794 monitored_index: 0,
795 iode: 0,
796 delta_x: 0,
797 delta_y: 0,
798 delta_z: 0,
799 delta_x_rate: 0,
800 delta_y_rate: 0,
801 delta_z_rate: 0,
802 delta_a_f0: 0,
803 delta_a_f1: 0,
804 time_of_day_s: None,
805 }
806}
807
808fn decode_iono_delays(preamble: u8, data: &[u8]) -> Result<SbasIonoDelays> {
809 let mut r = BitReader::new(data);
810 let band_number = r.u(4)? as u8;
811 let block_id = r.u(4)? as u8;
812 let mut entries: [SbasIgpDelay; 15] = core::array::from_fn(|_| SbasIgpDelay::default());
813 for entry in &mut entries {
814 entry.vertical_delay = r.u(9)? as u16;
815 entry.givei = r.u(4)? as u8;
816 }
817 let iodi = r.u(2)? as u8;
818 let mut reserved = SpareBits::new();
819 if r.remaining_bits() >= 7 {
820 reserved.push(r.u(7)?, 7);
821 }
822 Ok(SbasIonoDelays {
823 preamble,
824 band_number,
825 block_id,
826 iodi,
827 entries,
828 reserved,
829 })
830}
831
832fn encode_iono_delays(m: &SbasIonoDelays) -> Vec<u8> {
833 let mut w = CountedBitWriter::new();
834 w.push_u(u64::from(m.band_number), 4);
835 w.push_u(u64::from(m.block_id), 4);
836 for entry in &m.entries {
837 w.push_u(u64::from(entry.vertical_delay), 9);
838 w.push_u(u64::from(entry.givei), 4);
839 }
840 w.push_u(u64::from(m.iodi), 2);
841 push_spares(&mut w, &m.reserved);
842 w.pad_to(DATA_BITS);
843 w.into_bytes()
844}
845
846fn push_spares(w: &mut CountedBitWriter, spare: &SpareBits) {
847 for &(value, width) in &spare.0 {
848 w.push_u(value, usize::from(width));
849 }
850}
851
852fn push_prefix_spare(w: &mut CountedBitWriter, spare: &SpareBits, width: u8) -> usize {
853 if let Some(&(value, got_width)) = spare
854 .0
855 .first()
856 .filter(|&&(_, got_width)| got_width == width)
857 {
858 w.push_u(value, usize::from(got_width));
859 1
860 } else {
861 w.push_u(0, usize::from(width));
862 0
863 }
864}
865
866fn push_spares_from(w: &mut CountedBitWriter, spare: &SpareBits, start: usize) {
867 for &(value, width) in spare.0.iter().skip(start) {
868 w.push_u(value, usize::from(width));
869 }
870}
871
872fn read_bits_as_bytes(reader: &mut BitReader<'_>, nbits: usize) -> Result<Vec<u8>> {
873 let mut w = CountedBitWriter::new();
874 for _ in 0..nbits {
875 w.push_u(reader.u(1)?, 1);
876 }
877 Ok(w.into_bytes())
878}
879
880fn data_from_raw(raw: &[u8]) -> Vec<u8> {
881 let mut w = CountedBitWriter::new();
882 w.push_bits_from(raw, DATA_BITS.min(raw.len() * 8));
883 w.pad_to(DATA_BITS);
884 w.into_bytes()
885}
886
887fn bit_at(bytes: &[u8], bit_pos: usize) -> u8 {
888 (bytes[bit_pos / 8] >> (7 - (bit_pos % 8))) & 1
889}
890
891fn bits_as_u32(bytes: &[u8], bit_pos: usize, nbits: usize) -> u32 {
892 let mut value = 0u32;
893 for pos in bit_pos..bit_pos + nbits {
894 value = (value << 1) | u32::from(bit_at(bytes, pos));
895 }
896 value
897}
898
899fn parse_error(message: &str) -> Error {
900 Error::Parse(message.to_string())
901}
902
903#[cfg(test)]
904mod tests {
905 use super::*;
906 use crate::rtcm::crc::crc24q;
907
908 fn block(message: SbasMessage, form: SbasWireForm) -> Vec<u8> {
909 SbasBlock { form, message }.encode()
910 }
911
912 fn bits_i(bytes: &[u8], bit_pos: usize, nbits: usize) -> i32 {
913 let raw = bits_as_u32(bytes, bit_pos, nbits);
914 let sign_bit = 1u32 << (nbits - 1);
915 if raw & sign_bit != 0 {
916 raw as i32 - (1i32 << nbits)
917 } else {
918 raw as i32
919 }
920 }
921
922 #[test]
923 fn crc_bits_matches_byte_crc_when_aligned() {
924 let data = [0x53, 0x01, 0x23, 0x45, 0x67, 0x89];
925 assert_eq!(crc24q_bits(&data, data.len() * 8), crc24q(&data));
926 }
927
928 #[test]
929 fn prn_mask_decodes_and_round_trips_in_both_forms() {
930 let mut mask = [false; 210];
931 mask[0] = true;
932 mask[119] = true;
933 mask[157] = true;
934 let msg = SbasMessage::PrnMask(SbasPrnMask {
935 preamble: 0x53,
936 iodp: 2,
937 mask,
938 reserved: SpareBits::new(),
939 });
940 for form in [SbasWireForm::Body226, SbasWireForm::Framed250] {
941 let encoded = block(msg.clone(), form);
942 let decoded = SbasBlock::decode(&encoded, form).expect("valid SBAS block");
943 assert_eq!(decoded.encode(), encoded);
944 assert_eq!(decoded.message.message_type(), 1);
945 }
946 }
947
948 #[test]
949 fn corrupted_crc_is_rejected() {
950 let msg = SbasMessage::Unsupported(SbasUnsupported {
951 preamble: 0x9A,
952 message_type: 62,
953 data: vec![0; BODY_LEN],
954 });
955 let mut encoded = block(msg, SbasWireForm::Framed250);
956 encoded[31] ^= 0x40;
957 assert!(SbasBlock::decode(&encoded, SbasWireForm::Framed250).is_err());
958 }
959
960 #[test]
961 fn phase_a_message_classification() {
962 for mt in [0, 1, 2, 3, 4, 5, 6, 7, 9, 12, 17, 18, 24, 25, 26] {
963 assert!(is_phase_a_message(mt));
964 }
965 assert!(!is_phase_a_message(10));
966 assert!(!is_phase_a_message(63));
967 }
968
969 #[test]
970 fn mt24_encode_uses_rtklib_offsets() {
971 let body = block(
972 SbasMessage::MixedCorrections(SbasMixedCorrections {
973 preamble: 0x53,
974 fast: SbasMixedFastCorrections {
975 iodf: 1,
976 iodp: 2,
977 block_id: 3,
978 prc: [-1, 2, -3, 4, -5, 6],
979 udrei: [1, 2, 3, 4, 5, 6],
980 reserved: SpareBits(vec![(0b1010, 4)]),
981 },
982 long_term: SbasLongTermHalf {
983 velocity_code: false,
984 iodp: 2,
985 records: vec![
986 SbasLongTermRecord {
987 monitored_index: 5,
988 iode: 9,
989 delta_x: -10,
990 delta_y: 11,
991 delta_z: -12,
992 delta_x_rate: 0,
993 delta_y_rate: 0,
994 delta_z_rate: 0,
995 delta_a_f0: 13,
996 delta_a_f1: 0,
997 time_of_day_s: None,
998 },
999 SbasLongTermRecord {
1000 monitored_index: 6,
1001 iode: 10,
1002 delta_x: 14,
1003 delta_y: -15,
1004 delta_z: 16,
1005 delta_x_rate: 0,
1006 delta_y_rate: 0,
1007 delta_z_rate: 0,
1008 delta_a_f0: -17,
1009 delta_a_f1: 0,
1010 time_of_day_s: None,
1011 },
1012 ],
1013 reserved: SpareBits(vec![(1, 1)]),
1014 },
1015 }),
1016 SbasWireForm::Body226,
1017 );
1018
1019 assert_eq!(bits_as_u32(&body, 8, 6), 24);
1020 assert_eq!(bits_i(&body, 14, 12), -1);
1021 assert_eq!(bits_i(&body, 26, 12), 2);
1022 assert_eq!(bits_i(&body, 38, 12), -3);
1023 assert_eq!(bits_i(&body, 50, 12), 4);
1024 assert_eq!(bits_i(&body, 62, 12), -5);
1025 assert_eq!(bits_i(&body, 74, 12), 6);
1026 assert_eq!(bits_as_u32(&body, 86, 4), 1);
1027 assert_eq!(bits_as_u32(&body, 90, 4), 2);
1028 assert_eq!(bits_as_u32(&body, 94, 4), 3);
1029 assert_eq!(bits_as_u32(&body, 98, 4), 4);
1030 assert_eq!(bits_as_u32(&body, 102, 4), 5);
1031 assert_eq!(bits_as_u32(&body, 106, 4), 6);
1032 assert_eq!(bits_as_u32(&body, 110, 2), 2);
1033 assert_eq!(bits_as_u32(&body, 112, 2), 3);
1034 assert_eq!(bits_as_u32(&body, 114, 2), 1);
1035 assert_eq!(bits_as_u32(&body, 116, 4), 0b1010);
1036 assert_eq!(bits_as_u32(&body, 120, 1), 0);
1037 assert_eq!(bits_as_u32(&body, 121, 6), 5);
1038 assert_eq!(bits_as_u32(&body, 172, 6), 6);
1039 assert_eq!(bits_as_u32(&body, 223, 2), 2);
1040 assert_eq!(bits_as_u32(&body, 225, 1), 1);
1041 }
1042}