anise 0.10.0

Core of the ANISE library
Documentation
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
/*
 * ANISE Toolkit
 * Copyright (C) 2021-onward Christopher Rabotin <christop&her.rabotin@gmail.com> et al. (cf. AUTHORS.md)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 *
 * Documentation: https://nyxspace.com/
 */

/// Speed of light in kilometers per second (km/s)
pub const SPEED_OF_LIGHT_KM_S: f64 = 299_792.458;

/// Arcseconds to radians conversion factor (SOFA DAS2R).
pub const ARCSEC_TO_RAD: f64 = core::f64::consts::PI / (180.0 * 3600.0);

pub mod celestial_objects {
    use crate::{NaifId, ephemerides::EphemerisError};

    pub const SOLAR_SYSTEM_BARYCENTER: NaifId = 0;
    pub const MERCURY_BARYCENTER: NaifId = 1;
    pub const VENUS_BARYCENTER: NaifId = 2;
    pub const MERCURY: NaifId = 199;
    pub const VENUS: NaifId = 299;
    pub const EARTH_MOON_BARYCENTER: NaifId = 3;
    pub const MARS_BARYCENTER: NaifId = 4;
    pub const JUPITER_BARYCENTER: NaifId = 5;
    pub const SATURN_BARYCENTER: NaifId = 6;
    pub const URANUS_BARYCENTER: NaifId = 7;
    pub const NEPTUNE_BARYCENTER: NaifId = 8;
    pub const PLUTO_BARYCENTER: NaifId = 9;
    pub const SUN: NaifId = 10;
    pub const MOON: NaifId = 301;
    pub const EARTH: NaifId = 399;
    pub const MARS: NaifId = 499;
    pub const JUPITER: NaifId = 599;
    pub const SATURN: NaifId = 699;
    pub const URANUS: NaifId = 799;
    pub const NEPTUNE: NaifId = 899;
    pub const PLUTO: NaifId = 999;

    pub const fn celestial_name_from_id(id: NaifId) -> Option<&'static str> {
        match id {
            SOLAR_SYSTEM_BARYCENTER => Some("Solar System Barycenter"),
            MERCURY_BARYCENTER => Some("Mercury Barycenter"),
            VENUS_BARYCENTER => Some("Venus Barycenter"),
            MERCURY => Some("Mercury"),
            VENUS => Some("Venus"),
            EARTH_MOON_BARYCENTER => Some("Earth-Moon Barycenter"),
            MARS_BARYCENTER => Some("Mars Barycenter"),
            MARS => Some("Mars"),
            JUPITER_BARYCENTER => Some("Jupiter Barycenter"),
            JUPITER => Some("Jupiter"),
            SATURN_BARYCENTER => Some("Saturn Barycenter"),
            SATURN => Some("Saturn"),
            URANUS_BARYCENTER => Some("Uranus Barycenter"),
            URANUS => Some("Uranus"),
            NEPTUNE_BARYCENTER => Some("Neptune Barycenter"),
            NEPTUNE => Some("Neptune"),
            PLUTO_BARYCENTER => Some("Pluto Barycenter"),
            SUN => Some("Sun"),
            MOON => Some("Moon"),
            EARTH => Some("Earth"),
            _ => None,
        }
    }

    #[deprecated(since = "0.6.7")]
    pub fn id_to_celestial_name(name: &str) -> Result<NaifId, EphemerisError> {
        id_from_celestial_name(name)
    }

    /// Converts the provided ID to its human name. Only works for the common celestial bodies. Should be compatible with CCSDS OEM names
    pub fn id_from_celestial_name(name: &str) -> Result<NaifId, EphemerisError> {
        match name {
            "Mercury Barycenter" => Ok(MERCURY_BARYCENTER),
            "Venus Barycenter" => Ok(VENUS_BARYCENTER),
            "Mercury" => Ok(MERCURY),
            "Venus" => Ok(VENUS),
            "Earth" => Ok(EARTH),
            "Mars" => Ok(MARS),
            "Jupiter" => Ok(JUPITER),
            "Saturn" => Ok(SATURN),
            "Uranus" => Ok(URANUS),
            "Neptune" => Ok(NEPTUNE),
            "Pluto" => Ok(PLUTO),
            "Moon" => Ok(MOON),
            "Sun" => Ok(SUN),
            "Earth-Moon Barycenter" => Ok(EARTH_MOON_BARYCENTER),
            "Mars Barycenter" => Ok(MARS_BARYCENTER),
            "Jupiter Barycenter" => Ok(JUPITER_BARYCENTER),
            "Saturn Barycenter" => Ok(SATURN_BARYCENTER),
            "Uranus Barycenter" => Ok(URANUS_BARYCENTER),
            "Neptune Barycenter" => Ok(NEPTUNE_BARYCENTER),
            "Pluto Barycenter" => Ok(PLUTO_BARYCENTER),
            _ => Err(EphemerisError::NameToId {
                name: name.to_string(),
            }),
        }
    }
}

