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
//! Core astrometry for the [arcsec] plate solver.
//!
//! Given the pixels of an astronomical image and an approximate pointing, this crate
//! works out exactly where the image lies on the sky and returns a FITS-style WCS
//! solution. It implements the star-pattern matching approach introduced by ASTAP and
//! reads ASTAP's star databases (`.1476`, `.290` and `.001`). For hint-free ("blind")
//! solving it builds its own pattern index from those databases ([`mod@index`],
//! [`pipeline::index_solve()`]), and also reads Astrometry.net index files.
//!
//! The pipeline, driven by [`pipeline::solve_image`]:
//!
//! 1. **Detection** ([`detection`]) — background and noise estimation, then a
//! multi-pass star finder measuring centroid, HFD and SNR.
//! 2. **Patterns** ([`quads`]) — 4-star quads described by five distance ratios.
//! 3. **Search** ([`pipeline`]) — a square spiral around the hint; at each position
//! catalogue stars ([`catalog`]) are projected onto the tangent plane
//! ([`math::coords`]), turned into quads and matched against the image.
//! 4. **Fit and verify** ([`math::lsq`], [`wcs`]) — a least-squares plate fit,
//! checked star by star before it is accepted.
//!
//! Angles are radians throughout the API unless a name says otherwise.
//!
//! # Example
//!
//! ```no_run
//! use std::path::PathBuf;
//! use arcsec_core::ImageBuffer;
//! use arcsec_core::pipeline::{SearchSpeed, SolveMethod, SolveParams, solve_image};
//!
//! # fn load_pixels() -> ImageBuffer { ImageBuffer::new(4096, 4096) }
//! let img: ImageBuffer = load_pixels(); // row-major f32 pixels from your FITS reader
//! let params = SolveParams {
//! ra_hint: 83.82_f64.to_radians(),
//! dec_hint: (-5.39_f64).to_radians(),
//! fov: 1.2_f64.to_radians(),
//! search_radius: 10.0_f64.to_radians(),
//! quad_tolerance: 0.007,
//! hfd_min: 1.5,
//! max_stars: 500,
//! db_path: PathBuf::from("/usr/share/astap/data"),
//! db_name: "d50".into(),
//! binning: 1,
//! method: SolveMethod::Quads,
//! threads: 0,
//! speed: SearchSpeed::Auto,
//! };
//! let wcs = solve_image(&img, ¶ms)?;
//! println!(
//! "centre RA {:.4}°, Dec {:.4}°, scale {:.2}\"/px",
//! wcs.ra0.to_degrees(),
//! wcs.dec0.to_degrees(),
//! wcs.cdelt2 * 3600.0
//! );
//! # Ok::<(), arcsec_core::ArcsecError>(())
//! ```
//!
//! Progress is reported through the [`log`] crate at `info` level; install any
//! logger to see it.
//!
//! [arcsec]: https://github.com/cruzzil/arcsec
//! [`log`]: https://docs.rs/log
// Library-only API hygiene, on top of the workspace lints (which the CLI shares).
extern crate alloc;
use ;
/// Process-wide worker-thread limit. 0 = one per available core.
static MAX_THREADS: AtomicUsize = new;
/// Set the maximum number of worker threads any stage may use.
///
/// Detection bands, the background histogram, the pixel-range scan and the spiral
/// search each spawn their own workers, so a single knob has to reach all of them;
/// threading a parameter through every signature would be worse. `1` makes the whole
/// solve single-threaded, which is what you want when running many solves in
/// parallel yourself, or when profiling.
/// Resolve the thread limit: the configured value, or one per core if unset.
pub use ;
pub use ;
pub use ;
pub use ;