def get_moon_phase(self):
        """Get the current moon phase and waxing/waning information."""
        # These numbers are just guesses.
        phases = {
            "new": (0, 0.005),
            "crescent": (0.005, 0.47),
            "quarter": (0.47, 0.53),
            "gibbous": (0.53, 0.9925),
            "full": (0.9925, 1),
        }
        now_dt = datetime.datetime.now()

        illumination = moon.moon_illumination(Time(now_dt))

        for phase, (lower, upper) in phases.items():
            if lower < illumination <= upper:
                current_phase = phase
                break

        yesterday = Time(now_dt - datetime.timedelta(hours=1))
        trend = (
            "waning" if moon.moon_illumination(yesterday) > illumination else "waxing"
        )

        return (trend, current_phase, illumination)
Beispiel #2
0
def separateap():
    for ctime in times:
        t = start + ctime
        frame = AltAz(obstime=t, location=berlin)
        sunaltaz = get_sun(t).transform_to(frame)
        if sunaltaz.alt < astronight:
            #sunaltazs.append(sunaltaz.alt)
            #print(sunaltaz)
            #print(sunaltaz.alt)
            #print('Moon phase angle:', moon_phase_angle(t, berlin))
            lum = moon_illumination(t, berlin)
            #print('Moon illumination:', moon_illumination(t, berlin))

            if lum < maxillum:
                delta.append(ctime)
                illum.append(lum)
                moon = get_moon(t, berlin)
                moonaltaz = moon.transform_to(frame)
                #print("Moon's Altitude = {0.alt:.2}".format(moonaltaz))
                moonaltazs.append(moonaltaz.alt)
                # print(moonaltaz.alt)
                if mod == 'graph':
                    pass
                elif mod == 'text' or mod == 'both':
                    if moonaltaz.alt > Latitude(minalt * u.deg):

                        dtxt.write('Date and time: ' + str(t) + '\n' +
                                   'Altitude: ' + str(moonaltaz.alt) + '\n' +
                                   'Illumination: ' + str(lum) + '\n \n')

    if mod == 'text':
        pass
    elif mod == 'graph' or mod == 'both':
        chobj(delta, illum, moonaltazs)
Beispiel #3
0
def mooniap(place):
    moon = get_moon(times_1)
    lum = moon_illumination(times_1, place)
    moonaltaz = moon.transform_to(frame_1)

    #prinmooniap(times,lum,moonaltaz.alt,ntimes,sunaltazs_1,sunaltazs_2)

    moonaltaz_ori = moon.transform_to(frame_ori1)

    prinmooniap(times, lum, moonaltaz_ori.alt, ntimes, sunaltazs_ori1,
                sunaltazs_ori2)
Beispiel #4
0
    def get_moon_param(self):
        #self.__set_start_time()
       # self.__set_interval_time()
       # self.__set_framing()
        moon = get_moon(self.__times_1)
        illum = moon_illumination(self.__times_1, self.__place)
        moonaltaz = moon.transform_to(self.__frame_1)

        moon_pars = {'moon illumination': illum, 'moon altitude': moonaltaz.alt, 'moon azimuth': moonaltaz.az} 

        return moon_pars
