From 80e3c620085a0620df566dce917295e5c09cfb28 Mon Sep 17 00:00:00 2001
From: vertighel <davide.ricci82@gmail.com>
Date: Sat, 10 May 2025 21:57:14 +0200
Subject: [PATCH] Modified ini files, tried to generate some new header

---
 noche/headers/header_base_v1.ini      |  10 +-
 noche/noche.py                        | 292 +++++++++++++++++---------
 noche/observatories/abobservatory.ini |  49 +++--
 noche/observatories/grt.ini           |  52 ++---
 noche/observatories/oarpaf.ini        |  49 +++--
 noche/observatories/ogg.ini           |  52 ++---
 noche/observatories/opc.ini           |  49 +++--
 noche/observatories/ossfoligno.ini    |  46 ++--
 8 files changed, 357 insertions(+), 242 deletions(-)

diff --git a/noche/headers/header_base_v1.ini b/noche/headers/header_base_v1.ini
index f0d2d2a..7e770ea 100644
--- a/noche/headers/header_base_v1.ini
+++ b/noche/headers/header_base_v1.ini
@@ -33,12 +33,12 @@ MJD-OBS =             | [d] Modified Julian Date of observation
 TELESCOP =            | Telescope name
 FOCALLEN =            | [mm] Telescope focal length
 APTDIA =              | [mm] Telescope aperture diameter
-OBS-LONG =            | [deg] Observatory longitude (East < 0)
+OBS-LONG =            | [deg] Observatory longitude (East > 0)
 OBS-LAT =             | [deg] Observatory latitude (North > 0)
 OBS-ELEV =            | [m] Observatory altitude above sea level
 FOCUSPOS =            | [um] Focuser position
 FOCUSTEM =            | [C] Focuser temperature
-DEROTANG = 0          | [deg] Rotator angle
+DEROTANG = 0          | [deg] Rotator angle, if any
                       
 [Imaging]          
 INSTRUME =            | Instrument name
@@ -55,12 +55,12 @@ CCD-TEMP =            | [C] CCD temperature
 GAIN =                | [e-/ADU] Gain
 RDNOISE =             | [e- RMS] Readout noise
 
-IMAGETYP =            | Frame type (LIGHT, DARK, BIAS, FLAT)
+IMAGETYP =            | Frame type
 FILTER =              | Photometric filter used
 EXPTIME =             | [s] Exposure time
 XBINNING =            | Binning factor in X
 YBINNING =            | Binning factor in Y
-                      
+
 [Ambient]             
 SUNALT =              | [deg] Sun altitude (< 0 means night)
 MOONDIST =            | [deg] Moon-target angular distance
@@ -88,7 +88,7 @@ EQUINOX = 2000.0      | [yr] Equinox of coordinates
 
 [Final]
 FILEORIG =            | Original file name
-SWCREATE =            | Software that created this file
+SWCREATE =            | Software that created FILEORIG
 CHECKSUM =            | Checksum of header
 DATASUM =             | Data sum of FITS file
 ORIGIN = NOCHE v0.1   | Origin of FITS file
diff --git a/noche/noche.py b/noche/noche.py
index 6d67c3c..0ab931d 100644
--- a/noche/noche.py
+++ b/noche/noche.py
@@ -1,5 +1,7 @@
 import configparser
 from pathlib import Path
+from configparser import DuplicateOptionError
+from ast import literal_eval
 
 import numpy as np
 from astropy.coordinates import SkyCoord, EarthLocation
@@ -15,12 +17,12 @@ class Noche:
     This class provides tools to initialize and populate a FITS header for
     imaging or spectroscopic observations based on a configurable header
     template and site-specific observatory data.
-    
+
     Parameters
     ----------
     header_template_path : str, optional
         Path to the .ini configuration file with base header structure.
-    
+
     Attributes
     ----------
     header : astropy.io.fits.Header
@@ -32,7 +34,7 @@ class Noche:
     _location : astropy.coordinates.EarthLocation or None
         Observatory location.
     """
-    
+
     def __init__(self, header_template_path=None, debug=False):
         """
         Initialize the header builder with a template .ini file.
@@ -45,20 +47,20 @@ class Noche:
 
         if debug:
             log.setLevel('DEBUG')
-        
+
         self._coord = None
         self._location = None
         self._obstime = None
 
         self._head_dir = "headers"
         self._obs_dir = "observatories"
-        
+
         self.header = fits.Header()
+        self.hdu = None
+        
         self.load_default_header(header_template_path)
-        # self.load_noctis_observatory("oarpaf")
-        # self.set_coordinates(05.3, 70.0, Time.now().isot)
 
-        
+
     @property
     def noctis_observatories(self):
         """
@@ -72,30 +74,25 @@ class Noche:
         data_dir = Path(__file__).parent / self._obs_dir
         return [f.stem for f in data_dir.glob("*.ini")]
 
-        
+
     def load_default_header(self, path):
         """
         Load header from configuration file
-        
+
         Parameters
         ----------
         path : str
             Configuration file in .ini format
         """
-
+        
         if not path:
             path = Path(__file__).parent / self._head_dir / "header_base_v1.ini"
             log.info(path)
+
+        config = self._load_config(path)        
         
-        config = configparser.ConfigParser(inline_comment_prefixes=('#',))
-        try:
-            config.read(path)
-        except IndexError as e:
-            log.error("File not found")
-            
         for section in config.sections():
             for key, value in config.items(section):
-                
                 try:
                     val, comment = value.split("|")
                     comment = comment.strip()
@@ -106,68 +103,121 @@ class Noche:
                 val = self._parse(val)
                 self.header[key] = val, comment
 
-        self._update()        
-        
-        
-    def load_noctis_observatory(self, name='oarpaf', flavor=None):
+        self._update()
+
+
+    def load_noctis_observatory(self, name='oarpaf', fits_file=None):
         """
         Load one of the NOCTIS observatory parameters such as
         location and detector specifics,
         and update relevant FITS keywords.
