Skip to content

Go client library wrapping astrometry Docker container via CLI exec. Solve astronomical images locally without internet access.

License

Notifications You must be signed in to change notification settings

DiarmuidKelly/astrometry-go-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Astrometry Go Client

Version License Go Version Go Report Card

Offline astrometric plate-solving for Go - Solve astronomical images locally without internet access using the astrometry-dockerised-solver Docker container. Complete privacy and control over your data with no dependency on external services.

Features

  • 100% Offline Operation - No internet required after initial setup
  • Complete Privacy - Your images never leave your machine
  • No Rate Limits - Solve unlimited images without API quotas
  • Fast Local Processing - No network latency or upload time
  • Simple, type-safe API for plate-solving astronomical images
  • Docker-based integration (no complex installation required)
  • Support for scale hints, downsampling, and RA/Dec position hints
  • WCS header parsing with structured results
  • Context-aware with timeout support
  • Comprehensive error handling
  • CLI tool for quick command-line solving
  • Full test coverage with integration tests validating both Docker implementations (diarmuidk/astrometry-dockerised-solver and dm90/astrometry)

Why Offline?

Unlike cloud-based plate-solving services, this library runs entirely on your local machine:

  • Privacy First - Your astrophotography images and location data stay private
  • No Internet Required - Work in remote locations, observatories, or anywhere offline
  • Free & Unlimited - No API keys, rate limits, or subscription fees
  • Faster Processing - No upload/download time, just local computation
  • Full Control - Customize solve parameters without service restrictions
  • Self-Hosted - Perfect for automated pipelines, observatories, and air-gapped systems

Prerequisites

Note: Internet is only required once during initial setup to download the Docker image and index files. After setup, the solver runs 100% offline.

Required

  • Go 1.21+
  • Docker with the diarmuidk/astrometry-dockerised-solver image pulled (one-time download)
  • Astrometry.net index files downloaded to a local directory (one-time download)

Quick Setup

# Clone the repository (for development)
git clone https://github.com/DiarmuidKelly/astrometry-go-client.git
cd astrometry-go-client

# Download all index files (~350MB)
./scripts/download-indexes.sh

Index Files Guide

Astrometry.net requires index files that match your camera's field of view (FOV). Using an index that's too wide for your FOV guarantees failure - the star patterns physically won't fit in your image.

Choosing the Right Index Files

First, calculate your FOV:

FOV = 2 × arctan(sensor_width / (2 × focal_length))
FOV ≈ sensor_width / focal_length   (in radians)
FOV ≈ (sensor_width / focal_length) × 57.3   (in degrees)
FOV (degrees) = (sensor_width_mm / focal_length_mm) × 57.3

Then download 2-3 indexes that bracket your FOV:

Index File Field Width Size Full Frame (36mm) APS-C Canon (22.3mm, 1.6x) Download
index-4119 0.1° - 0.2° 144 KB 10,000-20,000mm 6,400-12,800mm Download
index-4118 0.2° - 0.28° 187 KB 7,300-10,000mm 4,500-6,400mm Download
index-4117 0.28° - 0.4° 248 KB 5,100-7,300mm 3,200-4,500mm Download
index-4116 0.4° - 0.56° 409 KB 3,600-5,100mm 2,300-3,200mm Download
index-4115 0.56° - 0.8° 740 KB 2,500-3,600mm 1,600-2,300mm Download
index-4114 0.8° - 1.1° 1.4 MB 1,800-2,500mm 1,200-1,600mm Download
index-4113 1.1° - 1.6° 2.7 MB 1,300-1,800mm 800-1,200mm Download
index-4112 1.6° - 2.2° 5.3 MB 900-1,300mm 600-800mm Download
index-4111 2.2° - 3.0° 10 MB 700-900mm 400-600mm Download
index-4110 3.0° - 4.2° 25 MB 500-700mm 300-400mm Download
index-4109 4.2° - 5.6° 50 MB 350-500mm 220-300mm Download
index-4108 5.6° - 8.0° 95 MB 250-350mm 160-220mm Download
index-4107 8.0° - 11.0° 165 MB 180-250mm 110-160mm Download

