def Location(et, ingress, sv, when): Coords = np.ones(3) [tgopos, _] = spice.spkpos(sv.front, et - when, sv.fframe, 'NONE', sv.target) [mexpos, _] = spice.spkpos(sv.front, et - when, sv.fframe, 'NONE', sv.obs) [states, _] = spice.spkezr(sv.target, et - when, sv.fframe, 'NONE', sv.obs) sc2scvector = states[0:3] velocity = states[3:6] relativespeed = np.linalg.norm(velocity) # e9 because we are converting from km to m (SPICE outputs km, but constants in m) veldopp = (relativespeed / constants.c) * 437.1e9 displacement = np.linalg.norm(sc2scvector) sc2scunitvector = np.true_divide(sc2scvector, displacement) # Extract the triaxial dimensions of Mars marsrad = spice.bodvrd(sv.front, 'RADII', 3) # For the ray that connects MEX and TGO, find the point on this ray that is closest to the Martian surface [nearestpoint, alt] = spice.npedln(marsrad[1][0], marsrad[1][1], marsrad[1][2], tgopos, sc2scunitvector) # THERE IS MORE SETTINGS ON THIS [radius, lon, lat] = spice.reclat(nearestpoint) # Rad -> Deg , frame inversion required (hence the negative 180) lon = 180 - (lon * (-180 / math.pi)) lat = lat * (-180 / math.pi) MexNadirTGOAngle = spice.vsep(-mexpos, -sc2scvector) MexNadirTGOAngle = MexNadirTGOAngle * (180 / math.pi) # produce a string of the date and time, because an ephemeris time is not human-readable date_time = spice.timout(et, 'MM-DD HR:MN:SC') ingress_date_time = spice.timout(ingress, 'MM-DD HR:MN:SC') return lon, lat, displacement, nearestpoint, alt, relativespeed, date_time, ingress_date_time, veldopp, MexNadirTGOAngle
def computeDataFrameGeometries(self, data_frames, greg_format_string, julian_format_string): for data_frame in data_frames: self.greg_format_string = greg_format_string self.julian_format_string = julian_format_string greg_split_string = self.greg_format_string.split(' ') data_frame.greg_time_system_string = [ i for i in greg_split_string if '::' in i ][0][2:] julian_split_string = self.julian_format_string.split(' ') data_frame.julian_time_system_string = [ i for i in julian_split_string if '::' in i ][0][2:] current_epoch = data_frame.start_epoch current_epoch = spice.str2et(current_epoch) stop_epoch = spice.str2et(data_frame.stop_epoch) time_remaining = stop_epoch - current_epoch while time_remaining >= 0.0: greg_date_string = spice.timout(current_epoch, self.greg_format_string) JD_date = spice.timout(current_epoch, self.julian_format_string) for body_pair in data_frame.body_pairs: # get target body state body_state, light_times = spice.spkez( spice.bodn2c(body_pair[1]), current_epoch, data_frame.frame, 'NONE', spice.bodn2c(body_pair[0])) distance_km = self.np.linalg.norm(body_state[0:3]) distance_AU = distance_km / self.AU data_frame.data[body_pair[0]][body_pair[1]].append([ greg_date_string, JD_date, body_state, distance_km, distance_AU ]) # compute solar geometries SEP_angle, SPE_angle = self.computeSEPandSPEangles( current_epoch, data_frame.spacecraft_SPICE_ID) data_frame.data['SEP'].append( [greg_date_string, JD_date, SEP_angle]) data_frame.data['SPE'].append( [greg_date_string, JD_date, SPE_angle]) # compute any eclipses self.computeCircleCircleOccultations(current_epoch, greg_date_string, JD_date, data_frame) if time_remaining == 0.0: break else: if time_remaining >= data_frame.time_step: current_epoch += data_frame.time_step time_remaining -= data_frame.time_step else: time_remaining -= stop_epoch - current_epoch current_epoch += stop_epoch - current_epoch
def computeOccultations(self, observer_in, occultation_bodies_in, target_in, target_shape_in, target_frame_in, step_size_in, aberration_correction_in, greg_format_string_in): self.greg_format_string = greg_format_string_in split_string = self.greg_format_string.split(' ') self.time_system_string = [i for i in split_string if '::' in i][0][2:] self.observer = str(observer_in) self.occultation_bodies = occultation_bodies_in self.target = target_in self.target_shape = target_shape_in self.target_frame = target_frame_in self.search_step_size = step_size_in self.aberration_correction = aberration_correction_in self.cumulative_results = {} for body in self.occultation_bodies: # add a new key to the results dictionary for this body self.cumulative_results[body.name] = [] for occultation_type in body.occultation_types: body.search_start_ET_seconds = spice.str2et(body.search_start) body.search_end_ET_seconds = spice.str2et(body.search_end) spice.wninsd(body.search_start_ET_seconds, body.search_end_ET_seconds, self.SPICE_search_window) spice.gfoclt(occultation_type, body.name, body.shape, body.frame, self.target, self.target_shape, self.target_frame, self.aberration_correction, self.observer, self.search_step_size, self.SPICE_search_window, self.results_window) winsiz = spice.wncard(self.results_window) for body_index in range(winsiz): [intbeg, intend] = spice.wnfetd(self.results_window, body_index) btmstr = spice.timout(intbeg, self.greg_format_string) etmstr = spice.timout(intend, self.greg_format_string) occultation_event = OccultationEvent() occultation_event.start = btmstr occultation_event.stop = etmstr occultation_event.start_JD = greg2Julian(btmstr) occultation_event.stop_JD = greg2Julian(etmstr) occultation_event.duration = intend - intbeg occultation_event.type = occultation_type occultation_event.observer = self.observer occultation_event.occulting_body = body.name occultation_event.occulting_body_shape = body.shape occultation_event.target = self.target occultation_event.target_body_shape = self.target_shape self.cumulative_results[body.name].append( occultation_event)
def secSinceJ20002Greg(secSinceJ2000): # returns the TDB Gregorian date # secSinceJ2000: TDB julian seconds since J2000 # delta_days: number of days that you want to advance the Gregorian date string by # time_system_string: string representation of the input gregorian_date's time system (e.g. "TDB", "UTC" etc.) return spice.timout(secSinceJ2000, "YYYY Mon DD ::TDB HR:MN:SC.###### TDB")
def parse_EMTG_event(self, SampleNumber, CentralBody, myEvent, ThrustMagnitude): self.SampleNumber = SampleNumber self.CentralBody = CentralBody self.Frame = 'EMO2000' self.EpochETJulian = myEvent.JulianDate self.EpochETGregorian = spiceypy.timout( spiceypy.unitim(myEvent.JulianDate, 'JDTDB', 'TDB'), 'YYYY Mon DD ::TDB HR:MN:SC') self.FinalMass = myEvent.Mass self.DVmagnitude = myEvent.DVmagorThrottle self.StartMass = self.FinalMass * exp( self.DVmagnitude * 1000.0 / myEvent.Isp / 9.80665) self.Mdot = ThrustMagnitude / 9.80665 / myEvent.Isp self.Duration = (self.StartMass - self.FinalMass) / self.Mdot self.ThrustX = 0.0 self.ThrustY = 0.0 self.ThrustZ = 0.0 if self.DVmagnitude > 1.0e-25: self.ThrustX = myEvent.DeltaVorThrustVectorControl[ 0] / self.DVmagnitude self.ThrustY = myEvent.DeltaVorThrustVectorControl[ 1] / self.DVmagnitude self.ThrustZ = myEvent.DeltaVorThrustVectorControl[ 2] / self.DVmagnitude self.ThrustMagnitude = ThrustMagnitude
def julian2Greg(julian_date): # returns the TDB Gregorian date # julian_date: TDB julian epoch # delta_days: number of days that you want to advance the Gregorian date string by # time_system_string: string representation of the input gregorian_date's time system (e.g. "TDB", "UTC" etc.) return spice.timout((julian_date - 2451545.0) * 86400.0, "YYYY Mon DD ::TDB HR:MN:SC.###### TDB")
def print_comet_mpc(c,name): RP,ECC,ARGP,LNODE,INC,tp = c t_perihelion = spice.unitim(tp,"JDTDB","ET") s = spice.timout(t_perihelion,"YYYY MM DD.#### ::RND ::TDB",16) year = int(s[0:4]) month = int(s[5:7]) day = float(s[8:]) mag = 8.0 slope = 4.0 return ' P%s %04d %02d %7.4f %9.6f %8.6f %8.4f %8.4f %8.4f 20171201 %4.1f %4.1f P/%s' % (name,year,month,day,RP,ECC,ARGP,LNODE,INC,mag,slope,name)
def comet(et,p,v): state = [p[0],p[1],p[2],v[0],v[1],v[2]] # km, km/s elts = spice.oscelt(state,et,mu) # compute osculating Keplerian parameters from a state vector RP,ECC,INC,LNODE,ARGP,M0,T0,MU = elts a = np.abs(RP/(1-ECC)) n = np.sqrt(MU/(a*a*a)) # rad/s TP = T0 - M0/n #fixme: need a case distinction for the sign of this correction? RP = RP/au INC = rad2deg(INC) LNODE = rad2deg(LNODE) ARGP = rad2deg(ARGP) TP = float(spice.timout(TP,"JULIAND.######## ::TDB")) #fixme return RP,ECC,ARGP,LNODE,INC,TP
def gregDateOffsetCalculator(gregorian_date, delta_days, time_system_string): # returns the time_system_string Gregorian date, offset from the input date string by delta_days # gregorian_date: SPICE compatible Gregorian date string # delta_days: number of days that you want to advance the Gregorian date string by # time_system_string: string representation of the output requested for the gregorian_date's time system (e.g. "TDB", "UTC" etc.) return spice.timout( spice.str2et( str( spice.str2et(gregorian_date) / 86400.0 + 2451545.0 + delta_days) + " JD TDB"), "YYYY Mon DD ::" + time_system_string + " HR:MN:SC.######")
def et_to_datetime(et, scale='TDB'): """ convert a SPICE ephemerides epoch (TBD seconds) to a python datetime object. The default time scale returned will be TDB but can be set to any of the accepted SPICE time scales. Args: et (float): SPICE ephemerides sceonds (TBD) scale (str, optional): time scale of output time (default: TDB) Returns: datetime: python datetime """ t = spiceypy.timout(et, 'YYYY-MON-DD HR:MN:SC.### ::{}'.format(scale), 41) return datetime.strptime(t, '%Y-%b-%d %H:%M:%S.%f')
def et_to_datetime(et, scale='TDB'): """ convert a SPICE ephemerides epoch (TBD seconds) to a python datetime object. The default time scale returned will be TDB but can be set to any of the accepted SPICE time scales. Args: et (float): SPICE ephemerides sceonds (TBD) scale (str, optional): time scale of output time (default: TDB) Returns: datetime: python datetime """ t = spice.timout(et, 'YYYY-MON-DD HR:MN:SC.### ::{}'.format(scale), 41) return datetime.strptime(t, '%Y-%b-%d %H:%M:%S.%f')
def occultations(body1, body2, start, end): cnfine = spiceypy.utils.support_types.SPICEDOUBLE_CELL(MAXWIN) result = spiceypy.utils.support_types.SPICEDOUBLE_CELL(MAXWIN) # Obtain the TDB time bounds of the confinement # window, which is a single interval in this case. et0 = spiceypy.str2et(start) et1 = spiceypy.str2et(end) # Insert the time bounds into the confinement window spiceypy.wninsd(et0, et1, cnfine) # 15-minute step. Ignore any occultations lasting less than 15 minutes. # Units are TDB seconds. step = 900.0 obsrvr = "Earth" # Loop over the occultation types. for occtype in types: # For each type, do a search for both transits of # Titan across Saturn and occultations of Titan by Saturn. for j in range(2): if not j: front = body1 fframe = "IAU_" + body1 back = body2 bframe = "IAU_" + body2 else: front = body2 fframe = "IAU_" + body2 back = body1 bframe = "IAU_" + body1 spiceypy.gfoclt(occtype, front, "ellipsoid", fframe, back, "ellipsoid", bframe, "lt", obsrvr, step, cnfine, result) # Display the results print() title = spiceypy.repmc("Condition: # occultation of # by #", "#", occtype) title = spiceypy.repmc(title, "#", back) title = spiceypy.repmc(title, "#", front) print(title) for r in result: print(spiceypy.timout(r, "YYYY Mon DD HR:MN:SC"))
def et2cal(time, format='UTC', support_ker=False, unload=False): """ Converts Ephemeris Time (ET) into UTC or Calendar TDB (CAL) time. Accepts a single time or a lists of times. This function assumes that the support kernels (meta-kernel or leapseconds kernel) has been loaded. :param time: Input ET time :type time: Union[float, list] :param format: Desired output format; 'UTC' or 'CAL' :type format: str :param unload: If True it will unload the input meta-kernel :type unload: bool :return: Output time in 'UTC', 'CAL' or 'TDB' :rtype: Union[str, list] """ timlen = 62 out_list = [] if support_ker: spiceypy.furnsh(support_ker) if isinstance(time, float) or isinstance(time, str): time = [time] for element in time: if format == 'UTC': out_elm = spiceypy.et2utc(element, 'ISOC', 3) elif format == 'CAL': out_elm = spiceypy.timout(element, "YYYY-MM-DDTHR:MN:SC.###::TDB", timlen) else: out_elm = element out_list.append(out_elm) if len(out_list) == 1: out_time = out_list[0] else: out_time = out_list if unload: spiceypy.unload(support_ker) return out_time
def __init__(self, start_time, end_time, labels, fig): self._panel_labels = labels self._panel_bottom = labels[-1] self._start_time = start_time self._end_time = end_time self._width = fig['base'].width.value() self._fig = fig self._sec_per_cm = (end_time - start_time) / self._width self._ticks = None self._tick_labels = None self._minor_locator = None if self._sec_per_cm > (16 * 86400): # monthly ticks warnings.warn('Monthly ticks not yet implemented') pass elif self._sec_per_cm > (5 * 86400): # Ticks every 5 days, minor ticks every day tick_start_time = self.round_up_day(self._start_time) self._ticks = np.arange(tick_start_time, self._end_time, 5 * 86400) self._tick_labels = [ spiceypy.timout(t, 'DOY') for t in self._ticks ] self._minor_locator = matplotlib.ticker.MultipleLocator(86400) elif self._sec_per_cm > (2 * 86400): # Ticks every 4 days, minor ticks every 12 hours tick_start_time = self.round_up_day(self._start_time) self._ticks = np.arange(tick_start_time, self._end_time, 4 * 86400) self._tick_labels = [ spiceypy.timout(t, 'DOY') for t in self._ticks ] self._minor_locator = matplotlib.ticker.MultipleLocator(12 * 3600) elif self._sec_per_cm > 86400: # Ticks every 2 days, minor ticks every 12 hours tick_start_time = self.round_up_day(self._start_time) self._ticks = np.arange(tick_start_time, self._end_time, 2 * 86400) self._tick_labels = [ spiceypy.timout(t, 'DOY') for t in self._ticks ] self._minor_locator = matplotlib.ticker.MultipleLocator(12 * 3600) elif self._sec_per_cm > 21600: # Ticks every 24 hours, minor ticks every 3 hours tick_start_time = self.round_up_day(self._start_time) self._ticks = np.arange(tick_start_time, self._end_time, 86400) self._tick_labels = [ spiceypy.timout(t, 'DOY') for t in self._ticks ] self._minor_locator = matplotlib.ticker.MultipleLocator(10800) elif self._sec_per_cm > 3600: # Ticks every 3 hours, minor ticks every 30 minutes tick_start_time = self.round_up_day(self._start_time) self._ticks = np.arange(tick_start_time, self._end_time, 3 * 3600) self._tick_labels = [ spiceypy.timout(t, 'DOY HR:MN') for t in self._ticks ] self._minor_locator = matplotlib.ticker.MultipleLocator(1800) elif self._sec_per_cm > 1800: # Ticks every hour, minor ticks every 10 minutes tick_start_time = self.round_up_day(self._start_time) self._ticks = np.arange(tick_start_time, self._end_time, 3600) self._tick_labels = [ spiceypy.timout(t, 'DOY HR:MN') for t in self._ticks ] self._minor_locator = matplotlib.ticker.MultipleLocator(600) else: # Ticks every 10 minutes tick_start_time = self.round_up_day(self._start_time) self._ticks = np.arange(tick_start_time, self._end_time, 600) self._tick_labels = [ spiceypy.timout(t, 'HR:MN') for t in self._ticks ] self._minor_locator = matplotlib.ticker.MultipleLocator(6)
def greg2Greg(gregorian_date, time_system_string): return spice.timout( spice.str2et(gregorian_date), "YYYY Mon DD ::" + time_system_string + " HR:MN:SC.######")
def SaveCarringtonRotations(Date0=19500101,Date1=20500101): ''' Create a list of times when Venus is at a solar longitude of 0, defining the start of a Carrington rotation. ''' #name the file to save the data in outpath = Globals.OutputPath + 'Venus/' if not os.path.isdir(outpath): os.system('mkdir -pv '+outpath) outfile = outpath + '0long.dat' #list the dates between date0 and date1 dates = ListDates(Date0,Date1) nd = dates.size nt = nd*24 #load the kernels sp.furnsh(lsk_path) sp.furnsh(spk_kernel) sp.furnsh(pck_kernel) sp.furnsh(hci_kernel) #get the ets et0 = utc2et(dates[0],0.0) et = et0 + np.arange(nt,dtype='float64')*3600 #get the array of longitudes lon = np.zeros(nt,dtype='float64') for i in range(0,nt): print('\rFinding longitude {0} of {1}'.format(i+1,nt),end='') pos,lt = sp.spkpos('VENUS',et[i],'IAU_SUN','NONE','SUN') lon[i] = np.arctan2(pos[1],pos[0]) print('') #find the bits where we cross 0 n=0 for i in range(0,nt-1): print('\rFinding 0s {:6.2f}%'.format(100*np.float32((i+1))/(nt-1)),end='') if lon[i] > 0 and lon[i+1] <= 0: if i == 0: inds = np.array([3,2,1,0]) elif i >= nt-2: inds = np.array([nt-1,nt-2,nt-3,nt-4]) else: inds = np.arange(4)[::-1]+i-1 f = InterpolatedUnivariateSpline(lon[inds],et[inds]) if n == 0: ets = np.array([f(0.0)],dtype='float64') else: ets = np.append(ets,f(0.0)) n+=1 print('') #store them in a file f=open(outfile,'w') for i in range(0,n): print('\rSaving time {0} of {1}'.format(i+1,n),end='') s = sp.timout(ets[i],"YYYYMMDD HRMNSC ::RND",32) date,ut = s.split() date = np.int32(date) ut = TT.HHMMtoDec(np.int32(ut),ss=True) utc = ContUT(np.array([date]),np.array([ut]))[0] outstr = '{:08d} {:f} {:f}\n'.format(date,ut,utc) f.write(outstr) f.close() print('') #unload kernels sp.unload(lsk_path) sp.unload(spk_kernel) sp.unload(pck_kernel) sp.unload(hci_kernel)
def parse_EMTG_event(self, SampleNumber, CentralBody, myEvent): self.SampleNumber = SampleNumber self.CentralBody = CentralBody self.Frame = 'EMO2000' self.EpochETJulian = myEvent.JulianDate self.EpochETGregorian = spiceypy.timout( spiceypy.unitim(myEvent.JulianDate, 'JDTDB', 'TDB'), 'YYYY Mon DD ::TDB HR:MN:SC') self.Mass = myEvent.Mass if 'Sun' in self.CentralBody: self.X = myEvent.SpacecraftState[0] self.Y = myEvent.SpacecraftState[1] self.Z = myEvent.SpacecraftState[2] self.VX = myEvent.SpacecraftState[3] self.VY = myEvent.SpacecraftState[4] self.VZ = myEvent.SpacecraftState[5] elif 'Earth' in self.CentralBody: self.X = myEvent.SpacecraftState[0] self.Y = myEvent.SpacecraftState[1] self.Z = myEvent.SpacecraftState[2] self.VX = myEvent.SpacecraftState[3] self.VY = myEvent.SpacecraftState[4] self.VZ = myEvent.SpacecraftState[5] rvec = numpy.array(myEvent.SpacecraftState[:3]) vvec = numpy.array(myEvent.SpacecraftState[3:]) r = numpy.linalg.norm(rvec) v = numpy.linalg.norm(vvec) h = numpy.cross(rvec, vvec) hhat = h / numpy.linalg.norm(h) r_dot_v = numpy.dot(rvec, vvec) mu = 398600.435436096 evec = 1.0 / mu * ((v * v - mu / r) * rvec - r_dot_v * vvec) e = numpy.linalg.norm(evec) Beta = numpy.arccos(1.0 / e) h_hat_cross_e = numpy.cross(hhat, evec) #compute b-plane coordinates Shat = numpy.cos(Beta) * evec / e + numpy.sin( Beta) * h_hat_cross_e / numpy.linalg.norm(h_hat_cross_e) khat = numpy.array([0.0, 0.0, -1.0]) T = numpy.cross(Shat, khat) That = T / numpy.linalg.norm(T) Rhat = numpy.cross(Shat, That) Bhat = numpy.cross(Shat, hhat) a = 1.0 / (2.0 / r - v * v / mu) b = a * numpy.sqrt(e * e - 1.0) B = Bhat * b self.BR = numpy.dot(B, Rhat) self.BT = numpy.dot(B, That) else: #if the central body is not the Sun then the we are whacking a target self.X = 0.0 self.Y = 0.0 self.Z = 0.0 #v-infinity is opposite delta-v self.VX = -myEvent.DeltaVorThrustVectorControl[0] self.VY = -myEvent.DeltaVorThrustVectorControl[1] self.VZ = -myEvent.DeltaVorThrustVectorControl[2] #compute the encounter state in the target frame encounter_radius = 1000.0 if ('Polymele' in self.CentralBody): encounter_radius = 399.0 Spacecraft_State_Target_Frame = numpy.array( [0.0, 0.0, encounter_radius]) #compute the basis vectors of the target frame Vinfinity = numpy.array([self.VX, self.VY, self.VZ]) VectorToSun = numpy.array([ -myEvent.SpacecraftState[0], -myEvent.SpacecraftState[1], -myEvent.SpacecraftState[2] ]) Xhat = Vinfinity / numpy.linalg.norm(Vinfinity) Yhat = numpy.cross(VectorToSun, Xhat) / numpy.linalg.norm( numpy.cross(VectorToSun, Xhat)) Zhat = numpy.cross(Xhat, Yhat) R_from_Ecliptic_to_Target = numpy.matrix([Xhat.T, Yhat.T, Zhat.T]) R_from_Target_to_Ecliptic = R_from_Ecliptic_to_Target.T #rotate the target state into the ecliptic frame Spacecraft_State_Ecliptic_Frame = R_from_Target_to_Ecliptic * numpy.matrix( Spacecraft_State_Target_Frame).T #set the output states self.X = Spacecraft_State_Ecliptic_Frame[0] self.Y = Spacecraft_State_Ecliptic_Frame[1] self.Z = Spacecraft_State_Ecliptic_Frame[2] #compute b-plane coordinates Shat = Vinfinity / numpy.linalg.norm(Vinfinity) hhat = numpy.cross(numpy.array(Spacecraft_State_Ecliptic_Frame.T), Shat) / encounter_radius Bhat = numpy.cross(Shat, hhat) khat = numpy.array([0.0, 0.0, 1.0]) That = numpy.cross(Shat, khat) Rhat = numpy.cross(Shat, That) Bmagnitude = numpy.linalg.norm( numpy.cross( Shat, numpy.cross(numpy.array(Spacecraft_State_Ecliptic_Frame.T), Shat))) self.BR = Bmagnitude * numpy.dot(Bhat, Rhat) self.BT = Bmagnitude * numpy.dot(Bhat, That)
### Convert list to string s_kernels = '[{}]'.format(','.join(bn_kernels)) ### Constants: NH SCLK sub-ticks per NH tick second tps = 5e4 met0, et0, rate0, met1, et1, rate1 = sp.gdpool('SCLK01_COEFFICIENTS_98', 0, 6) assert met0 == 0 correct_et0 = (et1 - ((met1 - met0) * rate0 / tps)) error0 = et0 - correct_et0 tpictr = '@YR-MON-DD-HR:MN:SC.##### ::TDB ::RND' correct_tdb0 = sp.timout(correct_et0, tpictr, 99) print(dict(error0=error0, correct_tdb0=correct_tdb0)) abscissa = list() ordinate = list() et1s = [et1, et0 + ((met1 - met0) * rate0 / tps)] start_et = min(et1s) - abs(error0) stop_et = max(et1s) + abs(error0) det = 0. ddet = 0.1 et = start_et + det
### Convert list to string s_kernels = '[{}]'.format(','.join(bn_kernels)) ### Constants: NH SCLK sub-ticks per NH tick second, seconds per year tps, spy = 5e4, sp.jyear() ### Interpret datetime.datetime string as UTC and convert to MET seconds date2met = lambda dayt: sp.sce2t( -98, sp.utc2et(dayt.strftime('%Y-%m-%dT%H:%M:%S'))) / tps ### Fudge factor; NH MET clock shifts 2s during the launch day met_fudge = -2 ### ET and date and MET, all 1d after SCLK zero time et = sp.sct2e(-98, 0.) - met_fudge s_0 = sp.timout(et, 'Sun MON DD HR:MN:SC YYYY', 99) date0 = datetime.datetime.strptime(s_0, '%c') met0 = date2met(date0) + met_fudge ### Year, as a floating point value, at that time offset_to_launch = 2000 + ( (date0 - datetime.datetime(2000, 1, 1, 12)).total_seconds() / spy) ### Time of leapsecond events, from LSK leapsecs_met_y = [ offset_to_launch + (sp.sce2t(-98, sp.utc2et(s.strip())) / (tps * spy)) for s in """ 2009-JAN-1/00:00:00 2012-JUL-1/00:00:00 2015-JUL-1/00:00:00 2017-JAN-1/00:00:00
def _espec_v4(data, options=None): if options is None: options= {'colorbars':['Blues','Greens','Reds']} # Load data from IDL spectrograms_by_species = data['spectrograms_by_species'] t = data['times'] x = data['positions'][0,:] y = data['positions'][1,:] z = data['positions'][2,:] o = data['orientations'] ebins = data['ebins'] # Separate parts H = spectrograms_by_species[:,:,0] He = spectrograms_by_species[:,:,1] CH4 = spectrograms_by_species[:,:,2] cnt_arr = H+He+CH4 # Build masked arrays for plotting the dominant species mH = masked_array(cnt_arr, mask=(~logical_and(H>He,H>CH4))) mHe = masked_array(cnt_arr, mask=(~logical_and(He>H,He>CH4))) mCH4 = masked_array(cnt_arr, mask=~logical_and(CH4>H,CH4>He)) # Setup subplot and colorbar axes # fig = plt.figure(figsize = (rcParams['figure.figsize'][0], 2*rcParams['figure.figsize'][1])) # gs = gridspec.GridSpec(8,1) # ax_spec = plt.subplot(gs[0,0]) # ax_theta = plt.subplot(gs[1,0], sharex=ax_spec) # ax_phi = plt.subplot(gs[2,0], sharex=ax_spec) # ax_spin = plt.subplot(gs[3,0], sharex=ax_spec) # # ax_xy = plt.subplot(gs[4:6,0]) # ax_xz = plt.subplot(gs[6:8,0], sharex=ax_xy) fig, (ax_spec, ax_theta, ax_phi, ax_spin, ax_xy, ax_xz) = plt.subplots(nrows=6, sharex=True, gridspec_kw={'height_ratios':[1,1,1,1,2,3]}, figsize=(rcParams['figure.figsize'][0], 2.25*rcParams['figure.figsize'][1])) fig.subplots_adjust(right=0.8, hspace=0.05) spec_pos = ax_spec.get_position() cbar_CH4 = fig.add_axes([spec_pos.x1+.01, spec_pos.y0+.01, 0.1/3, spec_pos.height-.02]) cbar_CH4_pos = cbar_CH4.get_position() cbar_He = fig.add_axes([cbar_CH4_pos.x1, spec_pos.y0+.01, 0.1/3, spec_pos.height-.02]) cbar_He_pos = cbar_He.get_position() cbar_H = fig.add_axes([cbar_He_pos.x1, spec_pos.y0+.01, 0.1/3,spec_pos.height-.02]) # Plot spectrograms and make colorbars Hhist = ax_spec.pcolormesh(t, ebins, mH, norm=LogNorm(), cmap=options['colorbars'][0], vmin=1e-3, vmax=1e4) Hcb = fig.colorbar(Hhist, cax=cbar_H) Hcb.ax.minorticks_on() Hehist = ax_spec.pcolormesh(t, ebins, mHe, norm=LogNorm(), cmap=options['colorbars'][1], vmin=1e-3, vmax=1e4) Hecb = fig.colorbar(Hehist, cax=cbar_He, format="") CH4hist = ax_spec.pcolormesh(t, ebins, mCH4, norm=LogNorm(), cmap=options['colorbars'][2], vmin=1e-3, vmax=1e4) CH4cb = fig.colorbar(CH4hist, cax=cbar_CH4, format="") # Plot orientations ax_theta.plot(t, o[0], marker='o', markersize=1.3, linestyle='None', color='black') ax_phi.plot(t, o[1], marker='o', markersize=1.3, linestyle='None', color='black') ax_spin.plot(t, o[2], marker='o', markersize=1.3, linestyle='None', color='black') ax_theta.set_ylim(-180,180) ax_phi.set_ylim(-180,180) ax_spin.set_ylim(-180,180) # Plot positions h = hr('/home/nathan/data/2017-Wed-Aug-30/pluto-1/databig','np') n = h.get_last_timestep()[-1] # Get grid spacing qx = h.para['qx'] qy = h.para['qy'] qzrange = h.para['qzrange'] # Find the center index of the grid cx = h.para['nx']/2 cy = h.para['ny']/2 cz = h.para['zrange']/2 # the offset of pluto from the center isn't always availible try: po = h.para['pluto_offset'] except KeyError: print("Couldn't get pluto_offset. It has been assumed to be 30, but it probably isn't.") po = 30 # Set constatnt for Pluto radius Rp = 1187. # km # Shift grid so that Pluto lies at (0,0,0) and convert from km to Rp qx = (qx - qx[len(qx)/2 + po])/Rp qy = (qy - qy[len(qy)/2])/Rp qzrange = (qzrange - qzrange[len(qzrange)/2])/Rp qt = [NH_tools.time_at_pos(x*Rp) for x in qx] T,Y = np.meshgrid(qt,qy) ax_xy.pcolormesh(T,Y,n[:,:,cz].transpose(), cmap='viridis', norm=LogNorm()) T,Z = np.meshgrid(qt,qzrange) ax_xz.pcolormesh(T,Z,n[:,cy,:].transpose(), cmap='viridis', norm=LogNorm()) ax_xy.plot(t, y/Rp-np.max(qy), color='black', linewidth=2) ax_xz.plot(t, np.zeros(z.shape), color='black', linewidth=2) # Set title, labels and adjust figure ax_spec.set_xlim(np.min(t), np.max(t)) ax_spec.set_ylim(5e1, 1e4) ax_spec.set_title("Synthetic SWAP Energy Spectrogram", y=1.4) Hecb.ax.set_title('COIN (Hz)', fontdict={'fontsize':'small'}) ax_spec.set_yscale('log') ax_spec.set_ylabel('Energy/Q ($eV/q$)') ax_theta.set_ylabel('Sun Theta\nAngle (deg)') ax_phi.set_ylabel('Sun Phi\nAngle (deg)') ax_spin.set_ylabel('Spin\nAngle (deg)') ax_xz.set_xlabel('Time (UTC)') ax_xy.set_ylabel('Y ($R_p$)') ax_xz.set_ylabel('Z ($R_p$)') ax_xy.set_ylim(np.min(qy), np.max(qy)) ax_xz.set_ylim(1.5*np.min(qy), 1.5*np.max(qy)) ax_xz.set_xticklabels([sp.timout(et, 'HR:MN:SC') for et in ax_spec.get_xticks()], rotation=-20, horizontalalignment='left') ax_twin = ax_spec.twiny() ax_twin.set_xlabel('X ($R_p$)') ax_twin.set_xlim(*ax_spec.get_xlim()) ax_twin.set_xticks(ax_spec.get_xticks()) ax_twin.set_xticklabels(['{0:.2f}'.format(NH_tools.pos_at_time(et)/1187) for et in ax_spec.get_xticks()], y=1.05) # This breaks the convention of previous versions return fig, None
ra = ra0.hms() dec = dec0.dms() dra = (ra1._degrees - ra0._degrees) / 5 # fixme: take modulo 24 hours ddec = (dec1.degrees - dec0.degrees) / 5 dra = dra / (180 / np.pi) ddec = ddec / (180 / np.pi) number = satellite.model.satnum err0, err1, elts = orbit_comet.comet_orbit_search( et + acp_time_bias + acp_lag_bias, ra, dec, dra, ddec) print('; satellite: <%s> alt: %.0f az: %.0f' % (satellite.name, alt.degrees, az.degrees)) print('; time: %s' % spice.timout(et, "YYYY-MM-DDTHR:MN:SC.### ::RND ::UTC", 24)) print('; ra %d %d %.2f dec %d %d %.2f dra %.3f ddec %.3f' % (ra[0], ra[1], ra[2], dec[0], dec[1], dec[2], dra * (180 / np.pi) * 3600, ddec * (180 / np.pi) * 3600)) x = orbit_comet.print_comet_mpc(elts, number) print('#INTERVAL %d' % t_small_exposure ) # take a short exposure after a (potentially) large slew print( '#TRACKOFF' ) # no orbital tracking: round stars (possibly useful for an initial astrometric estimate), trailed satellite (if visible) print(x) print('#INTERVAL %d' % t_large_exposure) # take the main exposure after a smaller slew print('#TRACKON') # orbital tracking: round satellite, trailed stars print(x)
dpi = 50 # Size of the output picture. 200 for cathy. 50 for web. 100 for high-res web. file_out_base = os.path.join(dir_frames, f'movie_w{width}_d{dpi}_{cmap}') file_out_gif = f'{file_out_base}_n{len(reqids)}_f{fps}.gif' for i,reqid_i in enumerate(reqids): # Convert from DOY to calendar day file_out_frame = f'{file_out_base}_{i:03}.png' doy = reqid_i.split('_')[-1][-3:] et = sp.utc2et(f'2018::{doy} 12:00:00') utc = sp.timout(et, "Month DD, YYYY", 20) utc = utc.replace(' 0', ' ') # Draw the frame f = plt.figure(frameon=False, figsize=(10, 5), dpi=dpi) # Change dpi to change output size # f.patch.set_facecolor('pink') canvas_width, canvas_height = f.canvas.get_width_height() ax = f.add_axes([0, 0, 1, 1]) ax.axis('off') # ax.set_facecolor('pink') plt.subplot(1,2,1) ax = plt.gca() # ax.set_facecolor('pink')
def print_comet_horizons(c,name,et): RP,ECC,ARGP,LNODE,INC,TP = c T0 = float(spice.timout(et,"JULIAND.#### ::TDB")) print("EPOCH=%.4f EC=%.6f QR=%.6f TP=%.4f OM=%.4f W=%.4f IN=%.4f" % (T0,ECC,RP,TP,LNODE,ARGP,INC))
# just run cosmo import spiceypy as spice from os import * import main A = 657605100 B = 658560507 C = 659891734 D = 660118099 et = A here = path.abspath(path.dirname(__file__)) PathtoMetaKernel1 = here + '/TGO/mk/em16_plan.tm' PathtoMetaKernel2 = here + '/MEX/krns/mk/MEX_OPS.tm' spice.furnsh(PathtoMetaKernel1) spice.furnsh(PathtoMetaKernel2) sv = main.SpiceVariables() main.CosmographiaCatalogFormer(et, sv) JulianTime = spice.timout(et, sv.TFMT) print('Gas Volumes Visualised for A ' + JulianTime)
def CosmographiaCatalogFormer(et, sv): # Select and occultation of interest, calculate the shape of it's profile, then add this to a dateframe result = occgradient(sv.front, et, sv.fframe, sv.obs, sv.target) profile = result[0] lowesttime = result[1] highesttime = result[2] lowesttimeFMT = spice.timout((et - result[1]), sv.TFMT) highesttimeFMT = spice.timout((et - result[2]), sv.TFMT) endtimeFMT = spice.timout(et, sv.TFMT) profile = np.transpose(profile) profiledataframe = pd.DataFrame(profile[:][0:highesttime], columns=['X', 'Y', 'Z']) # Example polygon velocities [this effects the dynamic texture of the profile, purely aesthetic]. These values have # been chosen to be slow and variable vs = np.array([[1.4236, -2.4657, 6.3948], [1.6404, -2.2997, 6.4047], [1.8386, -2.1150, 6.4145], [2.0166, -1.9136, 6.4243], [2.1730, -1.6977, 6.4339], [2.3068, -1.4696, 6.4435], [2.4170, -1.2315, 6.4530], [2.5029, -0.9859, 6.4625], [2.5029, -0.9859, 6.4625], [2.5029, -0.9859, 6.4625]]) # Iterate the polygon velocity states to be the length of profile and then combine with the positions vs = vs.repeat(200, axis=0) vt = vs[:][0:highesttime] velocitiesdataframe = pd.DataFrame(vt, columns=['dX', 'dY', 'dZ']) finalprofile = pd.concat([profiledataframe, velocitiesdataframe], axis=1) # Construct a JSON template depending on the size of the profile, split into sections of 10 points [smaller sections, # more smoothly the profile forms over time] blockcount = math.floor(highesttime / 10) * 10 i = 0 while i < (blockcount / 10): JS.JSONiterated = JS.JSONiterated + JS.JSONiterable i = i + 1 JSONtemplate = JS.JSONstart + JS.JSONiterated + JS.JSONend template = json.loads( JSONtemplate ) # load the template created above so it can be editted as JSON itemcounter = 0 # convert states into single string for cosmographia to understand [cosmogrpahia computes as # 3 postions and 3 velocities, no need for '\n'] for i in range(0, blockcount, 10): block = (finalprofile[i:i + 10].to_numpy()) * ( -1) # inverse due to reference frame inversion ProfileBlock = block.tolist() # convert to list for the JSON format states = [] for p in range(10): states.extend( ProfileBlock[p] ) # turn states into one long line to remove the '\n', which confuses cosmographia # vary the spread and intensity of the profile as it increases with alitude and the atmosphere becomes less dense profilespread = math.floor(itemcounter * (5 / (blockcount / 10))) profileintensity = math.floor(itemcounter * (30 / (blockcount / 10))) # edit the large JSON file iterativly, with 'itemcounter'moving down the JSON items (blocks of 10 profile points). # adding the states(position and velocity of the polygons), name of item (so comsmographia doesnt overwrite them), # the start time, end time(epoch of occultation), the spread and finally the intensity of colour template['items'][itemcounter]['geometry']['emitters'][0]['generator'][ 'states'] = states template['items'][itemcounter]['name'] = 'Profile ' + str(itemcounter) template['items'][itemcounter]['geometry']['emitters'][0][ 'startTime'] = spice.timout((et - i), sv.TFMT) template['items'][itemcounter]['geometry']['emitters'][0][ 'endTime'] = endtimeFMT template['items'][itemcounter]['geometry']['emitters'][0][ 'velocityVariation'] = (profilespread + 1) template['items'][itemcounter]['geometry']['emitters'][0][ 'endSize'] = profileintensity itemcounter = itemcounter + 1 # serialise the formed profile into a .json that cosmographia can read with open(' Profile.json', 'w') as file: json.dump(template, file, indent=3)