Beispiel #1
0
def simple_track(ra, dec, frame, time, input_lat, input_lon, alt, plot,
                 alt_limit):
    prototype_dish = EarthLocation(lat=input_lat * u.deg,
                                   lon=input_lon * u.deg,
                                   height=alt * u.m)
    sc = SkyCoord(ra,
                  dec,
                  unit='deg',
                  frame=frame,
                  equinox="J2000",
                  obstime=time[0],
                  location=prototype_dish)
    sc = sc.transform_to('icrs')
    prototype_dish_observer = Observer(location=prototype_dish)

    source_altaz = sc.transform_to(AltAz(obstime=time,
                                         location=prototype_dish))
    for i in range(len(source_altaz)):
        if source_altaz[i].alt.deg > alt_limit:
            print("{0} {1:3.8f} {2:3.8f} {3} {4}".format(
                time[i].mjd,
                source_altaz[i].az.deg,
                source_altaz[i].alt.deg,
                1,
                prototype_dish_observer.parallactic_angle(time=time[i],
                                                          target=sc).deg,
                file=sys.stdout,
                flush=True))
        else:
            break
    if plot == 1:
        plt.scatter(source_altaz.az.deg, source_altaz.alt.deg)
        plt.show()
Beispiel #2
0
def print_parangs(name, beg_time, end_time, coord_ra, coord_dec, timezone):
    """
    Prints the parallactic angle range given the start and end observing
    times (in UCT/GMT), the telescope name and the celestial target
    coordinates.

    Currently accepted telescope names : uGMRT or MeerKAT (case insensitive)

    The beg time and end time should be in the format '2020-01-01T00:00:00'

    The target coordinates must be in HMS DMS (i.e., 00h00m00s +00d00m00s)
    """

    from astroplan import Observer
    import astropy.units as u
    from astropy.coordinates import SkyCoord
    from astropy.time import Time

    import numpy as np

    if name.lower() == 'ugmrt' or name.lower() == 'gmrt':
        obs = Observer(longitude='74d02m59s',
                       latitude='19d05m47s',
                       elevation=0 * u.m,
                       name='uGMRT',
                       timezone=timezone)
    elif name.lower() == 'meerkat':
        obs = Observer(longitude='21d24m40s',
                       latitude='-30d43m16s',
                       elevation=0 * u.m,
                       name='MeerKAT',
                       timezone=timezone)
    elif name.lower() == 'alma':
        obs = Observer(longitude='67d45m12s',
                       latitude='-23d01m09s',
                       elevation=0 * u.m,
                       name='ALMA',
                       timezone=timezone)
    else:
        raise NotImplementedError("Unknown telescope name")

    coord = SkyCoord(coord_ra, coord_dec)

    beg_time = Time(beg_time)
    end_time = Time(end_time)

    parang_beg = np.rad2deg(obs.parallactic_angle(beg_time, coord))
    parang_end = np.rad2deg(obs.parallactic_angle(end_time, coord))

    print("Beginning parallactic angle {:.3f}".format(parang_beg))
    print("Ending parallactic angle {:.3f}".format(parang_end.deg))

    print("Parang delta {:.3f}".format(parang_end - parang_beg))
Beispiel #3
0
    obsTime = aTime(obsDT)

    raIn = curRow['RA']
    decIn = curRow['DEC']
    ra = raIn[0:2] + ' ' + raIn[3:5] + ' ' + raIn[6:]
    dec = decIn[0:3] + ' ' + decIn[4:6] + ' ' + decIn[7:]
    curStar = ac.SkyCoord(ra, dec, unit=(u.hourangle, u.deg))

    altaz = curStar.transform_to(ac.AltAz(location=obsLoc, obstime=obsTime))
    airmass = altaz.secz

    selData.loc[curRow.name, 'ALT'] = altaz.alt.value
    selData.loc[curRow.name, 'AZ'] = altaz.az.value
    selData.loc[curRow.name, 'CALC_AIRMASS'] = airmass.value

    parang = subaruObserver.parallactic_angle(obsTime,
                                              curStar).value / np.pi * 180
    selData.loc[curRow.name, 'PARANG'] = parang

    if counter == counterInt:
        counter = 0
        print('%s iterations took %f seconds' %
              (counterInt, time.time() - startTime))
        startTime = time.time()
    else:
        counter = counter + 1

plt.plot(selData['ALT'], selData['IMRA'], '.')
plt.xlabel('ALT')
plt.ylabel('IMRA')

