import glob
import numpy as np
import pylab as py
import matplotlib.pyplot as plt
import math
from astropy.io import fits as pyfits
import datetime
try:
    import urllib.request, urllib.parse, urllib.error
except:
    import urllib
    p2 = True # Python 2 is running, so the urllib command is different
import os, sys
from kai.reduce import kai_util
from kai.reduce import util
from kai.reduce import slalib
from kai import instruments
from astropy.table import Table
from astropy.time import Time
[docs]
module_dir = os.path.dirname(__file__) 
[docs]
def get_atm_conditions(year):
    """
    Retrieve atmospheric conditions from CFHT archive website,
    then calls dar.splitAtmosphereCFHT() to separate the data
    by months.
    """
    yearStr = str(year)
    if p2: # Python 2 command, necessary for IRAF
        _atm = urllib.urlopen("http://mkwc.ifa.hawaii.edu/archive/wx/cfht/cfht-wx.%s.dat" % yearStr)
    else:
        _atm = urllib.request.urlopen("http://mkwc.ifa.hawaii.edu/archive/wx/cfht/cfht-wx.%s.dat" % yearStr)
    atm = _atm.read()
    _atm.close()
    
    root = module_dir + '/weather/'
    if type(atm) == bytes:
        # this is for python 3
        atmfile = open(root + 'cfht-wx.' + yearStr + '.dat','wb')
    else:
        atmfile = open(root + 'cfht-wx.' + yearStr + '.dat','w')
    atmfile.write(atm)
    atmfile.close()
    splitAtmosphereCFHT(str(year)) 
[docs]
def keckDARcoeffs(lamda, year, month, day, hour, minute):
    """
    Calculate the differential atmospheric refraction
    for two objects observed at Keck.
    Input:
    lamda -- Effective wavelength (microns) assumed to be the same for both
    year, month, day, hour, minute of observation (HST)
    Output:
    refA
    refB
    """
    # iraf.noao()
    # Set up Keck observatory info
    # foo = iraf.noao.observatory(command="set", obsid="keck", Stdout=1)
    # obs = iraf.noao.observatory
    ####################
    # Setup all the parameters for the atmospheric refraction
    # calculations. Typical values obtained from the Mauna Kea
    # weather pages and from the web.
    ####################
    # 
    # Temperature Lapse Rate (Kelvin/meter)
    tlr = 0.0065
    # Precision required to terminate the iteration (radian)
    eps = 1.0e-9
    # Height above sea level (meters)
    # hm = obs.altitude
    hm = 4160.0                             #Keck height. Hard coded to remove iraf dependency
    # Latitude of the observer (radian)
    # phi = math.radians(obs.latitude)
    phi = math.radians(19.82833333333333)   #Keck latitude. Hard coded to remove iraf dependency
    # Pull from atmosphere logs.
    logDir = module_dir + '/weather/'
    logFile = logDir +'cfht-wx.'+ str(year) +'.'+ str(month).zfill(2) +'.dat'
    print(logFile)
    _atm = Table.read(logFile, format='ascii', header_start=None)
    atmYear = _atm['col1']
    atmMonth = _atm['col2']
    atmDay = _atm['col3']
    atmHour = _atm['col4']
    atmMin = _atm['col5']       # HST times
    atmTemp = _atm['col8']      # Celsius
    atmHumidity = _atm['col9']  # percent
    atmPressure = _atm['col10'] # mb pressure
    # Find the exact time match for year, month, day, hour
    idx = (np.where((atmYear == year) & (atmMonth == month) &
                    (atmDay == day) & (atmHour == hour)))[0]
    
    if (len(idx) == 0):
        print(( 'Could not find DAR data for %4d-%2d-%2d %2d:%2d in %s' % \
            
(year, month, day, hour, minute, logFile)))
    atmMin = atmMin[idx]
    atmTemp = atmTemp[idx]
    atmHumidity = atmHumidity[idx]
    atmPressure = atmPressure[idx]
    # Find the closest minute
    minDiff = abs(atmMin - minute)
    sdx = minDiff.argsort()
    # Select out the closest in time.
    # Ambient Temperature (Kelvin)
    # Should be around 274.0 Kelvin
    tdk = atmTemp[sdx[0]] + 272.15
    # Pressure at the observer (millibar)
    # Should be around 760.0 millibars
    pmb = atmPressure[sdx[0]]
    # Relative humidity (%)
    # Should be around 0.1 %
    rh = atmHumidity[sdx[0]] / 100.0 #relative humidity should be between 0 and 1
    # print(hm, tdk, pmb, rh, lamda, phi, tlr, eps)
    return slalib.refco(hm, tdk, pmb, rh, lamda, phi, tlr, eps) 