-        
+
         Parameters
         ----------
         name : str
             One of the supported observatories.
-        flavor : str, optional
-            Section name of the configuration file. If not specified, takes the first one.
         """
-        
+
         data_dir = Path(__file__).parent / self._obs_dir
         ini_path = data_dir / f"{name}.ini"
 
         log.debug(ini_path)
         if not ini_path.exists():
-            raise FileNotFoundError(f"Observatory config '{ini_path}' not found.")
+            msg = f"Observatory config '{ini_path}' not found."
+            # raise FileNotFoundError(msg)
+            log.error(msg)
+            return
 
-        self.load_observatory(str(ini_path), flavor=flavor)
-        
-        
-    def load_observatory(self, path=None, flavor=None):
+        self.load_observatory(path=str(ini_path), fits_file=fits_file)
+
+
+    def load_observatory(self, path=None, fits_file=None):
         """
         Load observatory parameters such as location and detector specifics,
         and update relevant FITS keywords.
-        
+
         Parameters
         ----------
         path : str
             Configuration file in .ini format.
-        flavor : str, optional
-            Section name of the configuration file. If not specified, takes the first one.
         """
-        
-        config = configparser.ConfigParser(inline_comment_prefixes=('#',))
-        config.read(path)
+
+        config = self._load_config(path)        
 
         sections = config.sections()
 
-        if not flavor:
-            loc = config[sections[0]]
-        else:
-            loc = config[flavor]
+        loc = config["Fixed"]
 
         self.set_location(loc["OBS-LONG"], loc["OBS-LAT"], loc["OBS-ELEV"])
-            
+
         for k in loc.keys():
+            log.info(k)
             val = self._parse(loc[k])
             if k in self.header:
                 self.header[k] = val
 
-        self.header["DETSIZE"] = f'[1:{self.header["NAXIS1"]},1:{self.header["NAXIS2"]}]'
-                
+        #self.header["DETSIZE"] = f'[1:{self.header["NAXIS1"]},1:{self.header["NAXIS2"]}]'
+
+        if fits_file:
+            self.fill_from_fits_file(path, fits_file)
+
         self._update()
 
-        
+
+    def fill_from_fits_file(self, path=None, fits_file=None):
+        """
+        Load relevant header info from a fits file of the specific observatory.
+
+         - [Mapping] section defines how the new keywords are related
+           to the old ones.
+           For example: FOCUSPOS = TELFOCUS
+
+         - [Formula] section defines the formula to apply to a header
+           value to match the specifics of the new one.
+           For example: FOCUSPOS = (x)/1000 # Transforming mm in um.
+
+        Parameters
+        ----------
+        path : str
+            Configuration file in .ini format.
+        fits_file : str
+            FITS file position.
+
+        """
+
+        config = self._load_config(path)        
+
+        filename = Path(fits_file)
+        self.load_fits(filename)
+        fits_file_header = self.hdu.header
+
+        sections = config.sections()
+
+        loc = config["Mapping"]
+        for k in loc.keys():
+            fits_keyword = loc[k]
+            fits_value = fits_file_header[fits_keyword]
+            val = self._parse(fits_value)
+            self.header[k] = val
+            log.info(f"{k:<8} : {val}")
+
+        loc = config["Formula"]
+        for k in loc.keys():
+            fits_formula = loc[k]
+            x = self.header[k]
+            self.header[k] = eval(fits_formula)
+            log.info(f"{k:<8} : from {x} to {self.header[k]}")
+            del x
+
+        self.set_coordinates(self.header["RA"],
+                             self.header["DEC"],
+                             obstime=self.header["DATE-OBS"])
+            
+        self._update()
+
+        self.header["FILEORIG"] = filename.name
+
+
     def set_location(self, lon, lat, alt):
         """
         Location of the observatory, Separate so that it is
@@ -182,14 +232,14 @@ class Noche:
         alt : float
             Elevation in meters.
         """
-        
+
         self._location = EarthLocation(lon, lat, alt)
-        
+
         self.header["OBS-LONG"] = lon
         self.header["OBS-LAT"] = lat
         self.header["OBS-ELEV"] = alt
-        
-        
+
+
     def set_obstime(self, obstime):
         """ Set observation time
 
@@ -198,24 +248,24 @@ class Noche:
         obstime : str, astropy.time.Time
             Time of the observation.
         """
-        
-        time = Time(obstime)        
+
+        time = Time(obstime)
         self._obstime = time
-        
+
         if self._coord != None:
             self._coord.obstime = time
-        
+
         self.header['DATE'] = time.isot.split("T")[0]
         self.header['DATE-OBS'] = time.isot
         self.header['MJD-OBS'] = time.mjd
 
         self._update()
-        
 
-    def set_object(self, objname, update_coord=True, obstime=None):
+
+    def set_object(self, objname, update_coord=False, obstime=None):
         """
         Resolve object coordinates and set OBJECT keyword.
-    
+
         Parameters
         ----------
         objname : str
