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()
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))
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'], '.')
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
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
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()
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()
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)