plt.plot(selData['ALT'], selData['PAD'], '.')
Beispiel #4
0
def get_localfcdata_xshooter(fc_params, inpars):
    '''
   Extracts all the important info to build a finding chart from a given XSHOOTER OB defined
   locally.
   
   Args:
      inpars: A dictionnary containing the OB parameters
   Returns:
       A dictionnary containing the XSHOOTER OB parameters
   '''

    # Acquisition
    fc_params['acq'] = copy.deepcopy(xshooter_acq_params)
    # Override the user input if clash with instrument mode
    fc_params['acq']['bos_ra'] = inpars['bos_ra']
    fc_params['acq']['bos_dec'] = inpars['bos_dec']
    fc_params['acq']['is_gs'] = inpars['is_gs']
    fc_params['acq']['gs'] = SkyCoord(inpars['gs_ra'],
                                      inpars['gs_dec'],
                                      frame='icrs',
                                      obstime=Time(fcm_m.obsdate),
                                      equinox='J2000')

    fc_params['acq']['acq_pa'] = inpars['acq_pa']

    # Deal with the blind offset by definition, the "target" in the OB is the
    # Acq. location, to which we "add" the blind offset to go to the real Target
    fc_params['target'] = fcm_t.offset_coord(
        fc_params['target'],
        delta_ra=fc_params['acq']['bos_ra'] * u.arcsec,
        delta_dec=fc_params['acq']['bos_dec'] * u.arcsec)

    if fc_params['acq']['acq_pa'] == 9999:
        fc_params['tags'] += ['parallactic_angle']

        if fcm_m.do_parang:
            # Ok, I need to compute the parallactic angle !
            # Use astroplan to do that
            UT2 = Observer(location=fcm_m.UT2_loc)
            fc_params['acq']['acq_pa'] = UT2.parallactic_angle(
                fcm_m.obsdate, fc_params['target']).to(u.deg).value + 180

            # Note: if I don't want to show the FoV when parang = 9999, I leave it as is, and
            # deal with it get_polygon().

            # Note 2: need to add 180deg, so that the orientation of the slit matches the screen on UT2 !

    # Observation
    fc_params['n_sci'] = 1
    fc_params['sci1'] = copy.deepcopy(xshooter_sci_params)
    fc_params['sci1']['noff'] = inpars['noff']
    fc_params['sci1']['ins_mode'] = inpars['ins_mode']
    fc_params['sci1']['obstype'] = [i for i in inpars['obstype'][0].split()]
    fc_params['sci1']['off1'] = [
        float(i) for i in str(inpars['off1'][0]).split()
    ]
    fc_params['sci1']['off2'] = [
        float(i) for i in str(inpars['off2'][0]).split()
    ]
    fc_params['sci1']['return'] = inpars['return']
    if inpars['coordtype'] == 'SLIT':
        fc_params['sci1'][
            'coordtype'] = 'DETECTOR'  # In fcmaker, SLIT = DETECTOR
    else:
        fc_params['sci1']['coordtype'] = inpars['coordtype']

    fc_params['sci1']['uvb_slt'] = inpars['uvb_slt'] * u.arcsec
    fc_params['sci1']['vis_slt'] = inpars['vis_slt'] * u.arcsec
    fc_params['sci1']['nir_slt'] = inpars['nir_slt'] * u.arcsec
    fc_params['sci1']['slt_throw'] = inpars['slt_throw'] * u.arcsec

    # Deal with the AutoNodOnSlit template
    # Pretend the nodding are regular offsets. Ignore any jittering.
    if fc_params['sci1']['slt_throw'].value > 0:
        fc_params['sci1']['noff'] = 2
        fc_params['sci1']['obstype'] = ['O', 'O']
        fc_params['sci1']['coordtype'] = 'DETECTOR'
        fc_params['sci1']['off1'] = [
            -fc_params['sci1']['slt_throw'].to(u.arcsec).value / 2.,
            +fc_params['sci1']['slt_throw'].to(u.arcsec).value
        ]
        fc_params['sci1']['off2'] = [0, 0]

    return fc_params
