def spacecraft_direction(self): """ Returns the x axis of the first velocity vector relative to the spacecraft. This indicates of the craft is moving forwards or backwards. From LROC Frame Kernel: lro_frames_2014049_v01.tf "+X axis is in the direction of the velocity vector half the year. The other half of the year, the +X axis is opposite the velocity vector" Hence we rotate the first velocity vector into the sensor reference frame, but the X component of that vector is inverted compared to the spacecraft so a +X indicates backwards and -X indicates forwards The returned velocity is also slightly off from the spacecraft velocity due to the sensor being attached to the craft with wax. Returns ------- direction : double X value of the first velocity relative to the sensor """ frame_chain = self.frame_chain lro_bus_id = spice.bods2c('LRO_SC_BUS') time = self.ephemeris_start_time state, _ = spice.spkezr(self.spacecraft_name, time, 'J2000', 'None', self.target_name) position = state[:3] velocity = state[3:] rotation = frame_chain.compute_rotation(1, lro_bus_id) rotated_velocity = spice.mxv(rotation._rots.as_matrix()[0], velocity) return rotated_velocity[0]
def ikid(self): """ Returns ------- : int Naif ID used to for indentifying the instrument in Spice kernels """ return spice.bods2c(self.instrument_id)
def _tc_id(self): """ Returns ikid of LISM_TC1 or LISM_TC2, depending which camera was used for capturing the image. Some keys are stored in the IK kernel under a general ikid for TC1/TC2 presumably because they are not affected by the addtional parameters encoded in the ikid returned by self.ikid. This method exists for those gdpool calls. """ return spice.bods2c("LISM_{}".format(super().instrument_id))
def ikid(self): """ Returns the Naif ID code for HRSC SRC. Returns ------- : int Naif ID used to for indentifying the instrument in Spice kernels """ return spice.bods2c("MEX_HRSC_SRC")
def ikid(self): """ Returns the Naif ID code for the HRSC head instrument This would be the Naif ID code for the base (or "head") instrument. Returns ------- : int Naif ID used to for indentifying the instrument in Spice kernels """ return spice.bods2c("MEX_HRSC_HEAD")
def utc2scs_spice(tais, sc): res = {} try: scid = sp.bods2c(sc) for tai in tais: et = sp.unitim(tai, 'tai', 'et') obt = sp.sce2s(scid, et) utc = sp.et2utc(et, 'isoc', 3) res[utc] = obt except sp.stypes.SpiceyError as ex: raise GeometrySpiceError(ex.value) else: return res
def state_internal(func_spice, utc, utc_end, deltat, kind, observer, target, ref, abcorr): try: tgt = sp.bods2c(target) obs = sp.bods2c(observer) except sp.stypes.SpiceyError as ex: raise GeometrySpiceError(ex.value) if abcorr is None: abcorr = 'NONE' if kind is not None: kind = kind.lower() try: xfunc = _POSITION_KIND[kind] except KeyError: raise ValidationError(kind) kwargs = {'observer': obs, 'target': tgt, 'ref': ref, 'abcorr': abcorr} tais = utc2tai(utc, utc_end, deltat) return wrap_result(distribute_work(func_spice, xfunc, tais, **kwargs))
def fikid(self): """ Naif ID code of the filter dependent instrument codes. Expects filter_number to be defined. This should be an integer containing the filter number from the pds3 label. Expects ikid to be defined. This should be the integer Naid ID code for the instrument. Returns ------- : int Naif ID code used in calculating focal length """ return spice.bods2c(self.instrument_id)
def __init__(self, obj, kernel=None): State.__init__(self, name=obj) if not core._spice_setup: core._setup_spice() if kernel is None: kernel = core.find_kernel(obj) core.load_kernel(kernel) self.kernel = kernel if isinstance(obj, int): obj = str(obj) naifid = spice.bods2c(obj) self.obj = obj self.naifid = naifid
def scs2utc_spice(scs, sc, deltat): res = {} try: scid = sp.bods2c(sc) et = sp.scs2e(scid, scs) if deltat is not None: tai = sp.unitim(et, 'et', 'tai') tai += deltat et = sp.unitim(tai, 'tai', 'et') utc = sp.et2utc(et, 'isoc', 3) res[scs] = utc except sp.stypes.SpiceyError as ex: raise GeometrySpiceError(ex.value) else: return res
def _tc_id(self): """ Returns ikid of LISM_TC1 or LISM_TC2, depending which camera was used for capturing the image. Some keys are stored in the IK kernel under a general ikid for TC1/TC2 presumably because they are not affected by the addtional parameters encoded in the ikid returned by self.ikid. This method exists for those gdpool calls. Expects instrument_id to be defined in the Pds3Label mixin. This should be a string containing either TC1 or TC2 Returns ------- : int ikid of LISM_TC1 or LISM_TC2 """ return spice.bods2c("LISM_{}".format(super().instrument_id))
def target_id(self): return spice.bods2c(self.target_name)
def spacecraft_id(self): return spice.bods2c(self.spacecraft_name)
all_boresight_vectors, all_boresight_names = readBoresightFile(BORESIGHT_VECTOR_FILE_PATH) for fileName, realBoresightUsed in fileNames_in.items(): hdf5file_path = os.path.join( r"C:\Users\iant\Documents\DATA\hdf5_copy\hdf5_level_0p3a", fileName[0:4], fileName[4:6], fileName[6:8], fileName+".h5") hdf5FileIn = h5py.File(hdf5file_path, "r") observationDTimes = hdf5FileIn["Geometry/ObservationDateTime"][...] dref = "TGO_NOMAD_SO" channelId = sp.bods2c(dref) #find channel id number [channelShape, name, boresightVector, nvectors, boresightVectorbounds] = sp.getfov(channelId, 4) SPICE_REFERENCE_FRAME = "TGO_SPACECRAFT" SPICE_ABERRATION_CORRECTION = "None" SPICE_OBSERVER = "-143" observationDTimesStart = [str(observationDTime[0]) for observationDTime in observationDTimes] observationDTimesEnd = [str(observationDTime[1]) for observationDTime in observationDTimes] obsTimesStart = [sp.utc2et(datetime.strip("<b>").strip("'")) for datetime in observationDTimesStart] obsTimesEnd = [sp.utc2et(datetime.strip("<b>").strip("'")) for datetime in observationDTimesEnd] #find times in occultation obsTimeMids = [obsTimesStart[0], np.mean([obsTimesStart[0],obsTimesStart[-1]]), obsTimesStart[-1]]
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 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
def ikid(self): return spice.bods2c(self.instrument_id)
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)
def body_code(self): '''Spice body code''' return spice.bods2c(self.targ)
def isd_from_json(data, meta): instrument_name = { 'IMAGING SCIENCE SUBSYSTEM NARROW ANGLE': 'CASSINI_ISS_NAC', 'IMAGING SCIENCE SUBSYSTEM WIDE ANGLE': 'CASSINI_ISS_WAC', 'IMAGING SCIENCE SUBSYSTEM - NARROW ANGLE': 'CASSINI_ISS_NAC', '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' } spacecraft_names = {'CASSINI ORBITER': 'CASSINI', 'MESSENGER': 'MESSENGER'} # This is the return dict isd = {} # Meta kernels are keyed by body, spacecraft, and year - grab from the data spacecraft_name = spacecraft_names[data['spacecraft_id']] target_name = data['target_name'] time = parser.parse(data['capture_date']) for k in meta: if k.year.year == time.year: obs_kernels = k.path # Load the meta kernel spice.furnsh(obs_kernels) path, tpe, handle, found = spice.kdata(0, 'TEXT') if not found: directory = os.path.dirname(path) directory = os.path.abspath(os.path.join(directory, '../iak')) additional_ik = glob.glob(directory + '/*.ti') spice.furnsh(additional_ik) # Spice likes ids over names, so grab the ids from the names instrument_name = instrument_name[data['instrument']] spacecraft_id = spice.bods2c(spacecraft_name) ikid = spice.bods2c(instrument_name) # Load the instrument and target metadata into the ISD isd['instrument_id'] = instrument_name isd['target_name'] = target_name isd['spacecraft_name'] = spacecraft_name # Prepend IAU to all instances of the body name reference_frame = 'IAU_{}'.format(target_name) # Load information from the IK kernel isd['focal_length'] = spice.gdpool('INS{}_FOCAL_LENGTH'.format(ikid), 0, 1) isd['focal_length_epsilon'] = spice.gdpool( 'INS{}_FL_UNCERTAINTY'.format(ikid), 0, 1) isd['nlines'] = spice.gipool('INS{}_PIXEL_LINES'.format(ikid), 0, 1) isd['nsamples'] = spice.gipool('INS{}_PIXEL_SAMPLES'.format(ikid), 0, 1) isd['original_half_lines'] = isd['nlines'] / 2.0 isd['original_half_samples'] = isd['nsamples'] / 2.0 isd['pixel_pitch'] = spice.gdpool('INS{}_PIXEL_PITCH'.format(ikid), 0, 1) isd['ccd_center'] = spice.gdpool('INS{}_CCD_CENTER'.format(ikid), 0, 2) isd['ifov'] = spice.gdpool('INS{}_IFOV'.format(ikid), 0, 1) isd['boresight'] = spice.gdpool('INS{}_BORESIGHT'.format(ikid), 0, 3) isd['transx'] = spice.gdpool('INS{}_TRANSX'.format(ikid), 0, 3) isd['transy'] = spice.gdpool('INS{}_TRANSY'.format(ikid), 0, 3) isd['itrans_sample'] = spice.gdpool('INS{}_ITRANSS'.format(ikid), 0, 3) isd['itrans_line'] = spice.gdpool('INS{}_ITRANSL'.format(ikid), 0, 3) try: isd['odt_x'] = spice.gdpool('INS-{}_OD_T_X'.format(ikid), 0, 10) except: isd['odt_x'] = np.zeros(10) isd['odt_x'][1] = 1 try: isd['odt_y'] = spice.gdpool('INS-{}_OD_T_Y'.format(ikid), 0, 10) except: isd['odt_y'] = np.zeros(10) isd['odt_y'][2] = 1 try: isd['starting_detector_sample'] = spice.gdpool( 'INS{}_FPUBIN_START_SAMPLE'.format(ikid), 0, 1) except: isd['starting_detector_sample'] = 0 try: isd['starting_detector_line'] = spice.gdpool( 'INS{}_FPUBIN_START_LINE'.format(ikid), 0, 1) except: isd['starting_detector_line'] = 0 # Get temperature from SPICE and adjust focal length if 'focal_plane_temperature' in data.keys(): try: # TODO: Remove once WAC temperature dependent is working temp_coeffs = spice.gdpool('INS-{}_FL_TEMP_COEFFS'.format(ikid), 0, 6) temp = data['focal_plane_temperature'] isd['focal_length'] = distort_focal_length(temp_coeffs, temp) except: isd['focal_length'] = spice.gdpool( 'INS-{}_FOCAL_LENGTH'.format(ikid), 0, 1) else: isd['focal_length'] = spice.gdpool('INS-{}_FOCAL_LENGTH'.format(ikid), 0, 1) # Get the radii from SPICE rad = spice.bodvrd(isd['target_name'], 'RADII', 3) radii = rad[1] isd['semi_major_axis'] = rad[1][0] isd['semi_minor_axis'] = rad[1][1] # Now time sclock = data['spacecraft_clock_count'] exposure_duration = data['exposure_duration'] 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 et = spice.scs2e(spacecraft_id, sclock) et += (exposure_duration / 2.0) isd['ephemeris_time'] = et # Get the Sensor Position loc, _ = spice.spkpos(isd['target_name'], et, reference_frame, 'LT+S', spacecraft_name) loc *= -1000 isd['x_sensor_origin'] = loc[0] isd['y_sensor_origin'] = loc[1] isd['z_sensor_origin'] = loc[2] # Get the rotation angles from MDIS NAC frame to Mercury body-fixed frame camera2bodyfixed = spice.pxform(instrument_name, reference_frame, et) opk = spice.m2eul(camera2bodyfixed, 3, 2, 1) isd['omega'] = opk[2] isd['phi'] = opk[1] isd['kappa'] = opk[0] # Get the sun position sun_state, lt = spice.spkezr("SUN", et, reference_frame, data['lighttime_correction'], target_name) # Convert to meters isd['x_sun_position'] = sun_state[0] * 1000 isd['y_sun_position'] = sun_state[1] * 1000 isd['z_sun_position'] = sun_state[2] * 1000 # Get the velocity v_state, lt = spice.spkezr(spacecraft_name, et, reference_frame, data['lighttime_correction'], target_name) isd['x_sensor_velocity'] = v_state[3] * 1000 isd['y_sensor_velocity'] = v_state[4] * 1000 isd['z_sensor_velocity'] = v_state[5] * 1000 # Misc. insertion # A lookup here would be smart - similar to the meta kernals, what is the newest model, etc. if 'model_name' not in data.keys(): isd['model_name'] = 'ISIS_MDISNAC_USGSAstro_1_Linux64_csm30.so' isd['min_elevation'] = data['min_elevation'] isd['max_elevation'] = data['max_elevation'] spice.unload(obs_kernels) # Also unload iak return isd