Source code for synospec.etc.telescopes

#!/bin/env/python3
# -*- encoding utf-8 -*-
"""
Define telesope parameters

----

.. include license and copyright
.. include:: ../include/copy.rst

----

.. include common links, assuming primary doc root is up one directory
.. include:: ../include/links.rst

"""
import os
import numpy

from . import efficiency
from .. import data_file

# TODO: Define a `TelescopePort` or `TelescopeFocalPlane` class that
# holds the f-ratio, platescale, and throughput information, and allow
# each `Telescope` definition to have multiple focal planes?

[docs]class Telescope: """ Collect useful telescope parameters. Args: longitude (scalar-like): Earth coordinate with the location of the telescope in degrees. latitude (scalar-like): Earth coordinate with the location of the telescope in degrees. elevation (scalar-like): Earth elevation above sea level of the telescope in meters. fratio (scalar-like): F-ratio or beam speed (focal length over diameter) for the telescope focal plane. platescale (scalar-like): Telescope platescale in mm/arcsec. throughput (:obj:`float`, :class:`~synospec.etc.efficiency.Efficiency`, :class:`~synospec.etc.efficiency.CombinedEfficiency`, optional): The throughput of the telescope from the top of the telescope to the focal plane. area (scalar-like, optional): The true or effective area of the telescope aperture in square centimeters. If not provided, calculated using ``diameter``. Must be provided if ``diameter`` is not. diameter (scalar-like, optional): Telescope diameter in meters. If provided, used to set the telescope area. Must be provided if `area` is not. obstruction (scalar-like, optional): The unitless fraction of the total telescope area lost due to the central obstruction. If provided, the telescope area is multiplied by (1-`obstruction`) to obtain its effective area. If not provided, the `area` or `diameter` is assumed to account for the central obstruction. Raises: ValueError: Raised if both or neither of `diameter` or `area` are provoded. """ def __init__(self, longitude, latitude, elevation, fratio, platescale, throughput=1., area=None, diameter=None, obstruction=None): self.longitude = longitude self.latitude = latitude self.elevation = elevation self.fratio = fratio self.platescale = platescale self._throughput = throughput # If area is provided, use it directly: if area is not None: self.area = area if diameter is not None: raise ValueError('Cannot provide area and diameter; provide one or the other.') self.diameter = numpy.sqrt(area/numpy.pi)*2/100 if obstruction is not None: warnings.warn('Obstruction and area provided, combining to get effective area.') elif diameter is not None: self.diameter = diameter self.area = numpy.pi*numpy.square(self.diameter*100/2) else: raise ValueError('Must provide area or diameter!') # Apply the central obsruction if provided. if obstruction is not None: self.area *= (1-obstruction) @property def throughput(self): #, wave=None): if self._throughput is None: # TODO: Should this really raise an error... raise ValueError('Throughput not defined.') return self._throughput
[docs]class KeckTelescope(Telescope): """ Keck telescopes on MaunaKea """ def __init__(self): # This assumes the f/15 secondary fratio = 15 platescale = 0.725 # Assumes the Nasymth port for the throughput; three # reflections off an aluminum coating. eta_file = str(data_file(filename='efficiency') / 'aluminum.db') single_reflection = efficiency.Efficiency.from_file(eta_file) throughput = efficiency.CombinedEfficiency(dict([(key,single_reflection) for key in ['m1', 'm2', 'm3']])) super(KeckTelescope, self).__init__(155.47833, 19.82833, 4160.0, fratio, platescale, throughput=throughput, area=723674.)
[docs]class SDSSTelescope(Telescope): """ SDSS 2.5-meter telescope at Apache Point Observatory. """ def __init__(self): super(SDSSTelescope, self).__init__(105.82028, 32.78028, 2788.0, 5., 0.06048, diameter=2.5, obstruction=0.286)
[docs]class APFTelescope(Telescope): """ Automated Planet Finder telescope at Lick Observatory. """ def __init__(self): super(APFTelescope, self).__init__(121.64278, 37.34139, 1283.0, 15., 0.17452, diameter=2.41, obstruction=0.02)
[docs]class TMTTelescope(Telescope): """ Thirty-Meter Telescope, assuming it is constructed on Maunakea. """ def __init__(self, reflectivity='req'): """ TMT telescope parameters. Args: reflectivity (:obj:`str`, optional): The reflectivity curve to use. For the ORD requirement or goal, use ``'req'`` or ``'goal'``, respectively. """ if reflectivity not in ['req', 'goal']: raise ValueError('Must set reflectivity to \'req\' or \'goal\'.') # Assumes the Nasymth port for the throughput; three # reflections off an aluminum coating. # eta_file = os.path.join(os.environ['SYNOSPEC_DIR'], 'data', 'efficiency', # 'uv_enhanced_silver.db') # single_reflection = efficiency.Efficiency.from_file(eta_file) eta_file = str(data_file(filename='efficiency') / 'tmt_ord.db') db = numpy.genfromtxt(eta_file) c = 3 if reflectivity == 'req' else 4 single_reflection = efficiency.Efficiency(db[:,c], wave=db[:,0]) throughput = efficiency.CombinedEfficiency(dict([(key,single_reflection) for key in ['m1', 'm2', 'm3']])) # TODO: These numbers are all temporary # Focal ratio is f/15 # Area is just taken as 30m diameter with a 10% obstruction # Plate scale is 1e3 * 15[f/] * 30 [m] / 206265 [arcsec/rad] = 2.182 [mm/arcsec]""" # Location is just taken to be the same as Keck. super(TMTTelescope, self).__init__(155.47833, 19.82833, 4160.0, 15., 2.182, throughput=throughput, diameter=30, obstruction=0.1)
#class Observation: # """Not yet implemented...""" # def __init__(self, airmass, sky_brightness, exposure_time, wavelength=None, band=None): # if wavelength is None and band is None: # raise ValueError('Must provide band or wavelength.') # # self.airmass = airmass # self.sky_brightness = sky_brightness # self.exposure_time = exposure_time # self.wavelength = wavelength # self.band = band # # @classmethod # def from_date_telescope_target(cls, date, telescope, target, exposure_time, wavelength=None, # band=None): # raise NotImplementedError('Not yet implemented!') # # Use date to get lunar phase # lunar_cycle = 29.53 # days # day_since_new_moon = int(lunar_cycle*(0.5 # - astroplan.moon_phase_angle(Time(date)).value/2/numpy.pi)) # sky_brightness = interpolate_sky_brightness(day_since_new_moon, wavelength=wavelength, # band=band) # # Use date, telescope, and target to get airmass # targ = SkyCoord(target) # tele = EarthLocation(lat=telescope.latitude*units.deg, lon=telescope.longitude*units.deg, # height=telescope.elevation*units.m) # targaltaz = targ.transform_to(AltAz(obstime=Time(date), location=tele)) # airmass = targaltaz.secz # # return cls(airmass, sky_brightness, exposure_time, wavelength=wavelength, band=band)