Loading noctua/api/camera.py +63 −63 Original line number Diff line number Diff line Loading @@ -196,87 +196,87 @@ class SnapshotState(BaseResource): res = constants.camera_state.get(raw, "Off") return self.make_response(res, raw_data=raw) class Snapshot(BaseResource): """Manage the acquisition of an image using the sequencer.""" # class Snapshot(BaseResource): # """Manage the acquisition of an image using the sequencer.""" async def get(self): """Retrieve the status of the image process.""" # async def get(self): # """Retrieve the status of the image process.""" data = { "paused": seq.tpl.paused if seq.tpl else None, "quitting": seq.tpl.aborted if seq.tpl else None, } return self.make_response(data) # data = { # "paused": seq.tpl.paused if seq.tpl else None, # "quitting": seq.tpl.aborted if seq.tpl else None, # } # return self.make_response(data) async def post(self): """Start a new image.""" data = await self.get_payload() # Templates are Layer 2 await self.run_blocking(seq.tpl.run, data) return self.make_response(getattr(seq.tpl, 'filenames', [])) # async def post(self): # """Start a new image.""" # data = await self.get_payload() # # Templates are Layer 2 # await self.run_blocking(seq.tpl.run, data) # return self.make_response(getattr(seq.tpl, 'filenames', [])) async def delete(self): """Stop the image process.""" # async def delete(self): # """Stop the image process.""" res = await self.run_blocking(seq.tpl.abort) return self.make_response(res) # res = await self.run_blocking(seq.tpl.abort) # return self.make_response(res) class SnapshotRecenter(BaseResource): """Manage the recentering via the the apply_offset function""" # class SnapshotRecenter(BaseResource): # """Manage the recentering via the the apply_offset function""" async def get(self): """Retrieve the recenter status and parameters of the box.""" # async def get(self): # """Retrieve the recenter status and parameters of the box.""" res = { "recenter": getattr(seq.tpl, 'recenter', None), "box": getattr(seq.tpl, 'box', None), } return self.make_response(res) # res = { # "recenter": getattr(seq.tpl, 'recenter', None), # "box": getattr(seq.tpl, 'box', None), # } # return self.make_response(res) async def put(self): """Set a given box to the recentering function""" # async def put(self): # """Set a given box to the recentering function""" data = await self.get_payload() if seq.tpl: seq.tpl.box = data return self.make_response(getattr(seq.tpl, 'box', None)) # data = await self.get_payload() # if seq.tpl: # seq.tpl.box = data # return self.make_response(getattr(seq.tpl, 'box', None)) async def post(self): """Enable the recentering function.""" # async def post(self): # """Enable the recentering function.""" if seq.tpl: seq.tpl.recenter = True return self.make_response(True) # if seq.tpl: # seq.tpl.recenter = True # return self.make_response(True) async def delete(self): """Disable the recentering function.""" # async def delete(self): # """Disable the recentering function.""" if seq.tpl: seq.tpl.recenter = False return self.make_response(False) # if seq.tpl: # seq.tpl.recenter = False # return self.make_response(False) class SnapshotDomeslewing(BaseResource): """Get dome slewing status""" # class SnapshotDomeslewing(BaseResource): # """Get dome slewing status""" async def get(self): """Retrieve the domeslewing status""" # async def get(self): # """Retrieve the domeslewing status""" res = {"domeslewing": getattr(seq.tpl, 'domeslewing', None)} return self.make_response(res) # res = {"domeslewing": getattr(seq.tpl, 'domeslewing', None)} # return self.make_response(res) async def post(self): """Enable the domeslewing function.""" # async def post(self): # """Enable the domeslewing function.""" if seq.tpl: seq.tpl.domeslewing = True return self.make_response(True) # if seq.tpl: # seq.tpl.domeslewing = True # return self.make_response(True) async def delete(self): """Disable the domeslewing function.""" # async def delete(self): # """Disable the domeslewing function.""" if seq.tpl: seq.tpl.domeslewing = False return self.make_response(False) # if seq.tpl: # seq.tpl.domeslewing = False # return self.make_response(False) class Connection(BaseResource): Loading noctua/config/api.ini +40 −36 Original line number Diff line number Diff line Loading @@ -199,9 +199,9 @@ device = cam resource = CoolerTemperatureSetpoint device = cam [/camera/cooler/warmup] resource = CoolerWarmup device = cam # [/camera/cooler/warmup] # resource = CoolerWarmup # device = cam # [/camera/filters] # resource = Filters Loading @@ -216,15 +216,6 @@ device = cam resource = FilterMovement device = cam # [/camera/frame] # resource = Frame # device = cam # [/camera/frame/custom] # resource = FrameCustom # device = cam # depends-on = /camera/power [/camera/frame/full] resource = FrameFull device = cam Loading @@ -237,32 +228,41 @@ device = cam resource = FrameSmall device = cam [/camera/snapshot/raw] resource = SnapshotRaw [/camera/settings] resource = Settings device = cam [/camera/snapshot] resource = Snapshot [/camera/snapshot/raw] resource = SnapshotRaw device = cam [/camera/snapshot/state] resource = SnapshotState device = cam [/camera/snapshot/recenter] resource = SnapshotRecenter device = cam # [/camera/frame] # resource = Frame # device = cam [/camera/snapshot/domeslewing] resource = SnapshotDomeslewing device = cam # [/camera/frame/custom] # resource = FrameCustom # device = cam # depends-on = /camera/power [/camera/settings] resource = Settings device = cam # [/camera/snapshot] # resource = Snapshot # device = cam # [/camera/snapshot/recenter] # resource = SnapshotRecenter # device = cam # [/camera/snapshot/domeslewing] # resource = SnapshotDomeslewing # device = cam ############## # camera2 (Atik — spectro/echelle station) # camera2 (Atik - spectro) ############## [/camera2/power] Loading Loading @@ -293,25 +293,29 @@ device = cam2 resource = CoolerTemperatureSetpoint device = cam2 [/camera2/cooler/warmup] resource = CoolerWarmup # [/camera2/cooler/warmup] # resource = CoolerWarmup # device = cam2 [/camera2/settings] resource = Settings device = cam2 [/camera2/snapshot] resource = Snapshot [/camera2/snapshot/raw] resource = SnapshotRaw device = cam2 [/camera2/snapshot/state] resource = SnapshotState device = cam2 [/camera2/snapshot/recenter] resource = SnapshotRecenter device = cam2 # [/camera2/snapshot] # resource = Snapshot # device = cam2 [/camera2/settings] resource = Settings device = cam2 # [/camera2/snapshot/recenter] # resource = SnapshotRecenter # device = cam2 # [/camera2/snapshot/domeslewing] # resource = SnapshotDomeslewing Loading noctua/devices/atik.py +4 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,10 @@ Interface with a Atik camera device using the SDK SDK Downloaded from https://downloads.atik-cameras.com/AtikCamerasSDK_2025_11_11_Master_2111.zip sudo apt install g++ sudo apt install libusb-1.0-0-dev sudo apt install libudev-dev sudo cp ../lib/Linux/atik.rules /usr/lib/udev/rules.d/ sudo udevadm control --reload-rules && sudo udevadm trigger sudo cp ../lib/Linux/64/NoFlyCapture/libatikcameras.so /usr/lib/ Loading noctua/web/pages/macros/sections/control_panel.html +7 −7 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ #} {% macro mount_pointing() %} <div class="card bg-dark border-secondary shadow-sm mb-3"> <div class="card mb-3"> <div class="card-body"> <h6 class="card-title text-info mb-3">Mount Pointing</h6> Loading Loading @@ -68,7 +68,7 @@ {% macro camera_exposure() %} <div class="card bg-dark border-secondary shadow-sm mb-3"> <div class="card mb-3"> <div class="card-body"> <h6 class="card-title text-info mb-3">Observation (Expose)</h6> Loading Loading @@ -151,7 +151,7 @@ {% endmacro %} {% macro stage_control() %} <div class="card bg-dark border-secondary shadow-sm mb-3"> <div class="card mb-3"> <div class="card-body"> <h6 class="card-title text-info d-flex justify-content-between"> Mirror Stage Loading @@ -166,11 +166,11 @@ <!-- Named Position Buttons (Step 1) --> <div class="btn-group btn-group-sm w-100 mb-3"> <button class="btn btn-outline-secondary btn-universal" <button class="btn btn-outline-primary btn-universal" data-method="PUT" data-url="/stage/named" data-value='"imaging"'>Imaging</button> <button class="btn btn-outline-secondary btn-universal" <button class="btn btn-outline-primary btn-universal" data-method="PUT" data-url="/stage/named" data-value='"spectro"'>Spectro</button> <button class="btn btn-outline-secondary btn-universal" <button class="btn btn-outline-primary btn-universal" data-method="PUT" data-url="/stage/named" data-value='"echelle"'>Échelle</button> </div> Loading Loading @@ -259,7 +259,7 @@ {% macro camera_windowing(label, fields) %} {% set safe_id = label | replace(' ', '-') | lower %} <div class="card bg-dark border-secondary shadow-sm mb-3"> <div class="card mb-3"> <div class="card-body"> <!-- Header: Widget title and global telemetry readout --> Loading noctua/web/pages/macros/sections/webcam_panel.html +22 −23 Original line number Diff line number Diff line Loading @@ -7,9 +7,9 @@ {% from "macros/widgets.html" import widget_onoff, widget_toggle %} {% macro webcam_panel() %} <div class="card bg-dark shadow border-secondary mb-3" id="webcam-card" data-subsystem="webcam"> <div class="card mb-3" id="webcam-card" data-subsystem="webcam"> <div class="position-relative bg-black text-center" style="min-height: 200px;"> <div class="position-relative text-center" style="min-height: 200px;"> <img id="webcam-snapshot" class="img-fluid w-100 object-fit-contain" style="max-height: 350px;" alt="Waiting for webcam..." src=""> </div> Loading @@ -21,11 +21,10 @@ {{ widget_onoff('Lamp', 'telescope-lamp', '/telescope/lamp') }} </div> <hr class="border-secondary my-2"> <aside class="row align-items-end g-2 mt-1"> <div class="col"> <label class="form-label small text-muted">Alt / Az delta</label> <div class="col-md-4 d-flex"> <label class="col-form-label">Move</label> </div> <div class="input-group input-group-sm"> <input id="webcam-position" class="form-control" type="number" value="20" style="max-width: 70px;"> Loading @@ -40,7 +39,7 @@ data-sign="+1" data-mode="1" title="Right">→</button> </div> </div> <div class="col-auto"> <div class="col"> {{ widget_toggle({'buttons': [ {'label': "Reset", 'endpoint': "/webcam/position", 'val': [15, 180], 'method': "PUT"}, {'label': "Look top", 'endpoint': "/webcam/position", 'val': [55, 170], 'method': "PUT"} Loading Loading
noctua/api/camera.py +63 −63 Original line number Diff line number Diff line Loading @@ -196,87 +196,87 @@ class SnapshotState(BaseResource): res = constants.camera_state.get(raw, "Off") return self.make_response(res, raw_data=raw) class Snapshot(BaseResource): """Manage the acquisition of an image using the sequencer.""" # class Snapshot(BaseResource): # """Manage the acquisition of an image using the sequencer.""" async def get(self): """Retrieve the status of the image process.""" # async def get(self): # """Retrieve the status of the image process.""" data = { "paused": seq.tpl.paused if seq.tpl else None, "quitting": seq.tpl.aborted if seq.tpl else None, } return self.make_response(data) # data = { # "paused": seq.tpl.paused if seq.tpl else None, # "quitting": seq.tpl.aborted if seq.tpl else None, # } # return self.make_response(data) async def post(self): """Start a new image.""" data = await self.get_payload() # Templates are Layer 2 await self.run_blocking(seq.tpl.run, data) return self.make_response(getattr(seq.tpl, 'filenames', [])) # async def post(self): # """Start a new image.""" # data = await self.get_payload() # # Templates are Layer 2 # await self.run_blocking(seq.tpl.run, data) # return self.make_response(getattr(seq.tpl, 'filenames', [])) async def delete(self): """Stop the image process.""" # async def delete(self): # """Stop the image process.""" res = await self.run_blocking(seq.tpl.abort) return self.make_response(res) # res = await self.run_blocking(seq.tpl.abort) # return self.make_response(res) class SnapshotRecenter(BaseResource): """Manage the recentering via the the apply_offset function""" # class SnapshotRecenter(BaseResource): # """Manage the recentering via the the apply_offset function""" async def get(self): """Retrieve the recenter status and parameters of the box.""" # async def get(self): # """Retrieve the recenter status and parameters of the box.""" res = { "recenter": getattr(seq.tpl, 'recenter', None), "box": getattr(seq.tpl, 'box', None), } return self.make_response(res) # res = { # "recenter": getattr(seq.tpl, 'recenter', None), # "box": getattr(seq.tpl, 'box', None), # } # return self.make_response(res) async def put(self): """Set a given box to the recentering function""" # async def put(self): # """Set a given box to the recentering function""" data = await self.get_payload() if seq.tpl: seq.tpl.box = data return self.make_response(getattr(seq.tpl, 'box', None)) # data = await self.get_payload() # if seq.tpl: # seq.tpl.box = data # return self.make_response(getattr(seq.tpl, 'box', None)) async def post(self): """Enable the recentering function.""" # async def post(self): # """Enable the recentering function.""" if seq.tpl: seq.tpl.recenter = True return self.make_response(True) # if seq.tpl: # seq.tpl.recenter = True # return self.make_response(True) async def delete(self): """Disable the recentering function.""" # async def delete(self): # """Disable the recentering function.""" if seq.tpl: seq.tpl.recenter = False return self.make_response(False) # if seq.tpl: # seq.tpl.recenter = False # return self.make_response(False) class SnapshotDomeslewing(BaseResource): """Get dome slewing status""" # class SnapshotDomeslewing(BaseResource): # """Get dome slewing status""" async def get(self): """Retrieve the domeslewing status""" # async def get(self): # """Retrieve the domeslewing status""" res = {"domeslewing": getattr(seq.tpl, 'domeslewing', None)} return self.make_response(res) # res = {"domeslewing": getattr(seq.tpl, 'domeslewing', None)} # return self.make_response(res) async def post(self): """Enable the domeslewing function.""" # async def post(self): # """Enable the domeslewing function.""" if seq.tpl: seq.tpl.domeslewing = True return self.make_response(True) # if seq.tpl: # seq.tpl.domeslewing = True # return self.make_response(True) async def delete(self): """Disable the domeslewing function.""" # async def delete(self): # """Disable the domeslewing function.""" if seq.tpl: seq.tpl.domeslewing = False return self.make_response(False) # if seq.tpl: # seq.tpl.domeslewing = False # return self.make_response(False) class Connection(BaseResource): Loading
noctua/config/api.ini +40 −36 Original line number Diff line number Diff line Loading @@ -199,9 +199,9 @@ device = cam resource = CoolerTemperatureSetpoint device = cam [/camera/cooler/warmup] resource = CoolerWarmup device = cam # [/camera/cooler/warmup] # resource = CoolerWarmup # device = cam # [/camera/filters] # resource = Filters Loading @@ -216,15 +216,6 @@ device = cam resource = FilterMovement device = cam # [/camera/frame] # resource = Frame # device = cam # [/camera/frame/custom] # resource = FrameCustom # device = cam # depends-on = /camera/power [/camera/frame/full] resource = FrameFull device = cam Loading @@ -237,32 +228,41 @@ device = cam resource = FrameSmall device = cam [/camera/snapshot/raw] resource = SnapshotRaw [/camera/settings] resource = Settings device = cam [/camera/snapshot] resource = Snapshot [/camera/snapshot/raw] resource = SnapshotRaw device = cam [/camera/snapshot/state] resource = SnapshotState device = cam [/camera/snapshot/recenter] resource = SnapshotRecenter device = cam # [/camera/frame] # resource = Frame # device = cam [/camera/snapshot/domeslewing] resource = SnapshotDomeslewing device = cam # [/camera/frame/custom] # resource = FrameCustom # device = cam # depends-on = /camera/power [/camera/settings] resource = Settings device = cam # [/camera/snapshot] # resource = Snapshot # device = cam # [/camera/snapshot/recenter] # resource = SnapshotRecenter # device = cam # [/camera/snapshot/domeslewing] # resource = SnapshotDomeslewing # device = cam ############## # camera2 (Atik — spectro/echelle station) # camera2 (Atik - spectro) ############## [/camera2/power] Loading Loading @@ -293,25 +293,29 @@ device = cam2 resource = CoolerTemperatureSetpoint device = cam2 [/camera2/cooler/warmup] resource = CoolerWarmup # [/camera2/cooler/warmup] # resource = CoolerWarmup # device = cam2 [/camera2/settings] resource = Settings device = cam2 [/camera2/snapshot] resource = Snapshot [/camera2/snapshot/raw] resource = SnapshotRaw device = cam2 [/camera2/snapshot/state] resource = SnapshotState device = cam2 [/camera2/snapshot/recenter] resource = SnapshotRecenter device = cam2 # [/camera2/snapshot] # resource = Snapshot # device = cam2 [/camera2/settings] resource = Settings device = cam2 # [/camera2/snapshot/recenter] # resource = SnapshotRecenter # device = cam2 # [/camera2/snapshot/domeslewing] # resource = SnapshotDomeslewing Loading
noctua/devices/atik.py +4 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,10 @@ Interface with a Atik camera device using the SDK SDK Downloaded from https://downloads.atik-cameras.com/AtikCamerasSDK_2025_11_11_Master_2111.zip sudo apt install g++ sudo apt install libusb-1.0-0-dev sudo apt install libudev-dev sudo cp ../lib/Linux/atik.rules /usr/lib/udev/rules.d/ sudo udevadm control --reload-rules && sudo udevadm trigger sudo cp ../lib/Linux/64/NoFlyCapture/libatikcameras.so /usr/lib/ Loading
noctua/web/pages/macros/sections/control_panel.html +7 −7 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ #} {% macro mount_pointing() %} <div class="card bg-dark border-secondary shadow-sm mb-3"> <div class="card mb-3"> <div class="card-body"> <h6 class="card-title text-info mb-3">Mount Pointing</h6> Loading Loading @@ -68,7 +68,7 @@ {% macro camera_exposure() %} <div class="card bg-dark border-secondary shadow-sm mb-3"> <div class="card mb-3"> <div class="card-body"> <h6 class="card-title text-info mb-3">Observation (Expose)</h6> Loading Loading @@ -151,7 +151,7 @@ {% endmacro %} {% macro stage_control() %} <div class="card bg-dark border-secondary shadow-sm mb-3"> <div class="card mb-3"> <div class="card-body"> <h6 class="card-title text-info d-flex justify-content-between"> Mirror Stage Loading @@ -166,11 +166,11 @@ <!-- Named Position Buttons (Step 1) --> <div class="btn-group btn-group-sm w-100 mb-3"> <button class="btn btn-outline-secondary btn-universal" <button class="btn btn-outline-primary btn-universal" data-method="PUT" data-url="/stage/named" data-value='"imaging"'>Imaging</button> <button class="btn btn-outline-secondary btn-universal" <button class="btn btn-outline-primary btn-universal" data-method="PUT" data-url="/stage/named" data-value='"spectro"'>Spectro</button> <button class="btn btn-outline-secondary btn-universal" <button class="btn btn-outline-primary btn-universal" data-method="PUT" data-url="/stage/named" data-value='"echelle"'>Échelle</button> </div> Loading Loading @@ -259,7 +259,7 @@ {% macro camera_windowing(label, fields) %} {% set safe_id = label | replace(' ', '-') | lower %} <div class="card bg-dark border-secondary shadow-sm mb-3"> <div class="card mb-3"> <div class="card-body"> <!-- Header: Widget title and global telemetry readout --> Loading
noctua/web/pages/macros/sections/webcam_panel.html +22 −23 Original line number Diff line number Diff line Loading @@ -7,9 +7,9 @@ {% from "macros/widgets.html" import widget_onoff, widget_toggle %} {% macro webcam_panel() %} <div class="card bg-dark shadow border-secondary mb-3" id="webcam-card" data-subsystem="webcam"> <div class="card mb-3" id="webcam-card" data-subsystem="webcam"> <div class="position-relative bg-black text-center" style="min-height: 200px;"> <div class="position-relative text-center" style="min-height: 200px;"> <img id="webcam-snapshot" class="img-fluid w-100 object-fit-contain" style="max-height: 350px;" alt="Waiting for webcam..." src=""> </div> Loading @@ -21,11 +21,10 @@ {{ widget_onoff('Lamp', 'telescope-lamp', '/telescope/lamp') }} </div> <hr class="border-secondary my-2"> <aside class="row align-items-end g-2 mt-1"> <div class="col"> <label class="form-label small text-muted">Alt / Az delta</label> <div class="col-md-4 d-flex"> <label class="col-form-label">Move</label> </div> <div class="input-group input-group-sm"> <input id="webcam-position" class="form-control" type="number" value="20" style="max-width: 70px;"> Loading @@ -40,7 +39,7 @@ data-sign="+1" data-mode="1" title="Right">→</button> </div> </div> <div class="col-auto"> <div class="col"> {{ widget_toggle({'buttons': [ {'label': "Reset", 'endpoint': "/webcam/position", 'val': [15, 180], 'method': "PUT"}, {'label': "Look top", 'endpoint': "/webcam/position", 'val': [55, 170], 'method': "PUT"} Loading