Beispiel #5
0
def get_p2fcdata_xshooter(fc_params, ob, api):
    '''
   Extracts all the important info to build a finding chart from a given XSHOOTER OB from p2.
   
   Args:
      fc_params: dictionnary of finding chart parameters
      ob: an api.getOB() object
      api: a p2api.ApiConnection() object 
   Returns:
       A dictionnary containing the OB parameters
   '''

    obId = ob['obId']

    # Fetch all the associated templates
    templates, templatesVersion = api.getTemplates(obId)

    fc_params['n_sci'] = 0  # The number of Science template in the OB

    # First of all, check if I have an acquisition template - required to set the
    # instrument mode, etc ...
    is_acq = False
    for t in templates:
        if t['type'] == 'acquisition':
            is_acq = True

    if not (is_acq):
        raise Exception('Ouch! I found no acquisition template (required)!')

    # Ok, from now on, I can assume that the OB does contain an acq template.

    # First, let us loop through all the templates in the OB, to see which ones are supported
    for (n, t) in enumerate(templates):
        # If this is a weird template, issue a warning, and continue
        if not (t['templateName'] in xshooter_templates):
            warnings.warn(
                'Template %s is not supported by fcmaker. Skipping it.' %
                (t['templateName']))
            continue

        # Ok, this is a template I know about. Let's check which it is.
        # p2api returns the templates in the order defined inside p2 by the user.
        # I.e. it places the acquisition first, even if it was not create first.
        if t['type'] == 'acquisition':

            # There should only ever be one acquisition ... let's make sure of this ...
            try:
                fc_params['acq']  # Not yet created, should create an error
            except:
                pass  # This is the expected route
            else:  # fc_params['acq'] already exists - this should not be!
                raise Exception(
                    'Ouch! There can only be one acquisition template per OB!')

            # Fill the acq parameters
            fc_params['acq'] = copy.deepcopy(xshooter_acq_params)

            tpl, tplVersion = api.getTemplate(obId, t['templateId'])
            for param in tpl['parameters']:

                # Guide Star
                if param['name'] == 'TEL.AG.GUIDESTAR':
                    if param['value'] in ['NONE', 'CATALOGUE']:
                        fc_params['acq']['is_gs'] = False
                    else:
                        fc_params['acq']['is_gs'] = True

                if param['name'] == 'TEL.GS1.ALPHA':
                    gs_ra = param['value']
                if param['name'] == 'TEL.GS1.DELTA':
                    gs_dec = param['value']

                # Blind offset
                if param['name'] == 'TEL.TARG.OFFSETALPHA':
                    fc_params['acq']['bos_ra'] = param['value']
                if param['name'] == 'TEL.TARG.OFFSETDELTA':
                    fc_params['acq']['bos_dec'] = param['value']

                # Position Angle
                if param['name'] == 'TEL.ROT.OFFANGLE':
                    fc_params['acq']['acq_pa'] = param['value']

            # Store the GS and TTS as SkyCoords
            if fc_params['acq']['is_gs']:
                fc_params['acq']['gs'] = SkyCoord(
                    gs_ra,
                    gs_dec,
                    obstime=fcm_m.obsdate,
                    #equinox=ob['target']['equinox'],
                    frame='icrs',
                    unit=(u.hourangle, u.deg))

            # Deal with the blind offset by definition, the "target" in the OB is the
            # Acq. location, to which we "add" the blind offset to go to the real Target
            fc_params['target'] = fcm_t.offset_coord(
                fc_params['target'],
                delta_ra=fc_params['acq']['bos_ra'] * u.arcsec,
                delta_dec=fc_params['acq']['bos_dec'] * u.arcsec)

            if fc_params['acq']['acq_pa'] == 9999:
                fc_params['tags'] += ['parallactic_angle']

                if fcm_m.do_parang:
                    # Ok, I need to compute the parallactic angle !
                    # Use astroplan to do that
                    UT2 = Observer(location=fcm_m.UT2_loc)
                    fc_params['acq']['acq_pa'] = UT2.parallactic_angle(
                        fcm_m.obsdate, fc_params['target']).to(
                            u.deg).value + 180

                    # Note 2: need to add 180deg, so that the orientation of the slit matches the screen on UT2 !

        elif t['type'] in ['science', 'calib']:

            # Ok, I found one more Science template
            fc_params['n_sci'] += 1

            temp_name = 'sci%i' % (fc_params['n_sci'])

            # Store all the relevant parameters in a dictionary
            fc_params[temp_name] = copy.deepcopy(xshooter_sci_params)

            tpl, tplVersion = api.getTemplate(obId, t['templateId'])

            # Set my own instrument mode, so I know what I am looking at later on.
            if 'ifu' in t['templateName']:
                fc_params[temp_name]['ins_mode'] = 'ifu'
            elif 'slt' in t['templateName']:
                fc_params[temp_name]['ins_mode'] = 'slt'
            elif 'img' in t['templateName']:
                fc_params[temp_name]['ins_mode'] = 'img'
            else:
                raise Exception('Ouch! This error is impossible!')

            for param in tpl['parameters']:

                if param['name'] == 'INS.OPTI3.NAME':
                    fc_params[temp_name]['uvb_slt'] = float(
                        param['value'].split('x')[0]) * u.arcsec
                if param['name'] == 'INS.OPTI4.NAME':
                    fc_params[temp_name]['vis_slt'] = float(
                        param['value'].split('x')[0]) * u.arcsec
                if param['name'] == 'INS.OPTI5.NAME':
                    fc_params[temp_name]['nir_slt'] = float(
                        param['value'].split('x')[0]) * u.arcsec
                if param['name'] == 'SEQ.NOD.THROW':
                    fc_params[temp_name][
                        'slt_throw'] = param['value'] * u.arcsec
                if param['name'] == 'SEQ.NOFFSET':
                    fc_params[temp_name]['noff'] = param['value']
                if param['name'] == 'SEQ.OBS.TYPE':
                    # Warning, here, XSHOOTER differs from MUSE !!!
                    fc_params[temp_name]['obstype'] = [
                        i for i in param['value'].split()
                    ]
                if param['name'] == 'SEQ.OFFSET.COORDS':
                    # NOTE: XSHOOTER allows for SLIT too, but I just call this DETECTOR as well
                    if param['value'] == 'SLIT':
                        fc_params[temp_name]['coordtype'] = 'DETECTOR'
                    else:
                        fc_params[temp_name]['coordtype'] = param['value']

                if param['name'] == 'SEQ.RELOFF1':
                    fc_params[temp_name]['off1'] = param['value']
                if param['name'] == 'SEQ.RELOFF2':
                    fc_params[temp_name]['off2'] = param['value']
                if param['name'] == 'SEQ.OFFSET.ZERO':
                    fc_params[temp_name]['return'] = param['value']
                if param['name'] == 'SEQ.FIXOFF.RA':
                    fc_params[temp_name]['fixoff1'] = param['value']
                if param['name'] == 'SEQ.FIXOFF.DEC':
                    fc_params[temp_name]['fixoff2'] = param['value']

            # Deal with the AutoNodOnSlit template
            # Pretend the nodding are regular offsets. Ignore any jittering.
            if fc_params[temp_name]['slt_throw'].value > 0:
                fc_params[temp_name]['noff'] = 2
                fc_params[temp_name]['obstype'] = ['O', 'O']
                fc_params[temp_name]['coordtype'] = 'DETECTOR'
                fc_params[temp_name]['off1'] = [
                    -fc_params[temp_name]['slt_throw'].to(u.arcsec).value / 2.,
                    +fc_params[temp_name]['slt_throw'].to(u.arcsec).value
                ]
                fc_params[temp_name]['off2'] = [0, 0]

            # Now, deal with the FixedSkyOffset templates.
            # Basically "converts them to a normal GenericOffset, and ignore any jitter
            if not (fc_params[temp_name]['fixoff1'] is
                    None) and not (fc_params[temp_name]['fixoff2'] is None):
                fc_params[temp_name]['noff'] = 2
                fc_params[temp_name]['obstype'] = ['O', 'S']
                fc_params[temp_name]['coordtype'] = 'SKY'
                fc_params[temp_name]['off1'] = [
                    0, fc_params[temp_name]['fixoff1']
                ]
                fc_params[temp_name]['off2'] = [
                    0, fc_params[temp_name]['fixoff2']
                ]

    return fc_params
