Пример #1
0
def get_datetime(from_value):
    """
    Ensure datetime values are in MJD. This is meant to handle any odd formats
    that telescopes have for datetime values.

    Relies on astropy, until astropy fails.

    :param from_value:
    :return: datetime instance
    """
    if from_value is not None:
        try:
            result = Time(from_value)
            result.format = 'mjd'
            return result
        except ValueError:
            try:
                # VLASS has a format astropy fails to understand
                # from datetime import datetime
                result = Time(
                    dt_datetime.strptime(from_value, '%H:%M:%S'))
                result.format = 'mjd'
                return result
            except ValueError:
                logging.error('Cannot parse datetime {}'.format(from_value))
                return None
    else:
        return None
Пример #2
0
    def time_chunk(self, nameFile):

        self.logger.info("\t ...  Observing time Info ... \n")

        t = tables.table(nameFile)
        self.time = t.getcol('TIME')
        t.close()

        starttime = self.time[0]
        endtime = self.time[-1]

        startdate = Time(starttime / 3600. / 24., format='mjd', scale='utc')
        startdate.format = 'iso'
        startdate.subformat = 'date_hm'

        enddate = Time(endtime / 3600. / 24., format='mjd', scale='utc')

        enddate.format = 'iso'
        enddate.subformat = 'date_hm'

        self.logger.info('\t Start date: {0:%y}{0:%b}{0:%d}:{0:%X}'.format(
            startdate.datetime))
        self.logger.info(
            '\t End date  : {0:%y}{0:%b}{0:%d}:{0:%X} \n\n'.format(
                enddate.datetime))

        return startdate, enddate
Пример #3
0
    def __getitem__(self, i):
        """ Supports indexing of the TimeScale, e.g. ts[0:10:2] """
        if isinstance(i, slice):
            skip = 1 if i.step is None else i.step
            start = 0 if i.start is None else i.start
            stop = self.n_step if i.stop is None else i.stop
            new_n_step = (stop - start) // skip

            new_time_unix = self.val_start + start * self.val_step
            new_time_obj = Time(new_time_unix, format='unix')
            new_time_obj.format = self.time_format

            new_delta_sec = self.val_step * skip
            new_delta_obj = TimeDelta(new_delta_sec, format='sec')
            new_delta_obj.format = self.time_delta_format

            #return self.val_start + self.val_step * np.arange(start, stop, step)
            return TimeScale(self.name,
                             new_time_obj.value,
                             new_delta_obj.value,
                             new_n_step,
                             time_format=new_time_obj.format,
                             time_delta_format=new_delta_obj.format)
        else:
            if i < 0 or i > self.n_step:
                raise IndexError(
                    f"Index ({i}) is out of axis bound ({self.n_step})")
            t = Time(i * self.val_step + self.val_start, format='unix')
            t.format = self.time_format
            return t
Пример #4
0
    def time_chunk(self, cfg_par):

        self.logger.info("\t ...  Observing time Info ... \n")
        self.msfile = cfg_par['general']['msfullpath']

        t = tables.table(self.msfile)
        self.time = t.getcol('TIME')
        t.close()

        starttime = self.time[0]

        endtime = self.time[-1]
        time_chunk = float(cfg_par['rfi']['chunks']['time_step']) * 60.

        times = np.arange(starttime, endtime + time_chunk * 1., time_chunk)

        cfg_par['rfi']['times'] = times
        startdate = Time(starttime / 3600. / 24., format='mjd', scale='utc')
        cfg_par['rfi']['startdate'] = startdate
        startdate.format = 'iso'
        startdate.subformat = 'date_hm'

        enddate = Time(endtime / 3600. / 24., format='mjd', scale='utc')
        cfg_par['rfi']['enddate'] = enddate

        enddate.format = 'iso'
        enddate.subformat = 'date_hm'

        self.logger.info('\t Start date: {0:%y}{0:%b}{0:%d}:{0:%X}'.format(
            startdate.datetime))
        self.logger.info(
            '\t End date  : {0:%y}{0:%b}{0:%d}:{0:%X} \n\n'.format(
                enddate.datetime))

        return times, startdate, enddate
Пример #5
0
def get_datetime(filename):
    # Reading binary data
    with open(filename, "rb") as f:
        bin_data = f.read()

    # Searching datetime start byte
    date_key = r'\xe0.\x00\x00\x0b'
    matches = []
    for match in finditer(date_key, bin_data):
        matches.append(match.span())
    date_offset = matches[-1][0] - 8

    # Unpacking datetime
    time_values = {}
    hours, days = unpack_from("<II", bin_data, date_offset)
    secs = days * 45.0 * 60.0 + hours / (4294967295.0 / 45.0) * 60.0
    time_values["UNIX"] = secs - 2938117104000.0

    # Convert to ISO and MJD time formats
    t = Time(time_values["UNIX"], format="unix")
    t.format = "iso"
    t.out_subfmt = "date_hm"
    time_values["ISO"] = t.value
    t.format = "mjd"
    time_values["MJD"] = str(int(np.floor(t.value)))

    return time_values
Пример #6
0
def rv(obj_name, v_sun_star, obstime_range=['2020-01-01','2020-12-31'], obsloc='Las Campanas Observatory'):
    '''Calculate the relative velocity between an exoplanet and the Earth
    Inputs
    ---
    obj_name: string
        The name of the object (e.g. star) that you want the coordinates of
    v_sun_star: integer or float in m/s
        The relative velocity between the Sun and the exoplanet system
    obstime_range: list of strings, optional
        A list containing the beginning and end date for the range of time that you're interested in calculating the
        RV between the Earth and the exoplanet of interest for. Defaults to ['2020-01-01','2020-12-31'].
    obsloc: string, optional
        Name of relevant observatory. Defaults to 'Las Campanas Observatory'. Not sure how much this affects the
        results.

    Returns
    ---
    rv_df: pandas dataframe
        columns: date (jd), rv (in km/s)
    '''

    # Give v_sun_star units
    v_sun_star = v_sun_star*m_per_s

    # Find the location of the observatory
    loc = coord.EarthLocation.of_site(obsloc)

    # Find the coordinates of the object (e.g. host star)
    sc = SkyCoord.from_name(obj_name)

    # Create an astropy time object with the specified range of times
    t1 = Time(obstime_range[0])
    t1.format = 'jd'  # 'jd' could be a function input

    t2 = Time(obstime_range[-1])
    t2.format = 'jd'  # 'jd' could be a function input

    # I'm not sure what the length of these steps is - Victoria 10/29/20
    times = Time(np.arange(t1.value, t2.value), format='jd')

    with coord.solar_system_ephemeris.set('jpl'):  # 'jpl' could be a function input
        rv_corrs = sc.radial_velocity_correction(obstime=times, location=loc)

    # Calculate Earth-centric RVs in km/s
    v_star = (v_sun_star - rv_corrs).to(km_per_s)

    # Get rid of time's unit
    times = times.value

    rv_df = pd.DataFrame({"date":times,'rv':v_star})

    return(rv_df)
Пример #7
0
def change_times_thread(logger, prfs_d, fits_image):
    """

    @param logger:
    @param prfs_d:
    @param fits_image:

    @return True:
    """
    fits_dir = '{}/{}/CCDs'.format(prfs_d['fits_dir'], mag)

    data, header = fits.getdata('{}/{}'.format(fits_dir, fits_image),
                                header=True)
    dither = fits_image[-6:-5]

    if dither == '1':
        header['DATE-OBS'] = prfs_d['time_1']
        # Change format
        t = Time(prfs_d['time_1'])
        t.format = 'mjd'
        header['MJD-OBS'] = float(str(t))
    elif dither == '2':
        header['DATE-OBS'] = prfs_d['time_2']
        t = Time(prfs_d['time_2'])
        t.format = 'mjd'
        header['MJD-OBS'] = float(str(t))
    elif dither == '3':
        header['DATE-OBS'] = prfs_d['time_3']
        t = Time(prfs_d['time_3'])
        t.format = 'mjd'
        header['MJD-OBS'] = float(str(t))
    elif dither == '4':
        header['DATE-OBS'] = prfs_d['time_4']
        t = Time(prfs_d['time_4'])
        t.format = 'mjd'
        header['MJD-OBS'] = float(str(t))
    else:
        header['DATE-OBS'] = prfs_d['time_1']
        # Change format
        t = Time(prfs_d['time_1'])
        t.format = 'mjd'
        header['MJD-OBS'] = float(str(t))

    print('opens {} date-obs {} mjd-obs {} d {}'.format(
        fits_image, header['DATE-OBS'], header['MJD-OBS'], dither))

    fits.writeto('{}/{}_copy'.format(fits_dir, fits_image),
                 data,
                 header,
                 clobber=True)

    return True
Пример #8
0
def change_times_thread(prfs_d, fits_image):
    """

    @param prfs_d:
    @param fits_image:

    @return True:
    """
    data, header = fits.getdata('{}/{}'.format(prfs_d['fits_dir'], fits_image),
                                header=True)
    dither = fits_image[-6:-5]

    print('dither {}'.format(dither))

    if dither == '1':
        header['DATE-OBS'] = prfs_d['time_1']
        # Change format
        t = Time(prfs_d['time_1'])
        t.format = 'mjd'
        header['MJD-OBS'] = float(str(t))
    elif dither == '2':
        header['DATE-OBS'] = prfs_d['time_2']
        t = Time(prfs_d['time_2'])
        t.format = 'mjd'
        header['MJD-OBS'] = float(str(t))
    elif dither == '3':
        header['DATE-OBS'] = prfs_d['time_3']
        t = Time(prfs_d['time_3'])
        t.format = 'mjd'
        header['MJD-OBS'] = float(str(t))
    elif dither == '4':
        header['DATE-OBS'] = prfs_d['time_4']
        t = Time(prfs_d['time_4'])
        t.format = 'mjd'
        header['MJD-OBS'] = float(str(t))
    else:
        header['DATE-OBS'] = prfs_d['time_1']
        # Change format
        t = Time(prfs_d['time_1'])
        t.format = 'mjd'
        header['MJD-OBS'] = float(str(t))

    print('opens {} date-obs {} mjd-obs {} d {}'.format(
        fits_image, header['DATE-OBS'], header['MJD-OBS'], dither))

    print('fits_image {}'.format(fits_image))
    fits.writeto('{}/{}_t.fits'.format(prfs_d['fits_dir'], fits_image[:-5]),
                 data, header)

    return True