/// Defines the orientations known to ANISE and SPICE.
/// References used in the constants.
/// \[1\] Jay Lieske, ``Precession Matrix Based on IAU (1976)
/// System of Astronomical Constants,'' Astron. Astrophys.
/// 73, 282-284 (1979).
///
/// \[2\] E.M. Standish, Jr., ``Orientation of the JPL Ephemerides,
/// DE 200/LE 200, to the Dynamical Equinox of J2000,''
/// Astron. Astrophys. 114, 297-302 (1982).
///
/// \[3\] E.M. Standish, Jr., ``Conversion of Ephemeris Coordinates
/// from the B1950 System to the J2000 System,'' JPL IOM
/// 314.6-581, 24 June 1985.
///
/// \[4\] E.M. Standish, Jr., ``The Equinox Offsets of the JPL
/// Ephemeris,'' JPL IOM 314.6-929, 26 February 1988.
///
/// \[5\] Jay Lieske, ``Expressions for the Precession  Quantities
/// Based upon the IAU (1976) System of Astronomical
/// Constants'' Astron. Astrophys. 58, 1-16 (1977).
///
/// \[6\] Laura Bass and Robert Cesarone "Mars Observer Planetary
/// Constants and Models" JPL D-3444 November 1990.
///
/// \[7\] "Explanatory Supplement to the Astronomical Almanac"
///  edited by P. Kenneth Seidelmann. University Science
///  Books, 20 Edgehill Road, Mill Valley, CA 94941 (1992)
pub mod orientations {
    use crate::{NaifId, orientations::OrientationError};

    /// Earth mean equator, dynamical equinox of J2000. The root reference frame for SPICE.
    pub const J2000: NaifId = 1;
    /// Earth mean equator, dynamical equinox of B1950.
    /// The B1950 reference frame is obtained by precessing the J2000 frame backwards from Julian year 2000 to Besselian year 1950, using the 1976 IAU precession model.
    /// The rotation from B1950 to J2000 is
    /// \[ -z \]  \[ theta \]  \[ -zeta \]
    ///         3            2            3
    /// The values for z, theta, and zeta are computed from the formulas given in table 5 of \[5\].
    /// z     =  1153.04066200330"
    /// theta =  1002.26108439117"
    /// zeta  =  1152.84248596724"
    pub const B1950: NaifId = 2;
    /// Fundamental Catalog (4). The FK4 reference frame is derived from the B1950 frame by applying the equinox offset determined by Fricke.
    /// \[ 0.525" \]
    ///             3
    pub const FK4: NaifId = 3;

    /// JPL Developmental Ephemeris (118). The DE-118 reference frame is nearly identical to the FK4 frame. It is also derived from the B1950 frame.
    /// Only the offset is different
    ///
    ///  \[ 0.53155" \]
    ///                3
    ///
    /// In \[2\], Standish uses two separate rotations,
    ///
    ///   \[ 0.00073" \]  P \[ 0.5316" \]
    ///                 3                3
    ///
    /// (where P is the precession matrix used above to define the B1950 frame). The major effect of the second rotation is to correct for truncating the magnitude of the first rotation.
    /// At his suggestion, we will use the untruncated value, and stick to a single rotation.
    ///
    ///
    /// Most of the other DE historical reference frames are defined relative to either the DE-118 or B1950 frame.
    /// The values below are taken from \[4\].
    ///```text
    ///    DE number  Offset from DE-118  Offset from B1950
    ///    ---------  ------------------  -----------------
    ///           96            +0.1209"           +0.4107"
    ///          102            +0.3956"           +0.1359"
    ///          108            +0.0541"           +0.4775"
    ///          111            -0.0564"           +0.5880"
    ///          114            -0.0213"           +0.5529"
    ///          122            +0.0000"           +0.5316"
    ///          125            -0.0438"           +0.5754"
    ///          130            +0.0069"           +0.5247"
    ///```
    pub const DE118: NaifId = 4;
    pub const DE096: NaifId = 5;
    pub const DE102: NaifId = 6;
    pub const DE108: NaifId = 7;
    pub const DE111: NaifId = 8;
    pub const DE114: NaifId = 9;
    pub const DE122: NaifId = 10;
    pub const DE125: NaifId = 11;
    pub const DE130: NaifId = 12;
    /// Galactic System II. The Galactic System II reference frame is defined by the following rotations:
    ///       o            o              o
    /// \[ 327  \]  \[ 62.6  \]  \[ 282.25  \]
    ///           3            1             3
    /// In the absence of better information, we assume the rotations are relative to the FK4 frame.
    pub const GALACTIC: NaifId = 13;
    pub const DE200: NaifId = 14;
    pub const DE202: NaifId = 15;
    /// Mars Mean Equator and IAU vector of J2000. The IAU-vector at Mars is the point on the mean equator of Mars where the equator ascends through the earth mean equator.
    /// This vector is the cross product of Earth mean north with Mars mean north.
    pub const MARSIAU: NaifId = 16;
    /// Ecliptic coordinates based upon the J2000 frame.
    /// The value for the obliquity of the ecliptic at J2000 is taken from page 114  of \[7\] equation 3.222-1.
    /// This agrees with the expression given in \[5\].
    pub const ECLIPJ2000: NaifId = 17;
    /// Ecliptic coordinates based upon the B1950 frame.
    /// The value for the obliquity of the ecliptic at B1950 is taken from page 171 of \[7\].
    pub const ECLIPB1950: NaifId = 18;
    /// JPL Developmental Ephemeris. (140)
    /// The DE-140 frame is the DE-400 frame rotated:
    ///
    ///   0.9999256765384668  0.0111817701197967  0.0048589521583895
    ///  -0.0111817701797229  0.9999374816848701 -0.0000271545195858
    ///  -0.0048589520204830 -0.0000271791849815  0.9999881948535965
    ///
    /// The DE-400 frame is treated as equivalent to the J2000 frame.
    pub const DE140: NaifId = 19;

