1use crate::{
4 message::time::{decode_gpst_epoch, encode_epoch, TimeResolution},
5 Error,
6};
7
8pub use frame::{PositionEcef3d, PositionGeo3d, TemporalSolution, Velocity3d, VelocityNED3d};
9use hifitime::{Epoch, TimeScale};
10
11mod fid;
12mod frame;
13
14use fid::FieldID;
16
17pub use frame::SolutionsFrame;
19
20#[derive(Debug, Clone, Default, PartialEq)]
21pub struct Solutions {
22 pub epoch: Epoch,
24 pub frames: Vec<SolutionsFrame>,
26}
27
28impl Iterator for Solutions {
29 type Item = SolutionsFrame;
30 fn next(&mut self) -> Option<Self::Item> {
31 self.frames.first().cloned()
32 }
33}
34
35impl Solutions {
36 const MIN_SIZE: usize = 4 + 2 + 1;
43
44 pub fn new(epoch: Epoch) -> Self {
88 Self {
89 epoch,
90 frames: Vec::with_capacity(8),
91 }
92 }
93
94 pub(crate) fn decode(mlen: usize, big_endian: bool, buf: &[u8]) -> Result<Self, Error> {
102 if mlen < Self::MIN_SIZE {
103 return Err(Error::NotEnoughBytes);
105 }
106
107 let epoch = decode_gpst_epoch(big_endian, TimeResolution::MilliSecond, buf)?;
109
110 let mut ptr = 6;
112 let mut frames = Vec::<SolutionsFrame>::with_capacity(8);
113
114 while ptr < mlen {
115 match SolutionsFrame::decode(big_endian, &buf[ptr..]) {
117 Ok(fr) => {
118 ptr += fr.encoding_size();
119 frames.push(fr);
120 },
121 Err(_) => {
122 if ptr == 6 {
123 return Err(Error::NotEnoughBytes);
125 } else {
126 break; }
128 },
129 }
130 }
131
132 Ok(Self { epoch, frames })
133 }
134
135 pub(crate) fn encode(&self, big_endian: bool, buf: &mut [u8]) -> Result<usize, Error> {
138 let size = self.encoding_size();
139 if buf.len() < size {
140 return Err(Error::NotEnoughBytes);
141 }
142
143 let t = self.epoch.to_time_scale(TimeScale::GPST);
145 let mut ptr = encode_epoch(t, TimeResolution::MilliSecond, big_endian, buf)?;
146
147 for fr in self.frames.iter() {
149 let size = fr.encode(big_endian, &mut buf[ptr..])?;
150 ptr += size;
151 }
152
153 Ok(size)
154 }
155
156 pub(crate) fn encoding_size(&self) -> usize {
159 let mut size = 6; for fr in self.frames.iter() {
161 size += fr.encoding_size(); }
163 size
164 }
165
166 pub fn with_comment(&self, comment: &str) -> Self {
169 let mut s = self.clone();
170 s.frames.push(SolutionsFrame::Comment(comment.to_string()));
171 s
172 }
173
174 pub fn with_extra_info(&self, info: &str) -> Self {
176 let mut s = self.clone();
177 if let Some(info) = s
179 .frames
180 .iter_mut()
181 .filter_map(|fr| match fr {
182 SolutionsFrame::Extra(info) => Some(info),
183 _ => None,
184 })
185 .reduce(|k, _| k)
186 {
187 *info = info.to_string(); } else {
189 s.frames.push(SolutionsFrame::Extra(info.to_string()));
190 }
191 s
192 }
193
194 pub fn with_pvt_ecef_wgs84(
196 &self,
197 x_ecef_m: f64,
198 y_ecef_m: f64,
199 z_ecef_m: f64,
200 velx_ecef_m_s: f64,
201 vely_ecef_m_s: f64,
202 velz_ecef_m_s: f64,
203 temporal_sol: TemporalSolution,
204 ) -> Self {
205 let mut s = self.clone();
206 s.frames.push(SolutionsFrame::AntennaEcefPosition(
207 PositionEcef3d::new_wgs84(x_ecef_m, y_ecef_m, z_ecef_m),
208 ));
209 s.frames
210 .push(SolutionsFrame::AntennaEcefVelocity(Velocity3d {
211 x_m_s: velx_ecef_m_s,
212 y_m_s: vely_ecef_m_s,
213 z_m_s: velz_ecef_m_s,
214 }));
215 s.frames
216 .push(SolutionsFrame::TemporalSolution(temporal_sol));
217 s
218 }
219
220 pub fn with_pvt_ecef(
222 &self,
223 x_ecef_m: f64,
224 y_ecef_m: f64,
225 z_ecef_m: f64,
226 velx_ecef_m_s: f64,
227 vely_ecef_m_s: f64,
228 velz_ecef_m_s: f64,
229 temporal_sol: TemporalSolution,
230 ellipsoid: &str,
231 ) -> Self {
232 let mut s = self.clone();
233 s.frames
234 .push(SolutionsFrame::AntennaEcefPosition(PositionEcef3d {
235 x_ecef_m,
236 y_ecef_m,
237 z_ecef_m,
238 ellipsoid: ellipsoid.to_string(),
239 }));
240 s.frames
241 .push(SolutionsFrame::AntennaEcefVelocity(Velocity3d {
242 x_m_s: velx_ecef_m_s,
243 y_m_s: vely_ecef_m_s,
244 z_m_s: velz_ecef_m_s,
245 }));
246 s.frames
247 .push(SolutionsFrame::TemporalSolution(temporal_sol));
248 s
249 }
250}
251
252#[cfg(test)]
253mod test {
254 use crate::{
255 message::record::{Solutions, TemporalSolution},
256 prelude::Epoch,
257 };
258
259 #[test]
260 fn pvt_solutions_ecef() {
261 let big_endian = true;
262
263 let t0 = Epoch::from_gpst_seconds(71.000);
264
265 let solutions = Solutions::new(t0).with_comment("Hello");
266
267 let mut buf = [0; 32];
268 let size = solutions.encode(big_endian, &mut buf).unwrap();
269
270 assert_eq!(size, solutions.encoding_size());
271 assert_eq!(
272 buf,
273 [
274 0, 0, 0, 1, 0x2a, 0xf8, 0, 5, 72, 101, 108, 108, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
275 0, 0, 0, 0, 0, 0, 0, 0, 0,
276 ]
277 );
278
279 let decoded = Solutions::decode(size, big_endian, &buf).unwrap();
280
281 assert_eq!(decoded, solutions);
282
283 let solutions = solutions.with_pvt_ecef_wgs84(
284 1.0,
285 2.0,
286 3.0,
287 4.0,
288 5.0,
289 6.0,
290 TemporalSolution {
291 offset_s: 7.0,
292 drift_s_s: None,
293 },
294 );
295
296 let mut buf = [0; 128];
297 let size = solutions.encode(big_endian, &mut buf).unwrap();
298
299 assert_eq!(size, solutions.encoding_size());
300 assert_eq!(
301 buf,
302 [
303 0, 0, 0, 1, 0x2a, 0xf8, 0, 5, 72, 101, 108, 108, 111, 1, 0, 63, 240, 0, 0, 0, 0, 0,
304 0, 64, 0, 0, 0, 0, 0, 0, 0, 64, 8, 0, 0, 0, 0, 0, 0, 3, 64, 16, 0, 0, 0, 0, 0, 0,
305 64, 20, 0, 0, 0, 0, 0, 0, 64, 24, 0, 0, 0, 0, 0, 0, 6, 64, 28, 0, 0, 0, 0, 0, 0, 0,
306 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
307 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
308 ]
309 );
310
311 let decoded = Solutions::decode(size, big_endian, &buf).unwrap();
312
313 assert_eq!(decoded, solutions);
314 }
315}