Camera Detector Controller Interface Software
If you encounter any problems or have questions about this project, please open an issue on the GitHub Issues page. Your feedback helps us improve the project!
- CMake 3.12 or higher
- cfitsio and CCFits libraries (expected in
/usr/local/lib) - gtest (Google Test) library (needed to run unit tests)
| Archon Controllers | ARC Controllers |
|---|---|
g++ 8.1 or higher (and C++17) |
g++ 8.3 (and C++17) |
| ARC API 3.6 and Arc66PCIe driver |
-
Change to the build directory:
$ cd build -
Start with a clean build: Delete the contents of the build directory, including the
CMakeFiles/subdirectory, but not the.gitignorefile.$ rm -Rf * -
Create the Makefile by running CMake (from the build directory).
-DCONTROLLER=is required; CMake stops with an error if it is missing:Archon ARC $ cmake -DCONTROLLER=archon ..$ cmake -DCONTROLLER=astrocam ..Add
-DINSTRUMENT=to build an instrument module, whose sources come fromcamerad/Instruments/<name>:$ cmake -DCONTROLLER=archon -DINSTRUMENT=hispec_tracking_camera ..
-DINTERFACE_TYPE=is separate from-DCONTROLLER=and selects only which emulator is built. It defaults toArchon;-DINTERFACE_TYPE=AstroCamskips the emulator, since none is implemented for ARC.To enable the shared-memory output (
SHM_ENABLEDin a.cfgfile, see Frame Outputs below), add-DENABLE_SHM_OUTPUT=ON -DImageStreamIO_DIR=<prefix>/lib/cmake:$ cmake -DENABLE_SHM_OUTPUT=ON -DImageStreamIO_DIR=/usr/local/lib/cmake ..
This requires ImageStreamIO to already be built and installed, since it isn't packaged for common distros:
$ git clone https://github.com/milk-org/ImageStreamIO.git $ cd ImageStreamIO && mkdir build && cd build $ cmake .. $ make $ sudo make install
ImageStreamIO's own
Config.cmakefiles install directly under<prefix>/lib/cmake/rather than the CMake-conventional<prefix>/lib/cmake/ImageStreamIO/, so-DImageStreamIO_DIR=...must always be given explicitly, even for a standard system-wide install.To build the Python module (see Python Module below), add
-DBUILD_PYTHON_MODULE=ON. It is off by default, so builds that don't want it never need pybind11:$ pip install pybind11 $ cmake -DBUILD_PYTHON_MODULE=ON ..
pybind11 is located by asking the interpreter CMake selected, so pass
-DPython3_EXECUTABLE=...to build against a specific one (a virtualenv, say). The module and that interpreter then always agree on the ABI.pybind11 is header-only and needed only to compile: the built module links cfitsio, CCfits and OpenCV but not pybind11, so it does not have to be present where the module is imported.
-
Compile the sources:
$ make
-
Run the Camera Server:
The configuration file is passed with
--configand is required.-
As a foreground process, logging to the console as well as to
LOGPATH:$ ../bin/camerad --foreground --config <file.cfg>
-
As a daemon, which is the default without
--foreground:$ ../bin/camerad --config <file.cfg>
Replace
<file.cfg>with an appropriate configuration file. See the example.cfgfiles in theconfigdirectory (per-instrument deployment configs live in each instrument's own repo under itsconfig/directory;config/demohere is a generic example).Logging always goes to a daily file under
LOGPATH. Whether it is also written to stderr follows--foreground, so an operator watching a console sees it and a daemon does not duplicate its whole log into the stderr redirect.LOG_STDERRin the.cfgoverrides that either way. -
-
(Optional) Run the Archon Emulator:
$ ../bin/emulator <file.cfg> -i <instrument>
The emulator reads
EMULATOR_PORTandEMULATOR_SYSTEMfrom the same.cfgthe server uses, so pointARCHON_IP/ARCHON_PORTat it to run without hardware.-i genericsuits the shipped test configs. -
(Optional) Run Unit Tests. The tests are excluded from the default target, so build them first:
$ make run_unit_tests $ ../bin/run_unit_tests
Built with -DBUILD_PYTHON_MODULE=ON, camera_interface lets a Python process own a camera directly, with no camerad process and no text protocol in between. It performs the same startup camerad does, then exposes the interface as methods:
import camera_interface
camera = camera_interface.Camera("hispecatc.cfg")
camera.open()
camera.load()
camera.power("on")
camera.exptime("0")
camera.expose("1")
print(camera.instrument_commands()) # this build's instrument-specific commands
camera.instrument_cmd("roi", "51 60 51 60")
print(camera.output_status()) # frames written, dropped, last fileEvery command camerad accepts is reachable: the base commands are bound as methods, instrument-specific ones go through instrument_cmd() (enumerable with instrument_commands()), and controller-specific ones such as mode, raw, readacf, loadtiming, heater and sensor go through controller_cmd(). Only exit is omitted, since the process belongs to the caller.
The controller and instrument are fixed at CMake configure time, so instrument_name() and controller_name() report which build was loaded. A failed command raises RuntimeError.
output_status() is a snapshot, never a barrier: the FITS writer queues and drops frames by design because disk is slower than acquisition can be, so nothing here lets a caller stall acquisition by waiting on an output. Anything needing to be woken per frame should attach to the shared-memory segment, which posts semaphores.
Commands release the GIL while they run, so a blocking expose() leaves the rest of the process responsive.
Logging follows LOG_STDERR from the .cfg; pass log_to_stderr= to override it per session. The C++ log always goes to its daily file under LOGPATH.
Every instrument publishes each acquired frame to one or more outputs, configured entirely via .cfg file keys (Camera::Interface::configure_frame_outputs() builds them from Camera::apply_config_overrides(), called once at startup for every instrument, not just HISPEC). Both outputs are independent; either, both, or neither can be enabled per instrument.
Writes one FITS file per frame asynchronously (a queue plus a dedicated writer thread, so the readout thread never blocks on disk I/O).
| Key | Default | Meaning |
|---|---|---|
FITS_ENABLED |
no |
Enable the FITS writer |
FITS_OUTPUT_DIR |
/tmp/images |
Base directory for FITS files; must already exist |
FITS_AUTODIR |
no |
Write into a YYYYMMDD subdirectory of FITS_OUTPUT_DIR |
FITS_BASENAME |
tracking |
Base filename for FITS files |
FITS_QUEUE_SIZE |
32 |
Max frames buffered for the writer thread; oldest is dropped if full |
FITS_DRAIN_TIMEOUT_MS |
5000 |
On shutdown, how long to keep draining the queue before giving up |
Publishes each frame as an ImageStreamIO shared-memory stream, readable by AO frameworks like cacao. Requires building with -DENABLE_SHM_OUTPUT=ON (see Build Instructions above); if a .cfg file sets SHM_ENABLED=yes on a build compiled without that flag, camerad logs a warning and skips it rather than failing.
| Key | Default | Meaning |
|---|---|---|
SHM_ENABLED |
no |
Enable the shared-memory writer |
SHM_SEGMENT_NAME |
camera |
ImageStreamIO stream name |
SHM_RING_BUFFER_SIZE |
4 |
Depth of ImageStreamIO's internal history ring buffer (CBsize); the live frame a real-time reader sees is separate from this |
SHM_DIR |
(unset) | Base directory ImageStreamIO writes into. If unset, ImageStreamIO falls back to its own default resolution (MILK_SHM_DIR env var, then /milk/shm). If set, it must already exist and be writable. |
Frame geometry (width/height/pixel depth) isn't a config key: it's fixed for an ImageStreamIO stream's whole life, so the writer (re)creates the stream automatically whenever it sees the geometry change from what's currently allocated.
For Archon Heater and HeaterX modules, the server exposes commands to control the closed-loop heaters and read/control the on-board temperature sensors. These require firmware to be loaded and a sufficiently recent Archon backplane.
Control heater A or B on the given module: enable state and target, PID
parameters, ramp, current limit, and input sensor.
heater <module> <A|B> [ <on|off> [target] | <target> | PID [<p> <i> <d>]
| RAMP [<on|off> [rate]] | ILIM [val] | INPUT [A|B|C] ]
| Form | Effect |
|---|---|
heater <module> <A|B> |
get enable state and target |
heater <module> <A|B> <on|off> [target] |
set enable state, optionally the target |
heater <module> <A|B> <target> |
set the target (range depends on backplane version) |
heater <module> <A|B> PID [<p> <i> <d>] |
get/set the P, I, D parameters (0–10000 each) |
heater <module> <A|B> RAMP [<on|off> [rate]] |
get/set ramp enable and ramprate (1–32767) |
heater <module> <A|B> ILIM [val] |
get/set the current limit (0–10000) |
heater <module> <A|B> INPUT [A|B|C] |
get/set the input sensor (C requires HeaterX) |
The target range defaults to backplane-version-dependent limits and can be
overridden in the .cfg file with HEATER_TARGET_MIN / HEATER_TARGET_MAX
(degrees C).
Set or get a temperature sensor's RTD excitation current and digital averaging.
sensor <module> <A|B|C> [ <current> | AVG [ <N> ] ]
| Form | Effect |
|---|---|
sensor <module> <A|B|C> |
get the excitation current (nano-amps) |
sensor <module> <A|B|C> <current> |
set the excitation current, 0–1600000 nA |
sensor <module> <A|B|C> AVG |
get the digital averaging count |
sensor <module> <A|B|C> AVG <N> |
set the digital averaging count N ∈ {1,2,4,8,…,256} |
Sensor C is available only on HeaterX modules.
David Hale
dhale@astro.caltech.edu