Пример #9
0
def get_pyephem_instance_for_type(target):
    """
    Constructs a pyephem body corresponding to the proper object type
    in order to perform positional calculations for the target

    :returns: FixedBody or EllipticalBody

    :raises Exception: When a target type other than sidereal or non-sidereal is supplied

    """
    if target.type == target.SIDEREAL:
        body = ephem.FixedBody()
        body._ra = Angle(str(target.ra) + 'd').to_string(unit=units.hourangle, sep=':')
        body._dec = Angle(str(target.dec) + 'd').to_string(unit=units.degree, sep=':')
        body._epoch = target.epoch if target.epoch else ephem.Date(DEFAULT_VALUES['epoch'])
        return body
    elif target.type == target.NON_SIDEREAL:
        body = ephem.EllipticalBody()
        body._inc = ephem.degrees(target.inclination) if target.inclination else 0
        body._Om = target.lng_asc_node if target.lng_asc_node else 0
        body._om = target.arg_of_perihelion if target.arg_of_perihelion else 0
        body._a = target.semimajor_axis if target.semimajor_axis else 0
        body._M = target.mean_anomaly if target.mean_anomaly else 0
        if target.ephemeris_epoch:
            epoch_M = Time(target.ephemeris_epoch, format='jd')
            epoch_M.format = 'datetime'
            body._epoch_M = ephem.Date(epoch_M.value)
        else:
            body._epoch_M = ephem.Date(DEFAULT_VALUES['epoch'])
        body._epoch = target.epoch if target.epoch else ephem.Date(DEFAULT_VALUES['epoch'])
        body._e = target.eccentricity if target.eccentricity else 0
        return body
    else:
        raise Exception("Object type is unsupported for visibility calculations")
Пример #10
0
def eq_to_alt_az(ra, dec, lat, long):
    ra = ra * 15
    time = datetime.utcnow()
    time_hrs = time.hour + time.minute / 60 + time.second / 3600
    time_julian = Time(time)
    time_julian.format = 'jd'
    time_j2000 = time_julian.value - 2451545  # Get J2000 time from substracting julian time of 1.1.2000 12:00 from current julian time.

    local_sid_time = (100.46 + 0.98547 * time_j2000 + long +
                      15 * time_hrs) % 360

    hour_angle = local_sid_time - ra

    alt = math.degrees(
        math.asin(
            math.sin(math.radians(dec)) * math.sin(math.radians(lat)) +
            math.cos(math.radians(dec)) * math.cos(math.radians(lat)) *
            math.cos(math.radians(hour_angle))))

    a = math.acos((math.sin(math.radians(dec)) -
                   math.sin(math.radians(alt)) * math.sin(math.radians(lat))) /
                  (math.cos(math.radians(alt)) * math.cos(math.radians(lat))))
    a = math.degrees(a)
    az = 0

    if math.degrees(math.sin(math.radians(hour_angle))) < 0:
        az = a
    else:
        az = 360 - a

    return alt, az
Пример #11
0
    def _process_photometry_from_plaintext(self, data_product):
        """
        Processes the photometric data from a plaintext file into a dict, which can then be  stored as a ReducedDatum
        for further processing or display. File is read using astropy as specified in the below documentation. The file
        is expected to be a multi-column delimited file, with headers for time, magnitude, filter, and error.
        # http://docs.astropy.org/en/stable/io/ascii/read.html

        :param data_product: Photometric DataProduct which will be processed into a dict
        :type data_product: DataProduct

        :returns: python dict containing the data from the DataProduct
        :rtype: dict
        """

        photometry = {}

        data = ascii.read(data_product.data.path)
        if len(data) < 1:
            raise InvalidFileFormatException(
                'Empty table or invalid file type')

        for datum in data:
            time = Time(float(datum['time']), format='mjd')
            utc = TimezoneInfo(utc_offset=0 * units.hour)
            time.format = 'datetime'
            value = {
                'magnitude': datum['magnitude'],
                'filter': datum['filter'],
                'error': datum['error']
            }
            photometry.setdefault(time.to_datetime(timezone=utc),
                                  []).append(value)

        return photometry
Пример #12
0
def get_leap_seconds(gps):
    """Find the number of leap seconds at a given gps time using astropy's time
    module (by comparing UTC to TAI and subtracting 19, the base difference
    between TAI and GPS scales)."""
    t = Time(gps, format='gps')
    t.format = 'iso'
    return (t.tai.to_datetime() - t.utc.to_datetime()).seconds - 19
Пример #13
0
def write_fits(img, metadata, fitsobj):
    imgtime = Time(metadata[0][0] + config.inttime*0.5, scale='utc', format='unix', location=(LOFAR_CS002_LONG, LOFAR_CS002_LAT))

    imgtime.format='isot'
    imgtime.out_subfmt = 'date_hms'
    filename = '%s_S%0.1f_I%ix%i_W%i_A%0.1f.fits' % (imgtime.datetime.strftime("%Y%m%d%H%M%SUTC"), np.mean(subbands), len(subbands), config.inttime, config.window, config.alpha)
    filename = os.path.join(config.output, filename)

    if os.path.exists(filename):
        logger.info("'%s' exists - skipping", filename)
        return

    # CRVAL1 should hold RA in degrees. sidereal_time returns hour angle in
    # hours.
    fitsobj.header['CRVAL1'] = imgtime.sidereal_time(kind='apparent').value  *  15
    fitsobj.header['DATE-OBS'] = str(imgtime)
    imgtime_end = imgtime + TimeDelta(config.inttime, format='sec')
    fitsobj.header['END_UTC'] = str(imgtime_end)
    t = Time.now();
    t.format = 'isot'
    fitsobj.header['DATE'] = str(t)
    fitsobj.data[0, 0, :, :] = img
    data = img[np.logical_not(np.isnan(img))]
    quality = rms.rms(rms.clip(data))
    high = data.max()
    low = data.min()
    fitsobj.header['DATAMAX'] = high
    fitsobj.header['DATAMIN'] = low
    fitsobj.header['HISTORY'][0] = 'AARTFAAC 6 stations superterp'
    fitsobj.header['HISTORY'][1] = 'RMS {}'.format(quality)
    fitsobj.header['HISTORY'][2] = 'DYNAMIC RANGE {}:{}'.format(int(round(high)), int(round(quality)))
    fitsobj.writeto(filename)
    logger.info("%s %0.3f %i:%i", filename, quality, int(round(high)), int(round(quality)))
Пример #14
0
    def on_save(self, path=None):
        """
        This is a hook that is called just before saving the file.
        It can be used, for example, to update values in the metadata
        that are based on the content of the data.

        Override it in the subclass to make it do something, but don't
        forget to "chain up" to the base class, since it does things
        there, too.

        Parameters
        ----------
        path : str
            The path to the file that we're about to save to.
        """
        if isinstance(path, str):
            self.meta.filename = os.path.basename(path)

        current_date = Time(datetime.datetime.now())
        current_date.format = 'isot'
        self.meta.date = current_date.value

        if not hasattr(self.meta, 'model_type'):
            klass = self.__class__.__name__
            if klass != 'DataModel':
                self.meta.model_type = klass
Пример #15
0
 def miriademoon(self, epoch, location):
     """
     Returns the table of RA, DEC and markers with Miriade calulator
     Attributes:
             target (string)         : target
             epoch (Time)            : epoch
             epoch_step (string)     : Miriade definition
             epoch_nsteps (int)      : Iteration Number
             coordtype (int)         : Miriade definition
             location (string)       : Miriade definition
     """
     epoch_iso = Time(epoch, format='fits')
     epoch_iso.format = 'iso'
     eph = Miriade.get_ephemerides('p:moon',
                                   epoch=epoch_iso.value,
                                   epoch_step='1m',
                                   epoch_nsteps=1,
                                   coordtype=1,
                                   location=location)
     ra = []
     dec = []
     marker = []
     for line in eph:
         ra.append(line['RA'])
         dec.append(line['DEC'])
         if line['V'] < -1:
             marker.append(-int(line['V']))
         else:
             marker.append(1)
     return Table([ra, dec, marker], names=['RA', 'DEC', 'MARKER'])
Пример #16
0
    def on_save(self, path=None):
        """
        This is a hook that is called just before saving the file.
        It can be used, for example, to update values in the metadata
        that are based on the content of the data.

        Override it in the subclass to make it do something, but don't
        forget to "chain up" to the base class, since it does things
        there, too.

        Parameters
        ----------
        path : str
            The path to the file that we're about to save to.
        """
        if isinstance(path, str):
            self.meta.filename = os.path.basename(path)

        current_date = Time(datetime.datetime.now())
        current_date.format = 'isot'
        self.meta.date = current_date.value

        # Enforce model_type to be the actual type of model being saved.
        self.meta.model_type = self._model_type

        # Remove None nodes from the tree:
        def _remove_none(node):
            if node is None:
                return RemoveNode
            else:
                return node

        self._instance = treeutil.walk_and_modify(self._instance, _remove_none)
Пример #17
0
def test_strftime_array():
    tstrings = ['2010-09-03 00:00:00', '2005-09-03 06:00:00', '1995-12-31 23:59:60']
    t = Time(tstrings)

    for format in t.FORMATS:
        t.format = format
        assert t.strftime('%Y-%m-%d %H:%M:%S').tolist() == tstrings