Total size of all indexes: ~350 MB

Example Setups

DSLR Astrophotography (APS-C sensor, 24mm wide):

mkdir -p ~/astrometry-data && cd ~/astrometry-data
# 200mm lens: ~7° FOV
wget http://data.astrometry.net/4100/index-4108.fits
wget http://data.astrometry.net/4100/index-4109.fits

# 50mm lens: ~27° FOV
wget http://data.astrometry.net/4100/index-4110.fits
wget http://data.astrometry.net/4100/index-4111.fits

Telescope (1000mm focal length, APS-C sensor):

mkdir -p ~/astrometry-data && cd ~/astrometry-data
# ~1.4° FOV
wget http://data.astrometry.net/4100/index-4113.fits
wget http://data.astrometry.net/4100/index-4114.fits

Quick Solver (50mm-300mm lenses - recommended default):

mkdir -p ~/astrometry-data && cd ~/astrometry-data
# Download 1.1° - 4.2° range (~43 MB total)
# Covers DSLR + 50-300mm focal lengths
wget http://data.astrometry.net/4100/index-4110.fits  # 3.0° - 4.2° (50-70mm)
wget http://data.astrometry.net/4100/index-4111.fits  # 2.2° - 3.0° (70-110mm)
wget http://data.astrometry.net/4100/index-4112.fits  # 1.6° - 2.2° (110-150mm)
wget http://data.astrometry.net/4100/index-4113.fits  # 1.1° - 1.6° (150-220mm)

Note: The scripts/download-indexes.sh script downloads all indexes automatically.

Why Index Matching Matters

Index files contain pre-computed star patterns (called "quads") at specific angular scales. Using the wrong index causes failure, not just slowness.

What happens with mismatched indexes:

Scenario Result Why
Narrow image (0.5°) + Wide index (6-8°) Guaranteed failure Star patterns in the index span 6-8°. Your 0.5° image physically cannot contain patterns that large - they don't fit in the frame.
Wide image (8°) + Narrow index (0.5°) ⚠️ Likely failure Your wide image does contain small sub-regions with narrow patterns, but matching is unreliable, very slow, and usually fails.

