Beispiel #1
0
    def compound_meta(self, headarr: List[fits.Header], meta_key: str):
        """
        Methods to generate metadata requiring interpretation of the header
        data, instead of simply reading the value of a header card.

        Args:
            headarr (:obj:`list`):
                List of `astropy.io.fits.Header`_ objects.
            meta_key (:obj:`str`):
                Metadata keyword to construct.

        Returns:
            object: Metadata value read from the header(s).
        """
        # Handle dispangle and mjd from superclass method
        retval = super().compound_meta(headarr, meta_key)

        # If superclass could not handle the meta key
        if retval is not None:
            return retval
        if meta_key == 'binning':
            binspec, binspatial = headarr[0]['CCDSUM'].split(' ')
            return parse.binning2string(binspec, binspatial)
        else:
            msgs.error("Not ready for this compound meta")
Beispiel #2
0
    def compound_meta(self, headarr, meta_key):
        """
        Methods to generate metadata requiring interpretation of the header
        data, instead of simply reading the value of a header card.

        Args:
            headarr (:obj:`list`):
                List of `astropy.io.fits.Header`_ objects.
            meta_key (:obj:`str`):
                Metadata keyword to construct.

        Returns:
            object: Metadata value read from the header(s).
        """
        if meta_key == 'binning':
            # PypeIt frame
            binspatial = headarr[0]['DETXBIN']
            binspec = headarr[0]['DETYBIN']
            return parse.binning2string(binspec, binspatial)
        elif meta_key == 'mjd':
            time = headarr[0]['DATE-AVG']
            ttime = Time(time, format='isot')
            return ttime.mjd
        elif meta_key == 'ra':
            objra = headarr[0]['OBJRA']  # Given in hours, not deg
            return objra * 15.
        msgs.error("Not ready for this compound meta")
Beispiel #3
0
    def compound_meta(self, headarr, meta_key):
        """

        Args:
            headarr: list
            meta_key: str

        Returns:
            value

        """
        if meta_key == 'binning':
            binspatial, binspec = parse.parse_binning(headarr[0]['BINNING'])
            binning = parse.binning2string(binspec, binspatial)
            return binning
        elif meta_key == 'dispangle':
            if headarr[0]['GRATEPOS'] == 3:
                return headarr[0]['G3TLTWAV']
            elif headarr[0]['GRATEPOS'] == 4:
                return headarr[0]['G4TLTWAV']
            else:
                msgs.warn(
                    'This is probably a problem. Non-standard DEIMOS GRATEPOS={0}.'
                    .format(headarr[0]['GRATEPOS']))
        else:
            msgs.error("Not ready for this compound meta")
Beispiel #4
0
 def compound_meta(self, headarr, meta_key):
     if meta_key == 'binning':
         binspatial, binspec = parse.parse_binning(headarr[0]['BINNING'])
         binning = parse.binning2string(binspec, binspatial)
         return binning
     else:
         msgs.error("Not ready for this compound meta")
Beispiel #5
0
    def compound_meta(self, headarr, meta_key):
        """
        Methods to generate metadata requiring interpretation of the header
        data, instead of simply reading the value of a header card.

        Args:
            headarr (:obj:`list`):
                List of `astropy.io.fits.Header`_ objects.
            meta_key (:obj:`str`):
                Metadata keyword to construct.

        Returns:
            object: Metadata value read from the header(s).
        """
        if meta_key == 'binning':
            # binning in the raw frames
            ccdsum = headarr[1].get('CCDSUM')
            if ccdsum is not None:
                binspatial, binspec = parse.parse_binning(ccdsum)
                binning = parse.binning2string(binspec, binspatial)
            else:
                # binning in the spec2d file
                binning = headarr[0].get('BINNING')
            if binning is None:
                msgs.error('Binning not found')
            return binning
Beispiel #6
0
    def compound_meta(self, headarr, meta_key):
        """
        Methods to generate metadata requiring interpretation of the header
        data, instead of simply reading the value of a header card.

        Args:
            headarr (:obj:`list`):
                List of `astropy.io.fits.Header`_ objects.
            meta_key (:obj:`str`):
                Metadata keyword to construct.

        Returns:
            object: Metadata value read from the header(s).
        """
        if meta_key == 'binning':
            binspatial = headarr[0]['HIERARCH ESO DET WIN1 BINX']
            binspec = headarr[0]['HIERARCH ESO DET WIN1 BINY']
            binning = parse.binning2string(binspec, binspatial)
            return binning
        elif meta_key == 'decker':
            try:  # Science
                decker = headarr[0]['HIERARCH ESO INS SLIT NAME']
            except KeyError:  # Standard!
                try:
                    decker = headarr[0]['HIERARCH ESO SEQ SPEC TARG']
                except KeyError:
                    return None
            return decker
        else:
            msgs.error("Not ready for this compound meta")