Пример #18
0
def test_trajectory_export():
    """Tests the interpolation accuracy for ISS."""

    # Init TLE
    line1 = "1 25544U 98067A   19343.69339541  .00001764  00000-0  38792-4 0  9991"
    line2 = "2 25544  51.6439 211.2001 0007417  17.6667  85.6398 15.50103472202482"

    # test init time
    init_time = Time(2458826.3, format="jd", scale="utc")
    init_time.format = "isot"

    duration = TimeDelta(3.0, format="jd")
    steps = 4000  # number of steps within the propagation duration

    dt, time_list = generate_timelist(init_time, duration, steps)
    prop_output = propagation_engine(line1, line2, time_list)

    ts = prop_output["traj"].to_timeseries()

    pvt = prop_output["traj"].coord_list[10]

    # print(ts)
    # print(ts["time", "v_X"])

    assert (ts.time[10] - pvt.obstime) < 17 * u.us
    assert (ts[10]["r_X"] - pvt.cartesian.x) < 1 * u.um
Пример #19
0
 def local_time_to_julian_day(self, date):
   microseconds, _ = modf(zero_if_none(date.second) * 1000000)
   local_datetime = pytz.timezone(self.timezone_id).localize(
     datetime.datetime(date.year, date.month, date.day, zero_if_none(date.hour), zero_if_none(date.minute), int(zero_if_none(date.second)), int(microseconds)))
   tm = Time(local_datetime, format="datetime")
   tm.format = "jd"
   return tm.value
Пример #20
0
def convert_time(analysis):

    #analysis is the fits file in the photometry function
    t = Time(analysis[0].header['DATE-OBS'])
    t.format = 'mjd'
    result = t.value
    return result
Пример #21
0
def carrington_rotation_time(crot, longitude: u.deg = None):
    """
    Return the time of a given Carrington rotation.

    Fractional Carrington rotation numbers can be provided in two ways:
    * Fractional numbers to ``crot``
    * Integer numbers to ``crot`` and a Carrington longitude to ``longitude``

    Inputs can be arrays.  If both ``crot`` and ``longitude`` are provided, the
    output shape will be the broadcasted combination.
    The round-trip from this method to `carrington_rotation_number` has
    absolute errors of < 0.11 seconds.

    Parameters
    ----------
    crot : `int`, `float`, `~astropy.units.Quantity`
        Carrington rotation number(s). Can be a fractional rotation number.
    longitude : `~astropy.units.Quantity`
        Carrington longitude(s), which must be > 0 degrees and <= 360 degrees.
        If provided, ``crot`` must be strictly integral.

    Returns
    -------
    `astropy.time.Time`

    Examples
    --------
    >>> from sunpy.coordinates.sun import carrington_rotation_time
    >>> import astropy.units as u
    >>> carrington_rotation_time(2242)
    <Time object: scale='utc' format='iso' value=2021-03-17 22:31:37.030>
    >>> carrington_rotation_time(2000.25)
    <Time object: scale='utc' format='iso' value=2003-02-27 02:52:57.315>
    >>> carrington_rotation_time(2000, 270*u.deg)
    <Time object: scale='utc' format='iso' value=2003-02-27 02:52:57.315>
    """
    crot = crot << u.one
    if longitude is not None:
        if not u.allclose(crot % 1, 0):
            raise ValueError("Carrington rotation number(s) must be integral if `longitude` is provided.")
        if (longitude <= 0*u.deg).any() or (longitude > 360*u.deg).any():
            raise ValueError("Carrington longitude(s) must be > 0 degrees and <= 360 degrees.")
        crot = crot + (1 - longitude/(360*u.deg))
    estimate = (constants.mean_synodic_period *
                (crot - 1)) + constants.first_carrington_rotation

    # The above estimate is inaccurate (see comments below in carrington_rotation_number),
    # so put the estimate into carrington_rotation_number to determine a correction amount
    def refine(estimate):
        crot_estimate = carrington_rotation_number(estimate)
        dcrot = crot - crot_estimate
        # Correct the estimate using a linear fraction of the Carrington rotation period
        return estimate + (dcrot * constants.mean_synodic_period)

    # Perform two iterations of the correction to achieve sub-second accuracy
    estimate = refine(estimate)
    estimate = refine(estimate)
    t = Time(estimate, scale='tt', format='jd').utc
    t.format = 'iso'
    return t
Пример #22
0
def convert_time(analysis):
    
    #analysis is the fits file in the photometry function
    t = Time(analysis[0].header['DATE-OBS'])
    t.format = 'mjd'
    result = t.value
    return result
Пример #23
0
    def _process_photometry_from_plaintext(self, data_product, extras):

        photometry = []

        data_aws = default_storage.open(data_product.data.name, 'r')
        data = ascii.read(data_aws.read(),
                          names=['time', 'filter', 'magnitude', 'error'])

        if len(data) < 1:
            raise InvalidFileFormatException('Empty table or invalid file type')

        for datum in data:
            time = Time(float(datum['time']), format='mjd')
            utc = TimezoneInfo(utc_offset=0*units.hour)
            time.format = 'datetime'
            value = {
                'timestamp': time.to_datetime(timezone=utc),
                'magnitude': datum['magnitude'],
                'filter': datum['filter'],
                'error': datum['error']
            }
            value.update(extras)

            photometry.append(value)

        return photometry
Пример #24
0
def test_strftime_leapsecond():
    time_string = '1995-12-31 23:59:60'
    t = Time(time_string)

    for format in t.FORMATS:
        t.format = format
        assert t.strftime('%Y-%m-%d %H:%M:%S') == time_string
Пример #25
0
def carrington_rotation_time(crot):
    """
    Return the time of a given Carrington rotation.

    The round-trip from this method to `carrington_rotation_number` has
    absolute errors of < 0.11 seconds.

    Parameters
    ----------
    crot : float
        Carrington rotation number. Can be a fractional cycle number.

    Returns
    -------
    astropy.time.Time
    """
    estimate = (constants.mean_synodic_period.to_value('day') *
                (crot - 1)) + constants.first_carrington_rotation

    # The above estimate is inaccurate (see comments below in carrington_rotation_number),
    # so put the estimate into carrington_rotation_number to determine a correction amount
    def refine(estimate):
        crot_estimate = carrington_rotation_number(estimate)
        dcrot = crot - crot_estimate
        # Correct the estimate using a linear fraction of the Carrington rotation period
        return estimate + (dcrot * constants.mean_synodic_period)

    # Perform two iterations of the correction to achieve sub-second accuracy
    estimate = refine(estimate)
    estimate = refine(estimate)
    t = Time(estimate, scale='tt', format='jd').utc
    t.format = 'iso'
    return t
Пример #26
0
def main(input_file, exp_code):
    exp_code = str(exp_code)
    # Load data from CSV file
    data = ascii.read(input_file, format='csv', comment='#', header_start=0)
    # Convert data into desired form
    az_deg = data['un_az']*180*(1/np.pi)
    el_deg = data['el']*180*(1/np.pi)
    rang = data['range']
    jd = data['jd']
    # Generate master schedule file
    print('Creating master schedule file.')
    with open(exp_code+'_master.out', 'w') as f:
        for i in range(0, len(data)):
            print("JD : " + format(jd[i], '.8f') + " ; Az : " + format(az_deg[i], '.3f') + " ; El : " + format(el_deg[i], '.3f') + " ; Rn :    0.0 ", file=f)
    print('Done!')
    # Now generate block schedules
    print('Generating schedule blocks.')
    interval = 0.00347 # 5 minutes in fraction of day
    start_time = jd[0]
    end_time = max(jd)
    num_scans = math.ceil((end_time - start_time)/interval) # calculate how many 5 min chunks are needed rounded up
    scantime_array = np.arange(start_time, start_time + (num_scans+0.1)*interval, interval) # the 0.1 makes sure we have a final value > the end time
    with open(exp_code+'_master.out', 'r') as file_read:
        if not os.path.exists(cwd + '/' + exp_code+'_blocks'): # check if subdir exists
            os.mkdir(cwd + '/' + exp_code+'_blocks') # create if it does not
        for line in file_read: # iterate through each line of master schedule file
            jd_time = float(line[5:21]) # extract JD time value
            scan_index = max(np.where((round(jd_time,8)/np.around(scantime_array, 8)) >= 1.0)[0]) # determine the scan that the given time will fall on - rounding required because of floating point rounding errors
            block_file_name = exp_code + '_b' + str(scan_index+1).zfill(3) + '.out' # determine the name of that scan's file
            with open(cwd + '/' + exp_code+'_blocks/' + block_file_name , 'a') as file_write: # write the data to the relevant file
                print(str.rstrip(line), file=file_write) # strip \n before printing
    print('Done!')
    # create shell script wrapper
    print('Creating shell script wrapper.')
    if os.path.exists(cwd + '/'+exp_code+ '_wrapper.sh'):
        os.remove(cwd + '/'+exp_code+ '_wrapper.sh')
    scan_times = Time(scantime_array, format='jd', scale='utc')
    scan_times.format = 'iso'
    with open(cwd + '/'+exp_code+ '_wrapper.sh', 'w') as f:
        print('#!/bin/bash\n', file=f) # append shebang to shell scripts
        print('currenttime=$(date +%s)\n', file=f) 
        block_list = sorted(os.listdir(cwd + '/'+exp_code+'_blocks')) # read block schedule subdirectory
        for i in range(0, len(block_list)):
            if (i % 2) == 0: # if even track normally
                print("if [ $currenttime -le $(date --date='" + str(scan_times[i]+ timedelta(seconds=30)) + " UTC' +%s) ]",file=f) # extra 30s is to account for the few seconds it takes for previous sattrk scan to finish
                print("then\n    sattrk -b -d 1/xs -i " + block_list[i] + " sys26m\nfi\n", file=f)
            else: # if odd track with offset
                print("if [ $currenttime -le $(date --date='" + str(scan_times[i]+ timedelta(seconds=30)) + " UTC' +%s) ]",file=f) # extra 30s is to account for the few seconds it takes for previous sattrk scan to finish
                print("then\n    sattrk -b -d 1/xs -x 5 -i " + block_list[i] + " sys26m\nfi\n", file=f) 
    print('Done!')
    # Write out the scan information of each 5 min block - for use with spectrum analyser recording and data reduction
    avg_el_list, avg_range_list = scanAvgElRange(data, scantime_array)
    print('Writing out schedule block start times.')
    if os.path.exists(cwd + '/'+exp_code+ '_scaninfo.out'):
        os.remove(cwd + '/'+exp_code+ '_scaninfo.out')
    with open(cwd + '/'+exp_code+ '_scaninfo.out','w') as f:
        for i in range(0, len(scantime_array)):
            print(scantime_array[i], avg_el_list[i], avg_range_list[i], file=f)
    print('Done!')
