def search_stars(obsinfo, mag_limit=7.0): query = ("SELECT" " CATALOG_NUMBER,RA,DEC,VISUAL_MAGNITUDE,PARLAX,SPECTRAL_TYPE" " FROM HIPPARCOS") nmrows, _error, _errmsg = spice.ekfind(query) stars = [] for row in range(nmrows): ra = spice.ekgd(1, row, 0)[0] * spice.rpd() dec = spice.ekgd(2, row, 0)[0] * spice.rpd() mag = spice.ekgd(3, row, 0)[0] vec = spice.radrec(1.0, ra, dec) tvec = spice.mxv(obsinfo.ref2obsmtx, vec) _tpa, tdist = vec_padist(obsinfo.center, tvec) if tdist < obsinfo.fov.fovmax and mag < mag_limit: parallax = spice.ekgd(4, row, 0)[0] spectral = spice.ekgc(5, row, 0)[0] distance = 1.0 / np.tan(parallax * spice.rpd()) distance = spice.convrt(distance, "AU", "km") pos = tvec * distance vp = viewport_frustum(obsinfo.fov.bounds_rect, obsinfo.width, obsinfo.height, pos) star = { "hip_id": spice.ekgi(0, row, 0)[0], "position": tvec, "ra": ra, "dec": dec, "spectral_type": spectral, "visual_magnitude": mag, "distance": distance, "image_pos": vp[0:2], "color": get_star_color(spectral), } stars.append(star) return stars
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 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 rotate_CAPS_SCframe(CAPS_actuation, instrument, anodes=False): """ Returns the CAPS pointing vector after actuation in the SC frame """ naifiddict = {'ims': -82820, 'els': -82821, 'ibs1': -82822, 'ibs2': -82823, 'ibs3': -82824} naifid = naifiddict[instrument] if not anodes: rotationmatrix = spice.spiceypy.axisar(np.array([0, 0, -1]), CAPS_actuation * spice.rpd()) temp = spice.mxv(rotationmatrix, spice.spiceypy.getfov(naifid, 20)[2]) if (instrument == 'ims' or instrument == 'els') and anodes == True: temp = [] rotationmatrix_act = spice.spiceypy.axisar(np.array([0, 0, -1]), CAPS_actuation * spice.rpd()) for anodenumber, x in enumerate(np.arange(70, -90, -20)): rotationmatrix_anode = spice.spiceypy.axisar(np.array([-1, 0, 0]), x * spice.rpd()) postanode_rotation = spice.mxv(rotationmatrix_anode, spice.spiceypy.getfov(naifid, 20)[2]) temp.append(spice.mxv(rotationmatrix_act, postanode_rotation)) return temp
import os import sys import math import gaiaif_util import spiceypy as sp rpd = sp.rpd() if "__main__" == __name__: ### RA,Dec box ### Circle ### Polygon (non-convex quadrilateral) ### Polygon (convex quadrilateral) for fov in (gaiaif_util.FOV([[315,89.2],[15,89.8]]) ,gaiaif_util.FOV([[345,89.3],0.5]) ,gaiaif_util.FOV([[30,89],[0,88.5],[5.0,88.9],[270,89]]) ,gaiaif_util.FOV([[30,89],[0,88.5],[315,88.5],[270,89]]) ,gaiaif_util.FOV([[225,89],[0,88.5],[315,88.5],[90,89]]) ,): radec_boxes = fov.get_radec_boxes() ###for radec_box in radec_boxes: print(radec_box) if fov.POLYGONTYPE == fov.fovtype: print(dict(fov_is_convex=fov.is_convex())) def inbox(ra,dec): for ralo,rahi,declo,dechi in radec_boxes: if ra<ralo: continue if ra>rahi: continue if dec<declo: continue
def EarthRepeatOrbits(jk, e, Variable, VarType, isHighFidelity=False, printStatus=False): """Find a set of repeating ground track orbits around Earth (Earth-repeat orbits).""" # importing the required modules import spiceypy as spice import math import numpy as np Result = 0 Nsolutions = 0 # Error handling jkSize = jk.shape if (int(jkSize[1]) != 2): print( 'Incorrect matrix size for jk matrix. Only two coloumns are required.' ) return Result elif (VarType != 'Alti') and (VarType != 'Inclin'): print( 'Inrecognized input for the argument specifying the variable type.' ) return Result elif e >= 1.0 or e < 0: print( 'Only circular or elliptical orbits are possible. Check the value of eccentricity.' ) return Result elif (Variable[1] - Variable[0]) % Variable[2] != 0: print( 'Integer number of steps are not possible. Check inputs for the argument - "Variable".' ) return Result # Extracting the parameters spice.furnsh("./External_files/Spice_kernels/kernel_load.txt") muE = spice.bodvrd('Earth', 'GM', 1) mu = muE[1][0] # [km3/s2] for earth J2 = 1082.63E-6 #J2 for earth RE = spice.bodvrd('EARTH', 'RADII', 3) Re = RE[1][0] # [km], Average radius of Earth De = 86164.1004 # [s], Sidereal day k2 = 0.75 * J2 * math.sqrt(mu) * Re * Re r2d = 1 / spice.rpd() # Radian to degree conversion # Creating storage steps = int((Variable[1] - Variable[0]) / Variable[2]) + 1 Result = np.zeros(int(jkSize[0]) * int(steps) * 6) Result.shape = [int(jkSize[0]), int(steps), 6] # Computations for count in range(0, jkSize[0]): #looping over j and k values for rowCount in range(0, int(steps)): #looping over variable values j = jk[count, 0] k = jk[count, 1] # Extracting the value of variable var = Variable[0] + rowCount * Variable[2] # Storing values known so far Result[count, rowCount, 0] = j Result[count, rowCount, 1] = k Result[count, rowCount, 2] = e Result[count, rowCount, 3] = var Result[count, rowCount, 4] = math.nan # to be computed Result[count, rowCount, 5] = math.nan # to be computed if isHighFidelity == False: if VarType == 'Alti': a = (Re + var) / (1 - e) T = 2 * math.pi * math.sqrt(a**3 / mu) DeltaL1 = -2 * math.pi * (T / De) # DeltaL2 = C1 * cosi C1 = (-3 * math.pi * J2 * Re**2) / (a**2 * (1 - e**2)**2) C2 = -2 * math.pi * k / j - DeltaL1 C3 = 2 * math.pi * k / j - DeltaL1 # Inverse cosine if (C2 < 0) & (abs(C2 / C1) <= 1): DeltaL2 = C2 i = math.acos( DeltaL2 / C1) * r2d # for i = [0, 90] deg Result[count, rowCount, 4] = i Nsolutions += 1 elif (C2 > 0) & (abs(C2 / C1) <= 1): DeltaL2 = C2 i = math.acos( DeltaL2 / C1) * r2d # for i = (90, 180] deg Result[count, rowCount, 4] = i Nsolutions += 1 elif (C3 < 0) & (abs(C3 / C1) <= 1): DeltaL2 = C3 i = math.acos( DeltaL2 / C1) * r2d # for i = [0, 90] deg Result[count, rowCount, 5] = i Nsolutions += 1 elif (C3 > 0) & (abs(C3 / C1) <= 1): DeltaL2 = C3 i = math.acos( DeltaL2 / C1) * r2d # for i = (90, 180] deg Result[count, rowCount, 5] = i Nsolutions += 1 elif VarType == 'Inclin': print( 'Function is not defined for low fidelity + unknown inclination case.' ) return Result else: if VarType == 'Alti': a = (Re + var) / (1 - e) C1 = -2 * k2 * (a**-3.5) * ( (1 - e**2)**-2) * De * r2d # RAAN_dot = C1 * cos(i) C2 = k2 * (a**-3.5) * ( (1 - e**2)** -2) * De * r2d # omega_dot = C2 *(5*(cosd(i))^2 -1) C3 = k2 * (a**-3.5) * ( (1 - e**2)** -1.5) * De * r2d # M_dot = C3 *(3*(cosd(i))^2 -1) n = De * r2d * math.sqrt(mu / a**3) # For the quadratic equation A*x^2 + B*x +C = 0 with x = cos(i) A = 5 * C2 + 3 * C3 B = C1 * j / k C = n - 360 * j / k - (C2 + C3) D = B**2 - 4 * A * C if D >= 0: if (D == 0) & (abs(-B / (2 * A)) <= 1): i1 = math.acos(-B / (2 * A)) Result[count, rowCount, 4] = i1 * r2d Nsolutions += 1 elif (D > 0): x1 = (-B + math.sqrt(D)) / (2 * A) x2 = (-B - math.sqrt(D)) / (2 * A) if abs(x1) <= 1: i1 = math.acos(x1) Result[count, rowCount, 4] = i1 * r2d Nsolutions += 1 if abs(x2) <= 1: i2 = math.acos(x2) Result[count, rowCount, 5] = i2 * r2d Nsolutions += 1 elif VarType == 'Inclin': a0 = math.pow( (mu * De**2 * k**2 / (4 * math.pi**2 * j**2)), 1 / 3) iterations = 0 i = var L_dot = 360 while True: iterations = iterations + 1 RAAN_dot = -2 * k2 * math.pow(a0, -3.5) * math.cos( i / r2d) * math.pow(1 - e**2, -2) * De * r2d omega_dot = k2 * math.pow( a0, -3.5) * (5 * (math.cos(i / r2d))**2 - 1) * math.pow(1 - e**2, -2) * De * r2d M_dot = k2 * math.pow(a0, -3.5) * ( 3 * (math.cos(i / r2d))**2 - 1) * math.pow( 1 - e**2, -1.5) * De * r2d n = (j / k) * (L_dot - RAAN_dot) - (omega_dot + M_dot) a1 = (mu / (n / (De * r2d))**2)**(1 / 3) if iterations > 1000: a0 = a1 break elif (abs(a1 - a0) < 10**(-10)): a0 = a1 break else: a0 = a1 # Only tracking the feasible solutions i.e. positive altitudes if ((a0 * (1 - e)) - Re) > 0: Nsolutions += 1 Result[count, rowCount, 4] = (a0 * (1 - e)) - Re # Printing the status of solutions obtained if printStatus == True: print(Nsolutions, ' solutions are obtained for the Earth-repeat orbits.') return Result
import spiceypy as sp import traceback as tb do_debug = 'DEBUG' in os.environ ######################################################################## ### Parse command-line argument(s), setup parameters ### --hang=<cone half-angle, degrees> default_hang = 30.0 hangdeg = float( ([default_hang] + [s[7:] for s in sys.argv[1:] if s.startswith('--hang=')]).pop()) ### Conversion factors between degrees and radians dpr, rpd = sp.dpr(), sp.rpd() ### Cone half-angle and samples of Declinations, from 0 to (89.5-delta) decdegs = [(0.5 * decdec) - (decdec > 0 and 1e-6 or 0) for decdec in range(180)] ### Convert to radians hangrad = rpd * hangdeg decrads = [rpd * decdeg for decdeg in decdegs] ### Trig functions of same tansqhang = math.tan(hangrad)**2 sinhang = math.sin(hangrad) coshang = math.cos(hangrad) ### List of triples of tan(Dec) squared, cos(Dec), and Dec
""" Compare gaiaif_util.py corrections for parallax and stellar aberration to equivalent correction in SPICE """ import os import math import fov_cmd import sqlite3 as sl3 import spiceypy as sp import gaiaif_util as gifu ### Setup debugging and conversions; define some string constants do_debug = 'DEBUG' in os.environ rpd, aupkm = sp.rpd(), sp.convrt(1., 'km', 'au') ( earth, ssb, j2000, none, LT, LTS, ra_dec, declination, equals, ) = 'earth 0 j2000 none lt lt+s ra/dec declination ='.upper().split() sp.furnsh('de421.bsp') gaia_db = 'gaia.sqlite3'
if Question ==1: ############ Question 1 - validation, solved using approach 2 i = 28.0 # %deg e = 0.0 # assumption D = 86164.1004 #s - import from spice muE = spice.bodvrd( 'EARTH', 'GM', 1 ); #km3/s2 for earth mu = muE[1][0] J2 = 1082.63E-6 #J2 for earth - import from spice RE = spice.bodvrd('EARTH','RADII',3) Re = RE[1][0] # Radius of Earth, from SPICE - pck00010.tpc, 6378.1366 km L_dot = 360.0 r2d = 1/spice.rpd() # Radians per degree print('r2d2 is: ',r2d) k2 = 0.75 * J2 * math.sqrt(mu)* Re*Re # Method 1 # READ 10.2.1. of the course handbook jkStore = np.matrix('14, 1; 43, 3; 29, 2; 59, 4; 74, 5 ; 15,1') #jk = jkStore[0][0] #print(jk) ## Method 2 # READ 10.2.1. of the course handbook #jkStore2 = np.array([14, 1, 43, 3, 29, 2, 59, 4, 74, 5 , 15, 1]) #jkStore2.shape = [6,2] # READ 10.2.1. of the course handbook
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
import os import sys import math import numpy import spiceypy as sp try: dpr except: dpr = sp.dpr() ### degree / Radian rpd = sp.rpd() ### Radian / degree rpmas = sp.convrt(1.,'arcseconds','radians') * 1e-3 ### Radian / milliarcsecond aupkm = sp.convrt(1.,'km','au') ### Astonomical Unit / kilometer recip_clight = 1.0 / sp.clight() ######################################################################## class FOV(object): """ Field-Of-View (FOV) and methods to determine if a ray is inside the FOV Attributes .L Length of FOV argument to .__init__ .fovtype FOV type: FOV.CIRCLETYPE; RADECBOXTYPE;,.POLYGONTYPE .radecdegs List of [RA,Dec] pairs of input vectors/vertices, degrees .uvfovxyzs List of Unit vectors of [X,Y,Z] triples of input vectors .hangdeg Cone half-angle for circle FOV, degrees .hangrad Cone half-angle for circle FOV, radians .radec_boxes List of lists: FOV bounding boxes; ralo,rahi,declo,dechi .convex True is FOV is convex, else False
lats.append(lat) lons.append(lon) data['Altitude'] = alts data['Longitude'] = lons data['Latitude'] = lats data = data[data['Peak Energy'] > 15] data.dropna(subset=['Bulk Azimuth'], inplace=True) data = data[(data["Actuation Direction"]=="positive") | (data["Actuation Direction"]=="negative")] velocityseries = pd.Series(dtype=float) flybyslist = data.Flyby.unique() for counter, flyby in enumerate(data.Flyby.unique()): tempdf = data[data['Flyby'] == flyby] tempvelocities = [np.sin(x * spice.rpd()) * titan_flybyvelocities[flyby] for x in tempdf["Bulk Deflection from Ram Angle"]] tempseries = pd.Series(data=tempvelocities, name=flyby) tempseries.reset_index(drop=True, inplace=True) velocityseries = velocityseries.append(tempseries) data.reset_index(drop=True, inplace=True) velocityseries.reset_index(drop=True, inplace=True) velocityseries.name = "Crosstrack velocity" absvelocityseries = abs(velocityseries) absvelocityseries.name = "Absolute Crosstrack velocity" data = pd.concat([data, velocityseries, absvelocityseries], axis=1) data["Azimuthal Ram Time temp"] = pd.to_datetime(data["Azimuthal Ram Time"]).apply(lambda x: x.replace(microsecond=0)) data.drop_duplicates(subset="Azimuthal Ram Time temp",inplace=True)