Commit 14f35fbe authored by vertighel's avatar vertighel
Browse files

Fix mappatura stato errore Atik; merge snapshot_*.py in un unico snapshot.py



atik.py normalizzava male ArtemisCameraState: l'SDK usa 5=CAMERA_FLUSHING
(non errore) e -1=CAMERA_ERROR (mai controllato), mentre stx/stl/mako sono
già allineati alla convenzione condivisa camera_state. Aggiunta _STATE_MAP
per convertire i codici nativi, esteso camera_state con 6=Flushing.

snapshot_imaging.py/snapshot_spectro.py/snapshot_echelle.py erano quasi
identici (differivano solo per default camera e gestione filtro per
l'échelle): unificati in un unico snapshot.py generico, control.js
aggiornato a puntare tutte e tre le stazioni al template "snapshot".

Co-Authored-By: default avatarClaude Sonnet 5 <noreply@anthropic.com>
parent 98a89ee9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -139,6 +139,7 @@ camera_state = {
    3: "Readout",
    4: "Downloading", # Atik
    5: "Error",
    6: "Flushing", # Atik
    None: none_val,
}

+19 −2
Original line number Diff line number Diff line
@@ -471,11 +471,28 @@ class Camera(BaseDevice):

    # --- Properties ---

    # Maps the Artemis SDK's native ArtemisCameraState codes onto the
    # project's shared camera_state convention (noctua.config.constants):
    # the SDK's 5 is CAMERA_FLUSHING (not an error), and its real error
    # code is -1, not 5.
    _STATE_MAP = {
        -1: 5,  # CAMERA_ERROR -> shared Error
        0: 0,   # CAMERA_IDLE
        1: 1,   # CAMERA_WAITING
        2: 2,   # CAMERA_EXPOSING
        3: 3,   # CAMERA_READING -> Readout
        4: 4,   # CAMERA_DOWNLOADING
        5: 6,   # CAMERA_FLUSHING -> shared Flushing
    }

    @property
    def state(self):
        """int : Current camera state (0=Idle, 1=Waiting, 2=Exposing, 3=Reading, 5=Error)."""
        """int : Current camera state, in the shared camera_state convention
        (0=Idle, 1=Waiting, 2=Exposing, 3=Readout, 4=Downloading, 5=Error,
        6=Flushing)."""
        h = self._check_connection()
        return self._lib.ArtemisCameraState(h) if h else -1
        raw = self._lib.ArtemisCameraState(h) if h else -1
        return self._STATE_MAP.get(raw, 5)

    @property
    def temperature(self):
+23 −9
Original line number Diff line number Diff line
@@ -19,18 +19,16 @@ from .basetemplate import BaseTemplate
from .fillheader import Template as FillHeader
from .lampsoff import Template as LampsOff

# Cameras whose filter wheel is never driven from here: always left on its
# current ("free") position (échelle/STL, no /scicam3/filter* route in api.ini).
_NO_FILTER_CAMERAS = {"cam3"}


class Template(BaseTemplate):
    def __init__(self):
        super().__init__()
        self.name = "snapshot"
        self.description = "Observes a field with the imaging or spectroscopy camera"
        # self.box = 300
        # self.recenter = False
        # #self.domeslewing = None
        # if not hasattr(self, "domeslewing"):
        #     log.warning("domeslewing attribute undefined, setting to False")
        #     self.domeslewing = False
        self.description = "Observes a field with the imaging, spectroscopic or échelle camera"

    def content(self, params):

@@ -54,6 +52,7 @@ class Template(BaseTemplate):
            exptime = params["exptime"]
            self.domeslewing = params.get("domeslewing") or False
            self.recenter = params.get("recenter") or False
            self.box = params.get("box") or 300

        except KeyError as e:
            msg = f"Parameter {e} not found"
@@ -101,7 +100,7 @@ class Template(BaseTemplate):
        ####################################

        # Changing filter if not the same as the current one (only if camera has a filter wheel)
        if filt and hasattr(cam, 'filter'):
        if filt and hasattr(cam, 'filter') and camera_name not in _NO_FILTER_CAMERAS:
            # On bad ctrl-c, filter can be 15 (?!)
            log.warning(f"Filter is number {cam.filter}")
            log.warning(
@@ -256,4 +255,19 @@ class Template(BaseTemplate):

            log.debug(f"filenames from obs tpl {self.filenames}")

            ######################################
            ##### Calculate and apply offset #####
            ######################################

            log.info(f"Recenter is {self.recenter}")
            if self.recenter:
                log.info(f"... with box={self.box}px")

            if imagetype == "Light":
                if self.recenter:
                    try:
                        apply_offset(
                            fits_file=fits_file, box=self.box, display=False)
                    except Exception as e:
                        log.warning(f"Cannot apply offset: {e}")
        return
+0 −264
Original line number Diff line number Diff line
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# System modules
from time import sleep

# Third-party modules
from astropy.time import Time

# Other templates
from ..config.constants import (camera_state, filter_name, filter_number,
                                filter_state, frame_number, frame_type,
                                image_state, temp_fits, viewer_fits_path)
from .. import devices as _dev
from ..devices import dom, tel
from ..utils.coordinates import apply_offset
from ..utils.logger import log
from .basetemplate import BaseTemplate
from .fillheader import Template as FillHeader
from .lampsoff import Template as LampsOff


class Template(BaseTemplate):
    def __init__(self):
        super().__init__()
        self.name = "snapshot_echelle"
        self.description = "Observes a field with the échelle camera"

    def content(self, params):

        ########################
        ##### Params check #####
        ########################

        try:
            camera_name = params.get("camera") or "cam3"
            cam = getattr(_dev, camera_name)
            fits_file = viewer_fits_path(getattr(cam, '_viewer_key', None))

            objname = params.get("object") or "test"
            repeat = params.get("repeat") or 1
            imagetype = params.get("imagetype") or "Light"
            binning = params.get("binning") or cam.binning
            filt = params.get("filter") or (
                filter_name.get(cam.filter) if hasattr(cam, 'filter') else None)
            xystart = params.get("xystart")
            xyend = params.get("xyend")
            exptime = params["exptime"]
            self.domeslewing = params.get("domeslewing") or False
            self.recenter = params.get("recenter") or False
            self.box = params.get("box") or 300

        except KeyError as e:
            msg = f"Parameter {e} not found"
            log.error(msg)
            self.error.append(msg)
            return

        #########################################
        ##### Switch off lamps if not Flat  #####
        #########################################

        if imagetype != "Flat":
            log.info("Not a 'Flat' imagetype: switching off lamps")
            lao = LampsOff()
            lao.run({})

        if imagetype != "Light":
            log.info("Not a 'Light' imagetype: switching off recentering")
            self.recenter = False

        ########################
        ##### Camera setup #####
        ########################

        # Bringing back the camera to Idle state.
        if cam.state != 0:
            log.warning(
                f"Resetting camera from state {camera_state[cam.state]}.")
            cam.abort()
            sleep(0.1)

        log.info(f"Camera is {camera_state[cam.state]}")

        # Changing binning if not the same as the current one
        if binning != cam.binning:
            log.info(f"Changing binning to {binning}")
            cam.binning = [binning, binning]
            sleep(0.1)

        log.info(f"Camera is {camera_state[cam.state]}")

        # # Changing filter if not the same as the current one (only if camera has a filter wheel)
        # if filt and hasattr(cam, 'filter'):
        #     # On bad ctrl-c, filter can be 15 (?!)
        #     log.warning(f"Filter is number {cam.filter}")
        #     log.warning(
        #         f"Filter is {filter_name[cam.filter]}, we have selected {filt}")

        #     if filter_number[filt] != cam.filter:
        #         msg = f"Changing filter to {filt}, "
        #         msg += f"Which is number {filter_number[filt]}"
        #         log.info(msg)

        #         cam.filter = filter_number[filt]
        #         sleep(0.2)

        #         status = 1
        #         while status != 0:
        #             self.check_pause_or_abort()

        #             try:
        #                 status = int(cam.is_moving)
        #             except ValueError as e:
        #                 msg = f"Error trying to contact the camera: {e}"
        #                 log.error(msg)
        #                 self.error.append(msg)

        #             if status == 2:
        #                 msg = f"Filter {filter_state[status]}"
        #                 log.error(msg)
        #                 self.error.append(msg)
        #                 return
        #             else:
        #                 log.warning(f"Filter {filter_state[status]}")
        #                 sleep(0.75)

        # Changing windowing (only when explicitly provided in params)
        if xystart is not None and xyend is not None:
            log.info(
                f"Changing windowing to xystart:{xystart}, xyend:{xyend}")
            cam.set_window(xystart[0],
                           xystart[1],
                           xyend[0]-xystart[0],
                           xyend[1]-xystart[1])

        #########################
        ##### Exposure loop #####
        #########################

        log.info(f"Starting {repeat} {imagetype} frames of {exptime}s")

        # Looping on the number of exposures
        for exposure in range(repeat):
            if self.check_pause_or_abort():
                return

            # RA, DEC, ALT, AZ, UTC are asked to cabinet
            # simultaneously in order to avoid latency.

            log.info(f"Asking cabinet time and coordinates")
            basic_cabinet = tel.coordinates
            gps = Time(tel.coordinates["utc"], format="unix")

            log.info(f"Starting Exposure {exposure + 1} of {repeat}")

            ##########################
            ##### Start exposure #####
            ##########################

            log.debug(frame_number[imagetype])
            try:
                cam.start(exptime,
                          frame_number[imagetype],
                          datetime=gps.isot)
            except Exception as e:
                msg = f"An error occurred : {e}"
                log.error(msg)
                self.error.append(msg)
                return

            # Some debug time counter
            now = Time.now()

            # Checking if the exposure is still running
            status = 1
            while status != 0:
                if self.check_pause_or_abort():
                    log.debug("check_pause_or_abort returned true. I'm here")
                    return

                try:
                    status = int(cam.state)
                except TypeError as e:
                    msg = f"Error trying to read camera status: {e}"
                    log.error(msg)
                    self.error.append(msg)

                if status == 5:  # i.e. error in camera
                    msg = f"Camera {camera_state[status]}"
                    log.error(msg)
                    self.error.append(msg)
                    return
                else:
                    elapsed = (Time.now() - now).sec.item()
                    msg = f"Camera {
                        camera_state[status]}, {
                        elapsed:.1f}/{exptime}s "
                    msg += f"of exposure {exposure}/{repeat - 1}"
                    log.warning(msg)
                    sleep(0.75)

            log.debug("check_pause_or_abort before the download")
            if self.check_pause_or_abort():
                log.debug("check_pause_or_abort returned true. I'm here")
                return

            ################################
            ##### Download from camera #####
            ################################

            log.info(f"Downloading frame as {temp_fits}")

            # Checking if the image is ready to be downloaded
            while cam.ready != 1:
                log.debug(f"Downloading... (cam state {image_state[cam.ready]})")
                sleep(0.5)
                if self.check_pause_or_abort():
                    log.warning(f"Buffer still {image_state[cam.ready]}")
                    return
            log.debug(f"Downloading... (cam state {image_state[cam.ready]})")

            cam.download()
            log.info(f"Elapsed {(Time.now() - now).sec.item():.2f}")

            ##################################
            ##### Update header and save #####
            ##################################

            fih_params = {
                "object": objname,
                "camera": camera_name,
                "imagetyp": imagetype,
                "fits_file": fits_file,
            }

            fih_params.update(basic_cabinet)  # radec, altaz, utc, lst

            fih = FillHeader()

            fih.run(fih_params)
            log.debug(f"observation {fih.filename}")

            self.filename = fih.filename
            self.filenames.append(fih.filename)

            log.debug(f"filenames from obs tpl {self.filenames}")

            ######################################
            ##### Calculate and apply offset #####
            ######################################

            log.info(f"Recenter is {self.recenter}")
            if self.recenter:
                log.info(f"... with box={self.box}px")

            if imagetype == "Light":
                if self.recenter:
                    try:
                        apply_offset(
                            fits_file=fits_file, box=self.box, display=False)
                    except Exception as e:
                        log.warning(f"Cannot apply offset: {e}")
        return
+0 −270
Original line number Diff line number Diff line
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# System modules
from time import sleep

# Third-party modules
from astropy.time import Time

# Other templates
from ..config.constants import (camera_state, filter_name, filter_number,
                                filter_state, frame_number, frame_type,
                                image_state, temp_fits, viewer_fits_path)
from .. import devices as _dev
from ..devices import dom, tel
from ..utils.coordinates import apply_offset
from ..utils.logger import log
from .basetemplate import BaseTemplate
from .fillheader import Template as FillHeader
from .lampsoff import Template as LampsOff


class Template(BaseTemplate):
    def __init__(self):
        super().__init__()
        self.name = "snapshot_imaging"
        self.description = "Observes a field with the imaging camera"
        # self.box = 300
        # self.recenter = False
        # #self.domeslewing = None
        # if not hasattr(self, "domeslewing"):
        #     log.warning("domeslewing attribute undefined, setting to False")
        #     self.domeslewing = False

    def content(self, params):

        ########################
        ##### Params check #####
        ########################

        try:
            camera_name = params.get("camera") or "cam1"
            cam = getattr(_dev, camera_name)
            fits_file = viewer_fits_path(getattr(cam, '_viewer_key', None))

            objname = params.get("object") or "test"
            repeat = params.get("repeat") or 1
            imagetype = params.get("imagetype") or "Light"
            binning = params.get("binning") or cam.binning
            filt = params.get("filter") or (
                filter_name.get(cam.filter) if hasattr(cam, 'filter') else None)
            xystart = params.get("xystart")
            xyend = params.get("xyend")
            exptime = params["exptime"]
            self.domeslewing = params.get("domeslewing") or False
            self.recenter = params.get("recenter") or False
            self.box = params.get("box") or 300

        except KeyError as e:
            msg = f"Parameter {e} not found"
            log.error(msg)
            self.error.append(msg)
            return

        #########################################
        ##### Switch off lamps if not Flat  #####
        #########################################

        if imagetype != "Flat":
            log.info("Not a 'Flat' imagetype: switching off lamps")
            lao = LampsOff()
            lao.run({})

        if imagetype != "Light":
            log.info("Not a 'Light' imagetype: switching off recentering")
            self.recenter = False

        ########################
        ##### Camera setup #####
        ########################

        # Bringing back the camera to Idle state.
        if cam.state != 0:
            log.warning(
                f"Resetting camera from state {camera_state[cam.state]}.")
            cam.abort()
            sleep(0.1)

        log.info(f"Camera is {camera_state[cam.state]}")

        # Changing binning if not the same as the current one
        if binning != cam.binning:
            log.info(f"Changing binning to {binning}")
            cam.binning = [binning, binning]
            sleep(0.1)

        log.info(f"Camera is {camera_state[cam.state]}")

        # Changing filter if not the same as the current one (only if camera has a filter wheel)
        if filt and hasattr(cam, 'filter'):
            # On bad ctrl-c, filter can be 15 (?!)
            log.warning(f"Filter is number {cam.filter}")
            log.warning(
                f"Filter is {filter_name[cam.filter]}, we have selected {filt}")

            if filter_number[filt] != cam.filter:
                msg = f"Changing filter to {filt}, "
                msg += f"Which is number {filter_number[filt]}"
                log.info(msg)

                cam.filter = filter_number[filt]
                sleep(0.2)

                status = 1
                while status != 0:
                    self.check_pause_or_abort()

                    try:
                        status = int(cam.is_moving)
                    except ValueError as e:
                        msg = f"Error trying to contact the camera: {e}"
                        log.error(msg)
                        self.error.append(msg)

                    if status == 2:
                        msg = f"Filter {filter_state[status]}"
                        log.error(msg)
                        self.error.append(msg)
                        return
                    else:
                        log.warning(f"Filter {filter_state[status]}")
                        sleep(0.75)

        # Changing windowing (only when explicitly provided in params)
        if xystart is not None and xyend is not None:
            log.info(
                f"Changing windowing to xystart:{xystart}, xyend:{xyend}")
            cam.set_window(xystart[0],
                           xystart[1],
                           xyend[0]-xystart[0],
                           xyend[1]-xystart[1])

        #########################
        ##### Exposure loop #####
        #########################

        log.info(f"Starting {repeat} {imagetype} frames of {exptime}s")

        # Looping on the number of exposures
        for exposure in range(repeat):
            if self.check_pause_or_abort():
                return

            # RA, DEC, ALT, AZ, UTC are asked to cabinet
            # simultaneously in order to avoid latency.

            log.info(f"Asking cabinet time and coordinates")
            basic_cabinet = tel.coordinates
            gps = Time(tel.coordinates["utc"], format="unix")

            log.info(f"Starting Exposure {exposure + 1} of {repeat}")

            ##########################
            ##### Start exposure #####
            ##########################

            log.debug(frame_number[imagetype])
            try:
                cam.start(exptime,
                          frame_number[imagetype],
                          datetime=gps.isot)
            except Exception as e:
                msg = f"An error occurred : {e}"
                log.error(msg)
                self.error.append(msg)
                return

            # Some debug time counter
            now = Time.now()

            # Checking if the exposure is still running
            status = 1
            while status != 0:
                if self.check_pause_or_abort():
                    log.debug("check_pause_or_abort returned true. I'm here")
                    return

                try:
                    status = int(cam.state)
                except TypeError as e:
                    msg = f"Error trying to read camera status: {e}"
                    log.error(msg)
                    self.error.append(msg)

                if status == 5:  # i.e. error in camera
                    msg = f"Camera {camera_state[status]}"
                    log.error(msg)
                    self.error.append(msg)
                    return
                else:
                    elapsed = (Time.now() - now).sec.item()
                    msg = f"Camera {
                        camera_state[status]}, {
                        elapsed:.1f}/{exptime}s "
                    msg += f"of exposure {exposure}/{repeat - 1}"
                    log.warning(msg)
                    sleep(0.75)

            log.debug("check_pause_or_abort before the download")
            if self.check_pause_or_abort():
                log.debug("check_pause_or_abort returned true. I'm here")
                return

            ################################
            ##### Download from camera #####
            ################################

            log.info(f"Downloading frame as {temp_fits}")

            # Checking if the image is ready to be downloaded
            while cam.ready != 1:
                log.debug(f"Downloading... (cam state {image_state[cam.ready]})")
                sleep(0.5)
                if self.check_pause_or_abort():
                    log.warning(f"Buffer still {image_state[cam.ready]}")
                    return
            log.debug(f"Downloading... (cam state {image_state[cam.ready]})")

            cam.download()
            log.info(f"Elapsed {(Time.now() - now).sec.item():.2f}")

            ##################################
            ##### Update header and save #####
            ##################################

            fih_params = {
                "object": objname,
                "camera": camera_name,
                "imagetyp": imagetype,
                "fits_file": fits_file,
            }

            fih_params.update(basic_cabinet)  # radec, altaz, utc, lst

            fih = FillHeader()

            fih.run(fih_params)
            log.debug(f"observation {fih.filename}")

            self.filename = fih.filename
            self.filenames.append(fih.filename)

            log.debug(f"filenames from obs tpl {self.filenames}")

            ######################################
            ##### Calculate and apply offset #####
            ######################################

            log.info(f"Recenter is {self.recenter}")
            if self.recenter:
                log.info(f"... with box={self.box}px")

            if imagetype == "Light":
                if self.recenter:
                    try:
                        apply_offset(
                            fits_file=fits_file, box=self.box, display=False)
                    except Exception as e:
                        log.warning(f"Cannot apply offset: {e}")
        return
Loading