[docs]
def download_koa_dat_files(date_str, telescope_str, param_name, download_loc='./'):
    """
    Download the KOA atmospheric condition file for given date and
    specified parameter, and save at the download location
    
    Parameters
    ----------
    date_str : str
        Date string for the night (e.g.: '20220525').
    telescope_str : str
        String to specify which Keck telescope's weather data to download.
        Can be 'k1' or 'k2'.
    param_name : str
        Name of the parameter to download the conditions table
        (e.g.: 'OutsideTemp')
    download_loc : str, default = './'
        Directory to store the downloaded atmospheric condition table file.
    """
    
    url = 'https://koa.ipac.caltech.edu/data33/WEATHER/{0}/nightly2/{1}_{0}_{2}.dat'.format(
        date_str, telescope_str, param_name)
    
    if p2: # Python 2 command, necessary for IRAF
        _atm = urllib.urlopen(url)
    else:
        _atm = urllib.request.urlopen(url)
    atm = _atm.read()
    _atm.close()
    
    out_file_name = '{0}_{1}_{2}.dat'.format(telescope_str, date_str, param_name)
    
    if type(atm) == bytes:
        # this is for python 3
        atmfile = open(download_loc + '/' + out_file_name, 'wb')
    else:
        atmfile = open(download_loc + '/' + out_file_name,'w')
    
    atmfile.write(atm)
    atmfile.close() 
[docs]
def keckDARcoeffs_koa(lamda, year, month, day, hour, minute, second,
        instrument=instruments.default_inst):
    """
    Calculate the differential atmospheric refraction
    for two objects observed at Keck, using atmospheric conditions obtained
    via the KOA.
    Parameters
    ----------
    lamda : float
        Effective wavelength (microns), assumed to be the same for both.
    year : int
        UTC year
    month : int
        UTC month
    day : int
        UTC date
    hour : int
        UTC hour
    minute : int
        UTC minute
    second : int, float
        UTC second
    
    Returns
    -------
    refA : float
    refB : float
    """
    # Set up datetime object for image time
    utc = datetime.datetime(year, month, day, hour, minute, second)
    
    # Setup all the parameters for the atmospheric refraction
    # calculations. Typical values obtained from the Mauna Kea
    # weather pages and from the web.
    
    # Temperature Lapse Rate (Kelvin/meter)
    tlr = 0.0065
    
    # Precision required to terminate the iteration (radian)
    eps = 1.0e-9
    
    # Following hard coded to remove iraf dependency
    # Height above sea level (meters)
    hm = 4160.0     #Keck height
    
    # Latitude of the observer (radian)
    phi = math.radians(19.82833333333333)   #Keck latitude
    
    # Write out atm files
    if not os.path.isdir('weather'):
        os.mkdir('weather')
    
    # Generate telescope and date strings for the atm files
    telescope_strs = {
        'NIRC2': 'k2',
        'OSIRIS': 'k1',
    }
    
    telescope_str = telescope_strs[instrument.name]
    
    date_str = '{0}{1}{2}'.format(year, str(month).zfill(2), str(day).zfill(2))
    
    dat_file_root = './weather/{0}_{1}_'.format(telescope_str, date_str)
    
    # Check if necessary logs are pulled for the observation date
    if not os.path.isfile(dat_file_root + 'OutsideTemp.dat'):
        download_koa_dat_files(
            date_str, telescope_str, 'OutsideTemp', './weather')
    
    if not os.path.isfile(dat_file_root + 'OutsideHumidity.dat'):
        download_koa_dat_files(
            date_str, telescope_str, 'OutsideHumidity', './weather')
    
    if not os.path.isfile(dat_file_root + 'Pressure.dat'):
        download_koa_dat_files(
            date_str, telescope_str, 'Pressure', './weather')
    
    # Read in tables and find closest rows to image time
    # Temperature
    temp_table = Table.read(dat_file_root + 'OutsideTemp.dat',
                            format='ascii.basic', data_start=1, delimiter='\t',
                            guess=False, fast_reader=False,
                            header_start=None,
                            names=['row', 'temp', 'timeinsecs',
                                   'datetime_utc'],
                           )
    
    temp_datetimes = Time(temp_table['datetime_utc'], format='iso').datetime
    temp_closest_index = np.argmin(np.abs(temp_datetimes - utc))
    
    atm_temp = (temp_table[temp_closest_index])['temp'] + 272.15    # Kelvin
    
    # Humidity
    hum_table = Table.read(dat_file_root + 'OutsideHumidity.dat',
                            format='ascii.basic', data_start=1, delimiter='\t',
                            guess=False, fast_reader=False,
                            header_start=None,
                            names=['row', 'humidity', 'timeinsecs',
                                   'datetime_utc'],
                           )
    
    hum_datetimes = Time(hum_table['datetime_utc'], format='iso').datetime
    hum_closest_index = np.argmin(np.abs(hum_datetimes - utc))
    
    atm_hum = (hum_table[hum_closest_index])['humidity'] / 100.     # Percent
    
    # Pressure
    pres_table = Table.read(dat_file_root + 'Pressure.dat',
                            format='ascii.basic', data_start=1, delimiter='\t',
                            guess=False, fast_reader=False,
                            header_start=None,
                            names=['row', 'pressure', 'timeinsecs',
                                   'datetime_utc'],
                           )
    
    pres_datetimes = Time(pres_table['datetime_utc'], format='iso').datetime
    pres_closest_index = np.argmin(np.abs(pres_datetimes - utc))
    
    atm_pres = (pres_table[pres_closest_index])['pressure']     # millibar
    
    # Calculate refraction coefficients
    print(hm, atm_temp, atm_pres, atm_hum, lamda, phi, tlr, eps)
    return slalib.refco(hm, atm_temp, atm_pres, atm_hum, lamda, phi, tlr, eps) 
