class ODR(object): """A class to parse Orbital Data Records (ODR) generated by Delft""" logging_name = 'isceobj.Orbit.ODR' @logged def __init__(self, file=None): self._file = file self._format = None self._satellite = None self._arcNumber = None self._cycleLength = None self._numberOfRecords = None self._version = None self._ephemeris = Orbit() self._ephemeris.setOrbitSource('ODR') self._ephemeris.setReferenceFrame('ECR') self.grs80 = Ellipsoid.Ellipsoid( *AstronomicalHandbook.PlanetsData.ellipsoid['Earth']['GRS-80']) return None #jng added the start and stop time. The computation of the velocities seems pretty time comsuming, so limit the orbit data extraction only to startTime nad stopTime def parseHeader(self, startTime=None, stopTime=None): fp = None try: fp = open(self._file, 'rb') except IOError as strerr: self.logger.error(strerr) buffer = fp.read(16) # Header 1 (format, satellite, dataStartTimeSeconds) = struct.unpack('>4s8si', buffer) buffer = fp.read(16) # Header 2 (cycleLength, number, numberOfRecords, version) = struct.unpack('>4i', buffer) self._format = format.decode('utf-8') self._satellite = satellite.decode('utf-8') self._arcNumber = number self._cycleLength = cycleLength * 1e3 # In cycle length in days self._numberOfRecords = numberOfRecords self._version = version positions = [] for i in range(numberOfRecords): buffer = fp.read(16) if not startTime == None: position = self.parseDataRecords(buffer) if position['time'] < startTime: continue if not stopTime == None: position = self.parseDataRecords(buffer) if position['time'] > stopTime: continue positions.append(self.parseDataRecords(buffer)) self.createStateVectors(positions) fp.close() def parseDataRecords(self, buffer): """Parse the individual data records for this ODR file""" (timeSeconds, latitude, longitude, height) = struct.unpack('>4i', buffer) time = self._utcSecondsToDatetime(timeSeconds) if (self._format == '@ODR'): latitude = latitude * 1e-6 longitude = longitude * 1e-6 elif (self._format == 'xODR'): latitude = latitude * 1e-7 longitude = longitude * 1e-7 height = height * 1e-3 xyz = self._convertToXYZ(latitude, longitude, height) return ({'time': time, 'x': xyz[0], 'y': xyz[1], 'z': xyz[2]}) def createStateVectors(self, positions): """Calculate the satellite velocity from the position data and create StateVector objects""" for i in range(len(positions)): t0 = positions[i]['time'] x0 = positions[i]['x'] y0 = positions[i]['y'] z0 = positions[i]['z'] sv = StateVector() sv.setTime(t0) sv.setPosition([x0, y0, z0]) sv.setVelocity([0.0, 0.0, 0.0]) self._ephemeris.addStateVector(sv) self._calculateVelocities() def _calculateVelocities(self): for sv in self._ephemeris: t0 = sv.getTime() t1 = t0 + datetime.timedelta(seconds=-0.5) t2 = t0 + datetime.timedelta(seconds=0.5) try: sv1 = self._ephemeris.interpolateOrbit(t1, method='legendre') sv2 = self._ephemeris.interpolateOrbit(t2, method='legendre') except ValueError: continue if (not sv1) or (not sv2): continue v1 = sv1.getPosition() v2 = sv2.getPosition() vx = (v2[0] - v1[0]) vy = (v2[1] - v1[1]) vz = (v2[2] - v1[2]) sv.setVelocity([vx, vy, vz]) def trimOrbit(self, startTime, stopTime): """Trim the list of state vectors to encompass the time span [startTime:stopTime]""" newOrbit = Orbit() newOrbit.setOrbitSource('ODR') newOrbit.setReferenceFrame('ECR') for sv in self._ephemeris: if ((sv.getTime() > startTime) and (sv.getTime() < stopTime)): newOrbit.addStateVector(sv) return newOrbit def getEphemeris(self): return self._ephemeris def _convertToXYZ(self, latitude, longitude, height): # The latitude, longitude and height are referenced to the center of mass of the satellite above the GRS80 ellipsoid xyz = self.grs80.llh_to_xyz([latitude, longitude, height]) return xyz def _utcSecondsToDatetime(self, seconds): """All of the ODR records are in UTC seconds from 1 Jan. 1985""" dataTime = datetime.datetime(year=1985, month=1, day=1) dataTime = dataTime + datetime.timedelta(seconds=seconds) return dataTime
class OrbitInfo(object): '''Class for storing metadata about a SAR scene.''' def __init__(self, fm): '''Initialize with a FrameMetadata object''' self._lookDict = {'right': -1, 'left': 1} self.direction = fm.direction self.fd = fm.doppler self.tStart = fm.sensingStart self.tStop = fm.sensingStop self.lookSide = self._lookDict[fm.lookDirection] self.prf = fm.prf self.rng = fm.startingRange self.coherenceThreshold = 0.2 self.orbVec = None self.tMid = self.tStart + sum( [self.tStop - self.tStart, datetime.timedelta()], datetime.timedelta()) / 2 self.pos = None self.vel = None self.peg = None self.rds = None self.hgt = None self.clook = None self.slook = None self.baseline = { 'horz': fm.horizontalBaseline, 'vert': fm.verticalBaseline, 'total': 0 } self.coherence = None self.planet = Planet(pname='Earth') self.unpackOrbitVectors(fm.orbit) self.computePeg() self.computeLookAngle() def getBaseline(self): return self.baseline def getCoherence(self): return self.coherence def unpackOrbitVectors(self, orb): self.orbVec = Orbit(source='json', quality='good') self.orbVec._referenceFrame = 'WGS-84' relTims = orb[0] satPos = orb[1] satVel = orb[2] refTime = orb[3] for kk in range(len(satPos)): vecTime = refTime + datetime.timedelta(seconds=relTims[kk]) tempVec = StateVector(time=vecTime, position=satPos[kk], velocity=satVel[kk]) self.orbVec.addStateVector(tempVec) stateVec = self.orbVec.interpolateOrbit(self.tMid, 'hermite') self.pos = stateVec.getPosition() self.vel = stateVec.getVelocity() return def computeLookAngle(self): self.clook = (2 * self.hgt * self.rds + self.hgt**2 + self.rng**2) / (2 * self.rng * (self.rds + self.hgt)) self.slook = numpy.sqrt(1 - self.clook**2) # print('Estimated Look Angle: %3.2f degrees'%(np.arccos(self.clook)*180.0/np.pi)) return def computePeg(self): shortOrb = Orbit() for i in range(-10, 10): time = self.tMid + datetime.timedelta(seconds=(i / self.prf)) sv = self.orbVec.interpolateOrbit(time, method='hermite') shortOrb.addStateVector(sv) objPeg = stdproc.createGetpeg() objPeg.wireInputPort(name='planet', object=self.planet) objPeg.wireInputPort(name='Orbit', object=shortOrb) stdWriter = create_writer("log", "", True, filename="orbitInfo.log") stdWriter.setFileTag("getpeg", "log") stdWriter.setFileTag("getpeg", "err") stdWriter.setFileTag("getpeg", "log") objPeg.setStdWriter(stdWriter) objPeg.estimatePeg() self.peg = objPeg.getPeg() self.rds = objPeg.getPegRadiusOfCurvature() self.hgt = objPeg.getAverageHeight() return def computeBaseline(self, slave): ''' Compute baseline between current object and another orbit object. This is meant to be used during data ingest. ''' mpos = numpy.array(self.pos) mvel = numpy.array(self.vel) #######From the ROI-PAC scripts rvec = mpos / numpy.linalg.norm(mpos) crp = numpy.cross(rvec, mvel) / numpy.linalg.norm(mvel) crp = crp / numpy.linalg.norm(crp) vvec = numpy.cross(crp, rvec) mvel = numpy.linalg.norm(mvel) spos = numpy.array(slave.pos) svel = numpy.array(slave.vel) svel = numpy.linalg.norm(svel) dx = spos - mpos z_offset = slave.prf * numpy.dot(dx, vvec) / mvel slave_time = slave.tMid - datetime.timedelta(seconds=z_offset / slave.prf) ####Remove these checks to deal with scenes from same track but not exactly overlaid # if slave_time < slave.tStart: # raise Exception('Out of bounds. Try the previous frame in time.') # elif slave.tStop < slave_time: # raise Exception('Out of bounds. Try the next frame in time.') try: svector = slave.orbVec.interpolateOrbit(slave_time, method='hermite') except: raise Exception( 'Error in interpolating orbits. Possibly using non geo-located images.' ) spos = numpy.array(svector.getPosition()) svel = numpy.array(svector.getVelocity()) svel = numpy.linalg.norm(svel) dx = spos - mpos hb = numpy.dot(dx, crp) vb = numpy.dot(dx, rvec) csb = self.lookSide * hb * self.clook + vb * self.slook self.baseline = {'horz': hb, 'vert': vb, 'total': csb} def computeCoherence(self, slave, Bcrit=400., Tau=180.0, Doppler=0.4): ''' This is meant to be estimate the coherence. This is for estimating which pairs to process. I assume that baseline dict is already available in the json input. baseline dict is w.r.t master and slave baseline is already precomputed w.r.t master. Typically: process a pair if Rho > 0.3 ''' Bperp = numpy.abs( self.lookSide * (self.baseline['horz'] - slave.horizontalBaseline) * self.clook + (self.baseline['vert'] - slave.verticalBaseline) * self.slook) Btemp = numpy.abs(self.tStart.toordinal() - slave.sensingStart.toordinal()) * 1.0 Bdop = numpy.abs( (self.fd * self.prf - slave.doppler * slave.prf) / self.prf) geomRho = (1 - numpy.clip(Bperp / Bcrit, 0., 1.)) tempRho = numpy.exp(-1.0 * Btemp / Tau) dopRho = Bdop < Doppler self.coherence = geomRho * tempRho * dopRho def computeCoherenceNoRef(self, slave, Bcrit=400., Tau=180.0, Doppler=0.4): ''' This is meant to be estimate the coherence. This is for estimating which pairs to process. Ignores baseline values in json and computes baseline between given pair. Master is not involved. Typically: process a pair if Rho > 0.3 ''' self.computeBaseline(OrbitInfo(slave)) Bperp = numpy.abs(self.lookSide * self.baseline['horz'] * self.clook + self.baseline['vert'] * self.slook) Btemp = numpy.abs(self.tStart.toordinal() - slave.sensingStart.toordinal()) * 1.0 Bdop = numpy.abs( (self.fd * self.prf - slave.doppler * slave.prf) / self.prf) print(('Bperp: %f (m) , Btemp: %f days, Bdop: %f (frac PRF)' % (Bperp, Btemp, Bdop))) geomRho = (1 - numpy.clip(Bperp / Bcrit, 0., 1.)) tempRho = numpy.exp(-1.0 * Btemp / Tau) dopRho = Bdop < Doppler self.coherence = geomRho * tempRho * dopRho print(('Expected Coherence: %f' % (self.coherence))) def isCoherent(self, slave, Bcrit=400., Tau=180, Doppler=0.4, threshold=0.3): #### Change this line to self.computeCoherence to go back to original. self.computeCoherenceNoRef(slave, Bcrit, Tau, Doppler) ret = False if (self.coherence >= threshold): ret = True return ret
class OrbitTest(unittest.TestCase): def setUp(self): logging.basicConfig() self.linearOrbit = Orbit() self.quadOrbit = Orbit() linpos, linvel = self.generateLinearSV(10, [[1.0, 2.0, 3.0]], [[1.0 / 60.0 for j in range(3)]]) quadpos, quadvel = self.generateQuadraticSV(10, [[1.0, 2.0, 3.0]], 0.1) dt = datetime.datetime(year=2010, month=1, day=1) for i in range(10): linsv = StateVector() quadsv = StateVector() linsv.setTime(dt) quadsv.setTime(dt) linsv.setPosition(linpos[i]) linsv.setVelocity(linvel[i]) quadsv.setPosition(quadpos[i]) quadsv.setVelocity(quadvel[i]) self.linearOrbit.addStateVector(linsv) self.quadOrbit.addStateVector(quadsv) dt = dt + datetime.timedelta(minutes=1) def tearDown(self): del self.linearOrbit del self.quadOrbit def generateLinearSV(self, num, pos, vel): for i in range(1, num): sv = [0.0 for j in range(3)] for j in range(3): sv[j] = pos[i - 1][j] + vel[i - 1][j] * 60.0 pos.append(sv) vel.append(vel[0]) return pos, vel def generateQuadraticSV(self, num, pos, rate): vel = [[0.0 for j in range(3)]] for t in range(1, num): newPos = [0.0 for j in range(3)] newVel = [0.0 for j in range(3)] for j in range(3): newPos[j] = pos[0][j] + rate * (t**2) newVel[j] = 2.0 * rate * t / 60.0 pos.append(newPos) vel.append(newVel) return pos, vel def testAddStateVector(self): a = None self.assertRaises(TypeError, self.linearOrbit.addStateVector, a) def testLinearInterpolateOrbit(self): ans = [2.5, 3.5, 4.5] sv = self.linearOrbit.interpolateOrbit(datetime.datetime(year=2010, month=1, day=1, hour=0, minute=1, second=30), method='linear') pos = sv.getPosition() for i in range(3): self.assertAlmostEquals(pos[i], ans[i], 5) ans = [1.225, 2.225, 3.225] sv = self.quadOrbit.interpolateOrbit(datetime.datetime(year=2010, month=1, day=1, hour=0, minute=1, second=30), method='linear') pos = sv.getPosition() for i in range(3): self.assertAlmostEquals(pos[i], ans[i], 5) def testHermiteInterpolateOrbit(self): ans = [2.5, 3.5, 4.5] sv = self.linearOrbit.interpolateOrbit(datetime.datetime(year=2010, month=1, day=1, hour=0, minute=1, second=30), method='hermite') pos = sv.getPosition() for i in range(3): self.assertAlmostEquals(pos[i], ans[i], 5) ans = [1.225, 2.225, 3.225] sv = self.quadOrbit.interpolateOrbit(datetime.datetime(year=2010, month=1, day=1, hour=0, minute=1, second=30), method='hermite') pos = sv.getPosition() for i in range(3): self.assertAlmostEquals(pos[i], ans[i], 5) def testLegendreInterpolateOrbit(self): ans = [4.5, 5.5, 6.5] sv = self.linearOrbit.interpolateOrbit(datetime.datetime(year=2010, month=1, day=1, hour=0, minute=3, second=30), method='legendre') pos = sv.getPosition() for i in range(3): self.assertAlmostEquals(pos[i], ans[i], 5) ans = [2.225, 3.225, 4.225] sv = self.quadOrbit.interpolateOrbit(datetime.datetime(year=2010, month=1, day=1, hour=0, minute=3, second=30), method='legendre') pos = sv.getPosition() for i in range(3): self.assertAlmostEquals(pos[i], ans[i], 5) def testInterpolateOrbitOutOfBounds(self): dt = datetime.datetime(year=2010, month=1, day=2) self.assertRaises(ValueError, self.linearOrbit.interpolateOrbit, dt)