Skip to main content

nyx_space/od/ground_station/
asn1.rs

1/*
2    Nyx, blazing fast astrodynamics
3    Copyright (C) 2018-onwards Christopher Rabotin <christopher.rabotin@gmail.com>
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU Affero General Public License as published
7    by the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU Affero General Public License for more details.
14
15    You should have received a copy of the GNU Affero General Public License
16    along with this program.  If not, see <https://www.gnu.org/licenses/>.
17*/
18
19use super::GroundStation;
20use crate::io::ConfigRepr;
21use crate::od::msr::MeasurementType;
22use crate::od::noise::StochasticNoise;
23use der::{Decode, Encode, Reader};
24use indexmap::{IndexMap, IndexSet};
25
26impl ConfigRepr for GroundStation {}
27
28#[derive(der::Sequence)]
29struct MsrNoisePair {
30    msr_type: MeasurementType,
31    noise: StochasticNoise,
32}
33
34impl<'a> Decode<'a> for GroundStation {
35    fn decode<R: Reader<'a>>(decoder: &mut R) -> der::Result<Self> {
36        let name: String = decoder.decode()?;
37        let location = decoder.decode()?;
38        // Measurement types are stored as a sequence of measurement types
39        let msr_types_vec: Vec<MeasurementType> = decoder.decode()?;
40        let measurement_types = IndexSet::from_iter(msr_types_vec);
41
42        let light_time_correction = decoder.decode()?;
43        let relativistic_corrections = decoder.decode()?;
44
45        // The flags tell us what happens next
46        let flags: u8 = decoder.decode()?;
47
48        let doppler_config = if flags & (1 << 0) != 0 {
49            Some(decoder.decode()?)
50        } else {
51            None
52        };
53
54        let timestamp_noise_s = if flags & (1 << 1) != 0 {
55            Some(decoder.decode()?)
56        } else {
57            None
58        };
59
60        let stochastic_noises = if flags & (1 << 2) != 0 {
61            // Stochastic noises are stored as a sequence of (MeasurementType, StochasticNoise) tuples (SEQUENCE of SEQUENCE)
62            // We define a helper struct for decoding
63
64            let stochastics_vec: Vec<MsrNoisePair> = decoder.decode()?;
65            let mut map = IndexMap::new();
66            for pair in stochastics_vec {
67                map.insert(pair.msr_type, pair.noise);
68            }
69            Some(map)
70        } else {
71            None
72        };
73
74        let obstruction_body = if flags & (1 << 3) != 0 {
75            Some(decoder.decode()?)
76        } else {
77            None
78        };
79
80        Ok(GroundStation {
81            name,
82            location,
83            measurement_types,
84            doppler_config,
85            light_time_correction,
86            timestamp_noise_s,
87            stochastic_noises,
88            obstructing_body: obstruction_body,
89            relativistic_corrections,
90        })
91    }
92}
93
94impl Encode for GroundStation {
95    fn encoded_len(&self) -> der::Result<der::Length> {
96        let msr_types_vec: Vec<MeasurementType> = self.measurement_types.iter().copied().collect();
97
98        let stochastics_vec = self.stochastic_noises.as_ref().map(|map| {
99            map.iter()
100                .map(|(k, v)| MsrNoisePair {
101                    msr_type: *k,
102                    noise: *v,
103                })
104                .collect::<Vec<MsrNoisePair>>()
105        });
106
107        self.name.encoded_len()?
108            + self.location.encoded_len()?
109            + msr_types_vec.encoded_len()?
110            + self.light_time_correction.encoded_len()?
111            + self.relativistic_corrections.encoded_len()?
112            + self.available_data().encoded_len()?
113            + self.doppler_config.encoded_len()?
114            + self.timestamp_noise_s.encoded_len()?
115            + stochastics_vec.encoded_len()?
116            + self.obstructing_body.encoded_len()?
117    }
118
119    fn encode(&self, encoder: &mut impl der::Writer) -> der::Result<()> {
120        self.name.encode(encoder)?;
121        self.location.encode(encoder)?;
122
123        let msr_types_vec: Vec<MeasurementType> = self.measurement_types.iter().copied().collect();
124        msr_types_vec.encode(encoder)?;
125
126        self.light_time_correction.encode(encoder)?;
127        self.relativistic_corrections.encode(encoder)?;
128        self.available_data().encode(encoder)?;
129
130        self.doppler_config.encode(encoder)?;
131        self.timestamp_noise_s.encode(encoder)?;
132
133        let stochastics_vec = self.stochastic_noises.as_ref().map(|map| {
134            map.iter()
135                .map(|(k, v)| MsrNoisePair {
136                    msr_type: *k,
137                    noise: *v,
138                })
139                .collect::<Vec<MsrNoisePair>>()
140        });
141        stochastics_vec.encode(encoder)?;
142
143        self.obstructing_body.encode(encoder)?;
144
145        Ok(())
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152    use crate::od::ground_station::DopplerConfig;
153    use crate::od::msr::IntegrationRef;
154    use crate::od::noise::{GaussMarkov, WhiteNoise};
155    use anise::astro::{Location, TerrainMask};
156    use anise::constants::frames::IAU_EARTH_FRAME;
157    use hifitime::{TimeUnits, Unit};
158
159    #[test]
160    fn test_ground_station_asn1_roundtrip() {
161        let mut measurement_types = IndexSet::new();
162        measurement_types.insert(MeasurementType::Range);
163        measurement_types.insert(MeasurementType::Doppler);
164
165        let mut stochastics = IndexMap::new();
166        stochastics.insert(
167            MeasurementType::Range,
168            StochasticNoise {
169                bias: Some(GaussMarkov::new(1.days(), 5e-3).unwrap()),
170                white_noise: Some(WhiteNoise::constant_white_noise(1e-3)),
171            },
172        );
173        stochastics.insert(
174            MeasurementType::Doppler,
175            StochasticNoise {
176                bias: Some(GaussMarkov::new(1.days(), 5e-5).unwrap()),
177                white_noise: None,
178            },
179        );
180
181        let mut gs = GroundStation {
182            name: "Test Station".to_string(),
183            location: Location {
184                latitude_deg: 40.427_222,
185                longitude_deg: 4.250_556,
186                height_km: 0.834_939,
187                frame: IAU_EARTH_FRAME.into(),
188                terrain_mask: TerrainMask::from_flat_terrain(5.0),
189                terrain_mask_ignored: false,
190            },
191            measurement_types: measurement_types.clone(),
192            doppler_config: None,
193            light_time_correction: true,
194            timestamp_noise_s: None,
195            stochastic_noises: None,
196            obstructing_body: None,
197            relativistic_corrections: false,
198        };
199
200        // 1. Minimal GroundStation (all optional fields None)
201        let mut buf = vec![];
202        gs.encode_to_vec(&mut buf).unwrap();
203        let decoded = GroundStation::from_der(&buf).unwrap();
204        assert_eq!(decoded, gs);
205
206        // 2. With DopplerConfig default
207        gs.doppler_config = Some(DopplerConfig::default());
208        buf.clear();
209        gs.encode_to_vec(&mut buf).unwrap();
210        let decoded = GroundStation::from_der(&buf).unwrap();
211        assert_eq!(decoded, gs);
212
213        // 3. With DopplerConfig custom
214        gs.doppler_config = Some(DopplerConfig {
215            integration_time: 10 * Unit::Second,
216            integration_ref: IntegrationRef::Start,
217        });
218        buf.clear();
219        gs.encode_to_vec(&mut buf).unwrap();
220        let decoded = GroundStation::from_der(&buf).unwrap();
221        assert_eq!(decoded, gs);
222
223        // 4. With timestamp_noise_s
224        gs.timestamp_noise_s = Some(StochasticNoise {
225            bias: Some(GaussMarkov::new(10 * Unit::Second, 1e-9).unwrap()),
226            white_noise: None,
227        });
228        buf.clear();
229        gs.encode_to_vec(&mut buf).unwrap();
230        let decoded = GroundStation::from_der(&buf).unwrap();
231        assert_eq!(decoded, gs);
232
233        // 5. With stochastic_noises
234        gs.stochastic_noises = Some(stochastics);
235        buf.clear();
236        gs.encode_to_vec(&mut buf).unwrap();
237        let decoded = GroundStation::from_der(&buf).unwrap();
238        assert_eq!(decoded, gs);
239
240        // 6. With only stochastic_noises (doppler_config and timestamp_noise_s unset)
241        gs.doppler_config = None;
242        gs.timestamp_noise_s = None;
243        buf.clear();
244        gs.encode_to_vec(&mut buf).unwrap();
245        let decoded = GroundStation::from_der(&buf).unwrap();
246        assert_eq!(decoded, gs);
247
248        // 7. With obstruction_body
249        gs.obstructing_body = Some(IAU_EARTH_FRAME.into());
250        buf.clear();
251        gs.encode_to_vec(&mut buf).unwrap();
252        let decoded = GroundStation::from_der(&buf).unwrap();
253        assert_eq!(decoded, gs);
254
255        // 8. With relativistic_corrections
256        gs.relativistic_corrections = true;
257        buf.clear();
258        gs.encode_to_vec(&mut buf).unwrap();
259        let decoded = GroundStation::from_der(&buf).unwrap();
260        assert_eq!(decoded, gs);
261    }
262}