Sourced from:
Google Gemini (2).md
Plate Solving on the Server¶
This document covers the plate-solving implementation decision for the server-side component. Plate solving is needed to: (1) validate incoming FITS files have correct WCS, (2) extract the precise RA/Dec of the image centre for telescope handoff coordination, and (3) catch and correct pointing errors before they propagate through the pipeline.
Decision: ASTAP as Primary, Astrometry.net as Fallback¶
ASTAP (CLI): - Written in Pascal; highly optimised for speed - Solves in typically under 1 second when given a position hint - Small footprint: single binary + star catalogue files - Fails gracefully when optical distortion is extreme or the mount is completely lost
Astrometry.net (solve-field):
- True blind solver — can solve with no prior pointing information
- Much more robust for corrupted or unusual images
- Significantly slower (10–60 seconds depending on catalogue caching and image quality)
- Requires 30–50 GB of index files for full blind capability
Recommended strategy: Try ASTAP first. If ASTAP returns an error code (solve failed), automatically fall back to Astrometry.net. This pattern is used in production by N.I.N.A. and similar observatory automation software.
Cloud Run Deployment (Scale-to-Zero)¶
The plate-solving service can run on Google Cloud Run with scale-to-zero (instances sleep when idle, cold-start on demand).
Key challenge: Star catalogs. ASTAP's .290 catalogue files are hundreds of MB. Do not bundle them in the Docker image.
Solution: Cloud Storage FUSE volume mount. Store catalogues in a GCS bucket. Cloud Run mounts the bucket as a local folder (/mnt/catalogs) at container startup using GCS FUSE. The mount takes milliseconds — ASTAP reads catalogue chunks on demand rather than loading everything into RAM.
Cold start characteristics: - Go container: ~100–400 ms cold start - Python + astropy stack: 5–12 seconds cold start - For a system that needs to react to transient alerts, Go is strongly preferred
Concurrency: ASTAP is CPU-intensive. Set max-concurrency to 4–10 on the Cloud Run instance to avoid CPU starvation when multiple solve requests arrive simultaneously.
WCS Handoff JSON Format¶
When Scope A solves its image and the server needs to command Scope B to point at the same location, the server passes a JSON payload:
{
"solve_metadata": {
"timestamp": "2025-12-30T19:45:00Z",
"solver": "ASTAP",
"success": true
},
"coordinates": {
"ra_deg": 249.773142,
"dec_deg": 9.804742,
"orientation_deg": 105.75,
"equinox": "J2000"
},
"field_of_view": {
"width_arcmin": 62.4,
"height_arcmin": 41.2,
"pixel_scale": 1.09
}
}
Key fields:
- ra_deg / dec_deg: Always decimal degrees, always J2000. Not HMS/DMS — floating point avoids string conversion errors.
- orientation_deg: Camera rotation angle ("North up" = 0°). Required if scopes in the array have different rotators.
- pixel_scale: Arcseconds per pixel. Helps Scope B verify its own optics will cover the same area of sky.
- equinox: "J2000": Explicit. Mounts operate in JNow internally; the plate solver always outputs J2000. Mixing these causes arcminute-scale pointing errors from precession.
Note on CRVAL vs image centre: CRVAL1/CRVAL2 in the WCS header are the coordinates of the reference pixel (CRPIX), which may not be the image centre. Use the --crpix-center flag in Astrometry.net, or calculate the true centre from the WCS transformation matrix if using ASTAP output directly.
Workflow for handoff:
1. Solve Scope A's image → extract ra_deg, dec_deg
2. POST JSON to server
3. Server PUTs the coordinates as the new target for next telescope(s) in the handover chain
4. Scope B receives the target, SLEWs to ra_deg/dec_deg, performs its own "near solve" using those coordinates as a hint to confirm alignment
Language Choice Summary¶
| Criterion | Go (ASTAP CLI) | Python (Astropy + Astrometry.net) |
|---|---|---|
| Cold start | ~100–400 ms | 5–12 seconds |
| RAM footprint | <100 MB | 400–800 MB |
| Development ease | Moderate | Easier (ecosystem) |
| Robustness | High (ASTAP battle-tested) | Highest (blind solver) |
| Cloud Run cost | Lower (shorter CPU time) | Higher (longer cold starts billed) |
Conclusion: For production API (fast, reliable pointing sync), use Go + ASTAP. For fallback / blind solves where the mount is completely lost, trigger the Python/Astrometry.net microservice as a secondary service.