    /// JPL Developmental Ephemeris. (142)
    /// The DE-142 frame is the DE-402 frame rotated:
    ///
    ///    0.9999256765402605  0.0111817697320531  0.0048589526815484
    ///   -0.0111817697907755  0.9999374816892126 -0.0000271547693170
    ///   -0.0048589525464121 -0.0000271789392288  0.9999881948510477
    ///
    /// The DE-402 frame is treated as equivalent to the J2000 frame.
    pub const DE142: NaifId = 20;

    /// JPL Developmental Ephemeris. (143)
    /// The DE-143 frame is the DE-403 frame rotated:
    ///
    ///    0.9999256765435852  0.0111817743077255  0.0048589414674762
    ///   -0.0111817743300355  0.9999374816382505 -0.0000271622115251
    ///   -0.0048589414161348 -0.0000271713942366  0.9999881949053349
    ///
    /// The DE-403 frame is treated as equivalent to the J2000 frame.
    pub const DE143: NaifId = 21;

    /// ICRS orientation axes (International Celestial Reference System).
    ///
    /// Related to J2000 (EME2000) by the IERS 2006 frame bias of ~23 mas
    /// (~0.7 m at Earth's surface). ID 22 is the next sequential ID after
    /// the SPICE built-in inertial frames (1-21); SPICE itself does not
    /// define a separate ICRS orientation.
    pub const ICRS: NaifId = 22;

    /// SOFA `iauBi00` longitude bias, arcseconds.
    /// Source: Chapront et al. (2002); IERS Conventions 2010 Ch. 5.
    pub const FRAME_BIAS_DPSIBI_ARCSEC: f64 = -0.041_775;
    /// SOFA `iauBi00` obliquity bias, arcseconds.
    /// Source: Chapront et al. (2002); IERS Conventions 2010 Ch. 5.
    pub const FRAME_BIAS_DEPSBI_ARCSEC: f64 = -0.006_819_2;
    /// SOFA `iauBi00` right-ascension correction, arcseconds.
    /// Source: Mathews, Herring & Buffett (2002) MHB2000.
    pub const FRAME_BIAS_DRA0_ARCSEC: f64 = -0.014_6;

