#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Copyright 2014 California Institute of Technology. ALL RIGHTS RESERVED. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # United States Government Sponsorship acknowledged. This software is subject to # U.S. export control laws and regulations and has been classified as 'EAR99 NLR' # (No [Export] License Required except when exporting to an embargoed country, # end user, or in support of a prohibited end use). By downloading this software, # the user agrees to comply with all applicable U.S. export laws and regulations. # The user has the responsibility to obtain export licenses, or other export # authority as may be required before exporting this software to any 'EAR99' # embargoed foreign country or citizen of those countries. # # Author: Eric Gurrola #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ from __future__ import (print_function, absolute_import,) from isceobj.Util.py2to3 import * import logging import datetime import math import isceobj from isceobj.Scene.Frame import Frame from isceobj.Orbit.Orbit import StateVector as OrbitStateVector from isceobj.Planet.Planet import Planet from isceobj.Planet.AstronomicalHandbook import Const from isceobj.Sensor import cosar from isceobj.Util.decorators import pickled, logged from iscesys import DateTimeUtil as DTU from iscesys.Component.Component import Component METADATAFILE = Component.Parameter( 'metadataFile', public_name='annotation file', default=None, type=str, mandatory=True, doc="Name of the input annotation file" ) SEGMENT_INDEX = Component.Parameter( 'segment_index', public_name='segment index', default=1, type=int, mandatory=False, doc="The index of the first SLC segment to process" ) def polyval(coeffs, rho): v = 0.0 for i, c in enumerate(coeffs): v += c*rho**i return v from isceobj.Sensor.Sensor import Sensor @pickled class UAVSAR_Stack(Component): """ A class representing a UAVSAR SLC. """ family = 'uavsar_stack' logging_name = 'isce.Sensor.UAVSAR_Stack' lookMap = {'RIGHT' : -1, 'LEFT' : 1} parameter_list = (METADATAFILE, SEGMENT_INDEX) @logged def __init__(self, name=''): super().__init__(family=self.family, name=name) self.frame = Frame() self.frame.configure() self._elp = None self._peg = None elp = self.elp return def _populatePlatform(self, **kwargs): platform = self.frame.getInstrument().getPlatform() platform.setMission('UAVSAR') platform.setPointingDirection( self.lookMap[self.metadata['Look Direction'].upper()]) platform.setPlanet(Planet(pname="Earth")) platform.setAntennaLength(self.metadata['Antenna Length']) return def _populateInstrument(self, **kwargs): instrument = self.frame.getInstrument() instrument.setRadarWavelength( self.metadata['Center Wavelength']) instrument.setPulseRepetitionFrequency( 1.0/self.metadata['Average Pulse Repetition Interval']) instrument.setRangePixelSize( self.metadata['1x1 SLC Range Pixel Spacing']) instrument.setAzimuthPixelSize( self.metadata['1x1 SLC Azimuth Pixel Spacing']) instrument.setPulseLength(self.metadata['Pulse Length']) instrument.setChirpSlope( -self.metadata['Bandwidth']/self.metadata['Pulse Length']) from isceobj.Constants.Constants import SPEED_OF_LIGHT instrument.setRangeSamplingRate( SPEED_OF_LIGHT/2.0/instrument.getRangePixelSize()) instrument.setIncidenceAngle(0.5*( self.metadata['Minimum Look Angle'] + self.metadata['Maximum Look Angle'])) return def _populateFrame(self): #Get the Start, Mid, and Stop times import datetime tStart = datetime.datetime.strptime( self.metadata['Start Time of Acquisition'], "%d-%b-%Y %H:%M:%S %Z" ) tStop = datetime.datetime.strptime( self.metadata['Stop Time of Acquisition'], "%d-%b-%Y %H:%M:%S %Z" ) dtMid = DTU.timeDeltaToSeconds(tStop - tStart)/2. tMid = tStart + datetime.timedelta(microseconds=int(dtMid*1e6)) frame = self.frame frame._frameNumber = 1 frame._trackNumber = 1 frame.setSensingStart(tStart) frame.setSensingStop(tStop) frame.setSensingMid(tMid) frame.setNumberOfLines( int(self.metadata['slc_1_1x1_mag.set_rows'])) frame.setNumberOfSamples( int(self.metadata['slc_1_1x1_mag.set_cols'])) frame.setPolarization(self.metadata['Polarization']) frame.C0 = self.metadata['slc_1_1x1_mag.col_addr'] frame.S0 = self.metadata[ 'Segment {} Data Starting Azimuth'.format(self.segment_index)] frame.nearLookAngle = self.metadata['Minimum Look Angle'] frame.farLookAngle = self.metadata['Maximum Look Angle'] frame.setStartingRange(self.startingRange) frame.platformHeight = self.platformHeight width = frame.getNumberOfSamples() deltaRange = frame.instrument.getRangePixelSize() nearRange = frame.getStartingRange() midRange = nearRange + (width/2.)*deltaRange frame.setFarRange(nearRange+width*deltaRange) self.extractDoppler() frame._ellipsoid = self.elp frame.peg = self.peg frame.procVelocity = self.velocity from isceobj.Location.Coordinate import Coordinate frame.upperLeftCorner = Coordinate() #The corner latitude, longitudes are given as a pair #of values in degrees at each corner (without rdf unit specified) llC = [] for ic in range(1,5): key = 'Segment {0} Data Approximate Corner {1}'.format( self.segment_index, ic) self.logger.info("key = {}".format(key)) self.logger.info("metadata[key] = {}".format(self.metadata[key], type(self.metadata[key]))) llC.append(list(map(float, self.metadata[key].split(',')))) frame.terrainHeight = self.terrainHeight frame.upperLeftCorner.setLatitude(llC[0][0]) frame.upperLeftCorner.setLongitude(llC[0][1]) frame.upperLeftCorner.setHeight(self.terrainHeight) frame.upperRightCorner = Coordinate() frame.upperRightCorner.setLatitude(llC[1][0]) frame.upperRightCorner.setLongitude(llC[1][1]) frame.upperRightCorner.setHeight(self.terrainHeight) frame.lowerRightCorner = Coordinate() frame.lowerRightCorner.setLatitude(llC[2][0]) frame.lowerRightCorner.setLongitude(llC[2][1]) frame.lowerRightCorner.setHeight(self.terrainHeight) frame.lowerLeftCorner = Coordinate() frame.lowerLeftCorner.setLatitude(llC[3][0]) frame.lowerLeftCorner.setLongitude(llC[3][1]) frame.lowerLeftCorner.setHeight(self.terrainHeight) frame.nearLookAngle = math.degrees(self.metadata['Minimum Look Angle']) frame.farLookAngle = math.degrees(self.metadata['Maximum Look Angle']) return def _populateFrameSolo(self): self.logger.info("UAVSAR_Stack._populateFrameSolo") def _populateExtras(self): pass def _populateOrbit(self, **kwargs): """ Create the orbit as the reference orbit defined by the peg """ numgroup = 1000 prf = self.frame.instrument.getPulseRepetitionFrequency() daz = self.frame.instrument.getAzimuthPixelSize() vel = daz * prf t0 = self.frame.getSensingStart() nlines = int(( self.frame.getSensingStop() - t0).total_seconds() * prf) #make sure the elp property has been called elp = self.elp orbit = self.frame.getOrbit() orbit.setOrbitSource('Header') for i in range(-5*numgroup, int(nlines/numgroup)*numgroup+5*numgroup, numgroup): delt = int(i * 1.0e6 /prf) torb = self.frame.getSensingStart() + datetime.timedelta(microseconds=delt) ###Need to compute offset ###While taking into account, rounding off in time ds = delt*1.0e-6*vel vec = OrbitStateVector() vec.setTime( torb ) posSCH = [self.frame.S0 + ds, 0.0, self.platformHeight] velSCH = [self.velocity, 0., 0.] posXYZ, velXYZ = elp.schdot_to_xyzdot(posSCH, velSCH) vec.setPosition(posXYZ) vec.setVelocity(velXYZ) orbit.addStateVector(vec) return #t0 = (self.frame.getSensingStart() - #datetime.timedelta(microseconds=delta)) #ds = deltaFactor*self.frame.instrument.getAzimuthPixelSize() #s0 = self.platformStartingAzimuth - numExtra*ds #self.logger.info("populateOrbit: frame.sensingStart, frame.sensingStop = ", self.frame.getSensingStart(), #self.frame.getSensingStop()) #self.logger.info("populateOrbit: deltaFactor, numExtra, dt = ", deltaFactor, numExtra, dt) #self.logger.info("populateOrbit: t0, startingAzimuth, platformStartingAzimuth, s0, ds = ", #t0, self.frame.S0, self.platformStartingAzimuth, s0, ds) #h = self.platformHeight #v = [self.velocity, 0., 0.] #self.logger.info("t0, dt = ", t0, dt) #self.logger.info("s0, ds, h = ", s0, ds, h) #self.logger.info("elp.pegRadCur = ", self.elp.pegRadCur) #self.logger.info("v = ", v[0]) #platform = self.frame.getInstrument().getPlatform() #elp = self.elp #make sure the elp property has been called #orbit = self.frame.getOrbit() #orbit.setOrbitSource('Header') #for i in range(int(self.frame.getNumberOfLines()/deltaFactor)+1000*numExtra+1): #vec = OrbitStateVector() #t = t0 + datetime.timedelta(microseconds=int(i*dt*1e6)) #vec.setTime(t) #posSCH = [s0 + i*ds , 0., h] #velSCH = v #posXYZ, velXYZ = self.elp.schdot_to_xyzdot(posSCH, velSCH) #sch_pos, sch_vel = elp.xyzdot_to_schdot(posXYZ, velXYZ) #vec.setPosition(posXYZ) #vec.setVelocity(velXYZ) #orbit.addStateVector(vec) #return def populateMetadata(self): self._populatePlatform() self._populateInstrument() self._populateFrame() #self.extractDoppler() self._populateOrbit() def parse(self): from iscesys.Parsers import rdf self.metadata = rdf.parse(self.metadataFile) self.populateMetadata() def extractImage(self): self.parse() slcImage = isceobj.createSlcImage() self.slcname = self.metadata['slc_{}_1x1'.format(self.segment_index)] slcImage.setFilename(self.slcname) slcImage.setXmin(0) slcImage.setXmax(self.frame.getNumberOfSamples()) slcImage.setWidth(self.frame.getNumberOfSamples()) slcImage.setAccessMode('r') self.frame.setImage(slcImage) return def extractDoppler(self): """ Read doppler values from the doppler file and fit a polynomial """ frame = self.frame instrument = frame.getInstrument() rho0 = frame.getStartingRange() drho = instrument.getRangePixelSize() #full res value, not spacing in the dop file prf = instrument.getPulseRepetitionFrequency() self.logger.info("extractDoppler: rho0, drho, prf = {}, {}, {}".format(rho0, drho, prf)) dopfile = self.metadata['dop'] f = open(dopfile,'r') x = f.readlines() #first line is a header import numpy z = numpy.array( [list(map(float, e)) for e in list(map(str.split, x[1:]))] ) rho = z[:,0] dop = z[:,1] #rho0 = rho[0] #drho = (rho[1] - rho[0])/2.0 rhoi = [(r-rho0)/drho for r in rho] polydeg = 6 #2 #Quadratic is built in for now fit = numpy.polynomial.polynomial.polyfit(rhoi, dop, polydeg, rcond=1.e-9, full=True) coefs = fit[0] res2 = fit[1][0] #sum of squared residuals self.logger.info("coeffs = {}".format(coefs)) self.logger.info("rms residual = {}".format(numpy.sqrt(res2/len(dop)))) o = open("dop.txt", 'w') for i, d in zip(rhoi, dop): val = polyval(coefs,i) res = d-val o.write("{0} {1} {2} {3}\n".format(i, d, val, res)) dopfile = self.metadata['dop'] self.dopplerVals = {'Near':polyval(coefs, 0)} #need this temporarily in this module self.logger.info("UAVSAR_Stack.extractDoppler: self.dopplerVals = {}".format(self.dopplerVals)) self.logger.info("UAVSAR_Stack.extractDoppler: prf = {}".format(prf)) #The doppler file values are in units rad/m. divide by 2*pi rad/cycle to convert #to cycle/m. Then multiply by velocity to get Hz and divide by prf for dimensionless #doppler coefficients dop_scale = self.velocity/2.0/math.pi coefs = [x*dop_scale for x in coefs] #Set the coefs in frame._dopplerVsPixel because that is where DefaultDopp looks for them self.frame._dopplerVsPixel = coefs return coefs @property def terrainHeight(self): #The peg point incorporates the actual terrainHeight return 0.0 @property def platformHeight(self): h = self.metadata['Global Average Altitude'] #Reduce the platform height by the terrain height because the #peg radius of curvature includes the terrain height h -= self.metadata['Global Average Terrain Height'] return h @property def platformStartingAzimuth(self): azimuth = self.frame.S0 return azimuth @property def startingRange(self): return self.metadata['Image Starting Slant Range'] @property def squintAngle(self): """ Update this to use the sphere rather than planar approximation. """ startingRange = self.startingRange h = self.platformHeight v = self.velocity prf = self.frame.getInstrument().getPulseRepetitionFrequency() wavelength = self.frame.getInstrument().getRadarWavelength() if h > startingRange: raise ValueError("Spacecraft Height too large (%s>%s)" % (h, startingRange)) sinTheta = math.sqrt( 1 - (h/startingRange)**2 ) fd = self.dopplerVals['Near'] sinSquint = fd/(2.0*v*sinTheta)*wavelength if sinSquint**2 > 1: raise ValueError( "Error in One or More of the Squint Calculation Values\n"+ "Doppler Centroid: %s\nVelocity: %s\nWavelength: %s\n" % (fd, v, wavelength) ) self.squint = math.degrees( math.atan2(sinSquint, math.sqrt(1-sinSquint**2)) ) #squint is also required in the frame. self.frame.squintAngle = math.radians(self.squint) return self.squint @property def heightDt(self): """ Delta(height)/Delta(Time) from frame start-time to mid-time """ return 0.0 @property def velocity(self): v = self.metadata['Average Along Track Velocity'] platform = self.frame.getInstrument().getPlatform() elp = self.elp peg = self.peg scale = (elp.pegRadCur + self.platformHeight)/elp.pegRadCur ds_ground = self.frame.instrument.getAzimuthPixelSize() dt = 1.0/self.frame.instrument.getPulseRepetitionFrequency() v1 = scale*ds_ground/dt return v1 @property def elp(self): if not self._elp: planet = Planet(pname="Earth") self._elp = planet.get_elp() return self._elp @property def peg(self): if not self._peg: peg = [math.degrees(self.metadata['Peg Latitude']), math.degrees(self.metadata['Peg Longitude']), math.degrees(self.metadata['Peg Heading'])] th = self.metadata['Global Average Terrain Height'] platform = self.frame.getInstrument().getPlatform() self.elp.setSCH(peg[0], peg[1], peg[2], th) rc = self.elp.pegRadCur from isceobj.Location.Peg import Peg self._peg = Peg(latitude=peg[0], longitude=peg[1], heading=peg[2], radiusOfCurvature=rc) self.logger.info("UAVSAR_Stack: peg radius of curvature = {}".format(self.elp.pegRadCur)) self.logger.info("UAVSAR_Stack: terrain height = {}".format(th)) return self._peg