zwo_mount_control 0.2.1

Rust library for controlling ZWO AM5/AM3 telescope mounts with satellite tracking support
Documentation
//! ZWO Mount Control - Rust library for controlling ZWO AM5/AM3 telescope mounts
//!
//! This library provides a comprehensive interface for controlling ZWO telescope mounts
//! via serial communication, with built-in support for satellite tracking using the
//! `space-dust` crate for TLE/SGP4 propagation.
//!
//! # Features
//!
//! - **GoTo/Slewing**: Command the mount to slew to any celestial coordinates
//! - **Manual Motion**: Control axis motion at various speeds (guide to max slew)
//! - **Tracking**: Enable/disable tracking with sidereal, lunar, solar, or custom rates
//! - **Alt-Az & Equatorial Modes**: Switch between altitude-azimuth and equatorial tracking
//! - **Autoguiding**: Send guide pulses for autoguiding applications
//! - **Satellite Tracking**: Track satellites using TLE data and SGP4 propagation
//! - **Mock Mount**: Test your application without physical hardware
//!
//! # Quick Start
//!
//! ## Connect to a Real Mount
//!
//! ```rust,ignore
//! use zwo_mount_control::{SerialMount, Mount, EquatorialPosition};
//!
//! // Connect via serial port (typical on Linux/Mac)
//! let mut mount = SerialMount::new("/dev/ttyUSB0");
//! mount.connect()?;
//!
//! // Get current position
//! let pos = mount.get_position()?;
//! println!("RA: {}h, Dec: {}°", pos.ra, pos.dec);
//!
//! // Slew to Vega
//! let vega = EquatorialPosition::from_hms_dms(18, 36, 56, 38, 47, 1);
//! mount.goto_equatorial(vega)?;
//! ```
//!
//! ## Use the Mock for Testing
//!
//! ```rust
//! use zwo_mount_control::{MockMount, Mount, EquatorialPosition};
//!
//! let mut mount = MockMount::new();
//! mount.connect().unwrap();
//! mount.unpark().unwrap();
//!
//! // Get current position
//! let pos = mount.get_position().unwrap();
//! println!("Position: {}", pos);
//! ```
//!
//! ## Satellite Tracking
//!
//! ```rust,ignore
//! use zwo_mount_control::{SatelliteTracker, MockMount, Mount};
//!
//! // ISS TLE data
//! let line1 = "1 25544U 98067A   24001.50000000  .00016717  00000-0  10270-3 0  9025";
//! let line2 = "2 25544  51.6400 208.9163 0006703  35.6028  75.3281 15.49560066429339";
//!
//! let mut tracker = SatelliteTracker::from_tle(line1, line2)?;
//!
//! // Set observer location (Los Angeles)
//! tracker.set_observer_location(34.0522, -118.2437, 71.0);
//!
//! // Find next pass
//! let pass = tracker.find_next_pass(chrono::Utc::now(), 24.0)?;
//! if let Some(p) = pass {
//!     println!("Next pass: {}", p);
//! }
//!
//! // Track with a mount
//! let mut mount = MockMount::new();
//! mount.connect()?;
//! tracker.track_pass(&mut mount)?;
//! ```
//!
//! # Module Overview
//!
//! | Module | Description |
//! |--------|-------------|
//! | [`mount`] | Core `Mount` trait and mount status types |
//! | [`serial_mount`] | Serial port mount implementation |
//! | [`mock_mount`] | Simulated mount for testing |
//! | [`protocol`] | Serial command definitions |
//! | [`coordinates`] | Coordinate conversion utilities |
//! | [`satellite_tracker`] | Satellite tracking with TLE propagation |
//! | [`error`] | Error types |
//!
//! # Coordinate Systems
//!
//! The mount supports two coordinate systems:
//!
//! ## Equatorial Coordinates (RA/Dec)
//!
//! - **Right Ascension (RA)**: Decimal hours (0-24)
//! - **Declination (Dec)**: Decimal degrees (-90 to +90)
//!
//! ## Horizontal Coordinates (Az/Alt)
//!
//! - **Azimuth (Az)**: Degrees from North (0-360°, clockwise)
//! - **Altitude (Alt)**: Degrees above horizon (0-90°)

pub mod coordinates;
pub mod error;
pub mod mock_mount;
pub mod mount;
pub mod protocol;
pub mod satellite_tracker;
pub mod serial_mount;

// Re-export main types for convenience
pub use coordinates::{Coordinates, EquatorialPosition, HorizontalPosition, TrackingRates};
pub use error::{MountError, MountResult};
pub use mock_mount::MockMount;
pub use mount::{Mount, MountStatus, SimulatedMount, SiteLocation};
pub use protocol::{Direction, MountMode, SlewRate, TrackingRate};
pub use satellite_tracker::{
    CableWrapCheck, CableWrapState, PidController, SatellitePass, SatelliteTracker, TrackingConfig,
    TrackingMode, TrackingState,
};
pub use serial_mount::{SerialConfig, SerialMount};

/// Convenience function to create an RA value from hours, minutes, seconds.
///
/// # Example
/// ```
/// use zwo_mount_control::ra;
///
/// let ra_vega = ra(18, 36, 56.0);
/// assert!((ra_vega - 18.6155556).abs() < 0.0001);
/// ```
pub fn ra(hours: u8, minutes: u8, seconds: f64) -> f64 {
    Coordinates::hms_to_decimal(hours, minutes, seconds)
}

/// Convenience function to create a Dec value from degrees, minutes, seconds.
///
/// # Example
/// ```
/// use zwo_mount_control::dec;
///
/// let dec_vega = dec(38, 47, 1.0);
/// assert!((dec_vega - 38.7836111).abs() < 0.0001);
///
/// let dec_south = dec(-23, 26, 21.0);
/// assert!((dec_south - (-23.439167)).abs() < 0.0001);
/// ```
pub fn dec(degrees: i16, minutes: u8, seconds: f64) -> f64 {
    Coordinates::dms_to_decimal(degrees, minutes, seconds)
}

/// Library version
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_ra_convenience() {
        let ra = ra(12, 30, 0.0);
        assert!((ra - 12.5).abs() < 0.0001);
    }

    #[test]
    fn test_dec_convenience() {
        let dec_val = dec(45, 30, 0.0);
        assert!((dec_val - 45.5).abs() < 0.0001);

        let dec_neg = dec(-23, 30, 0.0);
        assert!((dec_neg - (-23.5)).abs() < 0.0001);
    }

    #[test]
    fn test_mock_mount_basic() {
        let mut mount = MockMount::new();
        assert!(!mount.is_connected());

        mount.connect().unwrap();
        assert!(mount.is_connected());

        let pos = mount.get_position().unwrap();
        assert!(pos.ra >= 0.0 && pos.ra < 24.0);
        assert!(pos.dec >= -90.0 && pos.dec <= 90.0);
    }

    #[test]
    fn test_equatorial_position() {
        let pos = EquatorialPosition::from_hms_dms(18, 36, 56.0, 38, 47, 1.0);
        assert!((pos.ra - 18.6155556).abs() < 0.001);
        assert!((pos.dec - 38.7836111).abs() < 0.001);
    }
}