Testing ARGOS against the ASCOM Alpaca Simulator

ARGOS talks to the Seestar over ASCOM Alpaca. The same protocol is spoken by the official ASCOM Alpaca Simulators (OmniSim), so you can develop and test the whole app — connect, expose, preview, focus, jog, sequence — without the telescope plugged in.

The simulator is the ASCOM Initiative’s own, so testing against it tests conformance to the protocol rather than to our own assumptions about it.

Tip

Two ways to run it: Docker, which needs nothing installed (§2a), or a native .NET build (§2). Take Docker unless you need to debug the simulator itself.

1. What the simulator gives you

OmniSim exposes a full set of Alpaca devices on one HTTP port (default 32323): Camera, Telescope, Focuser, FilterWheel, and more. ARGOS connects to all of them exactly as it would to a real Seestar.

You can test…

How the sim behaves

Connection panel

All 4 devices connect on localhost:32323

Live preview / exposure

Camera returns a synthetic star field on StartExposure

Display pipeline (debayer, STF, histogram)

Runs on the real downloaded frame

Focus (HFD curve)

Focuser moves; metric computed from the frame

Mount jog / GoTo

Telescope reports & updates RA/Dec

Sequencer

Chained exposures run end-to-end

What it won’t reproduce: the Seestar’s native JSON-RPC features (UDP auto-discovery, native jog, the real GRBG star field). For native-protocol tests use the seestar_alp simulator instead — it is not bundled, and must be cloned next to this repository — see tests/conftest.py (seestar_simulator fixture).

2a. Running it via Docker (no .NET SDK needed — preferred on this Mac)

Clone it wherever you keep source; the commands below assume it sits beside this machine and .NET is not installed, so use Docker. One-time build (the ASCOM_COM define in ASCOM.Alpaca.Simulators.csproj must be conditioned on Windows — see §2 below — or the Linux build fails):

cd ../ASCOM.Alpaca.Simulators
docker build -t omnisim .

Run / stop (quote the --urls glob or zsh eats it):

docker rm -f omnisim 2>/dev/null
docker run -d --name omnisim -p 32323:32323 -p 32227:32227/udp \
    omnisim dotnet ascom.alpaca.simulators.dll '--urls=http://0.0.0.0:32323'
# … later:
docker stop omnisim

All four devices (telescope, camera, filterwheel, focuser) answer on http://localhost:32323 — unlike scripts/mock_alpaca_server.py, which only simulates telescope+camera on port 8765 and will make the filter wheel and focuser LOOK broken if you point the app at it.

2. Installing & building it (macOS arm64, native .NET alternative)

The simulator is a .NET app. It is not vendored in this repo — clone it next to your other dev projects:

git clone https://github.com/ASCOMInitiative/ASCOM.Alpaca.Simulators.git

You need the .NET SDK (Apple Silicon / arm64 build). Check it:

dotnet --version      # any 8.x or newer is fine

Install via brew install --cask dotnet-sdk if missing.

One required source edit (macOS only)

The project compiles Windows COM interop by default, which fails on macOS. Gate it to Windows so the Alpaca server still builds everywhere. In ASCOM.Alpaca.Simulators/ASCOM.Alpaca.Simulators.csproj, the ASCOM_COM define must be conditioned on Windows:

<DefineConstants Condition=" '$(OS)' == 'Windows_NT' ">$(DefineConstants);ASCOM_COM</DefineConstants>

(If you cloned fresh and it isn’t already conditioned, add the Condition=" '$(OS)' == 'Windows_NT' " part.) This edit lives in the simulator clone, not in this repository — it is not tracked by this repo.

3. Running it

cd ~/Documents/perso/dev/ASCOM.Alpaca.Simulators/ASCOM.Alpaca.Simulators
DOTNET_ROLL_FORWARD=LatestMajor dotnet run
  • DOTNET_ROLL_FORWARD=LatestMajor lets the project (targeting net8.0) run on a newer installed SDK (e.g. .NET 10) without retargeting.

  • First run builds the project (a minute or two); subsequent runs are instant.

Confirm it’s up (in another terminal):

curl -s http://localhost:32323/api/v1/telescope/0/connected
# → {"Value":...,"ErrorNumber":0,...}   means the server is live