Пример #27
0
 def time(self):
     """
     The timestamps of the data.
     """
     t = Time(self._data.index)
     # Set time format to enable plotting with astropy.visualisation.time_support()
     t.format = 'iso'
     return t
Пример #28
0
def utc_gregorian_to_jd(date):
  if date.hour is None:
    date.set_time_to_day_start()
  tm = Time(
    {"year": date.year, "month": date.month, "day": date.day, "hour": zero_if_none(date.hour), "minute": zero_if_none(date.minute), "second": zero_if_none(date.second)},
    format='ymdhms')
  tm.format = "jd"
  return tm.value
Пример #29
0
 def validate(self, obj, value):
     """ try to parse and return an ISO time string """
     try:
         the_time = Time(value)
         the_time.format = "iso"
         return the_time
     except ValueError:
         return self.error(obj, value)
Пример #30
0
def utc2mjd(dt):
    """
    Converts UTC values to MJD datetime objects
    """

    t = Time(dt, format='datetime')
    t.format = 'mjd'
    return t.value
Пример #31
0
def mjd2utc(mjd):
    """
    Converts MJD values to UTC datetime objects
    """

    t = Time(mjd, format='mjd')
    t.format = 'datetime'
    return t.value
Пример #32
0
def convertMJD(mjd):
    '''  Converts MJD to weekday ,date, month, year    '''
    mydate=Time(mjd,format='mjd')
    mydate.format='fits'
    tdate= date(int(mydate.value[0:4]),int(mydate.value[5:7]),int(mydate.value[8:10]))
    month = calendar.month_name[tdate.month]  
    weekday = calendar.day_name[tdate.weekday()] 
    ret_string = '{0} {1}-{2}-{3} '.format(weekday,int(mydate.value[8:10]),month,int(mydate.value[0:4]))
    return ret_string
Пример #33
0
def test_strftime_scalar():
    """Test of Time.strftime
    """
    time_string = '2010-09-03 06:00:00'
    t = Time(time_string)

    for format in t.FORMATS:
        t.format = format
        assert t.strftime('%Y-%m-%d %H:%M:%S') == time_string
Пример #34
0
def get_member_info(object_name, filtertype='r', imagetype='p'):
    """
    Query the ssois ephemeris for images of a given object. Then parse through for desired image type, 
    filter, exposure time, and telescope instrument
    """

    # From the given input, identify the desired filter and rename appropriately                    Replace this?
    if filtertype.lower().__contains__('r'):
        filtertype = 'r.MP9601'  # this is the old (standard) r filter for MegaCam
    if filtertype.lower().__contains__('u'):
        filtertype = 'u.MP9301'

    # Define time period of image search, basically while MegaCam in operation
    search_start_date = Time('2013-01-01', scale='utc')  # epoch1=2013+01+01
    search_end_date = Time('2017-01-01', scale='utc')  # epoch2=2017+1+1

    print "----- Searching for images of object {}".format(object_name)

    query = Query(object_name, search_start_date=search_start_date, search_end_date=search_end_date)
    try:
        objects = parse_ssois_return(query.get(), object_name, imagetype, camera_filter=filtertype)
    except IOError:
        print "Sleeping 30 seconds"
        time.sleep(30)
        objects = parse_ssois_return(query.get(), object_name, imagetype, camera_filter=filtertype)

    # Setup output, label columns
    if len(objects)>0:
        output = '{}/{}_object_images.txt'.format(_OUTPUT_DIR, object_name)
        with open(output, 'w') as outfile:
            outfile.write("{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\n".format(
                "Object", "Image", "Exp_time", "RA (deg)", "Dec (deg)", "time", "filter", "RA rate (\"/hr)", "Dec rate (\"/hr)"))

        mjds = []
        for line in objects:
            mjds.append(float(line['MJD'])) #Have to convert elements to floats
        start_time = Time(min(mjds), format='mjd') - 1.0*units.minute
        stop_time = Time(max(mjds), format='mjd') + 1.0*units.minute

        #Query Horizons once to establish position values over given time period, then give it a current time which it interpolates withl
        body = horizons.Body(object_name, start_time=start_time, stop_time=stop_time, step_size=10 * units.minute)

        for line in objects:
            with open(output, 'a') as outfile:
                time = Time(line['MJD'], format='mjd', scale='utc')
                time.format = 'iso'
                body.current_time = time
                p_ra = body.coordinate.ra.degree  
                p_dec = body.coordinate.dec.degree 
                ra_dot = body.ra_rate.to(units.arcsecond/units.hour).value
                dec_dot = body.dec_rate.to(units.arcsecond/units.hour).value
                try:
                    outfile.write("{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\n".format(
                        object_name, line['Image'], line['Exptime'], p_ra, p_dec,
                        Time(line['MJD'], format='mjd', scale='utc'), line['Filter'], ra_dot, dec_dot))
                except Exception, e:
                    print "Error writing to outfile: {}".format(e)
Пример #35
0
def test_strftime_array_2():
    tstrings = [['1998-01-01 00:00:01', '1998-01-01 00:00:02'],
                ['1998-01-01 00:00:03', '1995-12-31 23:59:60']]
    tstrings = np.array(tstrings)

    t = Time(tstrings)

    for format in t.FORMATS:
        t.format = format
        assert np.all(t.strftime('%Y-%m-%d %H:%M:%S') == tstrings)
        assert t.strftime('%Y-%m-%d %H:%M:%S').shape == tstrings.shape
Пример #36
0
def plot_magnitudes(mags=None, errors=None, times=None,
                    source=None, night=None, ref_mag=0,
                    alpha=0.25, y_range=None):
    """
    Plot one night of magnitude data for one source, overlaying a rolling
    mean and indication of mean/deviation.
    """
    mean = np.nanmean(mags)
    std = np.nanstd(mags)

    working_times = times # - night
    plt.errorbar(working_times, mags, yerr=errors, fmt='o', alpha=alpha,
                 label='night: {}'.format(night))
    plt.xlim(working_times.min(), working_times.max())
    plt.plot(plt.xlim(), [mean, mean], 'k--', )
    plt.plot(plt.xlim(), [mean + std, mean + std], 'k:')
    plt.plot(plt.xlim(), [mean - std, mean - std], 'k:')
    plt.plot(pd.rolling_mean(working_times, 20, center=True),
             pd.rolling_mean(mags, 20, center=True),
             color='gray', linewidth=3)

    if y_range:
        plt.ylim(y_range)
    else:
        # Make sure plot range is at least 0.1 mag...
        min_range = 0.1
        ylim = plt.ylim()
        if ylim[1] - ylim[0] < min_range:
            plt.ylim(mean - min_range/2, mean + min_range/2)

    ylim = plt.ylim()
    # Reverse vertical axis so brighter is higher
    plt.ylim((ylim[1], ylim[0]))

    # # Add marker indicating brightness of star.
    # size = 1000./(mean - ref_mag + 0.1)**2
    # plt.scatter([0.8*(plt.xlim()[1]-plt.xlim()[0]) + plt.xlim()[0]],
    #             [0.8*(plt.ylim()[1] - plt.ylim()[0]) + plt.ylim()[0]],
    #             c='red', marker='o', s=size)

    # plt.legend()
    calendar_date = Time(night, format='jd', out_subfmt='date')
    calendar_date.format = 'iso'

    plt.title('night {}'.format(calendar_date))
    plt.xlabel('time (days)')
    return mean, std
Пример #37
0
def convert_time(mytime2):
    
    """ This Function is convert the date we have into MJD units ..
        exp: 131009= 2013 October 9
        >>> 2012-10-09 (this is the format that Time function in astopry accepts.
        >>> 50987.00 is the final time
        
    """

    word2='20'
    for i in range(0,len(mytime2),2):
        word2+=str(mytime2[i]+mytime2[i+1])+'-'
    word2=word2[:-1]
    t=Time(word2)
    t.format='mjd'
    result=t.value
    return result
Пример #38
0
    def on_save(self, path=None):
        """
        This is a hook that is called just before saving the file.
        It can be used, for example, to update values in the metadata
        that are based on the content of the data.

        Override it in the subclass to make it do something, but don't
        forget to "chain up" to the base class, since it does things
        there, too.

        Parameters
        ----------
        path : str
            The path to the file that we're about to save to.
        """
        if isinstance(path, str):
            self.meta.filename = os.path.basename(path)

        current_date = Time(datetime.datetime.now())
        current_date.format = 'isot'
        self.meta.date = current_date.value
Пример #39
0
def convert_time(start_time, exposure):
    """Convert a start time and exposure length into an mjd_start and mjd_end
    time."""
    logging.debug('Begin convert_time.')
    if start_time is not None and exposure is not None:
        logging.debug(
            'Use date {} and exposure {} to convert time.'.format(start_time,
                                                                  exposure))
        if type(start_time) is float:
            t_start = Time(start_time, format='mjd')
        else:
            t_start = Time(start_time)
        dt = TimeDelta(exposure, format='sec')
        t_end = t_start + dt
        t_start.format = 'mjd'
        t_end.format = 'mjd'
        mjd_start = t_start.value
        mjd_end = t_end.value
        logging.debug('End convert_time mjd start {} mjd end {} .'.format(
            mjd_start, mjd_end))
        return mjd_start, mjd_end
    return None, None
