Commit 3134ccd5 authored by vertighel's avatar vertighel
Browse files

cameras.ini: pixel_scale/orientation per camera



Sostituisce le costanti globali pixscale/rotangle con chiavi per-camera
in cameras.ini (stesso valore di default ovunque, da tarare in futuro
per singola camera). Nuovo helper camera_optics() in constants.py,
usato da guider.py._pixel_to_offset via cam._viewer_key.

Contestualmente, api/fits_image.py non tiene più il ConfigParser di
cameras.ini in cache a livello di modulo: get_cameras_cfg() lo ricarica
in base all'mtime del file, cosà non serve riavviare il server per
vedere le modifiche (stesso pattern già usato per la cache dei FITS).

Co-Authored-By: default avatarClaude Sonnet 5 <noreply@anthropic.com>
parent 7eb9d211
Loading
Loading
Loading
Loading
+17 −3
Original line number Diff line number Diff line
@@ -58,12 +58,26 @@ viewer_blueprint = Blueprint('viewer', __name__)
PACKAGE_ROOT = Path(__file__).parent.parent.absolute()
PROJECT_ROOT = PACKAGE_ROOT.parent

_cfg = configparser.ConfigParser()
_cfg.read(PACKAGE_ROOT / 'config' / 'cameras.ini')
CAMERAS_INI = PACKAGE_ROOT / 'config' / 'cameras.ini'
cameras_cfg = configparser.ConfigParser()
cameras_cfg_mtime = None

_PNG_MAX_PX = 512


def get_cameras_cfg() -> configparser.ConfigParser:
    """Return cameras.ini, reloaded only when the file changes on disk."""
    global cameras_cfg_mtime
    try:
        mtime = os.path.getmtime(CAMERAS_INI)
    except OSError:
        return cameras_cfg
    if mtime != cameras_cfg_mtime:
        cameras_cfg.read(CAMERAS_INI)
        cameras_cfg_mtime = mtime
    return cameras_cfg


# ---------------------------------------------------------------------------
# FITS data cache
# ---------------------------------------------------------------------------
@@ -74,7 +88,7 @@ _fits_cache: dict[str, tuple[float, np.ndarray]] = {}

def _fits_path(cam_id: str) -> Path | None:
    """Return the filesystem path for a camera's latest FITS file."""
    raw_path = _cfg.get(cam_id, 'fits_path', fallback=None)
    raw_path = get_cameras_cfg().get(cam_id, 'fits_path', fallback=None)
    if raw_path is None:
        return None
    return PROJECT_ROOT / DATA_FOLDER / raw_path
+50 −36
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@
# device       — device name in devices.ini that the viewer loop
#                drives directly (tec only: viewer IS the acq loop)
# has_loop     — true if the viewer can run a hw-driven live loop
# pixel_scale  — [arcsec/px] at binning 1
# orientation  — [deg] camera rotation angle
# #########################################################

##############
@@ -23,17 +25,23 @@ role = sci
station     = 1
power_api   = /scicam1/power
fits_path   = fits/scicam1.fits
pixel_scale = 0.283
orientation = -60

[scicam2]
role        = sci
station     = 2
power_api   = /scicam2/power
fits_path   = fits/scicam2.fits
pixel_scale = 0.283
orientation = -60

# [scicam3]
# role        = sci
# station     = 3
# fits_path   = fits/scicam3.fits
# pixel_scale = 0.283
# orientation = -60

##############
# Technical (guider) cameras
@@ -46,6 +54,8 @@ power_api = /scicam1/power
fits_path   = fits/teccam1.fits
device      = tec1
has_loop    = true
pixel_scale = 0.283
orientation = -60

[teccam2]
role        = tec
@@ -53,6 +63,8 @@ station = 2
fits_path   = fits/teccam2.fits
device      = tec2
has_loop    = true
pixel_scale = 0.283
orientation = -60

[teccam3]
role        = tec
@@ -60,3 +72,5 @@ station = 3
fits_path   = fits/teccam3.fits
device      = tec3
has_loop    = true
pixel_scale = 0.283
orientation = -60
+15 −0
Original line number Diff line number Diff line
@@ -26,6 +26,21 @@ def viewer_fits_path(viewer_key):
        return temp_fits
    return str(_DATA_DIR / raw)


def camera_optics(viewer_key):
    """Return (pixel_scale, orientation) for a cam_id from cameras.ini.

    pixel_scale is in arcsec/px at binning 1, orientation in degrees.
    Falls back to (pixscale, rotangle) if the key is absent or not configured.
    """
    if not viewer_key:
        return pixscale, rotangle
    cfg = configparser.ConfigParser()
    cfg.read(_CAMERAS_INI)
    scale       = cfg.getfloat(viewer_key, 'pixel_scale', fallback=pixscale)
    orientation = cfg.getfloat(viewer_key, 'orientation', fallback=rotangle)
    return scale, orientation

# Telescope

lat = 44.5912  # [°] Latitude North from Greenwich.
+6 −4
Original line number Diff line number Diff line
@@ -17,8 +17,8 @@ from astropy.wcs import WCS

# Custom modules
from noctua import devices
from noctua.config.constants import (alt, frame_number, lat, lon, pixscale,
                                      rotangle, viewer_fits_path)
from noctua.config.constants import (alt, camera_optics, frame_number, lat,
                                      lon, viewer_fits_path)
from noctua.utils.analysis import fit_star
from noctua.utils.logger import log

@@ -279,9 +279,11 @@ class Guider:
        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)
        pscale  = pixscale * bin_x / 3600.0
        pscale  = cam_pixscale * bin_x / 3600.0

        rot_dev = getattr(devices, "rot", None)
        if rot_dev:
@@ -291,7 +293,7 @@ class Guider:
            rot_off = float(rot_off) if rot_off is not None else 0.0
        else:
            rot_off = 0.0
        angle = np.deg2rad(rotangle + rot_off)
        angle = np.deg2rad(cam_rotangle + rot_off)

        wcs = WCS(naxis=2)
        wcs.wcs.crpix = [nx / 2.0, ny / 2.0]