def __init__(self): Driver.__init__(self) self.log = logging.getLogger("featureSchedulerDriver") # FIXME: Get parameters for the seeing model! telescope_seeing = 0.25 optical_design_seeing = 0.08 camera_seeing = 0.3 self.seeing_model = SeeingModel( telescope_seeing=telescope_seeing, optical_design_seeing=optical_design_seeing, camera_seeing=camera_seeing) # self.sky_brightness = SkyModelPre() # The sky brightness in self.sky uses opsim fields. We need healpix here self.sky_brightness = self.sky.sky_brightness self.night = 0 self.target_list = {} self.scheduler = None self.sky_nside = 32 self.scheduler_visit_counting_bfs = 0 self.proposal_id_dict = {} self.time_distribution = False self.initialized = False
def _compute_band_fwhms(sampled_fields_band): band = sampled_fields_band["band"][0] assert np.all(band == sampled_fields_band["band"]) logger.debug("Calculating the model seeing in %s", band) seeing_model = SeeingModel() band_idx = ["u", "g", "r", "i", "z", "y"].index(band) sampled_fields_band = sampled_fields_band.copy() sampled_fields_band["fwhm"] = seeing_model( 0.7, sampled_fields_band["field_airmass"].values)["fwhmEff"][band_idx] return sampled_fields_band
def __init__(self, survey_start_time=DEFAULT_START_TIME): """Initialize source of simulated seeing values. Args: - survey_start_time :: an astropy.time.Time object designating the start of the survey. """ self.start_time = survey_start_time self.seeing_data = SeeingData(self.start_time) self.seeing_model = SeeingModel() self.seeing_filter_index = {} for filter_index, filter_name in enumerate( self.seeing_model.filter_list): self.seeing_filter_index[filter_name] = filter_index
def generate_ddf(ddf_name, nyears=10, space=2): previous_ddf = generate_dd_surveys() survey_names = np.array([survey.survey_name for survey in previous_ddf]) survey_indx = np.where(survey_names == ddf_name)[0].max() ddf_ra = previous_ddf[survey_indx].ra * u.rad ddf_dec = previous_ddf[survey_indx].dec * u.rad site = Site('LSST') location = EarthLocation(lat=site.latitude, lon=site.longitude, height=site.height) mjd = np.arange(59853.5, 59853.5 + 365.25 * nyears, 20. / 60 / 24.) times = Time(mjd, format='mjd', location=location) airmass_limit = 2.5 # demand airmass lower than this twilight_limit = -18. # Sun below this altitude in degrees dist_to_moon_limit = 30. # minimum distance to keep from moon degrees zenith_limit = 10. # Need to be this far away from zenith to start (20 min = 5 deg) g_m5_limit = 23.5 # mags season_gap = 20. # days. Count any gap longer than this as it's own season season_length_limit = 80 # Days. Demand at least this many days in a season # How long to keep attempting a DDF expire_dict = {1: 36. / 24., 2: 0.5} sun_coords = get_sun(times) moon_coords = get_moon(times) sched_downtime_data = ScheduledDowntimeData(Time(mjd[0], format='mjd')) observatory_up = np.ones(mjd.size, dtype=bool) for dt in sched_downtime_data(): indx = np.where((mjd >= dt['start'].mjd) & (mjd <= dt['end'].mjd))[0] observatory_up[indx] = False lst = times.sidereal_time('mean') sun_altaz = sun_coords.transform_to(AltAz(location=location)) # generate a night label for each timestep sun_rise = np.where((sun_altaz.alt[0:-1] < 0) & (sun_altaz.alt[1:] > 0))[0] night = np.zeros(mjd.size, dtype=int) night[sun_rise] = 1 night = np.cumsum(night) + 1 # 1-index for night sun_down = np.where(sun_altaz.alt < twilight_limit * u.deg)[0] ddf_coord = SkyCoord(ra=ddf_ra, dec=ddf_dec) ddf_altaz = ddf_coord.transform_to(AltAz(location=location, obstime=times)) ddf_airmass = 1. / np.cos(np.radians(90. - ddf_altaz.az.deg)) zenith = AltAz(alt=90. * u.deg, az=0. * u.deg) ddf_zenth_dist = zenith.separation(ddf_altaz) nside = 32 ddf_indx = raDec2Hpid(nside, ddf_coord.ra.deg, ddf_coord.dec.deg) sm = SkyModelPre() g_sb = mjd * 0 + np.nan indices = np.where((sun_altaz.alt < twilight_limit * u.deg) & (ddf_airmass > airmass_limit))[0] # In theory, one could reach into the sky brightness model and do a much faster interpolation # There might be an airmass limit on the sky brightness. for indx in sun_down: g_sb[indx] = sm.returnMags(mjd[indx], indx=[ddf_indx], filters='g', badval=np.nan)['g'] dist_to_moon = ddf_coord.separation(moon_coords) seeing_model = SeeingModel() ddf_approx_fwhmEff = seeing_model(0.7, ddf_airmass) # I think this should pluck out the g-filter. Really should be labled ddf_approx_fwhmEff = ddf_approx_fwhmEff['fwhmEff'][1].ravel() ddf_m5 = m5_flat_sed('g', g_sb, ddf_approx_fwhmEff, 30., ddf_airmass, nexp=1.) # demand sun down past twilight, ddf is up, and observatory is open, and not too close to the moon good = np.where((ddf_airmass < airmass_limit) & (sun_altaz.alt < twilight_limit * u.deg) & (ddf_airmass > 0) & (observatory_up == True) & (dist_to_moon > dist_to_moon_limit * u.deg) & (ddf_zenth_dist > zenith_limit * u.deg) & (ddf_m5 > g_m5_limit)) potential_nights = np.unique(night[good]) night_gap = potential_nights[1:] - potential_nights[0:-1] big_gap = np.where(night_gap > season_gap)[0] + 1 season = potential_nights * 0 season[big_gap] = 1 season = np.cumsum(season) u_seasons = np.unique(season) season_lengths = [] for se in u_seasons: in_se = np.where(season == se) season_lengths.append( np.max(potential_nights[in_se]) - np.min(potential_nights[in_se])) season_lengths = np.array(season_lengths) good_seasons = u_seasons[np.where(season_lengths > season_length_limit)[0]] gn = np.isin(season, good_seasons) potential_nights = potential_nights[gn] season = season[gn] obs_attempts = [] for sea in np.unique(season): night_indx = np.where(season == sea) obs_attempts.append( place_obs(potential_nights[night_indx], space=space)) obs_attempts = np.concatenate(obs_attempts) mjd_observe = [] m5_approx = [] for indx in np.where(obs_attempts > 0)[0]: in_night_indx = np.where(night == potential_nights[indx])[0] best_depth_indx = np.min( np.where( ddf_m5[in_night_indx] == np.nanmax(ddf_m5[in_night_indx]))[0]) mjd_start = mjd[in_night_indx[best_depth_indx]] m5_approx.append(ddf_m5[in_night_indx[best_depth_indx]]) mjd_end = mjd_start + expire_dict[obs_attempts[indx]] mjd_observe.append((mjd_start, mjd_end)) result = np.zeros(len(mjd_observe), dtype=[('mjd_start', '<f8'), ('mjd_end', '<f8'), ('label', '<U10')]) mjd_observe = np.array(mjd_observe) result['mjd_start'] = mjd_observe[:, 0] result['mjd_end'] = mjd_observe[:, 1] result['label'] = ddf_name return result #, ddf_ra, ddf_dec #, previous_ddf[survey_indx].observations, m5_approx
def __init__(self, nside=None, mjd_start=59853.5, seed=42, quickTest=True, alt_min=5., lax_dome=True, cloud_limit=0.3, sim_ToO=None, seeing_db=None, cloud_db=None, cloud_offset_year=0): """ Parameters ---------- nside : int (None) The healpix nside resolution mjd_start : float (59853.5) The MJD to start the observatory up at alt_min : float (5.) The minimum altitude to compute models at (degrees). lax_dome : bool (True) Passed to observatory model. If true, allows dome creep. cloud_limit : float (0.3) The limit to stop taking observations if the cloud model returns something equal or higher sim_ToO : sim_targetoO object (None) If one would like to inject simulated ToOs into the telemetry stream. seeing_db : filename of the seeing data database (None) If one would like to use an alternate seeing database cloud_offset_year : float, opt Offset into the cloud database by 'offset_year' years. Default 0. cloud_db : filename of the cloud data database (None) If one would like to use an alternate seeing database """ if nside is None: nside = set_default_nside() self.nside = nside self.cloud_limit = cloud_limit self.alt_min = np.radians(alt_min) self.lax_dome = lax_dome self.mjd_start = mjd_start # Conditions object to update and return on request self.conditions = Conditions(nside=self.nside) self.sim_ToO = sim_ToO # Create an astropy location self.site = Site('LSST') self.location = EarthLocation(lat=self.site.latitude, lon=self.site.longitude, height=self.site.height) # Load up all the models we need mjd_start_time = Time(self.mjd_start, format='mjd') # Downtime self.down_nights = [] self.sched_downtime_data = ScheduledDowntimeData(mjd_start_time) self.unsched_downtime_data = UnscheduledDowntimeData(mjd_start_time) sched_downtimes = self.sched_downtime_data() unsched_downtimes = self.unsched_downtime_data() down_starts = [] down_ends = [] for dt in sched_downtimes: down_starts.append(dt['start'].mjd) down_ends.append(dt['end'].mjd) for dt in unsched_downtimes: down_starts.append(dt['start'].mjd) down_ends.append(dt['end'].mjd) self.downtimes = np.array(list(zip(down_starts, down_ends)), dtype=list( zip(['start', 'end'], [float, float]))) self.downtimes.sort(order='start') # Make sure there aren't any overlapping downtimes diff = self.downtimes['start'][1:] - self.downtimes['end'][0:-1] while np.min(diff) < 0: # Should be able to do this wihtout a loop, but this works for i, dt in enumerate(self.downtimes[0:-1]): if self.downtimes['start'][i + 1] < dt['end']: new_end = np.max([dt['end'], self.downtimes['end'][i + 1]]) self.downtimes[i]['end'] = new_end self.downtimes[i + 1]['end'] = new_end good = np.where( self.downtimes['end'] - np.roll(self.downtimes['end'], 1) != 0) self.downtimes = self.downtimes[good] diff = self.downtimes['start'][1:] - self.downtimes['end'][0:-1] self.seeing_data = SeeingData(mjd_start_time, seeing_db=seeing_db) self.seeing_model = SeeingModel() self.seeing_indx_dict = {} for i, filtername in enumerate(self.seeing_model.filter_list): self.seeing_indx_dict[filtername] = i self.cloud_data = CloudData(mjd_start_time, cloud_db=cloud_db, offset_year=cloud_offset_year) sched_logger.info( f"Using {self.cloud_data.cloud_db} as cloud database with start year {self.cloud_data.start_time.iso}" ) self.sky_model = sb.SkyModelPre(speedLoad=quickTest) self.observatory = ExtendedObservatoryModel() self.observatory.configure_from_module() # Make it so it respects my requested rotator angles self.observatory.params.rotator_followsky = True self.filterlist = ['u', 'g', 'r', 'i', 'z', 'y'] self.seeing_FWHMeff = {} for key in self.filterlist: self.seeing_FWHMeff[key] = np.zeros(hp.nside2npix(self.nside), dtype=float) self.almanac = Almanac(mjd_start=mjd_start) # Let's make sure we're at an openable MJD good_mjd = False to_set_mjd = mjd_start while not good_mjd: good_mjd, to_set_mjd = self.check_mjd(to_set_mjd) self.mjd = to_set_mjd self.obsID_counter = 0
import sys if __name__ == "__main__": dds = generate_dd_surveys() mjd0 = 59853.5 delta_t = 15./60./24. # to days survey_length = 10.*365.25 sun_limit = np.radians(-12.) # degrees nominal_seeing = 0.7 # arcsec filtername = 'g' seeing_model = SeeingModel() seeing_indx = 1 #np.where(seeing_model.filter_list == filtername)[0] # result mjds = np.arange(mjd0, mjd0+survey_length, delta_t) # XXX Temp #mjds = mjds[0:100] names = ['mjd', 'sun_alt'] for survey in dds: names.append(survey.survey_name+'_airmass') names.append(survey.survey_name+'_sky_g') names.append(survey.survey_name+'_m5_g')
class FeatureSchedulerDriver(Driver): def __init__(self): Driver.__init__(self) self.log = logging.getLogger("featureSchedulerDriver") # FIXME: Get parameters for the seeing model! telescope_seeing = 0.25 optical_design_seeing = 0.08 camera_seeing = 0.3 self.seeing_model = SeeingModel( telescope_seeing=telescope_seeing, optical_design_seeing=optical_design_seeing, camera_seeing=camera_seeing) # self.sky_brightness = SkyModelPre() # The sky brightness in self.sky uses opsim fields. We need healpix here self.sky_brightness = self.sky.sky_brightness self.night = 0 self.target_list = {} self.scheduler = None self.sky_nside = 32 self.scheduler_visit_counting_bfs = 0 self.proposal_id_dict = {} self.time_distribution = False self.initialized = False # def start_survey(self, timestamp, night): # # self.start_time = timestamp # self.log.info("start_survey t=%.6f" % timestamp) # # self.survey_started = True # # self.sky.update(timestamp) # # (sunset, sunrise) = self.sky.get_night_boundaries(self.params.night_boundary) # self.log.debug("start_survey sunset=%.6f sunrise=%.6f" % (sunset, sunrise)) # if sunset <= timestamp < sunrise: # self.start_night(timestamp, night) # # self.sunset_timestamp = sunset # self.sunrise_timestamp = sunrise def create_area_proposal(self, propid, name, config_dict): '''Override create_area_proposal from superclass. One call to rule them all! :param propid: :param name: :param config_dict: :return: ''' if not self.initialized and name == 'WideFastDeep': self.initialize(config_dict) def create_sequence_proposal(self, propid, name, config_dict): pass def initialize(self, config_dict): # Load configuration from a python module. # TODO: # This can be changed later to load from a given string, I'm planning on getting the name from # lsst.sims.ocs.configuration.survey.general_proposals conf = import_module('lsst.sims.featureScheduler.driver.config') from lsst.ts.scheduler.kernel import Field self.scheduler = conf.scheduler self.sky_nside = conf.nside # self.scheduler_visit_counting_bfs = conf.scheduler_visit_counting_bfs # Now, configure the different proposals inside Driver # Get the proposal list from feature based scheduler survey_fields = {} for survey in self.scheduler.surveys: if 'proposals' in survey.features: for i, pid in enumerate( survey.features['proposals'].id.keys()): # This gets names of all proposals on all surveys, overwrites repeated and stores new ones self.proposal_id_dict[pid] = [ 0, survey.features['proposals'].id[pid] ] # # Now need to get fields for each proposal # for field in survey.fields: # if field['tag'] > 0: # if field['tag'] not in survey_fields.keys(): # # add and skip to next iteration # survey_fields[field['tag']] = np.array([field]) # continue # # if field['field_id'] not in survey_fields[field['tag']]['field_id']: # survey_fields[field['tag']] = np.append(survey_fields[field['tag']], # field) self.log.debug('Proposal_id_dict: %s' % self.proposal_id_dict.keys()) # self.log.debug('survey_fields: %s' % survey_fields.keys()) for pid in self.proposal_id_dict.keys(): self.proposal_id_dict[pid][0] = self.propid_counter self.propid_counter += 1 self.log.debug('%s: %s' % (pid, self.proposal_id_dict[pid])) prop = FeatureBasedProposal(pid, self.proposal_id_dict[pid][1], config_dict, self.sky) prop.configure_constraints(self.params) # create proposal field list # prop.survey_fields = len(survey_fields[pid]) # for field in survey_fields[pid]: # prop.survey_fields_dict[field['field_id']] = Field(fieldid=field['field_id'], # ra_rad=field['RA'], # dec_rad=field['dec'], # gl_rad=field['gl'], # gb_rad=field['gb'], # el_rad=field['el'], # eb_rad=field['eb'], # fov_rad=field['fov_rad']) self.science_proposal_list.append(prop) self.initialized = True def end_survey(self): self.log.info("end_survey") def start_night(self, timestamp, night): self.night = night super(FeatureSchedulerDriver, self).start_night(timestamp, night) for fieldid in self.target_list.keys(): for filtername in self.target_list[fieldid].keys(): self.target_list[fieldid][filtername].groupix = 0 def select_next_target(self): if not self.isnight: return self.nulltarget # Telemetry stream telemetry_stream = self.get_telemetry() self.scheduler.update_conditions(telemetry_stream) winner_target = self.scheduler.request_observation() if winner_target is None: self.log.debug( '[mjd = %.3f]: No target generated by the scheduler...' % telemetry_stream['mjd']) self.scheduler.flush_queue() self.last_winner_target = self.nulltarget.get_copy() return self.last_winner_target self.log.debug('winner target: %s' % winner_target) self.scheduler_winner_target = winner_target hpid = _raDec2Hpid(self.sky_nside, winner_target['RA'][0], winner_target['dec'][0]) propid = winner_target['survey_id'][0] filtername = winner_target['filter'][0] indx = self.proposal_id_dict[propid][0] target = self.generate_target(winner_target[0]) self.target_list[target.fieldid] = {filtername: target} self.science_proposal_list[indx].survey_targets_dict[ target.fieldid] = { filtername: target } target.time = self.time target.ang_rad = self.observatoryModel.radec2altazpa( self.observatoryModel.dateprofile, target.ra_rad, target.dec_rad)[2] self.observatoryModel.current_state.ang_rad = target.ang_rad slewtime, slew_state = self.observatoryModel.get_slew_delay(target) if slewtime > 0.: self.scheduler_winner_target[ 'mjd'] = telemetry_stream['mjd'] + slewtime / 60. / 60. / 24. self.scheduler_winner_target['night'] = self.night self.scheduler_winner_target['slewtime'] = slewtime self.scheduler_winner_target['skybrightness'] = \ self.sky_brightness.returnMags(self.observatoryModel.dateprofile.mjd, indx=[hpid], extrapolate=True)[filtername] self.scheduler_winner_target['FWHMeff'] = telemetry_stream[ 'FWHMeff_%s' % filtername][hpid] self.scheduler_winner_target['FWHM_geometric'] = \ telemetry_stream['FWHM_geometric_%s' % winner_target['filter'][0]][hpid] self.scheduler_winner_target['airmass'] = telemetry_stream[ 'airmass'][hpid] self.scheduler_winner_target['fivesigmadepth'] = m5_flat_sed( filtername, self.scheduler_winner_target['skybrightness'], self.scheduler_winner_target['FWHMeff'], self.scheduler_winner_target['exptime'], self.scheduler_winner_target['airmass']) self.scheduler_winner_target['alt'] = target.alt_rad self.scheduler_winner_target['az'] = target.az_rad self.scheduler_winner_target['rotSkyPos'] = target.ang_rad self.scheduler_winner_target['clouds'] = self.cloud self.scheduler_winner_target['sunAlt'] = telemetry_stream['sunAlt'] self.scheduler_winner_target['moonAlt'] = telemetry_stream[ 'moonAlt'] target.slewtime = slewtime target.airmass = telemetry_stream['airmass'][hpid] target.sky_brightness = self.sky_brightness.returnMags( self.observatoryModel.dateprofile.mjd, indx=[hpid], extrapolate=True)[filtername][0] self.observatoryModel2.set_state(self.observatoryState) self.observatoryState.ang_rad = target.ang_rad self.observatoryModel2.observe(target) target.seeing = self.seeing target.cloud = self.cloud ntime = self.observatoryModel2.current_state.time if ntime < self.sunrise_timestamp: # self.observatoryModel2.update_state(ntime) if self.observatoryModel2.current_state.tracking: target.time = self.time if self.last_winner_target.targetid == target.targetid: self.last_winner_target = self.nulltarget self.targetid -= 1 else: self.last_winner_target = target.get_copy() else: self.log.debug("select_next_target: target rejected %s" % (str(target))) self.log.debug("select_next_target: state rejected %s" % str(self.observatoryModel2.current_state)) self.last_winner_target = self.nulltarget self.targetid -= 1 else: self.last_winner_target = self.nulltarget self.targetid -= 1 else: self.log.debug('Fail state: %i' % slew_state) self.log.debug('Slewtime lower than zero! (slewtime = %f)' % slewtime) self.scheduler.flush_queue() self.targetid -= 1 self.last_winner_target = self.nulltarget self.log.debug(self.last_winner_target) for propid in self.proposal_id_dict.keys(): self.science_proposal_list[self.proposal_id_dict[propid] [0]].winners_list = [] self.science_proposal_list[indx].winners_list = [target.get_copy()] return self.last_winner_target def register_observation(self, observation): if observation.targetid > 0: self.scheduler.add_observation(self.scheduler_winner_target) return super(FeatureSchedulerDriver, self).register_observation(observation) else: return [] def update_time(self, timestamp, night): delta_time = timestamp - self.time self.time = timestamp self.observatoryModel.update_state(self.time) if not self.survey_started: self.start_survey(timestamp, night) if self.isnight: # if round(timestamp) >= round(self.sunrise_timestamp): if timestamp >= self.sunrise_timestamp: self.scheduler.flush_queue() # Clean queue if night is over! self.end_night(timestamp, night) else: # if round(timestamp) >= round(self.sunset_timestamp): if timestamp >= self.sunset_timestamp: self.start_night(timestamp, night) return self.isnight def generate_target(self, fb_observation, tag='generate'): '''Takes an observation array given by the feature based scheduler and generate an appropriate OpSim target. :param fb_observation: numpy.array :return: Target ''' # self.log.debug('%s: %s' % (tag, fb_observation)) self.targetid += 1 filtername = fb_observation['filter'] propid = fb_observation['survey_id'] target = Target() target.targetid = self.targetid target.fieldid = fb_observation['field_id'] target.filter = str(filtername) target.num_exp = fb_observation['nexp'] target.exp_times = [ fb_observation['exptime'] / fb_observation['nexp'] ] * fb_observation['nexp'] target.ra_rad = fb_observation['RA'] target.dec_rad = fb_observation['dec'] target.ang_rad = fb_observation['rotSkyPos'] target.propid = propid target.goal = 100 target.visits = 0 target.progress = 0.0 target.groupid = 1 target.groupix = 0 target.propid_list = [propid] target.need_list = [target.need] target.bonus_list = [target.bonus] target.value_list = [target.value] target.propboost_list = [target.propboost] target.sequenceid_list = [target.sequenceid] target.subsequencename_list = [target.subsequencename] target.groupid_list = [target.groupid] target.groupix_list = [target.groupix] target.is_deep_drilling_list = [target.is_deep_drilling] target.is_dd_firstvisit_list = [target.is_dd_firstvisit] target.remaining_dd_visits_list = [target.remaining_dd_visits] target.dd_exposures_list = [target.dd_exposures] target.dd_filterchanges_list = [target.dd_filterchanges] target.dd_exptime_list = [target.dd_exptime] return target def append_target(self, fb_observation): '''Takes an observation array given by the feature based scheduler and append it to an existing OpSim target. :param fb_observation: numpy.array :return: Target ''' # self.log.debug('append: %s' % fb_observation) # target = self.target_list[fb_observation['field_id']][fb_observation['filter']].get_copy() # self.targetid += 1 # target.targetid = self.targetid propid = fb_observation['survey_id'] # target.propid = propid # # if propid not in target.propid_list: # # target.propid_list = [propid] indx = self.proposal_id_dict[propid][0] # # if target.fieldid in self.science_proposal_list[indx].survey_targets_dict: # self.science_proposal_list[indx].survey_targets_dict[target.fieldid][target.filter] = target # else: # self.science_proposal_list[indx].survey_targets_dict[target.fieldid] = {target.filter: target} # # target.ra_rad = fb_observation['RA'] # target.dec_rad = fb_observation['dec'] # target.groupix = 0 # self.target_list[fb_observation['field_id']][fb_observation['filter']] = target.get_copy() target = self.generate_target(fb_observation, 'append') if target.fieldid in self.science_proposal_list[ indx].survey_targets_dict: self.science_proposal_list[indx].survey_targets_dict[ target.fieldid][target.filter] = target else: self.science_proposal_list[indx].survey_targets_dict[ target.fieldid] = { target.filter: target } return target def get_telemetry(self): telemetry_stream = {} telemetry_stream['mjd'] = copy.copy( self.observatoryModel.dateprofile.mjd) telemetry_stream['night'] = copy.copy(self.night) telemetry_stream['lmst'] = copy.copy( self.observatoryModel.dateprofile.lst_rad * 12. / np.pi) % 24. dp = DateProfile(0, self.observatoryModel.dateprofile.location) mjd, _ = dp(self.sunrise_timestamp) telemetry_stream['next_twilight_start'] = copy.copy(dp.mjd) dp = DateProfile(0, self.observatoryModel.dateprofile.location) mjd, _ = dp(self.sunset_timestamp) telemetry_stream['next_twilight_end'] = copy.copy(dp.mjd) telemetry_stream['last_twilight_end'] = copy.copy(dp.mjd) # Telemetry about where the observatory is pointing and what filter it's using. telemetry_stream['filter'] = copy.copy( self.observatoryModel.current_state.filter) telemetry_stream['mounted_filters'] = copy.copy( self.observatoryModel.current_state.mountedfilters) telemetry_stream['telRA'] = copy.copy( np.degrees(self.observatoryModel.current_state.ra_rad)) telemetry_stream['telDec'] = copy.copy( np.degrees(self.observatoryModel.current_state.dec_rad)) telemetry_stream['telAlt'] = copy.copy( np.degrees(self.observatoryModel.current_state.alt_rad)) telemetry_stream['telAz'] = copy.copy( np.degrees(self.observatoryModel.current_state.az_rad)) telemetry_stream['telRot'] = copy.copy( np.degrees(self.observatoryModel.current_state.rot_rad)) # What is the sky brightness over the sky (healpix map) telemetry_stream['skybrightness'] = copy.copy( self.sky_brightness.returnMags( self.observatoryModel.dateprofile.mjd)) # Find expected slewtimes over the sky. # The slewtimes telemetry is expected to be a healpix map of slewtimes, in the current filter. # There are other features that balance the filter change cost, separately. # (this filter aspect may be something to revisit in the future?) # Note that these slewtimes are -1 where the telescope is not allowed to point. alt, az = stupidFast_RaDec2AltAz( self.scheduler.ra_grid_rad, self.scheduler.dec_grid_rad, self.observatoryModel.location.latitude_rad, self.observatoryModel.location.longitude_rad, self.observatoryModel.dateprofile.mjd, self.observatoryModel.dateprofile.lst_rad) current_filter = self.observatoryModel.current_state.filter telemetry_stream['slewtimes'] = copy.copy( self.observatoryModel.get_approximate_slew_delay( alt, az, current_filter)) # What is the airmass over the sky (healpix map). telemetry_stream['airmass'] = copy.copy( self.sky_brightness.returnAirmass( self.observatoryModel.dateprofile.mjd)) delta_t = (self.time - self.start_time) telemetry_stream['clouds'] = self.cloud fwhm_geometric, fwhm_effective = self.seeing_model.seeing_at_airmass( self.seeing, telemetry_stream['airmass']) for i, filtername in enumerate(['u', 'g', 'r', 'i', 'z', 'y']): telemetry_stream['FWHMeff_%s' % filtername] = copy.copy( fwhm_effective[i]) # arcsec telemetry_stream['FWHM_geometric_%s' % filtername] = copy.copy( fwhm_geometric[i]) sunMoon_info = self.sky_brightness.returnSunMoon( self.observatoryModel.dateprofile.mjd) # Pretty sure these are radians telemetry_stream['sunAlt'] = copy.copy(np.max(sunMoon_info['sunAlt'])) telemetry_stream['moonAlt'] = copy.copy(np.max( sunMoon_info['moonAlt'])) return telemetry_stream