Beispiel #7
0
    def compound_meta(self, headarr, meta_key):
        """
        Methods to generate metadata requiring interpretation of the header
        data, instead of simply reading the value of a header card.

        Args:
            headarr (:obj:`list`):
                List of `astropy.io.fits.Header`_ objects.
            meta_key (:obj:`str`):
                Metadata keyword to construct.

        Returns:
            object: Metadata value read from the header(s).
        """
        if meta_key == 'binning':
            if 'HIERARCH ESO DET WIN1 BINX' in headarr[0]:
                binspatial = headarr[0]['HIERARCH ESO DET WIN1 BINX']
            else:
                binspatial = 1
            if 'HIERARCH ESO DET WIN1 BINY' in headarr[0]:
                binspec = headarr[0]['HIERARCH ESO DET WIN1 BINY']
            else:
                binspec = 1
            return parse.binning2string(binspec, binspatial)
        msgs.error("Not ready for this compound meta")
Beispiel #8
0
 def compound_meta(self, headarr, meta_key):
     if meta_key == 'binning':
         if 'HIERARCH ESO DET WIN1 BINX' in headarr[0]:
             binspatial = headarr[0]['HIERARCH ESO DET WIN1 BINX']
         else:
             binspatial = 1
         if 'HIERARCH ESO DET WIN1 BINY' in headarr[0]:
             binspec = headarr[0]['HIERARCH ESO DET WIN1 BINY']
         else:
             binspec = 1
         binning = parse.binning2string(binspec, binspatial)
         return binning
     elif meta_key in ['ra', 'dec']:
         try:  # Calibs do not have RA values
             coord = SkyCoord(ra=headarr[0]['RA'],
                              dec=headarr[0]['DEC'],
                              unit='deg')
         except:
             return None
         if meta_key == 'ra':
             return coord.ra.to_string(unit=units.hour,
                                       sep=':',
                                       pad=True,
                                       precision=2)
         else:
             return coord.dec.to_string(sep=':',
                                        pad=True,
                                        alwayssign=True,
                                        precision=1)
     else:
         msgs.error("Not ready for this compound meta")
Beispiel #9
0
 def compound_meta(self, headarr, meta_key):
     if meta_key == 'binning':
         binspatial = headarr[0]['HIERARCH ESO DET WIN1 BINX']
         binspec = headarr[0]['HIERARCH ESO DET WIN1 BINY']
         binning = parse.binning2string(binspec, binspatial)
         return binning
     elif meta_key in ['ra', 'dec']:
         try:  # Calibs do not have RA values
             coord = SkyCoord(ra=headarr[0]['RA'],
                              dec=headarr[0]['DEC'],
                              unit='deg')
         except:
             return None
         if meta_key == 'ra':
             return coord.ra.to_string(unit=units.hour,
                                       sep=':',
                                       pad=True,
                                       precision=2)
         else:
             return coord.dec.to_string(sep=':',
                                        pad=True,
                                        alwayssign=True,
                                        precision=1)
     elif meta_key == 'decker':
         try:  # Science
             decker = headarr[0]['HIERARCH ESO INS SLIT NAME']
         except KeyError:  # Standard!
             try:
                 decker = headarr[0]['HIERARCH ESO SEQ SPEC TARG']
             except KeyError:
                 return None
         return decker
     else:
         msgs.error("Not ready for this compound meta")
Beispiel #10
0
    def compound_meta(self, headarr: List[fits.Header], meta_key: str):
        """
        Methods to generate meta in a more complex manner than simply
        reading from the header. Super method handles mjd and dispangle

        binning is parsed from CCDSUM header

        Args:
            headarr: List[fits.Header]
              List of headers
            meta_key: str

        Returns:
            value:

        """
        # Handle dispangle and mjd from superclass method
        retval = super(P200DBSPRedSpectrograph, self).compound_meta(headarr, meta_key)
        
        # If superclass could not handle the meta key
        if retval is not None:
            return retval
        if meta_key == 'binning':
            binspec, binspatial = headarr[0]['CCDSUM'].split(' ')
            return parse.binning2string(binspec, binspatial)
        else:
            msgs.error("Not ready for this compound meta")
