### Generate and use star database in Rust Source: https://github.com/ssmichael1/tetra3rs/blob/main/README.md Example of generating a solver database from a star catalog, saving it to disk, and then loading it for solving. This is the initial setup for using tetra3rs in Rust. ```rust 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)?; // Save the database to disk for fast loading later db.save_to_file("data/my_database.bin")?; // ... or load a previously saved database let db = SolverDatabase::load_from_file("data/my_database.bin")?; // 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 }, // ... ]; 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); } ``` -------------------------------- ### Generate and Save Solver Database (Python) Source: https://context7.com/ssmichael1/tetra3rs/llms.txt Generates a star pattern hash-table database from the Gaia catalog in Python. This example uses the bundled catalog and saves the database for faster reloads. It demonstrates setting parameters like field-of-view range, epoch for proper motion, and star magnitude limits. ```python import tetra3rs db = tetra3rs.SolverDatabase.generate_from_gaia( max_fov_deg=20.0, min_fov_deg=5.0, epoch_proper_motion_year=2025.0, star_max_magnitude=9.0, patterns_per_lattice_field=50, verification_stars_per_fov=150, ) print(f"Database: {db.num_stars} stars, {db.num_patterns} patterns") print(f"FOV range: {db.min_fov_deg:.1f}° – {db.max_fov_deg:.1f}°") db.save_to_file("data/my_database.bin") db = tetra3rs.SolverDatabase.load_from_file("data/my_database.bin") ``` -------------------------------- ### Generate and Save Solver Database (Rust) Source: https://context7.com/ssmichael1/tetra3rs/llms.txt Generates a star pattern hash-table database from the Gaia catalog in Rust. This example requires a path to the binary catalog and saves the database for faster reloads. Configuration options for field-of-view and epoch are demonstrated. ```rust use tetra3::{GenerateDatabaseConfig, SolverDatabase}; let config = GenerateDatabaseConfig { max_fov_deg: 20.0, min_fov_deg: Some(5.0), epoch_proper_motion_year: Some(2025.0), ..Default::default() }; let db = SolverDatabase::generate_from_gaia("data/gaia_merged.bin", &config)?; db.save_to_file("data/my_database.bin")?; let db = SolverDatabase::load_from_file("data/my_database.bin")?; println!("Stars: {}, Patterns: {}", db.num_stars(), db.num_patterns()); ``` -------------------------------- ### Build tetra3rs Python Package from Source Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/getting-started/installation.md Clone the repository and install the tetra3rs Python package from source. Requires a Rust toolchain. ```sh git clone https://github.com/ssmichael1/tetra3rs.git cd tetra3rs pip install . ``` -------------------------------- ### Install Python Package Source: https://github.com/ssmichael1/tetra3rs/blob/main/CLAUDE.md Command to install the Python package in editable mode using pip, which relies on maturin for building Rust extensions. ```sh pip install -e . ``` -------------------------------- ### Create Camera Model with Radial Distortion Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/concepts/camera-model.md Instantiate a CameraModel with radial distortion coefficients. This example shows how to define and apply a Brown-Conrady model with k1 and k2 parameters. For full Brown-Conrady, include p1 and p2. ```python # Pure radial (Brown-Conrady with k1, k2, k3; tangential set to 0) distortion = tetra3rs.RadialDistortion(k1=-7e-9, k2=2e-15) # Or full Brown-Conrady with tangential / decentering coefficients: # distortion = tetra3rs.RadialDistortion(k1=-7e-9, p1=5e-7, p2=-3e-7) cam = tetra3rs.CameraModel( focal_length_px=11718.4, image_width=2048, image_height=1536, distortion=distortion, ) ``` -------------------------------- ### Install tetra3rs Python Package Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/getting-started/installation.md Install the tetra3rs Python package using pip. This is the standard method for Python installations. ```sh pip install tetra3rs ``` -------------------------------- ### Generate, Save, and Load Solver Database in Rust Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/getting-started/quickstart.md Demonstrates generating a star catalog database from Gaia, saving it to a binary file, and then reloading it. Configuration options include max FOV and epoch for proper motion. ```rust 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)?; // Save and reload db.save_to_file("data/my_database.bin")?; let db = SolverDatabase::load_from_file("data/my_database.bin")?; ``` -------------------------------- ### Add tetra3 Rust Crate Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/getting-started/installation.md Add the tetra3 Rust crate to your project using cargo. This is the standard method for Rust installations. ```sh cargo add tetra3 ``` -------------------------------- ### Load Solver Database (Python) Source: https://context7.com/ssmichael1/tetra3rs/llms.txt Loads a previously saved star pattern hash-table database from a file in Python. This allows for quick initialization of the solver without regenerating the database. ```python import tetra3rs db = tetra3rs.SolverDatabase.load_from_file("data/my_database.bin") ``` -------------------------------- ### Load Camera Model and Process Centroids Source: https://context7.com/ssmichael1/tetra3rs/llms.txt Demonstrates loading a camera model, extracting raw centroids from an image, and then either removing lens distortion before solving or letting the solver handle distortion internally. Also shows how to re-apply distortion to ideal positions for overlay. ```python cam = tetra3rs.CameraModel.load_from_file("data/my_camera.bin") distortion = cam.distortion # RadialDistortion or PolynomialDistortion # Extract raw centroids from a distorted image extraction = tetra3rs.extract_centroids(raw_image, sigma_threshold=5.0, max_centroids=150) # Remove lens distortion (distorted pixel coords → ideal/undistorted coords) undistorted = tetra3rs.undistort_centroids(extraction.centroids, distortion) # Solve with the undistorted centroids (no camera_model needed, distortion already applied) result = db.solve_from_centroids( undistorted, fov_estimate_deg=cam.fov_deg, image_width=cam.image_width, image_height=cam.image_height, ) # Or let the solver handle distortion internally via camera_model: result_auto = db.solve_from_centroids( extraction.centroids, # pass raw (distorted) centroids fov_estimate_deg=cam.fov_deg, image_shape=raw_image.shape, camera_model=cam, # solver undistorts internally ) # Re-apply distortion to projected catalog stars (ideal → distorted, for overlay) ideal_positions = [tetra3rs.Centroid(x, y) for x, y in catalog_xy_pairs] distorted_back = tetra3rs.distort_centroids(ideal_positions, distortion) ``` -------------------------------- ### CameraModel Construction and Usage Source: https://context7.com/ssmichael1/tetra3rs/llms.txt Demonstrates how to create and use the CameraModel class for representing camera intrinsics. This includes construction from a field of view or explicit parameters, performing pixel-to-tangent plane conversions, and saving/loading camera models to files. ```APIDOC ## CameraModel — Camera intrinsics: focal length, optical center, distortion Encapsulates the pinhole camera model plus optional lens distortion. Used to describe the camera to the solver and calibration routines. Can be constructed from a known field of view or with explicit parameters. Supports save/load to binary files and `pickle`. ### Construct from horizontal FOV ```python import tetra3rs cam = tetra3rs.CameraModel.from_fov(fov_deg=10.0, image_width=2048, image_height=1536) print(f"Focal length: {cam.focal_length_px:.1f} px") print(f"Pixel scale: {cam.pixel_scale() * 206265:.3f} arcsec/px") ``` ### Construct with explicit parameters ```python distortion = tetra3rs.RadialDistortion(k1=-7e-9, k2=2e-15, k3=0.0, p1=5e-7, p2=-3e-7) cam2 = tetra3rs.CameraModel( focal_length_px=11800.0, image_width=2048, image_height=2048, crpix=[12.3, -5.7], # optical center offset from image center [x, y] px parity_flip=False, # True for mirror-reflected images (e.g. some FITS) distortion=distortion, ) ``` ### Pixel ↔ tangent-plane conversion ```python xi, eta = cam2.pixel_to_tanplane(100.0, 200.0) # radians px, py = cam2.tanplane_to_pixel(xi, eta) # should round-trip ``` ### Save / load ```python cam2.save_to_file("data/my_camera.bin") cam3 = tetra3rs.CameraModel.load_from_file("data/my_camera.bin") assert abs(cam3.focal_length_px - cam2.focal_length_px) < 1e-4 ``` ### Inspect distortion model ```python if isinstance(cam3.distortion, tetra3rs.RadialDistortion): d = cam3.distortion print(f"Radial: k1={d.k1:.3e}, k2={d.k2:.3e}, k3={d.k3:.3e}") print(f"Tangential: p1={d.p1:.3e}, p2={d.p2:.3e}") elif isinstance(cam3.distortion, tetra3rs.PolynomialDistortion): d = cam3.distortion print(f"Polynomial order {d.order}, {d.num_coeffs} coeffs per axis") ``` ``` -------------------------------- ### Manually Create and Use Centroid Objects Source: https://context7.com/ssmichael1/tetra3rs/llms.txt Shows how to manually create `Centroid` objects with position, brightness, and optional covariance matrix. Demonstrates accessing centroid properties, calculating PSF semi-axes from covariance, shifting centroids, and pickle support. ```python import tetra3rs import numpy as np # Manually create centroids (e.g. from a custom detection pipeline) c1 = tetra3rs.Centroid(x=100.5, y=-200.3, brightness=4500.0) c2 = tetra3rs.Centroid(x=-50.0, y=30.7, brightness=2100.0) c3 = tetra3rs.Centroid(x=0.0, y=0.0) # no brightness # Access properties print(f"x={c1.x:.2f}, y={c1.y:.2f}, brightness={c1.brightness}") # Covariance matrix (from extract_centroids — None if not computed) extraction = tetra3rs.extract_centroids(image) c = extraction.centroids[0] if c.cov is not None: cov = c.cov # shape (2, 2): [[σ_xx, σ_xy], [σ_xy, σ_yy]] eigvals = np.linalg.eigvalsh(cov) semi_axes = np.sqrt(eigvals) print(f"PSF semi-axes: {semi_axes[0]:.2f} × {semi_axes[1]:.2f} px") # Shift a centroid (e.g. to convert from corner-origin to center-origin) image_center_x = extraction.image_width / 2.0 image_center_y = extraction.image_height / 2.0 # If your pipeline produces top-left origin coords, shift to image-center origin: c_shifted = c.with_offset( dx=-image_center_x, dy=-image_center_y, ) # Pickle support import pickle data = pickle.dumps(c1) c1_back = pickle.loads(data) assert abs(c1_back.x - c1.x) < 1e-6 ``` -------------------------------- ### Download Merged Gaia Catalog (Shell) Source: https://github.com/ssmichael1/tetra3rs/blob/main/CHANGELOG.md Use curl to download the pre-built merged Gaia catalog. This is a large file and may take some time. ```shell curl -o data/gaia_merged.bin "https://storage.googleapis.com/tetra3rs-testvecs/gaia_merged.bin" ``` -------------------------------- ### Constructing CameraModel in Tetra3rs Source: https://context7.com/ssmichael1/tetra3rs/llms.txt Demonstrates creating a CameraModel instance either from a specified field of view and image dimensions, or by providing explicit intrinsic parameters and distortion coefficients. Supports saving and loading camera models. ```python import tetra3rs # --- Construct from horizontal FOV --- cam = tetra3rs.CameraModel.from_fov(fov_deg=10.0, image_width=2048, image_height=1536) print(f"Focal length: {cam.focal_length_px:.1f} px") print(f"Pixel scale: {cam.pixel_scale() * 206265:.3f} arcsec/px") # --- Construct with explicit parameters --- distortion = tetra3rs.RadialDistortion(k1=-7e-9, k2=2e-15, k3=0.0, p1=5e-7, p2=-3e-7) cam2 = tetra3rs.CameraModel( focal_length_px=11800.0, image_width=2048, image_height=2048, crpix=[12.3, -5.7], # optical center offset from image center [x, y] px parity_flip=False, # True for mirror-reflected images (e.g. some FITS) distortion=distortion, ) # --- Pixel ↔ tangent-plane conversion --- xi, eta = cam2.pixel_to_tanplane(100.0, 200.0) # radians px, py = cam2.tanplane_to_pixel(xi, eta) # should round-trip # --- Save / load --- cam2.save_to_file("data/my_camera.bin") cam3 = tetra3rs.CameraModel.load_from_file("data/my_camera.bin") assert abs(cam3.focal_length_px - cam2.focal_length_px) < 1e-4 # --- Inspect distortion model --- if isinstance(cam3.distortion, tetra3rs.RadialDistortion): d = cam3.distortion print(f"Radial: k1={d.k1:.3e}, k2={d.k2:.3e}, k3={d.k3:.3e}") print(f"Tangential: p1={d.p1:.3e}, p2={d.p2:.3e}") elif isinstance(cam3.distortion, tetra3rs.PolynomialDistortion): d = cam3.distortion print(f"Polynomial order {d.order}, {d.num_coeffs} coeffs per axis") ``` -------------------------------- ### Build and Test Rust Project Source: https://github.com/ssmichael1/tetra3rs/blob/main/CLAUDE.md Commands for building the Rust project in release mode and running various test suites, including integration tests that require the 'image' feature. ```sh cargo build --release ``` ```sh cargo test # default features ``` ```sh cargo test --features image # integration tests (download test data on first run) ``` ```sh cargo test --test skyview_solve_test --features image -- --nocapture ``` ```sh cargo test --test tess_solve_test --features image -- --nocapture ``` -------------------------------- ### Create Camera Model with Explicit Parameters Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/concepts/camera-model.md Create a CameraModel instance by providing explicit intrinsic parameters. This method is useful when focal length, image dimensions, optical center offset, and parity are known. ```python cam = tetra3rs.CameraModel( focal_length_px=11718.4, image_width=2048, image_height=1536, crpix=[2.5, -1.3], # optical center offset parity_flip=True, # mirrored image ) ``` -------------------------------- ### Rust API for Tracking Mode Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/concepts/tracking.md Illustrates how to configure and use the `solve_from_centroids` function in Rust for tracking, providing an attitude hint from a previous solve. ```APIDOC ## Rust API for Tracking Mode This section demonstrates the configuration and usage of the `solve_from_centroids` function in Rust for tracking mode. ### Configuration and Solve ```rust use tetra3::{SolveConfig, SolverDatabase}; let config = SolveConfig { fov_estimate_rad: 15.0_f32.to_radians(), image_width: 1024, image_height: 1024, attitude_hint: prev_result.qicrs2cam, // Option hint_uncertainty_rad: 1.0_f32.to_radians(), camera_model: prev_result.camera_model.clone().unwrap(), ..Default::default() }; let result = db.solve_from_centroids(¢roids, &config); ``` **`SolveConfig` Fields Used for Tracking**: - `fov_estimate_rad`: Estimated field of view in radians. - `image_width`, `image_height`: Dimensions of the input image. - `attitude_hint`: An `Option` providing the attitude hint from a previous solve. This is crucial for enabling tracking mode. - `hint_uncertainty_rad`: The uncertainty of the attitude hint in radians. - `camera_model`: The `CameraModel` to use for the solve, often reused from a previous result for consistency. ``` -------------------------------- ### Run Tests in Rust Source: https://github.com/ssmichael1/tetra3rs/blob/main/CONTRIBUTING.md Commands to execute unit and integration tests for the project. Integration tests require the 'image' feature and will download test data on the first run. ```sh cargo test # unit tests ``` ```sh cargo test --features image # integration tests (downloads test data on first run) ``` -------------------------------- ### Python API for Tracking Mode Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/concepts/tracking.md Demonstrates how to use the `solve_from_centroids` function in Python for both initial lost-in-space solve and subsequent tracking solves using the previous result's attitude as a hint. ```APIDOC ## Python API for Tracking Mode This section shows how to use the `solve_from_centroids` function for both the initial lost-in-space solve and subsequent tracking solves. ### Frame 1: Lost-in-space solve ```python # Frame 1: lost-in-space result1 = db.solve_from_centroids( centroids1, fov_estimate_deg=15, image_shape=(1024, 1024), camera_model=cam ) ``` ### Frame 2: Tracking solve using previous result as hint ```python # Frame 2: seed tracking with frame 1's attitude result2 = db.solve_from_centroids( centroids2, fov_estimate_deg=15, image_shape=(1024, 1024), camera_model=result1.camera_model, # reuse refined focal length attitude_hint=result1.quaternion, # or result1.rotation_matrix_icrs_to_camera hint_uncertainty_deg=1.0, # how off the hint might be ) ``` **Parameters for `attitude_hint`**: - Accepts either a 4-element `[w, x, y, z]` quaternion (`result.quaternion` or a plain list/ndarray). - Accepts a 3x3 rotation matrix (`result.rotation_matrix_icrs_to_camera` or any 3x3 ndarray). **Quaternion Convention**: - Tetra3rs uses the **Hamilton convention** with the real part first: `q = [w, x, y, z]` where `q = w + x·i + y·j + z·k`. - This matches `numpy.quaternion`, `scipy`'s `Rotation.as_quat(scalar_first=True)`, and most aerospace literature. - It does **not** match `scipy`'s default `Rotation.as_quat()` (which is `[x, y, z, w]`). - **Unit**: `w² + x² + y² + z² = 1`. Ensure hints passed in are unit-length. - **Sense**: Rotates a vector **from the ICRS frame into the camera frame**. - **Composition**: Use Hamilton-order quaternion multiplication `q_new = q_delta ⊗ q_prior`. ``` -------------------------------- ### Download Star Catalog Source: https://github.com/ssmichael1/tetra3rs/blob/main/CONTRIBUTING.md Use this command to download the necessary star catalog for integration tests. The catalog is also downloaded automatically when running integration tests. ```sh mkdir -p data curl -o data/gaia_merged.bin "https://storage.googleapis.com/tetra3rs-testvecs/gaia_merged.bin" ``` -------------------------------- ### Create Camera Model from FOV Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/concepts/camera-model.md Instantiate a simple pinhole camera model using its field of view and image dimensions. Ensure the fov_deg parameter is correctly specified. ```python import tetra3rs # Simple pinhole model from field of view cam = tetra3rs.CameraModel.from_fov( fov_deg=10.0, image_width=2048, image_height=1536, ) ``` -------------------------------- ### Solve from Centroids with Attitude Hint Source: https://github.com/ssmichael1/tetra3rs/blob/main/README.md Use `solve_from_centroids` to determine attitude by matching catalog stars to detected centroids. Provides an attitude hint for faster convergence. The solver falls back to lost-in-space on failure unless `strict_hint` is enabled. ```python result = db.solve_from_centroids( centroids, fov_estimate_deg=15.0, image_shape=(1024, 1024), camera_model=prev_result.camera_model, attitude_hint=prev_result.quaternion, # or .rotation_matrix_icrs_to_camera hint_uncertainty_deg=1.0, ) ``` -------------------------------- ### Build tetra3rs from source Source: https://github.com/ssmichael1/tetra3rs/blob/main/README.md Build the tetra3rs library from source. This requires a Rust toolchain. ```sh pip install . ``` -------------------------------- ### Solve from Centroids (Python) Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/concepts/tracking.md First frame uses lost-in-space mode. Subsequent frames can use tracking mode by providing the previous frame's attitude as a hint. ```python # Frame 1: lost-in-space result1 = db.solve_from_centroids( centroids1, fov_estimate_deg=15, image_shape=(1024, 1024), camera_model=cam ) # Frame 2: seed tracking with frame 1's attitude result2 = db.solve_from_centroids( centroids2, fov_estimate_deg=15, image_shape=(1024, 1024), camera_model=result1.camera_model, # reuse refined focal length attitude_hint=result1.quaternion, # or result1.rotation_matrix_icrs_to_camera hint_uncertainty_deg=1.0, # how off the hint might be ) ``` -------------------------------- ### Generate Solver Database from Gaia Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/tutorials/basic-solve.ipynb Generates a solver database from the Gaia DR3 catalog. This precomputes star quad patterns for fast plate solving. Key parameters control the maximum field of view, pattern matching tolerance, and verification star density. ```python solver = t3rs.SolverDatabase.generate_from_gaia( max_fov_deg=15.0, pattern_max_error=0.005, lattice_field_oversampling=100, patterns_per_lattice_field=100, verification_stars_per_fov=1000, epoch_proper_motion_year=2018, ) ``` -------------------------------- ### Generate and Save a Solver Database Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/getting-started/quickstart.md Generates a star catalog database from the bundled Gaia catalog and saves it to a file for later use. Configure the maximum field of view and epoch for proper motion calculations. ```python import tetra3rs # Generate from the bundled Gaia catalog (installed with tetra3rs) db = tetra3rs.SolverDatabase.generate_from_gaia( max_fov_deg=20.0, epoch_proper_motion_year=2025.0, ) # Save for fast loading later db.save_to_file("my_database.bin") # ... or load a previously saved database db = tetra3rs.SolverDatabase.load_from_file("my_database.bin") ``` -------------------------------- ### Update SolverDatabase Generation (Rust) Source: https://github.com/ssmichael1/tetra3rs/blob/main/CHANGELOG.md Migrate from generating SolverDatabase from Hipparcos to Gaia. The API has changed to accommodate the new catalog source. ```rust // Before (0.3.x) let db = SolverDatabase::generate_from_hipparcos("data/hip2.dat", &config)?; // After (0.4.0) let db = SolverDatabase::generate_from_gaia("data/gaia_merged.bin", &config)?; ``` -------------------------------- ### Seeded Solve (Tracking Mode) in Python Source: https://context7.com/ssmichael1/tetra3rs/llms.txt Utilizes a prior attitude hint to speed up star identification, falling back to lost-in-space if the hint is stale. Requires at least 3 matched stars. ```python import tetra3rs import math, numpy as np db = tetra3rs.SolverDatabase.load_from_file("data/my_database.bin") # Frame 1: full lost-in-space solve result1 = db.solve_from_centroids( centroids1, fov_estimate_deg=15.0, image_shape=(1024, 1024), ) assert result1 is not None # Frame 2+: tracking mode — seed with previous frame's quaternion result2 = db.solve_from_centroids( centroids2, fov_estimate_deg=15.0, image_shape=(1024, 1024), camera_model=result1.camera_model, # reuse refined focal length attitude_hint=result1.quaternion, # [w, x, y, z], Hamilton scalar-first hint_uncertainty_deg=1.0, # catalog cone and match radius size strict_hint=False, # fall back to LIS if hint is stale ) # Alternatively pass a 3×3 rotation matrix as the hint result3 = db.solve_from_centroids( centroids3, fov_estimate_deg=15.0, image_shape=(1024, 1024), camera_model=result2.camera_model, attitude_hint=result2.rotation_matrix_icrs_to_camera, hint_uncertainty_deg=0.5, # tighter after a few frames of lock-in strict_hint=True, # fail rather than fall back (detect slews) ) if result3 is None: print("Hint failed — possible uncommanded slew, reverting to lost-in-space") result3 = db.solve_from_centroids(centroids3, fov_estimate_deg=15.0, image_shape=(1024,1024)) ``` -------------------------------- ### Generate Custom Gaia Catalog (Python) Source: https://github.com/ssmichael1/tetra3rs/blob/main/CHANGELOG.md Generate a custom Gaia catalog using a Python script. Specify the desired magnitude limit and output file. ```python python scripts/download_gaia_catalog.py --mag-limit 12.0 --output data/gaia_merged.bin ``` -------------------------------- ### Solve from Centroids (Rust) Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/concepts/tracking.md Configuration for solving from centroids in Rust, including optional attitude hint and its uncertainty. ```rust use tetra3::{SolveConfig, SolverDatabase}; let config = SolveConfig { fov_estimate_rad: 15.0_f32.to_radians(), image_width: 1024, image_height: 1024, attitude_hint: prev_result.qicrs2cam, // Option hint_uncertainty_rad: 1.0_f32.to_radians(), camera_model: prev_result.camera_model.clone().unwrap(), ..Default::default() }; let result = db.solve_from_centroids(¢roids, &config); ``` -------------------------------- ### Update SolverDatabase Generation (Python) Source: https://github.com/ssmichael1/tetra3rs/blob/main/CHANGELOG.md Migrate from generating SolverDatabase from Hipparcos to Gaia. The Python API now uses `generate_from_gaia` and utilizes a bundled catalog by default. ```python # Before (0.3.x) db = tetra3rs.SolverDatabase.generate_from_hipparcos() # After (0.4.0) db = tetra3rs.SolverDatabase.generate_from_gaia() # uses bundled gaia-catalog ``` -------------------------------- ### SolverDatabase Catalog Queries Source: https://context7.com/ssmichael1/tetra3rs/llms.txt Perform cone searches and retrieve individual stars from the internal star catalog using SolverDatabase. Demonstrates cone_search, get_star, and get_star_by_id. ```python import tetra3rs db = tetra3rs.SolverDatabase.load_from_file("data/my_database.bin") # Cone search: all catalog stars within 5° of the Orion belt region stars = db.cone_search(ra_deg=83.0, dec_deg=-1.0, radius_deg=5.0) print(f"Stars in Orion 5° cone: {len(stars)}") for s in stars[:5]: print(f" ID={s.id}, RA={s.ra_deg:.4f}°, Dec={s.dec_deg:.4f}°, mag={s.magnitude:.2f}") # Get the brightest star in the database (index 0) brightest = db.get_star(0) print(f"Brightest: ID={brightest.id}, mag={brightest.magnitude:.2f}") # Look up Sirius by Hipparcos catalog number sirius = db.get_star_by_id(32349) if sirius is not None: print(f"Sirius: RA={sirius.ra_deg:.3f}°, Dec={sirius.dec_deg:.3f}°, mag={sirius.magnitude:.2f}") # Cross-reference matched stars from a solve result if result is not None: for cat_id in result.matched_catalog_ids: star = db.get_star_by_id(int(cat_id)) if star is not None: label = "HIP" if cat_id < 0 else "Gaia" print(f" [{label}] {abs(cat_id)}: ({star.ra_deg:.4f}°, {star.dec_deg:.4f}°)") ``` -------------------------------- ### Generate Solver Database from Gaia Catalog Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/tutorials/multi-image-calibration.ipynb Generates a solver database from the Gaia catalog with parameters optimized for multi-image calibration, ensuring higher quality and denser pattern coverage. ```python solver = t3rs.SolverDatabase.generate_from_gaia( max_fov_deg=14.0, pattern_max_error=0.005, lattice_field_oversampling=100, patterns_per_lattice_field=500, verification_stars_per_fov=3000, epoch_proper_motion_year=2019, ) ``` -------------------------------- ### Generate Star Catalog with Custom Magnitude Limit (Python) Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/getting-started/installation.md Generate a custom star catalog using a Python script. Requires 'astroquery' and 'astropy' packages and allows specifying a magnitude limit and output file. ```sh pip install astroquery astropy python scripts/download_gaia_catalog.py --mag-limit 12.0 --output data/gaia_merged.bin ``` -------------------------------- ### Run TESS Integration Test Source: https://github.com/ssmichael1/tetra3rs/blob/main/README.md Execute the TESS integration test using cargo. Ensure the 'image' feature is enabled. The '--nocapture' flag is used to display test output. ```sh cargo test --test tess_solve_test --features image -- --nocapture ``` -------------------------------- ### SolverDatabase.solve_from_centroids Source: https://context7.com/ssmichael1/tetra3rs/llms.txt The main solver entry point. Accepts a list of Centroid objects or a numpy array of pixel positions. Returns a SolveResult on success or None on failure. Performs lost-in-space pattern-hash search by default; pass attitude_hint to enable tracking mode. ```APIDOC ## SolverDatabase.solve_from_centroids ### Description The main solver entry point. Accepts a list of `Centroid` objects or an Nx2/Nx3 numpy array of pixel positions (image-center origin). Returns a `SolveResult` on success or `None` on failure. Performs lost-in-space pattern-hash search by default; pass `attitude_hint` to enable tracking mode. ### Method `solve_from_centroids` ### Parameters - **centroids**: (list of `Centroid` or numpy array) - Required - A list of `Centroid` objects or an Nx2/Nx3 numpy array of pixel positions. - **fov_estimate_deg**: (float) - Optional - Estimated horizontal field of view in degrees. - **image_shape**: (tuple) - Optional - The shape of the image (height, width). - **image_width**: (int) - Optional - The width of the image. - **image_height**: (int) - Optional - The height of the image. - **fov_max_error_deg**: (float) - Optional - Maximum allowable error in the FOV estimate in degrees. - **match_radius**: (float) - Optional - Match distance as a fraction of the FOV. - **match_threshold**: (float) - Optional - Binomial false-positive probability cutoff. - **solve_timeout_ms**: (int) - Optional - Timeout for the solve operation in milliseconds. - **refine_iterations**: (int) - Optional - Number of iterative SVD refinement passes. - **attitude_hint**: (object) - Optional - Hint for enabling tracking mode. ### Request Example (Python) ```python import tetra3rs import numpy as np db = tetra3rs.SolverDatabase.load_from_file("data/my_database.bin") # --- From Centroid objects (output of extract_centroids) --- extraction = tetra3rs.extract_centroids(image, sigma_threshold=5.0, max_centroids=100) result = db.solve_from_centroids( extraction.centroids, fov_estimate_deg=10.0, image_shape=image.shape, fov_max_error_deg=3.0, match_radius=0.01, match_threshold=1e-5, solve_timeout_ms=5000, refine_iterations=2, ) # --- From a raw numpy array (Nx2 or Nx3) --- arr = np.array([[x1, y1], [x2, y2], [x3, y3], [x4, y4]], dtype=np.float64) result = db.solve_from_centroids( arr, fov_estimate_deg=15.0, image_width=1024, image_height=1024, ) ``` ### Response Example (Python) ```python if result is not None: print(f"RA: {result.ra_deg:.4f}°, Dec: {result.dec_deg:.4f}°, Roll: {result.roll_deg:.2f}°") print(f"FOV: {result.fov_deg:.3f}°") print(f"Matched {result.num_matches} stars in {result.solve_time_ms:.1f} ms") print(f"RMSE: {result.rmse_arcsec:.2f}", "P90: {result.p90e_arcsec:.2f}", "Max: {result.max_err_arcsec:.2f}") print(f"False-positive probability: {result.probability:.2e}") print(f"Parity flip: {result.parity_flip}") print(f"Matched centroid indices: {result.matched_centroids}") print(f"Matched catalog IDs: {result.matched_catalog_ids}") # negative = Hipparcos gap-fill else: print("No solution found") ``` ### Request Example (Rust) ```rust use tetra3::{SolverDatabase, SolveConfig, Centroid, SolveStatus}; let db = SolverDatabase::load_from_file("data/my_database.bin")?; 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 }, Centroid { x: 30.0, y: -180.0, mass: Some(30.0), cov: None }, Centroid { x: -200.0, y: 80.0, mass: Some(25.0), cov: None }, ]; let config = SolveConfig { fov_estimate_rad: (15.0_f32).to_radians(), 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, &config); ``` ### Response Example (Rust) ```rust if result.status == SolveStatus::MatchFound { println!("Attitude: {:?}", result.qicrs2cam.unwrap()); println!("Matched {} stars in {:.1} ms", result.num_matches.unwrap(), result.solve_time_ms); } ``` ``` -------------------------------- ### Core Classes Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/api/index.md Core classes for managing star pattern databases, camera models, and solving results. ```APIDOC ## SolverDatabase ### Description Star pattern database — generate, save/load, and solve. ### Usage [`SolverDatabase`](solver-database.md) ``` ```APIDOC ## CameraModel ### Description Camera intrinsics — focal length, optical center, parity, distortion. ### Usage [`CameraModel`](camera-model.md) ``` ```APIDOC ## SolveResult ### Description Plate-solve result — attitude, WCS, matched stars, pixel↔sky conversions. ### Usage [`SolveResult`](solve-result.md) ``` ```APIDOC ## CalibrateResult ### Description Camera calibration result — fitted camera model and statistics. ### Usage [`CalibrateResult`](calibrate-result.md) ``` -------------------------------- ### Run SkyView Integration Test Source: https://github.com/ssmichael1/tetra3rs/blob/main/README.md Execute a specific integration test for solving synthetic star field images generated by SkyView. Requires the `image` feature and `--nocapture` flag to see output. ```sh cargo test --test skyview_solve_test --features image -- --nocapture ``` -------------------------------- ### Summarize Iterative Solve Results Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/tutorials/basic-solve.ipynb Prints a summary of the final solve result, including boresight, field of view, pixel scale, number of matched stars, and root-mean-square error (RMSE). It also compares the solved pointing against the FITS World Coordinate System (WCS). ```python pred_ra, pred_dec = result.pixel_to_world(crpix_cx, crpix_cy) pred_coord = SkyCoord(ra=pred_ra * u.deg, dec=pred_dec * u.deg) sep = pred_coord.separation(fits_coord) arcsec_per_px = result.fov_deg * 3600 / img.shape[1] print(f"Boresight: RA={result.ra_deg:.4f}, Dec={result.dec_deg:.4f}") print(f"FOV: {result.fov_deg:.4f} deg") print(f"Pixel scale: {arcsec_per_px:.2f} arcsec/px") print(f"Matched stars: {result.num_matches}") print( f'RMSE: {result.rmse_arcsec:.2f}" ({result.rmse_arcsec / arcsec_per_px:.3f} px)' ) print(f"vs FITS WCS (CRPIX): {sep.to(u.arcsec):.2f}") ``` -------------------------------- ### Run Default Unit Tests Source: https://github.com/ssmichael1/tetra3rs/blob/main/README.md Execute the unit tests for the Tetra3rs library using Cargo. This command runs tests with the default feature set. ```sh cargo test ``` -------------------------------- ### Compare Solved Pointing Against FITS WCS Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/tutorials/multi-image-calibration.ipynb Compares the solved pointing from the calibration against FITS WCS ground truth. Calculates the angular separation between the solver's boresight and the WCS center pixel. Useful for verifying calibration accuracy. ```python arcsec_per_px = resarr[0].fov_deg * 3600 / 2048 rows = [] for idx, res in enumerate(resarr): ra_dec_model = res.pixel_to_world(0, 0) coord_fits = SkyCoord( ra_dec_fits[idx][0] * u.degree, ra_dec_fits[idx][1] * u.degree ) coord_model = SkyCoord(ra_dec_model[0] * u.degree, ra_dec_model[1] * u.degree) separation = coord_fits.separation(coord_model).arcsecond rmse_px = res.rmse_arcsec / arcsec_per_px rows.append( ( sectors[idx], res.num_matches, f"{res.rmse_arcsec:.2f}", f"{rmse_px:.3f}", f"{separation:.2f}", f"{res.solve_time_ms:.1f}", ) ) header = 'SectorMatchesRMSE (")RMSE (px)vs FITS WCS (")Solve (ms)' body = "".join( f"{s}{n}{r}{rpx}{sep}{t}" for s, n, r, rpx, sep, t in rows ) display(HTML(f"{header}{body}
")) ``` -------------------------------- ### SolveResult Source: https://context7.com/ssmichael1/tetra3rs/llms.txt Carries the full output of a successful solve: attitude quaternion and rotation matrix, boresight RA/Dec/Roll, WCS fields, match statistics, and pixel↔sky conversion methods. ```APIDOC ## SolveResult ### Description `SolveResult` carries the full output of a successful solve: attitude quaternion and rotation matrix, boresight RA/Dec/Roll, WCS fields (CD matrix, CRVAL, CRPIX), match statistics, and pixel↔sky conversion methods. ### Fields - **ra_deg**: (float) - Right Ascension of the boresight in degrees. - **dec_deg**: (float) - Declination of the boresight in degrees. - **roll_deg**: (float) - Roll angle of the boresight in degrees. - **quaternion**: (numpy array) - Attitude quaternion [w, x, y, z] (Hamilton scalar-first, ICRS → camera). - **rotation_matrix_icrs_to_camera**: (numpy array) - 3x3 proper rotation matrix (det=+1) from ICRS to camera coordinates. ### Request Example (Python) ```python import numpy as np import tetra3rs, pickle # Assume `result` is a successful SolveResult r = result # --- Attitude --- print(f"Boresight: RA={r.ra_deg:.4f}°, Dec={r.dec_deg:.4f}°, Roll={r.roll_deg:.2f}°") # Quaternion: [w, x, y, z], Hamilton scalar-first, ICRS → camera q = r.quaternion # shape (4,), dtype float64 print(f"Quaternion [w,x,y,z]: {q}") # Rotation matrix: 3×3, proper rotation (det=+1), ICRS → camera R = r.rotation_matrix_icrs_to_camera # shape (3, 3) assert abs(np.linalg.det(R) - 1.0) < 1e-6 ``` ``` -------------------------------- ### Iterative Solve and Calibrate Configuration Source: https://github.com/ssmichael1/tetra3rs/blob/main/docs/tutorials/basic-solve.ipynb Defines the configuration parameters for iterative solving and calibration passes. Each tuple specifies the match radius, refinement iterations, distortion order, and field-of-view error tolerance. ```python pass_configs = [ # (match_radius, refine_iterations, distortion_order, fov_error_deg) (0.01, 10, 3, 0.5), (0.005, 10, 4, 0.5), (0.003, 10, 5, 0.5), (0.002, 10, 6, 0.5), ] camera_model = None fov_estimate = 12.0 # approximate TESS CCD FOV in degrees fits_coord = SkyCoord(ra=fits_ra * u.deg, dec=fits_dec * u.deg) for pass_num, (mr, ri, order, fov_err) in enumerate(pass_configs, 1): result = solver.solve_from_centroids( centroids, fov_estimate_deg=fov_estimate, fov_max_error_deg=fov_err, image_shape=img.shape, match_radius=mr, match_threshold=1e-5, refine_iterations=ri, camera_model=camera_model, ) if result is None: print(f"Pass {pass_num}: FAILED to solve") break fov_estimate = result.fov_deg # Compare solver solution against FITS WCS at the CRPIX reference point pred_ra, pred_dec = result.pixel_to_world(crpix_cx, crpix_cy) pred_coord = SkyCoord(ra=pred_ra * u.deg, dec=pred_dec * u.deg) sep = pred_coord.separation(fits_coord).to(u.arcsec) print( f"Pass {pass_num}: {result.num_matches} matches, " f'RMSE={result.rmse_arcsec:.2f}", " f"vs FITS WCS={sep:.2f}" ) cal = solver.calibrate_camera( result, centroids, image_shape=img.shape, order=order, ) camera_model = cal.camera_model print( f" Calibration: RMSE {cal.rmse_before_px:.3f} -> {cal.rmse_after_px:.3f} px, " f"{cal.n_inliers} inliers, {cal.n_outliers} outliers" ) ```