[docs]
def kaidar(fitsFile, instrument=instruments.default_inst,
        use_koa_weather=False):
    """
    Use the FITS header to extract date, time, wavelength,
    elevation, and image orientation information. This is everything
    that is necessary to calculate the differential atmospheric
    refraction. The differential atmospheric refraction 
    is applicable only along the zenith direction of an image.
    This code calculates the predicted DAR using archived CFHT
    atmospheric data and the elevation and wavelength of the observations.
    Then the DAR correction is transformed into image coefficients that
    can be applied in image coordinates.
    """
    # Get header info
    img, hdr = pyfits.getdata(fitsFile, header=True)
    effWave = instrument.get_central_wavelength(hdr)
    elevation = hdr[instrument.hdr_keys['elevation']]
    # airmass = hdr['AIRMASS']
    # parang = hdr['PARANG']
    date = hdr['DATE-OBS'].split('-')
    year = int(date[0])
    month = int(date[1])
    day = int(date[2])
    utc = hdr['UTC'].split(':')
    hour = int(utc[0])
    minute = int(utc[1])
    second = int(math.floor(float(utc[2])))
    utc = datetime.datetime(year, month, day, hour, minute, second)
    utc2hst = datetime.timedelta(hours=-10)
    hst = utc + utc2hst
    
    if use_koa_weather:
        (refA, refB) = keckDARcoeffs_koa(effWave, utc.year, utc.month, utc.day,
                                         utc.hour, utc.minute, utc.second)
    else:
        (refA, refB) = keckDARcoeffs(effWave, hst.year, hst.month, hst.day,
                                     hst.hour, hst.minute)
    tanz = math.tan(math.radians(90.0 - elevation))
    tmp = 1.0 + tanz**2
    darCoeffL = tmp * (refA + 3.0 * refB * tanz**2)   #unitless
    darCoeffQ = -tmp * (refA*tanz +
                            3.0 * refB * (tanz + 2.0*tanz**3))   #units: radians^-1
    # Convert DAR coefficients for use with arcseconds
    # scale = instrument.get_plate_scale(hdr)
    # darCoeffQ *= 1.0 * scale / 206265.0
    darCoeffL *= 1.0
    darCoeffQ *= 1.0 / 206265.0             #units now arcsec^-1
   
    # # Lets determine the zenith and horizon unit vectors for
    # # this image.
    # pos_ang = instrument.get_position_angle(hdr)
    # pa = math.radians(parang + pos_ang)
    # zenithX = -math.sin(pa)
    # zenithY = math.cos(pa)
    # # Compute the predicted differential atmospheric refraction
    # # over a 10'' seperation along the zenith direction.
    # # Remember coeffecicents are only for deltaZ in pixels
    # deltaZ = img.shape[0] * scale
    # deltaR = darCoeffL * (deltaZ/scale) + darCoeffQ * (deltaZ/scale)**2
    # deltaR *= scale # now in arcseconds
    # magnification = (deltaZ + deltaR) / deltaZ
    # print(( 'DAR FITS file = %s' % (fitsFile)))
    # print(('DAR over 10": Linear dR = %f"  Quad dR = %f"' % \
    #       (darCoeffL * deltaZ, darCoeffQ * deltaZ**2)))
    # print(('DAR Magnification = %f' % (magnification)))
    # print(('DAR Vertical Angle = %6.1f' % (math.degrees(pa))))
    return (darCoeffL, darCoeffQ) 
