def sensor_orientation(self): if not hasattr(self, '_sensor_orientation'): current_et = self.starting_ephemeris_time qua = np.empty((self.number_of_ephemerides, 4)) for i in range(self.number_of_quaternions): # Find the rotation matrix camera2bodyfixed = spice.pxform(self.instrument_id, self.reference_frame, current_et) q = spice.m2q(camera2bodyfixed) qua[i,:3] = q[1:] qua[i,3] = q[0] current_et += getattr(self, 'dt_quaternion', 0) self._sensor_orientation = qua return self._sensor_orientation.tolist()
def _sensor_orientation(self): if not hasattr(self, '_orientation'): ephem = self.ephemeris_time qua = np.empty((len(ephem), 4)) for i, time in enumerate(ephem): # Find the rotation matrix camera2bodyfixed = spice.pxform(self.instrument_id, self.reference_frame, time) q = spice.m2q(camera2bodyfixed) qua[i,:3] = q[1:] qua[i,3] = q[0] self._orientation = qua return self._orientation.tolist()
def _sensor_orientation(self): if not hasattr(self, '_orientation'): ephem = self.ephemeris_time qua = np.empty((len(ephem), 4)) for i, time in enumerate(ephem): instrument = self.label.get("INSTRUMENT_ID") # Find the rotation matrix camera2bodyfixed = spice.pxform( "LISM_{}_HEAD".format(instrument), self.reference_frame, time) q = spice.m2q(camera2bodyfixed) qua[i, :3] = q[1:] qua[i, 3] = q[0] self._orientation = qua return self._orientation.tolist()
def frame_chain(self): if not hasattr(self, '_frame_chain'): nadir = self._props.get('nadir', False) self._frame_chain = FrameChain.from_spice( sensor_frame=self.sensor_frame_id, target_frame=self.target_frame_id, center_ephemeris_time=self.center_ephemeris_time, ephemeris_times=self.ephemeris_time, nadir=nadir) if nadir: # Logic for nadir calculation was taken from ISIS3 # SpiceRotation::setEphemerisTimeNadir rotation = self._frame_chain.compute_rotation( self.target_frame_id, 1) p_vec, v_vec, times = self.sensor_position rotated_positions = rotation.apply_at(p_vec, times) rotated_velocities = rotation.rotate_velocity_at( p_vec, v_vec, times) p_vec = rotated_positions v_vec = rotated_velocities velocity_axis = 2 # Get the default line translation with no potential flipping # from the driver trans_x = np.array( list(spice.gdpool('INS{}_ITRANSL'.format(self.ikid), 0, 3))) if (trans_x[0] < trans_x[1]): velocity_axis = 1 quats = [ spice.m2q( spice.twovec(-p_vec[i], 3, v_vec[i], velocity_axis)) for i, time in enumerate(times) ] quats = np.array(quats)[:, [1, 2, 3, 0]] rotation = TimeDependentRotation(quats, times, 1, self.sensor_frame_id) self._frame_chain.add_edge(rotation) return self._frame_chain
def get_isd(label): """ TODO: This function (and all like it) needs to open with some robust method to make sure this is in fact an MDIS label. """ instrument_name = { 'MDIS-NAC': 'MSGR_MDIS_NAC', 'MERCURY DUAL IMAGING SYSTEM NARROW ANGLE CAMERA': 'MSGR_MDIS_NAC', 'MERCURY DUAL IMAGING SYSTEM WIDE ANGLE CAMERA': 'MSGR_MDIS_WAC' } metakernel_dir = config.mdis mks = sorted(glob(os.path.join(metakernel_dir, '*.tm'))) instrument_id = instrument_name[label['INSTRUMENT_ID']] spacecraft_name = label['MISSION_NAME'] target_name = label['TARGET_NAME'] time = label['START_TIME'] messenger_mk = None for mk in mks: if str(time.year) in os.path.basename(mk): messenger_mk = mk spice.furnsh(messenger_mk) # Spice likes ids over names, so grab the ids from the names spacecraft_id = spice.bods2c(spacecraft_name) ikid = spice.bods2c(instrument_id) # Load the instrument and target metadata into the ISD reference_frame = 'IAU_{}'.format(target_name) isd = {} rad = spice.bodvrd(target_name, 'RADII', 3) isd['radii'] = {} isd['radii']['semimajor'] = rad[1][0] isd['radii']['semiminor'] = rad[1][1] isd['optical_distortion'] = {} odk_mssgr_x = spice.gdpool('INS{}_OD_T_X'.format(ikid), 0, 10) odk_mssgr_y = spice.gdpool('INS{}_OD_T_Y'.format(ikid), 0, 10) isd['optical_distortion']['x'] = list(odk_mssgr_x) isd['optical_distortion']['y'] = list(odk_mssgr_y) isd['focal2pixel_samples'] = list( spice.gdpool('INS{}_TRANSX'.format(ikid), 0, 3)) isd['focal2pixel_lines'] = list( spice.gdpool('INS{}_TRANSY'.format(ikid), 0, 3)) # Load information from the IK kernel isd['focal_length_model'] = {} focal_legnth_coeffs = spice.gdpool('INS{}_FL_TEMP_COEFFS '.format(ikid), 0, 5) isd['focal_length_model']['focal_length'] = focal_length_from_temp( label['FOCAL_PLANE_TEMPERATURE'].value, focal_legnth_coeffs) isd['focal_length_model']['focal_length_epsilon'] = float( spice.gdpool('INS{}_FL_UNCERTAINTY'.format(ikid), 0, 1)[0]) isd['image_lines'] = int( spice.gipool('INS{}_PIXEL_LINES'.format(ikid), 0, 1)[0]) isd['image_samples'] = int( spice.gipool('INS{}_PIXEL_SAMPLES'.format(ikid), 0, 1)[0]) isd['starting_detector_sample'] = int( spice.gdpool('INS{}_FPUBIN_START_SAMPLE'.format(ikid), 0, 1)[0]) isd['starting_detector_line'] = int( spice.gdpool('INS{}_FPUBIN_START_LINE'.format(ikid), 0, 1)[0]) # Now time sclock = label['SPACECRAFT_CLOCK_START_COUNT'] exposure_duration = label['EXPOSURE_DURATION'].value exposure_duration = exposure_duration * 0.001 # Scale to seconds # Get the instrument id, and, since this is a framer, set the time to the middle of the exposure start_et = spice.scs2e(spacecraft_id, sclock) start_et += (exposure_duration / 2.0) end_et = spice.scs2e( spacecraft_id, label['SPACECRAFT_CLOCK_STOP_COUNT']) + (exposure_duration / 2.0) del_et = end_et - start_et et = (start_et + end_et) / 2 isd['starting_ephemeris_time'] = start_et isd['dt_ephemeris'] = del_et isd['number_of_ephemerides'] = 1 isd['interpolation_method'] = 'lagrange' isd['center_ephemeris_time'] = et # Get the rotation angles from MDIS NAC frame to Mercury body-fixed frame camera2bodyfixed = spice.pxform(instrument_id, reference_frame, et) quat = spice.m2q(camera2bodyfixed) isd['sensor_orientation'] = list(quat) # Get the Sensor Position loc, _ = spice.spkpos(target_name, et, reference_frame, 'LT+S', spacecraft_name) isd['sensor_location'] = {} isd['sensor_location']['x'] = loc[0] isd['sensor_location']['y'] = loc[1] isd['sensor_location']['z'] = loc[2] isd['sensor_location']['unit'] = 'm' # Get the velocity v_state, lt = spice.spkezr(spacecraft_name, et, reference_frame, 'NONE', target_name) isd['sensor_velocity'] = {} isd['sensor_velocity']['x'] = v_state[3] isd['sensor_velocity']['y'] = v_state[4] isd['sensor_velocity']['z'] = v_state[5] isd['sensor_velocity']['unit'] = 'm' isd['reference_height'] = {} isd['reference_height']['minheight'] = label.get('min_valid_height', -8000) isd['reference_height']['maxheight'] = label.get('max_valid_height', 8000) isd['reference_height']['unit'] = 'KM' # Get the sun position sun_state, lt = spice.spkezr("SUN", et, reference_frame, 'NONE', target_name) # Get the sun position, convert to meters xpos, ypos, zpos = [e.value for e in label['SC_SUN_POSITION_VECTOR']] xvel, yvel, zvel = [e.value for e in label['SC_SUN_VELOCITY_VECTOR']] # lighttime should always be off isd['sun_position'] = {} isd['sun_position']['x'] = sun_state[0] isd['sun_position']['y'] = sun_state[1] isd['sun_position']['z'] = sun_state[2] isd['sun_velocity'] = {} isd['sun_velocity']['x'] = sun_state[3] isd['sun_velocity']['y'] = sun_state[4] isd['sun_velocity']['z'] = sun_state[5] return isd
def __Geometry(self, boresight=''): #if self.geometry_flag is True and \ # self.time.window.all() == self.previous_tw.all(): # return distance = [] altitude = [] boresight_latitude = [] boresight_longitude = [] latitude = [] longitude = [] subpoint_xyz = [] subpoint_pgc = [] subpoint_pcc = [] zaxis_target_angle = [] myaxis_target_angle = [] yaxis_target_angle = [] xaxis_target_angle = [] beta_angle = [] qs, qx, qy, qz = [], [], [] ,[] x, y, z = [],[],[] tar = self.target time = self.time for et in time.window: try: # # Compute the distance # ptarg, lt = spiceypy.spkpos(tar.name, et, tar.frame, time.abcorr, self.name) x.append(ptarg[0]) y.append(ptarg[1]) z.append(ptarg[2]) vout, vmag = spiceypy.unorm(ptarg) distance.append(vmag) # # Compute the geometric sub-observer point. # if tar.frame == 'MARSIAU': tar_frame = 'IAU_MARS' else: tar_frame = tar.frame spoint, trgepc, srfvec = spiceypy.subpnt(tar.method, tar.name, et, tar_frame, time.abcorr, self.name) subpoint_xyz.append(spoint) # # Compute the observer's altitude from SPOINT. # dist = spiceypy.vnorm(srfvec) altitude.append(dist) # # Convert the sub-observer point's rectangular coordinates to # planetographic longitude, latitude and altitude. # spglon, spglat, spgalt = spiceypy.recpgr(tar.name, spoint, tar.radii_equ, tar.flat) # # Convert radians to degrees. # spglon *= spiceypy.dpr() spglat *= spiceypy.dpr() subpoint_pgc.append([spglon, spglat, spgalt]) # # Convert sub-observer point's rectangular coordinates to # planetocentric radius, longitude, and latitude. # spcrad, spclon, spclat = spiceypy.reclat(spoint) # # Convert radians to degrees. # spclon *= spiceypy.dpr() spclat *= spiceypy.dpr() subpoint_pcc.append([spclon, spclat, spcrad]) latitude.append(spclat) #TODO: Remove with list extraction longitude.append(spclon) # TODO: Remove with list extraction # # Compute the geometric sub-boresight point. # if tar.frame == 'MARSIAU': tar_frame = 'IAU_MARS' else: tar_frame = tar.frame if boresight: try: id = spiceypy.bodn2c(boresight) (shape,framen, bsight, n, bounds) = spiceypy.getfov(id, 80) mat = spiceypy.pxform(framen,tar_frame,et) except: framen = boresight bsight = 0,0,1 else: bsight = self.name try: if tar.method == 'INTERCEPT/ELLIPSOID': method = 'ELLIPSOID' else: method = tar.method spoint, trgepc, srfvec = spiceypy.sincpt(method, tar.name, et, tar_frame, time.abcorr, self.name, framen, bsight) # # Convert the sub-observer point's rectangular coordinates to # planetographic longitude, latitude and altitude. # spglon, spglat, spgalt = spiceypy.recpgr(tar.name, spoint, tar.radii_equ, tar.flat) # # Convert radians to degrees. # spglon *= spiceypy.dpr() spglat *= spiceypy.dpr() # # Convert sub-observer point's rectangular coordinates to # planetocentric radius, longitude, and latitude. # spcrad, spclon, spclat = spiceypy.reclat(spoint) # # Convert radians to degrees. # spclon *= spiceypy.dpr() spclat *= spiceypy.dpr() boresight_latitude.append(spclat) boresight_longitude.append(spclon) except: pass # # Compute the angle between the observer's S/C axis and the # geometric sub-observer point # obs_tar, ltime = spiceypy.spkpos(tar.name, et, 'J2000', time.abcorr, self.name) obs_zaxis = [0, 0, 1] obs_myaxis = [0, -1, 0] obs_yaxis = [0, 1, 0] obs_xaxis = [1, 0, 0] # # We need to account for when there is no CK attitude available. # try: matrix = spiceypy.pxform(self.frame, 'J2000', et) z_vecout = spiceypy.mxv(matrix, obs_zaxis) zax_target_angle = spiceypy.vsep(z_vecout, obs_tar) zax_target_angle *= spiceypy.dpr() zaxis_target_angle.append(zax_target_angle) my_vecout = spiceypy.mxv(matrix, obs_myaxis) myax_target_angle = spiceypy.vsep(my_vecout, obs_tar) myax_target_angle *= spiceypy.dpr() myaxis_target_angle.append(myax_target_angle) y_vecout = spiceypy.mxv(matrix, obs_myaxis) yax_target_angle = spiceypy.vsep(y_vecout, obs_tar) yax_target_angle *= spiceypy.dpr() yaxis_target_angle.append(yax_target_angle) x_vecout = spiceypy.mxv(matrix, obs_myaxis) xax_target_angle = spiceypy.vsep(x_vecout, obs_tar) xax_target_angle *= spiceypy.dpr() xaxis_target_angle.append(xax_target_angle) quat = spiceypy.m2q(spiceypy.invert(matrix)) qs.append(quat[0]) qx.append(-1*quat[1]) qy.append(-1*quat[2]) qz.append(-1*quat[3]) except: zaxis_target_angle.append(0.0) myaxis_target_angle.append(0.0) yaxis_target_angle.append(0.0) xaxis_target_angle.append(0.0) qs.append(0.0) qx.append(0.0) qy.append(0.0) qz.append(0.0) beta_angle.append(spiops.beta_angle(self.name, self.target.name, et)) except: boresight_latitude = 0 boresight_longitude = 0 distance = 0 altitude = 0 latitude = 0 longitude = 0 subpoint_xyz = [0,0,0] subpoint_pgc = [0,0,0] subpoint_pcc = [0,0,0] zaxis_target_angle = 0 myaxis_target_angle = 0 yaxis_target_angle = 0 xaxis_target_angle = 0 beta_angle = 0 (qx, qy, qz, qs) = 0, 0, 0, 0 (x, y, z) = 0, 0, 0 self.boresight_latitude = boresight_latitude self.boresight_longitude = boresight_longitude self.distance = distance self.altitude = altitude self.latitude = latitude self.longitude = longitude self.subpoint_xyz = subpoint_xyz self.subpoint_pgc = subpoint_pgc self.subpoint_pcc = subpoint_pcc self.zaxis_target_angle = zaxis_target_angle self.myaxis_target_angle = myaxis_target_angle self.yaxis_target_angle = yaxis_target_angle self.xaxis_target_angle = xaxis_target_angle self.beta_angle = beta_angle self.quaternions = [qx, qy, qz, qs] self.trajectory = [x,y,z] self.geometry_flag = True self.previous_tw = self.time.window return
def get_isd(label): mission_name = {"CASSINI-HUYGENS": "CASSINI"} instrument_names = {"ISSNA": "CASSINI_ISS_NAC", "ISSWA": "CASSINI_ISS_WAC"} metakernal_dir = config.cassini mks = sorted(glob(metakernal_dir + '/*.tm')) instrument_name = instrument_names[label['INSTRUMENT_ID']] spacecraft_name = mission_name[label['MISSION_NAME']] target_name = label['TARGET_NAME'] time = label['START_TIME'] cassini_mk = None for mk in mks: if str(time.year) in os.path.basename(mk): cassini_mk = mk spice.furnsh(cassini_mk) # Spice likes ids over names, so grab the ids from the names spacecraft_id = spice.bods2c(spacecraft_name) instrument_id = spice.bods2c(instrument_name) # Load the instrument and target metadata into the ISD reference_frame = 'IAU_{}'.format(target_name) isd = {} rad = spice.bodvrd(target_name, 'RADII', 3) isd['semimajor'] = rad[1][0] * 1000 isd['semiminor'] = rad[1][1] * 1000 # transx and transy are unavailable so fill in with pixel_size after conversion to millimeters from microns # assuming pixels are square pixel_size = int( spice.gipool('INS{}_PIXEL_SIZE'.format(instrument_id), 0, 1)[0]) * 0.001 isd['focal2pixel_samples'] = [0.0, pixel_size, 0.0] isd['focal2pixel_lines'] = [0.0, 0.0, pixel_size] # unavailable so default to 0 isd['starting_detector_sample'] = 0.0 isd['starting_detector_line'] = 0.0 isd['image_lines'] = float( spice.gipool('INS{}_PIXEL_LINES'.format(instrument_id), 0, 1)[0]) isd['image_samples'] = float( spice.gipool('INS{}_PIXEL_SAMPLES'.format(instrument_id), 0, 1)[0]) isd['focal_length_model'] = {} isd['focal_length_model']['focal_length'] = float( spice.gdpool('INS{}_FOCAL_LENGTH'.format(instrument_id), 0, 1)[0]) isd['focal_length_model']['focal_length_epsilon'] = float( spice.gdpool('INS{}_FL_UNCERTAINTY'.format(instrument_id), 0, 1)[0]) # this following part is ripped from the mdis driver. Since they are both framers this code should be able to be applied to both # Now time sclock = label['SPACECRAFT_CLOCK_START_COUNT'] exposure_duration = label['EXPOSURE_DURATION'].value exposure_duration = exposure_duration * 0.001 # Scale to seconds # Get the instrument id, and, since this is a framer, set the time to the middle of the exposure start_et = spice.scs2e(spacecraft_id, sclock) start_et += (exposure_duration / 2.0) end_et = spice.scs2e( spacecraft_id, label['SPACECRAFT_CLOCK_STOP_COUNT']) + (exposure_duration / 2.0) del_et = end_et - start_et et = (start_et + end_et) / 2 isd['starting_ephemeris_time'] = start_et isd['dt_ephemeris'] = del_et isd['number_of_ephemerides'] = 1 isd['interpolation_method'] = 'lagrange' isd['center_ephemeris_time'] = et # Get the rotation angles from MDIS NAC frame to Mercury body-fixed frame camera2bodyfixed = spice.pxform(instrument_name, reference_frame, et) quat = spice.m2q(camera2bodyfixed) # cassini follows spice style for quaternions so no transformation is needed isd['sensor_orientation'] = list(quat) # Get the Sensor Position loc, _ = spice.spkpos(target_name, et, reference_frame, 'None', spacecraft_name) loc *= -1000 isd['sensor_location'] = {} isd['sensor_location']['x'] = loc[0] isd['sensor_location']['y'] = loc[1] isd['sensor_location']['z'] = loc[2] isd['sensor_location']['unit'] = 'm' # Get the velocity v_state, lt = spice.spkezr(spacecraft_name, et, reference_frame, 'NONE', target_name) isd['sensor_velocity'] = {} isd['sensor_velocity']['x'] = v_state[3] * 1000 isd['sensor_velocity']['y'] = v_state[4] * 1000 isd['sensor_velocity']['z'] = v_state[5] * 1000 isd['sensor_velocity']['unit'] = 'm' isd['reference_height'] = {} isd['reference_height']['minheight'] = label.get('min_valid_height', -8000) isd['reference_height']['maxheight'] = label.get('max_valid_height', 8000) isd['reference_height']['unit'] = 'KM' # Get the sun position sun_state, lt = spice.spkezr("SUN", et, reference_frame, 'NONE', target_name) # lighttime should always be off isd['sun_position'] = {} isd['sun_position']['x'] = sun_state[0] * 1000 isd['sun_position']['y'] = sun_state[1] * 1000 isd['sun_position']['z'] = sun_state[2] * 1000 isd['sun_velocity'] = {} isd['sun_velocity']['x'] = sun_state[3] * 1000 isd['sun_velocity']['y'] = sun_state[4] * 1000 isd['sun_velocity']['z'] = sun_state[5] * 1000 # cassini has no optical distortion model isd['optical_distortion'] = None return isd
def create_ck(kernels, frames_to_convert, fks, stop_ets, output_ck): """ Create CK from FKs and times at which the FKs end being valid """ ### FURNSH any kernels for kernel in kernels: sp.furnsh(kernel) ### Set last ET to None so it will be initialized in loop's first pass last_et = None ### Do not overwrite existing output CK assert (not sp.exists(output_ck) ) or dict()['Cannot overwrite existing CK[{}]'.format(output_ck)] ### Initialize CK handle to None ck_handle = None ### Loop over CKs while fks: ### Get FK and corresponding stop ET fk = fks.pop() stop_et = stop_ets.pop() ### Loop over reference frames (reffrms) for reffrm in frames_to_convert: ### Get reffrm ID, SCLK ID; N.B. latter comes from new FK reffrm_id = sp.gipool('FRAME_{}'.format(reffrm.upper()), 0, 1)[0] sclk_id = sp.gipool('CK_{}_{}'.format(reffrm_id, 'SCLK'), 0, 1)[0] ### Set start DP-SCLK of window for this old FK (outer loop) ### - Set to zero for first window ### - Covnert ET to DP-SCLK for subsequent windows if last_et is None: begtim = 0.0 else: begtim = sp.sce2t(sclk_id, last_et) ### Load old FK, get RELATIVE frame name, get time-invariant ### matrix, and unload old FK sp.furnsh(fk) relative_reffrm = sp.gcpool( 'TKFRAME_{}_{}'.format(reffrm_id, 'RELATIVE'), 0, 1, 99)[0] mtx = sp.pxform(relative_reffrm, reffrm, 0.0) sp.unload(fk) ### Covnert matrix to quaternion quat = sp.m2q(mtx) ### Calculate tick rate: seconds per tick rate = (sp.sct2e(sclk_id, 1e3) - sp.sct2e(sclk_id, 0.)) / 1e3 if doVerbose: ### if VERBOSE environment variable is present, log information print(( relative_reffrm, reffrm, fk, last_et, stop_et, '{:010.3f}'.format(1. / rate), quat, )) ### Set stop DP-SCLK of window if stop_et < -1e30: ### Use end of encoded DP_SCLK for final window endtim = sp.gdpool('SCLK_PARTITION_END_{}'.format(-sclk_id), 0, 999)[-1] else: ### Else convert stop ET to DP-SCLK endtim = sp.sce2t(sclk_id, stop_et) if doDebug: ### Debug output pprint.pprint( dict(fk=fk, reffrm=reffrm, reffrm_id=reffrm_id, relative_reffrm=relative_reffrm, rate=rate, begtim=begtim, endtim=endtim, diff=endtim - begtim, mtx=mtx, quat=quat)) ### Open CK, once if ck_handle is None: ck_handle = sp.ckopn(output_ck, 'ORX FK REPLACEMENT', 0) ### Write Type 2 CK segment with one record; angular velocity = 0 sp.ckw02(ck_handle, begtim, endtim, reffrm_id, relative_reffrm, '{}[{}]'.format(os.path.basename(fk), reffrm)[:40], 1, [begtim], [endtim], [quat], [[0., 0., 0.]], [rate]) ### Save stop ET for start ET of next pass last_et = stop_et ### Close CK if not (ck_handle is None): sp.ckcls(ck_handle)
def test_ck(kernels, frames_to_convert, fks, stop_ets, output_ck): """ Text CK against FKs at times at which the FKs are valid """ ### Load base kernels (LSK, new FK, SCLK) for kernel in kernels + [output_ck]: sp.furnsh(kernel) ### Set last ET to None so it will be initialized in loop's first pass last_et = None ### Create dict of info; keys will be new FK filenames dt = dict() while fks: ### Pop FK and stop ET off of lists fk = fks.pop() stop_et = stop_ets.pop() ### Create dict for this FK dt[fk] = dict() ### Loop over refernce frames for reffrm in frames_to_convert: ### Get reffrm ID, SCLK ID; N.B. latter comes from new FK reffrm_id = sp.gipool('FRAME_{}'.format(reffrm.upper()), 0, 1)[0] sclk_id = sp.gipool('CK_{}_{}'.format(reffrm_id, 'SCLK'), 0, 1)[0] ### Set start DP-SCLK of window for this old FK (outer loop) ### - Set to zero for first window ### - Covnert ET to DP-SCLK for subsequent windows if last_et is None: et_lo = sp.sct2e(sclk_id, 0.) else: et_lo = last_et ### Load old FK, get RELATIVE frame name, get time-invariant ### matrix, and unload old FK sp.furnsh(fk) relative_reffrm = sp.gcpool( 'TKFRAME_{}_{}'.format(reffrm_id, 'RELATIVE'), 0, 1, 99)[0] sp.unload(fk) ### Get ETs at which to do the tests: ### - 10s after start of window ### - 10s before end of window, or 1e6s after start if last window if stop_et < -1e30: et_test_lo = et_lo + 10. et_test_hi = et_lo + 1e6 else: et_delta = min([10., (stop_et - et_lo) / 3.]) et_test_lo = et_lo + et_delta et_test_hi = stop_et - et_delta ### Save the relative reffrm, the reffrm, the window, and an empty ### dict for this reffrm under this FK dt[fk][reffrm] = (relative_reffrm, et_test_lo, et_test_hi, dict()) ### For next pass last_et = stop_et ### Clear all kernels, and test sp.kclear() assert 0 == sp.ktotal('all') ### Load base kernels including new FK and new CK for kernel in kernels + [output_ck]: sp.furnsh(kernel) ### Loop over old FKs, reffrms, and ETs for fk in dt: for reffrm in dt[fk]: ### Retrieve relative reffrm, ETs and quat dict relative_reffrm, et_test_lo, et_test_hi, dtquat = dt[fk][reffrm] for et in ( et_test_lo, et_test_hi, ): ### Lookup CK-based matrix, convrt to and save quat at each ET dtquat[et] = sp.m2q(sp.pxform(relative_reffrm, reffrm, et)) ### Loop over the old FKs again for fk in dt: ### Clear all kernels, and test sp.kclear() assert 0 == sp.ktotal('all') ### Load only the old FK sp.furnsh(fk) ### Loop over reffrms, and ETs for reffrm in dt[fk]: relative_reffrm, et_test_lo, et_test_hi, dtquat = dt[fk][reffrm] for et in ( et_test_lo, et_test_hi, ): ### Calculate norm of difference of CK-based and FK-based quats quat_error = round( sp.vnorm(dtquat[et] - sp.m2q(sp.pxform(relative_reffrm, reffrm, et))), 16) ### Output that norm as an error for each case, which norm ### should be zero print( dict(fk=fk, quat_error=quat_error, relative_reffrm=relative_reffrm, reffrm=reffrm, et='{:015.4f}'.format(et)))
def get_isd(label): metakernel_dir = config.mro mks = sorted(glob(os.path.join(config.mro, '*.tm'))) time = label['START_TIME'] mro_mk = None for mk in mks: if str(time.year) in os.path.basename(mk): mro_mk = mk spice.furnsh(mro_mk) isd = {} instrument_name = label['INSTRUMENT_NAME'] spacecraft_name = label['SPACECRAFT_NAME'] target_name = label['TARGET_NAME'] # Spice likes ids over names, so grab the ids from the names spacecraft_id = spice.bods2c( 'MRO') # Label specifies: MARS_RECONNAISSANCE_ORBITER ikid = spice.bods2c('MRO_CTX') # Label specifies: CONTEXT CAMERA # Load the instrument and target metadata into the ISD reference_frame = 'IAU_{}'.format(target_name) # Instrument / Spacecraft Metadata isd['OPTICAL_DIST_COEF'] = [ 0, 0, 0 ] # spice.gdpool('INS{}_OD_K'.format(ikid),0, 3) isd['ITRANSL'] = spice.gdpool('INS{}_ITRANSL'.format(ikid), 0, 3) isd['ITRANSS'] = spice.gdpool('INS{}_ITRANSS'.format(ikid), 0, 3) isd['DETECTOR_SAMPLE_ORIGIN'] = spice.gdpool( 'INS{}_BORESIGHT_SAMPLE'.format(ikid), 0, 1) isd['DETECTOR_LINE_ORIGIN'] = spice.gdpool( 'INS{}_BORESIGHT_LINE'.format(ikid), 0, 1) isd['DETECTOR_SAMPLE_SUMMING'] = label['SAMPLING_FACTOR'] isd['STARTING_SAMPLE'] = 0 # label['SAMPLE_FIRST_PIXEL'] isd['TOTAL_LINES'] = nlines = label['IMAGE']['LINES'] isd['TOTAL_SAMPLES'] = label['IMAGE'][ 'LINE_SAMPLES'] # spice.gdpool('INS{}_PIXEL_SAMPLES'.format(ikid), 0, 1) isd['SENSOR_TYPE'] = 'USGSAstroLineScanner' isd['MOUNTING_ANGLES'] = np.zeros(3) isd['ISIS_Z_DIRECTION'] = 1 isd['STARTING_LINE'] = 1.0 isd['DETECTOR_LINE_OFFSET'] = 0.0 # Body Parameters target_name = label['TARGET_NAME'] rad = spice.bodvrd(target_name, 'RADII', 3) a = rad[1][1] b = rad[1][2] isd['SEMI_MAJOR_AXIS'] = a * 1000 # Scale to meters isd['ECCENTRICITY'] = np.sqrt(1 - (b**2 / a**2)) # Standard eccentricity isd['FOCAL'] = spice.gdpool('INS{}_FOCAL_LENGTH'.format(ikid), 0, 1) isd['ABERR'] = 0 isd['ATMREF'] = 0 isd['PLATFORM'] = 1 # It really is hard coded this way... isd['TRI_PARAMETERS'] = np.zeros(18) isd['TRI_PARAMETERS'][15] = isd['FOCAL'] # Time sclock = label['SPACECRAFT_CLOCK_START_COUNT'] et = spice.scs2e(spacecraft_id, sclock) isd['STARTING_EPHEMERIS_TIME'] = et half_lines = nlines / 2 isd['INT_TIME'] = line_rate = label['LINE_EXPOSURE_DURATION'][ 0] * 0.001 # Scale to seconds center_sclock = et + half_lines * line_rate isd['CENTER_EPHEMERIS_TIME'] = center_sclock isd['SCAN_DURATION'] = line_rate * nlines # The socetlinekeywords code is pushing ephemeris and quaternion off of either side of the image. Therefore, # the code needs to know when the start time is. Since we are not pushing off the edge of the image, the start-time # should be identical to the actual image start time. isd['T0_QUAT'] = isd['T0_EPHEM'] = isd['STARTING_EPHEMERIS_TIME'] - isd[ 'CENTER_EPHEMERIS_TIME'] isd['DT_EPHEM'] = 80 * isd['INT_TIME'] # This is every 300 lines # Determine how many ephemeris points to compute n_ephemeris = int(isd['SCAN_DURATION'] / isd['DT_EPHEM']) if n_ephemeris % 2 == 0: n_ephemeris += 1 isd['NUMBER_OF_EPHEM'] = n_ephemeris eph = np.empty((n_ephemeris, 3)) eph_rates = np.empty(eph.shape) current_et = et for i in range(n_ephemeris): loc_direct, _ = spice.spkpos(target_name, current_et, 'IAU_MARS', 'NONE', 'MRO') state, _ = spice.spkezr(target_name, current_et, 'IAU_MARS', 'NONE', 'MRO') eph[i] = loc_direct eph_rates[i] = state[3:] current_et += isd[ 'DT_EPHEM'] # Increment the time by the number of lines being stepped eph *= -1000 # Reverse to be from body center and convert to meters eph_rates *= -1000 # Same, reverse and convert isd['EPHEM_PTS'] = eph.flatten() isd['EPHEM_RATES'] = eph_rates.flatten() # Why should / should not the n_quaternions equal the number of ephemeris pts? n_quaternions = n_ephemeris isd['NUMBER_OF_QUATERNIONS'] = n_quaternions isd['DT_QUAT'] = isd['SCAN_DURATION'] / n_quaternions qua = np.empty((n_quaternions, 4)) current_et = et for i in range(n_quaternions): # Find the rotation matrix camera2bodyfixed = spice.pxform('MRO_CTX', 'IAU_MARS', current_et) q = spice.m2q(camera2bodyfixed) qua[i, :3] = q[1:] qua[i, 3] = q[0] current_et += isd['DT_QUAT'] isd['QUATERNIONS'] = qua.flatten() # Now the 'optional' stuff isd['REFERENCE_HEIGHT'] = label.get('reference_height', 30) isd['MIN_VALID_HT'] = label.get('min_valid_height', -8000) isd['MAX_VALID_HT'] = label.get('max_valid_height', 8000) isd['IMAGE_ID'] = label.get('image_id', 'UNKNOWN') isd['SENSOR_ID'] = label.get('sensor_id', 'USGS_LINE_SCANNER') isd['PLATFORM_ID'] = label.get('platform_id', 'UNKNOWN') isd['TRAJ_ID'] = label.get('traj_id', 'UNKNOWN') isd['COLL_ID'] = label.get('coll_id', 'UNKNOWN') isd['REF_DATE_TIME'] = label.get('ref_date_time', 'UNKNOWN') spice.unload(mro_mk) return isd
for p in iFiles: # getting positions and quaternion in the camera frame et1Str = getImageTime(p) target = 'HAYABUSA' et1 = spiceypy.utc2et(et1Str) frame = 'ITOKAWA_FIXED' center = 'ITOKAWA' state1 = spiceypy.spkezr(target, spiceypy.utc2et(et1Str), frame, 'none', center)[0] stateSun = spiceypy.spkezr('SUN', spiceypy.utc2et(et1Str), frame, 'none', center)[0] frame2 = 'HAYABUSA_AMICA' frame1 = 'ITOKAWA_FIXED' rot1 = spiceypy.pxform(frame1, frame2, et1) quat1 = spiceypy.m2q(rot1) # SPICE data are in km s.setObjectPosition( 'camera', vec3(state1[0] * 1000, state1[1] * 1000, state1[2] * 1000)) # opposite convetion is used between SurRender and JPL s.setObjectAttitude('camera', vec4(quat1[0], -quat1[1], -quat1[2], -quat1[3])) # SPICE data are in km s.setObjectPosition( 'sun', vec3(stateSun[0] * 1000, stateSun[1] * 1000, stateSun[2] * 1000)) s.setObjectPosition('asteroid', vec3(0, 0, 0)) s.printState(s.getState())
def ctx_isd_from_json(data, meta): time = parser.parse(data['START_TIME']) for k in meta: if k.year.year == time.year: obs_kernels = k.path # Load the meta kernel spice.furnsh(obs_kernels) isd = {} spacecraft_name = data['SPACECRAFT_NAME'] spacecraft_id = spice.bods2c('MRO') # Need to map CONTEXT CAMERA to what spice wants - MRO_CTX instrument_name = data['INSTRUMENT_NAME'] ikid = isd['IKCODE'] = spice.bods2c('MRO_CTX') # Instrument / Spacecraft Metadata isd['OPTICAL_DIST_COEF'] = spice.gdpool('INS{}_OD_K'.format(ikid), 0, 3) isd['ITRANSL'] = spice.gdpool('INS{}_ITRANSL'.format(ikid), 0, 3) isd['ITRANSS'] = spice.gdpool('INS{}_ITRANSS'.format(ikid), 0, 3) isd['DETECTOR_SAMPLE_ORIGIN'] = spice.gdpool( 'INS{}_BORESIGHT_SAMPLE'.format(ikid), 0, 1) isd['DETECTOR_LINE_ORIGIN'] = spice.gdpool( 'INS{}_BORESIGHT_LINE'.format(ikid), 0, 1) isd['DETECTOR_SAMPLE_SUMMING'] = data['SAMPLING_FACTOR'] isd['DETECTOR_SAMPLE_SUMMING'] = data['SAMPLING_FACTOR'] isd['STARTING_SAMPLE'] = data['SAMPLE_FIRST_PIXEL'] isd['TOTAL_LINES'] = nlines = data['IMAGE']['LINES'] isd['TOTAL_SAMPLES'] = spice.gdpool('INS{}_PIXEL_SAMPLES'.format(ikid), 0, 1) isd['SENSOR_TYPE'] = 'USGSAstroLineScanner' isd['MOUNTING_ANGLES'] = np.zeros(3) isd['ISIS_Z_DIRECTION'] = 1 isd['STARTING_LINE'] = 1.0 isd['DETECTOR_LINE_OFFSET'] = 0.0 # Body Parameters target_name = find_in_dict(data, 'TARGET_NAME') rad = spice.bodvrd(target_name, 'RADII', 3) a = rad[1][1] b = rad[1][2] isd['SEMI_MAJOR_AXIS'] = a * 1000 # Scale to meters isd['ECCENTRICITY'] = sqrt(1 - (b**2 / a**2)) # Standard eccentricity isd['FOCAL'] = spice.gdpool('INS{}_FOCAL_LENGTH'.format(ikid), 0, 1) isd['ABERR'] = 0 isd['ATMREF'] = 0 isd['PLATFORM'] = 1 # It really is hard coded this way... isd['TRI_PARAMETERS'] = np.zeros(18) isd['TRI_PARAMETERS'][15] = isd['FOCAL'] # Time sclock = find_in_dict(data, 'SPACECRAFT_CLOCK_START_COUNT') et = spice.scs2e(spacecraft_id, sclock) isd['STARTING_EPHEMERIS_TIME'] = et half_lines = nlines / 2 isd['INT_TIME'] = line_rate = data['LINE_EXPOSURE_DURATION'][ 0] * 0.001 # Scale to seconds center_sclock = et + half_lines * line_rate isd['CENTER_EPHEMERIS_TIME'] = center_sclock isd['SCAN_DURATION'] = line_rate * nlines # The socetlinekeywords code is pushing ephemeris and quaternion off of either side of the image. Therefore, # the code needs to know when the start time is. Since we are not pushing off the edge of the image, the start-time # should be identical to the actual image start time. isd['T0_QUAT'] = isd['T0_EPHEM'] = isd['STARTING_EPHEMERIS_TIME'] - isd[ 'CENTER_EPHEMERIS_TIME'] isd['DT_EPHEM'] = 80 * isd['INT_TIME'] # This is every 300 lines # Determine how many ephemeris points to compute n_ephemeris = int(isd['SCAN_DURATION'] / isd['DT_EPHEM']) if n_ephemeris % 2 == 0: n_ephemeris += 1 isd['NUMBER_OF_EPHEM'] = n_ephemeris eph = np.empty((n_ephemeris, 3)) eph_rates = np.empty(eph.shape) current_et = et for i in range(n_ephemeris): loc_direct, _ = spice.spkpos(target_name, current_et, 'IAU_MARS', 'LT+S', 'MRO') state, _ = spice.spkezr(target_name, current_et, 'IAU_MARS', 'LT+S', 'MRO') eph[i] = loc_direct eph_rates[i] = state[3:] current_et += isd[ 'DT_EPHEM'] # Increment the time by the number of lines being stepped eph *= -1000 # Reverse to be from body center and convert to meters eph_rates *= -1000 # Same, reverse and convert isd['EPHEM_PTS'] = eph.flatten() isd['EPHEM_RATES'] = eph_rates.flatten() # Why should / should not the n_quaternions equal the number of ephemeris pts? n_quaternions = n_ephemeris isd['NUMBER_OF_QUATERNIONS'] = n_quaternions isd['DT_QUAT'] = isd['SCAN_DURATION'] / n_quaternions qua = np.empty((n_quaternions, 4)) current_et = et for i in range(n_quaternions): # Find the rotation matrix camera2bodyfixed = spice.pxform('MRO_CTX', 'IAU_MARS', current_et) q = spice.m2q(camera2bodyfixed) qua[i][:3] = q[1:] qua[i][-1] = q[0] current_et += isd['DT_QUAT'] isd['QUATERNIONS'] = qua.flatten() # Now the 'optional' stuff isd['REFERENCE_HEIGHT'] = data.get('reference_height', 30) isd['MIN_VALID_HT'] = data.get('min_valid_height', -8000) isd['MAX_VALID_HT'] = data.get('max_valid_height', 8000) isd['IMAGE_ID'] = data.get('image_id', 'UNKNOWN') isd['SENSOR_ID'] = data.get('sensor_id', 'USGS_LINE_SCANNER') isd['PLATFORM_ID'] = data.get('platform_id', 'UNKNOWN') isd['TRAJ_ID'] = data.get('traj_id', 'UNKNOWN') isd['COLL_ID'] = data.get('coll_id', 'UNKNOWN') isd['REF_DATE_TIME'] = data.get('ref_date_time', 'UNKNOWN') spice.unload(obs_kernels) return json.dumps(isd, cls=NumpyEncoder)