"""Spectrograph is a class that general describes a spectrograph.  This includes
describing the telescope, slit,  collimator, grating, camera, and detector.
 
HISTORY
20090912 SMC  First written by SM Crawford
 
Limitations:
-Still need to verify how alpha, grating angle, beta, and camera angle
 to see if I can hardwire some of the tasks
 
"""
 
 
import math
 
from SpectrographEquations import *
from Grating import Grating
from Optics import Optics
from Slit import Slit
from Detector import Detector
from CCD import CCD
 
 
 
class Spectrograph(Grating, Optics, Slit, Detector):
   """A class describing a spectrograph and functions 
      related to a spectrograph.  All angles are in degrees.
   """
 
   def __init__(self, camang=45, gratang=45, grating=Grating(), camera=Optics(),\
                collimator=Optics(), telescope=Optics(), slit=Slit(),           \
                detector=Detector()):  
 
       #initiate the grating
       self.grating=grating 
 
       #initiate the telescope
       self.telescope=telescope
 
       #initiate the collimator
       self.collimator=collimator
 
       #initiate the camera
       self.camera=camera
 
       #initiate the slit
       self.slit=slit
 
       #initiate the detector
       self.detector=detector
 
       #set up the angles in the system
       self.gratang=gratang
       self.camang=camang
 
       return
 
   def alpha(self):
       return self.gratang
 
   def beta(self):
       return self.camang-self.gratang
 
   def gamma(self):
       return self.gamma
 
   def calc_wavelength(self, alpha, beta, gamma=0.0, nd=n_index):
       """Apply the grating equation to determine the wavelength
          returns wavelength in mm
       """
       w=gratingequation(self.grating.sigma,self.grating.order, self.grating.sign, alpha, beta, gamma=gamma, nd=nd)
       return w
 
   def calc_angdisp(self, beta):
       """Calculate the angular dispersion according to m/sigma/cos beta 
 
          returns angular dispersion in 1/mm
       """
       A=calc_angdisp(self.grating.sigma, self.grating.order, beta)
       return A
 
   def calc_lindisp(self, beta): 
       """Calculate the linear dispersion according to f_cam * A 
 
          return linear dispersion in mm/mm
 
       """
       return calc_lindisp(self.camera.focallength,self.grating.sigma, self.grating.order, beta)
 
   def calc_demagspatial(self):
       """Calculate the spatial demagnification
 
          returns the spatial demagnification
       """
       return calc_demagspatial(self.collimator.focallength,self.camera.focallength)
 
   def calc_demagspectral(self, alpha, beta):
       """Calculate the spectral demagnification
 
          returns the spectral demagnification
       """
       return self.calc_demagspatial()/se.calc_anamorph(alpha, beta)
 
   def calc_spatslitimage(self):
       """Calculate the spatial extant of the slit image
 
          return in mm
       """
       return self.slit.width/self.calc_demagspatial()
 
   def calc_specslitimage(self, beta):
       """Calculate the spectral extant of the slit image
 
          return in mm
       """
       return self.slit.width*self.calc_lindisp(beta)
 
   def calc_resolelement(self, alpha, beta):
       """Calculate the resolution of a single element for a filled slit
 
          return the wavelength resolution in mm
       """
       dw=calc_resolelement(self.slit.width, self.collimator.focallength,   \
                               self.grating.sigma, self.grating.order,         \
                               alpha, beta )
       return dw
 
   def calc_resolution(self, w, alpha, beta):
       """Calculate the resolution at a given wavelength.  w/dw
 
          returns resolution
       """
       return w/self.calc_resolelement(alpha, beta)
 
   def calc_centralwavelength(self):  
       """Calculate the central wavlength
 
          return waveleng in mm
       """
       return self.calc_wavelength(self.alpha(), -self.beta())
 
   def calc_redwavelength(self):
       """For the detector, calculate the maximum red wavelength
          Assume just the width of the detector
 
          return waveleng in mm
       """
       dbeta=math.degrees(math.atan(0.5*self.detector.width/self.camera.focallength))
       return self.calc_wavelength(self.alpha(), -self.beta()-dbeta)
 
   def calc_bluewavelength(self):
       """For the detector, calculate the maximum blue wavelength
          Assume just the width of the detector
 
          return waveleng in mm
       """
       dbeta=math.degrees(math.atan(0.5*self.detector.width/self.camera.focallength))
       return self.calc_wavelength(self.alpha(), -self.beta()+dbeta)