The critical rule:

  • Index scale larger than your FOV → guaranteed failure (patterns don't fit in your image)
  • Index scale smaller than your FOV → likely failure (unreliable matching of sub-regions)

Best practice: Download 2-3 indexes that bracket your expected FOV (one above, one below). This ensures reliable, fast solving regardless of minor FOV variations.

Docker Setup

This library uses the diarmuidk/astrometry-dockerised-solver Docker container to perform plate-solving. You have several options for running the dependency:

Docker Execution Modes

The library supports two Docker execution modes:

1. Docker Run Mode (Default)

How it works: The library spawns a new Docker container for each solve operation, then removes it.

Pros:

  • Simple setup - no container management required
  • Clean isolation per solve

Cons:

  • Slower for multiple solves (~1-2s container startup overhead per solve)
  • More Docker overhead

Setup: Pull the image, then use the library - that's it!

# Pull from DockerHub (recommended)
docker pull diarmuidk/astrometry-dockerised-solver:latest

# Or from GitHub Container Registry (GHCR)
docker pull ghcr.io/diarmuidkelly/astrometry-dockerised-solver:latest

Client configuration:

config := &client.ClientConfig{
    IndexPath: "/path/to/astrometry-data",  // Path to your index files
    // DockerImage defaults to ghcr.io/diarmuidkelly/astrometry-dockerised-solver:latest
    // To use dm90/astrometry instead: DockerImage: "dm90/astrometry"
}
c, err := client.NewClient(config)

2. Docker Exec Mode (Recommended for Development)

How it works: Uses a long-running Docker container and executes solve commands via docker exec.

Pros:

  • Faster: No container startup overhead (typically 1-2s faster per solve)
  • Ideal for development, testing, or batch processing

Cons:

  • Requires manual container management

Setup Option A: Using Docker Compose (Recommended)

# 1. Copy and configure environment
cp .env.example .env
# Edit .env and set ASTROMETRY_INDEX_PATH to your index files directory

# 2. Start the container
docker compose up -d

# 3. Verify it's running
docker ps | grep astrometry-solver

Setup Option B: Manual Docker Run

docker run -d \
  --name astrometry-solver \
  -v ~/astrometry-data:/usr/local/astrometry/data:ro \
  -v /tmp/astrometry-shared:/shared-data \
  diarmuidk/astrometry-dockerised-solver:latest \
  tail -f /dev/null

Alternative: You can also use the original dm90/astrometry image if you prefer:

docker pull dm90/astrometry:latest

Client configuration:

config := &client.ClientConfig{
    IndexPath:     "/path/to/astrometry-data",
    UseDockerExec: true,
    ContainerName: "astrometry-solver",
}
c, err := client.NewClient(config)

Performance Comparison

Operation Docker Run Mode Docker Exec Mode Difference
First solve ~15s ~13s ~2s faster
Subsequent solves (each) ~15s ~13s ~2s faster
Setup overhead None Start container once One-time

Recommendation: Use docker exec mode for development/testing. Either mode works well for production depending on your orchestration setup.

Full Stack Setup

For a complete REST API server with web interface, see the Astrometry API Server project. It includes:

  • docker-compose orchestration for both the API server and solver
  • RESTful HTTP API
  • Swagger documentation
  • Health monitoring

Installation

As a Library

go get github.com/DiarmuidKelly/astrometry-go-client

CLI Tool

go install github.com/DiarmuidKelly/astrometry-go-client/cmd/astro-cli@latest

Or build from source:

git clone https://github.com/DiarmuidKelly/astrometry-go-client.git
cd astrometry-go-client
make install

Quick Start

Library Usage

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/DiarmuidKelly/astrometry-go-client"
)

func main() {
    // Create client
    config := &client.ClientConfig{
        IndexPath: "/path/to/astrometry-data",
    }
    c, err := client.NewClient(config)
    if err != nil {
        log.Fatal(err)
    }

    // Configure solve options
    opts := client.DefaultSolveOptions()
    opts.ScaleLow = 1.0   // 1 arcmin/width
    opts.ScaleHigh = 3.0  // 3 arcmin/width

    // Solve the image
    result, err := c.Solve(context.Background(), "image.jpg", opts)
    if err != nil {
        log.Fatal(err)
    }

    if result.Solved {
        fmt.Printf("RA: %.6f, Dec: %.6f\n", result.RA, result.Dec)
        fmt.Printf("Pixel Scale: %.2f arcsec/pixel\n", result.PixelScale)
    } else {
        fmt.Println("Image could not be solved")
    }
}

CLI Usage

astro-cli \
  --image photo.jpg \
  --index-path ~/astrometry-data \
  --scale-low 1 \
  --scale-high 3 \
  --downsample 2

Output (JSON):

{
  "solved": true,
  "ra": 120.123456,
  "dec": 45.987654,
  "pixel_scale": 1.23,
  "rotation": 15.5,
  "field_width": 2.5,
  "field_height": 1.8,
  "solve_time": 12.34
}

API Reference

Client Configuration

type ClientConfig struct {
    DockerImage   string        // Default: "ghcr.io/diarmuidkelly/astrometry-dockerised-solver:latest"
                                 // Also compatible with: "dm90/astrometry"
    IndexPath     string        // Required: path to index files
    TempDir       string        // Optional: temp directory for processing
    Timeout       time.Duration // Default: 5 minutes
    UseDockerExec bool          // Use docker exec mode (default: false)
    ContainerName string        // Container name for docker exec mode
}

