deep_time/dt/
to_bin_ccsds.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(
19 &self,
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 const EPOCH_OFFSET: i64 = 1_325_419_167;
31
32 let rem_attos = self.to_sec_ufrac();
33 let total_tai_seconds = self.to_sec64().saturating_add(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 ((rem_attos as u128) * scale + 500_000_000_000_000_000) / 1_000_000_000_000_000_000
41 };
42
43 let mut buf = [0u8; Self::CCSDS_C_AND_D_MAX_SIZE];
44 let mut pos = 0usize;
45
46 let needs_extension = n_coarse > 4 || n_frac > 3 || extension;
48
49 let base_coarse = if n_coarse <= 4 { n_coarse - 1 } else { 3 };
51 let base_frac = if n_frac <= 3 { n_frac } else { 3 };
52
53 let mut p1 = 0b0001_0000u8; p1 |= (base_coarse << 2) & 0b0000_1100;
56 p1 |= base_frac & 0b0000_0011;
57 if needs_extension {
58 p1 |= 0b1000_0000;
59 }
60 buf[pos] = p1;
61 pos += 1;
62
63 if needs_extension {
64 let add_coarse = n_coarse.saturating_sub(4); let add_frac = n_frac.saturating_sub(3); let mut p2 = 0u8;
69 p2 |= (add_coarse & 0b11) << 5; p2 |= (add_frac & 0b111) << 2; buf[pos] = p2;
74 pos += 1;
75 }
76
77 let coarse = total_tai_seconds as u64;
79 for i in (0..n_coarse).rev() {
80 buf[pos] = (coarse >> (i as u32 * 8)) as u8;
81 pos += 1;
82 }
83
84 for i in (0..n_frac).rev() {
86 buf[pos] = (frac_scaled >> (i as u32 * 8)) as u8;
87 pos += 1;
88 }
89
90 Ok((buf, pos))
91 }
92
93 pub fn to_ccsds_d(
99 &self,
100 n_day: u8,
101 sub_ms_code: u8,
102 extension: bool,
103 ) -> Result<([u8; Self::CCSDS_C_AND_D_MAX_SIZE], usize), DtErr> {
104 if !matches!(n_day, 2 | 3) {
105 return Err(an_err!(DtErrKind::InvalidNumber, "n_day: {}", n_day));
106 } else if !matches!(sub_ms_code, 0..=2) {
107 return Err(an_err!(DtErrKind::InvalidItem, "sub-millisecond code"));
108 }
109
110 let utc = self.to(Scale::UTC);
111
112 const EPOCH_OFFSET: i64 = 1_325_419_135;
113 let rem_attos = utc.to_sec_ufrac();
114 let total_utc_seconds = (Dt::i128_to_i64(utc.to_sec())).saturating_add(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 ((rem_attos as u128 + 500_000_000_000_000) / 1_000_000_000_000_000) as u64;
122
123 let millis_of_day = sec_of_day * 1000 + additional_ms;
124
125 let remaining_attos_in_ms = (rem_attos as u128) % 1_000_000_000_000_000;
127
128 let frac_scaled = match sub_ms_code {
129 0 => 0u64,
130 1 => ((remaining_attos_in_ms * 65_536u128) / 1_000_000_000_000_000u128) as u64,
131 2 => {
132 const PS_SCALE: u128 = 1u128 << 32;
133 ((remaining_attos_in_ms * PS_SCALE) / 1_000_000_000_000_000u128) as u64
134 }
135 _ => return Err(an_err!(DtErrKind::InvalidItem, "sub-millisecond code")),
136 };
137
138 let mut buf = [0u8; Self::CCSDS_C_AND_D_MAX_SIZE];
139 let mut pos = 0usize;
140
141 let mut p1 = 0b0100_0000u8;
142 if extension {
143 p1 |= 0b1000_0000;
144 }
145 if n_day == 3 {
146 p1 |= 0b0000_0100;
147 }
148 p1 |= sub_ms_code;
149 buf[pos] = p1;
150 pos += 1;
151
152 if extension {
153 buf[pos] = 0;
154 pos += 1;
155 }
156
157 for i in (0..n_day).rev() {
158 buf[pos] = (day_count >> (i * 8)) as u8;
159 pos += 1;
160 }
161
162 for i in (0..4).rev() {
163 buf[pos] = (millis_of_day >> (i * 8)) as u8;
164 pos += 1;
165 }
166
167 let n_frac = match sub_ms_code {
168 0 => 0,
169 1 => 2,
170 2 => 4,
171 _ => return Err(an_err!(DtErrKind::InvalidItem, "sub-millisecond code")),
172 };
173 for i in (0..n_frac).rev() {
174 buf[pos] = (frac_scaled >> (i * 8)) as u8;
175 pos += 1;
176 }
177
178 Ok((buf, pos))
179 }
180
181 pub const CCSDS_CCS_MAX_SIZE: usize = 14; pub fn to_ccsds_ccs(
202 &self,
203 use_doy: bool,
204 n_subsec: u8,
205 ) -> Result<([u8; Self::CCSDS_CCS_MAX_SIZE], usize), DtErr> {
206 if n_subsec > 6 {
207 return Err(an_err!(DtErrKind::OutOfRange, "n_subsec: {}", n_subsec));
208 }
209
210 let ymd = self.target(Scale::UTC).to_ymd();
212
213 let mut buf = [0u8; Self::CCSDS_CCS_MAX_SIZE];
214 let mut pos = 0usize;
215
216 let mut p1 = 0b0101_0000u8; if use_doy {
219 p1 |= 0b0000_1000; }
221 p1 |= n_subsec & 0b0000_0111; buf[pos] = p1;
223 pos += 1;
224
225 let bcd = |val: u32| -> u8 {
227 let hi = (val / 10) as u8;
228 let lo = (val % 10) as u8;
229 (hi << 4) | lo
230 };
231
232 let year = ymd.yr as u32;
234 let y_hi = year / 100;
235 let y_lo = year % 100;
236 buf[pos] = bcd(y_hi);
237 buf[pos + 1] = bcd(y_lo);
238 pos += 2;
239
240 if !use_doy {
242 buf[pos] = bcd(ymd.mo as u32);
244 buf[pos + 1] = bcd(ymd.day as u32);
245 } else {
246 let doy = ymd.day_of_yr() as u32;
248 buf[pos] = bcd(doy / 100); buf[pos + 1] = bcd(doy % 100);
250 }
251 pos += 2;
252
253 buf[pos] = bcd(ymd.hr as u32);
255 buf[pos + 1] = bcd(ymd.min as u32);
256 buf[pos + 2] = bcd(ymd.sec as u32); pos += 3;
258
259 if n_subsec > 0 {
261 let decimal_places = (2 * n_subsec) as u32;
262 let scale = 10u128.pow(decimal_places);
263
264 let frac_scaled =
266 (ymd.attos as u128 * scale + 500_000_000_000_000_000) / 1_000_000_000_000_000_000;
267
268 let mut remaining = frac_scaled;
269 for i in (0..n_subsec).rev() {
270 let pair = (remaining % 100) as u32;
271 remaining /= 100;
272 buf[pos + i as usize] = bcd(pair);
273 }
274 pos += n_subsec as usize;
275 }
276
277 Ok((buf, pos))
278 }
279
280 #[inline]
286 pub fn to_ccsds_bin(&self) -> Result<([u8; Self::CCSDS_C_AND_D_MAX_SIZE], usize), DtErr> {
287 if self.target.uses_leap_seconds() {
288 self.to_ccsds_d(2, 1, false)
289 } else {
290 self.to_ccsds_c(4, 4, false)
291 }
292 }
293}