Beispiel #11
0
 def compound_meta(self, headarr, meta_key):
     if meta_key == 'binning':
         binspatial = headarr[0]['CCDXBIN']
         binspec = headarr[0]['CCDYBIN']
         return parse.binning2string(binspec, binspatial)
     else:
         msgs.error("Not ready for this compound meta")
Beispiel #12
0
 def compound_meta(self, headarr, meta_key):
     if meta_key == 'binning':
         # PypeIt frame
         binspatial = headarr[0]['DETXBIN']
         binspec = headarr[0]['DETYBIN']
         return parse.binning2string(binspec, binspatial)
     elif meta_key == 'mjd':
         time = headarr[0]['DATE-AVG']
         ttime = Time(time, format='isot')
         return ttime.mjd
     else:
         msgs.error("Not ready for this compound meta")
Beispiel #13
0
 def compound_meta(self, headarr, meta_key):
     if meta_key == 'binning':
         if 'HIERARCH ESO DET WIN1 BINX' in headarr[0]:
             binspatial = headarr[0]['HIERARCH ESO DET WIN1 BINX']
         else:
             binspatial = 1
         if 'HIERARCH ESO DET WIN1 BINY' in headarr[0]:
             binspec = headarr[0]['HIERARCH ESO DET WIN1 BINY']
         else:
             binspec = 1
         binning = parse.binning2string(binspec, binspatial)
         return binning
     else:
         msgs.error("Not ready for this compound meta")
Beispiel #14
0
    def compound_meta(self, headarr, meta_key):
        """

        Args:
            headarr: list
            meta_key: str

        Returns:
            value

        """
        if meta_key == 'binning':
            binspatial, binspec = parse.parse_binning(headarr[0]['BINNING'])
            return parse.binning2string(binspec, binspatial)
Beispiel #15
0
    def compound_meta(self, headarr, meta_key):
        """
        Methods to generate metadata requiring interpretation of the header
        data, instead of simply reading the value of a header card.

        Args:
            headarr (:obj:`list`):
                List of `astropy.io.fits.Header`_ objects.
            meta_key (:obj:`str`):
                Metadata keyword to construct.

        Returns:
            object: Metadata value read from the header(s).
        """
        if meta_key == 'binning':
            binspatial, binspec = parse.parse_binning(headarr[0]['BINNING'])
            binning = parse.binning2string(binspec, binspatial)
            return binning
        elif meta_key == 'slitwid':
            # Get the slice scale
            slicescale = 0.00037718  # Degrees per 'large slicer' slice
            ifunum = headarr[0]['IFUNUM']
            if ifunum == 2:
                slicescale /= 2.0
            elif ifunum == 3:
                slicescale /= 4.0
            return slicescale
        elif meta_key == 'ra' or meta_key == 'dec':
            try:
                if self.is_nasmask(headarr[0]):
                    hdrstr = 'RABASE' if meta_key == 'ra' else 'DECBASE'
                else:
                    hdrstr = 'RA' if meta_key == 'ra' else 'DEC'
            except KeyError:
                try:
                    hdrstr = 'TARGRA' if meta_key == 'ra' else 'TARGDEC'
                except KeyError:
                    hdrstr = ''
            return headarr[0][hdrstr]
        elif meta_key == 'pressure':
            return headarr[0]['WXPRESS'] * 0.001 * units.bar
        elif meta_key == 'temperature':
            return headarr[0]['WXOUTTMP'] * units.deg_C
        elif meta_key == 'humidity':
            return headarr[0]['WXOUTHUM'] / 100.0
        elif meta_key == 'obstime':
            return Time(headarr[0]['DATE-END'])
        else:
            msgs.error("Not ready for this compound meta")
Beispiel #16
0
    def compound_meta(self, headarr, meta_key):
        """

        Args:
            headarr: list
            meta_key: str

        Returns:
            value

        """
        if meta_key == 'binning':
            binspatial, binspec = parse.parse_binning(np.array([headarr[0]['CCDXBIN'], headarr[0]['CCDYBIN']]))
            binning = parse.binning2string(binspatial, binspec)
            return binning
        msgs.error("Not ready for this compound meta")
