target-match 0.3.2

Given a telescope pointing and field of view, rank which catalogued sky objects fall on the frame. Matches by sky position; holds no catalogue data and does no I/O.
Documentation

target-match

Rust library that identifies which catalogued sky objects a telescope frame covers, given a pointing (right ascension / declination) and a field of view.

Matching is by angular position; object names are never used as search keys (a designation can be carried on a result for display). The crate holds no catalogue data and performs no I/O: callers supply objects by implementing the one-method SkyObject trait, and the library computes the geometry — angular separation, in-frame Membership (a fast circular radius that approximates the frame, or the exact rectangle, with optional camera rotation), tangent-plane Offsets, and deterministic ranking.

The field of view can be computed from Optics (focal length, pixel size, binning, sensor dimensions), from a pixel scale, or supplied directly via Field. Each Field constructor is fallible: non-positive or non-finite inputs return Error::InvalidOptics (the snippets below .unwrap() on known-good values). Matching itself is infallible once inputs are valid. Pointings at any epoch are precessed to J2000 before matching.

Coordinate and angle types come from the skymath crate and appear directly in this crate's API (skymath::Equatorial, skymath::Angle — with decimal and strict/lenient sexagesimal parsing); skymath is re-exported as target_match::skymath so a version-matched copy is always available.

API documentation, generated from the source on every release: docs.rs/target-match. For a task-oriented walkthrough, see the guide module.

Usage

[dependencies]
target-match = "0.3"
use skymath::{Angle, Equatorial, ParseMode};
use target_match::{rank, Constraint, Field, Optics, RadiusPolicy, SkyObject};

// Your catalogue type — target-match owns no catalogue data, and never reads the name.
struct Target { name: &'static str, ra_deg: f64, dec_deg: f64 }
impl SkyObject for Target {
    fn position(&self) -> Equatorial {
        Equatorial::j2000(Angle::from_degrees(self.ra_deg), Angle::from_degrees(self.dec_deg)).unwrap()
    }
}

let catalog = [
    Target { name: "M 31", ra_deg: 10.6847, dec_deg: 41.2688 },
    Target { name: "M 33", ra_deg: 23.4621, dec_deg: 30.6599 },
];

// Where the scope pointed (decimal degrees or sexagesimal)...
let pointing = Equatorial::parse_j2000("00:42:44.3", "+41:16:09", ParseMode::Strict).unwrap();

// ...and how much sky the frame covers (from optics, a pixel scale, or a direct FOV).
let field = Field::from_optics(Optics {
    focal_mm: 800.0, pixel_um: (3.76, 3.76), binning: (1, 1), pixels: (6248, 4176),
}).unwrap();

// Nearest catalogued object within the frame's search radius. `within` uses a
// circular approximation (here the circumscribed circle, half the diagonal); for
// the exact sensor rectangle use `Constraint::frame(&field)`.
let hits = rank(pointing, &catalog, Constraint::within(&field, RadiusPolicy::Circumscribed).nearest_one());
assert_eq!(hits[0].object.name, "M 31");

Each hit is a Match carrying the borrowed object, its separation, in-frame flag, offset, and position angle. The Query mode on the constraint selects all-within-field, nearest-one, or nearest-N. To test a single object without ranking a catalogue, call is_framed.

For a batch of frames against one catalogue, build the index once with Matcher::from_objects(..) and call .query(pointing, constraint) repeatedly. See examples/identify.rs for a runnable end-to-end demo, and the guide module for a task-oriented walkthrough.

Features

  • serde (off by default) — derives Serialize/Deserialize on the public match types, and forwards to skymath/serde for the coordinate types they embed.

Development

Requires a stable Rust toolchain (pinned via rust-toolchain.toml) and, optionally, just.

just verify   # fmt-check + clippy (-D warnings) + tests
just test
just doc

License

Licensed under the Apache License, Version 2.0.