Пример #40
0
    def __init__(self, init=None, schema=None,
                 pass_invalid_values=False, strict_validation=False,
                 **kwargs):
        """
        Parameters
        ----------
        init : shape tuple, file path, file object, astropy.io.fits.HDUList, numpy array, None

            - None: A default data model with no shape

            - shape tuple: Initialize with empty data of the given
              shape

            - file path: Initialize from the given file (FITS or ASDF)

            - readable file object: Initialize from the given file
              object

            - ``astropy.io.fits.HDUList``: Initialize from the given
              `~astropy.io.fits.HDUList`.

            - A numpy array: Used to initialize the data array

            - dict: The object model tree for the data model

        schema : tree of objects representing a JSON schema, or string naming a schema, optional
            The schema to use to understand the elements on the model.
            If not provided, the schema associated with this class
            will be used.

        pass_invalid_values: If true, values that do not validate the schema
            will be added to the metadata. If false, they will be set to None

        strict_validation: if true, an schema validation errors will generate
            an excption. If false, they will generate a warning.

        kwargs: Aadditional arguments passed to lower level functions
        """

        # Override value of validation parameters
        # if environment value set
        self._pass_invalid_values = self.get_envar("PASS_INVALID_VALUES",
                                                    pass_invalid_values)
        self._strict_validation = self.get_envar("STRICT_VALIDATION",
                                                 strict_validation)

        # Load the schema files
        if schema is None:
            schema_path = os.path.join(URL_PREFIX, self.schema_url)
            # Create an AsdfFile so we can use its resolver for loading schemas
            asdf_file = AsdfFile()
            schema = asdf_schema.load_schema(schema_path,
                                             resolver=asdf_file.resolver,
                                             resolve_references=True)

        self._schema = mschema.merge_property_trees(schema)

        # Provide the object as context to other classes and functions
        self._ctx = self

        # Determine what kind of input we have (init) and execute the
        # proper code to intiailize the model
        self._files_to_close = []
        self._iscopy = False
        is_array = False
        is_shape = False
        shape = None

        if init is None:
            asdffile = self.open_asdf(init=None, **kwargs)

        elif isinstance(init, dict):
            asdffile = self.open_asdf(init=init, **kwargs)

        elif isinstance(init, np.ndarray):
            asdffile = self.open_asdf(init=None, **kwargs)

            shape = init.shape
            is_array = True

        elif isinstance(init, tuple):
            for item in init:
                if not isinstance(item, int):
                    raise ValueError("shape must be a tuple of ints")

            shape = init
            is_shape = True
            asdffile = self.open_asdf(init=None, **kwargs)

        elif isinstance(init, DataModel):
            asdffile = None
            self.clone(self, init)
            if not isinstance(init, self.__class__):
                self.validate()
            return

        elif isinstance(init, AsdfFile):
            asdffile = init

        elif isinstance(init, fits.HDUList):
            asdffile = fits_support.from_fits(init, self._schema, self._ctx)

        elif isinstance(init, (str, bytes)):
            if isinstance(init, bytes):
                init = init.decode(sys.getfilesystemencoding())
            file_type = filetype.check(init)

            if file_type == "fits":
                hdulist = fits.open(init)
                asdffile = fits_support.from_fits(hdulist,
                                              self._schema,
                                              self._ctx,
                                              **kwargs)
                self._files_to_close.append(hdulist)

            elif file_type == "asdf":
                asdffile = self.open_asdf(init=init, **kwargs)

            else:
                # TODO handle json files as well
                raise IOError(
                        "File does not appear to be a FITS or ASDF file.")

        else:
            raise ValueError(
                "Can't initialize datamodel using {0}".format(str(type(init))))

        # Initialize object fields as determined from the code above
        self._shape = shape
        self._instance = asdffile.tree
        self._asdf = asdffile

        # Initalize class dependent hidden fields
        self._no_asdf_extension = False

        # Instantiate the primary array of the image
        if is_array:
            primary_array_name = self.get_primary_array_name()
            if not primary_array_name:
                raise TypeError(
                    "Array passed to DataModel.__init__, but model has "
                    "no primary array in its schema")
            setattr(self, primary_array_name, init)

        if is_shape:
            if not self.get_primary_array_name():
                raise TypeError(
                    "Shape passed to DataModel.__init__, but model has "
                    "no primary array in its schema")

        # if the input is from a file, set the filename attribute
        if isinstance(init, str):
            self.meta.filename = os.path.basename(init)
        elif isinstance(init, fits.HDUList):
            info = init.fileinfo(0)
            if info is not None:
                filename = info.get('filename')
                if filename is not None:
                    self.meta.filename = os.path.basename(filename)

        # if the input model doesn't have a date set, use the current date/time
        if not self.meta.hasattr('date'):
            current_date = Time(datetime.datetime.now())
            current_date.format = 'isot'
            self.meta.date = current_date.value

        # store the data model type, if not already set
        klass = self.__class__.__name__
        if klass != 'DataModel':
            if not self.meta.hasattr('model_type'):
                self.meta.model_type = klass

        # initialize arrays from keyword arguments when they are present

        for attr, value in kwargs.items():
            if value is not None:
                subschema = properties._get_schema_for_property(self._schema,
                                                                attr)
                if 'datatype' in subschema:
                    setattr(self, attr, value)
Пример #41
0
    parser.add_argument('--norb',type=int,help='Number of orbits in sky plot',default=100)
    parser.add_argument('--Qmax',type=float,help='Max apocenter to plot',default=1000.0)

    parser.add_argument('--interactive','-i',action='store_true',help='Interactive plot')

    parser.add_argument('--zvzfile',type=str,help='zvz file name',default='zvz.png')
    parser.add_argument('--elemfile',type=str,help='elem file name',default='elem_noerr.png')
    parser.add_argument('--skyfile',type=str,help='sky orbits file name',default='sky.png')
    parser.add_argument('--pickle_zvz',action='store_true',help='pickle z/vz data')
    parser.add_argument('--pickle_zvz_file',type=str,help='pickle file name',default='zvz.pkl')

    args = parser.parse_args()

    # dt using astropy Time
    d = Time(args.date2)-Time(args.date1)
    d.format = 'jd'
    dt = d.value/365.25

    # compute the basic parameters
    if args.sep1 != None:
        R,V,B,phi,pa0,zsgn = seppa2rvbphi(args.sep1,args.pa1,args.sep2,args.pa2,args.mass,dt,args.distance)
        titlestr =  'Input parameters\n$S_1:'+str(args.sep1)+'$"  $PA_1:'+str(args.pa1)+'^\circ$  $D_1$:'+str(args.date1)+'    $S_2:'+str(args.sep2)+'$"  $PA_2:'+str(args.pa2)+'^\circ$  $D_2$:'+str(args.date2)+'    $M_\star:'+str(args.mass)+'M_\odot$  $d:'+str(args.distance)+'pc$\n\n\n'
    else:
        R,V,B,phi,pa0,zsgn = cart2rvbphi(args.N1,args.E1,args.N2,args.E2,args,mass,dt,args.distance)
        titlestr =  'Input parameters\n$N_1:'+str(args.N1)+'$"  $E_1:'+str(args.E1)+'$"  $D_1$:'+str(args.date1)+'    $N_2:'+str(args.N2)+'$"  $E_2:'+str(args.E2)+'$"  $D_2$:'+str(args.date2)+'    $M_\star:'+str(args.mass)+'M_\odot$  $d:'+str(args.distance)+'pc$\n\n\n'

    print('')
    print('R =', R, 'au')
    print('V =', V, 'au / yr')
    print('B =', B)
    print('phi =', phi/np.pi*180, 'deg')