[docs]
def darPlusDistortion(inputFits, outputRoot, xgeoim=None, ygeoim=None,
        instrument=instruments.default_inst,
        use_koa_weather=False):
    """
    Create lookup tables (stored as FITS files) that can be used
    to correct DAR. Optionally, the shifts due to DAR can be added
    to existing NIRC2 distortion lookup tables if the xgeoim/ygeoim
    input parameters are set.
    Inputs:
    inputFits - a NIRC2 image for which to determine the DAR correction
    outputRoot - the root name for the output. This will be used as the
        root name of two new images with names, <outputRoot>_x.fits and 
        <outputRoot>_y.fits.
    Optional Inputs:
    xgeoim/ygeoim - FITS images used in Drizzle distortion correction
        (lookup tables) will be modified to incorporate the DAR correction.
        The order of the correction is 1. distortion, 2. DAR.
        
    """
    # Get the size of the image and the half-points
    hdr = pyfits.getheader(inputFits)
    imgsizeX = int(hdr['NAXIS1'])
    imgsizeY = int(hdr['NAXIS2'])
    halfX = int(round(imgsizeX / 2.0))
    halfY = int(round(imgsizeY / 2.0))
    # First get the coefficients
    (darCoeffL, darCoeffQ) = kaidar(inputFits, instrument=instrument,
                                    use_koa_weather=use_koa_weather)
    # Convert DAR coefficients for use with units of NIRC2 pixels
    scale = instrument.get_plate_scale(hdr)
    darCoeffL *= 1.0                
    darCoeffQ *= 1.0 * scale #/ 206265.0    
    pa = math.radians(instrument.get_parallactic_angle(hdr) + instrument.get_position_angle(hdr))
    #(a, b) = kaidarPoly(inputFits)
    # Create two 1024 arrays (or read in existing ones) for the
    # X and Y lookup tables
    if ((xgeoim == None) or (xgeoim == '')):
        x = np.zeros((imgsizeY, imgsizeX), dtype=float)
    else:
        x = pyfits.getdata(xgeoim)
    if ((ygeoim == None) or (ygeoim == '')):
        y = np.zeros((imgsizeY, imgsizeX), dtype=float)
    else:
        y = pyfits.getdata(ygeoim)
    # Get proper header info.
    fits = pyfits.open(inputFits)
    axisX = np.arange(imgsizeX, dtype=float) - halfX
    axisY = np.arange(imgsizeY, dtype=float) - halfY
    xcoo2d, ycoo2d = np.meshgrid(axisX, axisY)
    xnew1 = xcoo2d + x
    ynew1 = ycoo2d + y
    # Rotate coordinates clockwise by PA so that zenith is along +ynew2
    # PA = parallactic angle (angle from +y to zenith going CCW)
    sina = math.sin(pa)
    cosa = math.cos(pa)
    xnew2 = xnew1 * cosa + ynew1 * sina
    ynew2 = -xnew1 * sina + ynew1 * cosa
    # Apply DAR correction along the y axis
    xnew3 = xnew2
    ynew3 = ynew2*(1 + darCoeffL) + ynew2*np.abs(ynew2)*darCoeffQ
    # Rotate coordinates counter-clockwise by PA back to original
    xnew4 = xnew3 * cosa - ynew3 * sina
    ynew4 = xnew3 * sina + ynew3 * cosa
    #xnew2 = a[0] + a[1]*xnew1 + a[2]*ynew1 + \
    #        a[3]*xnew1**2 + a[4]*xnew1*ynew1 + a[5]*ynew1**2
    #ynew2 = b[0] + b[1]*xnew1 + b[2]*ynew1 + \
    #        b[3]*xnew1**2 + b[4]*xnew1*ynew1 + b[5]*ynew1**2
    x = xnew4 - xcoo2d
    y = ynew4 - ycoo2d
    xout = outputRoot + '_x.fits'
    yout = outputRoot + '_y.fits'
    util.rmall([xout, yout])
    fits[0].data = x
    fits[0].writeto(xout, output_verify='silentfix')
    fits[0].data = y
    fits[0].writeto(yout, output_verify='silentfix')
    return (xout, yout) 