    /// Body fixed IAU rotation
    pub const IAU_MERCURY: NaifId = 199;
    pub const IAU_VENUS: NaifId = 299;
    /// Low fidelity Earth frame orientation by the International Astronomical Union (IAU)
    pub const IAU_EARTH: NaifId = 399;
    /// High fidelity Earth frame orientation by the NAIF, requires the "Earth high prec" BPC kernel
    pub const ITRF93: NaifId = 3000;
    /// Low fidelity Moon frame orientation by the International Astronomical Union (IAU)
    pub const IAU_MOON: NaifId = 301;
    /// High fidelity Moon Mean Earth equator orientation frame (used for cartography), requires the Moon PA BPC kernel
    pub const MOON_ME: NaifId = 31001;
    /// High fidelity Moon Principal Axes orientation frame (used for gravity field and mass concentrations), requires the Moon PA BPC kernel
    pub const MOON_PA: NaifId = 31000;
    /// High fidelity Moon Mean Earth orientation frame of the DE421 (used for cartography), requires the Moon PA BPC kernel
    pub const MOON_ME_DE421: NaifId = 31007;
    /// High fidelity Moon Mean Earth orientation frame of the DE440 (used for cartography), requires the Moon PA BPC kernel moon_pa_de440_200625.bpc.
    pub const MOON_ME_DE440_ME421: NaifId = 31009;
    /// High fidelity Moon Principal Axes orientation frame of the DE421 (used for gravity field and mass concentrations), requires the Moon PA BPC kernel
    pub const MOON_PA_DE421: NaifId = 31008;
    /// High fidelity Moon Principal Axes orientation frame of the DE440 (used for gravity field and mass concentrations), requires the Moon PA BPC kernel moon_pa_de440_200625. Note that the ID is the same as the MOON_PA_DE421.
    pub const MOON_PA_DE440: NaifId = 31008;
    pub const IAU_MARS: NaifId = 499;
    pub const IAU_JUPITER: NaifId = 599;
    pub const IAU_SATURN: NaifId = 699;
    pub const IAU_URANUS: NaifId = 799;
    pub const IAU_NEPTUNE: NaifId = 899;

    /// Angle between J2000 to solar system ecliptic J2000 ([ECLIPJ2000]), in radians (about 23.43929 degrees). Apply this rotation about the X axis (R1)
    ///
    /// Also used as SOFA's `EPS0` (obliquity at J2000.0) by J2000→ICRS frame-bias rotation in the rotate_to_parent function.
    /// Changing this constant shifts both the ECLIPJ2000 rotation and the ICRS frame bias — that is intentional, but be aware of the coupling.
    pub const J2000_TO_ECLIPJ2000_ANGLE_RAD: f64 = 0.40909280422232897;

    /// The Earth Mean of Date uses the IAU2006 Precession model.
    pub const EARTH_MOD: NaifId = 0xA0E0_0300_u32 as i32;
    /// Earth MOD with the 2000 Precession model.
    pub const EARTH_MOD_2000: NaifId = 0xA0E0_0100_u32 as i32;
    /// Earth MOD with the 1976 Precession model (legacy MOD).
    pub const EARTH_MOD_1976: NaifId = 0xA0E0_0000_u32 as i32;

    /// The Earth True of Date uses the IAU2006 Precession and Nutation models.
    pub const EARTH_TOD: NaifId = 0xA0E1_0303_u32 as i32;
    /// Earth MOD with the 2000 Precession model and the 2000A Nutation model.
    pub const EARTH_TOD_2000A: NaifId = 0xA0E1_0101_u32 as i32;
    /// Earth MOD with the 2000 Precession model and the 2000B Nutation model.
    pub const EARTH_TOD_2000B: NaifId = 0xA0E1_0102_u32 as i32;
    /// Earth MOD with the 1976 Precession model and the 1980 Nutation model.
    pub const EARTH_TOD_1980: NaifId = 0xA0E1_0000_u32 as i32;

    /// The Earth True Equator True Equinox Of Date uses the IAU2006 Precession and Nutation models.
    pub const EARTH_TEME: NaifId = 0xA0E2_0303_u32 as i32;
    /// Earth TEME with the 1976 Precession model and the 1980 Nutation model.
    pub const EARTH_TEME_LEGACY: NaifId = 0xA0E2_0000_u32 as i32;

    /// Moon Mean of Date (MOD) orientation uses the loaded IAU model from the planetary constants kernel, ignoring oscillatory terms and prime meridian.
    pub const MOON_MOD: NaifId = 0xA0B0_012D_u32 as i32;
    /// Moon True of Date (TOD) orientation uses the loaded IAU model from the planetary constants kernel, ignoring only the prime meridian.
    pub const MOON_TOD: NaifId = 0xA0B1_012D_u32 as i32;