@@ -225,32 +275,32 @@ class Noche:
         obstime : str or astropy.time.Time, optional
             Observation time to associate with coordinates.
         """
-            
+
         if update_coord:
             try:
                 coord = SkyCoord.from_name(objname)
                 self._coord = coord
 
                 if obstime:
-                    time = Time(obstime)        
+                    time = Time(obstime)
                     self.set_obstime(time)
-                
+
                 log.info("Found catalog name")
                 coorstr = coord.to_string(style='hmsdms', sep=' ', precision=1, pad=True)
                 log.info(f"Corresponds to {coorstr}")
-                
+
                 if obstime:
                     self.set_coordinates(coord.ra, coord.dec, obstime)
                 else:
                     self.set_coordinates(coord.ra, coord.dec)
-                    
+
             except NameResolveError as e:
                 log.error("Cannot resolve name")
                 return [None, None]
-        
+
         self.header["OBJECT"] = objname
 
-            
+
     def set_coordinates(self, ra, dec, obstime=None):
         """
         RA e DEC in decimal or sexagesimal format, and ISO observation time
@@ -272,28 +322,30 @@ class Noche:
         if obstime:
             time = Time(obstime)
             self.set_obstime(time)
-            
-        self.header['RA'] = coord.ra.to_string(unit=u.hourangle, sep=':')
-        self.header['DEC'] = coord.dec.to_string(unit=u.deg, sep=':')
+
+        self.header['RA'] = coord.ra.to_string(unit=u.hourangle, sep=':',
+                                               pad=True, precision=1)
+        self.header['DEC'] = coord.dec.to_string(unit=u.deg, sep=':', pad=True,
+                                                 precision=1)
         self.header['RA_DEG'] = coord.ra.deg
         self.header['DEC_DEG'] = coord.dec.deg
 
         self._update()
 
-            
+
     def set_altaz_and_parallactic(self):
         """
         Compute and update Alt/Az, Airmass, LST, HA, Position and Parallactic Angle.
-    
+
         Raises
         ------
         ValueError
             If observation time or location is not set.
         """
-        
+
         if self._obstime == None or self._location == None:
             raise ValueError("Observation Time, Instrument parameters must be set.")
-        
+
         self._coord.obstime = self._obstime
         self._coord.location = self._location
         altaz = self._coord.altaz
@@ -305,7 +357,7 @@ class Noche:
 
         # Local Sideral Time and Hour Angle
         lst = self._obstime.sidereal_time('mean', longitude=self._location.lon)
-        ha = (lst - self._coord.ra).hour 
+        ha = (lst - self._coord.ra).hour
         lst_hours = lst.hour
 
         self.header['LST'] = round(lst_hours, 2)
@@ -327,30 +379,38 @@ class Noche:
     def set_wcs(self, angle=None):
         """
         Set World Coordinate System (WCS) parameters.
-    
+
         Parameters
         ----------
         angle : float, optional
             Rotation angle in degrees. Defaults to DEROTANG.
-    
+
         Raises
         ------
         ValueError
             If coordinates or location are not set.
         """
-          
+
         if self._coord == None or self._location == None:
             raise ValueError("Observation Coordinates, Instrument parameters must be set.")
 
-        crpix = [self.header['NAXIS1']/2, self.header['NAXIS2']/2]
-        cdelt = [round(self.header["PIXSCALE"]*u.arcsec.to(u.deg), 7),
-                 round(self.header["PIXSCALE"]*u.arcsec.to(u.deg), 7)]
+
+        detsize = self.header['DETSIZE'].strip('[]')
+        x_str, y_str = detsize.split(',')
+
+        # Ottieni i limiti numerici
+        _, xsize = map(int, x_str.split(':'))
+        _, ysize = map(int, y_str.split(':'))
+
+        crpix = [xsize/self.header["XBINNING"]/2, ysize/self.header["YBINNING"]/2]
+        cdelt1 = round(self.header["PIXSCALE"]*self.header["XBINNING"]*u.arcsec.to(u.deg), 7)
+        cdelt2 = round(self.header["PIXSCALE"]*self.header["YBINNING"]*u.arcsec.to(u.deg), 7)
 
         if not angle:
             angle = self.header["DEROTANG"]
-            
+
         angle = np.deg2rad(angle)
-            
+
         crval_ra = self._coord.ra.deg
         crval_dec = self._coord.dec.deg
 
@@ -358,61 +418,87 @@ class Noche:
         self.header['CRPIX2'] = crpix[1]
         self.header['CRVAL1'] = crval_ra
         self.header['CRVAL2'] = crval_dec
-        self.header['CDELT1'] = cdelt[0]
-        self.header['CDELT2'] = cdelt[1]
-        self.header["PC1_1"] = -np.cos(angle)
+        self.header['CDELT1'] = cdelt1
+        self.header['CDELT2'] = cdelt2
+        self.header["PC1_1"] = +np.cos(angle) *-1 # E to left
+        self.header["PC1_2"] = -np.sin(angle)
         self.header["PC2_1"] = +np.sin(angle)
-        self.header["PC1_2"] = +np.sin(angle)
         self.header["PC2_2"] = +np.cos(angle)
 
-        
+
     def set_ambient(self):
         """
         Set ambient keywords: Moon distance, phase and Sun altitude.
-    
+
         Raises
         ------
         ValueError
             If coordinates or location are not set.
         """
-            
+
         if self._coord == None or self._location == None:
             raise ValueError("Observation Time, Observing location must be set.")
 
         time = self._coord.obstime
         loc = self._location
-        
+
         # Sun and Moon in alt-az coordinates
         with solar_system_ephemeris.set('builtin'):
             sun = get_body("sun", time)
             sun.location = loc
             moon = get_body("moon", time)
