def obt2utc(self, obt_string): # Obt to Ephemeris time (seconds past J2000) ephemeris_time = spiceypy.scs2e(self.solar_orbiter_naif_id, obt_string) # Ephemeris time to Utc # Format of output epoch: ISOC (ISO Calendar format, UTC) # Digits of precision in fractional seconds: 3 return spiceypy.et2utc(ephemeris_time, "ISOC", 3)
def sunDistanceAU(time: str, target: str) -> float: """Returns distance in AU between Sun and observed body from MRO.""" base_kernel_path = Path(isis.environ["ISIS3DATA"]) / "base" / "kernels" lsk = sorted(Path(base_kernel_path / "lsk").glob("naif*.tls"))[-1] pck = sorted(Path(base_kernel_path / "spk").glob("de*.bsp"))[-1] sat = sorted(Path(base_kernel_path / "spk").glob("mar*.bsp"))[-1] sclk = sorted( Path( Path(isis.environ["ISIS3DATA"]) / "mro" / "kernels" / "sclk" ).glob("MRO_SCLKSCET.*.65536.tsc") )[-1] spiceypy.furnsh([str(lsk), str(pck), str(sat), str(sclk)]) et = spiceypy.scs2e(-74999, time) targ = target.lower() if targ == "sky" or targ == "cal" or targ == "phobos" or targ == "deimos": targ = "mars" (sunv, lt) = spiceypy.spkpos(targ, et, "J2000", "LT+S", "sun") sunkm = spiceypy.vnorm(sunv) # Return in AU units return sunkm / 1.49597870691e8
def ephemeris_stop_time(self): if self.spacecraft_clock_stop_count: return spice.scs2e(self.spacecraft_id, self.spacecraft_clock_stop_count) else: return spice.str2et( self.utc_stop_time.strftime("%Y-%m-%d %H:%M:%S"))
def ephemeris_start_time(self): if not hasattr(self, '_ephemeris_start_time'): sclock = self.label['IsisCube']['Instrument'][ 'SpacecraftClockCount'] self._ephemeris_start_time = spice.scs2e(self.spacecraft_id, sclock) return self._ephemeris_start_time
def toUTC(self, sclk, fmt='ISOC', prec=5): '''Convert Spacecraft clock time to UTC''' if not self.loaded: self.load() if isinstance(sclk, (int, float)): sclk = '1/%f' % sclk elif '_' in sclk: sclk = '1/' + sclk.split('_')[0] return spice.et2utc(spice.scs2e(self.sc, sclk), fmt, prec)
def ephemeris_start_time(self): """ The starting ephemeris time for LRO is computed by taking the LRO:SPACECRAFT_CLOCK_PREROLL_COUNT, as defined in the label, and adding offsets that were taken from an IAK. ------- : double Starting ephemeris time of the image """ start_time = spice.scs2e(self.spacecraft_id, self.label['IsisCube']['Instrument']['SpacecraftClockPrerollCount']) return start_time + self.constant_time_offset + self.additional_preroll * self.exposure_duration
def ephemeris_start_time(self): """ Compute the center ephemeris time for a Dawn Frame camera. This is done via a spice call but 193 ms needs to be added to account for the CCD being discharged or cleared. """ if not hasattr(self, '_ephemeris_start_time'): sclock = self.spacecraft_clock_start_count self._ephemeris_start_time = spice.scs2e(self.spacecraft_id, sclock) self._ephemeris_start_time += 193.0 / 1000.0 return self._ephemeris_start_time
def ephemeris_stop_time(self): """ Returns the ephemeris stop time of the image. Expects spacecraft_id to be defined. This must be the integer Naif Id code for the spacecraft. Expects spacecraft_clock_stop_count to be defined. This must be a string containing the stop clock count of the spacecraft Returns ------- : double Ephemeris stop time of the image """ return spice.scs2e(self.spacecraft_id, self.spacecraft_clock_stop_count)
def ephemeris_start_time(self): """ Returns the ephemeris start time of the image. Expects spacecraft_id to be defined. This should be the integer Naif ID code for the spacecraft. Returns ------- : float ephemeris start time of the image """ if not hasattr(self, '_ephemeris_start_time'): sclock = self.label['IsisCube']['Instrument'][ 'SpacecraftClockCount'] self._ephemeris_start_time = spice.scs2e(self.spacecraft_id, sclock) return self._ephemeris_start_time
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 ephemeris_stop_time(self): """ Returns the ephemeris stop time of the image. Expects spacecraft_id to be defined. This must be the integer Naif Id code for the spacecraft. Expects spacecraft_clock_stop_count to be defined. This must be a string containing the stop clock count of the spacecraft Returns ------- : double Ephemeris stop time of the image """ if self.spacecraft_clock_stop_count: return spice.scs2e(self.spacecraft_id, self.spacecraft_clock_stop_count) else: return spice.str2et( self.utc_stop_time.strftime("%Y-%m-%d %H:%M:%S"))
def ephemeris_start_time(self): """ Returns the ephemeris_start_time of the image. Expects spacecraft_clock_start_count to be defined. This should be a float containing the start clock count of the spacecraft. Expects spacecraft_id to be defined. This should be the integer Naif ID code for the spacecraft. Returns ------- : float ephemeris start time of the image. """ if not hasattr(self, '_ephemeris_start_time'): sclock = self.spacecraft_clock_start_count self._starting_ephemeris_time = spice.scs2e( self.spacecraft_id, sclock) return self._starting_ephemeris_time
def sclk2jd(self, sclk): """ Convert spacecraft time to TDB Julian Dates (JD). Parameters: sclk (ndarray): Timestamps in TESS Spacecraft Time. Returns: ndarray: Timestamps in TDB Julian Dates (TDB). """ sclk = np.atleast_1d(sclk) N = len(sclk) jd = np.empty(N, dtype='float64') for k in range(N): et = spiceypy.scs2e(-95, sclk[k]) jd[k] = spiceypy.unitim(et, 'ET', 'JDTDB') return jd
def ephemeris_start_time(self): """ Overridden to use the alternate instrument ID. Also computes an offset to match what is being done in ISIS code. Expects spacecraft_clock_start_count to be defined. Returns ------- : float ephemeris start time of the image """ ephemeris_start_time = spice.scs2e(self.alt_ikid, str(self.spacecraft_clock_start_count)) if self.exposure_duration <= .420: offset1 = 7.0 / 8.0 * 4.48 else: offset1 = 3.0 / 8.0 * 4.48 offset2 = 1.0 / 64.0 * 4.48 return ephemeris_start_time + offset1 + offset2
def scet_to_datetime(self, scet): """ Convert SCET to datetime. Parameters ---------- scet : `str`, `int`, `float` SCET time as number or spacecraft clock string e.g. `1.0` or `'625237315:44104'` Returns ------- `datetime.datetime` Datetime of SCET """ ephemeris_time = None if isinstance(scet, (float, int)): ephemeris_time = spiceypy.sct2e(SOLAR_ORBITER_ID, scet) elif isinstance(scet, str): ephemeris_time = spiceypy.scs2e(SOLAR_ORBITER_ID, scet) return spiceypy.et2datetime(ephemeris_time)
def scet_to_utc(self, scet): """ Convert SCET to UTC time string in ISO format. Parameters ---------- scet : `str`, `int`, `float` SCET time as a number or spacecraft clock string e.g. `1.0` or `625237315:44104` Returns ------- `str` UTC time string in ISO format """ # Obt to Ephemeris time (seconds past J2000) ephemeris_time = None if isinstance(scet, (float, int)): ephemeris_time = spiceypy.sct2e(SOLAR_ORBITER_ID, scet) elif isinstance(scet, str): ephemeris_time = spiceypy.scs2e(SOLAR_ORBITER_ID, scet) # Ephemeris time to Utc # Format of output epoch: ISOC (ISO Calendar format, UTC) # Digits of precision in fractional seconds: 6 return spiceypy.et2utc(ephemeris_time, "ISOC", 3)
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 ephemeris_start_time(self): if not hasattr(self, '_ephemeris_start_time'): sclock = self.spacecraft_clock_start_count self._starting_ephemeris_time = spice.scs2e(self.spacecraft_id, sclock) return self._starting_ephemeris_time
def get_fits_headers(self, *, start_time, average_time): try: et = spiceypy.scs2e(-144, str(average_time)) except (SpiceBADPARTNUMBER, SpiceINVALIDSCLKSTRING): et = spiceypy.utc2et(average_time.isot) # HeliographicStonyhurst solo_sun_hg, sun_solo_lt = spiceypy.spkezr('SOLO', et, 'SUN_EARTH_CEQU', 'None', 'Sun') # Convert to spherical and add units hg_rad, hg_lon, hg_lat = spiceypy.reclat(solo_sun_hg[:3]) hg_rad = hg_rad * u.km hg_lat, hg_lon = (hg_lat * u.rad).to('deg'), (hg_lon * u.rad).to('deg') # Calculate radial velocity add units rad_vel, *_ = spiceypy.reclat(solo_sun_hg[3:]) rad_vel = rad_vel * (u.km / u.s) rsun_arc = np.arcsin((1 * u.R_sun) / hg_rad).decompose().to('arcsec') solo_sun_hee, _ = spiceypy.spkezr('SOLO', et, 'SOLO_HEE', 'None', 'Sun') solo_sun_hci, _ = spiceypy.spkezr('SOLO', et, 'SOLO_HCI', 'None', 'Sun') solo_sun_hae, _ = spiceypy.spkezr('SOLO', et, 'SUN_ARIES_ECL', 'None', 'Sun') solo_sun_heeq, _ = spiceypy.spkezr('SOLO', et, 'SOLO_HEEQ', 'None', 'Sun') solo_sun_gse, earth_solo_lt = spiceypy.spkezr('SOLO', et, 'EARTH_SUN_ECL', 'None', 'Earth') sun_earth_hee, sun_earth_lt = spiceypy.spkezr('Earth', et, 'SOLO_HEE', 'None', 'Sun') precision = 2 headers = ( ('SPICE_MK', self.meta_kernel_path.name, 'SPICE meta kernel file'), ('RSUN_ARC', rsun_arc.to_value('arcsec'), '[arcsec] Apparent photospheric solar radius'), # ('CAR_ROT', ,), Doesn't make sense as we don't have a crpix ('HGLT_OBS', np.around(hg_lat.to_value('deg'), precision), '[deg] s/c heliographic latitude (B0 angle)'), ('HGLN_OBS', np.around(hg_lon.to_value('deg'), precision), '[deg] s/c heliographic longitude'), # Not mistake same values know by different terms ('CRLT_OBS', np.around(hg_lat.to_value('deg'), precision), '[deg] s/c Carrington latitude (B0 angle)'), ('CRLN_OBS', np.around(hg_lon.to_value('deg'), precision), '[deg] s/c Carrington longitude (L0 angle)'), ('DSUN_OBS', np.around(hg_rad.to_value('m'), precision), '[m] s/c distance from Sun'), ('HEEX_OBS', np.around((solo_sun_hee[0]*u.km).to_value('m'), precision), '[m] s/c Heliocentric Earth Ecliptic X'), ('HEEY_OBS', np.around((solo_sun_hee[1]*u.km).to_value('m'), precision), '[m] s/c Heliocentric Earth Ecliptic Y'), ('HEEZ_OBS', np.around((solo_sun_hee[2]*u.km).to_value('m'), precision), '[m] s/c Heliocentric Earth Ecliptic Z'), ('HCIX_OBS', np.around((solo_sun_hci[0]*u.km).to_value('m'), precision), '[m] s/c Heliocentric Inertial X'), ('HCIY_OBS', np.around((solo_sun_hci[1]*u.km).to_value('m'), precision), '[m] s/c Heliocentric Inertial Y'), ('HCIZ_OBS', np.around((solo_sun_hci[2]*u.km).to_value('m'), precision), '[m] s/c Heliocentric Inertial Z'), ('HCIX_VOB', np.around((solo_sun_hci[3]*(u.km/u.s)).to_value('m/s'), precision), '[m/s] s/c Heliocentric Inertial X Velocity'), ('HCIY_VOB', np.around((solo_sun_hci[4]*(u.km/u.s)).to_value('m/s'), precision), '[m/s] s/c Heliocentric Inertial Y Velocity'), ('HCIZ_VOB', np.around((solo_sun_hci[5]*(u.km/u.s)).to_value('m/s'), precision), '[m/s] s/c Heliocentric Inertial Z Velocity'), ('HAEX_OBS', np.around((solo_sun_hae[0]*u.km).to_value('m'), precision), '[m] s/c Heliocentric Aries Ecliptic X'), ('HAEY_OBS', np.around((solo_sun_hae[1]*u.km).to_value('m'), precision), '[m] s/c Heliocentric Aries Ecliptic Y'), ('HAEZ_OBS', np.around((solo_sun_hae[0]*u.km).to_value('m'), precision), '[m] s/c Heliocentric Aries Ecliptic Z'), ('HEQX_OBS', np.around((solo_sun_heeq[0]*u.km).to_value('m'), precision), '[m] s/c Heliocentric Earth Equatorial X'), ('HEQY_OBS', np.around((solo_sun_heeq[1]*u.km).to_value('m'), precision), '[m] s/c Heliocentric Earth Equatorial Y'), ('HEQZ_OBS', np.around((solo_sun_heeq[2]*u.km).to_value('m'), precision), '[m] s/c Heliocentric Earth Equatorial Z'), ('GSEX_OBS', np.around((solo_sun_gse[0]*u.km).to_value('m'), precision), '[m] s/c Geocentric Solar Ecliptic X'), ('GSEY_OBS', np.around((solo_sun_gse[1]*u.km).to_value('m'), precision), '[m] s/c Geocentric Solar Ecliptic Y'), ('GSEZ_OBS', np.around((solo_sun_gse[2]*u.km).to_value('m'), precision), '[m] s/c Geocentric Solar Ecliptic Y'), ('OBS_VR', np.around(rad_vel.to_value('m/s'), precision), '[m/s] Radial velocity of spacecraft relative to Sun'), ('EAR_TDEL', np.around(sun_earth_lt - sun_solo_lt, precision), '[s] Time(Sun to Earth) - Time(Sun to S/C)'), ('SUN_TIME', np.around(sun_solo_lt, precision), '[s] Time(Sun to s/c)'), ('DATE_EAR', (start_time + np.around((sun_earth_lt - sun_solo_lt), precision)*u.s).fits, 'Start time of observation, corrected to Earth'), ('DATE_SUN', (start_time - np.around(sun_solo_lt, precision)*u.s).fits, 'Start time of observation, corrected to Su'), ) return headers
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 ephemeris_start_time(self): return spice.scs2e(self.spacecraft_id, self.spacecraft_clock_start_count)
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 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
def starting_ephemeris_time(self): if not hasattr(self, '_starting_ephemeris_time'): sclock = self.label['SPACECRAFT_CLOCK_START_COUNT'] self._starting_ephemeris_time = spice.scs2e( self.spacecraft_id, sclock) return self._starting_ephemeris_time