The browser UI at http://localhost:32323 lets you inspect device state and change the port (Server settings → Server Port) if 32323 is taken. Keep it at 32323 — that’s the value ARGOS and the tests default to (SIMULATOR_PORT in tests/conftest.py).

4. Manual app test against the sim

With the simulator running, launch ARGOS:

cd /path/to/argos
./run.sh

Then walk the app:

Step

Where

Check

1. Connect

Connection

IP address localhost, port 32323 → Connect equipment (the 4 devices become Ready)

2. Preview

Observe

Start a 1 s exposure → image appears, no UI freeze, auto-STF stretches it

3. Display

Image display dock

R/G/B histogram visible; black/white/midtone sliders react; view selector (super-pixel / bilinear / CFA channels)

4. Measure

on the image

Crosshair cursor + ROI selection → stats (min/max/mean/σ) show in the bar (not a big column)

5. Focus

Focusing dock

HFD curve fills as exposures roll in

6. Mount

Telescope dock

RA/Dec read back; jog moves the mount

7. Filter

Filter selector in the Acquisition dock

Connect the wheel → current filter shown; selecting a slot rotates it (Dark/IR/LP). The filter wheel has no dock of its own

8. Sequence

The Plan mode in the sidebar

Chain a few Light frames → progress advances, frames render

5. Automated integration tests

A simulator-backed test suite drives the real device wrappers + workers the way a live session does. Every test is decorated @simulator_required, so it auto-skips when the sim is down and never breaks a normal pytest run.

# Full suite (sim tests skip if it's not running)
uv run --extra dev pytest

# Just the simulator suite (start the sim first)
uv run --extra dev pytest tests/core/test_simulator_*.py -v

File

Validates

test_simulator_camera.py

connect + metadata, gain tolerance, exposure state machine, image download, every display view (render_view), frame_metrics + detect_stars, full stretch path

test_simulator_telescope.py

position ranges, slew reaches target, tracking toggle, sync

test_simulator_focuser.py

caps + position, absolute move, relative step, halt-when-idle

test_simulator_filterwheel.py

connect, read position, change filter (Dark/IR/LP)

test_simulator_sequence.py

end-to-end SequenceWorker → FITS subs in the Siril folder + per-frame QA headers + a valid session.json (§7); a multi-filter plan drives the wheel and ends on the last filter

test_simulator_session.py

original smoke: camera→display pipeline, telescope position

test_telescope.py

also carries @simulator_required cases alongside its unit tests

The sequence test runs the worker’s run() synchronously on the test thread, so its Qt signals fire by direct connection — no event loop needed.

Pure-logic pieces that back these flows are also unit-tested without the sim: test_metrics.py (detect_stars FWHM/eccentricity) and test_session_log.py (session.json).

6. Known sim quirks (expected, not bugs)

The OmniSim camera is generic, so a couple of things differ from a real Seestar. ARGOS tolerates both on purpose:

  • Gain not implemented → log line Camera does not implement Gain — skipping. The sim has no Gain property; the real Seestar does and is unaffected. (set_gain/get_gain in core/alpaca/camera.py swallow the NotImplementedException.)

  • JSON instead of ImageBytes → log line ImageBytes unavailable/mismatched — using JSON imagearray. The sim’s binary buffer doesn’t match its reported dimensions, so the wrapper falls back to the JSON ImageArray path, which infers the true shape. The real device uses the fast binary path.

Both are informational. If you see them, the sim is working as intended.

7. Troubleshooting

Symptom

Fix

curl localhost:32323 refused

Simulator not running — start it (§3)

Tests all skip with “Simulator not running”

Same — start the sim, or it’s on a different port

Build error about COM / Windows interop

Apply the ASCOM_COM Windows gate (§2)

dotnet run complains about framework version

Prefix with DOTNET_ROLL_FORWARD=LatestMajor (§3)

Port 32323 already in use

Change it in the sim’s web UI (Server settings) and keep tests/conftest.py / the Connection panel in sync

App connects but no image

Give the exposure time to finish; the sim needs ~1 s + download before ImageReady

See also: ARCHITECTURE.md · CONTRIBUTING.md