Beispiel #17
0
 def compound_meta(self, headarr, meta_key):
     if meta_key == 'binning':
         binspatial = headarr[0]['HIERARCH ESO DET WIN1 BINX']
         binspec = headarr[0]['HIERARCH ESO DET WIN1 BINY']
         binning = parse.binning2string(binspec, binspatial)
         return binning
     elif meta_key == 'decker':
         try:  # Science
             decker = headarr[0]['HIERARCH ESO INS SLIT NAME']
         except KeyError:  # Standard!
             try:
                 decker = headarr[0]['HIERARCH ESO SEQ SPEC TARG']
             except KeyError:
                 return None
         return decker
     else:
         msgs.error("Not ready for this compound meta")
Beispiel #18
0
    def compound_meta(self, headarr, meta_key):
        """
        Methods to generate metadata requiring interpretation of the header
        data, instead of simply reading the value of a header card.

        Args:
            headarr (:obj:`list`):
                List of `astropy.io.fits.Header`_ objects.
            meta_key (:obj:`str`):
                Metadata keyword to construct.

        Returns:
            object: Metadata value read from the header(s).
        """
        if meta_key == 'binning':
            binspatial, binspec = parse.parse_binning(headarr[0]['CCDSUM'])
            binning = parse.binning2string(binspec, binspatial)
            return binning
Beispiel #19
0
    def compound_meta(self, headarr, meta_key):
        """
        Methods to generate metadata requiring interpretation of the header
        data, instead of simply reading the value of a header card.

        Args:
            headarr (:obj:`list`):
                List of `astropy.io.fits.Header`_ objects.
            meta_key (:obj:`str`):
                Metadata keyword to construct.

        Returns:
            object: Metadata value read from the header(s).
        """
        if meta_key == 'binning':
            """
            Binning in blue channel headers is space-separated rather than comma-separated.
            """
            binspec, binspatial = headarr[0]['CCDSUM'].split()
            binning = parse.binning2string(binspec, binspatial)
            return binning
        elif meta_key == 'mjd':
            """
            Need to combine 'DATE-OBS' and 'UT' headers and then use astropy to make an mjd.
            """
            date = headarr[0]['DATE-OBS']
            ut = headarr[0]['UT']
            ttime = Time(f"{date}T{ut}", format='isot')
            return ttime.mjd
        elif meta_key == 'lampstat01':
            """
            If the comparison mirror is in, there will be a 'COMPLAMP' header entry containing the lamps
            that are turned on. However, if the comparison mirror is out, then this header entry doesn't exist.
            So need to test for it and set to 'Off' if it's not there.
            """
            if 'COMPLAMP' in headarr[0]:
                return headarr[0]['COMPLAMP']
            else:
                return 'off'

        msgs.error(
            f"Not ready for compound meta, {meta_key}, for MMT Blue Channel.")
Beispiel #20
0
    def compound_meta(self, headarr, meta_key):
        """

        Args:
            headarr: list
            meta_key: str

        Returns:
            value

        """
        if meta_key == 'binning':
            binspatial, binspec = parse.parse_binning(headarr[0]['BINNING'])
            return parse.binning2string(binspec, binspatial)
        elif meta_key == 'mjd':
            time = '{:s}T{:s}'.format(headarr[0]['UT-DATE'], headarr[0]['UT-TIME'])
            ttime = Time(time, format='isot')
            return ttime.mjd
        else:
            msgs.error("Not ready for this compound meta")
