Skip to main content

Crate tetra3

Crate tetra3 

Source
Expand description

§tetra3

A fast, robust lost-in-space star plate solver written in Rust.

Status: Alpha — The core solver is based on well-vetted algorithms but has only been tested against a limited set of images. The API is not yet stable and may change between releases. Having said that, it has been made to work on both low-SNR images taken with a backyard camera and high-star-density images from more complex telescopes.

Given a set of star centroids extracted from a camera image, tetra3 identifies the stars against a catalog and returns the camera’s pointing direction as a quaternion — no prior attitude estimate required.

Documentation: For tutorials, concept guides, and Python API reference, see the tetra3rs documentation.

§Features

  • Lost-in-space solving — determines attitude from star patterns with no initial guess
  • Tracking mode — when an attitude hint is available (e.g. the previous frame’s solution), skip the 4-star pattern-hash phase and match centroids directly against catalog stars near the hinted boresight. Set SolveConfig::attitude_hint / SolveConfig::hint_uncertainty_rad. Succeeds with as few as 3 stars, robust to sparse / low-SNR fields, with automatic fallback to lost-in-space unless SolveConfig::strict_hint is set.
  • Fast — geometric hashing of 4-star patterns with breadth-first (brightest-first) search
  • Robust — statistical verification via binomial false-positive probability
  • Multiscale — supports a range of field-of-view scales in a single database
  • Proper motion — propagates Gaia DR3 / Hipparcos catalog positions to any observation epoch
  • Compact binary databases — databases serialize with postcard in a portable, lightweight binary format with no offset-size limit, so wide-FOV-range multiscale databases of any size load cleanly.
  • Centroid extraction — detect stars from images with local background subtraction, connected-component labeling, and quadratic sub-pixel peak refinement (image feature)
  • Camera model — unified CameraModel struct (focal length, optical center, parity, distortion) used throughout the solve and calibration pipeline
  • Distortion calibration — fit SIP polynomial or radial distortion models from one or more solved images via calibrate_camera
  • WCS output — solve results include FITS-standard WCS fields (CD matrix, CRVAL) and SolveResult::pixel_to_world / SolveResult::world_to_pixel methods
  • Stellar aberration — optional correction for the ~20″ apparent shift in star positions caused by the observer’s barycentric velocity; set SolveConfig::observer_velocity_km_s (use earth_barycentric_velocity for ground-based / Earth-orbiting observers)
  • Tested on real spacecraft imagery — successfully solves NASA TESS Full Frame Images (~12° FOV, significant optical distortion). Multi-image calibration across 10 TESS sectors achieves sub-arcsec agreement with FITS WCS solutions

§Example

use tetra3::{GenerateDatabaseConfig, SolverDatabase, SolveConfig, Centroid, SolveStatus};

// Generate a database from the Gaia catalog
let config = GenerateDatabaseConfig {
    max_fov_deg: 20.0,
    epoch_proper_motion_year: Some(2025.0),
    ..Default::default()
};
let db = SolverDatabase::generate_from_gaia("data/gaia_merged.bin", &config).unwrap();

// Save for fast loading later, or load a previously saved database
db.save_to_file("data/my_database.bin").unwrap();
let db = SolverDatabase::load_from_file("data/my_database.bin").unwrap();

// Solve from image centroids (pixel coordinates, origin at image center)
let centroids = vec![
    Centroid { x: 100.0, y: 200.0, mass: Some(50.0), cov: None },
    Centroid { x: -50.0, y: -10.0, mass: Some(45.0), cov: None },
    // ... more centroids ...
];

let solve_config = SolveConfig {
    fov_estimate_rad: (15.0_f32).to_radians(), // horizontal FOV
    image_width: 1024,
    image_height: 1024,
    fov_max_error_rad: Some((2.0_f32).to_radians()),
    ..Default::default()
};

let result = db.solve_from_centroids(&centroids, &solve_config);
if result.status == SolveStatus::MatchFound {
    let q = result.qicrs2cam.unwrap();
    println!("Attitude: {q}");
    println!("Matched {} stars in {:.1} ms",
        result.num_matches.unwrap(), result.solve_time_ms);
}

§Tracking mode

For frame-to-frame solving where each solve seeds the next, pass the prior attitude as a hint to skip the 4-star pattern-hash phase:

let config = SolveConfig {
    attitude_hint: prev.qicrs2cam,
    hint_uncertainty_rad: 1.0_f32.to_radians(),
    camera_model: prev.camera_model.clone().unwrap(),
    ..SolveConfig::new((15.0_f32).to_radians(), 1024, 1024)
};
let result = db.solve_from_centroids(&centroids, &config);

The solver projects catalog stars near the hinted boresight, nearest-neighbor matches them to centroids, and runs the same Wahba SVD + verification + WCS refine path as lost-in-space. Tracking succeeds with as few as 3 matched stars (LIS needs 4) and is robust to pattern-hash failures from sparse / low-SNR fields. On failure it falls back to lost-in-space automatically unless SolveConfig::strict_hint is true.

§Stellar aberration

Stellar aberration shifts apparent star positions by up to ~20″ due to the observer’s barycentric velocity (~30 km/s for Earth). The pattern-matching step is unaffected (inter-star angular separations are invariant to first order in v/c), but the final attitude quaternion is biased by ~20″ unless corrected.

Pass the observer’s barycentric velocity (ICRS, km/s) via SolveConfig::observer_velocity_km_s. The solver applies the classical correction s' = (s + β) / |s + β| to all catalog vectors before matching and refinement.

For ground-based observers, earth_barycentric_velocity is the whole story. For observers on LEO spacecraft, Earth-orbital velocity (~7.5 km/s, ~5″ bias) is not negligible — pass the total barycentric velocity (Earth-around-Sun + spacecraft-around-Earth) instead of just earth_barycentric_velocity alone.

use tetra3::{earth_barycentric_velocity, SolveConfig};

let v = earth_barycentric_velocity(9321.0); // days since J2000.0
let config = SolveConfig {
    observer_velocity_km_s: Some(v),
    ..SolveConfig::new((10.0_f32).to_radians(), 1024, 1024)
};

§Algorithm overview

  1. Pattern generation — select combinations of 4 bright centroids; compute 6 pairwise angular separations and normalize into 5 edge ratios (a geometric invariant)
  2. Hash lookup — quantize the edge ratios into a key and probe a precomputed hash table for matching catalog patterns
  3. Attitude estimation — solve Wahba’s problem via SVD to find the rotation from catalog (ICRS) to camera frame
  4. Verification — project nearby catalog stars into the camera frame, count matches, and accept only if the false-positive probability (binomial CDF) is below threshold
  5. Refinement — re-estimate the rotation using all matched star pairs via iterative SVD passes
  6. WCS fit — constrained 3-DOF tangent-plane refinement (rotation angle θ + CRVAL offset) with sigma-clipping, producing FITS-standard WCS output

§Credits

This crate is a Rust implementation of the tetra3 / cedar-solve algorithm:

  • tetra3 — the original Python implementation by Gustav Pettersson at ESA
  • cedar-solve — Steven Rosenthal’s C++/Rust star plate solver, which this implementation closely follows
  • Paper: G. Pettersson, “Tetra3: a fast and robust star identification algorithm,” ESA GNC Conference, 2023

This Rust implementation was developed by Steven Michael with assistance from Claude Code (Anthropic).

Re-exports§

pub use camera_model::CameraModel;
pub use error::Error;
pub use error::Result;
pub use distortion::calibrate_camera;
pub use distortion::CalibrateConfig;
pub use distortion::CalibrateResult;
pub use distortion::Distortion;
pub use distortion::DistortionModelType;
pub use distortion::PolynomialDistortion;
pub use distortion::RadialDistortion;
pub use solver::DatabaseProperties;
pub use solver::GenerateDatabaseConfig;
pub use solver::SolveConfig;
pub use solver::SolveResult;
pub use solver::SolveStatus;
pub use solver::SolverDatabase;
pub use aberration::earth_barycentric_velocity;
pub use star::*;
pub use starcatalog::*;

Modules§

aberration
Stellar aberration utilities.
camera_model
Camera intrinsics model: focal length, optical center, parity, and distortion.
distortion
Lens distortion models for correcting optical distortion in star images.
error
Error type for tetra3.
solver
Star plate solver based on the tetra3/cedar-solve algorithm.
star
starcatalog
Spatial star catalog optimized for fast cone (angular-radius) searches.

Structs§

Centroid

Type Aliases§

Matrix2
Quaternion
Vector3