28 releases
0.7.8 | Dec 18, 2023 |
---|---|
0.7.6 | Oct 18, 2023 |
0.7.4 | Nov 19, 2022 |
0.7.1 | Oct 24, 2021 |
0.4.1 | Mar 31, 2021 |
#194 in Algorithms
285 downloads per month
Used in 4 crates
1MB
606 lines
rust-spice
WOW! The complete NASA/NAIF Spice toolkit is actually usable on Rust
Intro | Requirements | Usage | In action | In development | Multi-threaded usage | Roadmap | Contributors | License
Intro
SPICE is An Observation Geometry System for Space Science Missions. Visit their website.
Requirements
- Install CSPICE library for your platform.
- Set the environment variable
CSPICE_DIR
to your CSPICE installation folder (where CSPICE subfoldersinclude
andlib
are located. You can do that in the Cargo configuration). - In the
cspice/lib
folder you might need for Unix systems to rename the static library to match standards:cspice.a
->libcspice.a
See other requirements at cspice-sys
library which provides
unsafe bindings to CSPICE.
Usage
Add the dependency rust-spice to your Cargo.toml
:
[dependencies]
rust-spice = "*" # replace * by the latest version of the crate
cspice-sys
library depends on Clang which might not be
available to your system. In this case, you can use the feature noclang
:
[dependencies]
rust-spice = {version = "*", default-features = false, features = ["noclang"] }
To enable the lock
feature (see ## Multi-threaded usage).
[dependencies]
rust-spice = {version = "*", features = ["lock"] }
In action
A nice and idiomatic interface to Spice,
use spice;
let mut kernel = spice::furnsh("/Users/gregoireh/data/spice-kernels/hera/kernels/mk/hera_study_PO_EMA_2024.tm");
let et = spice::str2et("2027-MAR-23 16:00:00");
let (position, light_time) = spice::spkpos("DIMORPHOS", et, "J2000", "NONE", "SUN");
// position -> 18.62640405424448, 21.054373008357004, -7.136291402940499
// light time -> 0.00009674257074746383
spice::kclear();
You can look for some inspirations in the core tests.
This dataset used as an example can be downloaded from here.
In development
Developing an idiomatic interface for Spice in Rust takes time, and not all functions are implemented yet. In the documentation online, a complete guide details which functions are available. If yours is not, you can always use the unsafe API which contains all cspice functions.
For instance, with the unsafe API, the example above would be,
use spice;
use std::ffi::CString;
unsafe {
let kernel = CString::new("/Users/gregoireh/data/spice-kernels/hera/kernels/mk/hera_study_PO_EMA_2024.tm").unwrap().into_raw();
spice::c::furnsh_c(kernel);
let mut et = 0.0;
let date = CString::new("2027-MAR-23 16:00:00").unwrap().into_raw();
spice::c::str2et_c(date, &mut et);
let target_c = CString::new("DIMORPHOS").unwrap().into_raw();
let frame_c = CString::new("J2000").unwrap().into_raw();
let abcorr_c = CString::new("NONE").unwrap().into_raw();
let observer_c = CString::new("SUN").unwrap().into_raw();
let mut light_time = 0.0;
let mut position = [0.0, 0.0, 0.0];
spice::c::spkpos_c(
target_c,
et,
frame_c,
abcorr_c,
observer_c,
&mut position[0],
&mut light_time,
);
spice::c::kclear_c();
}
Much less friendly.. yet it is available. I would love some help in order to complete the idiomatic development. You can raise an issue or propose a pull request for the implementation of a specific function.
Multi-threaded usage
CSPICE itself contains massive amounts of shared mutable state and is thus not thread-safe - concurrent
calls to any SPICE functions will almost always lead to crashes. To prevent this, if you need
to call SPICE functions from multiple threads, this crate provides a thread-safe API with the lock
feature. When enabled, the API is exposed in the form of associated functions on a guard singleton
SpiceLock
, which is !Sync + Send
. You can then only share this singleton and thus the methods it
provides between threads using a Mutex
, preventing concurrent API usage.
The lock exposes the neat versions of functions where available, and the raw versions for the rest. For functions which have neither, you will have to use the unsafe (and unguarded) direct C bindings. Just make sure you have the lock before calling them.
# #[cfg(feature = "lock")]
# {
use spice::SpiceLock;
// `try_acquire` will return `Err` if a lock already exists
let sl = SpiceLock::try_acquire().unwrap();
// SPICE functions are now associated functions of the lock with a `&self` arg
let mut kernel = sl.furnsh("/Users/gregoireh/data/spice-kernels/hera/kernels/mk/hera_study_PO_EMA_2024.tm");
let et = sl.str2et("2027-MAR-23 16:00:00");
let (position, light_time) = sl.spkpos("DIMORPHOS", et, "J2000", "NONE", "SUN");
sl.kclear();
# }
Roadmap
- provide a packaging of the test assets
- complete most-used API
- complete whole API
- refactoring of the procedural macros
- refactoring of Cell
Contributors
Hall of fame:
- @s-rah: #2
- @pixldemon: #6 #10
A huge thanks for their contributions!!
License
Licensed under the Apache License, Version 2.0.
Dependencies
~5–16MB
~176K SLoC