def plot_all(startimes, endtimes, midtimes, duration, target, obs):
	n = len(startimes)
	w = int(np.ceil(np.sqrt(n)))
	h = int(np.ceil(n/w))

	half_duration = datetime.timedelta(hours=0.5*duration[0])

	fig, axs = plt.subplots(w,h,figsize=(3*h,3*w),sharey=True)
	
	m = 0
	for i in np.arange(w):
		for j in np.arange(h):
			if m>=n:
				axs[i,j].set_visible(False)
				continue

			year = midtimes[m][6:10]
			mid_time = Time(year  + '-' + midtimes[m].replace('/','-').replace('-%s'%year,'') + ':00')
			
			start_time = -1*half_duration + mid_time.datetime
			end_time   = half_duration + mid_time.datetime

			# add airmass plot - sample airmass time window from UT 0 so x axis reads correct data - created start of grid by dumb hack of taking start_time and subtracting start_time.hour
			dt_array = np.arange(0,30,0.1)
			time_array = [start_time - datetime.timedelta(hours=start_time.hour) + datetime.timedelta(hours=q) for q in dt_array]
			ax = plot_airmass(target, obs, time_array, ax=axs[i,j],brightness_shading=False, altitude_yaxis=False)
			
			# get moon info
			moon_ill = round(moon.moon_illumination(mid_time),2)
			moon_coord = moon.get_moon(mid_time,location=obs.location) 
			moon_sep = np.abs(moon_coord.ra  - target.ra)

			axs[i,j].axvspan(start_time, end_time,ymin=0, ymax=10, color='plum',alpha=0.8)

			# narrower x-axis
			xlo = datetime.datetime(day=start_time.day, year=start_time.year, month= start_time.month)
			axs[i,j].set_xlim(xlo + datetime.timedelta(hours=4),xlo + datetime.timedelta(hours=17))

			# Put moon info in each panel
			txt_shift = 5 if start_time.hour > 8 else 12
			txt = axs[i,j].annotate('moon:%s\n%sdeg'%(moon_ill,round(moon_sep.value)),\
				(xlo + datetime.timedelta(hours=txt_shift),2.5),\
				fontsize=8)
			txt.set_bbox(dict(facecolor='white', alpha=0.5))

			start = start_time - datetime.timedelta(hours=10)
			twilight1 =obs.twilight_evening_astronomical(Time(start), which='next').datetime
			twilight2 = obs.twilight_morning_astronomical(Time(start), which='next').datetime
			ax.axvspan(twilight1, twilight2, ymin=0, ymax=1, color='lightgrey',zorder=-10)

			fig.suptitle(planet + ' from ' + site, fontsize=18)
			m+=1

	plt.subplots_adjust(hspace=0.6,top=0.92)
Beispiel #6
0
    def compute_constraint(self, times, observer, targets):
        illumination = np.array([moon_illumination(times, observer.location)]*len(targets))
	#                       ^                                           ^
	# I added these brackets and the *len(targets) to force illumination to have the correct shape
        if self.min is None and self.max is not None:
            mask = self.max >= illumination
        elif self.max is None and self.min is not None:
            mask = self.min <= illumination
        elif self.min is not None and self.max is not None:
            mask = ((self.min <= illumination) &
                    (illumination <= self.max))
        else:
            raise ValueError("No max and/or min specified in "
                             "MoonSeparationConstraint.")
        return mask