[docs]
def applyDAR(inputFits, spaceStarlist, plot=False, instrument=instruments.default_inst, plotdir = './'):
    """
    inputFits: (str) name of fits file associated with this starlist
    spaceStarlist: (astropy table) must include columns 'x0' and 'y0'.
    Input a starlist in x=RA (+x = west) and y=Dec (arcseconds) taken from
    space and introduce differential atmospheric refraction (DAR). The amount
    of DAR that is applied depends on the header information in the input fits
    file. The resulting output starlist should contain what was observed
    after the starlight passed through the atmosphere, but before the
    starlight passed through the telescope. Only achromatic DAR is 
    applied in this code.
    returns spaceStarlist with updated 'x0' and 'y0'
    """
    # Get header info
    #hdr = pyfits.getheader(fits)
    #effWave = hdr['EFFWAVE']
    #elevation = hdr['EL']
    #lamda = hdr['CENWAVE']
    #airmass = hdr['AIRMASS']
    #parang = hdr['PARANG']
    #
    #date = hdr['DATE-OBS'].split('-')
    #year = int(date[0])
    #month = int(date[1])
    #day = int(date[2])
    #utc = hdr['UTC'].split(':')
    #hour = int(utc[0])
    #minute = int(utc[1])
    #second = int(math.floor(float(utc[2])))
    #utc = datetime.datetime(year, month, day, hour, minute, second)
    #utc2hst = datetime.timedelta(hours=-10)
    #hst = utc + utc2hst
    #(refA, refB) = keckDARcoeffs(effWave, hst.year, hst.month, hst.day,
    #                             hst.hour, hst.minute)
    #tanz = math.tan(math.radians(90.0 - elevation))
    #tmp = 1.0 + tanz**2
    #darCoeffL = tmp * (refA + 3.0 * refB * tanz**2)
    #darCoeffQ = -tmp * (refA*tanz +
    #                        3.0 * refB * (tanz + 2.0*tanz**3))
    # Convert DAR coefficients for use with arcseconds
    #darCoeffL *= 1.0
    #darCoeffQ *= 1.0 / 206265.0
    
    # Lets determine the zenith and horizon unit vectors for
    # this image. The angle we need is simply the parallactic
    # (or vertical) angle since ACS images are North Up already.
    #pa = math.radians(parang)
    #MS: presumably the above code is all replacable with this call (which uses the intrument object
    (darCoeffL, darCoeffQ) = kaidar(inputFits, instrument=instrument)
    hdr = pyfits.getheader(inputFits)
    pa = math.radians(instrument.get_parallactic_angle(hdr))
    ##########
    #
    # Read in the starlist
    #
    ##########
    # _list = Table.read(spaceStarlist, format='ascii')
    # cols = list(_list.columns.keys())
    # names = [_list[ss][0].strip() for ss in range(len(_list))]
    # mag = _list[cols[1]]
    # date = _list[cols[2]]
    # x = _list[cols[3]] # RA in arcsec
    # y = _list[cols[4]]
    # xe = _list[cols[5]]
    # ye = _list[cols[6]]
    x = spaceStarlist['x0']     #already in arcseconds, x increasing to west.
    y = spaceStarlist['y0']
    # Magnify everything in the y (zenith) direction. Do it relative to
    # the first star. Even though dR depends on dzObs (ground observed dz),
    # it is a small mistake and results in less than a 10 micro-arcsec
    # change in dR.
    dx = x - x[0]
    dy = y - y[0]
    # Rotate coordinates CW so that the zenith angle is at +ynew
    sina = math.sin(pa)
    cosa = math.cos(pa)
    xnew1 = dx * cosa + dy * sina
    ynew1 = -dx * sina + dy * cosa
    # Apply DAR
    xnew2 = xnew1
    ynew2 = ynew1 * (1.0 - darCoeffL) - ynew1 * np.abs(ynew1) * darCoeffQ
    # Rotate coordinates CCW back to original angle
    xnew3 = xnew2 * cosa - ynew2 * sina
    ynew3 = xnew2 * sina + ynew2 * cosa
    xnew = xnew3 + x[0]
    ynew = ynew3 + y[0]
    ##########
    #
    # Write out the starlist
    #
    ##########
    # Save the current directory
    # newFits = fits.replace('.fits', '').split('/')[-1]
    # newList = newFits + '_acs.lis'
    # print(newList)
    # _new = open(newList, 'w')
    # for i in range(len(names)):
    #     _new.write('%10s  %7.3f  %7.2f  %10.4f  %10.4f  0  0  10  1  1  8\n' % \
    #           (names[i], mag[i], date[i], xnew[i], ynew[i]))
    # _new.close()
    # if (plot==True):
    #     py.clf()
    #     py.quiver(x, y, xnew - x, ynew - y, scale=0.02)
    #     py.quiver([0], [0], [0.001], [0], color='r', scale=0.02)
    #     py.axis([-5, 5, -5, 5])
    #     py.show()
        
    if (plot==True):
        # plt.close('all')
        # plt.plot(figsize=(4,4))
        q = plt.quiver(x, y, xnew - x, ynew - y, scale=0.02)
        # plt.xlabel('RA (arcsec)')
        # plt.ylabel('DEC (arcsec)')
        plt.xlabel(r'$\Delta$ RA * cos(Dec) (arcsec)')
        plt.ylabel(r'$\Delta$ Dec (arcsec)')
        plt.figtext(0.15,0.85,'q =' + str(round(math.degrees(pa))))
        plt.axis('equal')
        quiv_label = '1 mas'
        quiv_label_val = 0.001
        plt.quiverkey(q, 0.78, 0.85, quiv_label_val, quiv_label, coordinates='figure', labelpos='E', color='green')     
        plt.savefig(plotdir+ inputFits[-26:-5] + '.pdf', bbox_inches='tight')
        # plt.show()
    spaceStarlist['x0'] = xnew
    spaceStarlist['y0'] = ynew 
    return spaceStarlist 
