def caps_all_anodes(tempdatetime): et = spice.datetime2et(tempdatetime) sclkdp = spice.sce2c( -82, et) # converts an et to a continuous encoded sc clock (ticks) caps_els_anode_vecs = [] for anodenumber, x in enumerate(np.arange(70, -90, -20)): # print(anodenumber, x) rotationmatrix_anode = spice.spiceypy.axisar(np.array( [1, 0, 0]), x * spice.rpd()) # Get angles for different anodes # print("rotationmatrix_anode", rotationmatrix_anode) postanode_rotation = spice.vhat( spice.mxv(rotationmatrix_anode, -spice.spiceypy.getfov( -82821, 20)[2])) # Apply rotation for anodes # print("postanode_rotation", postanode_rotation) # print("caps_els_boresight", caps_els_boresight) cassini_caps_mat = spice.ckgp( -82821, sclkdp, 0, 'CASSINI_CAPS_BASE')[0] # Get actuation angle # print("cassini_caps_mat", cassini_caps_mat) cassini_caps_act_vec = spice.mxv( cassini_caps_mat, postanode_rotation) # Rotate with actuator # print("Actuating frame", cassini_caps_act_vec) CAPS_act_2_titan_cmat = spice.ckgp( -82000, sclkdp, 0, 'IAU_TITAN')[0] # Find matrix to transform to IAU_TITAN frame CAPS_act_2_titan_cmat_transpose = spice.xpose( CAPS_act_2_titan_cmat) # Tranpose matrix rotated_vec = spice.mxv(CAPS_act_2_titan_cmat_transpose, cassini_caps_act_vec) # Apply Matrix # print("rotated_vec ", rotated_vec) caps_els_anode_vecs.append(rotated_vec) return caps_els_anode_vecs
def convert_to_inst(self, coords): """ Convert the given coordinates to the instrument frame Parameters ---------- coords : `astropy.coordinate.SkyCoord` The coordinates to transform to the instrument frame frame : `str`, optional The instrument coordinate frame (ILS or OPT) Returns ------- tuple x and y coordinates """ icrs_coords = coords.transform_to('icrs') cart_coords = icrs_coords.represent_as('cartesian') et = spiceypy.datetime2et(coords.obstime.to_datetime()) sc = spiceypy.sce2c(SOLAR_ORBITER_ID, et) cmat, sc = spiceypy.ckgp(SOLAR_ORBITER_STIX_ILS_FRAME_ID, sc, 0, 'J2000') vec = cmat @ cart_coords.xyz.value # Rotate about z so +x towards the Sun # vec = np.array([[-1, 0, 0], [0, -1, 0], [0, 0, 1]]) @ vec distance, latitude, longitude = spiceypy.reclat(vec.reshape(3)) y = (latitude + np.pi) * u.rad x = (np.pi/2 - longitude) * u.rad return x, y
def find_cmatrix(ets_in): """find list of c-matrices given ephemeris times and time errors""" obs="-143" ref="J2000" inst = int(obs) * 1000 matrices = [] sctol = sp.sctiks(int(obs), SPICE_TOLERANCE) for et in ets_in: scticks = sp.sce2c(int(obs), et) [matrix, sc_time] = sp.ckgp(inst, scticks, sctol, ref) matrices.append(matrix) return matrices
def getCameraMatrix(frame, spacecraft, instrument, ephemerisTime): """ Get camera pointing matrix C. This is the world-to-camera matrix, from the base frame to the given instrument's fixed frame at the given ephemeris time. """ sclkdp = spice.sce2c(spacecraft, ephemerisTime) # spacecraft clock double # sclkch = spice.sce2s(spacecraft, ephemerisTime) # spacecraft clock string # print 'sclkdp',sclkdp # see https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/ckgp_c.html tolerance = spice.sctiks(spacecraft, "0:00:800") # time tolerance C, clkout, found = spice.ckgp(instrument, sclkdp, tolerance, frame) if not found: print 'camera pointing matrix not found for time', sclkdp # continue C = None return C
def cassini_ramdirection_SCframe(tempdatetime, target='CASSINI', frame='J2000', observ='titan', corrtn='NONE', output=False): et = spice.datetime2et(tempdatetime) state, ltime = spice.spkezr(target, et, frame, corrtn, observ) ramdir = spice.vhat(state[3:6]) # Gets Attitude sclkdp = spice.sce2c(-82, et) # converts an et to a continuous encoded sc clock (ticks) ckgp_output = spice.ckgp(-82000, sclkdp, 0, frame) cmat = ckgp_output[0] ram_unit = spice.mxv(cmat, ramdir) if output: print('ET = {:20.6f}'.format(et)) print('VX = {:20.6f}'.format(ram_unit[0])) print('VY = {:20.6f}'.format(ram_unit[1])) print('VZ = {:20.6f}'.format(ram_unit[2])) return ram_unit
def caps_crosstrack(tempdatetime, windspeed): et = spice.datetime2et(tempdatetime) state, ltime = spice.spkezr("CASSINI", et, "IAU_TITAN", "NONE", "titan") ramdir = spice.vhat(state[3:6]) # print("ramdir",ramdir) # Gets Attitude sclkdp = spice.sce2c( -82, et) # converts an et to a continuous encoded sc clock (ticks) ckgp_output = spice.ckgp(-82000, sclkdp, 0, "IAU_TITAN") cmat = ckgp_output[0] spacecraft_axis = np.array([0, 0, 1]) rotated_spacecraft_axis = spice.mxv(cmat, spacecraft_axis) # print("cmat", cmat) # print("rotated spacecraft axis",rotated_spacecraft_axis) ram_unit = spice.mxv(cmat, -ramdir) # Ram Unit in SC coords # print("ram_unit",ram_unit) if windspeed < 0: rotationmatrix = spice.axisar(np.array([0, 0, -1]), 90 * spice.rpd()) if windspeed > 0: rotationmatrix = spice.axisar(np.array([0, 0, -1]), -90 * spice.rpd()) # print(rotationmatrix) crossvec = spice.mxv( rotationmatrix, ram_unit) # Rotate ram unit to find crosstrack velocity vector # print("temp crossvec",crossvec) # print("vsep SC Frame",spice.vsep(ram_unit,crossvec)*spice.dpr()) cmat_t = spice.xpose(cmat) crossvec_titan = spice.mxv(cmat_t, crossvec) # Transform back to IAU Titan Frame # print("crossvec", crossvec) # print("crossvec_titan", crossvec_titan, spice.unorm(crossvec_titan)) # print("vsep titan frame", spice.vsep(ramdir, crossvec_titan) * spice.dpr()) return crossvec_titan
def get_orientation(self, date, frame): """ Get the orientation or roll, pith and yaw of STIX (ILS or OPT). Parameters ---------- date : `datetime.datetime` Date at which to obtain orientation information frame : `str` Name of the coordinate frame Returns ------- tuple Roll, pitch and yaw of the spacecraft """ et = spiceypy.datetime2et(date) sc = spiceypy.sce2c(SOLAR_ORBITER_ID, et) cmat, sc = spiceypy.ckgp(SOLAR_ORBITER_SRF_FRAME_ID, sc, 0, frame) vec = cmat @ np.eye(3) roll, pitch, yaw = spiceypy.m2eul(vec, 1, 2, 3) roll, pitch, yaw = np.rad2deg([roll, pitch, yaw])*u.deg return roll, pitch, yaw
def getTargetPosition(target, craft, camera, time): """ get position of target in world coordinates relative to the observing craft. """ # get target code targetId = spice.bodn2c(target) # eg 'Jupiter'->599 # get spacecraft instrument spacecraft = -31 if craft=='Voyager1' else -32 spacecraftBus = spacecraft * 1000 spacecraftScanPlatform = spacecraftBus - 100 spacecraftNarrowCamera = spacecraftScanPlatform - 1 spacecraftWideCamera = spacecraftScanPlatform - 2 # instrument = spacecraftBus # use for NAIF continuous kernels instrument = spacecraftScanPlatform # use for PDS discrete kernels # get ephemeris time # note: target and spacecraft locations are stored relative to J2000 # time is utc time as string ephemerisTime = spice.str2et(time) # seconds since J2000 (will be negative) # sclkch = spice.sce2s(spacecraft, ephemerisTime) # spacecraft clock ticks, string # sclkdp = spice.sce2c(spacecraft, ephemerisTime) # spacecraft clock ticks, double clockTicks = spice.sce2c(spacecraft, ephemerisTime) # spacecraft clock ticks, double # print 'clockTicks',clockTicks # get position of target relative to spacecraft # this is the direction from craft to target in ECLIPB1950 frame observer = 'Voyager ' + craft[-1] # eg 'Voyager 1' frame = 'ECLIPB1950' # coordinate frame abberationCorrection = 'NONE' position, lightTime = spice.spkpos(target, ephemerisTime, frame, abberationCorrection, observer) # print 'target position relative to observer', position return position
def caps_crosstrack_spice(tempdatetime, windspeed): et = spice.datetime2et(tempdatetime) sclkdp = spice.sce2c( -82, et) # converts an et to a continuous encoded sc clock (ticks) state, ltime = spice.spkezr("CASSINI", et, "IAU_TITAN", "NONE", "titan") ramdir = spice.vhat(state[3:6]) # print("ramdir",ramdir) # Gets Attitude sclkdp = spice.sce2c( -82, et) # converts an et to a continuous encoded sc clock (ticks) ckgp_output = spice.ckgp(-82000, sclkdp, 0, "IAU_TITAN") cmat = ckgp_output[0] print("cmat", cmat) ram_unit = spice.mxv(cmat, ramdir) # Ram Unit in SC coords # print("ram_unit", ram_unit) anglediff = spice.vsepg( ram_unit[:2], np.array([0, 1, 0]), 2) # Find azimuthal angle between normal boresight and ram direction # print("anglediff", anglediff * spice.dpr()) cassini_ram_mat = spice.rotate(-anglediff, 3) # print("cassini_ram_mat", cassini_ram_mat) # Rotates rotational axis with actuation # cassini_caps_mat = spice.ckgp(-82821, sclkdp, 0, 'CASSINI_CAPS_BASE')[0] # Rotation matrix of actuation # print("cassini_caps_mat", cassini_caps_mat) anode_rotational_axis = spice.mxv(cassini_ram_mat, np.array([1, 0, 0])) # Rotate with actuator print("Rotational Axis", anode_rotational_axis) rotationmatrix_1 = spice.spiceypy.axisar(anode_rotational_axis, -70 * spice.rpd()) rotationmatrix_2 = spice.spiceypy.axisar(anode_rotational_axis, 70 * spice.rpd()) ram_unit_rotated1 = spice.mxv(rotationmatrix_1, ram_unit) ram_unit_rotated2 = spice.mxv(rotationmatrix_2, ram_unit) scframe_spiceplane = spice.psv2pl([0, 0, 0], ram_unit_rotated1, ram_unit_rotated2) print("ram_unit", ram_unit, ram_unit_rotated1, ram_unit_rotated2) print("SC frame spice normal", spice.psv2pl([0, 0, 0], ram_unit_rotated1, ram_unit_rotated2)) cmat_t = spice.xpose(cmat) ram_unit_rotated1_titan = spice.mxv( cmat_t, ram_unit_rotated1) # Transform back to IAU Titan Frame ram_unit_rotated2_titan = spice.mxv( cmat_t, ram_unit_rotated2) # Transform back to IAU Titan Frame spiceplanenormal = spice.mxv(cmat_t, spice.pl2nvp(scframe_spiceplane)[0]) # Old method finding normal in titan frame # spiceplane = spice.psv2pl(state[:3], ram_unit_rotated1_titan, ram_unit_rotated2_titan) # spiceplanenormal = spice.pl2nvp(spiceplane)[0] print("SPICE NORMAL", spiceplanenormal) # print("Spice normal, sc frame", scframe_spicenormal_titan) if windspeed > 0: spiceplanenormal = -1 * spiceplanenormal print("spice plane fipped", windspeed, spiceplanenormal) print("vsep titan frame", spice.vsep(ramdir, spiceplanenormal) * spice.dpr()) return spiceplanenormal, ram_unit_rotated1_titan, ram_unit_rotated2_titan
def get_orientation(dt, frame1='SOLO_SRF', frame2='SOLO_EQUAT_NORM'): """ Get the orientation or roll, pith and yaw of STIX (ILS or OPT). Taken from https://github.com/i4Ds/STIXCore/blob/9a765a33f2e924ead669b9b99afc1e41a4d2d8e8/stixcore /ephemeris/tests/test_position.py#L28-L40 Parameters ---------- date : `datetime.datetime` Date at which to obtain orientation information frame : `str` Name of the coordinate frame Returns ------- tuple Roll, pitch and yaw of the spacecraft SOLO mission specific generic frames: SOLO_SUN_RTN Sun Solar Orbiter Radial-Tangential-Normal SOLO_SOLAR_MHP S/C-centred mirror helioprojective SOLO_IAU_SUN_2009 Sun Body-Fixed based on IAU 2009 report SOLO_IAU_SUN_2003 Sun Body-Fixed based on IAU 2003 report SOLO_GAE Geocentric Aries Ecliptic at J2000 (GAE) SOLO_GSE Geocentric Solar Ecliptic at J2000 (GSE) SOLO_HEE Heliocentric Earth Ecliptic at J2000 (HEE) SOLO_VSO Venus-centric Solar Orbital (VSO) Heliospheric Coordinate Frames developed for the NASA STEREO mission: SOLO_ECLIPDATE Mean Ecliptic of Date Frame SOLO_HCI Heliocentric Inertial Frame SOLO_HEE_NASA Heliocentric Earth Ecliptic Frame SOLO_HEEQ Heliocentric Earth Equatorial Frame SOLO_GEORTN Geocentric Radial Tangential Normal Frame Heliocentric Generic Frames(*): SUN_ARIES_ECL Heliocentric Aries Ecliptic (HAE) SUN_EARTH_CEQU Heliocentric Earth Equatorial (HEEQ) SUN_EARTH_ECL Heliocentric Earth Ecliptic (HEE) SUN_INERTIAL Heliocentric Inertial (HCI) Geocentric Generic Frames: EARTH_SUN_ECL (*) Geocentric Solar Ecliptic (GSE) EARTH_MECL_MEQX (*) Earth Mean Ecliptic and Equinox of date frame (Auxiliary frame for EARTH_SUN_ECL) EARTH_MECL_MEQX_J2000 Earth Mean Ecliptic and Equinox at J2000 frame (Auxiliary frame for SOLO_GSE and SOLO_HEE) """ et = sp.datetime2et(dt) sc = sp.sce2c(SOLAR_ORBITER_ID, et) #convert to clock ticks #tol=sp.sctiks(int(sc), "1:000") tol= 1.0 #cmat, sc= sp.ckgp(SOLAR_ORBITER_SRF_FRAME_ID, sc, tol, 'SOLO_ECLIP_NORM') #cmat, sc= sp.ckgp(SOLAR_ORBITER_SRF_FRAME_ID, sc, tol, 'SOLO_EQUAT_NORM') frame_id=SOLAR_ORBITER_SRF_FRAME_ID if frame1=='SOLO_SRF' else SOLAR_ORBITER_STIX_OPT_FRAME_D cmat, sc= sp.ckgp(frame_id, sc, tol, frame2) #get c-matrix orientiation information roll, pitch, yaw = sp.m2eul(cmat, 1, 2, 3) #matrix to Euler angles # Following lines taken from from get_sunspice_roll.pro #https://www.heliodocs.com/xdoc/xdoc_list.php?dir=$SSW/packages/sunspice/idl #if abs(roll) > 2 *math.pi: # roll=roll-math.copysign(2*math.pi,roll) #if abs(pitch) > 0.5* math.pi: # #taken from idl code get_sunspice_roll.pro # pitch = math.copysign(2*math.pi, pitch) - pitch # yaw = yaw - math.copysign(2*math.pi, yaw) # roll = roll - math.copysign(2*math.pi, roll) roll, pitch, yaw = np.rad2deg([roll, pitch, yaw])#convert to arcmin #take from Frederic idl code #if yaw <0: # yaw+=360 #yaw = yaw-180 pitch = -pitch roll = -roll return (roll, pitch, yaw)
def getCameraMatrix(craft, camera, time): """ get the camera matrix for the given craft and camera at a utc time. assumes libimg.loadKernels has been called. returns C, the 3x3 rotation matrix as a numpy array. """ # get target code # targetId = spice.bodn2c(target) # eg 'Jupiter'->599 # get spacecraft instrument spacecraft = -31 if craft=='Voyager1' else -32 spacecraftBus = spacecraft * 1000 spacecraftScanPlatform = spacecraftBus - 100 spacecraftNarrowCamera = spacecraftScanPlatform - 1 spacecraftWideCamera = spacecraftScanPlatform - 2 # instrument = spacecraftBus # use for NAIF continuous kernels instrument = spacecraftScanPlatform # use for PDS discrete kernels # get field of view and focal length # f is the focal length relative to the screen halfwidth of 1.0 # i.e. screen coordinates are -1.0 to 1.0 #. use ik # print 'camera',camera fov = config.cameraFOVs[camera] # degrees - 0.424 or 3.169 screenHalfwidth = 1.0 f = screenHalfwidth / math.tan(fov/2 * math.pi/180) # print 'f=focal length',f # get ephemeris time # note: target and spacecraft locations are stored relative to J2000 # time is utc time as string ephemerisTime = spice.str2et(time) # seconds since J2000 (will be negative) # sclkch = spice.sce2s(spacecraft, ephemerisTime) # spacecraft clock ticks, string # sclkdp = spice.sce2c(spacecraft, ephemerisTime) # spacecraft clock ticks, double clockTicks = spice.sce2c(spacecraft, ephemerisTime) # spacecraft clock ticks, double # print 'clockTicks',clockTicks # # get position of target relative to spacecraft # # this is the direction from craft to target in ECLIPB1950 frame # observer = 'Voyager ' + craft[-1] # eg 'Voyager 1' # frame = 'ECLIPB1950' # coordinate frame # abberationCorrection = 'NONE' # position, lightTime = spice.spkpos(target, ephemerisTime, frame, # abberationCorrection, observer) # print 'target position relative to observer', position # get camera pointing matrix C # C is the world-to-camera transformation matrix. # ie C is a rotation matrix from the base frame 'frame' to # the instrument-fixed frame at the time clockTicks +/- tolerance. # https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/ckgp_c.html tolerance = spice.sctiks(spacecraft, "0:00:800") # time tolerance frame = 'ECLIPB1950' # coordinate frame # ckgp is 'camera kernel get pointing' # note: the pointing information is stored in the time frame J2000, # but the coordinates are in the ECLIPB1950 coordinate frame. C, clkout, found = spice.ckgp(instrument, clockTicks, tolerance, frame) # print 'C=camera pointing matrix - transform world to camera coords' # print C return C