1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! NAIF/JPL SPK ephemeris support (binary DAF container + high-level access).
//!
//! This module is the entry point for reading NAIF/SPK kernels and exposing a
//! clean API to query ephemerides. Internally it follows the canonical DAF/SPK
//! layout: a binary DAF header, an ASCII human header, segment summaries,
//! per‑segment directory/footer, and per‑record Chebyshev coefficients.
//!
//! ### Units & time scales
//! * Epochs are in **ET/TDB seconds from J2000** (SPK convention),
//! * Positions are **kilometers** and velocities are **km/s**,
//! * DAF “addresses” are in **double‑precision words** (8‑byte units, 1‑based).
//!
//! ### Endianness
//! Most public NAIF kernels ship as `"LTL-IEEE"`; integers are read little‑endian.
//! If you need `"BIG-IEEE"`, dispatch the integer/float readers accordingly.
//!
//! # File layout (NAIF DAF/SPK)
//!
//! A SPK file is a DAF container made of **fixed 1024-byte records**.
//! *DAF addresses* are in **double-precision words** (DP-words = 8 bytes), 1-based.
//! One 1024-byte record contains 128 DP-words. Address 1 corresponds to the
//! **first word** of **Record #1** (the File Record).
//!
//! ```text
//! +-------------------------------------------------------------------------------------------------+
//! | DAF / SPK KERNEL |
//! +-------------------------------------------------------------------------------------------------+
//! | RECORD #1 |
//! | (FILE RECORD) |
//! +-------------------------------------------------------------------------------------------------+
//! | IDWORD[8] | "DAF/SPK " |
//! | ND(i32), NI(i32) | #doubles / #integers per summary |
//! | IFNAME[60] | Internal file name |
//! | FWARD(i32), BWARD(i32) | Record numbers (1-based) of first/last Summary Records |
//! | FREE(i32) | First free address (in DP-words) |
//! | LOCFMT[8] | "LTL-IEEE" or "BIG-IEEE" |
//! | RESERVED[603] | Padding |
//! | FTPSTR[28] | NAIF FTP sentinel |
//! +-------------------------------------------------------------------------------------------------+
//! | RECORDS #2 .. (FWARD-1) : COMMENT RECORDS |
//! | ASCII area (includes the JPL “ephemeris header”: version, dates, coverage calendar + JD, etc.) |
//! +-------------------------------------------------------------------------------------------------+
//! | RECORD #(FWARD) : SUMMARY RECORD |
//! | (and possibly subsequent ones up to BWARD if the table does not fit in a single record) |
//! +-------------------------------------------------------------------------------------------------+
//! | Control area (at the beginning of the Summary Record) |
//! | NEXT(i32), PREV(i32), NSUM(i32 or f64 depending on impl.) * |
//! | * Some parsers read NSUM as f64 for DP-word alignment |
//! | |
//! | Array of SUMMARIES (fixed size per entry = SS DP-words) |
//! | SS = ND + ceil(NI/2) (integers are packed two per DP-word) |
//! | |
//! | For SPK (ND=2, NI=6), one summary encodes: |
//! | start_epoch (f64, ET seconds from J2000) |
//! | end_epoch (f64, ET seconds from J2000) |
//! | target (i32, NAIF ID) |
//! | center (i32, NAIF ID) |
//! | frame_id (i32, e.g. J2000 = 1) |
//! | data_type (i32, e.g. Type 2 = Chebyshev position only) |
//! | initial_addr (i32, start address of segment, DP-words, 1-based) |
//! | final_addr (i32, end address of segment, DP-words, 1-based) |
//! +-------------------------------------------------------------------------------------------------+
//! | NAME RECORD(S) (optional) |
//! | Each summary may have an associated segment name. |
//! +-------------------------------------------------------------------------------------------------+
//! | ELEMENT (DATA) RECORDS |
//! | Each **SPK segment** (pointed by a summary) occupies an address range [initial..final]. |
//! | In a **Type 2** segment (Chebyshev position only), the data are a sequence of |
//! | **Ephemeris Records** of fixed size `rsize` (in DP-words). |
//! | |
//! | EPHEMERIS RECORD (Type 2) |
//! | mid (f64) : midpoint ET (s) |
//! | radius (f64) : half interval length (s) |
//! | X coeffs (f64 × ncoeff) -> position X (km) |
//! | Y coeffs (f64 × ncoeff) -> position Y (km) |
//! | Z coeffs (f64 × ncoeff) -> position Z (km) |
//! | |
//! | DIRECTORY FOOTER (4 f64) – **always stored at the very end of the segment** |
//! | init (f64) : epoch of first record (ET s) |
//! | intlen (f64) : interval length per record (s) |
//! | rsize (f64) : record size in DP-words (8 B * rsize = bytes per record) |
//! | n_records (f64) : number of records in the segment |
//! +-------------------------------------------------------------------------------------------------+
//!
//! Addressing and record geometry
//! -----------------------------
//! • 1 DP-word = 8 bytes ; 1 record = 1024 bytes = 128 DP-words.
//! • Record #k (1-based) covers addresses ((k-1)*128 + 1) .. (k*128).
//! • Byte offset for DAF address `A`: `offset_bytes = (A-1) * 8`.
//!
//! Interpolation (Type 2 segments)
//! -------------------------------
//! • Normalized time : `t = clamp( (et - mid) / radius , -1, 1 )`
//! • Position [km] : Σ c_n · T_n(t) for each axis (X,Y,Z)
//! • Velocity [km/s] : Σ c_n · T'_n(t) · (2 / radius)
//! ```
//!
//! ## Notes
//! * Most public kernels are `"LTL-IEEE"` (little-endian).
//! * Integers in summaries are packed into DP-words → `SS = ND + ceil(NI/2)`.
//! * Segment *Name Records* exist but are not required for interpolation.
//!
//! The high‑level API you are expected to use is provided by:
//! * [`naif_data`] – load a BSP and query ephemerides,
//! * [`naif_ids`] – typed NAIF identifiers (targets/centers, SPK types),
//! * [`naif_version`] – enum of known JPL kernel versions.
// DAF header reader (binary, private)
// Directory/footer per segment (private)
// Chebyshev records + interpolation (private)
// ASCII JPL header parser (private)
// Segment summary record (private)
// High-level loader (public API)
// NAIF identifiers & types (public API)
// Known JPL ephemeris versions (public API)
/// Print a hex + ASCII dump of a byte slice (debug/inspection).
///
/// Arguments
/// -----------------
/// * `data`: Byte slice to dump.
///
/// Return
/// ----------
/// * `()` – the function writes to `stdout`.
///
/// See also
/// ------------
/// * `naif_data` – for reading raw blocks before decoding (useful when debugging).
/// * `daf_header`/`summary_record` – to correlate offsets with decoded structures.