1use crate::formats::timestamp::{ShortFormat, TimestampFormat};
2use crate::formats::{LeapIndicator, Mode, PrimarySource, ReferenceIdentifier, Stratum, Version};
3use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
4
5#[derive(Debug, PartialEq, Default, Clone)]
6pub struct Packet {
7 pub li: LeapIndicator,
8 pub vn: Version,
9 pub mode: Mode,
10 pub stratum: Stratum,
11 pub poll: i8, pub precision: i8,
13 pub delay: ShortFormat,
14 pub dispersion: ShortFormat,
15 pub ref_id: ReferenceIdentifier,
16 pub ref_time: TimestampFormat,
17 pub orig_time: TimestampFormat,
18 pub recv_time: TimestampFormat,
19 pub transmit_time: TimestampFormat,
20}
21
22impl Packet {
23 pub fn new_client() -> Packet {
24 Packet {
25 mode: Mode::Client,
26 vn: Version::Ver2,
27 transmit_time: TimestampFormat::now(),
28 ..Default::default()
29 }
30 }
31
32 pub fn try_from<T: ReadBytesExt>(mut rdr: T) -> e_utils::AnyResult<Packet> {
33 let li_vn_mode = rdr.read_u8()?;
34 let li = LeapIndicator::from_repr(li_vn_mode >> 6).ok_or("From LeapIndicator")?;
35 let vn = Version::from_repr((li_vn_mode >> 3) & 0b111).ok_or("From Version")?;
36 let mode = Mode::from_repr(li_vn_mode & 0b111).ok_or("From Mode")?;
37
38 let stratum = Stratum::new(rdr.read_u8()?);
39
40 let poll = rdr.read_i8()?;
41 let precision = rdr.read_i8()?;
42 let delay = rdr.read_u32::<NetworkEndian>()?.into();
43 let dispersion = rdr.read_u32::<NetworkEndian>()?.into();
44 let ref_id_raw = rdr.read_u32::<NetworkEndian>()?.into();
45 let ref_id = if stratum.primary() {
46 let source = PrimarySource::from_repr(ref_id_raw).ok_or("From PrimarySource")?;
47 ReferenceIdentifier::Primary(source)
48 } else if stratum.secondary() {
49 ReferenceIdentifier::Secondary(ref_id_raw)
50 } else {
51 return Err(format!("Unsupported stratum: {}", stratum.get_value()).into());
52 };
53 let ref_time = rdr.read_u64::<NetworkEndian>()?.into();
54 let orig_time = rdr.read_u64::<NetworkEndian>()?.into();
55 let recv_time = rdr.read_u64::<NetworkEndian>()?.into();
56 let transmit_time = rdr.read_u64::<NetworkEndian>()?.into();
57
58 Ok(Packet {
59 li: li,
60 vn: vn,
61 mode: mode,
62 stratum: stratum,
63 poll: poll,
64 precision: precision,
65 delay: delay,
66 dispersion: dispersion,
67 ref_id: ref_id,
68 ref_time: ref_time,
69 orig_time: orig_time,
70 recv_time: recv_time,
71 transmit_time: transmit_time,
72 })
73 }
74}
75
76impl From<Packet> for Vec<u8> {
77 fn from(p: Packet) -> Vec<u8> {
78 let mut buf = Vec::with_capacity(48);
79 let mut li_vn_mode = 0;
80 li_vn_mode |= (p.li as u8) << 6;
81 li_vn_mode |= (p.vn as u8) << 3;
82 li_vn_mode |= p.mode as u8;
83 buf.push(li_vn_mode);
84 buf.push(p.stratum.get_value());
85 buf.push(p.poll as u8);
86 buf.push(p.precision as u8);
87 buf.write_u32::<NetworkEndian>(p.delay.into())
88 .expect("can't fail");
89 buf.write_u32::<NetworkEndian>(p.dispersion.into())
90 .expect("can't fail");
91 buf.write_u32::<NetworkEndian>(p.ref_id.into())
92 .expect("can't fail");
93 buf.write_u64::<NetworkEndian>(p.ref_time.into())
94 .expect("can't fail");
95 buf.write_u64::<NetworkEndian>(p.orig_time.into())
96 .expect("can't fail");
97 buf.write_u64::<NetworkEndian>(p.recv_time.into())
98 .expect("can't fail");
99 buf.write_u64::<NetworkEndian>(p.transmit_time.into())
100 .expect("can't fail");
101 buf
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::Packet;
108 use crate::formats::timestamp::{ShortFormat, TimestampFormat};
109 use crate::formats::{
110 LeapIndicator, Mode, PrimarySource, ReferenceIdentifier, Stratum, Version,
111 };
112 use std::io::Cursor;
113
114 #[test]
115 fn packet_from_bytes() {
116 let input = vec![
117 20u8, 1, 3, 240, 0, 0, 0, 0, 0, 0, 0, 24, 67, 68, 77, 65, 215, 188, 128, 105, 198, 169,
118 46, 99, 215, 187, 177, 194, 159, 47, 120, 0, 215, 188, 128, 113, 45, 236, 230, 45, 215,
119 188, 128, 113, 46, 35, 158, 108,
120 ];
121 let expected_output = Packet {
122 li: LeapIndicator::NoWarning,
123 vn: Version::Ver2,
124 mode: Mode::Server,
125 stratum: Stratum::new(1),
126 poll: 3,
127 precision: -16,
128 delay: ShortFormat { sec: 0, frac: 0 },
129 dispersion: ShortFormat { sec: 0, frac: 24 },
130 ref_id: ReferenceIdentifier::Primary(PrimarySource::CDMA),
131 ref_time: TimestampFormat {
132 sec: 3619455081,
133 frac: 3332976227,
134 },
135 orig_time: TimestampFormat {
136 sec: 3619402178,
137 frac: 2670688256,
138 },
139 recv_time: TimestampFormat {
140 sec: 3619455089,
141 frac: 770500141,
142 },
143 transmit_time: TimestampFormat {
144 sec: 3619455089,
145 frac: 774086252,
146 },
147 };
148
149 let rdr = Cursor::new(input);
150 assert_eq!(expected_output, Packet::try_from(rdr).unwrap());
151 }
152
153 #[test]
154 fn packet_to_bytes() {
155 let expected_output = vec![
156 20, 1, 3, 240, 0, 0, 0, 0, 0, 0, 0, 24, 67, 68, 77, 65, 215, 188, 128, 105, 198, 169,
157 46, 99, 215, 187, 177, 194, 159, 47, 120, 0, 215, 188, 128, 113, 45, 236, 230, 45, 215,
158 188, 128, 113, 46, 35, 158, 108,
159 ];
160 let input = Packet {
161 li: LeapIndicator::NoWarning,
162 vn: Version::Ver2,
163 mode: Mode::Server,
164 stratum: Stratum::new(1),
165 poll: 3,
166 precision: -16,
167 delay: ShortFormat { sec: 0, frac: 0 },
168 dispersion: ShortFormat { sec: 0, frac: 24 },
169 ref_id: ReferenceIdentifier::Primary(PrimarySource::CDMA),
170 ref_time: TimestampFormat {
171 sec: 3619455081,
172 frac: 3332976227,
173 },
174 orig_time: TimestampFormat {
175 sec: 3619402178,
176 frac: 2670688256,
177 },
178 recv_time: TimestampFormat {
179 sec: 3619455089,
180 frac: 770500141,
181 },
182 transmit_time: TimestampFormat {
183 sec: 3619455089,
184 frac: 774086252,
185 },
186 };
187 let output: Vec<u8> = input.into();
188 assert_eq!(output, expected_output);
189 }
190
191 #[test]
192 fn packet_conversion_roundtrip() {
193 let input = vec![
194 20, 1, 3, 240, 0, 0, 0, 0, 0, 0, 0, 24, 67, 68, 77, 65, 215, 188, 128, 105, 198, 169,
195 46, 99, 215, 187, 177, 194, 159, 47, 120, 0, 215, 188, 128, 113, 45, 236, 230, 45, 215,
196 188, 128, 113, 46, 35, 158, 108,
197 ];
198 let rdr = Cursor::new(&input);
199 let output: Vec<u8> = Packet::try_from(rdr).unwrap().into();
200 assert_eq!(input.as_slice(), output.as_slice());
201 }
202}