def get_transit_observability(site,
                              ra,
                              dec,
                              name,
                              t_mid_0,
                              period,
                              duration,
                              n_transits=100,
                              obs_start_time=Time(
                                  dt.datetime.today().isoformat()),
                              min_local_time=dt.time(16, 0),
                              max_local_time=dt.time(9, 0),
                              min_altitude=20 * u.deg,
                              oot_duration=30 * u.minute,
                              minokmoonsep=30 * u.deg):
    """
    note: barycentric corrections not yet implemented. (could do this myself!)
    -> 16 minutes of imprecision is baked into this observability calculator!

    args:

        site (astroplan.observer.Observer)

        ra, dec (units u.deg), e.g.:
            ra=101.28715533*u.deg, dec=16.71611586*u.deg,
        or can also accept
            ra="17 56 35.51", dec="-29 32 21.5"

        name (str), e.g., "Sirius"

        t_mid_0 (float): in BJD_TDB, preferably (but see note above).

        period (astropy quantity, units time)

        duration (astropy quantity, units time)

        n_transits (int): number of transits forward extrapolated to

        obs_start_time (astropy.Time object): when to start calculation from

        min_local_time, max_local_time: earliest time when you think observing
        is OK. E.g., 16:00 local and 09:00 local are earliest and latest. Note
        this constraint is a bit silly, since the astroplan "AtNightConstraint"
        is imposed automatically. As implemented, these are ignored.

        min_altitude (astropy quantity, units deg): 20 degrees is the more
        relevant constraint.

        oot_duration (astropy quantity, units time): with which to brack
        transit observations, to get an OOT baseline.
    """

    if (isinstance(ra, u.quantity.Quantity)
            and isinstance(dec, u.quantity.Quantity)):
        target_coord = SkyCoord(ra=ra, dec=dec)
    elif (isinstance(ra, str) and isinstance(dec, str)):
        target_coord = SkyCoord(ra=ra, dec=dec, unit=(u.hourangle, u.deg))
    else:
        raise NotImplementedError

    target = FixedTarget(coord=target_coord, name=name)

    primary_eclipse_time = Time(t_mid_0, format='jd')

    system = EclipsingSystem(primary_eclipse_time=primary_eclipse_time,
                             orbital_period=period,
                             duration=duration,
                             name=name)

    midtransit_times = system.next_primary_eclipse_time(obs_start_time,
                                                        n_eclipses=n_transits)

    # for the time being, omit any local time constraints.
    constraints = [
        AtNightConstraint.twilight_civil(),
        AltitudeConstraint(min=min_altitude),
        MoonSeparationConstraint(min=minokmoonsep)
    ]
    #constraints = [AtNightConstraint.twilight_civil(),
    #               AltitudeConstraint(min=min_altitude),
    #               LocalTimeConstraint(min=min_local_time, max=max_local_time)]

    # observable just at midtime (bottom)
    b = is_event_observable(constraints, site, target, times=midtransit_times)

    # observable full transits (ingress, bottom, egress)
    ing_egr = system.next_primary_ingress_egress_time(obs_start_time,
                                                      n_eclipses=n_transits)

    ibe = is_event_observable(constraints,
                              site,
                              target,
                              times_ingress_egress=ing_egr)

    # get moon separation over each transit. take minimum moon sep at
    # ing/tmid/egr as the moon separation.
    moon_tmid = get_moon(midtransit_times, location=site.location)
    moon_separation_tmid = moon_tmid.separation(target_coord)

    moon_ing = get_moon(ing_egr[:, 0], location=site.location)
    moon_separation_ing = moon_ing.separation(target_coord)

    moon_egr = get_moon(ing_egr[:, 1], location=site.location)
    moon_separation_egr = moon_egr.separation(target_coord)

    moon_separation = np.round(
        np.array(
            [moon_separation_tmid, moon_separation_ing,
             moon_separation_egr]).min(axis=0), 0).astype(int)

    moon_illumination = np.round(
        100 * moon.moon_illumination(midtransit_times), 0).astype(int)

    # completely observable transits (OOT, ingress, bottom, egress, OOT)
    oot_ing_egr = np.concatenate(
        (np.array(ing_egr[:, 0] - oot_duration)[:, None],
         np.array(ing_egr[:, 1] + oot_duration)[:, None]),
        axis=1)

    oibeo = is_event_observable(constraints,
                                site,
                                target,
                                times_ingress_egress=oot_ing_egr)

    ing_tmid_egr = np.concatenate(
        (np.array(ing_egr[:, 0])[:, None], np.array(midtransit_times)[:, None],
         np.array(ing_egr[:, 1])[:, None]),
        axis=1)

    return ibe, oibeo, ing_tmid_egr, moon_separation, moon_illumination
Beispiel #8
0
        if 'EXPTIME' in h:
            exp = float(h['EXPTIME'])
        else:
            exp = -1.0
        if 'TIME-OBS' in h:
            #
            iso_date = h['TIME-OBS']
            hms = iso_date.split(':')
            moon = -1.0
        elif 'DATE-LOC' in h:
            # '2020-02-29T20:13:34.920'
            iso_date = h['DATE-LOC']
            hms = iso_date.split('T')[1].split(':')
            if len(hms) == 1:
                # '2017-11-19T22-43-30.506'
                # there was a time we did it wrong....
                hms = h['DATE-LOC'].split('T')[1].split('-')
                moon = -3.0
            else:
                if Qmoon:
                    # example iso_date (in UT):   2021-09-12T19:58:21.025
                    # print("HMS: ",iso_date)
                    t = Time(iso_date)
                    moon = moon_illumination(t)
        else:
            hms = -999.999
            moon = -2.0
        t = float(hms[0]) + float(hms[1]) / 60 + float(hms[2]) / 3600

        print("%.4f %g %g %g %s" % (t, np.median(dc), exp, moon, ffile))
