ZwoController

An Elixir library for controlling ZWO AM5 telescope mounts via serial communication.

Features

Installation

Add zwo_controller to your list of dependencies in mix.exs:

def deps do
  [
    {:zwo_controller, "~> 0.1.0"}
  ]
end

Or for the latest development version from GitHub:

def deps do
  [
    {:zwo_controller, github: "jmcguigs/zwo_controller"}
  ]
end

Then fetch dependencies:

mix deps.get

Quick Start

Connect to a Real Mount

# Connect via serial port (typical on Linux/Mac)
{:ok, mount} = ZwoController.start_mount(port: "/dev/ttyUSB0")

# On Windows
{:ok, mount} = ZwoController.start_mount(port: "COM3")

Use the Mock for Testing

{:ok, mount} = ZwoController.start_mock()

Basic Operations

# Get current position (RA in decimal hours, DEC in decimal degrees)
{:ok, pos} = ZwoController.position(mount)
IO.puts("RA: #{pos.ra}h, DEC: #{pos.dec}°")

# Slew to coordinates
ZwoController.goto(mount, 12.5, 45.0)

# Slew to Vega using HMS/DMS
ra = ZwoController.ra(18, 36, 56)   # 18h 36m 56s
dec = ZwoController.dec(38, 47, 1)  # +38° 47' 01"
ZwoController.goto(mount, ra, dec)

# Enable sidereal tracking
ZwoController.track(mount, :sidereal)

# Manual motion
ZwoController.set_rate(mount, 5)    # Set medium speed
ZwoController.move(mount, :north)   # Start moving north
Process.sleep(1000)
ZwoController.stop(mount)           # Emergency stop

Autoguiding

# Set guide rate to 0.5x sidereal
ZwoController.set_guide_rate(mount, 0.5)

# Send guide pulses (direction, duration in ms)
ZwoController.guide(mount, :north, 200)
ZwoController.guide(mount, :east, 150)

Tracking Modes

ZwoController.track(mount, :sidereal)  # For stars
ZwoController.track(mount, :lunar)     # For the Moon
ZwoController.track(mount, :solar)     # For the Sun

ZwoController.track_off(mount)         # Disable tracking

Mount Mode (Alt-Az vs Equatorial)

The ZWO AM5 can operate in two modes depending on physical installation:

# Alt-Az mode - for mount on tripod (no wedge)
# Use for azimuth/altitude tracking (satellites, terrestrial objects)
ZwoController.set_altaz_mode(mount)

# Equatorial mode - for mount on wedge (polar aligned)
# Use for tracking celestial objects that move with Earth's rotation
ZwoController.set_polar_mode(mount)

# Check current mount type
{:ok, status} = ZwoController.status(mount)
IO.inspect(status.mount_type)  # => :altaz or :equatorial

Important: The mount mode must match your physical setup:

For satellite tracking or azimuth/altitude control, you must set Alt-Az mode first:

ZwoController.set_altaz_mode(mount)
Process.sleep(1000)  # Wait for mode change
{:ok, pos} = ZwoController.altaz(mount)  # Now returns Az/Alt coordinates

Home and Park

ZwoController.home(mount)  # Go to home position
ZwoController.park(mount)  # Go to park position

Coordinate System

The mount supports two coordinate systems depending on its mode:

Equatorial Coordinates (RA/Dec)

Used when mount is in equatorial mode (on wedge, polar aligned):

# Get current RA/Dec position
{:ok, pos} = ZwoController.position(mount)
IO.puts("RA: #{pos.ra}h, DEC: #{pos.dec}°")

Horizontal Coordinates (Az/Alt)

Used when mount is in Alt-Az mode (on tripod, no wedge):

# Get current Az/Alt position
{:ok, pos} = ZwoController.altaz(mount)
IO.puts("Az: #{pos.az}°, Alt: #{pos.alt}°")

Converting Coordinates

alias ZwoController.Coordinates

# HMS to decimal hours
ra = Coordinates.hms_to_ra(12, 30, 45)  # 12h 30m 45s → 12.5125

# DMS to decimal degrees
dec = Coordinates.dms_to_dec(-23, 26, 21)  # -23° 26' 21" → -23.439...

# Decimal to HMS/DMS
Coordinates.ra_to_hms(12.5)   # → %{hours: 12, minutes: 30, seconds: 0.0}
Coordinates.dec_to_dms(-23.5) # → %{degrees: -23, minutes: 30, seconds: 0.0}

Module Overview

Module Description
ZwoController High-level convenience API
ZwoController.Mount GenServer-based mount controller
ZwoController.Mock Simulated mount for testing
ZwoController.Protocol Serial command definitions (includes :AA# for Alt-Az mode, :AP# for Polar mode)
ZwoController.Coordinates Coordinate conversion utilities
ZwoController.SatelliteTracker Satellite tracking with TLE propagation

Low-Level Access

For advanced usage, you can send raw commands:

alias ZwoController.Protocol

# Send any command
{:ok, response} = ZwoController.raw(mount, Protocol.get_version())

# Or construct custom commands
{:ok, response} = ZwoController.raw(mount, ":GVP#")  # Get mount model

Hardware Requirements

# Linux: Add user to dialout group for serial access
sudo usermod -a -G dialout $USER
# Log out and back in for changes to take effect

License

MIT