    /// Given the frame ID, try to return a human name
    /// Source: <https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/frames.html#Appendix.%20%60%60Built%20in''%20Inertial%20Reference%20Frames>
    pub const fn orientation_name_from_id(id: NaifId) -> Option<&'static str> {
        match id {
            J2000 => Some("J2000"),
            B1950 => Some("B1950"),
            FK4 => Some("FK4"),
            GALACTIC => Some("Galactic"),
            MARSIAU => Some("Mars IAU"),
            ECLIPJ2000 => Some("ECLIPJ2000"),
            ECLIPB1950 => Some("ECLIPB1950"),
            IAU_MERCURY => Some("IAU_MERCURY"),
            IAU_VENUS => Some("IAU_VENUS"),
            IAU_EARTH => Some("IAU_EARTH"),
            IAU_MOON => Some("IAU_MOON"),
            MOON_ME => Some("MOON_ME"),
            MOON_PA => Some("MOON_PA"),
            ICRS => Some("ICRS"),
            ITRF93 => Some("ITRF93"),
            IAU_MARS => Some("IAU_MARS"),
            IAU_JUPITER => Some("IAU_JUPITER"),
            IAU_SATURN => Some("IAU_SATURN"),
            IAU_NEPTUNE => Some("IAU_NEPTUNE"),
            IAU_URANUS => Some("IAU_URANUS"),
            _ => None,
        }
    }

    #[deprecated(
        since = "0.6.7",
        note = "use id_from_orientation_name, the original function was incorrectly named"
    )]
    pub fn id_to_orientation_name(name: &str) -> Result<NaifId, OrientationError> {
        id_from_orientation_name(name)
    }

    /// Converts the provided ID to its human name. Only works for the common celestial bodies. Should be compatible with CCSDS OEM names
    pub fn id_from_orientation_name(name: &str) -> Result<NaifId, OrientationError> {
        match name {
            "J2000" | "EME2000" => Ok(J2000),
            "ICRS" | "GCRF" | "ICRF" => Ok(ICRS),
            "B1950" => Ok(B1950),
            "FK4" => Ok(FK4),
            "Galactic" => Ok(GALACTIC),
            "Mars IAU" => Ok(MARSIAU),
            "ECLIPJ2000" => Ok(ECLIPJ2000),
            "ECLIPB1950" => Ok(ECLIPB1950),
            "IAU_MERCURY" => Ok(IAU_MERCURY),
            "IAU_VENUS" => Ok(IAU_VENUS),
            "IAU_EARTH" => Ok(IAU_EARTH),
            "IAU_MOON" => Ok(IAU_MOON),
            "MOON_ME" => Ok(MOON_ME),
            "MOON_PA" => Ok(MOON_PA),
            "ITRF93" => Ok(ITRF93),
            "IAU_MARS" => Ok(IAU_MARS),
            "IAU_JUPITER" => Ok(IAU_JUPITER),
            "IAU_SATURN" => Ok(IAU_SATURN),
            "IAU_NEPTUNE" => Ok(IAU_NEPTUNE),
            "IAU_URANUS" => Ok(IAU_URANUS),
            _ => Err(OrientationError::OrientationNameToId {
                name: name.to_string(),
            }),
        }
    }
}

pub mod frames {
    use hifitime::{Duration, TimeScale};

    use crate::prelude::Frame;

    use super::{celestial_objects::*, orientations::*};

    pub const SSB_J2000: Frame = Frame::new(SOLAR_SYSTEM_BARYCENTER, J2000);
    pub const MERCURY_J2000: Frame = Frame::new(MERCURY_BARYCENTER, J2000);
    pub const VENUS_J2000: Frame = Frame::new(VENUS_BARYCENTER, J2000);
    pub const EARTH_MOON_BARYCENTER_J2000: Frame = Frame::new(EARTH_MOON_BARYCENTER, J2000);
    /// The Mars frame is not available in the standard DE files, and requires a Mars specific BSP.
    pub const MARS_J2000: Frame = Frame::new(MARS, J2000);
    pub const MARS_BARYCENTER_J2000: Frame = Frame::new(MARS_BARYCENTER, J2000);
    pub const JUPITER_BARYCENTER_J2000: Frame = Frame::new(JUPITER_BARYCENTER, J2000);
    pub const SATURN_BARYCENTER_J2000: Frame = Frame::new(SATURN_BARYCENTER, J2000);
    pub const URANUS_BARYCENTER_J2000: Frame = Frame::new(URANUS_BARYCENTER, J2000);
    pub const NEPTUNE_BARYCENTER_J2000: Frame = Frame::new(NEPTUNE_BARYCENTER, J2000);
    pub const PLUTO_BARYCENTER_J2000: Frame = Frame::new(PLUTO_BARYCENTER, J2000);
    pub const SUN_J2000: Frame = Frame::new(SUN, J2000);
    pub const MOON_J2000: Frame = Frame::new(MOON, J2000);
    pub const EARTH_J2000: Frame = Frame::new(EARTH, J2000);
    pub const EME2000: Frame = Frame::new(EARTH, J2000);
    pub const EARTH_ECLIPJ2000: Frame = Frame::new(EARTH, ECLIPJ2000);
    /// Geocentric Celestial Reference Frame (Earth-centered, ICRS axes).
    pub const GCRF: Frame = Frame::new(EARTH, ICRS);
    /// International Celestial Reference Frame (SSB-centered, ICRS axes).
    pub const ICRF: Frame = Frame::new(SOLAR_SYSTEM_BARYCENTER, ICRS);

