def test_spicepy_state_transformation(self): spice.furnsh(self.cass.metakernel) T = spice.sxform('IAU_EARTH', 'IAU_SATURN', self.etOne) R = spice.pxform('IAU_EARTH', 'IAU_SATURN', self.etOne) (Rout, wout) = spice.xf2rav(T) np.testing.assert_array_almost_equal(Rout, R) spice.kclear()
def from_spice(cls, *args, sensor_frame, target_frame, center_ephemeris_time, ephemeris_times=[], **kwargs): frame_chain = cls() times = np.array(ephemeris_times) sensor_time_dependent_frames, sensor_constant_frames = cls.frame_trace( sensor_frame, center_ephemeris_time) target_time_dependent_frames, target_constant_frames = cls.frame_trace( target_frame, center_ephemeris_time) time_dependent_frames = list( zip(sensor_time_dependent_frames[:-1], sensor_time_dependent_frames[1:])) constant_frames = list( zip(sensor_constant_frames[:-1], sensor_constant_frames[1:])) target_time_dependent_frames = list( zip(target_time_dependent_frames[:-1], target_time_dependent_frames[1:])) target_constant_frames = list( zip(target_constant_frames[:-1], target_constant_frames[1:])) time_dependent_frames.extend(target_time_dependent_frames) constant_frames.extend(target_constant_frames) for s, d in time_dependent_frames: quats = np.zeros((len(times), 4)) avs = np.zeros((len(times), 3)) for j, time in enumerate(times): state_matrix = spice.sxform(spice.frmnam(s), spice.frmnam(d), time) rotation_matrix, avs[j] = spice.xf2rav(state_matrix) quat_from_rotation = spice.m2q(rotation_matrix) quats[j, :3] = quat_from_rotation[1:] quats[j, 3] = quat_from_rotation[0] rotation = TimeDependentRotation(quats, times, s, d, av=avs) frame_chain.add_edge(rotation=rotation) for s, d in constant_frames: quats = np.zeros(4) rotation_matrix = spice.pxform(spice.frmnam(s), spice.frmnam(d), times[0]) quat_from_rotation = spice.m2q(rotation_matrix) quats[:3] = quat_from_rotation[1:] quats[3] = quat_from_rotation[0] rotation = ConstantRotation(quats, s, d) frame_chain.add_edge(rotation=rotation) return frame_chain
# Load the SPICE kernels via a meta file spiceypy.furnsh('kernel_meta.txt') # Create an initial date-time object that is converted to a string DATETIME_UTC = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S') # Convert to Ephemeris Time (ET) using the SPICE function utc2et DATETIME_ET = spiceypy.utc2et(DATETIME_UTC) #%% # ECLIPJ2000_DE405 and ECLIPJ2000 appear to be similar?! A transformation # matrix between both coordinate systems (for state vectors) should be # consequently the identity matrix MAT = spiceypy.sxform(instring='ECLIPJ2000_DE405', \ tostring='ECLIPJ2000', \ et=DATETIME_ET) # Let's print the transformation matrix row-wise (spoiler alert: it is the # identity matrix) print('Transformation matrix between ECLIPJ2000_DE405 and ECLIPJ2000') for mat_row in MAT: print(f'{np.round(mat_row, 2)}') print('\n') #%% # Compute the state vector of Ceres in ECLIPJ2000 as seen from the Sun CERES_STATE_VECTOR, _ = spiceypy.spkgeo(targ=2000001, \ et=DATETIME_ET, \ ref='ECLIPJ2000',
def sensor_position(self): """ Returns a tuple with information detailing the position of the sensor at the time of the image. Expects ephemeris_time to be defined. This must be a floating point number containing the ephemeris time. Expects spacecraft_name to be defined. This must be a string containing the name of the spacecraft containing the sensor. Expects reference_frame to be defined. This must be a sring containing the name of the target reference frame. Expects target_name to be defined. This must be a string containing the name of the target body. Returns ------- : (positions, velocities, times) a tuple containing a list of positions, a list of velocities, and a list of times """ if not hasattr(self, '_position'): ephem = self.ephemeris_time pos = [] vel = [] target = self.spacecraft_name observer = self.target_name # Check for ISIS flag to fix target and observer swapping if self.swap_observer_target: target = self.target_name observer = self.spacecraft_name for time in ephem: # spkezr returns a vector from the observer's location to the aberration-corrected # location of the target. For more information, see: # https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/FORTRAN/spicelib/spkezr.html if self.correct_lt_to_surface and self.light_time_correction.upper( ) == 'LT+S': obs_tar_state, obs_tar_lt = spice.spkezr( target, time, 'J2000', self.light_time_correction, observer) # ssb to spacecraft ssb_obs_state, ssb_obs_lt = spice.spkezr( observer, time, 'J2000', 'NONE', 'SSB') radius_lt = (self.target_body_radii[2] + self.target_body_radii[0]) / 2 / ( scipy.constants.c / 1000.0) adjusted_time = time - obs_tar_lt + radius_lt ssb_tar_state, ssb_tar_lt = spice.spkezr( target, adjusted_time, 'J2000', 'NONE', 'SSB') state = ssb_tar_state - ssb_obs_state matrix = spice.sxform("J2000", self.reference_frame, time) state = spice.mxvg(matrix, state, 6, 6) else: state, _ = spice.spkezr(target, time, self.reference_frame, self.light_time_correction, observer) if self.swap_observer_target: pos.append(-state[:3]) vel.append(-state[3:]) else: pos.append(state[:3]) vel.append(state[3:]) # By default, SPICE works in km, so convert to m self._position = [p * 1000 for p in pos] self._velocity = [v * 1000 for v in vel] return self._position, self._velocity, self.ephemeris_time
def sxform_val(inf, outf, et): print(f"\n{inf} -> {outf}") print(sp.sxform(inf, outf, et))
def convert_coord(self, dates, coord_src, system_src, system_dst, observe_src=None, observe_dst=None, precess=False): """ Function to convert coordinates betwen different reference frames. :param dates: Astropy time object of dates(s). :param coord_src: Array of coordinates to convert. Should be len(dates)*3 for positions only, or len(dates)*6 for full state :param system_src: String name of coordinate system of coord array. :param system_dst: String name of coordinate system to transform coord array to. :param observe_src: String name of observatory for origin of system from. Only needed for some systems. :param observe_dst: String name of observatory for origin of system to. Only needed for some systems. :param precess: Boolean. If True accounts for precession in coordinate system. :return state: Array, giving state at each dates. Either len(dates)x3 or len(dates)x6, depending on no_velocity :return ltime (optional): The light travel time between observatory and target """ # If coordinates input as a list, then bung them into an array. if isinstance(coord_src, list): if all([isinstance(c, (float, int)) for c in coord_src]): coord_src = np.array(coord_src) coord_src = np.squeeze(coord_src) else: print( "ERROR: coord_src should be a numpy array of coordinates or a list of floats/ints." ) # If coord only has one dimension, set it so that time is zeroth. if coord_src.ndim == 1: n_coords = 1 n_components = coord_src.size else: n_coords = coord_src.shape[0] n_components = coord_src.shape[1] # Check dates and coord sizes match. if dates.size != n_coords: print( "Error: Number of dates does not correspond to number of coordinates." ) # Check coord components are either 3 or 6, for position or state. Set no_velocity flag too. if n_components == 3: no_velocity = True elif n_components == 6: no_velocity = False else: print( "ERROR: Invalid dimension of position vector or state vector") # Get NAIF formated name for src and dst observer and frame. # Get SRC frame and osberver if observe_src is None: # Observer defined by the frame. frame_src, observe_src = self.get_system_frame_names( system_src, precess=precess) else: # Observer must be specified for this frame observe_src = self.get_naif_body_code(observe_src) # Get naif frame and observer frame_src, observe_src = self.get_system_frame_names( system_src, observatory=observe_src, precess=precess) # Repeat for DST frame and observer if observe_dst is None: # Observer defined by frame frame_dst, observe_dst = self.get_system_frame_names( system_dst, precess=precess) else: # Observer must be specified for this frame observe_dst = self.get_naif_body_code(observe_dst) # Get naif frame and observ frame_dst, observe_dst = self.get_system_frame_names( system_dst, observatory=observe_dst, precess=precess) # Convert to ephemeris time if dates.isscalar: et = spice.str2et(dates.isot) else: et = spice.str2et(dates.isot.tolist()) # If observer changes, first do origin shift. if observe_src != observe_dst: # Get location of observe_dst relative to observe_src in frame_src corr = 'NONE' if no_velocity: observe_src_state, ltime = spice.spkpos( observe_dst, et, frame_src, corr, observe_src) else: # Velocity requested. This needs spkezr, which only accepts floats. So loop et and call spkezr for # each et. Preallocate state and ltime, in this case state is a len(et)x6 array. if dates.isscalar: observe_src_state, ltime = spice.spkezr( observe_dst, et, frame_src, corr, observe_src) else: observe_src_state = np.zeros(coord_src.shape, dtype=float) ltime = np.zeros(dates.size, dtype=float) for i in range(dates.size): observe_src_state[i, :], ltime[i] = spice.spkezr( observe_dst, et[i], frame_src, corr, observe_src) # Now shift the origin coord_src = coord_src - observe_src_state # Must loop through dates for the matrix multiplication. Preallocate space for output. # Now rotate from src frame to dst frame. if dates.isscalar: if no_velocity: # Get rotation matrix for position only transform = spice.pxform(frame_src, frame_dst, et) coord_dst = np.matmul(transform, coord_src) else: # Get rotation matrix for full state transform = spice.sxform(frame_src, frame_dst, et) coord_dst = np.matmul(transform, coord_src) else: # Must loop through dates for the matrix multiplication. Preallocate space for output. coord_dst = np.zeros(coord_src.shape, dtype=float) for i in range(dates.size): if no_velocity: transform = spice.pxform(frame_src, frame_dst, et[i]) else: transform = spice.sxform(frame_src, frame_dst, et[i]) coord_dst[i, :] = np.matmul(transform, coord_src[i, :]) return coord_dst
def save(self,base_fname, force): """ Save asteroid state vectors in a NAIF SPICE SPK file Base filename will be appended by asteroid internal ID + extension (.bsp) Parameters ---------- base_fname: string Base file name. force : boolean Force the program to rewrite asteroid SPK files """ n_segments = 1 # This parameter should be an argument or should always be 1 segmentsize = (len(self.svec)-1)//n_segments # File name to save SPK to self.spkname = base_fname+str(self.id)+".bsp" if force: if os.path.isfile(self.spkname): os.system("rm %s" %(self.spkname)) handle = sp.spkopn(self.spkname,"spkfile",500) for i in np.arange(0,n_segments): # Computing first and last index for segment idx_first=i*segmentsize+1 idx_last =(i+1)*segmentsize+1 # first elements are initial values from initial orbit so ignoring those segmenttimeet = self.timeset[idx_first:idx_last] segmenttime = self.svec[idx_first:idx_last,8] segmentx = self.svec[idx_first:idx_last,1]*shared.au2km segmenty = self.svec[idx_first:idx_last,2]*shared.au2km segmentz = self.svec[idx_first:idx_last,3]*shared.au2km segmentxd = self.svec[idx_first:idx_last,4]*shared.au2km/shared.day2s segmentyd = self.svec[idx_first:idx_last,5]*shared.au2km/shared.day2s segmentzd = self.svec[idx_first:idx_last,6]*shared.au2km/shared.day2s tmpstates=[segmentx,segmenty,segmentz,segmentxd,segmentyd,segmentzd] spicestates=np.swapaxes(tmpstates,0,1) spicestates=spicestates.tolist() # Coordinate transformation from Ecliptic to Equatorial frame for saving as SPKs spicestateseq=copy.deepcopy(spicestates) # spicestateseq=spicestates[:] counter=0 for state in spicestates: matrix=sp.sxform("ECLIPJ2000","J2000",segmenttimeet[counter]) spicestateseq[counter]=sp.mxvg(matrix,spicestates[counter],6,6) counter=counter+1 # SPK Type 9, Lagrange interpolation (Open Orb states are heliocentric, so central body is 10, which is the Sun) # sp.spkw09(handle, self.spiceid, 10, "J2000", segmenttimeet[0], segmenttimeet[-1], str(i), 10, segmentsize, spicestateseq, segmenttimeet) # SPK Type 5 (Two body interpolation) sp.spkw05(handle,2444444+self.id,10, "J2000", segmenttimeet[0], segmenttimeet[-1], str(i), shared.gms, segmentsize, spicestateseq, segmenttimeet) sp.spkcls(handle)
#to change the approximation used change the method of each planet to EULER, EULERCROMER OR VERLET #for the sun mass_sun = (constants.GM_sun / UPPER_G).value pos_sun, vel_sun = get_body_barycentric_posvel("sun", t, ephemeris="jpl") sunarray = [ pos_sun.xyz[0].to("m").value, pos_sun.xyz[1].to("m").value, pos_sun.xyz[2].to("m").value, vel_sun.xyz[0].to("m/s").value, vel_sun.xyz[1].to("m/s").value, vel_sun.xyz[2].to("m/s").value, ] trans = sxform("J2000", "ECLIPJ2000", t.jd) sunarrayecl = mxvg(trans, sunarray, 6, 6) position_sun = [sunarrayecl[0], sunarrayecl[1], sunarrayecl[2]] velocity_sun = [sunarrayecl[3], sunarrayecl[4], sunarrayecl[5]] sun = Particle(position_sun, velocity_sun, np.array([0,0,0]), name='Sun', mass = mass_sun, algorithm = "EULER") #print(mass_sun) #print(velocity_sun) #for mercury mass_mercury = (constants.GM_mercury / UPPER_G).value pos_mercury, vel_mercury = get_body_barycentric_posvel("mercury", t, ephemeris="jpl") mercuryarray = [ pos_mercury.xyz[0].to("m").value, pos_mercury.xyz[1].to("m").value, pos_mercury.xyz[2].to("m").value, vel_mercury.xyz[0].to("m/s").value,