deep_time/dt/
to_ccsds_bin.rs1use crate::{Dt, DtErr, DtErrKind, SEC_PER_DAYI64, Scale, an_err};
2
3impl Dt {
4 pub const CCSDS_C_AND_D_MAX_SIZE: usize = 32;
6
7 pub fn to_ccsds_c(
18 &self,
19 current: Scale,
20 n_coarse: u8,
21 n_frac: u8,
22 extension: bool,
23 ) -> Result<([u8; Self::CCSDS_C_AND_D_MAX_SIZE], usize), DtErr> {
24 if !(1..=7).contains(&n_coarse) {
25 return Err(an_err!(DtErrKind::OutOfRange, "coarse: {}", n_coarse,));
26 } else if n_frac > 10 {
27 return Err(an_err!(DtErrKind::OutOfRange, "frac: {}", n_frac));
28 }
29
30 let tai = self.to(current, Scale::TAI);
31
32 const EPOCH_OFFSET: i64 = 1_325_419_167;
33 let total_tai_seconds = tai.sec + EPOCH_OFFSET;
34
35 let frac_scaled = if n_frac == 0 {
36 0u128
37 } else {
38 let scale = 1u128 << (8 * n_frac as u32);
39 (tai.attos as u128 * scale + 500_000_000_000_000_000) / 1_000_000_000_000_000_000
40 };
41
42 let mut buf = [0u8; Self::CCSDS_C_AND_D_MAX_SIZE];
43 let mut pos = 0usize;
44
45 let needs_extension = n_coarse > 4 || n_frac > 3 || extension;
47
48 let base_coarse = if n_coarse <= 4 { n_coarse - 1 } else { 3 };
50 let base_frac = if n_frac <= 3 { n_frac } else { 3 };
51
52 let mut p1 = 0b0001_0000u8; p1 |= (base_coarse << 2) & 0b0000_1100;
55 p1 |= base_frac & 0b0000_0011;
56 if needs_extension {
57 p1 |= 0b1000_0000;
58 }
59 buf[pos] = p1;
60 pos += 1;
61
62 if needs_extension {
63 let add_coarse = n_coarse.saturating_sub(4); let add_frac = n_frac.saturating_sub(3); let mut p2 = 0u8;
68 p2 |= (add_coarse & 0b11) << 5; p2 |= (add_frac & 0b111) << 2; buf[pos] = p2;
73 pos += 1;
74 }
75
76 let coarse = total_tai_seconds as u64;
78 for i in (0..n_coarse).rev() {
79 buf[pos] = (coarse >> (i as u32 * 8)) as u8;
80 pos += 1;
81 }
82
83 for i in (0..n_frac).rev() {
85 buf[pos] = (frac_scaled >> (i as u32 * 8)) as u8;
86 pos += 1;
87 }
88
89 Ok((buf, pos))
90 }
91
92 pub fn to_ccsds_d(
97 &self,
98 current: Scale,
99 n_day: u8,
100 sub_ms_code: u8,
101 extension: bool,
102 ) -> Result<([u8; Self::CCSDS_C_AND_D_MAX_SIZE], usize), DtErr> {
103 if !matches!(n_day, 2 | 3) {
104 return Err(an_err!(DtErrKind::InvalidNumber, "n_day: {}", n_day));
105 } else if !matches!(sub_ms_code, 0 | 1 | 2) {
106 return Err(an_err!(DtErrKind::InvalidItem, "sub-millisecond code"));
107 }
108
109 let utc = self.to(current, Scale::UTC);
110
111 const EPOCH_OFFSET: i64 = 1_325_419_135;
114 let total_utc_seconds = utc.sec + EPOCH_OFFSET;
115
116 let day_count = (total_utc_seconds / SEC_PER_DAYI64) as u64;
117 let sec_of_day = (total_utc_seconds % SEC_PER_DAYI64) as u64;
118
119 let additional_ms =
121 ((utc.attos as u128 + 500_000_000_000_000) / 1_000_000_000_000_000) as u64;
122 let millis_of_day = sec_of_day * 1000 + additional_ms;
123
124 let remaining_attos_in_ms = (utc.attos as u128) % 1_000_000_000_000_000;
126
127 let frac_scaled = match sub_ms_code {
128 0 => 0u64,
129 1 => ((remaining_attos_in_ms * 65_536u128) / 1_000_000_000_000_000u128) as u64,
130 2 => {
131 const PS_SCALE: u128 = 1u128 << 32;
132 ((remaining_attos_in_ms * PS_SCALE) / 1_000_000_000_000_000u128) as u64
133 }
134 _ => return Err(an_err!(DtErrKind::InvalidItem, "sub-millisecond code")),
135 };
136
137 let mut buf = [0u8; Self::CCSDS_C_AND_D_MAX_SIZE];
138 let mut pos = 0usize;
139
140 let mut p1 = 0b0100_0000u8;
141 if extension {
142 p1 |= 0b1000_0000;
143 }
144 if n_day == 3 {
145 p1 |= 0b0000_0100;
146 }
147 p1 |= sub_ms_code;
148 buf[pos] = p1;
149 pos += 1;
150
151 if extension {
152 buf[pos] = 0;
153 pos += 1;
154 }
155
156 for i in (0..n_day).rev() {
157 buf[pos] = (day_count >> (i * 8)) as u8;
158 pos += 1;
159 }
160
161 for i in (0..4).rev() {
162 buf[pos] = (millis_of_day >> (i * 8)) as u8;
163 pos += 1;
164 }
165
166 let n_frac = match sub_ms_code {
167 0 => 0,
168 1 => 2,
169 2 => 4,
170 _ => return Err(an_err!(DtErrKind::InvalidItem, "sub-millisecond code")),
171 };
172 for i in (0..n_frac).rev() {
173 buf[pos] = (frac_scaled >> (i * 8)) as u8;
174 pos += 1;
175 }
176
177 Ok((buf, pos))
178 }
179
180 pub const CCSDS_CCS_MAX_SIZE: usize = 14; pub fn to_ccsds_ccs(
198 &self,
199 current: Scale,
200 use_doy: bool,
201 n_subsec: u8,
202 ) -> Result<([u8; Self::CCSDS_CCS_MAX_SIZE], usize), DtErr> {
203 if n_subsec > 6 {
204 return Err(an_err!(DtErrKind::OutOfRange, "n_subsec: {}", n_subsec));
205 }
206
207 let gt = if current.uses_leap_seconds() {
209 self.to_gregorian_time(current)
210 } else {
211 let utc = self.to(current, Scale::UTC);
212 utc.to_gregorian_time(Scale::UTC)
213 };
214
215 let mut buf = [0u8; Self::CCSDS_CCS_MAX_SIZE];
216 let mut pos = 0usize;
217
218 let mut p1 = 0b0101_0000u8; if use_doy {
221 p1 |= 0b0000_1000; }
223 p1 |= n_subsec & 0b0000_0111; buf[pos] = p1;
225 pos += 1;
226
227 let bcd = |val: u32| -> u8 {
229 let hi = (val / 10) as u8;
230 let lo = (val % 10) as u8;
231 (hi << 4) | lo
232 };
233
234 let year = gt.yr as u32;
236 let y_hi = year / 100;
237 let y_lo = year % 100;
238 buf[pos] = bcd(y_hi);
239 buf[pos + 1] = bcd(y_lo);
240 pos += 2;
241
242 if !use_doy {
244 buf[pos] = bcd(gt.mo as u32);
246 buf[pos + 1] = bcd(gt.day as u32);
247 } else {
248 let doy = gt.day_of_yr as u32;
250 buf[pos] = bcd(doy / 100); buf[pos + 1] = bcd(doy % 100);
252 }
253 pos += 2;
254
255 buf[pos] = bcd(gt.hr as u32);
257 buf[pos + 1] = bcd(gt.min as u32);
258 buf[pos + 2] = bcd(gt.sec as u32); pos += 3;
260
261 if n_subsec > 0 {
263 let decimal_places = (2 * n_subsec) as u32;
264 let scale = 10u128.pow(decimal_places);
265
266 let frac_scaled = ((gt.attos as u128 * scale + 500_000_000_000_000_000)
268 / 1_000_000_000_000_000_000) as u128;
269
270 let mut remaining = frac_scaled;
271 for i in (0..n_subsec).rev() {
272 let pair = (remaining % 100) as u32;
273 remaining /= 100;
274 buf[pos + i as usize] = bcd(pair);
275 }
276 pos += n_subsec as usize;
277 }
278
279 Ok((buf, pos))
280 }
281
282 #[inline]
288 pub fn to_ccsds_bin(
289 &self,
290 current: Scale,
291 ) -> Result<([u8; Self::CCSDS_C_AND_D_MAX_SIZE], usize), DtErr> {
292 if current.uses_leap_seconds() {
293 self.to_ccsds_d(current, 2, 1, false)
294 } else {
295 self.to_ccsds_c(current, 4, 4, false)
296 }
297 }
298}