-            moon.location = loc            
-    
+            moon.location = loc
+
         # MOONDIST: angular distance between target and Moon
         moondist = moon.separation(self._coord, origin_mismatch="ignore").deg
         self.header['MOONDIST'] = round(moondist, 1)
-    
+
         # MOONPHAS: Moon phase
         elongation = moon.separation(sun).deg
         moonphas = (1 + np.cos(np.radians(elongation))) / 2
         self.header['MOONPHAS'] = round(moonphas, 2)
-    
+
         # SUNALT: Sun altitude above the horizon
         sunalt = sun.altaz.alt.deg
         self.header['SUNALT'] = round(sunalt, 1)
 
-        
+
     def check_empty(self):
         """
         List the header keywords that are still empty
         """
-        
+
         for k in self.header:
             if self.header[k] == None:
                 print(k, self.header[k])
 
-    
+    def load_fits(self, fits_file):
+
+        filename = fits_file
+        hdul = fits.open(filename)
+        self.hdu = hdul[0]
+
+            
+    def write_noctis_fits(self, filename=None, overwrite=True):
+        noche_hdu = self.hdu.copy()
+        noche_hdu.header = self.header
+        noche_hdu.writeto(filename,
+                          overwrite=overwrite, checksum=True)
+
+    @staticmethod
+    def _load_config(path):
+                
+        config = configparser.ConfigParser(inline_comment_prefixes=('#',))
+
+        try:
+            config.read(path)
+            return config
+        except IndexError as e:
+            log.error("File not found: {e}")
+        except DuplicateOptionError as e:
+            log.error(e)
+                
+
     def _update(self):
         """
         If coordinates, observation and location are provided, then
@@ -424,24 +510,27 @@ class Noche:
             self.set_ambient()
             self.set_wcs()
 
-            
+
     def _parse(self, val):
         """
         Parse values from configuration file.
-    
+
         Parameters
         ----------
         val : str
             Value to be parsed.
