arcsec_core/lib.rs
1//! Core astrometry for the [arcsec] plate solver.
2//!
3//! Given the pixels of an astronomical image and an approximate pointing, this crate
4//! works out exactly where the image lies on the sky and returns a FITS-style WCS
5//! solution. It implements the star-pattern matching approach introduced by ASTAP and
6//! reads ASTAP's star databases (`.1476`, `.290` and `.001`). For hint-free ("blind")
7//! solving it builds its own pattern index from those databases ([`mod@index`],
8//! [`pipeline::index_solve()`]), and also reads Astrometry.net index files.
9//!
10//! The pipeline, driven by [`pipeline::solve_image`]:
11//!
12//! 1. **Detection** ([`detection`]) — background and noise estimation, then a
13//! multi-pass star finder measuring centroid, HFD and SNR.
14//! 2. **Patterns** ([`quads`]) — 4-star quads described by five distance ratios.
15//! 3. **Search** ([`pipeline`]) — a square spiral around the hint; at each position
16//! catalogue stars ([`catalog`]) are projected onto the tangent plane
17//! ([`math::coords`]), turned into quads and matched against the image.
18//! 4. **Fit and verify** ([`math::lsq`], [`wcs`]) — a least-squares plate fit,
19//! checked star by star before it is accepted.
20//!
21//! Angles are radians throughout the API unless a name says otherwise.
22//!
23//! # Example
24//!
25//! ```no_run
26//! use std::path::PathBuf;
27//! use arcsec_core::ImageBuffer;
28//! use arcsec_core::pipeline::{SearchSpeed, SolveMethod, SolveParams, solve_image};
29//!
30//! # fn load_pixels() -> ImageBuffer { ImageBuffer::new(4096, 4096) }
31//! let img: ImageBuffer = load_pixels(); // row-major f32 pixels from your FITS reader
32//! let params = SolveParams {
33//! ra_hint: 83.82_f64.to_radians(),
34//! dec_hint: (-5.39_f64).to_radians(),
35//! fov: 1.2_f64.to_radians(),
36//! search_radius: 10.0_f64.to_radians(),
37//! quad_tolerance: 0.007,
38//! hfd_min: 1.5,
39//! max_stars: 500,
40//! db_path: PathBuf::from("/usr/share/astap/data"),
41//! db_name: "d50".into(),
42//! binning: 1,
43//! method: SolveMethod::Quads,
44//! threads: 0,
45//! speed: SearchSpeed::Auto,
46//! };
47//! let wcs = solve_image(&img, ¶ms)?;
48//! println!(
49//! "centre RA {:.4}°, Dec {:.4}°, scale {:.2}\"/px",
50//! wcs.ra0.to_degrees(),
51//! wcs.dec0.to_degrees(),
52//! wcs.cdelt2 * 3600.0
53//! );
54//! # Ok::<(), arcsec_core::ArcsecError>(())
55//! ```
56//!
57//! Progress is reported through the [`log`] crate at `info` level; install any
58//! logger to see it.
59//!
60//! [arcsec]: https://github.com/cruzzil/arcsec
61//! [`log`]: https://docs.rs/log
62
63#![warn(missing_docs)]
64// Library-only API hygiene, on top of the workspace lints (which the CLI shares).
65#![warn(
66 clippy::must_use_candidate,
67 clippy::return_self_not_must_use,
68 clippy::missing_errors_doc,
69 clippy::missing_panics_doc,
70 clippy::doc_markdown
71)]
72
73extern crate alloc;
74
75use core::sync::atomic::{AtomicUsize, Ordering};
76
77/// Process-wide worker-thread limit. 0 = one per available core.
78static MAX_THREADS: AtomicUsize = AtomicUsize::new(0);
79
80/// Set the maximum number of worker threads any stage may use.
81///
82/// Detection bands, the background histogram, the pixel-range scan and the spiral
83/// search each spawn their own workers, so a single knob has to reach all of them;
84/// threading a parameter through every signature would be worse. `1` makes the whole
85/// solve single-threaded, which is what you want when running many solves in
86/// parallel yourself, or when profiling.
87pub fn set_max_threads(n: usize) {
88 MAX_THREADS.store(n, Ordering::Relaxed);
89}
90
91/// Resolve the thread limit: the configured value, or one per core if unset.
92#[must_use]
93pub fn max_threads() -> usize {
94 match MAX_THREADS.load(Ordering::Relaxed) {
95 0 => std::thread::available_parallelism().map_or(1, core::num::NonZero::get),
96 n => n,
97 }
98}
99
100pub mod catalog;
101pub mod detection;
102pub mod error;
103pub mod index;
104pub mod math;
105pub mod pipeline;
106pub mod quads;
107pub mod types;
108pub mod wcs;
109
110#[cfg(test)]
111mod test_support;
112
113pub use catalog::{AnetIndex, AnetIndexEntry, AnetStar, load_anet_index, peek_anet_scale};
114pub use error::{ArcsecError, Result};
115pub use pipeline::{BlindSolveParams, blind_solve};
116pub use types::{
117 ImageBuffer, MatchedStar, PairedPositions, PlateConstants, Quad, QuadList, Star, StarList,
118 WcsSolution,
119};