def get_event_observability(
    eventclass,
    site, ra, dec, name, t_mid_0, period, duration, n_transits=100,
    obs_start_time=Time(dt.datetime.today().isoformat()),
    min_altitude = None,
    oot_duration = 30*u.minute,
    minokmoonsep = 30*u.deg,
    max_airmass = None,
    twilight_limit = 'nautical'):
    """
    note: barycentric corrections not yet implemented. (could do this myself!)
    -> 16 minutes of imprecision is baked into this observability calculator!

    args:

        eventclass: e.g., "OIBE". Function does NOT return longer events.

        site (astroplan.observer.Observer)

        ra, dec (units u.deg), e.g.:
            ra=101.28715533*u.deg, dec=16.71611586*u.deg,
        or can also accept
            ra="17 56 35.51", dec="-29 32 21.5"

        name (str), e.g., "Sirius"

        t_mid_0 (float): in BJD_TDB, preferably (but see note above).

        period (astropy quantity, units time)

        duration (astropy quantity, units time)

        n_transits (int): number of transits forward extrapolated to

        obs_start_time (astropy.Time object): when to start calculation from

        min_altitude (astropy quantity, units deg): 20 degrees is the more
        relevant constraint.

        max_airmass: e.g., 2.5. One of max_airmass or min_altitude is required.

        oot_duration (astropy quantity, units time): with which to brack
        transit observations, to get an OOT baseline.

        twilight_limit: 'astronomical', 'nautical', 'civil' for -18, -12, -6
        deg.
    """
    if eventclass not in [
        'OIBEO', 'OIBE', 'IBEO', 'IBE', 'BEO', 'OIB', 'OI', 'EO'
    ]:
        raise AssertionError

    if (isinstance(ra, u.quantity.Quantity) and
        isinstance(dec, u.quantity.Quantity)
    ):
        target_coord = SkyCoord(ra=ra, dec=dec)
    elif (isinstance(ra, str) and
          isinstance(dec, str)
    ):
        target_coord = SkyCoord(ra=ra, dec=dec, unit=(u.hourangle, u.deg))
    else:
        raise NotImplementedError

    if (
        not isinstance(max_airmass, float)
        or isinstance(min_altitude, u.quantity.Quantity)
    ):
        raise NotImplementedError

    target = FixedTarget(coord=target_coord, name=name)

    primary_eclipse_time = Time(t_mid_0, format='jd')

    system = EclipsingSystem(primary_eclipse_time=primary_eclipse_time,
                             orbital_period=period, duration=duration,
                             name=name)

    midtransit_times = system.next_primary_eclipse_time(
        obs_start_time, n_eclipses=n_transits)

    # for the time being, omit any local time constraints.
    if twilight_limit == 'astronomical':
        twilight_constraint = AtNightConstraint.twilight_astronomical()
    elif twilight_limit == 'nautical':
        twilight_constraint = AtNightConstraint.twilight_nautical()
    else:
        raise NotImplementedError('civil twilight is janky.')

    constraints = [twilight_constraint,
                   AltitudeConstraint(min=min_altitude),
                   AirmassConstraint(max=max_airmass),
                   MoonSeparationConstraint(min=minokmoonsep)]

    # tabulate ingress and egress times.
    ing_egr = system.next_primary_ingress_egress_time(
        obs_start_time, n_eclipses=n_transits
    )

    oibeo_window = np.concatenate(
        (np.array(ing_egr[:,0] - oot_duration)[:,None],
         np.array(ing_egr[:,1] + oot_duration)[:,None]),
        axis=1)
    oibe_window = np.concatenate(
        (np.array(ing_egr[:,0] - oot_duration)[:,None],
         np.array(ing_egr[:,1])[:,None]),
        axis=1)
    ibeo_window = np.concatenate(
        (np.array(ing_egr[:,0])[:,None],
         np.array(ing_egr[:,1] + oot_duration)[:,None]),
        axis=1)
    oib_window = np.concatenate(
        (np.array(ing_egr[:,0] - oot_duration)[:,None],
         np.array(midtransit_times)[:,None]),
        axis=1)
    beo_window = np.concatenate(
        (np.array(midtransit_times)[:,None],
         np.array(ing_egr[:,1] + oot_duration)[:,None]),
        axis=1)
    ibe_window = ing_egr
    oi_window = np.concatenate(
        (np.array(ing_egr[:,0] - oot_duration)[:,None],
        np.array(ing_egr[:,0])[:,None]),
        axis=1)
    eo_window = np.concatenate(
        (np.array(ing_egr[:,1])[:,None],
        np.array(ing_egr[:,1] + oot_duration)[:,None]),
        axis=1)

    keys = ['oibeo','oibe','ibeo','oib','beo','ibe','oi','eo']
    windows = [oibeo_window, oibe_window, ibeo_window,
               oib_window, beo_window, ibe_window, oi_window, eo_window]
    is_obs_dict = {}
    for key, window in zip(keys, windows):
        is_obs_dict[key] = np.array(
            is_event_observable(constraints, site, target,
                                times_ingress_egress=window)
        ).flatten()

    is_obs_df = pd.DataFrame(is_obs_dict)

    is_obs_df['ing'] = ing_egr[:,0]
    is_obs_df['egr'] = ing_egr[:,1]
    is_obs_df['isoing'] = Time(ing_egr[:,0], format='iso')
    is_obs_df['isoegr'] = Time(ing_egr[:,1], format='iso')

    # this function returns the observable events that are LONGEST. e.g.,
    # during an OIBEO transit you COULD observe just OIB, but why would you?

    if eventclass == 'OIBEO':
        event_ind = np.array(is_obs_df[eventclass.lower()])[None,:]
    elif eventclass in ['IBEO', 'OIBE']:
        event_ind = np.array(
            is_obs_df[eventclass.lower()] & ~is_obs_df['oibeo']
        )[None,:]
    elif eventclass in ['IBE', 'OIB', 'BEO']:
        event_ind = np.array(
            is_obs_df[eventclass.lower()]
            & ~is_obs_df['oibeo']
            & ~is_obs_df['oibe']
            & ~is_obs_df['ibeo']
        )[None,:]
    elif eventclass in ['OI', 'EO']:
        event_ind = np.array(
            is_obs_df[eventclass.lower()]
            & ~is_obs_df['oibeo']
            & ~is_obs_df['oibe']
            & ~is_obs_df['ibeo']
            & ~is_obs_df['oib']
            & ~is_obs_df['ibe']
            & ~is_obs_df['beo']
        )[None,:]

    # get moon separation over each transit. take minimum moon sep at
    # ing/tmid/egr as the moon separation.
    moon_tmid = get_moon(midtransit_times, location=site.location)
    moon_separation_tmid = moon_tmid.separation(target_coord)

    moon_ing = get_moon(ing_egr[:,0], location=site.location)
    moon_separation_ing = moon_ing.separation(target_coord)

    moon_egr = get_moon(ing_egr[:,1], location=site.location)
    moon_separation_egr = moon_egr.separation(target_coord)

    moon_separation = np.round(np.array(
        [moon_separation_tmid, moon_separation_ing,
         moon_separation_egr]).min(axis=0),0).astype(int)

    moon_illumination = np.round(
        100*moon.moon_illumination(midtransit_times),0).astype(int)

    # completely observable transits (OOT, ingress, bottom, egress, OOT)
    oibeo = is_event_observable(constraints, site, target,
                                times_ingress_egress=oibeo_window)

    ing_tmid_egr = np.concatenate(
        (np.array(ing_egr[:,0])[:,None],
         np.array(midtransit_times)[:,None],
         np.array(ing_egr[:,1])[:,None]),
        axis=1)

    target_window = np.array(windows)[
        int(np.argwhere(np.array(keys)==eventclass.lower())), :, :
    ]

    return (
        event_ind, oibeo, ing_tmid_egr, target_window,
        moon_separation, moon_illumination
    )
