def test_configure(self): temp_model = ObservatoryModel(self.location) temp_model.configure_from_module() self.assertEqual(temp_model.location.longitude_rad, math.radians(-70.7494)) self.assertEqual(temp_model.location.longitude, -70.7494) self.assertEqual(temp_model.current_state.telalt_rad, math.radians(86.5))
class ObservatoryStateMock: def __init__(self): self.ptg = salobj.Controller("MTPtg") self.location = ObservatoryLocation() self.location.for_lsst() self.model = ObservatoryModel(self.location) self.model.configure_from_module() self.telemetry_sleep_time = 0.02 self.run_current_target_status_loop = True self.current_target_status_task = None self._started = False self.start_task = asyncio.create_task(self.start()) async def current_target_status(self): while self.run_current_target_status_loop: time = utils.current_tai() self.model.update_state(time) await self.ptg.tel_currentTargetStatus.set_write( timestamp=time, demandAz=self.model.current_state.telaz, demandEl=self.model.current_state.telalt, demandRot=self.model.current_state.telrot, demandRa=self.model.current_state.ra, demandDec=self.model.current_state.dec, parAngle=self.model.current_state.pa, ) await asyncio.sleep(self.telemetry_sleep_time) async def start(self): if not self._started: self._started = True await self.ptg.start_task self.run_current_target_status_loop = True self.current_target_status_task = asyncio.create_task( self.current_target_status()) async def close(self): self.run_current_target_status_loop = False try: await asyncio.wait_for(self.current_target_status_task, timeout=self.telemetry_sleep_time * 5) finally: await self.ptg.close() async def __aenter__(self): await self.start_task return self async def __aexit__(self, type, value, traceback): await self.close()
class SlewTimeSource(object): """Callable object to calculate slew times.""" pre_position = ObservatoryPosition() post_position = ObservatoryPosition() def __init__(self, survey_start_time=DEFAULT_START_TIME): """Initialize slew time calculator. """ self.start_time = survey_start_time self.observatory = ObservatoryModel() self.observatory.configure_from_module() self.observatory.params.rotator_followsky = True def __call__(self, seconds_into_survey, pre_alt, pre_az, pre_band, post_alt, post_az, post_band): """Calculate the slew time. Args: - seconds_into_survey :: the time into the survey (in seconds) - pre_alt :: the alt of the starting pointing, in degrees - pre_az :: the az of the starting pointing, in degrees - pre_band :: the filter at the starting pointing - post_alt :: the alt of the ending pointing, in degrees - post_az :: the az of the ending pointing, in degrees - post_band :: the filter at the starting pointing """ current_time = self.start_time + TimeDelta(seconds_into_survey, format='sec') self.pre_position.time = current_time self.pre_position.tracking = True self.pre_position.alt_rad = np.radians(pre_alt) self.pre_position.az_rad = np.radians(pre_az) self.pre_position.rot_rad = self.observatory.current_state.rot_rad self.pre_position.filter = pre_band pre_state = self.observatory.get_closest_state(self.pre_position) self.post_position.time = current_time self.post_position.tracking = True self.post_position.alt_rad = np.radians(post_alt) self.post_position.az_rad = np.radians(post_az) self.post_position.rot_rad = self.observatory.current_state.rot_rad self.post_position.filter = post_band post_state = self.observatory.get_closest_state(self.post_position) slew_time = self.observatory.get_slew_delay_for_state( pre_state, post_state, False) return slew_time
class TestFeatureSchedulerTarget(unittest.TestCase): def setUp(self) -> None: self.observatory_model = ObservatoryModel() self.observatory_model.configure_from_module() start_time = Time(59853.983, format="mjd", scale="tai") self.observatory_model.update_state(start_time.unix) return super().setUp() def test_constructor(self): observation = self.make_fbs_observation(note="std") target = FeatureSchedulerTarget( observing_script_name="observing_script", observing_script_is_standard=True, observation=observation, ) slew_time, error = self.observatory_model.get_slew_delay(target) self.assertEqual(error, 0) self.assertGreater(slew_time, 0.0) def test_get_script_config(self): observation = self.make_fbs_observation(note="std") target = FeatureSchedulerTarget( observing_script_name="observing_script", observing_script_is_standard=True, observation=observation, ) script_config_expected = { "targetid": target.targetid, "band_filter": target.filter, "ra": Angle(float(observation["RA"][0]), unit=units.rad).to_string(unit=units.hourangle, sep=":"), "dec": Angle(float(observation["dec"][0]), unit=units.rad).to_string(unit=units.degree, sep=":"), "name": observation["note"][0], "program": observation["note"][0].rsplit("_", maxsplit=1)[0], "rot_sky": target.ang, "obs_time": target.obs_time, "num_exp": target.num_exp, "exp_times": target.exp_times, "estimated_slew_time": target.slewtime, } script_config_yaml = target.get_script_config() script_config_unpacked = yaml.safe_load(script_config_yaml) self.assertEqual(script_config_expected, script_config_unpacked) def test_get_script_config_cwfs(self): observation = self.make_fbs_observation(note="cwfs") target = FeatureSchedulerTarget( observing_script_name="observing_script", observing_script_is_standard=True, observation=observation, ) script_config_expected = dict(find_target=dict( az=math.degrees(float(observation["az"][0])), el=math.degrees(float(observation["alt"][0])), )) script_config_yaml = target.get_script_config() script_config_unpacked = yaml.safe_load(script_config_yaml) self.assertEqual(script_config_expected, script_config_unpacked) def test_get_script_config_cwfs_with_additional_config(self): observation = self.make_fbs_observation(note="cwfs") target = FeatureSchedulerTarget( observing_script_name="observing_script", observing_script_is_standard=True, observation=observation, script_configuration_cwfs=dict(filter="SDSSg", grating="empty_1"), ) script_config_expected = dict( find_target=dict( az=math.degrees(float(observation["az"][0])), el=math.degrees(float(observation["alt"][0])), ), filter="SDSSg", grating="empty_1", ) script_config_yaml = target.get_script_config() script_config_unpacked = yaml.safe_load(script_config_yaml) self.assertEqual(script_config_expected, script_config_unpacked) def test_get_script_config_spec(self): observation = self.make_fbs_observation(note="spec:HD12345") target = FeatureSchedulerTarget( observing_script_name="observing_script", observing_script_is_standard=True, observation=observation, ) script_config_expected = { "object_name": observation["note"][0], "object_dec": Angle(float(observation["dec"][0]), unit=units.rad).to_string(unit=units.degree, sep=":"), "object_ra": Angle(float(observation["RA"][0]), unit=units.rad).to_string(unit=units.hourangle, sep=":"), } script_config_yaml = target.get_script_config() script_config_unpacked = yaml.safe_load(script_config_yaml) self.assertEqual(script_config_expected, script_config_unpacked) def test_get_script_config_spec_with_additional_config(self): observation = self.make_fbs_observation(note="spec:HD12345") target = FeatureSchedulerTarget( observing_script_name="observing_script", observing_script_is_standard=True, observation=observation, script_configuration_spec=dict( filter_sequence=["SDSSg", "SDSSg"], grating_sequence=["empty_1", "empty_1"], ), ) script_config_expected = { "object_name": observation["note"][0], "object_dec": Angle(float(observation["dec"][0]), unit=units.rad).to_string(unit=units.degree, sep=":"), "object_ra": Angle(float(observation["RA"][0]), unit=units.rad).to_string(unit=units.hourangle, sep=":"), "filter_sequence": ["SDSSg", "SDSSg"], "grating_sequence": ["empty_1", "empty_1"], } script_config_yaml = target.get_script_config() script_config_unpacked = yaml.safe_load(script_config_yaml) self.assertEqual(script_config_expected, script_config_unpacked) def test_get_script_config_multiple_observations(self): filter_obs = "gri" observations = self.make_fbs_observation("std", filter_obs=filter_obs) target = FeatureSchedulerTarget( observing_script_name="observing_script", observing_script_is_standard=True, observation=observations, ) slew_time, error = self.observatory_model.get_slew_delay(target) script_config = yaml.safe_load(target.get_script_config()) self.assertEqual(len(script_config["exp_times"]), len(filter_obs) * 2) for filter_name in filter_obs: self.assertIn(filter_name, script_config["band_filter"]) self.assertEqual(error, 0) self.assertGreater(slew_time, 0.0) def make_fbs_observation(self, note, filter_obs="r"): observations = np.concatenate( [empty_observation() for _ in range(len(filter_obs))]) ra, dec, _ = self.observatory_model.altaz2radecpa( self.observatory_model.dateprofile, 65.0, 180.0) for obs_filter, observation in zip(filter_obs, observations): observation["RA"] = ra observation["dec"] = dec observation["mjd"] = self.observatory_model.dateprofile.mjd observation["filter"] = obs_filter observation["exptime"] = 30.0 observation["nexp"] = 2 observation["note"] = note return observations
def test_transitioner(self): observatory = ObservatoryModel() observatory.configure_from_module() pachon = astroplan.Observer.at_site("Cerro Pachon") start_time = astropy.time.Time(59598.66945625747, format='mjd') readout_time = observatory.params.readouttime * u.second exptime = 15 * u.second nexp = 2 targets = [ astroplan.FixedTarget(coord=SkyCoord(ra=81.1339111328125 * u.deg, dec=-17.532396316528303 * u.deg), name="1857"), astroplan.FixedTarget(coord=SkyCoord(ra=81.1758499145508 * u.deg, dec=-12.2103328704834 * u.deg), name="2100") ] constraints = [ astroplan.AltitudeConstraint(30 * u.deg, 89 * u.deg), astroplan.AirmassConstraint(2.2), astroplan.AtNightConstraint.twilight_astronomical() ] obs_blocks = [ astroplan.ObservingBlock.from_exposures( target, 1, exptime, nexp, readout_time, configuration={'filter': band}, constraints=constraints) for band in ('g', 'i') for target in targets ] self.assertGreater(observatory.params.readouttime, 1.5) self.assertGreater(observatory.params.filter_changetime, 15.0) slew_src = SlewTimeSource() transitioner = apsupp.OpsimTransitioner(slew_src) whitepaper_transitioner = apsupp.LSSTTransitioner() for old_block, new_block in zip(obs_blocks[:-1], obs_blocks[1:]): block = transitioner(old_block, new_block, start_time, pachon) wp_block = whitepaper_transitioner(old_block, new_block, start_time, pachon) duration_seconds = (block.duration / u.second).value wp_duration_seconds = (wp_block.duration / u.second).value self.assertAlmostEqual(duration_seconds, wp_duration_seconds, delta=5) old_filter = old_block.configuration['filter'] new_filter = new_block.configuration['filter'] if old_filter == new_filter: min_expected_duration = observatory.params.readouttime else: min_expected_duration = max( observatory.params.readouttime, observatory.params.filter_changetime) self.assertGreaterEqual(duration_seconds, min_expected_duration) self.assertLess(duration_seconds, 600)