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 |
Live preview / exposure |
Camera returns a synthetic star field on |
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-sdkif 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=LatestMajorlets the project (targetingnet8.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 |
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 |
|---|---|
connect + metadata, gain tolerance, exposure state machine, image download, every display view ( |
|
position ranges, slew reaches target, tracking toggle, sync |
|
caps + position, absolute move, relative step, halt-when-idle |
|
connect, read position, change filter (Dark/IR/LP) |
|
end-to-end |
|
original smoke: camera→display pipeline, telescope position |
|
also carries |
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_gainincore/alpaca/camera.pyswallow theNotImplementedException.)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 JSONImageArraypath, 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 |
|---|---|
|
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 |
|
Prefix with |
Port |
Change it in the sim’s web UI (Server settings) and keep |
App connects but no image |
Give the exposure time to finish; the sim needs ~1 s + download before |
See also: ARCHITECTURE.md · CONTRIBUTING.md