DWARF Alpaca is one FastAPI process exposing one configured physical or simulated DWARF as Alpaca Telescope/0, Camera/0, Focuser/0, and, where supported, FilterWheel/0.
Alpaca client
+-- UDP 32227 discovery ----------> DiscoveryService
`-- HTTP Alpaca request ----------> FastAPI device/management router
|
v
shared DwarfSession
+------------+-------------+
| | |
WebSocket HTTP FTP
commands/notifies parameters final files
| | |
`------------+-------------'
|
physical DWARF
dwarf-alpacacallsdwarf_alpaca.cli:main.servestarts directly;startcan provision, load persisted connectivity, preflight the master lock, and start.dwarf-alpaca-guiandrun_gui.pycreate the PySide6 control center. Its background server uses the same app/session code.server.build_appselects aDeviceProfile, configures the session singleton, mounts management and device routers, preloads filters during FastAPI lifespan, and shuts the session down on exit.server.run_serverowns Uvicorn and the optional UDP discovery context.- Discovery replies with the configured HTTP URL. Device metadata comes from the same active profile as management endpoints.
device_profile.py is the centralized static capability source:
DeviceProfile: identity, client ID, camera, protocol, capture, and filters.ProtocolProfile: WebSocket version/device ID and V2/V3 command families.CameraProfile: sensor geometry, pixels, binning, gain/exposure bounds and raw metadata.CaptureCapabilities: selected workflow, FITS, binning/frame/progress, stop and abort truthfulness.FilterCapabilities: labels, control path, and discovery support.
Profiles are starting evidence, not runtime proof. Parameter configuration and notifications refine runtime filter/exposure/gain options. DWARF 3 remains on the legacy 1.2/device-1 family. DWARF mini uses 1.20/device-4 and V3 camera/focus messages. A model is never silently migrated to the other command family.
The singleton session is the domain boundary used by every router. It owns:
- logical-device reference counts and lazy connection teardown;
- WebSocket, HTTP and FTP clients;
- master lock and time/timezone synchronization;
- request serialization and notification dispatch;
- GOTO, tracking, manual-axis and calibration state;
- camera/focuser state and cached parameter discovery;
- filter, exposure, gain, binning and frame-count application;
- capture identity, baselines, task and phase;
- temperature monitoring and reconnect cleanup;
- FITS/JPEG retrieval and decoding.
DwarfWsClient permits only one outstanding request for a module/command tuple.
The session serializes device commands around that constraint. Primary correlation
keys are consumed only by response packet types; explicitly registered notification
aliases can complete commands such as master lock. Other notifications are dispatched
without disturbing pending requests.
IDLE
-> CONFIGURING
-> WAITING_FOR_DARK
-> STARTING
-> EXPOSING
-> PROCESSING
-> TRANSFERRING
-> READY
Any active state -> ABORTING -> IDLE
Start ambiguity -> UNKNOWN
Configuration/transfer/decode error -> FAILED
- The Camera router validates Alpaca duration, gain, symmetric binning and frame count, copies the request into the session, and rejects overlap.
- A unique capture ID and start/file baselines are recorded.
- The session discovers the exact firmware parameter table. Exposure and gain may deterministically select the nearest discrete firmware value, but command failure stops the capture and the applied value is recorded.
- The selected filter must have a writable control and confirmed ACK/notification.
- Binning, FITS format, and frame count are required feature writes.
- For light frames, dark status is checked. Unknown, missing, or temperature-mismatched darks follow the app's Continue behavior by default; a caller or configuration can require failure instead.
- Production uses raw astronomy live stacking. Direct PHOTO_RAW/PHOTOGRAPH is experimental and disabled by default because long exposure, gain, and raw output have not been proved.
- A start ACK proves start. A mini timeout is accepted only if an independent V3 progress, observation, or device-state value changed.
- Firmware notification
15209.current_countand FTP FITS discovery are watched concurrently. Reaching the requested raw-frame count or finding the first new FITS sends11006immediately; the laterstacked_countis not required. FTP retrieval then finishes with stacking stopped. FTP is preferred, scans only the newest timestamped astronomy folders first, and album is fallback. A result must be newer than the baseline and capture start, and the selected device path is recorded in the capture log. 15208is the authoritative capture lifecycle (idle=0,running=1,stopping=2,stopped=3). A subsequent exposure waits for idle/stopped rather than assuming the first FITS or a11006response makes the camera reusable. Command16405mirrors the APK's whole-device state query and recovers a missed lifecycle notification.- FITS stays unsigned 16-bit after FITS scaling. JPEG stays genuine 8-bit RGB; its source format/bit depth is reported and never promoted to fake sensor depth.
ImageReady,CameraState,PercentCompleted,ImageArray, andImageBytesread the same session capture state.
Graceful StopExposure is not advertised because distinct stop semantics are not proved. AbortExposure sends the astronomy stop command and requires confirmation.
| Module | Role |
|---|---|
dwarf/ws_client.py |
Binary protobuf envelope, correlation, ping, notifications |
dwarf/http_client.py |
Parameter configuration, album listing and static media |
dwarf/ftp_client.py |
Final photo/FITS discovery and download |
dwarf/rtsp_client.py |
Optional live preview; not authoritative final image |
dwarf/ble_provisioner.py |
BLE discovery, authentication and Wi-Fi provisioning |
dwarf/state.py |
Persisted connectivity state |
dwarf/exposure.py |
Firmware exposure-table parsing and deterministic selection |
| Kind | Examples |
|---|---|
| Device authoritative | ACK/error, lock, notifications, temperature, new file |
| Locally inferred | Timer percentage, recent-GOTO validity, inter-signal phase |
| Cached | Parameter tables, filter options, last FTP/album entry |
| Simulated | Synthetic image/motion/focus under force_simulation |
| Persisted | STA IP, mode, timezone, BLE address, Wi-Fi credential cache |
The persisted Wi-Fi password cache is plaintext and is a documented security debt. It must not be logged or included in diagnostics.
All .proto files are sources. python scripts/generate_protos.py uses the exact
grpcio-tools development dependency, generates all bindings, and rewrites package
imports. --check detects missing/stale bindings. Hatchling builds the package;
PyInstaller remains the supported Windows GUI executable path. uv.lock is the
reproducible application/development environment.
See the engineering audit for evidence, history, dependency decisions, protocol confidence, test results, and limitations.