Beispiel #6
0
def OTF(bore_sight, frame, time_step, start_time, step, lat, lon, alt,
        rotation, x_length, y_length, seperation, plot, alt_limit):
    prototype_dish = EarthLocation(lat=lat * u.deg,
                                   lon=lon * u.deg,
                                   height=alt * u.m)
    if frame == "altaz":
        centriod = SkyCoord(bore_sight[0],
                            bore_sight[1],
                            unit='deg',
                            frame="altaz",
                            location=prototype_dish,
                            obstime=Time.now())
        bore_sight = (centriod.icrs.ra.deg, centriod.icrs.dec.deg)
    elif frame == "galactic" or frame == "ircs":
        centriod = SkyCoord(bore_sight[0],
                            bore_sight[1],
                            unit='deg',
                            frame=frame)
        bore_sight = (centriod.icrs.ra.deg, centriod.icrs.dec.deg)

    step_y = int(np.ceil((y_length / seperation)))
    step_x = int(np.ceil((x_length / seperation)))
    start_x = int(step_x / 2 - step_x - 1)
    end_x = int(step_x / 2 + 2)
    start_y = int(step_y / 2 - step_y)
    end_y = int(step_y / 2 + 1)
    pixel_coordinates_x = []
    pixel_coordinates_y = []
    pixel_coordinates_z = []
    reverse = 0
    for j in range(start_y, end_y):
        x = []
        y = []
        z = []

        for i in range(start_x, end_x):
            if i == start_x and j == start_y and j % 2 == 0:
                _append(x, y, z, i - 1, j, 0, rotation)
            else:
                pass
            if i == start_x and j % 2 == 0 and j != start_y:
                #print("I am here")
                _append(x, y, z, i - 1, j - 1, 0, rotation)
                _append(x, y, z, i - 1.8661, j - 0.5, 0, rotation)
                _append(x, y, z, i - 1, j, 0, rotation)
            if i == start_x or i == end_x - 1:
                #print("i am at {} start_x + 1 = {}, start_x = {}".format(i, start_x + 1, start_x))
                _append(x, y, z, i, j, 0, rotation)
            else:
                _append(x, y, z, i, j, 1, rotation)
            if i == end_x - 1 and j % 2 != 0 and reverse != 0:
                #print("I am here")
                _append(x, y, z, i + 1, j, 0, rotation)
                _append(x, y, z, i + 1.8661, j - 0.5, 0, rotation)
                _append(x, y, z, i + 1, j - 1, 0, rotation)

        if j % 2 != 0:
            x.reverse()
            y.reverse()
            z.reverse()
            reverse = 0
            pixel_coordinates_x.append(x)
            pixel_coordinates_y.append(y)
            pixel_coordinates_z.append(z)

        else:
            reverse = 1
            pixel_coordinates_x.append(x)
            pixel_coordinates_y.append(y)
            pixel_coordinates_z.append(z)

    x = unpackTuple(pixel_coordinates_x)
    y = unpackTuple(pixel_coordinates_y)
    z = unpackTuple(pixel_coordinates_z)
    x[:] = [x * seperation for x in x]
    y[:] = [y * seperation for y in y]
    pixel_coordinates = list(zip(x, y))
    if plot == 1:
        plt.figure(figsize=(10, 5))
        plt.subplot(1, 2, 1)
        plt.xlabel("x")
        plt.ylabel("y")
        plt.plot(x, y, "-o")
        plt.scatter(x, y)

    data = convert_pixel_coordinate_to_equatorial(pixel_coordinates,
                                                  bore_sight)

    x_val = [x[0] for x in data]
    y_val = [x[1] for x in data]
    if plot == 1:
        plt.subplot(1, 2, 2)
        plt.scatter(x_val, y_val, zorder=1)
        plt.scatter(bore_sight[0], bore_sight[1], color='r', zorder=2)
        plt.plot(x_val, y_val, "-o", zorder=0)
        plt.xlabel("ra")
        plt.ylabel("dec")

    v_time = [start_time + i * u.s * time_step for i in range(0, len(x_val))]
    sc = SkyCoord(x_val, y_val, unit='deg', frame="icrs")
    prototype_dish_observer = Observer(location=prototype_dish)
    parallactic_angle = prototype_dish_observer.parallactic_angle(time=v_time,
                                                                  target=sc)
    center = SkyCoord(x_val[0], y_val[0], unit='deg', frame="icrs")
    grid_altaz = sc.transform_to(
        AltAz(obstime=start_time, location=prototype_dish))
    source_altaz = center.transform_to(
        AltAz(obstime=start_time, location=prototype_dish))
    if plot == 1:
        plt.figure(figsize=(10, 5))
        plt.subplot(1, 2, 1)
        plt.scatter(grid_altaz.az.deg, grid_altaz.alt.deg, zorder=0)
        plt.plot(grid_altaz.az.deg, grid_altaz.alt.deg, "-o", zorder=1)
        plt.scatter(source_altaz.az.deg,
                    source_altaz.alt.deg,
                    color="g",
                    zorder=2)
        plt.xlabel("Az")
        plt.ylabel("Alt")

        sep = []
        frame = "altaz"
        for i in range(0, len(x_val)):
            c1 = SkyCoord(grid_altaz.az.deg[i] * u.deg,
                          grid_altaz.alt.deg[i] * u.deg,
                          frame=frame)
            if i != len(x_val) - 1:
                c2 = SkyCoord(grid_altaz.az.deg[i + 1] * u.deg,
                              grid_altaz.alt.deg[i + 1] * u.deg,
                              frame=frame)
                sep.append(c1.separation(c2).deg / time_step)

        plt.subplot(1, 2, 2)
        plt.plot(sep[:], color="y")

    alt = grid_altaz.alt.deg[:]
    az = grid_altaz.az.deg[:]
    #print("total scan time {}s".format((v_time[-1]- v_time[0])*86400))
    converted = list(zip(az, alt, v_time, z))
    for i in range(len(converted)):
        if converted[i][1] > alt_limit:
            print("{0} {1:3.8f} {2:3.8f} {3} {4}".format(
                converted[i][2].mjd, converted[i][0], converted[i][1],
                converted[i][3], parallactic_angle.deg[i]),
                  file=sys.stdout,
                  flush=True)
        else:
            break