Beispiel #10
0
def derive_lightcurve_info(lightcurve_pk):
   """
   Function to derive extra information about the observations from the 
   fits files, calculate several parameters, and derive weather information
   from external sources is possible.
   
   This information is stored in the lightcurve database entry
   """
   
   #-- get lightcurve
   lightcurve = LightCurve.objects.get(pk=lightcurve_pk)
   hjd, flux, header = lightcurve.get_lightcurve()
   
   
   #-- load info from lightcurve header
   data = instrument_headers.extract_header_info(header)
   
   # HJD
   lightcurve.hjd = data.get('hjd', 2400000)
   lightcurve.hjd_start = data.get('hjd_start', 2400000)
   lightcurve.hjd_end = data.get('hjd_end', 2400000)
   
   # pointing info
   lightcurve.objectname = data.get('objectname', '')
   lightcurve.ra = data.get('ra', -1)
   lightcurve.dec = data.get('dec', -1)
   lightcurve.alt = data.get('alt', -1)
   lightcurve.az = data.get('az', -1)
   lightcurve.airmass = data.get('airmass', -1)
   
   # telescope and instrument info
   lightcurve.instrument = data.get('instrument', 'UK')
   lightcurve.telescope = data.get('telescope', 'UK')
   lightcurve.passband = data.get('passband', 'UK')
   lightcurve.exptime = data.get('exptime', -1)
   lightcurve.cadence = data.get('cadence', -1)
   lightcurve.duration = data.get('duration', -1)
   lightcurve.observer = data.get('observer', 'UK')
   lightcurve.filetype = data.get('filetype', 'UK')
   
   # observing conditions
   if isfloat(data.get('wind_speed', -1)):
      lightcurve.wind_speed = data.get('wind_speed', -1)
   
   if isfloat(data.get('wind_direction', -1)):
      lightcurve.wind_direction = data.get('wind_direction', -1)
      
   lightcurve.seeing = data.get('seeing', -1)

   
   
   #-- observatory
   lightcurve.observatory = instrument_headers.get_observatory(header, lightcurve.project)
   
   #-- save the changes
   lightcurve.save()
   
   #-- if the observatory is in space, no moon ect can be calculated
   if lightcurve.observatory.space_craft:
      return
   
   #-- calculate moon parameters
   time = Time(lightcurve.hjd, format='jd')
   
   # moon illumination wit astroplan (astroplan returns a fraction, but we store percentage)
   lightcurve.moon_illumination =  np.round(moon_illumination(time=time)*100, 1)
   
   # get the star and moon coordinates at time and location of observations
   star = SkyCoord(ra=lightcurve.ra*u.deg, dec=lightcurve.dec*u.deg,)
   moon = get_moon(time)
   
   observatory = lightcurve.observatory.get_EarthLocation()
   frame = AltAz(obstime=time, location=observatory)
   
   star = star.transform_to(frame)
   moon = moon.transform_to(frame)
   
   # store the separation between moon and target
   lightcurve.moon_separation = np.round(star.separation(moon).degree, 1)
   
   #-- get object alt-az and airmass if not stored in header
   if lightcurve.alt == 0:
      lightcurve.alt = star.alt.degree
   if lightcurve.az == 0:
      lightcurve.az = star.az.degree
   if lightcurve.airmass <= 0:
      lightcurve.airmass = np.round(star.secz.value, 2)
   
   #-- save the changes
   lightcurve.save()