Beispiel #21
0
 def compound_meta(self, headarr, meta_key):
     if meta_key == 'binning':
         binspatial, binspec = parse.parse_binning(headarr[0]['BINNING'])
         binning = parse.binning2string(binspec, binspatial)
         return binning
     elif 'lampstat' in meta_key:
         idx = int(meta_key[-2:])
         curr_date = time.Time(headarr[0]['MJD-OBS'], format='mjd')
         # Modern -- Assuming the change occurred with the new red detector
         t_newlamp = time.Time("2014-02-15",
                               format='isot')  # LAMPS changed in Header
         if curr_date > t_newlamp:
             lamp_names = [
                 'MERCURY', 'NEON', 'ARGON', 'CADMIUM', 'ZINC', 'KRYPTON',
                 'XENON', 'FEARGON', 'DEUTERI', 'FLAMP1', 'FLAMP2',
                 'HALOGEN'
             ]
             return headarr[0][lamp_names[
                 idx - 1]]  # Use this index is offset by 1
         else:  # Original lamps
             plamps = headarr[0]['LAMPS'].split(',')
             # https: // www2.keck.hawaii.edu / inst / lris / instrument_key_list.html
             old_lamp_names = [
                 'MERCURY', 'NEON', 'ARGON', 'CADMIUM', 'ZINC', 'HALOGEN'
             ]
             if idx <= 5:  # Arcs
                 return ('off' if plamps[idx - 1] == '0' else 'on')
             elif idx == 10:  # Current FLAMP1
                 return headarr[0]['FLIMAGIN'].strip()
             elif idx == 11:  # Current FLAMP2
                 return headarr[0]['FLSPECTR'].strip()
             elif idx == 12:  # Current Halogen slot
                 return ('off' if plamps[len(old_lamp_names) -
                                         1] == '0' else 'on')
             else:  # Lamp didn't exist.  Set to None
                 return 'None'
     else:
         msgs.error("Not ready for this compound meta")
Beispiel #22
0
    def compound_meta(self, headarr, meta_key):
        """
        Methods to generate metadata requiring interpretation of the header
        data, instead of simply reading the value of a header card.

        Args:
            headarr (:obj:`list`):
                List of `astropy.io.fits.Header`_ objects.
            meta_key (:obj:`str`):
                Metadata keyword to construct.

        Returns:
            object: Metadata value read from the header(s).
        """
        if meta_key == 'binning':
            binspatial, binspec = parse.parse_binning(headarr[0]['BINNING'])
            return parse.binning2string(binspec, binspatial)
        elif meta_key == 'mjd':
            time = '{:s}T{:s}'.format(headarr[0]['UT-DATE'],
                                      headarr[0]['UT-TIME'])
            ttime = Time(time, format='isot')
            return ttime.mjd
        else:
            msgs.error("Not ready for this compound meta")
Beispiel #23
0
    def compound_meta(self, headarr, meta_key):
        """
        Methods to generate metadata requiring interpretation of the header
        data, instead of simply reading the value of a header card.

        Args:
            headarr (:obj:`list`):
                List of `astropy.io.fits.Header`_ objects.
            meta_key (:obj:`str`):
                Metadata keyword to construct.

        Returns:
            object: Metadata value read from the header(s).
        """
        if meta_key == 'binning':
            binspec, binspatial = [
                int(item) for item in headarr[1]['CCDSUM'].split(' ')
            ]
            return parse.binning2string(binspec, binspatial)
        elif meta_key == 'mjd':
            ttime = Time(headarr[1]['DATE-OBS'], format='isot')
            return ttime.mjd
        else:
            msgs.error("Not ready for this compound meta")
Beispiel #24
0
 def compound_meta(self, headarr, meta_key):
     if meta_key == 'binning':
         binspatial, binspec = parse.parse_binning(headarr[0]['BINNING'])
         binning = parse.binning2string(binspec, binspatial)
         return binning
     elif meta_key == 'slitwid':
         # Get the slice scale
         slicescale = 0.00037718  # Degrees per 'large slicer' slice
         ifunum = headarr[0]['IFUNUM']
         if ifunum == 2:
             slicescale /= 2.0
         elif ifunum == 3:
             slicescale /= 4.0
         return slicescale
     elif meta_key == 'ra' or meta_key == 'dec':
         try:
             if self.is_nasmask(headarr[0]):
                 hdrstr = 'RABASE' if meta_key == 'ra' else 'DECBASE'
             else:
                 hdrstr = 'RA' if meta_key == 'ra' else 'DEC'
         except KeyError:
             try:
                 hdrstr = 'TARGRA' if meta_key == 'ra' else 'TARGDEC'
             except KeyError:
                 hdrstr = ''
         return headarr[0][hdrstr]
     elif meta_key == 'pressure':
         return headarr[0]['WXPRESS'] * 0.001 * units.bar
     elif meta_key == 'temperature':
         return headarr[0]['WXOUTTMP'] * units.deg_C
     elif meta_key == 'humidity':
         return headarr[0]['WXOUTHUM'] / 100.0
     elif meta_key == 'obstime':
         return Time(headarr[0]['DATE-END'])
     else:
         msgs.error("Not ready for this compound meta")