#print("{0} {1:3.8f} {2:3.8f}".format(time.isot, source_altaz.az.deg, source_altaz.alt.deg), file=sys.stdout, flush=True)
    if plot == 1:
        plt.show()
Beispiel #7
0
def cross_scan(center, width, duration, start_time, time_step, position_angle,
               plot, lat, lon, alt, alt_limit):
    pos_step = time_step * duration
    speed = width / duration
    start_separation = width / 2
    separation = width / pos_step
    #print("Width/2 =",start_separation)
    #print("positional sepration = ", separation)
    #print("positional step = ", pos_step)
    #print("scan speed =", speed)
    #print("scan time =", width/speed)
    position_angle = position_angle * u.deg
    x = []
    y = []
    t = []
    f = []
    c1 = SkyCoord(center[0] * u.deg, center[1] * u.deg, frame='icrs')
    #center = SkyCoord(x_val[0], y_val[0], unit='deg', frame="icrs")
    prototype_dish = EarthLocation(lat=lat * u.deg,
                                   lon=lon * u.deg,
                                   height=alt * u.m)
    prototype_dish_observer = Observer(location=prototype_dish)
    altaz = c1.transform_to(AltAz(obstime=start_time, location=prototype_dish))
    # print(altaz.alt.deg)
    c1 = SkyCoord(altaz.az.deg, altaz.alt.deg, unit='deg', frame='altaz')

    start_pos = c1.directional_offset_by(position_angle, start_separation)
    v_time = [
        start_time + i * u.s * time_step
        for i in range(0,
                       int(pos_step) * 2 + 2)
    ]
    counter = 0
    for i in range(0, int(pos_step) + 1):
        new_pos = start_pos.directional_offset_by(position_angle,
                                                  -i * separation)
        x.append(new_pos.az.deg)
        y.append(new_pos.alt.deg)
        t.append(v_time[i])
        if i != int(pos_step):
            f.append(1)
        else:
            f.append(0)
        counter = i
    position_angle = position_angle + 90 * u.deg
    start_pos = c1.directional_offset_by(position_angle, start_separation)
    for i in range(0, int(pos_step) + 1):
        new_pos = start_pos.directional_offset_by(position_angle,
                                                  -i * separation)
        x.append(new_pos.az.deg)
        y.append(new_pos.alt.deg)
        t.append(v_time[counter] + 10 * u.s)
        if i != 0:
            f.append(1)
        else:
            f.append(0)
        counter += 1

    if plot == 1:
        plt.figure(figsize=(5, 5))
        plt.scatter(x, y)
        plt.scatter(altaz.az.deg, altaz.alt.deg, color="r")
        plt.ylabel('Alt')
        plt.xlabel('Az')
    #prototype_dish_observer.parallactic_angle(time=time[i], target=sc).deg
    #print(len(t), len(x), len(y), len(v_time))

    for i in range(len(v_time)):
        #print(x[i], y[i])
        altaz = SkyCoord(x[i],
                         y[i],
                         unit='deg',
                         frame="altaz",
                         obstime=v_time[i],
                         location=prototype_dish)
        # altaz = AltAz(np.deg2rad(x[i])*u.rad, np.deg2rad(y[i])*u.rad:,
        # obstime=v_time[i], location=prototype_dish)
        sc = altaz.transform_to('icrs')
        print("{0} {1:3.8f} {2:3.8f} {3} {4}".format(
            t[i].mjd, x[i], y[i], f[i],
            prototype_dish_observer.parallactic_angle(time=v_time[i],
                                                      target=sc).deg))
    if plot == 1:
        plt.show()