-    
+
         Returns
         -------
         int, float, bool, or str
             Parsed value.
         """
-                   
-        val = val.strip()
-            
+
+        try:
+            val = val.strip()
+        except AttributeError as e:
+            pass
+
         if not val:
             val = None
         else:
@@ -460,6 +549,5 @@ class Noche:
                 else:
                     # string
                     pass
-                
-        return val
 
+        return val
diff --git a/noche/observatories/abobservatory.ini b/noche/observatories/abobservatory.ini
index 83c09c8..72ef2bc 100644
--- a/noche/observatories/abobservatory.ini
+++ b/noche/observatories/abobservatory.ini
@@ -1,35 +1,40 @@
-[ABObservatory]
+[Fixed]
 TELESCOP = ABObservatory          # Telescope name
 APTDIA = 300                      # [mm] Telescope aperture diameter
 FOCALLEN = 1170                   # [mm] Telescope focal length
-OBS-LONG = -11.2430135            # [deg] Observatory longitude (East < 0)  
+OBS-LONG = -11.2430135            # [deg] Observatory longitude (East > 0)  
 OBS-LAT = 43.5235203              # [deg] Observatory latitude (North > 0)  
 OBS-ELEV = 1025                   # [m] Observatory altitude above sea level
+DEROTANG = 0                      # [deg] Rotator angle, if any
 INSTRUME = ABOb instrument        # Instrument name
 OBSTYPE = Imaging                 # Observation type
 DETECTOR = Atik 383 L             # Detector identifier
-NAXIS1 = 3354                     # [px] X Dimensions of detector in binning 1
-NAXIS2 = 2529                     # [px] Y dimension of detector in binning 1
+DETSIZE = [1:3354,1:2529]         # [px] [1:x,1:y] Physical CCD dimensions
 XPIXSZ = 5.4                      # [um] Pixel X axis size
 YPIXSZ = 5.4                      # [um] Pixel Y axis size
-PIXSCALE = 3600                   # [arcsec/px] Plate scale in binning 1
+PIXSCALE = 36                     # [arcsec/px] Plate scale in binning 1
 GAIN =                            # [e-/ADU] Gain
 RDNOISE =                         # [e- RMS] Readout noise
 
-[ABObservatory-bis]
-TELESCOP = ABObservatory          # Telescope name
-APTDIA = 300                      # [mm] Telescope aperture diameter
-FOCALLEN = 1170                   # [mm] Telescope focal length
-OBS-LONG = -11.2430135            # [deg] Observatory longitude (East < 0)  
-OBS-LAT = 43.5235203              # [deg] Observatory latitude (North > 0)  
-OBS-ELEV = 1025                   # [m] Observatory altitude above sea level
-INSTRUME = ABOb instrument        # Instrument name
-OBSTYPE = Imaging                 # Observation type
-DETECTOR = Atik 383 L             # Detector identifier
-NAXIS1 = 3354                     # [px] X Dimensions of detector in binning 1
-NAXIS2 = 2529                     # [px] Y dimension of detector in binning 1
-XPIXSZ = 5.4                      # [um] Pixel X axis size
-YPIXSZ = 5.4                      # [um] Pixel Y axis size
-PIXSCALE = 3600                   # [arcsec/px] Plate scale in binning 1
-GAIN =                            # [e-/ADU] Gain
-RDNOISE =                         # [e- RMS] Readout noise
+[Mapping]
+NAXIS1 = NAXIS1                   # [px] X Dimensions of detector
+NAXIS2 = NAXIS2                   # [px] Y dimension of detector
+OBSERVER = OBSERVER               # Observer name
+OBJECT   = OBJECT                 # Name of observed object
+RA = OBJCTRA                      # In sexagesimal or decimal format
+DEC = OBJCTDEC                    # In sexagesimal or decimal format
+DATE-OBS = DATE-OBS               # [YYYY-MM-DDTHH:MM:SS] UTC observation date
+FOCUSTEM = FOCUSTEM               # [C] Focuser temperature
+FOCUSPOS = FOCUSPOS               # [um] Focuser position
+SET-TEMP = SET-TEMP               # [C] CCD temperature set point
+CCD-TEMP = CCD-TEMP               # [C] CCD temperature
+IMAGETYP = IMAGETYP               # Frame type
+FILTER = FILTER                   # Photometric filter used
+EXPTIME = EXPTIME                 # [s] Exposure time
+XBINNING = XBINNING               # Binning factor in X
+YBINNING = YBINNING               # Binning factor in Y
+TEMPERAT = AOCAMBT                # [C] Ambient temperature
+SWCREATE = SWCREATE               # Software that created FILEORIG
+
+[Formula]
+
diff --git a/noche/observatories/grt.ini b/noche/observatories/grt.ini
index 3147d02..3f92168 100644
--- a/noche/observatories/grt.ini
+++ b/noche/observatories/grt.ini
@@ -1,35 +1,39 @@
-[GRT]
+[Fixed]
 TELESCOP = GRT                    # Telescope name
 APTDIA = 400                      # [mm] Telescope aperture diameter
 FOCALLEN = 1520                   # [mm] Telescope focal length
-OBS-LONG = -11.2430135            # [deg] Observatory longitude (East < 0)  
-OBS-LAT = 43.5235203              # [deg] Observatory latitude (North > 0)  
-OBS-ELEV = 1025                   # [m] Observatory altitude above sea level
+OBS-LONG = 14.020563              # [deg] Observatory longitude (East > 0)  
+OBS-LAT =  37.9391183             # [deg] Observatory latitude (North > 0)  
+OBS-ELEV = 606                    # [m] Observatory altitude above sea level
+DEROTANG = 0                      # [deg] Rotator angle, if any
 INSTRUME = GRT instrument         # Instrument name
 OBSTYPE = Imaging                 # Observation type
 DETECTOR = Moravian CMOS C4-16000 # Detector identifier
-NAXIS1 = 4096                     # [px] X Dimensions of detector in binning 1
-NAXIS2 = 4096                     # [px] Y dimension of detector in binning 1
+DETSIZE = [1:4096,1:4096]         # [px] [1:x,1:y] Physical CCD dimensions
 XPIXSZ = 9                        # [um] Pixel X axis size
 YPIXSZ = 9                        # [um] Pixel Y axis size
 PIXSCALE = 1.22                   # [arcsec/px] Plate scale in binning 1
-GAIN =                            # [e-/ADU] Gain
+GAIN = 0.850                      # [e-/ADU] Gain
 RDNOISE =                         # [e- RMS] Readout noise
 
-[GRT-bis]
-TELESCOP = GRT                    # Telescope name
-APTDIA = 400                      # [mm] Telescope aperture diameter
-FOCALLEN = 1520                   # [mm] Telescope focal length
-OBS-LONG = -11.2430135            # [deg] Observatory longitude (East < 0)  
-OBS-LAT = 43.5235203              # [deg] Observatory latitude (North > 0)  
-OBS-ELEV = 1025                   # [m] Observatory altitude above sea level
-INSTRUME = GRT instrument         # Instrument name
-OBSTYPE = Imaging                 # Observation type
-DETECTOR = Moravian CMOS C4-16000 # Detector identifier
-NAXIS1 = 4096                     # [px] X Dimensions of detector in binning 1
-NAXIS2 = 4096                     # [px] Y dimension of detector in binning 1
-XPIXSZ = 9                        # [um] Pixel X axis size
-YPIXSZ = 9                        # [um] Pixel Y axis size
-PIXSCALE = 1.22                   # [arcsec/px] Plate scale in binning 1
-GAIN =                            # [e-/ADU] Gain
-RDNOISE =                         # [e- RMS] Readout noise
+[Mapping]
+NAXIS1 = NAXIS1                   # [px] X Dimensions of detector
+NAXIS2 = NAXIS2                   # [px] Y dimension of detector
+OBSERVER = OBSERVER               # Observer name
+OBJECT   = OBJECT                 # Name of observed object
+RA = OBJCTRA                      # In sexagesimal or decimal format
+DEC = OBJCTDEC                    # In sexagesimal or decimal format
+DATE-OBS = DATE-OBS               # [YYYY-MM-DDTHH:MM:SS] UTC observation date
+FOCUSTEM = FOCTEMP                # [C] Focuser temperature
+FOCUSPOS = FOCPOS                 # [um] Focuser position
+SET-TEMP = SET-TEMP               # [C] CCD temperature set point
+CCD-TEMP = CCD-TEMP               # [C] CCD temperature
+IMAGETYP = IMAGETYP               # Frame type
+FILTER = FILTER                   # Photometric filter used
+EXPTIME = EXPTIME                 # [s] Exposure time
+XBINNING = XBINNING               # Binning factor in X
+YBINNING = YBINNING               # Binning factor in Y
+# TEMPERAT = AOCAMBT                # [C] Ambient temperature
+SWCREATE = SWCREATE               # Software that created FILEORIG
+
+[Formula]
diff --git a/noche/observatories/oarpaf.ini b/noche/observatories/oarpaf.ini
index cc7d8b3..c9870fe 100644
--- a/noche/observatories/oarpaf.ini
+++ b/noche/observatories/oarpaf.ini
@@ -1,35 +1,40 @@
-[OARPAF]
+[Fixed]
 TELESCOP = OARPAF                 # Telescope name
 APTDIA = 800                      # [mm] Telescope aperture diameter
 FOCALLEN = 6400                   # [mm] Telescope focal length
-OBS-LONG = -9.2034                # [deg] Observatory longitude (East < 0)  
+OBS-LONG = 9.2034                 # [deg] Observatory longitude (East > 0)  
 OBS-LAT = 44.5912                 # [deg] Observatory latitude (North > 0)  
 OBS-ELEV = 1469                   # [m] Observatory altitude above sea level
+DEROTANG = -89.67                 # [deg] Rotator angle, if any
 INSTRUME = Cerbero                # Instrument name
 OBSTYPE = Imaging                 # Observation type
 DETECTOR = SBIG STX-16081         # Detector identifier
-NAXIS1 = 4144                     # [px] X Dimensions of detector in binning 1
-NAXIS2 = 4126                     # [px] Y dimension of detector in binning 1
+DETSIZE = [1:4144,1:4126]         # [px] [1:x,1:y] Physical CCD dimensions
 XPIXSZ = 9                        # [um] Pixel X axis size
 YPIXSZ = 9                        # [um] Pixel Y axis size
-PIXSCALE = 0.29                   # [arcsec/px] Plate scale in binning 1
+PIXSCALE = 0.283                  # [arcsec/px] Plate scale in binning 1
 GAIN = 1.5                        # [e-/ADU] Gain
 RDNOISE = 8.0                     # [e- RMS] Readout noise
 
-[OARPAF-bis]
-TELESCOP = OARPAF                 # Telescope name
-APTDIA = 800                      # [mm] Telescope aperture diameter
-FOCALLEN = 6400                   # [mm] Telescope focal length
-OBS-LONG = -9.2034                # [deg] Observatory longitude (East < 0)  
-OBS-LAT = 44.5912                 # [deg] Observatory latitude (North > 0)  
-OBS-ELEV = 1469                   # [m] Observatory altitude above sea level
-INSTRUME = Cerbero                # Instrument name
-OBSTYPE = Spectroscopy            # Observation type
-DETECTOR = SBIG STL-11000M        # Detector identifier
-NAXIS1 = 2048                     # [px] X Dimensions of detector in binning 1
-NAXIS2 = 4096                     # [px] Y dimension of detector in binning 1
-XPIXSZ = 9                        # [um] Pixel X axis size
-YPIXSZ = 9                        # [um] Pixel Y axis size
-PIXSCALE = 0.29                   # [arcsec/px] Plate scale in binning 1
-GAIN =                            # [e-/ADU] Gain
-RDNOISE =                         # [e- RMS] Readout noise
+[Mapping]
+NAXIS1 = NAXIS1                   # [px] X Dimensions of detector
+NAXIS2 = NAXIS2                   # [px] Y dimension of detector
+OBSERVER = OBSERVER               # Observer name
+OBJECT   = OBJECT                 # Name of observed object
+RA = RA                           # In sexagesimal or decimal format
+DEC = DEC                         # In sexagesimal or decimal format
+DATE-OBS = DATE-OBS               # [YYYY-MM-DDTHH:MM:SS] UTC observation date
+FOCUSTEM = HIERARCH TEL M2 TEMP   # [C] Focuser temperature
+FOCUSPOS = TELFOCUS               # [um] Focuser position
+SET-TEMP = SET-TEMP               # [C] CCD temperature set point
+CCD-TEMP = CCD-TEMP               # [C] CCD temperature
+IMAGETYP = IMAGETYP               # Frame type
+FILTER = FILTER                   # Photometric filter used
+EXPTIME = EXPTIME                 # [s] Exposure time
+XBINNING = XBINNING               # Binning factor in X
+YBINNING = YBINNING               # Binning factor in Y
+TEMPERAT = HIERARCH CAM AMBIENT   # [C] Ambient temperature
+SWCREATE = SWCREATE               # Software that created FILEORIG
+
+[Formula]
+RA = (x)/15
diff --git a/noche/observatories/ogg.ini b/noche/observatories/ogg.ini
index 9e9ea63..0e3b4bb 100644
--- a/noche/observatories/ogg.ini
+++ b/noche/observatories/ogg.ini
@@ -1,35 +1,39 @@
-[OGG]
+[Fixed]
 TELESCOP = OGG                    # Telescope name
 APTDIA = 300                      # [mm] Telescope aperture diameter
-FOCALLEN = 1170                   # [mm] Telescope focal length
-OBS-LONG = -11.2430135            # [deg] Observatory longitude (East < 0)  
-OBS-LAT = 43.5235203              # [deg] Observatory latitude (North > 0)  
-OBS-ELEV = 1025                   # [m] Observatory altitude above sea level
+FOCALLEN = 1200                   # [mm] Telescope focal length
+OBS-LONG = 16.9                   # [deg] Observatory longitude (East > 0)  
+OBS-LAT = 38.29                   # [deg] Observatory latitude (North > 0)  
+OBS-ELEV = 456                    # [m] Observatory altitude above sea level
 INSTRUME = OGG obs instrument     # Instrument name
+DEROTANG = 0                      # [deg] Rotator angle, if any
 OBSTYPE = Imaging                 # Observation type
 DETECTOR = CMOS QHY268            # Detector identifier
-NAXIS1 = 6280                     # [px] X Dimensions of detector in binning 1
-NAXIS2 = 4210                     # [px] Y dimension of detector in binning 1
+DETSIZE = [1:6280,1:4210]         # [px] [1:x,1:y] Physical CCD dimensions
 XPIXSZ = 3.7                      # [um] Pixel X axis size
 YPIXSZ = 3.7                      # [um] Pixel Y axis size
 PIXSCALE = 4644                   # [arcsec/px] Plate scale in binning 1
 GAIN =                            # [e-/ADU] Gain
 RDNOISE =                         # [e- RMS] Readout noise
 
-[OGG-bis]
-TELESCOP = OGG                    # Telescope name
-APTDIA = 300                      # [mm] Telescope aperture diameter
-FOCALLEN = 1170                   # [mm] Telescope focal length
-OBS-LONG = -11.2430135            # [deg] Observatory longitude (East < 0)  
-OBS-LAT = 43.5235203              # [deg] Observatory latitude (North > 0)  
-OBS-ELEV = 1025                   # [m] Observatory altitude above sea level
-INSTRUME = OGG obs instrument     # Instrument name
-OBSTYPE = Imaging                 # Observation type
-DETECTOR = CMOS QHY268            # Detector identifier
-NAXIS1 = 6280                     # [px] X Dimensions of detector in binning 1
-NAXIS2 = 4210                     # [px] Y dimension of detector in binning 1
-XPIXSZ = 3.7                      # [um] Pixel X axis size
-YPIXSZ = 3.7                      # [um] Pixel Y axis size
-PIXSCALE = 4644                   # [arcsec/px] Plate scale in binning 1
-GAIN =                            # [e-/ADU] Gain
-RDNOISE =                         # [e- RMS] Readout noise
+[Mapping]
+NAXIS1 = NAXIS1                   # [px] X Dimensions of detector
+NAXIS2 = NAXIS2                   # [px] Y dimension of detector
+OBSERVER = OBSERVER               # Observer name
+OBJECT   = OBJECT                 # Name of observed object
+RA = OBJCTRA                      # In sexagesimal or decimal format
+DEC = OBJCTDEC                    # In sexagesimal or decimal format
+DATE-OBS = DATE-OBS               # [YYYY-MM-DDTHH:MM:SS] UTC observation date
+# FOCUSTEM = FOCUSTEM               # [C] Focuser temperature
+FOCUSPOS = FOCUS                  # [um] Focuser position
+SET-TEMP = SET-TEMP               # [C] CCD temperature set point
+CCD-TEMP = CCD-TEMP               # [C] CCD temperature
+IMAGETYP = IMAGETYP               # Frame type
+FILTER = FILTER                   # Photometric filter used
+EXPTIME = EXPTIME                 # [s] Exposure time
+XBINNING = XBINNING               # Binning factor in X
+YBINNING = YBINNING               # Binning factor in Y
+TEMPERAT = EXT-TEMP               # [C] Ambient temperature
+SWCREATE = SWCREATE               # Software that created FILEORIG
+
+[Formula]
diff --git a/noche/observatories/opc.ini b/noche/observatories/opc.ini
index d3d10b3..6de6292 100644
--- a/noche/observatories/opc.ini
+++ b/noche/observatories/opc.ini
@@ -1,35 +1,40 @@
-[OPC]
+[Fixed]
 TELESCOP = OPC                    # Telescope name
 APTDIA = 800                      # [mm] Telescope aperture diameter
 FOCALLEN = 6400                   # [mm] Telescope focal length
-OBS-LONG = -11.2430135            # [deg] Observatory longitude (East < 0)  
+OBS-LONG = 11.2430135             # [deg] Observatory longitude (East > 0)  
 OBS-LAT = 43.5235203              # [deg] Observatory latitude (North > 0)  
-OBS-ELEV = 1025                   # [m] Observatory altitude above sea level
+OBS-ELEV = 450                    # [m] Observatory altitude above sea level
+DEROTANG = -90.0                  # [deg] Rotator angle, if any
 INSTRUME = OPC Instrument         # Instrument name
 OBSTYPE = Imaging                 # Observation type
 DETECTOR = Moravian G4 - KAF09000 # Detector identifier
-NAXIS1 = 3056                     # [px] X Dimensions of detector in binning 1
-NAXIS2 = 3056                     # [px] Y dimension of detector in binning 1
+DETSIZE = [1:4144,1:4126]         # [px] [1:x,1:y] Physical CCD dimensions
 XPIXSZ = 12                       # [um] Pixel X axis size
 YPIXSZ = 12                       # [um] Pixel Y axis size
 PIXSCALE = 0.38                   # [arcsec/px] Plate scale in binning 1
 GAIN =                            # [e-/ADU] Gain
 RDNOISE =                         # [e- RMS] Readout noise
 
-[OPC-bis]
-TELESCOP = OPC                    # Telescope name
-APTDIA = 800                      # [mm] Telescope aperture diameter
-FOCALLEN = 6400                   # [mm] Telescope focal length
-OBS-LONG = -11.2430135            # [deg] Observatory longitude (East < 0)  
-OBS-LAT = 43.5235203              # [deg] Observatory latitude (North > 0)  
-OBS-ELEV = 1025                   # [m] Observatory altitude above sea level
-INSTRUME = OPC Instrument         # Instrument name
-OBSTYPE = Imaging                 # Observation type
-DETECTOR = Moravian G4 - KAF09000 # Detector identifier
-NAXIS1 = 3056                     # [px] X Dimensions of detector in binning 1
-NAXIS2 = 3056                     # [px] Y dimension of detector in binning 1
-XPIXSZ = 12                       # [um] Pixel X axis size
-YPIXSZ = 12                       # [um] Pixel Y axis size
-PIXSCALE = 0.38                   # [arcsec/px] Plate scale in binning 1
-GAIN =                            # [e-/ADU] Gain
-RDNOISE =                         # [e- RMS] Readout noise
+[Mapping]
+NAXIS1 = NAXIS1                   # [px] X Dimensions of detector
+NAXIS2 = NAXIS2                   # [px] Y dimension of detector
+# OBSERVER =                        # Observer name
+OBJECT   = OBJECT                 # Name of observed object
+RA = RA                           # In sexagesimal or decimal format
+DEC = DEC                         # In sexagesimal or decimal format
+DATE-OBS = DATE-OBS               # [YYYY-MM-DDTHH:MM:SS] UTC observation date
+FOCUSTEM = FOCUSTEM               # [C] Focuser temperature
+FOCUSPOS = FOCUSPOS               # [um] Focuser position
+SET-TEMP = SET-TEMP               # [C] CCD temperature set point
+CCD-TEMP = CCD-TEMP               # [C] CCD temperature
+IMAGETYP = IMAGETYP               # Frame type
+FILTER = FILTER                   # Photometric filter used
+EXPTIME = EXPTIME                 # [s] Exposure time
+XBINNING = XBINNING               # Binning factor in X
+YBINNING = YBINNING               # Binning factor in Y
+# TEMPERAT =                        # [C] Ambient temperature
+SWCREATE = SWCREATE               # Software that created FILEORIG
+
+[Formula]
+RA = (x)/15
diff --git a/noche/observatories/ossfoligno.ini b/noche/observatories/ossfoligno.ini
index 0e23e7b..9920a99 100644
--- a/noche/observatories/ossfoligno.ini
+++ b/noche/observatories/ossfoligno.ini
@@ -1,35 +1,39 @@
-[OSS-Foligno]
+[Fixed]
 TELESCOP = Oss-Foligno            # Telescope name
 APTDIA = 300                      # [mm] Telescope aperture diameter
 FOCALLEN = 1950                   # [mm] Telescope focal length
-OBS-LONG = -11.2430135            # [deg] Observatory longitude (East < 0)  
+OBS-LONG = -11.2430135            # [deg] Observatory longitude (East > 0)  
 OBS-LAT = 43.5235203              # [deg] Observatory latitude (North > 0)  
 OBS-ELEV = 1025                   # [m] Observatory altitude above sea level
+DEROTANG = 0                      # [deg] Rotator angle, if any
 INSTRUME = Foligno instrument     # Instrument name
 OBSTYPE = Imaging                 # Observation type
 DETECTOR = QHY174MM               # Detector identifier
-NAXIS1 = 1900                     # [px] X Dimensions of detector in binning 1
-NAXIS2 = 1200                     # [px] Y dimension of detector in binning 1
+DETSIZE = [1:1900,1:1200]         # [px] [1:x,1:y] Physical CCD dimensions
 XPIXSZ = 5.86                     # [um] Pixel X axis size
 YPIXSZ = 5.86                     # [um] Pixel Y axis size
 PIXSCALE = 0.62                   # [arcsec/px] Plate scale in binning 1
 GAIN =                            # [e-/ADU] Gain
 RDNOISE =                         # [e- RMS] Readout noise
 
-[OSS-Foligno-bis]
-TELESCOP = Oss-Foligno            # Telescope name
-APTDIA = 300                      # [mm] Telescope aperture diameter
-FOCALLEN = 1950                   # [mm] Telescope focal length
-OBS-LONG = -11.2430135            # [deg] Observatory longitude (East < 0)  
-OBS-LAT = 43.5235203              # [deg] Observatory latitude (North > 0)  
-OBS-ELEV = 1025                   # [m] Observatory altitude above sea level
-INSTRUME = Foligno instrument     # Instrument name
-OBSTYPE = Imaging                 # Observation type
-DETECTOR = QHY174MM               # Detector identifier
-NAXIS1 = 1900                     # [px] X Dimensions of detector in binning 1
-NAXIS2 = 1200                     # [px] Y dimension of detector in binning 1
-XPIXSZ = 5.86                     # [um] Pixel X axis size
-YPIXSZ = 5.86                     # [um] Pixel Y axis size
-PIXSCALE = 0.62                   # [arcsec/px] Plate scale in binning 1
-GAIN =                            # [e-/ADU] Gain
-RDNOISE =                         # [e- RMS] Readout noise
+[Mapping]
+NAXIS1 = NAXIS1                   # [px] X Dimensions of detector
+NAXIS2 = NAXIS2                   # [px] Y dimension of detector
+OBSERVER = OBSERVER               # Observer name
+OBJECT   = OBJECT                 # Name of observed object
+RA = OBJCTRA                      # In sexagesimal or decimal format
+DEC = OBJCTDEC                    # In sexagesimal or decimal format
+DATE-OBS = DATE-OBS               # [YYYY-MM-DDTHH:MM:SS] UTC observation date
+FOCUSTEM = FOCUSTEM               # [C] Focuser temperature
+FOCUSPOS = FOCUSPOS               # [um] Focuser position
+SET-TEMP = SET-TEMP               # [C] CCD temperature set point
+CCD-TEMP = CCD-TEMP               # [C] CCD temperature
+IMAGETYP = IMAGETYP               # Frame type
+FILTER = FILTER                   # Photometric filter used
+EXPTIME = EXPTIME                 # [s] Exposure time
+XBINNING = XBINNING               # Binning factor in X
+YBINNING = YBINNING               # Binning factor in Y
+TEMPERAT = AOCAMBT                # [C] Ambient temperature
+SWCREATE = SWCREATE               # Software that created FILEORIG
+
+[Formula]
-- 
GitLab