[docs]
def removeDAR(inputFits, groundStarlist, plot=False, instrument=instruments.default_inst, plotdir = './'):
    """
    inputFits: (str) name of fits file associated with this starlist
    groundStarlist: (astropy table) must include columns 'x' and 'y'.
    The inverse of applyDAR(). Takes a starlist in x and y pixels taken from
    the ground and removes differential atmospheric refraction (DAR). The amount
    of DAR that is applied depends on the header information in the input fits
    file. The resulting output starlist should contain what would be observed above the atmosphere.
    Distortion should be applied before this function. Only achromatic DAR is 
    applied in this code.
    returns groundStarlist with updated 'x0' and 'y0'
    """
    (darCoeffL, darCoeffQ) = kaidar(inputFits, instrument=instrument)
    hdr = pyfits.getheader(inputFits)
    imgsizeX = int(hdr['NAXIS1'])
    imgsizeY = int(hdr['NAXIS2'])
    halfX = int(round(imgsizeX / 2.0))
    halfY = int(round(imgsizeY / 2.0))
    scale = instrument.get_plate_scale(hdr)
    darCoeffL *= 1.0                
    darCoeffQ *= 1.0 * scale #/ 206265.0   
    pa = math.radians(instrument.get_parallactic_angle(hdr) + instrument.get_position_angle(hdr))
  
    x = groundStarlist['x']     # in pixels, x increasing to west?
    y = groundStarlist['y']
    xnew1 = x - halfX
    ynew1 = y - halfY
    # Rotate coordinates clockwise by PA so that zenith is along +ynew2
    # PA = parallactic angle (angle from +y to zenith going CCW)
    sina = math.sin(pa)
    cosa = math.cos(pa)
    xnew2 = xnew1 * cosa + ynew1 * sina
    ynew2 = -xnew1 * sina + ynew1 * cosa
    # Apply DAR correction along the y axis
    xnew3 = xnew2
    ynew3 = ynew2*(1 + darCoeffL) + ynew2*np.abs(ynew2)*darCoeffQ
    # Rotate coordinates counter-clockwise by PA back to original
    xnew4 = xnew3 * cosa - ynew3 * sina
    ynew4 = xnew3 * sina + ynew3 * cosa
    xnew = xnew4 + halfX
    ynew = ynew4 + halfY
    if (plot==True):
        # plt.close('all')
        plt.figure(figsize=(6,6))
        q = plt.quiver(x, y, xnew - x, ynew - y, scale=2.0)
        plt.xlabel('x (pixels)')
        plt.ylabel('y (pixels)')
        # plt.xlabel(r'$\Delta$ RA * cos(DEC) (arcsec)')
        # plt.ylabel(r'$\Delta$ DEC (arcsec)')
        plt.figtext(0.15,0.85,'q =' + str(round(math.degrees(pa))))
        quiv_label = '0.1 pix'
        quiv_label_val = 0.1
        plt.quiverkey(q, 0.80, 0.85, quiv_label_val, quiv_label, coordinates='figure', labelpos='E', color='green')     
        plt.savefig(plotdir+ inputFits[-26:-5] + '.jpg', bbox_inches='tight')
        # plt.show()
    groundStarlist['x'] = xnew
    groundStarlist['y'] = ynew 
    return groundStarlist   