Пример #42
0
def match_wcs(fiss_file,smooth=False,**kwargs):
    """
    Match the wcs information of FISS files with the SDO/HMI file.
    
    Parameters
    ----------
    fiss_file : str or list
        A single of fiss file or the list of fts file.
    sdo_file : (optional) str
        A SDO/HMI data to use for matching the wcs.
        If False, then download the HMI data on the VSO site.
    dirname : (optional) str
        The directory name for saving the npz data.

        If False, the dirname is the present working directory.
    filename : (optional) str
        The file name for saving the npz data.
        There are no need to add the extension.
        If False, the filename is the date of FISS data.
    sil : (optional) bool
        If False, it print the ongoing time index.
        Default is True.
    sdo_path : (optional) str
        The directory name for saving the HMI data.
    method : (optioinal) bool
        If True, then manually match the wcs.
        If False, you have a no choice to this yet. kkk.
    wvref : (optional) float
        The referenced wavelength for making raster image.
    reflect : (optional) bool
        Correct the reflection of FISS data.
        Default is True.
    alpha : (optional) float
        The transparency of the image to 
    missing : (optional) float
        The extrapolated value of interpolation.
        Default is -1.
        
    Returns
    -------
    match_angle : float
        The angle to rotate the FISS data to match the wcs information.
    wcsx : float
        The x-axis value of image center in WCS arcesec unit.
    wcsy : float
        The y-axis value of image center in WCS arcesec unit.
    
    
    Example
    -------
    >>> from glob import glob
    >>> from fisspy.image import coalignment
    >>> file=glob('*_A1_c.fts')
    >>> dirname='D:\\im\\so\\hot'
    >>> sdo_path='D:\\im\\sdo\\path'
    >>> coalignment.match_wcs(file,dirname=dirname,sil=False,
                              sdo_path=sdo_path)
    
    """
    ref_frame=kwargs.get('ref_frame',len(fiss_file)//2)
    kwargs['ref_frame']=ref_frame
    fiss_file0=fiss_file[ref_frame]
    sdo_file=kwargs.pop('sdo_file',False)
    sil=kwargs.get('sil',False)
    sdo_path=kwargs.pop('sdo_path',os.getcwd())
    
    if not sdo_file:
        h=getheader(fiss_file0)
        tlist=h['date']
        t=Time(tlist,format='isot',scale='ut1')
        tjd=t.jd
        t1=tjd-22/24/3600
        t2=tjd+22/24/3600
        t1=Time(t1,format='jd')
        t2=Time(t2,format='jd')
        t1.format='isot'
        t2.format='isot'
        hmi=(a.Time(t1.value,t2.value),
             a.Instrument('HMI'),
             a.vso.Physobs('intensity'))
        res=Fido.search(hmi)
        if not sil:
            print('Download the SDO/HMI file')
        sdo_file=Fido.fetch(res,path=os.path.join(sdo_path, '{file}'))
        sdo_file=sdo_file[0]
        
        if not sil:
            print('SDO/HMI file name is %s'%sdo_file)
    manual(fiss_file,sdo_file,**kwargs)
    return
Пример #43
0
def main(fname, output=False, separator=":", verbose=True):

    # verbose printing on flag raise
    v_print = print if verbose else lambda *a, **k: None

    ############### Observation Settings
    ospath = os.getcwd() + "/"
    
    path = "/".join(fname.split("/")[:-1]) #+ "/"
    print("Path = {}",path)

    nod_dates = []
    nod_slits = []
    nod_exptimes = []
    nod_instruments = []
    nod_object = []
    nod_filter = []
    nod_airmass = []

    with open(fname, "r") as f:
        for line in f:

            #Replace ":"" in list file with "-"
            if separator != ":":
                line_parts = line.split(":")
                line = separator.join(line_parts)

            fitsname = line[:-1] + ".fits"
            v_print("The fits file to open is" , fitsname)
            try:
                v_print("Trying location = {}".format(path + "../Raw_files/" + fitsname))
                header = fits.getheader(path + "../Raw_files/" + fitsname)
            except:
                try:
                    v_print("Trying location = {}".format(path + "Raw_files/" + fitsname))
                    header = fits.getheader(path + "Raw_files/" + fitsname)
                except:
                    try:
                        header = fits.getheader(path + fitsname)
                    except:
                        v_print("Having issues finding files from list.")
                        raise

            obsdate = header["DATE-OBS"]
            nod_dates.append(obsdate)
            nod_slits.append(header["HIERARCH ESO INS SLIT1 WID"])
            nod_exptimes.append(header["EXPTIME"])
            nod_instruments.append(header["INSTRUME"])
            nod_object.append(header["OBJECT"])
            nod_filter.append(header["ESO INS FILT1 NAME"])

            # AIRMASS values
            airmass_start = header["HIERARCH ESO TEL AIRM START"]
            airmass_end = header["HIERARCH ESO TEL AIRM END"]
            nod_mean_airmass = round((airmass_start + airmass_end) / 2 , 4)
            nod_airmass.append(nod_mean_airmass)
            #ESO TEL AIRM END: 1.204
            #ESO TEL AIRM START: 1.214

        
    v_print("Values obtained from the list files")
    v_print("The nod date-obs = {} ".format(nod_dates))
    v_print("The nod Slit Widths = {}".format(nod_slits))
    v_print("The nod Exposure times = {} seconds".format(nod_exptimes))
    v_print("The listed Instruments = {} ".format(nod_instruments))
        
       
    if len(set(nod_instruments)) == 1:
        instrument = nod_instruments[0]
    else:
        #raise error as list intruments are not unique
        raise NodError("Nods in list were not taken on the same instrument")
    if len(set(nod_exptimes)) == 1:
        exptime = nod_exptimes[0]
    else:
        #raise error as list exptime are not unique
        raise NodError("Nods in the list do not have same exposure times")
    if len(set(nod_slits)) == 1:
        slit_width = nod_slits[0]
    else:
        #raise error as list exptime are not unique
        raise NodError("Nods in list do not have same slit widths")
    # Get average time of observation (adding exposure time)
    if len(set(nod_object)) == 1:
        Object = nod_object[0]
    else:
        #raise error as list exptime are not unique
        raise NodError("Nods in list are not of the Object")
    
    if len(set(nod_filter)) == 1:
        nod_filter = nod_filter[0]
    else:
        #raise error as list exptime are not unique
        raise NodError("Nods in list are not of the Object")
  
    

    jd_days = []
    for nod_date in nod_dates:
        t = Time(nod_date, format="fits") + TimeDelta(exptime/2, format="sec")
        t.format= 'jd'
        jd_days.append(t.value)
    v_print("Nod dates converted into JD {}".format(jd_days))
    if max(jd_days)-min(jd_days) > len(jd_days)*2*exptime/86400.:
        raise NodError("Issue with time for the nod observations took longer " \
            "then twice exposure time for each exposure (maybe need to add a lower" \
            " limit for quick observations) ")
    
    mean_obs_time = np.mean(jd_days)
    median_obs_time = np.median(jd_days)

    obs_begin_time = Time(nod_dates[0], format="fits")
    print(" obs_begin_time",  obs_begin_time, "nod_dates[0]", nod_dates[0])
    obs_end_time = Time(nod_dates[-1], format="fits") + TimeDelta(exptime, format="sec")
    
    mean_obs_t = Time(mean_obs_time, format="jd")
    mean_obs_t.format = "fits"
    med_obs_t = Time(median_obs_time, format="jd")
    med_obs_t.format = "fits"

    obs_begin_time2 = Time(obs_begin_time)
    obs_end_time2 = Time(obs_end_time)
    obs_begin_time2.format = "jd"
    obs_end_time2.format = "jd"
    obs_total = obs_end_time2-obs_begin_time2         
    obs_total_minutes = obs_total * 24*60
   
    Middle_obs = obs_begin_time2 + TimeDelta(60*obs_total_minutes/2, format="sec")
    Middle_obs.format = "fits"
    Middle_obs2 = Time(Middle_obs)
    Middle_obs2.format = "jd"

    v_print("Mean obs time averged in JD {}".format(mean_obs_time))
    v_print("Median obs time in JD {}".format(median_obs_time))
    v_print("Begin obs time in JD {}".format(obs_begin_time))
    v_print("End obs time in JD {}".format(obs_end_time))

    
    target_ra = header["RA"]   # of the last observation in the list
    target_dec = header["DEC"]

    obs_wl_min = header["HIERARCH ESO INS WLEN MIN"]
    obs_wl_max = header["HIERARCH ESO INS WLEN MAX"] 

    # Print header values to find the ones most interested in.
    ##for key in header:
        #print(str(key) + ": " + str(header[key]))
    
    
    Obs_name = header["ESO OBS NAME"]
    
    Nabcycles = header["ESO SEQ NABCYCLES"] # ESO SEQ NABCYCLES: 4
    NEXP = header["ESO TPL NEXP"] # ESO TPL NEXP: 8
    
    # Observation IDs
    obs_pi_id= header["ESO OBS PI-COI ID"]
    prosal_id= header["ESO OBS PROG ID"]
    tpl_id = header["ESO TPL ID"]  #CRIRES_spec_obs_AutoNodOnSlit
    ao_loop_state = header["ESO AOS RTC LOOP STATE"]


    #Airmass calculation
    mean_airmass = np.mean(nod_airmass)
    airmass_range = np.max(nod_airmass)-np.min(nod_airmass)

    ####### Observatory Settings 
    instrument = header["INSTRUME"]
    telescope = header["TELESCOP"]
    if "VLT" in telescope:
        obs_name = "ESO Paranal Chile (VLT)"
    else:
        v_print("Don't have TAPAS telecope location name (Using the Obervation value)")
        obs_name = telescope

    obs_long = header["HIERARCH ESO TEL GEOLON"]
    obs_lat = header["HIERARCH ESO TEL GEOLAT"]
    obs_alt = header["HIERARCH ESO TEL GEOELEV"]

    ####### Target Settings
    ra_angle = Angle(target_ra, u.degree)
    ra_j2000 = str(ra_angle.to_string(unit=u.hour, sep=':', precision=0, pad=True))  # Extra str to get rid of u"string" which failed in template
    dec_angle = Angle(target_dec, u.deg)
    dec_j2000 = str(dec_angle.to_string(unit=u.degree, sep=':', precision=0, pad=True)) # Extra str to get rid of u"string" which failed in template
    v_print("RA degrees = {0}, RA Angle = {1}, RA for tapas = {2}".format(target_ra, ra_angle, ra_j2000))
    v_print("DEC degrees = {0}, DEC Angle = {1}, DEC for tapas = {2}".format(target_dec, dec_angle, dec_j2000))

        
    # Resolving power rule of thumb
    R = 100000*0.2 / slit_width
            
    resolving_power = int(R)
    print("Resolving Power = {}".format(resolving_power))

    



    if output:
       output_file = output
    else: 
        output_file = "key_observation_values.txt"
    try:
        with open(output_file, "w") as out:
############################################################
            #Put the useful parameters I need to know here
            
            out.write("KEY CRIRES OBSERVATION PARAMETERS:\n------------------\n")
            out.write("Target Name           = {}\n".format(Object))
            out.write("Observation Name      = {}\n".format(Obs_name))
            out.write("RA                    = {}, {} \n".format(ra_angle, ra_j2000))
            out.write("DEC                   = {}, {} \n".format(dec_angle, dec_j2000))
            
            out.write("\nOBS IDs:\n------------------\n")
            out.write("Proposal ID           = {}\n".format(prosal_id))
            out.write("PI-COI ID             = {}\n".format(obs_pi_id))
            out.write("Obs Template ID       = {}\n".format(tpl_id))
            out.write("AO State(OPEN=noAo)   = {}\n".format(ao_loop_state))

            out.write("\nDetector Setup:\n------------------\n")
            out.write("Telescope             = {}\n".format(telescope))
            out.write("Instrument            = {}\n".format(instrument))
            out.write("Spec Filter           = {}\n".format(nod_filter))
            out.write("Min wavelength        = {} nm\n".format(obs_wl_min))
            out.write("Max Wavelength        = {} nm\n".format(obs_wl_max))
            out.write("Slit width            = {} \n".format(slit_width))
            out.write("Resolving Power       = {}\n".format(resolving_power))
            out.write("Exposure time         = {}\n".format(exptime))   
            out.write("Number AB cycles      = {}\n".format(Nabcycles))
            out.write("Number of Exposures   = {}\n".format(NEXP))
                 
            out.write("\nTime Calculations:\n------------------\n")
            # FITS Format
            out.write("## FITS format\n")
            out.write("Obs Start time        = {}\n".format(obs_begin_time))
            out.write("Average obs time      = {}\n".format(mean_obs_t))
            out.write("Median obs time       = {}\n".format(med_obs_t))
            #out.write("Middle of obs         = {} \n".format(Middle_obs))
            out.write("Obs End time          = {}\n".format(obs_end_time))
            obs_begin_time.format = "jd"
            obs_end_time.format = "jd"
            # JD Format
            out.write("## JD format\n")
            out.write("Obs start time (JD)   = {}\n".format(obs_begin_time2))
            out.write("Average obs time (JD) = {}\n".format(mean_obs_time))      
            out.write("Medaian obs time (JD) = {}\n".format(median_obs_time))
            #out.write("Middle of obs (JD)    = {}\n".format(Middle_obs2))
            out.write("Obs End time (JD)     = {}\n".format(obs_end_time2))
            
            out.write("Total observation time  [inc. exptime] (JD)  = {} \n".format(obs_total))
            out.write("Total observation time  [inc. exptime] (min) = {} \n".format(obs_total_minutes))
            out.write("Total intergration time [NEXP*exptime] (min) = {} \n".format(exptime*NEXP/60))
      
            out.write("\nAirmass values:\n------------------\n")
            out.write("Nod airmasses = {}\n".format(nod_airmass))
            out.write("Mean airmass valu       = {}\n".format(mean_airmass))
            out.write("Airmass range [max-min] = {}\n".format(airmass_range))
            
            # DATE CREATED
            out.write("\n\nThis file was generated on {}\n".format(datetime.datetime.now()))
            

#############################################################
        print("Saved Observation information to \t {0}".format(output_file))
    except:
        print("Failed to save Observation information to \t {0}.".format(output_file))
        raise
def main(fname="/home/jneal/Phd/data/Crires/BDs-DRACS/HD30501-1/Combined_Nods/CRIRE.2012-04-07T00:08:29.976_1.nod.ms.norm.sum.wavecal.fits", 
        listspectra=False, resolvpower=False, unit="vacuum", instrument_function="gaussian",
        sampling=10, berv=False, tapas_format="ASCII", constituents="all", output_file=False,
        request_id=10, wl_min=False, wl_max=False, request_number=None, verbose=False):
    
    # verbose printing on flag raise
    v_print = print if verbose else lambda *a, **k: None

    
    # Dotfile for saving requst number for iteration  
    home = os.path.expanduser("~")
    dot_file = os.path.join(home, ".tapas_request_num")
    
    ############### Observation Settings
    ospath = os.getcwd() + "/"
    v_print("OS path {}".format(ospath))

    path = "/".join(fname.split("/")[:-1]) + "/"
    v_print("Path obtained from fname = {}".format(path))

    if listspectra:
        nod_dates = []
        nod_slits = []
        nod_exptimes = []
        nod_instruments = []
        with open(fname, "r") as f:
            for line in f:
                fitsname = line[:-1] + ".fits"
                v_print("The fits file to open is" ,fitsname)
                try:
                    v_print("Trying location{}".format(path + "../Raw_files/" + fitsname))
                    header = fits.getheader(path + "../Raw_files/" + fitsname)
                except:
                    try:
                        v_print("Trying location  = {}".format(path + "Raw_files/" + fitsname))
                        header = fits.getheader(path + "Raw_files/" + fitsname)
                    except:
                        try:
                            header = fits.getheader(path + fitsname)
                        except:
                            v_print("Having issues finding files from list.")
                            raise

                obsdate = header["DATE-OBS"]
                nod_dates.append(obsdate)
                nod_slits.append(header["HIERARCH ESO INS SLIT1 WID"])
                nod_exptimes.append(header["EXPTIME"])
                nod_instruments.append(header["INSTRUME"])
            v_print("Values obtained from the list files")
            v_print("The nod date-obs = {} ".format(nod_dates))
            v_print("The nod Slit Widths = {}".format( nod_slits))
            v_print("The nod Exposure times = {} seconds".format(nod_exptimes))
            v_print("The listed Instruments = {} ".format(nod_instruments))
            
            if len(set(nod_instruments)) == 1:
                instrument = nod_instruments[0]
            else:
                #raise error as list intruments are not unique
                raise NodError("Nods in list were not taken on the same instrument")
            if len(set(nod_exptimes)) == 1:
                exptime = nod_exptimes[0]
            else:
                #raise error as list exptime are not unique
                raise NodError("Nods in the list do not have same exposure times")
            if len(set(nod_slits)) == 1:
                slit_width = nod_slits[0]
            else:
                #raise error as list exptime are not unique
                raise NodError("Nods in list do not have same slit widths")
            # Get average time of observation (adding exposure time)
            jd_days = []
            for nod_date in nod_dates:
                t = Time(nod_date, format="fits") + TimeDelta(exptime/2, format="sec")
                t.format= 'jd'
                jd_days.append(t.value)
            v_print("Nod dates converted into JD {}".format(jd_days))
            if max(jd_days)-min(jd_days) > len(jd_days)*2*exptime/86400.:
                raise NodError("Issue with time for the nod observations took longer then twice exposure time for each exposure (maybe need to add a lower limit for quick observations) ")
            mean_obs_time = np.mean(jd_days)
            v_print("Mean obs time averged in JD {}".format(mean_obs_time))
            v_print("Median obs time in JD {}".format(np.median(jd_days)))
            date = Time(mean_obs_time, format="jd")
            date.format = "fits"  # Turn back into fits format
            date = date.value[:-5] + "Z"    # Going from Time object back to string and adding Z to end for Tapas
            target_ra = header["RA"]   # of the last observation in the list
            target_dec = header["DEC"]
            obs_wl_min = header["HIERARCH ESO INS WLEN MIN"]
            obs_wl_max = header["HIERARCH ESO INS WLEN MAX"] 
    else:
        header = fits.getheader(fname)
    
        date = header["DATE-OBS"][:-1]+"Z"     
        target_ra = header["RA"]
        target_dec = header["DEC"]
        obs_wl_min = header["HIERARCH ESO INS WLEN MIN"]
        obs_wl_max = header["HIERARCH ESO INS WLEN MAX"] 
        slit_width = header["HIERARCH ESO INS SLIT1 WID"]

    ####### Observatory Settings 
    instrument = header["INSTRUME"]
    telescope = header["TELESCOP"]
    if "VLT" in telescope:
    	obs_name = "ESO Paranal Chile (VLT)"
    else:
    	v_print("Don't have TAPAS telecope location name (Using the Obervation value)")
    	obs_name = telescope

    obs_long = header["HIERARCH ESO TEL GEOLON"]
    obs_lat = header["HIERARCH ESO TEL GEOLAT"]
    obs_alt = header["HIERARCH ESO TEL GEOELEV"]

    ####### Target Settings
    ra_angle = Angle(target_ra, u.degree)
    ra_j2000 = str(ra_angle.to_string(unit=u.hour, sep=':', precision=0, pad=True))  # Extra str to get rid of u"string" which failed in template
    dec_angle = Angle(target_dec, u.deg)
    dec_j2000 = str(dec_angle.to_string(unit=u.degree, sep=':', precision=0, pad=True)) # Extra str to get rid of u"string" which failed in template
    v_print("RA degrees = {0}, RA Angle = {1}, RA for tapas = {2}".format(target_ra, ra_angle, ra_j2000))
    v_print("DEC degrees = {0}, DEC Angle = {1}, DEC for tapas = {2}".format(target_dec, dec_angle, dec_j2000))

    if wl_min:
        spec_range_min = int(wl_min)
    else:
        spec_range_min = int(obs_wl_min - 10)
    if wl_max:
        spec_range_max = int(wl_max)
    else:
        spec_range_max = int(obs_wl_max + 10) 
    
    # Check for TAPAS consistency    
    #Tapas wavelength range is from 350 to 2500 nm in vacuum.
    if spec_range_min < 350:
        v_print("Lower wavelength bound of {} was below tapas minimum. Setting to tapas minimum of 350 nm".format(spec_range_min))
        spec_range_min = 350
    elif spec_range_min > 2500:
        v_print("Wavelength Lower bound of {} is above Tapas maximum wavelength of 2500 nm. Check your wavelength units".format(spec_range_min))
        raise("Wavelength bounds Error")
    if spec_range_max > 2500:
        v_print("Upper wavelength bound of {} was above tapas maximum. Setting to tapas maximum of 2500 nm".format(spec_range_max))
        spec_range_max = 2500
    elif spec_range_max < 350:
        v_print("Wavelength Upper bound of {} is below Tapas minimum wavelength of 350 nm. Check your wavelength units".format(spec_range_max))
        raise("Wavelength bounds Error")

    ########### Tapas Specifications
    if tapas_format in ["ascii","fits","vetcdf","vo"]:
          tapas_format = tapas_format.upper()
    else:
        v_print("TAPAS format was not correct, using default of ASCII")
        tapas_format = "ASCII"

# open save file, find request id, add 1
# get request number from previous xml file if not given.




    if not request_number:
        # Load previous tapas request number
        try:
            with open(dot_file, "r") as req:
                old_number = req.readline().split("=")[1]
                v_print("Previous request_number = {}".format(old_number))
                request_number = int(old_number) + 1
                v_print("New request_number number = {}".format(request_number))
        except:
            request_number = 0
            print("Could not read request number from previous request in {0}. The default value of {1} will need to be manually changed when submitting the request.".format(dot_file, request_number))
    else:
        # Manually given request number
        v_print("Manually given request number={0} will be used.\n".format(request_number))
        


    ##### Specify Species in tapas spectra
    #Could possibly make this a binary AND operation
    if constituents == "all":
        species_map = [1,1,1,1,1,1,1]
        species_id = 10
    elif constituents == "ray":
        species_map = [1,0,0,0,0,0,0]
        species_id = 11
    elif constituents == "h2o":
        species_map = [0,1,0,0,0,0,0]
        species_id = 12
    elif constituents == "o3":
        species_map = [0,0,1,0,0,0,0]
        species_id = 13
    elif constituents == "o2":
        species_map = [0,0,0,1,0,0,0]
        species_id = 14
    elif constituents == "co2":
        species_map = [0,0,0,0,1,0,0]
        species_id = 15
    elif constituents == "ch4":
        species_map = [0,0,0,0,0,1,0]
        species_id = 16
    elif constituents == "n2o":
        species_map = [0,0,0,0,0,0,1]
        species_id = 17
    elif constituents == "not_h2o":
        species_map = [1,0,1,1,1,1,1]
        species_id = 18
    else:
        species_id = 9999
        raise SpeciesError("Choice of constituents was not in valid range.")

    # Request id baised on the species present unless manually specified
    if request_id:
        Request_ID = request_id  
    else: 
        Request_ID = species_id

    if species_map[0]:
        ray = "YES"
    else:
        ray = "NO"
    if species_map[1]:
        h20 = "YES"   # alternate yes no for h20 for scaling
    else:
        h20 = "NO"   # alternate yes no for h20 for scaling
    if species_map[2]:
        o2 = "YES"
    else:
        o2 = "NO"
    if species_map[3]:
        o3 = "YES"
    else:
        o3 = "NO"
    if species_map[4]:
        co2 = "YES"
    else:
        co2 = "NO"
    if species_map[5]:
        ch4 = "YES"
    else:
        ch4 = "NO"
    if species_map[6]:
        n2o = "YES"
    else:
        n2o = "NO"    

    if "air" in unit.lower():
        spectral_choice = "Standard Wavelength (nm)" 
    if "vac" in unit.lower():
        spectral_choice = "Vacuum Wavelength (nm)" 
    if "wave" in unit.lower():
        spectral_choice = "Wavenumber (cm-1)"

    instrument_function = instrument_function.lower()
    if "none" in instrument_function:
        ilsf_choice = -1    # -1, 0, 1
    elif "gaussian" in instrument_function:
        ilsf_choice = 1    # -1, 0, 1
    else:
        v_print("Instrument function not specifid correctly\n Valid choices are none or gaussian, The default is gaussian.")

    sampling_ratio = sampling # defualt 10

    if resolvpower:
       resolving_power = int(resolvpower)
       v_print("Resolving power manually specified at {}".format(resolving_power))
    else:
        if "CRIRES" in instrument:
            v_print("Resolving Power\nUsing the rule of thumb equation from the CRIRES manual. \nWarning! The use of adpative optics is not checked for!!")
            R = 100000*0.2 / slit_width
            
            resolving_power = int(R)
            v_print("Slit width was {0} inches.\nTherefore the resolving_power is set = {1}".format(slit_width, resolving_power))
            
            #TO DO Specify other instruments in here?

        else:
            v_print("Resolving power not defined")

    if berv:
        apply_berv = "YES"
    else:
        apply_berv = "NO"


    ###### Atmosphere Parameters
    # hours(date[11:13]) only seem to work if multiple of 06 hours 
    #assuming 00 is for 00->06 hours 
    hour = int(date[11:13])
    if hour >= 0 and hour < 6:
        hour_out = "00"
    elif hour >= 6 and hour < 12:
        hour_out = "06"
    elif hour >= 12 and hour < 18:
        hour_out = "12"
    elif hour >= 18 and hour < 24:
        hour_out = "18"
    else: 
        raise HourError("Error with the arletty timing. The request will fail")

    file_date = date[0:4] + date[5:7] + date[8:10] + hour_out
    arletty_file = "canr_" + file_date + ".arl"
    ecmwf_file = "canr_" + file_date + "_qo3.txt"
    
    v_print("arletty_file", arletty_file)
    v_print("ecmwf_file", ecmwf_file)

    d = {"request_number":request_number, "Request_ID":Request_ID, "tapas_format":tapas_format, "ray":ray, "h20":h20, "o3":o3, "o2":o2, "co2":co2, "ch4":ch4, "n2o":n2o, "date":date, "obs_name":obs_name, "obs_long":obs_long, "obs_lat":obs_lat, "obs_alt":obs_alt, "ra_j2000":ra_j2000, "dec_j2000":dec_j2000, "spectral_choice":spectral_choice, "spec_range_min":spec_range_min, "spec_range_max":spec_range_max, "ilsf_choice":ilsf_choice, "resolving_power":resolving_power, "sampling_ratio":sampling_ratio, "apply_berv":apply_berv, "arletty_file":arletty_file, "ecmwf_file":ecmwf_file}

    template = """<?xml version="1.0" encoding="UTF-8"?>
<tapas Id="Ether_TAPAS_$request_number">
<request Id="$Request_ID">
<preferences>
<format valid="VO,ASCII,FITS,NETCDF">$tapas_format</format>
<rayleigh_extinction valid="YES,NO">$ray</rayleigh_extinction>
<h2o_extinction valid="YES,NO">$h20</h2o_extinction>
<o3_extinction valid="YES,NO">$o3</o3_extinction>
<o2_extinction valid="YES,NO">$o2</o2_extinction>
<co2_extinction valid="YES,NO">$co2</co2_extinction>
<ch4_extinction valid="YES,NO">$ch4</ch4_extinction>
<n2o_extinction valid="YES,NO">$n2o</n2o_extinction>
</preferences>
<observation>
<date>$date</date>
<observatory>
<name>$obs_name</name>
<longitude min="-180" max="180">$obs_long</longitude>
<latitude min="-90" max="90">$obs_lat</latitude>
<altitude min="0" max="10000">$obs_alt</altitude>
</observatory>
<los>
<ra_j2000>$ra_j2000</ra_j2000>
<dec_j2000>$dec_j2000</dec_j2000>
</los>
<instrument>
<spectral_choice valid="Vacuum Wavelength (nm),Standard Wavelength (nm),Wavenumber (cm-1)">$spectral_choice</spectral_choice>
<spectral_range>$spec_range_min $spec_range_max</spectral_range>
<ilsf_choice valid="-1,0,1">$ilsf_choice</ilsf_choice>
<resolving_power min="0">$resolving_power</resolving_power>
<sampling_ratio min="0">$sampling_ratio</sampling_ratio>
<appli_berv valid="YES,NO">$apply_berv</appli_berv>
</instrument>
</observation>
<atmosphere>
<reference valid="0,1,2,3,4,5,6">0</reference>
<arletty_file>/data/tapas///arletty/$arletty_file</arletty_file>
<ecmwf_file>/data/tapas///ecmwf/$ecmwf_file</ecmwf_file>
</atmosphere>
</request> 
</tapas> """



# ECMWF = ARLETTY 
#[{"text":"ECMWF","value":0},{"text":"TROPICAL","value":1},{"text":"MEDIUM_LATITUDE_SUMMER","value":2},{"text":"MEDIUM_LATITUDE_WINTER","value":3},{"text":"SUBARCTIC_SUMMER","value":4},{"text":"SUBARCTIC_WINTER","value":5},{"text":"US_STANDARD_1976","value":6}]

#######################TODO - Missing all in one transmission

#tapasTexts["NM_STANDARD"] = "Standard Wavelength (nm)";
#            tapasTexts["NM_VACUUM"] = "Vacuum Wavelength (nm)";
#            tapasTexts["CM"] = "Wavenumber (cm-1)";
#
#            tapasTexts["NONE"] = "None";
#            tapasTexts["GAUSSIAN"] = "Gaussian";
#            tapasTexts["ECMWF"] = "ARLETTY";
#            tapasTexts["TROPICAL"] = "Tropical";
#            tapasTexts["MEDIUM_LATITUDE_SUMMER"] = "Average latitude summer";
#            tapasTexts["MEDIUM_LATITUDE_WINTER"] = "Average latitude winter";
#            tapasTexts["SUBARCTIC_SUMMER"] = "Subarctic summer";
#            tapasTexts["SUBARCTIC_WINTER"] = "Subarctic winter";
#            tapasTexts["US_STANDARD_1976"] = "Standard US 1976";


    from string import Template
    s = Template(template)

    v_print("Xml Template object = {}".format(s))
    
    sub = s.substitute(d)
    #print(sub)

    # Save xml ouptut for copying to tapas 
    #TO TRY in future - submit straight to tapas

    #output_file = "/home/jneal/Phd/Codes/UsefulModules/Tapas_xml_request_file.xml"

    if not output_file:
        output_file = "tapas_request_{}.xml".format(request_number)
    else:
        # Add request number
        split = output_file.split(".")
        if len(split) == 2:
            output_file = split[0] + "_{}.".format(request_number) + split[1]
        else:
            print("Warning output filename may not be the expected format 'some_name.xml'")
        print("Split output file", split)


    try:
        with open(output_file, "w") as out:
    	    out.write(sub)
        print("Saved tapas xml request to \t {0}".format(output_file))
    except:
        print("Failed to save xml request to \t {0}. \nHere is a printed version.".format(output_file))
        print("\n{}\n".format(sub))

    try:
        with open(dot_file, "w") as req:
            req.write("request_number = {0}".format(request_number))
        v_print("Stored current request number {0} in {1} for later iteration".format(request_number, dot_file))
    except:
        v_print("Failed to store request number {0} in {1} .".format(request_number, dot_file))