Skip to content

Commit

Permalink
Add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristopherRabotin committed Nov 16, 2023
1 parent 4a1d85c commit 6cfa546
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 94 deletions.
11 changes: 10 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,16 @@ keywords = ["attitude", "navigation", "instrument", "spacecraft", "ephemeris"]
categories = ["science", "simulation"]
readme = "README.md"
license = "MPL-2.0"
exclude = ["cspice*", "data", "analysis", ".vscode", ".github", ".venv"]
exclude = [
"cspice*",
"data",
"analysis",
".vscode",
".github",
".venv",
".vscode",
"*.sh",
]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
144 changes: 138 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# ANISE (Attitude, Navigation, Instrument, Spacecraft, Ephemeris)

ANISE, inspired by the iconic Dune universe, is a modern Rust-based library aimed at revolutionizing space navigation and ephemeris calculations. It reimagines the functionalities of the NAIF SPICE toolkit with enhanced performance, precision, and ease of use, leveraging Rust's safety and speed.
ANISE, inspired by the iconic Dune universe, reimagines the functionalities of the NAIF SPICE toolkit with enhanced performance, precision, and ease of use, leveraging Rust's safety and speed.

[Please fill out our user survey](https://7ug5imdtt8v.typeform.com/to/qYDB14Hj)
[**Please fill out our user survey**](https://7ug5imdtt8v.typeform.com/to/qYDB14Hj)

## Introduction

Expand All @@ -16,9 +16,11 @@ ANISE stands validated against the traditional SPICE toolkit, ensuring accuracy

## Features

+ **High Precision**: Achieves near machine precision in translations and minimal errors in rotations.
+ **High Precision**: Matches SPICE to machine precision in translations and minimal errors in rotations.
+ **Time System Conversions**: Extensive support for various time systems crucial in astrodynamics.
+ **Rust Efficiency**: Harnesses the speed and safety of Rust for space computations.
+ **Multi-threaded:** Yup! Forget about mutexes and race conditions you're used to in SPICE, ANISE _guarantees_ that you won't have any race conditions.
+ **Frame safety**: ANISE checks all frames translations or rotations are physically valid before performing any computation, even internally.

## Getting Started

Expand All @@ -30,14 +32,144 @@ cargo add anise

## Usage

Here's a simple example to get started with ANISE:
Please refer to the [test suite](./tests/) for comprehensive examples until I write better documentation.

### Full example

ANISE provides the ability to create Cartesian states (also simply called `Orbit`s), calculate orbital elements from them in an error free way (computations that may fail return a `Result` type), and transform these states into other frames via the loaded context, called `Almanac`, which stores all of the SPICE and ANISE files you need.

```rust
use anise::prelude::*;
// ANISE provides pre-built frames, but examples below show how to build them from their NAIF IDs.
use anise::constants::frames::{EARTH_ITRF93, EARTH_J2000};

// Initialize an empty Almanac
let ctx = Almanac::default();

// Load a SPK/BSP file
let spk = SPK::load("data/de440.bsp").unwrap();
// Load the high precision ITRF93 kernel
let bpc = BPC::load("data/earth_latest_high_prec.bpc").unwrap();
// Build the planetary constants file, which includes the gravitational parameters and the IAU low fidelity rotations
use anise::naif::kpl::parser::convert_tpc;
// Note that the PCK variable can also be serialized to disk to avoid having to rebuild it next time.
let pck = convert_tpc("data/pck00008.tpc", "data/gm_de431.tpc").unwrap();

// And add all of these to the Almanac context
let almanac = ctx
.with_spk(spk)
.unwrap()
.with_bpc(bpc)
.unwrap()
.with_planetary_data(pck);

// Let's build an orbit
// Start by grabbing a copy of the frame.
let eme2k = almanac.frame_from_uid(EARTH_J2000).unwrap();

// Define an epoch, in TDB, but you may specify UTC, TT, TAI, GPST, and more.
let epoch = Epoch::from_str("2021-10-29 12:34:56 TDB").unwrap();

// Define the orbit from its Keplerian orbital elements.
// Note that we must specify the frame of this orbit: ANISE checks all frames are valid before any translation or rotation, even internally.
let orig_state = Orbit::keplerian(
8_191.93, 1e-6, 12.85, 306.614, 314.19, 99.887_7, epoch, eme2k,
);

// Transform that orbit into another frame.
let state_itrf93 = almanac
.transform_to(orig_state, EARTH_ITRF93, Aberration::None)
.unwrap();

// The `:x` prints this orbit's Keplerian elements
println!("{orig_state:x}");
// The `:X` prints the prints the range, altitude, latitude, and longitude with respect to the planetocentric frame in floating point with units if frame is celestial,
println!("{state_itrf93:X}");

// Convert back
let from_state_itrf93_to_eme2k = almanac
.transform_to(state_itrf93, EARTH_J2000, Aberration::None)
.unwrap();

println!("{from_state_itrf93_to_eme2k}");

// Check that our return data matches the original one exactly
assert_eq!(orig_state, from_state_itrf93_to_eme2k);
```

### Loading and querying a PCK/BPC file (high fidelity rotation)

```rust
use anise::prelude::*;
use anise::constants::frames::{EARTH_ITRF93, EME2000};

let pck = "data/earth_latest_high_prec.bpc";

// Example code demonstrating a basic operation with ANISE
let bpc = BPC::load(pck).unwrap();
let almanac = Almanac::from_bpc(bpc).unwrap();

// Load the useful frame constants
use anise::constants::frames::*;

// Define an Epoch in the dynamical barycentric time scale
let epoch = Epoch::from_str("2020-11-15 12:34:56.789 TDB").unwrap();

// Query for the DCM
let dcm = almanac.rotate_from_to(EARTH_ITRF93, EME2000, epoch).unwrap();

println!("{dcm}");
```

Please refer to the [test suite](./tests/) for comprehensive examples until I write better documentation.
### Loading and querying a text PCK/KPL file (low fidelity rotation)

```rust
use anise::prelude::*;
// Load the TPC converter, which will create the ANISE representation too, in ASN1 format, that you may reuse.
use anise::naif::kpl::parser::convert_tpc;

// Note that the ASN1 ANISE format for planetary data also stores the gravity parameters, so we must convert both at once into a single ANISE file.
let planetary_data = convert_tpc("data/pck00008.tpc", "data/gm_de431.tpc").unwrap();

let almanac = Almanac {
planetary_data,
..Default::default()
};

// Load the useful frame constants
use anise::constants::frames::*;

// Define an Epoch in the dynamical barycentric time scale
let epoch = Epoch::from_str("2020-11-15 12:34:56.789 TDB").unwrap();

// Query for the DCM to the immediate parent
let dcm = almanac.rotation_to_parent(IAU_VENUS_FRAME, epoch).unwrap();

println!("{dcm}");
```

### Loading and querying an SPK/BSP file (ephemeris)

```rust
use anise::prelude::*;
use anise::constants::frames::*;

let spk = SPK::load("./data/de440s.bsp").unwrap();
let ctx = Almanac::from_spk(spk).unwrap();

// Define an Epoch in the dynamical barycentric time scale
let epoch = Epoch::from_str("2020-11-15 12:34:56.789 TDB").unwrap();

let state = ctx
.translate_from_to(
VENUS_J2000,
EARTH_MOON_BARYCENTER_J2000,
epoch,
Aberration::None,
)
.unwrap();

println!("{state}");
```

## Contributing

Expand Down
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
/*
* ANISE Toolkit
* Copyright (C) 2021-2023 Christopher Rabotin <[email protected]> et al. (cf. AUTHORS.md)
Expand Down Expand Up @@ -26,12 +27,13 @@ pub mod structure;

/// Re-export of hifitime
pub mod time {
pub use core::str::FromStr;
pub use hifitime::*;
}

pub mod prelude {
pub use crate::almanac::Almanac;
pub use crate::astro::Aberration;
pub use crate::astro::{orbit::Orbit, Aberration};
pub use crate::errors::InputOutputError;
pub use crate::frames::*;
pub use crate::math::units::*;
Expand Down
2 changes: 0 additions & 2 deletions src/naif/kpl/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@ pub fn convert_tpc<P: AsRef<Path>>(pck: P, gm: P) -> Result<PlanetaryDataSet, Da
let mut coeffs = [PhaseAngle::<0>::default(); MAX_NUT_PREC_ANGLES];
let mut num = 0;
for (i, nut_prec) in nut_prec_data.chunks(phase_deg).enumerate() {
// TODO: Convert to PhaseAngle without any nut prec angles ... or move the nut prec angles into its own field?
coeffs[i] = PhaseAngle::<0> {
offset_deg: nut_prec[0],
rate_deg: nut_prec[1],
Expand All @@ -281,7 +280,6 @@ pub fn convert_tpc<P: AsRef<Path>>(pck: P, gm: P) -> Result<PlanetaryDataSet, Da
constant.nut_prec_angles = coeffs;
};

// Todo: Switch this to a Map of ID -> constant, and another map of Name -> ID.
// Skip the DER serialization in full.
dataset_builder.push_into(&mut buf, &constant, Some(object_id), None)?;
info!("Added {object_id}");
Expand Down
24 changes: 7 additions & 17 deletions tests/almanac/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// Start by creating the ANISE planetary data
use anise::{
astro::orbit::Orbit,
constants::frames::{EARTH_ITRF93, EARTH_J2000, JUPITER_BARYCENTER_J2000},
constants::frames::{EARTH_ITRF93, EARTH_J2000},
naif::kpl::parser::convert_tpc,
prelude::{Aberration, Almanac, BPC, SPK},
prelude::{Aberration, Almanac, Orbit, BPC, SPK},
};
use core::str::FromStr;
use hifitime::Epoch;
Expand Down Expand Up @@ -63,21 +62,12 @@ fn test_state_transformation() {
println!("{orig_state:x}");
println!("{state_itrf93:X}");

// Check that doing the same from the original state matches
let from_orig_state_to_jupiter = almanac
.transform_to(orig_state, JUPITER_BARYCENTER_J2000, Aberration::None)
// Convert back
let from_state_itrf93_to_eme2k = almanac
.transform_to(state_itrf93, EARTH_J2000, Aberration::None)
.unwrap();

println!("{from_orig_state_to_jupiter}");
println!("{from_state_itrf93_to_eme2k}");

// Convert the ITRF93 to Jupiter J2000

let from_state_itrf93_to_jupiter = almanac
.transform_to(state_itrf93, JUPITER_BARYCENTER_J2000, Aberration::None)
.unwrap();

println!("{from_state_itrf93_to_jupiter}");

// TODO: Reenable this.
// assert_eq!(from_orig_state_to_jupiter, from_state_itrf93_to_jupiter);
assert_eq!(orig_state, from_state_itrf93_to_eme2k);
}
Loading

0 comments on commit 6cfa546

Please sign in to comment.