Beispiel #25
0
    def compound_meta(self, headarr, meta_key):
        """

        Args:
            headarr: list
            meta_key: str

        Returns:
            value

        """
        if meta_key == 'binning':
            binspatial, binspec = parse.parse_binning(headarr[0]['BINNING'])
            binning = parse.binning2string(binspec, binspatial)
            return binning
        elif meta_key == 'dispangle':
            if headarr[0]['GRATEPOS'] == 3:
                return headarr[0]['G3TLTWAV']
            elif headarr[0]['GRATEPOS'] == 4:
                return headarr[0]['G4TLTWAV']
            else:
                debugger.set_trace()
        else:
            msgs.error("Not ready for this compound meta")
Beispiel #26
0
    def compound_meta(self, headarr, meta_key):
        """
        Methods to generate metadata requiring interpretation of the header
        data, instead of simply reading the value of a header card.

        Args:
            headarr (:obj:`list`):
                List of `astropy.io.fits.Header`_ objects.
            meta_key (:obj:`str`):
                Metadata keyword to construct.

        Returns:
            object: Metadata value read from the header(s).
        """
        if meta_key == 'binning':
            # Binning in lois headers is space-separated (like Gemini).
            binspec, binspatial = parse.parse_binning(headarr[0]['CCDSUM'])
            return parse.binning2string(binspec, binspatial)

        if meta_key == 'mjd':
            # Use astropy to convert 'DATE-OBS' into a mjd.
            ttime = Time(headarr[0]['DATE-OBS'], format='isot')
            return ttime.mjd

        if meta_key == 'lampstat01':
            # The spectral comparison lamps turned on are listed in `LAMPCAL`, but
            #  if no lamps are on, then this string is blank.  Return either the
            #  populated `LAMPCAL` string, or 'off' to ensure a positive entry for
            #  `lampstat01`.
            lampcal = headarr[0]['LAMPCAL'].strip()
            return 'off' if lampcal == '' else lampcal

        if meta_key == 'dispname':
            # Convert older FITS keyword GRATING (gpmm/blaze) into the newer
            #  Grating ID names (DVx) for easier identification of disperser.
            gratings = {
                "150/5000": "DV1",
                "300/4000": "DV2",
                "300/6750": "DV3",
                "400/8500": "DV4",
                "500/5500": "DV5",
                "600/4900": "DV6",
                "600/6750": "DV7",
                "831/8000": "DV8",
                "1200/5000": "DV9",
                "2160/5000": "DV10",
                "UNKNOWN": "DVxx"
            }
            if headarr[0]['GRATING'] not in gratings:
                raise ValueError(
                    f"Grating value {headarr[0]['GRATING']} not recognized.")
            return f"{gratings[headarr[0]['GRATING']]} ({headarr[0]['GRATING']})"

        if meta_key == 'decker':
            # Provide a stub for future inclusion of a decker on LDT/DeVeny.
            return headarr[0]['DECKER'] if 'DECKER' in headarr[0].keys(
            ) else 'None'

        if meta_key == 'filter1':
            # Remove the parenthetical knob position to leave just the filter name
            return headarr[0]['FILTREAR'].split()[0].upper()

        if meta_key == 'cenwave':
            # The central wavelength is more descriptive of the grating angle position
            #  than just the angle value.  Use the DeVeny grating angle formula to
            #  return the central wavelength of the configuration.

            # Extract lines/mm, catch 'UNKNOWN' grating
            lpmm = (np.inf if headarr[0]["GRATING"] == "UNKNOWN" else float(
                headarr[0]["GRATING"].split("/")[0]))

            # DeVeny Fixed Optical Angles in radians
            CAMCOL = np.deg2rad(55.00)  # Camera-to-Collimator Angle
            COLL = np.deg2rad(10.00)  # Collimator-to-Grating Angle
            # Grating angle in radians
            theta = np.deg2rad(float(headarr[0]['GRANGLE']))
            # Wavelength in Angstroms
            wavelen = ((np.sin(COLL + theta) + np.sin(COLL + theta - CAMCOL)
                        )  # Angles
                       * 1.0e7  # Angstroms/mm
                       / lpmm  # lines / mm
                       )
            # Round the wavelength to the nearest 5A
            return np.around(wavelen / 5, decimals=0) * 5

        msgs.error(f"Not ready for compound meta {meta_key} for LDT/DeVeny")