Solve Options

type SolveOptions struct {
    ScaleLow         float64  // Lower bound of image scale
    ScaleHigh        float64  // Upper bound of image scale
    ScaleUnits       string   // "degwidth", "arcminwidth", "arcsecperpix"
    DownsampleFactor int      // Reduce resolution (default: 2)
    DepthLow         int      // Min quads to try (default: 10)
    DepthHigh        int      // Max quads to try (default: 20)
    NoPlots          bool     // Disable plot generation (default: true)
    RA               float64  // RA hint in degrees (optional)
    Dec              float64  // Dec hint in degrees (optional)
    Radius           float64  // Search radius in degrees (optional)
    Verbose          bool     // Enable verbose output
}

Result Structure

type Result struct {
    Solved      bool              // Whether the image was solved
    RA          float64           // Right ascension (J2000, degrees)
    Dec         float64           // Declination (J2000, degrees)
    PixelScale  float64           // arcsec/pixel
    Rotation    float64           // Field rotation (degrees)
    FieldWidth  float64           // Field of view width (degrees)
    FieldHeight float64           // Field of view height (degrees)
    WCSHeader   map[string]string // Raw WCS header fields
    OutputFiles []string          // Paths to generated files
    SolveTime   float64           // Solve duration (seconds)
}

Methods

NewClient(config *ClientConfig) (*Client, error)

Creates a new astrometry client with the given configuration.

Solve(ctx context.Context, imagePath string, opts *SolveOptions) (*Result, error)

Solves a single image file and returns the plate solution.

SolveBytes(ctx context.Context, data []byte, format string, opts *SolveOptions) (*Result, error)

Solves image data from a byte slice (useful for in-memory images).

Examples

See the examples/ directory for more usage examples:

  • examples/basic/ - Basic plate-solving example
  • examples/batch/ - Batch processing multiple images (coming soon)
  • examples/with-hints/ - Using RA/Dec hints for faster solving (coming soon)

Error Handling

The library provides structured error types:

var (
    ErrNoSolution    = errors.New("no solution found")
    ErrTimeout       = errors.New("solve operation timed out")
    ErrDockerFailed  = errors.New("docker command failed")
    ErrInvalidInput  = errors.New("invalid input parameters")
    ErrWCSParseFailed = errors.New("failed to parse WCS output")
)

Example:

result, err := c.Solve(ctx, imagePath, opts)
if err != nil {
    if errors.Is(err, client.ErrTimeout) {
        log.Println("Solve timed out - try increasing timeout or downsample")
    } else if errors.Is(err, client.ErrDockerFailed) {
        log.Println("Docker error - check Docker is running")
    }
    return err
}

if !result.Solved {
    log.Println("No solution - try adjusting scale parameters")
}

Performance Tips

  1. Use scale bounds: Providing ScaleLow and ScaleHigh dramatically speeds up solving
  2. Downsample: Higher downsample factors (2-4) work well for most images
  3. RA/Dec hints: If you know approximate coordinates, use them to reduce search space
  4. Index files: Download only the indexes appropriate for your field of view

Development

Building

make build        # Build all binaries
make test         # Run tests
make lint         # Run linter
make clean        # Clean build artifacts

Running Tests

go test ./...

Integration tests requiring Docker can be run with:

go test -tags=integration ./...

Contributing

Contributions welcome! See CONTRIBUTING.md for workflow details.

Quick Start:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feat/my-feature)
  3. Make your changes with conventional commits
  4. Push and create a PR with [MAJOR], [MINOR], or [PATCH] prefix

Auto-release workflow handles versioning, changelog, and releases on PR merge.

License

This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.

Links

Acknowledgments

About

Go client library wrapping astrometry Docker container via CLI exec. Solve astronomical images locally without internet access.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •