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
//! # 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](https://ssmichael1.github.io/tetra3rs/).
//!
//! ## Features
//!
//! - **Lost-in-space solving** — determines attitude from star patterns with no initial guess
//! - **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 Hipparcos catalog positions to any observation epoch
//! - **Zero-copy deserialization** — databases serialize with [rkyv](https://docs.rs/rkyv)
//! for instant loading
//! - **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 RMSE <15″ and <10″ agreement with FITS WCS solutions
//!
//! ## Example
//!
//! ```no_run
//! use tetra3::{GenerateDatabaseConfig, SolverDatabase, SolveConfig, Centroid, SolveStatus};
//!
//! // Generate a database from the Hipparcos catalog
//! let config = GenerateDatabaseConfig {
//! max_fov_deg: 20.0,
//! epoch_proper_motion_year: Some(2025.0),
//! ..Default::default()
//! };
//! let db = SolverDatabase::generate_from_hipparcos("data/hip2.dat", &config).unwrap();
//!
//! // Save for fast loading later, or load a previously saved database
//! db.save_to_file("data/my_database.rkyv").unwrap();
//! let db = SolverDatabase::load_from_file("data/my_database.rkyv").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(¢roids, &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);
//! }
//! ```
//!
//! ## 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 a first-order
//! correction to all catalog vectors before matching and refinement.
//!
//! For Earth-based or Earth-orbiting observers, [`earth_barycentric_velocity`]
//! provides an approximate velocity from a circular-orbit model:
//!
//! ```no_run
//! 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**](https://github.com/esa/tetra3) — the original Python implementation by
//! Gustav Pettersson at ESA
//! - [**cedar-solve**](https://github.com/smroid/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](https://claude.ai/claude-code) (Anthropic).
//!
/// Raw star catalogs; currently Tycho-2 & Hipparcos
pub
pub use CameraModel;
pub use *;
pub use ;
pub use ;
pub use ;
pub use earth_barycentric_velocity;
pub use *;
pub use *;
// Commonly used types
// Note: 32-bit floats are sufficient for most of the math
// We switch to 64-bit for the SVD used in the final solver step,
// as 32-bit floats have shown to be insufficiently accurate for that step.
pub type Quaternion = UnitQuaternion;
pub type Vector3 = Vector3;
pub type Matrix2 = Matrix2;