1use crate::validate;
32
33pub const WORD_LENGTH: usize = 30;
35pub const SUBFRAME_LENGTH: usize = 300;
37pub const PREAMBLE: u32 = 0b1000_1011;
39
40const TWO_POW_4: f64 = 16.0;
44const TWO_POW_M5: f64 = 1.0 / 32.0;
45const TWO_POW_M19: f64 = 1.0 / 524_288.0;
46const TWO_POW_M29: f64 = 1.0 / 536_870_912.0;
47const TWO_POW_M31: f64 = 1.0 / 2_147_483_648.0;
48const TWO_POW_M33: f64 = 1.0 / 8_589_934_592.0;
49const TWO_POW_M43: f64 = 1.0 / 8_796_093_022_208.0;
50const TWO_POW_M55: f64 = 1.0 / 36_028_797_018_963_968.0;
51
52#[derive(Clone, Copy, Debug, PartialEq)]
56pub enum LnavNumber {
57 Int(i64),
59 Float(f64),
61}
62
63impl LnavNumber {
64 fn as_f64(self) -> f64 {
65 match self {
66 LnavNumber::Int(i) => i as f64,
67 LnavNumber::Float(f) => f,
68 }
69 }
70
71 fn as_i64_truncated(self) -> i64 {
72 match self {
73 LnavNumber::Int(i) => i,
74 LnavNumber::Float(f) => f as i64,
75 }
76 }
77
78 fn is_int(self) -> bool {
79 matches!(self, LnavNumber::Int(_))
80 }
81}
82
83#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85pub enum LnavField {
86 Tow,
87 Alert,
88 AntiSpoof,
89 Integrity,
90 TlmMessage,
91 WeekNumber,
92 L2Code,
93 L2PDataFlag,
94 UraIndex,
95 SvHealth,
96 Iodc,
97 Tgd,
98 Toc,
99 Af2,
100 Af1,
101 Af0,
102 Iode,
103 Crs,
104 DeltaN,
105 M0,
106 Cuc,
107 Eccentricity,
108 Cus,
109 SqrtA,
110 Toe,
111 FitIntervalFlag,
112 Aodo,
113 Cic,
114 Omega0,
115 Cis,
116 I0,
117 Crc,
118 Omega,
119 OmegaDot,
120 Idot,
121}
122
123impl LnavField {
124 pub fn name(self) -> &'static str {
126 match self {
127 LnavField::Tow => "tow",
128 LnavField::Alert => "alert",
129 LnavField::AntiSpoof => "anti_spoof",
130 LnavField::Integrity => "integrity",
131 LnavField::TlmMessage => "tlm_message",
132 LnavField::WeekNumber => "week_number",
133 LnavField::L2Code => "l2_code",
134 LnavField::L2PDataFlag => "l2_p_data_flag",
135 LnavField::UraIndex => "ura_index",
136 LnavField::SvHealth => "sv_health",
137 LnavField::Iodc => "iodc",
138 LnavField::Tgd => "tgd",
139 LnavField::Toc => "toc",
140 LnavField::Af2 => "af2",
141 LnavField::Af1 => "af1",
142 LnavField::Af0 => "af0",
143 LnavField::Iode => "iode",
144 LnavField::Crs => "crs",
145 LnavField::DeltaN => "delta_n",
146 LnavField::M0 => "m0",
147 LnavField::Cuc => "cuc",
148 LnavField::Eccentricity => "eccentricity",
149 LnavField::Cus => "cus",
150 LnavField::SqrtA => "sqrt_a",
151 LnavField::Toe => "toe",
152 LnavField::FitIntervalFlag => "fit_interval_flag",
153 LnavField::Aodo => "aodo",
154 LnavField::Cic => "cic",
155 LnavField::Omega0 => "omega0",
156 LnavField::Cis => "cis",
157 LnavField::I0 => "i0",
158 LnavField::Crc => "crc",
159 LnavField::Omega => "omega",
160 LnavField::OmegaDot => "omega_dot",
161 LnavField::Idot => "idot",
162 }
163 }
164}
165
166#[derive(Clone, Copy, Debug, PartialEq)]
168pub enum LnavError {
169 OutOfRange { field: LnavField, value: LnavNumber },
172 ParityFailed { subframe: u8, word: u8 },
174 BadWordLength { expected: usize, actual: usize },
176 BadSubframeLength { subframe: u8 },
178}
179
180#[derive(Clone, Copy, Debug)]
183pub struct LnavParams {
184 pub week_number: LnavNumber,
185 pub l2_code: LnavNumber,
186 pub l2_p_data_flag: LnavNumber,
187 pub ura_index: LnavNumber,
188 pub sv_health: LnavNumber,
189 pub iodc: LnavNumber,
190 pub tgd: LnavNumber,
191 pub toc: LnavNumber,
192 pub af0: LnavNumber,
193 pub af1: LnavNumber,
194 pub af2: LnavNumber,
195 pub iode: LnavNumber,
196 pub crs: LnavNumber,
197 pub delta_n: LnavNumber,
198 pub m0: LnavNumber,
199 pub cuc: LnavNumber,
200 pub eccentricity: LnavNumber,
201 pub cus: LnavNumber,
202 pub sqrt_a: LnavNumber,
203 pub toe: LnavNumber,
204 pub fit_interval_flag: LnavNumber,
205 pub aodo: LnavNumber,
206 pub cic: LnavNumber,
207 pub omega0: LnavNumber,
208 pub cis: LnavNumber,
209 pub i0: LnavNumber,
210 pub crc: LnavNumber,
211 pub omega: LnavNumber,
212 pub omega_dot: LnavNumber,
213 pub idot: LnavNumber,
214}
215
216#[derive(Clone, Copy, Debug)]
218pub struct LnavOptions {
219 pub tow: LnavNumber,
220 pub alert: LnavNumber,
221 pub anti_spoof: LnavNumber,
222 pub integrity: LnavNumber,
223 pub tlm_message: LnavNumber,
224}
225
226#[derive(Clone, Copy, Debug, PartialEq)]
231pub struct LnavDecoded {
232 pub week_number: i64,
233 pub l2_code: i64,
234 pub ura_index: i64,
235 pub sv_health: i64,
236 pub iodc: i64,
237 pub tgd: f64,
238 pub toc: i64,
239 pub af0: f64,
240 pub af1: f64,
241 pub af2: f64,
242 pub iode: i64,
243 pub crs: f64,
244 pub delta_n: f64,
245 pub m0: f64,
246 pub cuc: f64,
247 pub eccentricity: f64,
248 pub cus: f64,
249 pub sqrt_a: f64,
250 pub toe: i64,
251 pub fit_interval_flag: i64,
252 pub aodo: i64,
253 pub cic: f64,
254 pub omega0: f64,
255 pub cis: f64,
256 pub i0: f64,
257 pub crc: f64,
258 pub omega: f64,
259 pub omega_dot: f64,
260 pub idot: f64,
261}
262
263#[derive(Clone, Copy)]
266enum FieldKind {
267 Uint { bits: u32 },
269 UintScaled { bits: u32, scale: f64 },
271 SintScaled { bits: u32, scale: f64 },
273}
274
275fn validate_field(field: LnavField, value: LnavNumber, kind: FieldKind) -> Result<(), LnavError> {
278 let in_range = match kind {
279 FieldKind::Uint { bits } => {
280 value.is_int() && {
281 let v = value.as_i64_truncated();
282 v >= 0 && v < (1i64 << bits)
283 }
284 }
285 FieldKind::UintScaled { bits, scale } => {
286 value.as_f64() >= 0.0 && {
287 let n = round_half_away(value.as_f64() / scale);
288 n >= 0 && n < (1i64 << bits)
289 }
290 }
291 FieldKind::SintScaled { bits, scale } => {
292 value.as_f64().is_finite() && {
295 let n = round_half_away(value.as_f64() / scale);
296 let limit = 1i64 << (bits - 1);
297 n >= -limit && n < limit
298 }
299 }
300 };
301
302 if in_range {
303 Ok(())
304 } else {
305 Err(LnavError::OutOfRange { field, value })
306 }
307}
308
309fn round_half_away(x: f64) -> i64 {
311 x.round() as i64
312}
313
314pub fn tow(bits: &[u8]) -> Option<u64> {
321 how_word(bits).map(|how| bits_to_uint(&how[0..17]))
322}
323
324pub fn subframe_id(bits: &[u8]) -> Option<u64> {
329 how_word(bits).map(|how| bits_to_uint(&how[19..22]))
330}
331
332fn how_word(bits: &[u8]) -> Option<Vec<u8>> {
333 match bits.len() {
334 WORD_LENGTH => Some(bits.to_vec()),
335 SUBFRAME_LENGTH => Some(bits[WORD_LENGTH..2 * WORD_LENGTH].to_vec()),
336 _ => None,
337 }
338}
339
340pub fn parity(data24: &[u8], d29_prev: u8, d30_prev: u8) -> Result<[u8; 6], LnavError> {
346 validate::exact_len(data24, 24, "lnav parity data bits").map_err(|error| {
347 LnavError::BadWordLength {
348 expected: error.expected,
349 actual: error.actual,
350 }
351 })?;
352
353 let d = |n: usize| data24[n - 1];
355
356 let d25 = xor(&[
357 d29_prev,
358 d(1),
359 d(2),
360 d(3),
361 d(5),
362 d(6),
363 d(10),
364 d(11),
365 d(12),
366 d(13),
367 d(14),
368 d(17),
369 d(18),
370 d(20),
371 d(23),
372 ]);
373 let d26 = xor(&[
374 d30_prev,
375 d(2),
376 d(3),
377 d(4),
378 d(6),
379 d(7),
380 d(11),
381 d(12),
382 d(13),
383 d(14),
384 d(15),
385 d(18),
386 d(19),
387 d(21),
388 d(24),
389 ]);
390 let d27 = xor(&[
391 d29_prev,
392 d(1),
393 d(3),
394 d(4),
395 d(5),
396 d(7),
397 d(8),
398 d(12),
399 d(13),
400 d(14),
401 d(15),
402 d(16),
403 d(19),
404 d(20),
405 d(22),
406 ]);
407 let d28 = xor(&[
408 d30_prev,
409 d(2),
410 d(4),
411 d(5),
412 d(6),
413 d(8),
414 d(9),
415 d(13),
416 d(14),
417 d(15),
418 d(16),
419 d(17),
420 d(20),
421 d(21),
422 d(23),
423 ]);
424 let d29 = xor(&[
425 d30_prev,
426 d(1),
427 d(3),
428 d(5),
429 d(6),
430 d(7),
431 d(9),
432 d(10),
433 d(14),
434 d(15),
435 d(16),
436 d(17),
437 d(18),
438 d(21),
439 d(22),
440 d(24),
441 ]);
442 let d30 = xor(&[
443 d29_prev,
444 d(3),
445 d(5),
446 d(6),
447 d(8),
448 d(9),
449 d(10),
450 d(11),
451 d(13),
452 d(15),
453 d(19),
454 d(22),
455 d(23),
456 d(24),
457 ]);
458
459 Ok([d25, d26, d27, d28, d29, d30])
460}
461
462pub fn parity_valid(word30: &[u8], d29_prev: u8, d30_prev: u8) -> bool {
468 if word30.len() != WORD_LENGTH {
469 return false;
470 }
471 let source: Vec<u8> = word30[0..24].iter().map(|b| b ^ d30_prev).collect();
472 let received = &word30[24..30];
473 parity(&source, d29_prev, d30_prev).is_ok_and(|par| par.as_slice() == received)
474}
475
476pub fn encode(params: &LnavParams, opts: &LnavOptions) -> Result<[Vec<u8>; 3], LnavError> {
481 validate_field(LnavField::Tow, opts.tow, FieldKind::Uint { bits: 17 })?;
482 validate_field(LnavField::Alert, opts.alert, FieldKind::Uint { bits: 1 })?;
483 validate_field(
484 LnavField::AntiSpoof,
485 opts.anti_spoof,
486 FieldKind::Uint { bits: 1 },
487 )?;
488 validate_field(
489 LnavField::Integrity,
490 opts.integrity,
491 FieldKind::Uint { bits: 1 },
492 )?;
493 validate_field(
494 LnavField::TlmMessage,
495 opts.tlm_message,
496 FieldKind::Uint { bits: 14 },
497 )?;
498
499 let w1 = subframe1_words(params)?;
500 let w2 = subframe2_words(params)?;
501 let w3 = subframe3_words(params)?;
502
503 let tlm = tlm_data(
504 opts.tlm_message.as_i64_truncated(),
505 opts.integrity.as_i64_truncated(),
506 );
507
508 let sf1 = assemble_subframe(&prepend_headers(&tlm, opts, 1, w1))?;
509 let sf2 = assemble_subframe(&prepend_headers(&tlm, opts, 2, w2))?;
510 let sf3 = assemble_subframe(&prepend_headers(&tlm, opts, 3, w3))?;
511
512 Ok([sf1, sf2, sf3])
513}
514
515pub fn decode(sf1: &[u8], sf2: &[u8], sf3: &[u8]) -> Result<LnavDecoded, LnavError> {
520 verify_subframe(sf1, 1)?;
521 verify_subframe(sf2, 2)?;
522 verify_subframe(sf3, 3)?;
523
524 let w1 = source_words(sf1);
525 let w2 = source_words(sf2);
526 let w3 = source_words(sf3);
527
528 let mut d = LnavDecoded {
529 week_number: 0,
530 l2_code: 0,
531 ura_index: 0,
532 sv_health: 0,
533 iodc: 0,
534 tgd: 0.0,
535 toc: 0,
536 af0: 0.0,
537 af1: 0.0,
538 af2: 0.0,
539 iode: 0,
540 crs: 0.0,
541 delta_n: 0.0,
542 m0: 0.0,
543 cuc: 0.0,
544 eccentricity: 0.0,
545 cus: 0.0,
546 sqrt_a: 0.0,
547 toe: 0,
548 fit_interval_flag: 0,
549 aodo: 0,
550 cic: 0.0,
551 omega0: 0.0,
552 cis: 0.0,
553 i0: 0.0,
554 crc: 0.0,
555 omega: 0.0,
556 omega_dot: 0.0,
557 idot: 0.0,
558 };
559
560 decode_subframe1(&mut d, &w1);
561 decode_subframe2(&mut d, &w2);
562 decode_subframe3(&mut d, &w3);
563
564 Ok(d)
565}
566
567struct WordEntry {
572 data: Vec<u8>,
573 solve: bool,
574}
575
576impl WordEntry {
577 fn raw(data: Vec<u8>) -> Self {
578 WordEntry { data, solve: false }
579 }
580 fn solved(data: Vec<u8>) -> Self {
581 WordEntry { data, solve: true }
582 }
583}
584
585fn subframe1_words(p: &LnavParams) -> Result<Vec<WordEntry>, LnavError> {
586 validate_field(
587 LnavField::WeekNumber,
588 p.week_number,
589 FieldKind::Uint { bits: 10 },
590 )?;
591 validate_field(LnavField::L2Code, p.l2_code, FieldKind::Uint { bits: 2 })?;
592 validate_field(
593 LnavField::L2PDataFlag,
594 p.l2_p_data_flag,
595 FieldKind::Uint { bits: 1 },
596 )?;
597 validate_field(
598 LnavField::UraIndex,
599 p.ura_index,
600 FieldKind::Uint { bits: 4 },
601 )?;
602 validate_field(
603 LnavField::SvHealth,
604 p.sv_health,
605 FieldKind::Uint { bits: 6 },
606 )?;
607 validate_field(LnavField::Iodc, p.iodc, FieldKind::Uint { bits: 10 })?;
608 validate_field(
609 LnavField::Tgd,
610 p.tgd,
611 FieldKind::SintScaled {
612 bits: 8,
613 scale: TWO_POW_M31,
614 },
615 )?;
616 validate_field(
617 LnavField::Toc,
618 p.toc,
619 FieldKind::UintScaled {
620 bits: 16,
621 scale: TWO_POW_4,
622 },
623 )?;
624 validate_field(
625 LnavField::Af2,
626 p.af2,
627 FieldKind::SintScaled {
628 bits: 8,
629 scale: TWO_POW_M55,
630 },
631 )?;
632 validate_field(
633 LnavField::Af1,
634 p.af1,
635 FieldKind::SintScaled {
636 bits: 16,
637 scale: TWO_POW_M43,
638 },
639 )?;
640 validate_field(
641 LnavField::Af0,
642 p.af0,
643 FieldKind::SintScaled {
644 bits: 22,
645 scale: TWO_POW_M31,
646 },
647 )?;
648
649 let iodc = p.iodc.as_i64_truncated();
650 let iodc_msb = (iodc >> 8) & 0x3;
651 let iodc_lsb = iodc & 0xFF;
652 let l2_p_data_flag = p.l2_p_data_flag.as_i64_truncated();
653
654 let mut word3 = pack_uint(p.week_number.as_i64_truncated(), 10);
656 word3.extend(pack_uint(p.l2_code.as_i64_truncated(), 2));
657 word3.extend(pack_uint(p.ura_index.as_i64_truncated(), 4));
658 word3.extend(pack_uint(p.sv_health.as_i64_truncated(), 6));
659 word3.extend(pack_uint(iodc_msb, 2));
660
661 let mut word4 = pack_uint(l2_p_data_flag, 1);
663 word4.extend(zeros(23));
664 let word5 = zeros(24);
666 let word6 = zeros(24);
667 let mut word7 = zeros(16);
669 word7.extend(pack_sint(p.tgd.as_f64(), 8, TWO_POW_M31));
670 let mut word8 = pack_uint(iodc_lsb, 8);
672 word8.extend(pack_uint_scaled(p.toc.as_f64(), 16, TWO_POW_4));
673 let mut word9 = pack_sint(p.af2.as_f64(), 8, TWO_POW_M55);
675 word9.extend(pack_sint(p.af1.as_f64(), 16, TWO_POW_M43));
676 let word10 = pack_sint(p.af0.as_f64(), 22, TWO_POW_M31);
678
679 Ok(vec![
680 WordEntry::raw(word3),
681 WordEntry::raw(word4),
682 WordEntry::raw(word5),
683 WordEntry::raw(word6),
684 WordEntry::raw(word7),
685 WordEntry::raw(word8),
686 WordEntry::raw(word9),
687 WordEntry::solved(word10),
688 ])
689}
690
691fn decode_subframe1(p: &mut LnavDecoded, w: &[Vec<u8>]) {
692 let word3 = &w[0];
693 let word7 = &w[4];
694 let word8 = &w[5];
695 let word9 = &w[6];
696 let word10 = &w[7];
697
698 p.week_number = bits_to_uint(slice(word3, 1, 10)) as i64;
699 p.l2_code = bits_to_uint(slice(word3, 11, 2)) as i64;
700 p.ura_index = bits_to_uint(slice(word3, 13, 4)) as i64;
701 p.sv_health = bits_to_uint(slice(word3, 17, 6)) as i64;
702 let iodc_msb = bits_to_uint(slice(word3, 23, 2)) as i64;
703
704 p.tgd = unpack_sint(slice(word7, 17, 8), TWO_POW_M31);
705 let iodc_lsb = bits_to_uint(slice(word8, 1, 8)) as i64;
706 p.toc = unpack_uint_scaled_int(slice(word8, 9, 16), TWO_POW_4);
707 p.af2 = unpack_sint(slice(word9, 1, 8), TWO_POW_M55);
708 p.af1 = unpack_sint(slice(word9, 9, 16), TWO_POW_M43);
709 p.af0 = unpack_sint(slice(word10, 1, 22), TWO_POW_M31);
710
711 p.iodc = (iodc_msb << 8) | iodc_lsb;
712}
713
714fn subframe2_words(p: &LnavParams) -> Result<Vec<WordEntry>, LnavError> {
717 validate_field(LnavField::Iode, p.iode, FieldKind::Uint { bits: 8 })?;
718 validate_field(
719 LnavField::Crs,
720 p.crs,
721 FieldKind::SintScaled {
722 bits: 16,
723 scale: TWO_POW_M5,
724 },
725 )?;
726 validate_field(
727 LnavField::DeltaN,
728 p.delta_n,
729 FieldKind::SintScaled {
730 bits: 16,
731 scale: TWO_POW_M43,
732 },
733 )?;
734 validate_field(
735 LnavField::M0,
736 p.m0,
737 FieldKind::SintScaled {
738 bits: 32,
739 scale: TWO_POW_M31,
740 },
741 )?;
742 validate_field(
743 LnavField::Cuc,
744 p.cuc,
745 FieldKind::SintScaled {
746 bits: 16,
747 scale: TWO_POW_M29,
748 },
749 )?;
750 validate_field(
751 LnavField::Eccentricity,
752 p.eccentricity,
753 FieldKind::UintScaled {
754 bits: 32,
755 scale: TWO_POW_M33,
756 },
757 )?;
758 validate_field(
759 LnavField::Cus,
760 p.cus,
761 FieldKind::SintScaled {
762 bits: 16,
763 scale: TWO_POW_M29,
764 },
765 )?;
766 validate_field(
767 LnavField::SqrtA,
768 p.sqrt_a,
769 FieldKind::UintScaled {
770 bits: 32,
771 scale: TWO_POW_M19,
772 },
773 )?;
774 validate_field(
775 LnavField::Toe,
776 p.toe,
777 FieldKind::UintScaled {
778 bits: 16,
779 scale: TWO_POW_4,
780 },
781 )?;
782 validate_field(
783 LnavField::FitIntervalFlag,
784 p.fit_interval_flag,
785 FieldKind::Uint { bits: 1 },
786 )?;
787 validate_field(LnavField::Aodo, p.aodo, FieldKind::Uint { bits: 5 })?;
788
789 let m0 = pack_sint(p.m0.as_f64(), 32, TWO_POW_M31);
790 let ecc = pack_uint_scaled(p.eccentricity.as_f64(), 32, TWO_POW_M33);
791 let sqrt_a = pack_uint_scaled(p.sqrt_a.as_f64(), 32, TWO_POW_M19);
792
793 let mut word3 = pack_uint(p.iode.as_i64_truncated(), 8);
795 word3.extend(pack_sint(p.crs.as_f64(), 16, TWO_POW_M5));
796 let mut word4 = pack_sint(p.delta_n.as_f64(), 16, TWO_POW_M43);
798 word4.extend_from_slice(&m0[0..8]);
799 let word5 = m0[8..32].to_vec();
801 let mut word6 = pack_sint(p.cuc.as_f64(), 16, TWO_POW_M29);
803 word6.extend_from_slice(&ecc[0..8]);
804 let word7 = ecc[8..32].to_vec();
806 let mut word8 = pack_sint(p.cus.as_f64(), 16, TWO_POW_M29);
808 word8.extend_from_slice(&sqrt_a[0..8]);
809 let word9 = sqrt_a[8..32].to_vec();
811 let mut word10 = pack_uint_scaled(p.toe.as_f64(), 16, TWO_POW_4);
813 word10.extend(pack_uint(p.fit_interval_flag.as_i64_truncated(), 1));
814 word10.extend(pack_uint(p.aodo.as_i64_truncated(), 5));
815
816 Ok(vec![
817 WordEntry::raw(word3),
818 WordEntry::raw(word4),
819 WordEntry::raw(word5),
820 WordEntry::raw(word6),
821 WordEntry::raw(word7),
822 WordEntry::raw(word8),
823 WordEntry::raw(word9),
824 WordEntry::solved(word10),
825 ])
826}
827
828fn decode_subframe2(p: &mut LnavDecoded, w: &[Vec<u8>]) {
829 let (word3, word4, word5, word6, word7, word8, word9, word10) =
830 (&w[0], &w[1], &w[2], &w[3], &w[4], &w[5], &w[6], &w[7]);
831
832 p.iode = bits_to_uint(slice(word3, 1, 8)) as i64;
833 p.crs = unpack_sint(slice(word3, 9, 16), TWO_POW_M5);
834 p.delta_n = unpack_sint(slice(word4, 1, 16), TWO_POW_M43);
835 let mut m0_bits = slice(word4, 17, 8).to_vec();
836 m0_bits.extend_from_slice(slice(word5, 1, 24));
837 p.m0 = unpack_sint(&m0_bits, TWO_POW_M31);
838 p.cuc = unpack_sint(slice(word6, 1, 16), TWO_POW_M29);
839 let mut ecc_bits = slice(word6, 17, 8).to_vec();
840 ecc_bits.extend_from_slice(slice(word7, 1, 24));
841 p.eccentricity = unpack_uint_scaled(&ecc_bits, TWO_POW_M33);
842 p.cus = unpack_sint(slice(word8, 1, 16), TWO_POW_M29);
843 let mut sqrt_a_bits = slice(word8, 17, 8).to_vec();
844 sqrt_a_bits.extend_from_slice(slice(word9, 1, 24));
845 p.sqrt_a = unpack_uint_scaled(&sqrt_a_bits, TWO_POW_M19);
846 p.toe = unpack_uint_scaled_int(slice(word10, 1, 16), TWO_POW_4);
847 p.fit_interval_flag = bits_to_uint(slice(word10, 17, 1)) as i64;
848 p.aodo = bits_to_uint(slice(word10, 18, 5)) as i64;
849}
850
851fn subframe3_words(p: &LnavParams) -> Result<Vec<WordEntry>, LnavError> {
854 validate_field(
855 LnavField::Cic,
856 p.cic,
857 FieldKind::SintScaled {
858 bits: 16,
859 scale: TWO_POW_M29,
860 },
861 )?;
862 validate_field(
863 LnavField::Omega0,
864 p.omega0,
865 FieldKind::SintScaled {
866 bits: 32,
867 scale: TWO_POW_M31,
868 },
869 )?;
870 validate_field(
871 LnavField::Cis,
872 p.cis,
873 FieldKind::SintScaled {
874 bits: 16,
875 scale: TWO_POW_M29,
876 },
877 )?;
878 validate_field(
879 LnavField::I0,
880 p.i0,
881 FieldKind::SintScaled {
882 bits: 32,
883 scale: TWO_POW_M31,
884 },
885 )?;
886 validate_field(
887 LnavField::Crc,
888 p.crc,
889 FieldKind::SintScaled {
890 bits: 16,
891 scale: TWO_POW_M5,
892 },
893 )?;
894 validate_field(
895 LnavField::Omega,
896 p.omega,
897 FieldKind::SintScaled {
898 bits: 32,
899 scale: TWO_POW_M31,
900 },
901 )?;
902 validate_field(
903 LnavField::OmegaDot,
904 p.omega_dot,
905 FieldKind::SintScaled {
906 bits: 24,
907 scale: TWO_POW_M43,
908 },
909 )?;
910 validate_field(LnavField::Iode, p.iode, FieldKind::Uint { bits: 8 })?;
911 validate_field(
912 LnavField::Idot,
913 p.idot,
914 FieldKind::SintScaled {
915 bits: 14,
916 scale: TWO_POW_M43,
917 },
918 )?;
919
920 let omega0 = pack_sint(p.omega0.as_f64(), 32, TWO_POW_M31);
921 let i0 = pack_sint(p.i0.as_f64(), 32, TWO_POW_M31);
922 let omega = pack_sint(p.omega.as_f64(), 32, TWO_POW_M31);
923
924 let mut word3 = pack_sint(p.cic.as_f64(), 16, TWO_POW_M29);
926 word3.extend_from_slice(&omega0[0..8]);
927 let word4 = omega0[8..32].to_vec();
929 let mut word5 = pack_sint(p.cis.as_f64(), 16, TWO_POW_M29);
931 word5.extend_from_slice(&i0[0..8]);
932 let word6 = i0[8..32].to_vec();
934 let mut word7 = pack_sint(p.crc.as_f64(), 16, TWO_POW_M5);
936 word7.extend_from_slice(&omega[0..8]);
937 let word8 = omega[8..32].to_vec();
939 let word9 = pack_sint(p.omega_dot.as_f64(), 24, TWO_POW_M43);
941 let mut word10 = pack_uint(p.iode.as_i64_truncated(), 8);
943 word10.extend(pack_sint(p.idot.as_f64(), 14, TWO_POW_M43));
944
945 Ok(vec![
946 WordEntry::raw(word3),
947 WordEntry::raw(word4),
948 WordEntry::raw(word5),
949 WordEntry::raw(word6),
950 WordEntry::raw(word7),
951 WordEntry::raw(word8),
952 WordEntry::raw(word9),
953 WordEntry::solved(word10),
954 ])
955}
956
957fn decode_subframe3(p: &mut LnavDecoded, w: &[Vec<u8>]) {
958 let (word3, word4, word5, word6, word7, word8, word9, word10) =
959 (&w[0], &w[1], &w[2], &w[3], &w[4], &w[5], &w[6], &w[7]);
960
961 p.cic = unpack_sint(slice(word3, 1, 16), TWO_POW_M29);
962 let mut omega0_bits = slice(word3, 17, 8).to_vec();
963 omega0_bits.extend_from_slice(slice(word4, 1, 24));
964 p.omega0 = unpack_sint(&omega0_bits, TWO_POW_M31);
965 p.cis = unpack_sint(slice(word5, 1, 16), TWO_POW_M29);
966 let mut i0_bits = slice(word5, 17, 8).to_vec();
967 i0_bits.extend_from_slice(slice(word6, 1, 24));
968 p.i0 = unpack_sint(&i0_bits, TWO_POW_M31);
969 p.crc = unpack_sint(slice(word7, 1, 16), TWO_POW_M5);
970 let mut omega_bits = slice(word7, 17, 8).to_vec();
971 omega_bits.extend_from_slice(slice(word8, 1, 24));
972 p.omega = unpack_sint(&omega_bits, TWO_POW_M31);
973 p.omega_dot = unpack_sint(slice(word9, 1, 24), TWO_POW_M43);
974 p.idot = unpack_sint(slice(word10, 9, 14), TWO_POW_M43);
975}
976
977fn tlm_data(tlm_message: i64, integrity: i64) -> Vec<u8> {
980 let mut bits = pack_uint(PREAMBLE as i64, 8);
982 bits.extend(pack_uint(tlm_message, 14));
983 bits.extend(pack_uint(integrity, 1));
984 bits.push(0);
985 bits
986}
987
988fn how_data(tow: i64, alert: i64, anti_spoof: i64, sf_id: i64) -> WordEntry {
989 let mut base = pack_uint(tow, 17);
991 base.extend(pack_uint(alert, 1));
992 base.extend(pack_uint(anti_spoof, 1));
993 base.extend(pack_uint(sf_id, 3));
994 base.extend(zeros(2));
995 WordEntry::solved(base)
996}
997
998fn prepend_headers(
1000 tlm: &[u8],
1001 opts: &LnavOptions,
1002 sf_id: i64,
1003 words: Vec<WordEntry>,
1004) -> Vec<WordEntry> {
1005 let how = how_data(
1006 opts.tow.as_i64_truncated(),
1007 opts.alert.as_i64_truncated(),
1008 opts.anti_spoof.as_i64_truncated(),
1009 sf_id,
1010 );
1011 let mut entries = Vec::with_capacity(words.len() + 2);
1012 entries.push(WordEntry::raw(tlm.to_vec()));
1013 entries.push(how);
1014 entries.extend(words);
1015 entries
1016}
1017
1018fn assemble_subframe(entries: &[WordEntry]) -> Result<Vec<u8>, LnavError> {
1023 let mut bits = Vec::with_capacity(SUBFRAME_LENGTH);
1024 let (mut d29_prev, mut d30_prev) = (0u8, 0u8);
1025
1026 for entry in entries {
1027 let data = if entry.solve {
1028 solve_tbits(&entry.data, d29_prev, d30_prev)?
1029 } else {
1030 pad24(&entry.data)
1031 };
1032 let source = pad24(&data);
1033 let par = parity(&source, d29_prev, d30_prev)?;
1034 for b in &source {
1035 bits.push(b ^ d30_prev);
1036 }
1037 bits.extend_from_slice(&par);
1038 d29_prev = par[4];
1039 d30_prev = par[5];
1040 }
1041
1042 Ok(bits)
1043}
1044
1045fn solve_tbits(data24: &[u8], d29_prev: u8, d30_prev: u8) -> Result<Vec<u8>, LnavError> {
1047 let mut base = pad24(data24);
1048 base[22] = 0;
1049 base[23] = 0;
1050 let par = parity(&base, d29_prev, d30_prev)?;
1051 let d24 = par[4];
1052 let d23 = par[5] ^ d24;
1053 base[22] = d23;
1054 base[23] = d24;
1055 Ok(base)
1056}
1057
1058fn pad24(bits: &[u8]) -> Vec<u8> {
1059 let mut out = bits.to_vec();
1060 out.resize(24, 0);
1061 out
1062}
1063
1064fn verify_subframe(bits: &[u8], sf: u8) -> Result<(), LnavError> {
1065 if bits.len() != SUBFRAME_LENGTH {
1066 return Err(LnavError::BadSubframeLength { subframe: sf });
1067 }
1068
1069 let (mut d29_prev, mut d30_prev) = (0u8, 0u8);
1070 for (idx, word) in bits.chunks(WORD_LENGTH).enumerate() {
1071 if parity_valid(word, d29_prev, d30_prev) {
1072 d29_prev = word[28];
1073 d30_prev = word[29];
1074 } else {
1075 return Err(LnavError::ParityFailed {
1076 subframe: sf,
1077 word: (idx + 1) as u8,
1078 });
1079 }
1080 }
1081 Ok(())
1082}
1083
1084fn source_words(bits: &[u8]) -> Vec<Vec<u8>> {
1086 let mut decoded = Vec::with_capacity(10);
1087 let mut d30_prev = 0u8;
1088 for word in bits.chunks(WORD_LENGTH) {
1089 let source: Vec<u8> = word[0..24].iter().map(|b| b ^ d30_prev).collect();
1090 d30_prev = word[29];
1091 decoded.push(source);
1092 }
1093 decoded.split_off(2)
1095}
1096
1097fn pack_uint(value: i64, bits: u32) -> Vec<u8> {
1100 (0..bits).rev().map(|i| ((value >> i) & 1) as u8).collect()
1101}
1102
1103fn pack_uint_scaled(value: f64, bits: u32, scale: f64) -> Vec<u8> {
1104 pack_uint(round_half_away(value / scale), bits)
1105}
1106
1107fn pack_sint(value: f64, bits: u32, scale: f64) -> Vec<u8> {
1108 let int = round_half_away(value / scale);
1109 pack_twos_complement(int, bits)
1110}
1111
1112fn pack_twos_complement(int: i64, bits: u32) -> Vec<u8> {
1113 let mask = (1i64 << bits) - 1;
1114 pack_uint(int & mask, bits)
1115}
1116
1117fn bits_to_uint(bits: &[u8]) -> u64 {
1118 bits.iter().fold(0u64, |acc, &b| (acc << 1) | u64::from(b))
1119}
1120
1121fn unpack_uint_scaled(bits: &[u8], scale: f64) -> f64 {
1122 bits_to_uint(bits) as f64 * scale
1123}
1124
1125fn unpack_uint_scaled_int(bits: &[u8], scale: f64) -> i64 {
1126 round_half_away(bits_to_uint(bits) as f64 * scale)
1128}
1129
1130fn unpack_sint(bits: &[u8], scale: f64) -> f64 {
1131 bits_to_sint(bits) as f64 * scale
1132}
1133
1134fn bits_to_sint(bits: &[u8]) -> i64 {
1135 let n = bits.len() as u32;
1136 let raw = bits_to_uint(bits) as i64;
1137 if raw & (1i64 << (n - 1)) == 0 {
1138 raw
1139 } else {
1140 raw - (1i64 << n)
1141 }
1142}
1143
1144fn zeros(n: usize) -> Vec<u8> {
1145 vec![0u8; n]
1146}
1147
1148fn slice(word: &[u8], start_1based: usize, len: usize) -> &[u8] {
1150 &word[start_1based - 1..start_1based - 1 + len]
1151}
1152
1153fn xor(bits: &[u8]) -> u8 {
1154 bits.iter().fold(0u8, |acc, &b| acc ^ b)
1155}
1156
1157#[cfg(test)]
1158mod tests;