[docs]
def splitAtmosphereCFHT(year):
    """
    Take an original archive file containing atmospheric parameters and
    split it up into seperate files for individual months. This makes
    later calls to calculate DAR parameters MUCH faster.
    """
    yearStr = str(year)
    logDir = module_dir + '/weather/'
    logFile = logDir + '/cfht-wx.' + yearStr + '.dat'
    _infile = open(logFile, 'r')
    outfiles = []
    for ii in range(1, 12+1):
        monthStr = str(ii).zfill(2)
        _month = open(logDir + '/cfht-wx.' +yearStr+ '.' +monthStr+ '.dat', 'w')
        outfiles.append( _month )
    for line in _infile:
        fields = line.split()
        month = int(fields[1])
        
        # Check for wrong month number
        if not (month > 0 and month <= 12):
            continue
        
        _outfile = outfiles[month-1]
        _outfile.write(line)
    for _month in outfiles:
        _month.close() 
        
        
    
[docs]
def test_darPlusDistortion():
    data_dir = module_dir + '/distortion/'
    file_geox_darunfix = data_dir + 'nirc2dist_xgeoim.fits'
    file_geoy_darunfix = data_dir + 'nirc2dist_ygeoim.fits'
    data_dir = '/u/ghezgroup/data/m92_test/08jul_new_on/'
    file_geox_darfix = data_dir + 'reduce/kp/gc_f1/ce0249geo_x.fits'
    file_geoy_darfix = data_dir + 'reduce/kp/gc_f1/ce0249geo_y.fits'
    xon = pyfits.getdata(file_geox_darfix)
    yon = pyfits.getdata(file_geoy_darfix)
    xoff = pyfits.getdata(file_geox_darunfix)
    yoff = pyfits.getdata(file_geoy_darunfix)
    # Make arrays with the coordinates for each 
    imgsize = 1024
    axisX = np.arange(imgsize, dtype=float)
    axisY = np.arange(imgsize, dtype=float)
    xcoo2d, ycoo2d = np.meshgrid(axisX, axisY)
    # Lets trim so that we only keep every 20th pixel
    idx = np.arange(25, imgsize, 50)
    xon = xon.take(idx, axis=0).take(idx, axis=1)
    yon = yon.take(idx, axis=0).take(idx, axis=1)
    xoff = xoff.take(idx, axis=0).take(idx, axis=1)
    yoff = yoff.take(idx, axis=0).take(idx, axis=1)
    xcoo2d = xcoo2d.take(idx, axis=0).take(idx, axis=1)
    ycoo2d = ycoo2d.take(idx, axis=0).take(idx, axis=1)
    # Calculate differences
    xdiff = xon - xoff
    ydiff = yon - yoff
    # Make vector plots
    py.clf()
    qvr = py.quiver2([xcoo2d], [ycoo2d], [xdiff], [ydiff],
                     units='width', scale=5, 
                     width=0.005, headwidth=3, headlength=3, 
                     headaxislength=3)
    py.quiverkey(qvr, 100, 1120, 1.0, '1 pixel', coordinates='data', color='r')
    py.xlabel('NIRC2 X (pixel)')
    py.ylabel('NIRC2 Y (pixel)')
    py.title('Arrows point to DAR Fix')
    #py.savefig('plots/vector_daroffon.png')
    py.show()