    /// Body fixed IAU rotation
    pub const IAU_MERCURY_FRAME: Frame = Frame::new(MERCURY, IAU_MERCURY);
    pub const IAU_VENUS_FRAME: Frame = Frame::new(VENUS, IAU_VENUS);
    /// Low fidelity Earth centered body fixed frame by the International Astronomical Union (IAU)
    pub const IAU_EARTH_FRAME: Frame = Frame::new(EARTH, IAU_EARTH);
    /// Low fidelity Moon centered body fixed frame by the International Astronomical Union (IAU)
    pub const IAU_MOON_FRAME: Frame = Frame::new(MOON, IAU_MOON);
    /// High fidelity Moon Mean Earth equator body fixed frame (used for cartography), requires the Moon PA BPC kernel
    pub const MOON_ME_FRAME: Frame = Frame::new(MOON, MOON_ME);
    /// High fidelity Moon Mean Earth equator body fixed frame (used for cartography), requires the Moon PA BPC kernel
    pub const MOON_ME_DE421_FRAME: Frame = Frame::new(MOON, MOON_ME_DE421);
    /// High fidelity Moon Mean Earth equator body fixed frame of the DE440 (used for cartography), requires the Moon PA BPC kernel moon_pa_de440_200625.bpc.
    pub const MOON_ME_DE440_ME421_FRAME: Frame = Frame::new(MOON, MOON_ME_DE440_ME421);
    /// High fidelity Moon Principal Axes body fixed frame (used for gravity field and mass concentrations), requires the Moon PA BPC kernel
    pub const MOON_PA_FRAME: Frame = Frame::new(MOON, MOON_PA);
    /// High fidelity Moon Mean Earth equator body fixed frame (used for cartography), requires the Moon PA BPC kernel
    pub const MOON_PA_DE421_FRAME: Frame = Frame::new(MOON, MOON_PA_DE421);
    /// High fidelity Moon Mean Earth equator body fixed frame (used for cartography), requires the Moon PA BPC kernel moon_pa_de440_200625. Note that the ID is the same as the MOON_PA_DE421.
    pub const MOON_PA_DE440_FRAME: Frame = Frame::new(MOON, MOON_PA_DE440);
    pub const IAU_MARS_FRAME: Frame = Frame::new(MARS, IAU_MARS);
    pub const IAU_JUPITER_FRAME: Frame = Frame::new(JUPITER, IAU_JUPITER);
    pub const IAU_SATURN_FRAME: Frame = Frame::new(SATURN, IAU_SATURN);
    pub const IAU_NEPTUNE_FRAME: Frame = Frame::new(NEPTUNE, IAU_NEPTUNE);
    pub const IAU_URANUS_FRAME: Frame = Frame::new(URANUS, IAU_URANUS);

    /// High fidelity Earth centered body fixed frame by the NAIF, requires the "Earth high prec" BPC kernel
    pub const EARTH_ITRF93: Frame = Frame::new(EARTH, ITRF93);

    /// The Earth Mean of Date frame uses the IAU2006 Precession model.
    pub const EARTH_MOD_FRAME: Frame = Frame::new_inertial(EARTH, EARTH_MOD);
    /// The Earth True of Date frame uses the IAU2006 Precession and Nutation models.
    pub const EARTH_TOD_FRAME: Frame = Frame::new_inertial(EARTH, EARTH_TOD);
    /// The Earth Mean of Date frame uses the IAU1976 Precession model.
    pub const EARTH_MOD_LEGACY_FRAME: Frame = Frame::new_inertial(EARTH, EARTH_MOD_1976);
    /// The Earth True of Date frame uses the 1976 Precession model and the 1980 Nutation model.
    pub const EARTH_TOD_LEGACY_FRAME: Frame = Frame::new_inertial(EARTH, EARTH_TOD_1980);
    /// The Earth True Equator True Equinox Of Date frame uses the IAU2006 Precession and Nutation models.
    pub const EARTH_TEME_FRAME: Frame = Frame::new_inertial(EARTH, EARTH_TEME);
    /// The Earth True Equator True Equinox Of Date frame uses the 1976 Precession model and the 1980 Nutation model.
    pub const EARTH_TEME_LEGACY_FRAME: Frame = Frame::new_inertial(EARTH, EARTH_TEME_LEGACY);

