Source code for jwst_novt.timeline

import datetime
import re

import numpy as np
import pandas as pd
import requests
from astropy.time import Time
from jwst_gtvt.constants import URL
from jwst_gtvt.jwst_tvt import Ephemeris

from jwst_novt.constants import JWST_MAXIMUM_DATE, JWST_MINIMUM_DATE

__all__ = ["jwst_maximum_date", "timeline"]


[docs] def timeline(ra, dec, start_date=None, end_date=None, instrument=None): """ Retrieve a visibility timeline for NIRSpec and NIRCam. Calls `jwst_gtvt.Ephemeris` for target visibility and position angle (PA). PA values for times at which the target is not visible are set to NaN. Parameters ---------- ra : float Instrument center RA in degrees. dec : float Instrument center Dec in degrees. start_date : astropy.time.Time, optional If not specified, the current time is used. end_date : astropy.time.Time, optional If not specified, the start_date plus one year is used. instrument : {'NIRSpec', 'NIRCam'}, optional If not specified, both NIRSpec and NIRCam data are returned, using the same RA and Dec for the instrument center. Returns ------- timeline_dataframe : pandas.DataFrame Columns are Time (datetime), V3PA (JWST V3 PA), {instrument}_min_PA (minimum PA for instrument), and {instrument}_max_PA (maximum PA for instrument), where instrument may be NIRCAM, NIRSPEC, or both. """ # default start date to now if start_date is None: start_date = Time.now() # default end date to start date + one year if end_date is None: end_date = start_date + datetime.timedelta(days=365) # check for reasonable values if end_date <= start_date: msg = "End date must be later than start date." raise ValueError(msg) if start_date < Time(JWST_MINIMUM_DATE) - datetime.timedelta(days=1): msg = f"No JWST ephemeris available prior to {JWST_MINIMUM_DATE}" raise ValueError(msg) max_date = jwst_maximum_date() if end_date > Time(max_date): msg = f"No JWST ephemeris available after {max_date}" raise ValueError(msg) ephemeris = Ephemeris(start_date=start_date, end_date=end_date) ephemeris.get_fixed_target_positions(str(ra), str(dec)) if instrument is None: instruments = ["NIRSpec", "NIRCam"] else: instruments = [instrument] # get the dataframe from the ephemeris ephem = ephemeris.dataframe times = Time(ephem["MJD"], format="mjd").datetime # when the specified position is not in the Field of Regard (FOR), # set the PA to NaN not_visible = ~ephem["in_FOR"] ephem.loc[not_visible, "V3PA"] = np.nan # relevant data for this application is the V3 position angle (PA) # and NIRSpec and NIRCam min and max PA timeline_data = {"Time": times, "V3PA": ephem["V3PA"]} for ins in instruments: for key in ["min", "max"]: col = ins.upper() + f"_{key}_pa_angle" ephem.loc[not_visible, col] = np.nan timeline_data[col.replace("pa_angle", "PA")] = ephem[col] return pd.DataFrame(timeline_data)
[docs] def jwst_maximum_date(): """Retrieve the last available date for JWST ephemerides.""" # attempt to retrieve an ephemeris for a date too far in the future start_date = JWST_MINIMUM_DATE future_date = "9999-01-01" request_url = URL.format(start_date, future_date) try: # this should return an error message containing the last good date ephemeris_request = requests.get(request_url, timeout=10) # parse the date from the message m = re.match( r".*after A\.D\. (\d{4}-[a-zA-Z]+-\d{1,2})", ephemeris_request.text ) dt = m.groups()[0] end_date = ( datetime.datetime.strptime(dt, "%Y-%b-%d") .astimezone(datetime.UTC) .strftime("%Y-%m-%d") ) except Exception: # if the above fails for any reason, fall back to a known good date end_date = JWST_MAXIMUM_DATE return end_date