Commit 8f08b217 authored by vertighel's avatar vertighel
Browse files

guider.py: legge il binning dall'header FITS, non più dal device live



_pixel_to_offset() chiamava ancora cam.binning per calcolare la scala
pixel — per Mako questo fa un self.get('BinningHorizontal') live via SDK,
quindi Guide su teccam2/3 falliva con "Mako connection failed" ogni volta
che la camera non era già connessa altrove, anche se _acquire() era già
puramente passivo (legge solo da disco).

_acquire() ora ritorna anche l'header (già scritto da download() con
XBINNING/YBINNING su stx.py/atik.py/mako.py) invece di solo i dati;
_pixel_to_offset()/_step_ao_x()/_offload_to_telescope() lo ricevono in
catena e leggono il binning da lì. Il guider non tocca più l'hardware in
nessun punto.

Co-Authored-By: default avatarClaude Sonnet 5 <noreply@anthropic.com>
parent b02bbf46
Loading
Loading
Loading
Loading
+16 −14
Original line number Diff line number Diff line
@@ -145,7 +145,7 @@ class Guider:
            self.error.append(f"camera '{self.camera}' not found")
            return

        data = self._acquire(cam)
        data, header = self._acquire(cam)
        if data is None:
            return
        ny, nx = data.shape
@@ -186,7 +186,7 @@ class Guider:
        self.last_correction = [round(dpx, 1), round(dpy, 1)]

        if self.actuator == "telescope":
            dalt, daz = self._pixel_to_offset(cam, nx, ny, star_x, star_y, tgt_x, tgt_y)
            dalt, daz = self._pixel_to_offset(cam, header, nx, ny, star_x, star_y, tgt_x, tgt_y)
            log.info(f"Guider: Δalt={dalt*3600:.2f}\" Δaz={daz*3600:.2f}\"")
            try:
                current = devices.tel.offset
@@ -199,9 +199,9 @@ class Guider:
                self.error.append(msg)

        elif self.actuator == "ao_x":
            self._step_ao_x(cam, nx, ny, dpx, dpy, tgt_x, tgt_y)
            self._step_ao_x(cam, header, nx, ny, dpx, dpy, tgt_x, tgt_y)

    def _step_ao_x(self, cam, nx, ny, dpx, dpy, tgt_x, tgt_y):
    def _step_ao_x(self, cam, header, nx, ny, dpx, dpy, tgt_x, tgt_y):

        ao = getattr(devices, "ao", None)
        if ao is None or self._ao_calib_inv is None:
@@ -214,7 +214,7 @@ class Guider:
        lo = 100 - hi
        if self.ao_offload and any(p > hi or p < lo for p in new_pos):
            log.info(f"Guider: AO-X offload triggered (pos={new_pos})")
            self._offload_to_telescope(cam, nx, ny, tgt_x, tgt_y)
            self._offload_to_telescope(cam, header, nx, ny, tgt_x, tgt_y)
            new_pos = [50.0 + delta[0], 50.0 + delta[1]]

        new_pos      = [max(0.0, min(100.0, p)) for p in new_pos]
@@ -223,7 +223,7 @@ class Guider:
        self.last_offset = new_pos
        log.info(f"Guider: AO-X → ({new_pos[0]:.1f}, {new_pos[1]:.1f})")

    def _offload_to_telescope(self, cam, nx, ny, tgt_x, tgt_y):
    def _offload_to_telescope(self, cam, header, nx, ny, tgt_x, tgt_y):

        ao = getattr(devices, "ao", None)
        if ao is None:
@@ -232,7 +232,7 @@ class Guider:
        pixel_corr   = self._ao_calib @ delta_center
        natural_x    = tgt_x - pixel_corr[0]
        natural_y    = tgt_y - pixel_corr[1]
        dalt, daz = self._pixel_to_offset(cam, nx, ny, natural_x, natural_y, tgt_x, tgt_y)
        dalt, daz = self._pixel_to_offset(cam, header, nx, ny, natural_x, natural_y, tgt_x, tgt_y)
        try:
            current = devices.tel.offset
            devices.tel.offset = [current[0] + dalt, current[1] + daz]
@@ -243,7 +243,7 @@ class Guider:
        self.ao_pos = [50.0, 50.0]

    def _acquire(self, cam):
        """Read the latest FITS frame for this camera from disk.
        """Read the latest FITS frame (and header) for this camera from disk.

        The guider never drives acquisition itself — it only reads whatever
        the camera's own loop (or, for scicam, the last Expose) last wrote.
@@ -253,22 +253,24 @@ class Guider:

        fits_path = viewer_fits_path(getattr(cam, "_viewer_key", None))
        try:
            return fits.getdata(fits_path).astype(np.float32)
            data, header = fits.getdata(fits_path, header=True)
            return data.astype(np.float32), header
        except Exception as e:
            msg = f"acquire: {e}"
            log.error(f"Guider: {msg}")
            self.error.append(msg)
            return None
            return None, None

    def _pixel_to_offset(self, cam, nx, ny, star_x, star_y, tgt_x, tgt_y):
    def _pixel_to_offset(self, cam, header, nx, ny, star_x, star_y, tgt_x, tgt_y):

        coordinates = devices.tel.coordinates
        radec = coordinates["radec"]

        cam_pixscale, cam_rotangle = camera_optics(getattr(cam, "_viewer_key", None))

        binning = cam.binning
        bin_x   = binning[0] if isinstance(binning, (list, tuple)) else int(binning)
        # Binning from the FITS header (written by download() at save time),
        # not a live query — the guider never talks to the camera directly.
        bin_x  = header.get('XBINNING', 1) if header else 1
        pscale = cam_pixscale * bin_x / 3600.0

        rot_dev = getattr(devices, "rot", None)