Exemple #1
0
    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()
Exemple #2
0
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}
Exemple #3
0
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
Exemple #4
0
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'
Exemple #6
0
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}
Exemple #7
0
    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
Exemple #9
0
    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))
Exemple #10
0
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
Exemple #11
0
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
Exemple #13
0
 def get_context_data(self, *args, **kwargs):
     context = super().get_context_data(*args, **kwargs)
     context['installed_facilities'] = get_service_classes()
     return context
Exemple #14
0
def facility_choices():
    return [(k, k) for k in get_service_classes().keys()]
Exemple #15
0
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}