Beispiel #8
0
class Simulation(object):
    """A class to encapsulate an SDSS-5 simulation
    """
    def __init__(self,
                 plan,
                 observatory,
                 idx=1,
                 schedule="normal",
                 redo_exp=True):
        if (observatory == 'apo'):
            timezone = "US/Mountain"
            fclear = 0.5
            elev = 2788
            self.telescope = {
                "alt": 30,
                "az": 90,
                "par_angle": 0,
                "alt_slew": 1.5,
                "az_slew": 2.0,
                "rot_slew": 2.0
            }
            self.obsCheck = apoCheck
            self.moveTelescope = self.moveSloanTelescope
        if (observatory == 'lco'):
            timezone = "US/Eastern"
            fclear = 0.7
            elev = 2134
            self.telescope = {"ra": 0, "dec": -30}
            self.obsCheck = lcoCheck
            self.moveTelescope = self.moveDuPontTelescope

        self.redo_exp = redo_exp

        self.obsHist = {
            "lst": list(),
            "ra": list(),
            "bright": list(),
            "field_pk": list(),
            "weather": list(),
            "mjd": list()
        }

        self.scheduler = roboscheduler.scheduler.Scheduler(
            observatory=observatory, schedule=schedule)
        self.weather = observesim.weather.Weather(
            mjd_start=self.scheduler.start,
            mjd_end=self.scheduler.end,
            seed=idx,
            fclear=fclear)
        self.observatory = Observer(longitude=self.scheduler.longitude * u.deg,
                                    latitude=self.scheduler.latitude * u.deg,
                                    elevation=elev * u.m,
                                    name=observatory,
                                    timezone=timezone)
        self.scheduler.initdb(designbase=plan)
        self.field_ra = self.scheduler.fields.racen
        self.field_dec = self.scheduler.fields.deccen
        self.field_pk = self.scheduler.fields.pk

        cadencelist = self.scheduler.fields.cadencelist.cadences
        cadences = self.scheduler.fields.cadence

        self.nom_duration = np.float32(15. / 60. / 24.)
        self.cals = np.float32(3. / 60. / 24.)
        self.observe = observesim.observe.Observe(defaultExp=self.nom_duration,
                                                  cadencelist=cadencelist,
                                                  cadences=cadences)
        self.bossReadout = np.float32(70. / 60. / 60. / 24.)

        self.curr_mjd = np.float32(1e9)

        self.coord = SkyCoord(self.field_ra * u.deg, self.field_dec * u.deg)

        self.slews = list()
        self.slew_mjds = list()
        self.slew_alt = list()
        self.slew_az = list()
        self.slew_rot = list()
        self.slew_ra = list()
        self.slew_dec = list()

        self.hit_lims = 0

        self.redo_apg = 0
        self.redo_r = 0
        self.redo_b = 0

    def moveDuPontTelescope(self, mjd, fieldidx):
        next_ra, next_dec = self.field_ra[fieldidx], self.field_dec[fieldidx]

        ra_slew = np.abs(next_ra - self.telescope["ra"])
        dec_slew = np.abs(next_dec - self.telescope["dec"])

        if ra_slew > 180:
            ra_slew = 360 - ra_slew
            assert ra_slew > 0, "forgot circular math? ra"

        dec_time = decTime(dec_slew)
        ra_time = raTime(ra_slew)

        self.telescope["ra"] = self.field_ra[fieldidx]
        self.telescope["dec"] = self.field_dec[fieldidx]

        return max([dec_time, ra_time]), ra_slew, dec_slew

    def moveSloanTelescope(self, mjd, fieldidx):
        altaz = self.observatory.altaz(Time(mjd, format="mjd"),
                                       self.coord[fieldidx])
        alt = altaz.alt.deg
        az = altaz.az.deg
        angle = self.observatory.parallactic_angle(Time(mjd, format="mjd"),
                                                   self.coord[fieldidx]).deg

        alt_slew = np.abs(alt - self.telescope["alt"])
        az_slew = np.abs(az - self.telescope["az"])
        if az_slew > 180:
            az_slew = 360 - az_slew
            assert az_slew > 0, "forgot circular math?  az"
        rot_slew = np.abs(angle - self.telescope["par_angle"])
        if rot_slew > 180:
            rot_slew = 360 - rot_slew
            assert rot_slew > 0, "forgot circular math? rot"

        alt_time = alt_slew / self.telescope["alt_slew"]
        az_time = az_slew / self.telescope["az_slew"]
        rot_time = rot_slew / self.telescope["rot_slew"]

        self.telescope["alt"] = alt
        self.telescope["az"] = az
        self.telescope["par_angle"] = angle

        return max([alt_time, az_time, rot_time]), alt_slew, az_slew, rot_slew

    def siteObs(self, fieldidx, mjd):
        """Check observability issues at site, e.g. zenith at APO
           or enclosure, etc
           for any number of mjds, e.g. for a whole observing window
        """

        try:
            len(mjd)
        except TypeError:
            mjd = np.array([mjd])

        try:
            len(fieldidx)
        except TypeError:
            fieldidx = np.array([fieldidx])

        altaz = self.observatory.altaz(Time(mjd, format="mjd"),
                                       self.coord[fieldidx],
                                       grid_times_targets=True)
        # altaz shape = (fields x mjds)
        alt = altaz.alt.deg.flatten()
        az = altaz.az.deg.flatten()
        res = self.obsCheck(alt, az)
        good = res.reshape((len(fieldidx), len(mjd)))

        # axis 1 is along fields, I guess...
        return np.all(good, axis=1)

    def bright(self, mjd=None):
        if mjd is None:
            mjd = self.curr_mjd
        skybrightness = self.scheduler.skybrightness(mjd)
        return skybrightness > 0.35

    def nextField(self):
        # dark time or brighttime? to guess at how long we need for obs
        if not self.bright():
            airmass_weight = 1.05
        else:
            airmass_weight = 0.05
        # integer division floors; no partial exposures
        maxExp = int((self.nextchange - self.curr_mjd) //
                     (self.nom_duration * 1.3**airmass_weight))
        if maxExp == 0:
            # self.curr_mjd = self.curr_mjd + self.nom_duration
            return -1, 1, True
        field_pk, nexposures = self.scheduler.nextfield(mjd=self.curr_mjd,
                                                        maxExp=maxExp)
        # assert fieldid is not None, f"can't schedule {self.curr_mjd}, {self.bright()}"
        if (field_pk is not None):
            fieldidx = np.where(self.field_pk == field_pk)[0]
            site_check = self.siteObs(fieldidx, [
                self.curr_mjd + n * (self.nom_duration)
                for n in range(nexposures)
            ])
            # maxTime = self.nextchange - self.curr_mjd
            maxTime = maxExp * self.nom_duration

            if not site_check:
                field_idxs, nexps = self.scheduler.nextfield(mjd=self.curr_mjd,
                                                             maxExp=maxExp,
                                                             returnAll=True)

                obs_fields = self.siteObs(field_idxs, [
                    self.curr_mjd + n * (self.nom_duration)
                    for n in range(nexposures)
                ])
                field_idxs = field_idxs[obs_fields]
                nexps = nexps[obs_fields]
                if len(field_idxs) == 0:
                    # print("all fields collide with something :( ")
                    # print(obs_fields)
                    self.hit_lims += 1. / 20
                    return -1, 1. / 20, False

                fieldidx, nexposures = sortFields(field_idxs,
                                                  nexps,
                                                  self.nom_duration,
                                                  maxTime=maxTime)
                if fieldidx == -1:
                    # print("baawaaaaaahhhahahaa :( ")
                    # self.curr_mjd = self.curr_mjd + self.nom_duration/20
                    return -1, 1. / 20, False
            field_pk = int(self.field_pk[fieldidx])

            return field_pk, nexposures, False
        else:
            # if not self.bright():
            #     assert False, f"{self.curr_mjd} ugh"
            return -1, 1, False

    def bookKeeping(self, fieldidx, i=-1):
        """figure out SN and keep track, etc
        """
        alt, az = self.scheduler.radec2altaz(mjd=self.curr_mjd,
                                             ra=self.field_ra[fieldidx],
                                             dec=self.field_dec[fieldidx])
        airmass = 1 / np.cos(np.pi * (90 - alt) / 180.)
        if alt < 20:
            print(i, alt, az, self.curr_mjd, fieldidx, "TOO LOW!!")
            if alt < 0:
                print("booooooooo")
                # assert False, "ugh"

        result = self.observe.result(
            mjd=self.curr_mjd,
            field_pk=self.field_pk[fieldidx],
            airmass=airmass,
            epochidx=self.scheduler.fields.icadence[fieldidx])
        duration = result["duration"]
        if duration < 0 or np.isnan(duration):
            print("HOOOWWWOWOWOWOWW")
            print(i, alt, az, self.curr_mjd, field_pk)

        self.curr_mjd = self.curr_mjd + duration + self.bossReadout

        # move telescope for tracking
        self.moveTelescope(self.curr_mjd, fieldidx)

        self.obsHist["lst"].append(self.scheduler.lst(self.curr_mjd)[0])
        self.obsHist["ra"].append(self.field_ra[fieldidx])
        self.obsHist["bright"].append(self.bright())
        self.obsHist["field_pk"].append(self.field_pk[fieldidx])
        self.obsHist["weather"].append(False)
        self.obsHist["mjd"].append(self.curr_mjd)

        return result

    def observeField(self, field_pk, nexposures):
        fieldidx = int(np.where(self.field_pk == field_pk)[0])

        slewtime, *axes = self.moveTelescope(self.curr_mjd, fieldidx)

        self.slews.append(int(slewtime))
        self.slew_mjds.append(int(self.curr_mjd))

        if len(axes) == 3:
            self.slew_alt.append(float(axes[0]))
            self.slew_az.append(float(axes[1]))
            self.slew_rot.append(float(axes[2]))

            self.slew_ra.append(np.nan)
            self.slew_dec.append(np.nan)
        else:
            self.slew_ra.append(float(axes[0]))
            self.slew_dec.append(float(axes[1]))

            self.slew_alt.append(np.nan)
            self.slew_az.append(np.nan)
            self.slew_rot.append(np.nan)

        # slewtime is in seconds...
        self.curr_mjd = self.curr_mjd + self.cals + np.float32(
            slewtime / 60. / 60. / 24.)

        field_exp_count = nexposures
        for i in range(nexposures):
            # each "exposure" is a design

            if (self.curr_mjd > self.nextchange):
                oops = (self.curr_mjd - self.nextchange) * 24 * 60
                if oops > 5:
                    print("NOOOO! BAD!", oops)
                    # print(i, nexposures, self.telescope)
                continue

            res = self.bookKeeping(fieldidx, i=i)

            if self.bright():
                if res["apgSN2"] < 100 and self.redo_exp:
                    field_exp_count += 1
                    self.redo_apg += 1
                    self.scheduler.update(field_pk=self.field_pk[fieldidx],
                                          result=res,
                                          finish=False)
                    res = self.bookKeeping(fieldidx, i=i)
                    self.scheduler.update(field_pk=self.field_pk[fieldidx],
                                          result=res,
                                          finish=True)
                else:
                    self.scheduler.update(field_pk=self.field_pk[fieldidx],
                                          result=res,
                                          finish=True)
            else:
                if (res["rSN2"] < 0.2 or res["bSN2"] < 0.2) and self.redo_exp:
                    field_exp_count += 1
                    if res["rSN2"] < 0.2:
                        self.redo_r += 1
                    else:
                        self.redo_b += 1
                    self.scheduler.update(field_pk=self.field_pk[fieldidx],
                                          result=res,
                                          finish=False)
                    res = self.bookKeeping(fieldidx, i=i)
                    self.scheduler.update(field_pk=self.field_pk[fieldidx],
                                          result=res,
                                          finish=True)
                else:
                    self.scheduler.update(field_pk=self.field_pk[fieldidx],
                                          result=res,
                                          finish=True)
        if self.bright():
            ap_tot = np.sum(
                self.scheduler.observations.apgSN2[-1 * field_exp_count:])
            # print(f"{nexposures} {field_exp_count} {len(self.scheduler.observations.apgSN2[-1*field_exp_count:])}")
            # print(f"AP SN {ap_tot:7.1f} VS {300 * nexposures}")
            if ap_tot < 650 * nexposures and self.redo_exp:
                self.redo_apg += 1
                self.scheduler.update(field_pk=self.field_pk[fieldidx],
                                      result=res,
                                      finish=False)
                res = self.bookKeeping(fieldidx, i=i)
                self.scheduler.update(field_pk=self.field_pk[fieldidx],
                                      result=res,
                                      finish=True)
        else:
            r_tot = np.sum(self.scheduler.observations.rSN2[-1 *
                                                            field_exp_count:])
            b_tot = np.sum(self.scheduler.observations.bSN2[-1 *
                                                            field_exp_count:])
            # print(f"{nexposures} {field_exp_count} {len(self.scheduler.observations.bSN2[-1*field_exp_count:])}")
            # print(f"B  SN {b_tot:7.1f} VS {2.5 * nexposures} \nR  SN {r_tot:7.1f} VS {5 * nexposures}")
            if (b_tot < 1.4 * nexposures
                    or r_tot < 3.4 * nexposures) and self.redo_exp:
                if r_tot < 3.4 * nexposures:
                    self.redo_r += 1
                else:
                    self.redo_b += 1
                self.scheduler.update(field_pk=self.field_pk[fieldidx],
                                      result=res,
                                      finish=False)
                res = self.bookKeeping(fieldidx, i=i)
                self.scheduler.update(field_pk=self.field_pk[fieldidx],
                                      result=res,
                                      finish=True)

    def observeMJD(self, mjd):
        mjd_evening_twilight = self.scheduler.evening_twilight(mjd)
        mjd_morning_twilight = self.scheduler.morning_twilight(mjd)
        self.curr_mjd = mjd_evening_twilight
        # int_mjd = int(self.curr_mjd)
        if mjd % 100 == 0:
            print("!!!!", mjd)

        # guesses = np.arange(0, 1, 0.05)

        self.nextchange = mjd_morning_twilight

        while (self.curr_mjd < mjd_morning_twilight
               and self.curr_mjd < self.scheduler.end_mjd()):
            # should we do this now?
            isclear, nextchange_weather = self.weather.clear(mjd=self.curr_mjd)
            onoff, nextchange_on = self.scheduler.on(mjd=self.curr_mjd)

            nextchange = np.min(
                np.array(
                    [nextchange_weather, nextchange_on, mjd_morning_twilight]))

            if not isclear:
                # count = 0
                # dur = float(nextchange - self.curr_mjd)
                while self.curr_mjd < nextchange:
                    if nextchange - self.curr_mjd < self.nom_duration:
                        self.curr_mjd = nextchange
                        continue
                    self.obsHist["lst"].append(
                        self.scheduler.lst(self.curr_mjd)[0])
                    self.obsHist["ra"].append(-1)
                    self.obsHist["bright"].append(self.bright())
                    self.obsHist["field_pk"].append(-1)
                    self.obsHist["weather"].append(True)
                    self.obsHist["mjd"].append(self.curr_mjd)
                    self.curr_mjd += self.nom_duration + self.bossReadout + self.cals
                    # count += 1
                # print("WEATHER ", self.curr_mjd, f"night {night_len*24:.1f}, weather {dur*24:.1f}", count)
            elif (onoff != 'on'):
                self.curr_mjd = nextchange

            if self.nextchange - self.curr_mjd < self.nom_duration:
                self.curr_mjd = self.nextchange
                continue

            field_pk, nexposures, noTime = self.nextField()
            if field_pk == -1:
                if noTime:
                    self.curr_mjd = self.curr_mjd + self.nom_duration
                    continue
                # raise Exception()
                # print("skipped ", self.curr_mjd)
                self.obsHist["lst"].append(
                    self.scheduler.lst(self.curr_mjd)[0])
                self.obsHist["ra"].append(np.nan)
                self.obsHist["bright"].append(self.bright())
                self.obsHist["field_pk"].append(-1)
                self.obsHist["weather"].append(False)
                self.obsHist["mjd"].append(self.curr_mjd)
                self.curr_mjd = self.curr_mjd + self.nom_duration
                continue
            self.observeField(field_pk, nexposures)

        # if mjd % 10 == 0:
        #     self.scheduler.priorityLogger.write(name=str(mjd) + "-" + self.observatory.name)

    def lstToArray(self):
        assert len(self.obsHist["weather"]) == len(
            self.obsHist["lst"]), "lst tracking bad!"
        dtype = [('lst', np.float64), ('ra', np.float64), ('bright', np.bool_),
                 ('field_pk', np.int32), ('weather', np.bool_),
                 ('mjd', np.float64)]
        lstOut = np.zeros(len(self.obsHist["lst"]), dtype=dtype)
        lstOut["lst"] = np.array(self.obsHist["lst"])
        lstOut["ra"] = np.array(self.obsHist["ra"])
        lstOut["bright"] = np.array(self.obsHist["bright"])
        lstOut["field_pk"] = np.array(self.obsHist["field_pk"])
        lstOut["weather"] = np.array(self.obsHist["weather"])
        lstOut["mjd"] = np.array(self.obsHist["mjd"])
        return (lstOut)

    def slewsToArray(self):
        dtype = [('time', np.int32), ('mjd', np.int32), ('alt', np.float64),
                 ('az', np.float64), ('rot', np.float64), ('ra', np.float64),
                 ('dec', np.float64)]
        arrayOut = np.zeros(len(self.slews), dtype=dtype)
        arrayOut["time"] = np.array(self.slews)
        arrayOut["mjd"] = np.array(self.slew_mjds)
        arrayOut["alt"] = np.array(self.slew_alt)
        arrayOut["az"] = np.array(self.slew_az)
        arrayOut["rot"] = np.array(self.slew_rot)
        arrayOut["ra"] = np.array(self.slew_ra)
        arrayOut["dec"] = np.array(self.slew_dec)

        return (arrayOut)