    /// Moon Mean of Date (MOD) orientation uses the loaded IAU model from the planetary constants kernel, ignoring oscillatory terms and prime meridian.
    pub const MOON_MOD_FRAME: Frame = Frame::new_inertial(MOON, MOON_MOD);
    /// Moon True of Date (TOD) orientation uses the loaded IAU model from the planetary constants kernel, ignoring only the prime meridian.
    pub const MOON_TOD_FRAME: Frame = Frame::new_inertial(MOON, MOON_TOD);

    /* *** Planet centric inertial frames defined below *** */

    /// Mercury Centered Inertial Frame, defined as IAU_MERCURY inertially frozen at J2000 ET/TDB
    pub const MERCURY_INERTIAL_FRAME: Frame = Frame {
        ephemeris_id: MERCURY,
        orientation_id: IAU_MERCURY,
        force_inertial: true,
        frozen_epoch: Some(hifitime::Epoch {
            duration: Duration::ZERO,
            time_scale: TimeScale::ET,
        }),
        mu_km3_s2: None,
        shape: None,
    };

    /// Venus Centered Inertial Frame, defined as IAU_VENUS inertially frozen at J2000 ET/TDB
    pub const VENUS_INERTIAL_FRAME: Frame = Frame {
        ephemeris_id: VENUS,
        orientation_id: IAU_VENUS,
        force_inertial: true,
        frozen_epoch: Some(hifitime::Epoch {
            duration: Duration::ZERO,
            time_scale: TimeScale::ET,
        }),
        mu_km3_s2: None,
        shape: None,
    };

    /// Moon Centered Inertial Frame, defined as IAU_MOON inertially frozen at J2000 ET/TDB. The default IAU_MOON frame in ANISE is the [pck00011.tpc](https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/pck00011.tpc) frame, which uses the IAU2015 model.
    /// Ansys STK users: ANISE Moon Inertial may be slightly different than the [STK counterpart](https://help.agi.com/stk/#stk/referenceFramesCBdescriptions.htm#moonInertial) because the STK implementation uses the IAU2003 model.
    pub const MOON_INERTIAL_FRAME: Frame = Frame {
        ephemeris_id: MOON,
        orientation_id: IAU_MOON,
        force_inertial: true,
        frozen_epoch: Some(hifitime::Epoch {
            duration: Duration::ZERO,
            time_scale: TimeScale::ET,
        }),
        mu_km3_s2: None,
        shape: None,
    };

    /// Mars Centered Inertial Frame, defined as IAU_MARS inertially frozen at J2000 ET/TDB
    pub const MARS_INERTIAL_FRAME: Frame = Frame {
        ephemeris_id: MARS,
        orientation_id: IAU_MARS,
        force_inertial: true,
        frozen_epoch: Some(hifitime::Epoch {
            duration: Duration::ZERO,
            time_scale: TimeScale::ET,
        }),
        mu_km3_s2: None,
        shape: None,
    };

    /// Jupiter Centered Inertial Frame, defined as IAU_JUPITER inertially frozen at J2000 ET/TDB
    pub const JUPITER_INERTIAL_FRAME: Frame = Frame {
        ephemeris_id: JUPITER,
        orientation_id: IAU_JUPITER,
        force_inertial: true,
        frozen_epoch: Some(hifitime::Epoch {
            duration: Duration::ZERO,
            time_scale: TimeScale::ET,
        }),
        mu_km3_s2: None,
        shape: None,
    };

    /// Saturn Centered Inertial Frame, defined as IAU_SATURN inertially frozen at J2000 ET/TDB
    pub const SATURN_INERTIAL_FRAME: Frame = Frame {
        ephemeris_id: SATURN,
        orientation_id: IAU_SATURN,
        force_inertial: true,
        frozen_epoch: Some(hifitime::Epoch {
            duration: Duration::ZERO,
            time_scale: TimeScale::ET,
        }),
        mu_km3_s2: None,
        shape: None,
    };

    /// Uranus Centered Inertial Frame, defined as IAU_URANUS inertially frozen at J2000 ET/TDB
    pub const URANUS_INERTIAL_FRAME: Frame = Frame {
        ephemeris_id: URANUS,
        orientation_id: IAU_URANUS,
        force_inertial: true,
        frozen_epoch: Some(hifitime::Epoch {
            duration: Duration::ZERO,
            time_scale: TimeScale::ET,
        }),
        mu_km3_s2: None,
        shape: None,
    };

    /// Neptune Centered Inertial Frame, defined as IAU_NEPTUNE inertially frozen at J2000 ET/TDB
    pub const NEPTUNE_INERTIAL_FRAME: Frame = Frame {
        ephemeris_id: NEPTUNE,
        orientation_id: IAU_NEPTUNE,
        force_inertial: true,
        frozen_epoch: Some(hifitime::Epoch {
            duration: Duration::ZERO,
            time_scale: TimeScale::ET,
        }),
        mu_km3_s2: None,
        shape: None,
    };
}