Beispiel #11
0
def derive_spectrum_info(spectrum_pk, user_info={}):
    """
        Function to derive extra information about the observations from the
        fits files, calculate several parameters, and derive weather information
        from external sources if possible.

        This information is stored in the spectrum database entry
    """
    #   Get spectrum
    spectrum = Spectrum.objects.get(pk=spectrum_pk)
    wave, flux, header = spectrum.get_spectrum()

    #   Get min and max wavelength
    spectrum.minwave = np.min(np.array(wave).flatten())
    spectrum.maxwave = np.max(np.array(wave).flatten())

    #   Load info from spectrum header
    data = instrument_headers.extract_header_info(header, user_info=user_info)

    #   HJD
    spectrum.hjd = data.get('hjd', 2400000)

    #   Pointing info
    #spectrum.objectname = data.get('objectname', '')
    spectrum.objectname = data.get('objectname', spectrum.hjd)
    spectrum.ra = data.get('ra', -1)
    spectrum.dec = data.get('dec', -1)
    spectrum.alt = data.get('alt', -1)
    spectrum.az = data.get('az', -1)
    spectrum.airmass = data.get('airmass', -1)

    # telescope and instrument info
    spectrum.instrument = data.get('instrument', 'UK')
    spectrum.telescope = data.get('telescope', 'UK')
    spectrum.exptime = data.get('exptime', -1)
    spectrum.observer = data.get('observer', 'UK')
    spectrum.resolution = data.get('resolution', -1)
    spectrum.snr = data.get('snr', -1)
    spectrum.barycor = data.get('barycor', 0)
    spectrum.barycor_bool = data.get('barycor_bool', False)

    #    Spectrum normalized?
    spectrum.normalized = data.get('normalized', False)

    #   Observing conditions
    if isfloat(data.get('wind_speed', -1)):
        spectrum.wind_speed = data.get('wind_speed', -1)

    if isfloat(data.get('wind_direction', -1)):
        spectrum.wind_direction = data.get('wind_direction', -1)

    spectrum.seeing = data.get('seeing', -1)

    #    Flux unit & calibrated flux?
    spectrum.fluxcal = data.get('fluxcal', False)
    spectrum.flux_units = data.get('flux_units', 'arbitrary unit')

    if spectrum.fluxcal:
        spectrum.flux_units = data.get('flux_units', 'ergs/cm/cm/s/A')

    if spectrum.normalized:
        spectrum.flux_units = 'normalized'
        spectrum.fluxcal = False

    #    Note
    spectrum.note = data.get('note', '')

    #    Observatory
    if 'obs_pk' in user_info.keys():
        #   Selection from dropdown in the user info form
        spectrum.observatory = Observatory.objects.get(pk=user_info['obs_pk'])
    elif 'observatory_id' in user_info.keys():
        #   Selection based on the information given in the user info form
        if user_info['observatory_id'] == None:
            #   If form contains no information try header
            spectrum.observatory = instrument_headers.get_observatory(
                header,
                spectrum.project,
            )
        else:
            spectrum.observatory = Observatory.objects.get(
                pk=user_info['observatory_id'])
    else:
        #   Extract observatory infos from header or create a new observatory
        spectrum.observatory = instrument_headers.get_observatory(
            header,
            spectrum.project,
        )

    #   Save the changes
    spectrum.save()

    #   Calculate moon parameters
    time = Time(spectrum.hjd, format='jd')

    #   Moon illumination wit astroplan (astroplan returns a fraction, but we
    #   store percentage)
    spectrum.moon_illumination = np.round(
        moon_illumination(time=time) * 100, 1)

    #   Get the sky and moon coordinates at time and location of observations
    sky = SkyCoord(
        ra=spectrum.ra * u.deg,
        dec=spectrum.dec * u.deg,
    )
    moon = get_moon(time)

    #   Set observatory and transform sky coordinates to altitude & azimuth
    observatory = spectrum.observatory.get_EarthLocation()
    frame = AltAz(obstime=time, location=observatory)

    star = sky.transform_to(frame)
    moon = moon.transform_to(frame)

    #   Store the separation between moon and target
    spectrum.moon_separation = np.round(star.separation(moon).degree, 1)

    #   Get object alt-az and airmass if not stored in header
    if spectrum.alt <= 0:
        spectrum.alt = star.alt.degree
    if spectrum.az <= 0:
        spectrum.az = star.az.degree
    if spectrum.airmass <= 0:
        spectrum.airmass = np.round(star.secz.value, 2)

    #   Barycentric correction
    if not spectrum.barycor_bool:
        vb = sky.radial_velocity_correction(
            obstime=time,
            location=observatory,
        )
        vb = vb.to(u.km / u.s)
        spectrum.barycor = vb.value

    #   Save the changes
    spectrum.save()

    return "Spectrum details added/updated", True