313 lines
8.3 KiB
Python
313 lines
8.3 KiB
Python
#!/usr/bin/env python
|
|
import os
|
|
import tempfile
|
|
import isce
|
|
import numpy as np
|
|
import datetime
|
|
from isceobj.Orbit.Orbit import StateVector, Orbit
|
|
from collections import OrderedDict
|
|
|
|
try:
|
|
import SpiceyPy
|
|
except ImportError:
|
|
raise Exception('SpiceyPy Python bindings need to be installed to be able to use this library.')
|
|
|
|
class SpiceDatabase(object):
|
|
'''
|
|
Class for dealing with SPICE kernel files.
|
|
'''
|
|
|
|
dbdir = os.path.join(os.path.dirname(__file__), 'db')
|
|
dblist = os.path.join(dbdir, 'kernels.list')
|
|
|
|
def __init__(self):
|
|
'''
|
|
Load the databasename.
|
|
'''
|
|
rdict = OrderedDict()
|
|
infile = open(self.dblist, 'r')
|
|
line = infile.readline()
|
|
while line:
|
|
llist = line.split('=')
|
|
if len(llist)==2 :
|
|
rdict[llist[0].strip()] = os.path.join(self.dbdir, llist[1].strip())
|
|
line = infile.readline()
|
|
infile.close()
|
|
self.data = rdict
|
|
|
|
def getList(self):
|
|
'''Get list of kernel files.'''
|
|
|
|
ll = []
|
|
for key, val in self.data.items():
|
|
ll.append(val)
|
|
|
|
return ll
|
|
|
|
def getKernel(self, key):
|
|
return self.data[key]
|
|
|
|
def __getitem__(self, key):
|
|
return self.data[key]
|
|
|
|
|
|
class ISCEOrbit(object):
|
|
'''
|
|
Class for converting ISCE orbits to CSPICE orbits.
|
|
'''
|
|
|
|
def __init__(self, orbit):
|
|
self.orbit = orbit
|
|
self.db = SpiceDatabase()
|
|
|
|
def exportToSPK(self, spkfile,frame='ITRF93'):
|
|
'''
|
|
Export ISCE orbit to SPK file.
|
|
'''
|
|
|
|
if frame not in ('ITRF93', 'J2000', 'ECI_TOD', 'ECLIPJ2000'):
|
|
raise Exception('CSPICE currently only supports ITRF93, J2000, ECLIPJ2000, ECI_TOD.')
|
|
|
|
tmpDir = tempfile.mkdtemp(dir='.')
|
|
|
|
hdrfile = os.path.join(tmpDir, 'hdrfile')
|
|
setupfile = os.path.join(tmpDir, 'setupfile')
|
|
self.exportOrbitToHeader(hdrfile)
|
|
self.createSetupFile(setupfile, frame=frame)
|
|
self.runMkspk(hdrfile, setupfile, spkfile)
|
|
|
|
for root, dirs, files in os.walk(tmpDir):
|
|
for filename in files:
|
|
try:
|
|
os.unlink(os.path.join(tmpDir, filename))
|
|
except:
|
|
os.system("rm "+os.path.join(tmpDir, filename))
|
|
|
|
os.rmdir(tmpDir)
|
|
|
|
|
|
def exportOrbitToHeader(self, hdrfile):
|
|
'''
|
|
Exports a given Orbit to SPICE compatible HDR format.
|
|
'''
|
|
|
|
fid = open(hdrfile, 'w')
|
|
for sv in self.orbit:
|
|
tim = sv.getTime()
|
|
pos = sv.getPosition()
|
|
vel = sv.getVelocity()
|
|
pos =[str(x/1000.) for x in pos]
|
|
vel = [str(x/1000.) for x in vel]
|
|
|
|
out = [str(tim)] + pos + vel
|
|
fid.write(','.join(out) + '\n')
|
|
|
|
fid.close()
|
|
|
|
def createSetupFile(self, setupfile, frame=None):
|
|
'''
|
|
Creates a setup file to use with mkspk.
|
|
'''
|
|
|
|
fmtString = """\\begindata
|
|
INPUT_DATA_TYPE = 'STATES'
|
|
OUTPUT_SPK_TYPE = 13
|
|
OBJECT_ID = -123710
|
|
OBJECT_NAME = 'RADARSAT'
|
|
CENTER_ID = 399
|
|
CENTER_NAME = 'EARTH'
|
|
REF_FRAME_NAME = '{0}'
|
|
PRODUCER_ID = 'ISCE py3'
|
|
DATA_ORDER = 'EPOCH X Y Z VX VY VZ'
|
|
INPUT_DATA_UNITS = ( 'ANGLES=DEGREES' 'DISTANCES=km')
|
|
DATA_DELIMITER = ','
|
|
LINES_PER_RECORD = 1
|
|
LEAPSECONDS_FILE = '{1}'
|
|
POLYNOM_DEGREE = 3
|
|
SEGMENT_ID = 'SPK_STATES_13'
|
|
TIME_WRAPPER = '# UTC'
|
|
PCK_FILE = ('{2}')
|
|
|
|
"""
|
|
|
|
frameString = """FRAME_DEF_FILE = ('{0}')
|
|
|
|
"""
|
|
|
|
txtString="\\begintext"
|
|
|
|
tfirst = self.orbit._stateVectors[0].getTime()
|
|
|
|
leap = self.db['LEAPSECONDS']
|
|
|
|
if tfirst.date() < datetime.date(2000,1,1):
|
|
pck = self.db['EARTHHIGHRES']
|
|
else:
|
|
pck = self.db['EARTHHIGHRESLATEST']
|
|
tod = self.db['EARTHECI_TOD']
|
|
|
|
#####Link them to temp dir
|
|
tmpdir = os.path.dirname(setupfile)
|
|
leaplnk = os.path.join(tmpdir, os.path.basename(leap))
|
|
try:
|
|
os.link(leap, leaplnk)
|
|
except:
|
|
os.system("ln -s "+leap+" "+leaplnk)
|
|
|
|
pcklnk = os.path.join(tmpdir, os.path.basename(pck))
|
|
try:
|
|
os.link(pck, pcklnk)
|
|
except:
|
|
os.system("ln -s "+pck+" "+pcklnk)
|
|
|
|
|
|
if frame == 'ECI_TOD':
|
|
todlnk = os.path.join(tmpdir, os.path.basename(tod))
|
|
try:
|
|
os.link(tod, todlnk)
|
|
except:
|
|
os.system("ln -s "+tod+" "+todlnk)
|
|
|
|
outstr = fmtString.format(frame, leaplnk, pcklnk)
|
|
|
|
if frame == 'ECI_TOD':
|
|
outstr = outstr + frameString.format(todlnk)
|
|
|
|
outstr = outstr + txtString
|
|
|
|
fid = open(setupfile, 'w')
|
|
fid.write(outstr)
|
|
fid.close()
|
|
|
|
|
|
def runMkspk(self, hdrfile, setupfile, spkfile):
|
|
if os.path.exists(spkfile):
|
|
print('Removing old version of spk file')
|
|
os.remove(spkfile)
|
|
|
|
cmd = ['mkspk', '-input '+hdrfile,
|
|
'-setup '+ setupfile, '-output ' + spkfile]
|
|
os.system(' '.join(cmd))
|
|
|
|
pass
|
|
|
|
|
|
class SpiceOrbit(object):
|
|
'''
|
|
Orbit for dealing with Spice bsp files.
|
|
'''
|
|
|
|
def __init__(self, spkfile):
|
|
'''
|
|
Constructor.
|
|
'''
|
|
self.spkfile = spkfile
|
|
self.db = SpiceDatabase()
|
|
|
|
def initSpice(self):
|
|
ll = self.db.getList()
|
|
for val in ll:
|
|
SpiceyPy.furnsh(val)
|
|
|
|
SpiceyPy.furnsh(self.spkfile)
|
|
|
|
def interpolateOrbit(self, time, frame='ITRF93'):
|
|
|
|
if frame not in ('ITRF93', 'J2000', 'ECI_TOD', 'ECLIPJ2000'):
|
|
raise Exception('Currently only ITRF93/J2000 frames are supported.')
|
|
et = SpiceyPy.str2et(str(time))
|
|
res,lt = SpiceyPy.spkezr('-123710', et,
|
|
frame, 'None', 'EARTH')
|
|
sv = StateVector()
|
|
sv.setTime(time)
|
|
sv.setPosition([x*1000.0 for x in res[0:3]])
|
|
sv.setVelocity([x*1000.0 for x in res[3:6]])
|
|
return sv
|
|
|
|
def loadHdrAsOrbit(fname, date=None):
|
|
'''Read a hdr file and convert to ISCE orbit'''
|
|
from isceobj.Orbit.Orbit import Orbit, StateVector
|
|
|
|
if date is None:
|
|
date = datetime.datetime.now().date()
|
|
|
|
t0 = datetime.datetime(year=date.year,
|
|
month = date.month,
|
|
day = date.day)
|
|
orb = Orbit()
|
|
inData = np.loadtxt(fname)
|
|
|
|
for line in inData:
|
|
time = t0 + datetime.timedelta(seconds = line[0])
|
|
sv = StateVector()
|
|
sv.setTime(time)
|
|
sv.setPosition(line[1:4].tolist())
|
|
sv.setVelocity(line[4:7].tolist())
|
|
orb.addStateVector(sv)
|
|
print(sv)
|
|
|
|
return orb
|
|
|
|
|
|
def dumpOrbitToHdr(orbit, filename, date=None):
|
|
'''
|
|
Dump orbit to ROI_PAC style hdr file.
|
|
'''
|
|
|
|
if date is None:
|
|
date = orbit._stateVectors[0].getTime().date()
|
|
|
|
t0 = datetime.datetime(year = date.year,
|
|
month = date.month,
|
|
day = date.day)
|
|
|
|
nVec = len(orbit._stateVectors)
|
|
arr = np.zeros((nVec, 7))
|
|
|
|
for ind,sv in enumerate(orbit._stateVectors):
|
|
arr[ind][0] = (sv.getTime() - t0).total_seconds()
|
|
arr[ind][1:4] = sv.getPosition()
|
|
arr[ind][4:] = sv.getVelocity()
|
|
|
|
|
|
np.savetxt(filename, arr, fmt='%10.6f')
|
|
|
|
|
|
class ECI2ECEF(object):
|
|
'''
|
|
Class for converting Inertial orbits to ECEF orbits using JPL's SPICE library.
|
|
'''
|
|
|
|
def __init__(self, orbit, eci='J2000', ecef='ITRF93'):
|
|
'''
|
|
Currently J2000 and ITRF3 frames are supported by the SPICE library.
|
|
'''
|
|
|
|
self.eci = eci
|
|
self.ecef = ecef
|
|
self.orbit = orbit
|
|
|
|
def convert(self):
|
|
'''Convert ECI orbit to ECEF orbit.'''
|
|
|
|
date = self.orbit._stateVectors[0].getTime().date()
|
|
bspName = 'inertial_orbit_' + date.strftime('%Y%m%d') + '.bsp'
|
|
|
|
####Convert ISCE orbit to SPICE orbit file
|
|
sorb = ISCEOrbit(self.orbit)
|
|
sorb.exportToSPK(bspName, frame=self.eci)
|
|
|
|
####Convert coordinates with corrections
|
|
spk = SpiceOrbit(bspName)
|
|
spk.initSpice()
|
|
|
|
wgsOrbit = Orbit()
|
|
wgsOrbit.setOrbitSource( self.orbit.getOrbitSource())
|
|
wgsOrbit.setOrbitQuality( self.orbit.getOrbitQuality())
|
|
for sv in self.orbit:
|
|
tim = sv.getTime()
|
|
spksv = spk.interpolateOrbit(tim, frame=self.ecef)
|
|
wgsOrbit.addStateVector(spksv)
|
|
|
|
return wgsOrbit
|