def _process_spectrum_from_fits(self, data_product): data_aws = default_storage.open(data_product.data.name, 'rb') flux, header = fits.getdata(data_aws.open(), header=True) for facility_class in get_service_classes(): facility = get_service_class(facility_class)() if facility.is_fits_facility(header): flux_constant = facility.get_flux_constant() date_obs = facility.get_date_obs(header) break else: flux_constant = self.DEFAULT_FLUX_CONSTANT date_obs = datetime.now() dim = len(flux.shape) if dim == 3: flux = flux[0, 0, :] elif flux.shape[0] == 2: flux = flux[0, :] flux = flux * flux_constant header['CUNIT1'] = 'Angstrom' wcs = WCS(header=header, naxis=1) spectrum = Spectrum1D(flux=flux, wcs=wcs) return spectrum, Time(date_obs).to_datetime()
def facility_status(): """ Collect the facility status from the registered facilities and pass them to the facility_status.html partial template. See lco.py Facility implementation for example. :return: """ facility_statuses = [] for _, facility_class in get_service_classes().items(): facility = facility_class() weather_urls = facility.get_facility_weather_urls() status = facility.get_facility_status() # add the weather_url to the site dictionary for site in status.get('sites', []): url = next((site_url['weather_url'] for site_url in weather_urls.get('sites', []) if site_url['code'] == site['code']), None) if url is not None: site['weather_url'] = url facility_statuses.append(status) return {'facilities': facility_statuses}
def get_visibility(target, start_time, end_time, interval, airmass_limit=10): """ Calculates the airmass for a target for each given interval between the start and end times. The resulting data omits any airmass above the provided limit (or default, if one is not provided), as well as any airmass calculated during the day. :param start_time: start of the window for which to calculate the airmass :type start_time: datetime :param end_time: end of the window for which to calculate the airmass :type end_time: datetime :param interval: time interval, in minutes, at which to calculate airmass within the given window :type interval: int :param airmass_limit: maximum acceptable airmass for the resulting calculations :type airmass_limit: int :returns: A dictionary containing the airmass data for each site. The dict keys consist of the site name prepended with the observing facility. The values are the airmass data, structured as an array containing two arrays. The first array contains the set of datetimes used in the airmass calculations. The second array contains the corresponding set of airmasses calculated. :rtype: dict """ if not airmass_limit: airmass_limit = 10 visibility = {} body = get_pyephem_instance_for_type(target) sun = ephem.Sun() for observing_facility in facility.get_service_classes(): observing_facility_class = facility.get_service_class(observing_facility) sites = observing_facility_class().get_observing_sites() for site, site_details in sites.items(): positions = [[], []] observer = observer_for_site(site_details) rise_sets = get_rise_set(observer, sun, start_time, end_time) curr_interval = start_time while curr_interval <= end_time: time = curr_interval last_rise_set = get_last_rise_set_pair(rise_sets, time) sunup = time > last_rise_set[0] and time < last_rise_set[1] if last_rise_set else False observer.date = curr_interval body.compute(observer) alt = Angle(str(body.alt), unit=units.degree) az = Angle(str(body.az), unit=units.degree) altaz = AltAz(alt=alt.to_string(unit=units.rad), az=az.to_string(unit=units.rad)) airmass = altaz.secz positions[0].append(curr_interval) positions[1].append( airmass.value if (airmass.value > 1 and airmass.value <= airmass_limit) and not sunup else None ) curr_interval += timedelta(minutes=interval) visibility['({0}) {1}'.format(observing_facility, site)] = positions return visibility
class ObservationTemplateFilter(FilterSet): """ Defines the available fields for filtering the list of ``ObservationTemplate`` objects. """ facility = ChoiceFilter(choices=[(k, k) for k in get_service_classes().keys()]) name = CharFilter(lookup_expr='icontains') class Meta: model = ObservationTemplate fields = ['name', 'facility']
def handle(self, *args, **options): facility_classes = {} for facility_name in facility.get_service_classes(): facility_classes[facility_name] = facility.get_service_class( facility_name)() observation_records = ObservationRecord.objects.all() for record in observation_records: if record.status not in facility_classes[ record.facility].get_terminal_observing_states(): facility_classes[record.facility].update_observation_status( record.observation_id) facility_classes[record.facility].save_data_products(record) return 'completed command'
def facility_map(): facility_locations = [] for facility_class in get_service_classes().values(): facility = facility_class() sites = facility.get_observing_sites() # Flatten each facility site dictionary and add text label for use in facility map # Resulting list is of the format [['LCO', 'Siding Spring', 'coj', -31.272, 149.07, 1116], ...] facility_locations.extend([ [facility.name, site_name] + [value for value in site_data.values()] for site_name, site_data in sites.items() ]) data = [ dict( lat=[site[3] for site in facility_locations], lon=[site[4] for site in facility_locations], text=[f'{site[0]}: {site[1]}' for site in facility_locations], hoverinfo='text', mode='markers', type='scattergeo' ) ] layout = { 'title': 'Facility Sites', 'hovermode': 'closest', 'showlegend': False, 'geo': { 'projection': { 'type': 'mollweide', }, 'showcoastlines': False, 'showland': True, 'lonaxis': { 'showgrid': True, 'range': [0, 360], }, 'lataxis': { 'showgrid': True, 'range': [-90, 90], }, } } figure = offline.plot(go.Figure(data=data, layout=layout), output_type='div', show_link=False) return {'figure': figure}
def _process_spectrum_from_fits(self, data_product): """ Processes the data from a spectrum from a fits file into a Spectrum1D object, which can then be serialized and stored as a ReducedDatum for further processing or display. File is read using specutils as specified in the below documentation. # https://specutils.readthedocs.io/en/doc-testing/specutils/read_fits.html :param data_product: Spectroscopic DataProduct which will be processed into a Spectrum1D :type data_product: tom_dataproducts.models.DataProduct :returns: Spectrum1D object containing the data from the DataProduct :rtype: specutils.Spectrum1D :returns: Datetime of observation, if it is in the header and the file is from a supported facility, current datetime otherwise :rtype: AstroPy.Time """ flux, header = fits.getdata(data_product.data.path, header=True) for facility_class in get_service_classes(): facility = get_service_class(facility_class)() if facility.is_fits_facility(header): flux_constant = facility.get_flux_constant() date_obs = facility.get_date_obs_from_fits_header(header) break else: flux_constant = self.DEFAULT_FLUX_CONSTANT date_obs = datetime.now() dim = len(flux.shape) if dim == 3: flux = flux[0, 0, :] elif flux.shape[0] == 2: flux = flux[0, :] flux = flux * flux_constant header['CUNIT1'] = 'Angstrom' wcs = WCS(header=header, naxis=1) spectrum = Spectrum1D(flux=flux, wcs=wcs) return spectrum, Time(date_obs).to_datetime()
def get_24hr_airmass(target, start_time, interval, airmass_limit): end_time = start_time + datetime.timedelta(days=1) visibility = {} sun = ephem.Sun() body = utils.get_pyephem_instance_for_type(target) for observing_facility in facility.get_service_classes(): if observing_facility != 'LCO': continue observing_facility_class = facility.get_service_class( observing_facility) sites = observing_facility_class().get_observing_sites() for site, site_details in sites.items(): positions = [[], []] observer = utils.observer_for_site(site_details) sun_up_times = get_up_times(observer, sun, start_time, end_time, interval) obj_up_times = get_up_times(observer, body, start_time, end_time, interval) good_times = sorted(list(obj_up_times - sun_up_times)) for time in good_times: observer.date = time body.compute(observer) alt = Angle(str(body.alt), unit=u.degree) az = Angle(str(body.az), unit=u.degree) altaz = AltAz(alt=alt.to_string(unit=u.rad), az=az.to_string(unit=u.rad)) airmass = altaz.secz positions[0].append(time) positions[1].append(airmass.value if ( airmass.value > 1 and airmass.value <= airmass_limit ) else None) visibility['({0}) {1}'.format(observing_facility, site)] = positions return visibility
def handle(self, *args, **options): target = None if options['target_id']: try: target = Target.objects.get(pk=options['target_id']) except ObjectDoesNotExist: raise Exception('Invalid target id provided') failed_records = {} for facility_name in facility.get_service_classes(): clazz = facility.get_service_class(facility_name) failed_records[facility_name] = clazz().update_all_observation_statuses(target=target) success = True for facility_name, errors in failed_records.items(): if len(errors) > 0: success = False break if success: return 'Update completed successfully' else: return 'Update completed with errors: {0}'.format(str(failed_records))
class DataProductUploadForm(forms.Form): observation_record = forms.ModelChoiceField( ObservationRecord.objects.all(), widget=forms.HiddenInput(), required=False) target = forms.ModelChoiceField(Target.objects.all(), widget=forms.HiddenInput(), required=False) files = forms.FileField(widget=forms.ClearableFileInput( attrs={'multiple': True})) tag = forms.ChoiceField(choices=DataProduct.DATA_PRODUCT_TYPES) facility = forms.ChoiceField( choices=[('', '----')] + [(k, k) for k in get_service_classes().keys()] + [('No processing', 'No processing')], required=False, help_text= 'Facility algorithm used to process the data - spectroscopy only') observation_timestamp = forms.SplitDateTimeField( label='Observation Time', widget=forms.SplitDateTimeWidget(date_attrs={ 'placeholder': 'Observation Date', 'type': 'date' }, time_attrs={ 'format': '%H:%M:%S', 'placeholder': 'Observation Time', 'type': 'time', 'step': '1' }), required=False, help_text= 'Timestamp of the observation during which data was collected - spectroscopy only' ) referrer = forms.CharField(widget=forms.HiddenInput()) def __init__(self, *args, **kwargs): hide_target_fields = kwargs.pop('hide_target_fields', False) super(DataProductUploadForm, self).__init__(*args, **kwargs) if hide_target_fields: self.fields['facility'].widget = forms.HiddenInput() def clean(self): cleaned_data = super().clean() # For dataproducts uploaded to target detail pages, facility and observation timestamp are only valid for # spectroscopy. Bulk photometry uploads already have timestamp information per datum, and facility # information can vary by datum. # For dataproducts uploaded to observation pages, facility is taken from the observing record. Timestamp is # simply ignored for photometry submissions--however, this should be improved upon in the future. if cleaned_data.get('tag', '') == PHOTOMETRY[0]: if cleaned_data.get('observation_timestamp'): if not cleaned_data.get('observation_record'): raise forms.ValidationError( 'Observation timestamp is not valid for uploaded photometry' ) if cleaned_data.get('facility'): if not cleaned_data.get('observation_record'): raise forms.ValidationError( 'Facility is not valid for uploaded photometry.') elif cleaned_data.get('tag', '') == SPECTROSCOPY[0]: if not cleaned_data.get('observation_timestamp'): raise forms.ValidationError( 'Observation timestamp is required for spectroscopy.') if not cleaned_data.get('facility'): if not cleaned_data.get('observation_record'): raise forms.ValidationError( 'Facility is required for spectroscopy.') else: cleaned_data['facility'] = cleaned_data.get( 'observation_record').facility return cleaned_data
def get_sidereal_visibility(target, start_time, end_time, interval, airmass_limit): """ Uses astroplan to calculate the airmass for a sidereal target for each given interval between the start and end times. The resulting data omits any airmass above the provided limit (or default, if one is not provided), as well as any airmass calculated during the day (defined as between astronomical twilights). Important note: only works for sidereal targets! For non-sidereal visibility, see here: https://github.com/TOMToolkit/tom_nonsidereal_airmass :param start_time: start of the window for which to calculate the airmass :type start_time: datetime :param end_time: end of the window for which to calculate the airmass :type end_time: datetime :param interval: time interval, in minutes, at which to calculate airmass within the given window :type interval: int :param airmass_limit: maximum acceptable airmass for the resulting calculations :type airmass_limit: int :returns: A dictionary containing the airmass data for each site. The dict keys consist of the site name prepended with the observing facility. The values are the airmass data, structured as an array containing two arrays. The first array contains the set of datetimes used in the airmass calculations. The second array contains the corresponding set of airmasses calculated. :rtype: dict """ if target.type != 'SIDEREAL': msg = '\033[1m\033[91mAirmass plotting is only supported for sidereal targets\033[0m' logger.info(msg) empty_visibility = {} return empty_visibility if end_time < start_time: raise Exception('Start must be before end') if airmass_limit is None: airmass_limit = 10 body = FixedTarget(name=target.name, coord=SkyCoord(target.ra, target.dec, unit='deg')) visibility = {} sun, time_range = get_astroplan_sun_and_time(start_time, end_time, interval) for observing_facility in facility.get_service_classes(): observing_facility_class = facility.get_service_class( observing_facility) sites = observing_facility_class().get_observing_sites() for site, site_details in sites.items(): observer = Observer( longitude=site_details.get('longitude') * units.deg, latitude=site_details.get('latitude') * units.deg, elevation=site_details.get('elevation') * units.m) sun_alt = observer.altaz(time_range, sun).alt obj_airmass = observer.altaz(time_range, body).secz bad_indices = np.argwhere( (obj_airmass >= airmass_limit) | (obj_airmass <= 1) | (sun_alt > -18 * units.deg) # between astronomical twilights, i.e. sun is up ) obj_airmass = [ None if i in bad_indices else float(airmass) for i, airmass in enumerate(obj_airmass) ] visibility[f'({observing_facility}) {site}'] = ( time_range.datetime, obj_airmass) return visibility
def get_24hr_airmass(target, interval, airmass_limit): plot_data = [] start = Time(datetime.datetime.utcnow()) end = Time(start.datetime + datetime.timedelta(days=1)) time_range = time_grid_from_range( time_range = [start, end], time_resolution = interval*u.minute) time_plot = time_range.datetime fixed_target = FixedTarget(name = target.name, coord = SkyCoord( target.ra, target.dec, unit = 'deg' ) ) #Hack to speed calculation up by factor of ~3 sun_coords = get_sun(time_range[int(len(time_range)/2)]) fixed_sun = FixedTarget(name = 'sun', coord = SkyCoord( sun_coords.ra, sun_coords.dec, unit = 'deg' ) ) #Colors to match SNEx1 colors = { 'Siding Spring': '#3366cc', 'Sutherland': '#dc3912', 'Teide': '#8c6239', 'Cerro Tololo': '#ff9900', 'McDonald': '#109618', 'Haleakala': '#990099' } for observing_facility in facility.get_service_classes(): observing_facility_class = facility.get_service_class(observing_facility) sites = observing_facility_class().get_observing_sites() for site, site_details in sites.items(): observer = Observer( longitude = site_details.get('longitude')*u.deg, latitude = site_details.get('latitude')*u.deg, elevation = site_details.get('elevation')*u.m ) sun_alt = observer.altaz(time_range, fixed_sun).alt obj_airmass = observer.altaz(time_range, fixed_target).secz bad_indices = np.argwhere( (obj_airmass >= airmass_limit) | (obj_airmass <= 1) | (sun_alt > -18*u.deg) #between astro twilights ) obj_airmass = [np.nan if i in bad_indices else float(x) for i, x in enumerate(obj_airmass)] label = '({facility}) {site}'.format( facility = observing_facility, site = site ) plot_data.append( go.Scatter(x=time_plot, y=obj_airmass, mode='lines', name=label, marker=dict(color=colors.get(site))) ) return plot_data
def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) context['installed_facilities'] = get_service_classes() return context
def facility_choices(): return [(k, k) for k in get_service_classes().keys()]
def get_24hr_airmass(target, interval, airmass_limit): plot_data = [] start = Time(datetime.datetime.utcnow()) end = Time(start.datetime + datetime.timedelta(days=1)) time_range = time_grid_from_range(time_range=[start, end], time_resolution=interval * u.minute) time_plot = time_range.datetime fixed_target = FixedTarget(name=target.name, coord=SkyCoord(target.ra, target.dec, unit='deg')) #Hack to speed calculation up by factor of ~3 sun_coords = get_sun(time_range[int(len(time_range) / 2)]) fixed_sun = FixedTarget(name='sun', coord=SkyCoord(sun_coords.ra, sun_coords.dec, unit='deg')) for observing_facility in facility.get_service_classes(): if observing_facility != 'LCO': continue observing_facility_class = facility.get_service_class( observing_facility) sites = observing_facility_class().get_observing_sites() for site, site_details in sites.items(): observer = Observer(longitude=site_details.get('longitude') * u.deg, latitude=site_details.get('latitude') * u.deg, elevation=site_details.get('elevation') * u.m) sun_alt = observer.altaz(time_range, fixed_sun).alt obj_airmass = observer.altaz(time_range, fixed_target).secz bad_indices = np.argwhere( (obj_airmass >= airmass_limit) | (obj_airmass <= 1) | (sun_alt > -18 * u.deg) #between astro twilights ) obj_airmass = [ np.nan if i in bad_indices else float(x) for i, x in enumerate(obj_airmass) ] label = '({facility}) {site}'.format(facility=observing_facility, site=site) plot_data.append( go.Scatter( x=time_plot, y=obj_airmass, mode='lines', name=label, )) return plot_data
def observing_buttons(target): """ Displays the observation buttons for all facilities available in the TOM. """ facilities = get_service_classes() return {'target': target, 'facilities': facilities}
def observing_buttons(target): facilities = get_service_classes() return {'target': target, 'facilities': facilities}