/// Typical planetary constants that aren't found in SPICE input files.
pub mod usual_planetary_constants {
    /// Mean angular velocity of the Earth in deg/s
    /// Source: G. Xu and Y. Xu, "GPS", DOI 10.1007/978-3-662-50367-6_2, 2016 (confirmed by <https://hpiers.obspm.fr/eop-pc/models/constants.html>)
    pub const MEAN_EARTH_ANGULAR_VELOCITY_DEG_S: f64 = 0.004178079012116429;
    /// Mean angular velocity of the Moon in deg/s, computed from hifitime:
    /// ```py
    /// >>> moon_period = Unit.Day*27+Unit.Hour*7+Unit.Minute*43+Unit.Second*12
    /// >>> tau/moon_period.to_seconds()
    /// 2.661698975163682e-06
    /// ```
    /// Source: <https://www.britannica.com/science/month#ref225844> via <https://en.wikipedia.org/w/index.php?title=Lunar_day&oldid=1180701337>
    pub const MEAN_MOON_ANGULAR_VELOCITY_DEG_S: f64 = 2.661_698_975_163_682e-6;
}

#[cfg(test)]
mod constants_ut {
    use crate::constants::orientations::{
        B1950, ECLIPB1950, ECLIPJ2000, FK4, J2000, MARSIAU, orientation_name_from_id,
    };

    use crate::constants::celestial_objects::*;

    #[test]
    fn orient_name_from_id() {
        assert_eq!(orientation_name_from_id(J2000).unwrap(), "J2000");
        assert_eq!(orientation_name_from_id(B1950).unwrap(), "B1950");
        assert_eq!(orientation_name_from_id(ECLIPB1950).unwrap(), "ECLIPB1950");
        assert_eq!(orientation_name_from_id(ECLIPJ2000).unwrap(), "ECLIPJ2000");
        assert_eq!(orientation_name_from_id(FK4).unwrap(), "FK4");
        assert_eq!(orientation_name_from_id(MARSIAU).unwrap(), "Mars IAU");
        assert!(orientation_name_from_id(-1).is_none());
    }

    #[test]
    fn object_name_from_id() {
        assert_eq!(
            celestial_name_from_id(SOLAR_SYSTEM_BARYCENTER).unwrap(),
            "Solar System Barycenter"
        );
        assert_eq!(celestial_name_from_id(MERCURY).unwrap(), "Mercury");
        assert_eq!(celestial_name_from_id(VENUS).unwrap(), "Venus");
        assert_eq!(
            celestial_name_from_id(EARTH_MOON_BARYCENTER).unwrap(),
            "Earth-Moon Barycenter"
        );
        assert_eq!(
            celestial_name_from_id(MARS_BARYCENTER).unwrap(),
            "Mars Barycenter"
        );
        assert_eq!(
            celestial_name_from_id(JUPITER_BARYCENTER).unwrap(),
            "Jupiter Barycenter"
        );
        assert_eq!(
            celestial_name_from_id(SATURN_BARYCENTER).unwrap(),
            "Saturn Barycenter"
        );
        assert_eq!(
            celestial_name_from_id(URANUS_BARYCENTER).unwrap(),
            "Uranus Barycenter"
        );
        assert_eq!(
            celestial_name_from_id(NEPTUNE_BARYCENTER).unwrap(),
            "Neptune Barycenter"
        );
        assert_eq!(
            celestial_name_from_id(PLUTO_BARYCENTER).unwrap(),
            "Pluto Barycenter"
        );
        assert_eq!(celestial_name_from_id(SUN).unwrap(), "Sun");
        assert_eq!(celestial_name_from_id(MOON).unwrap(), "Moon");
        assert_eq!(celestial_name_from_id(EARTH).unwrap(), "Earth");
        assert!(celestial_name_from_id(-1).is_none());
    }

    #[test]
    fn icrs_orientation_name_round_trip() {
        use crate::constants::orientations::{
            ICRS, J2000, id_from_orientation_name, orientation_name_from_id,
        };

        assert_eq!(orientation_name_from_id(ICRS).unwrap(), "ICRS");

        assert_eq!(id_from_orientation_name("ICRS").unwrap(), ICRS);
        assert_eq!(id_from_orientation_name("GCRF").unwrap(), ICRS);
        assert_eq!(id_from_orientation_name("ICRF").unwrap(), ICRS);

        assert_eq!(id_from_orientation_name("J2000").unwrap(), J2000);
        assert_eq!(id_from_orientation_name("EME2000").unwrap(), J2000);
    }
}