ISCE_INSAR/applications/make_raw.py

441 lines
16 KiB
Python

#!/usr/bin/env python3
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Copyright 2010 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: Walter Szeliga
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import isce
from isce import logging
from iscesys.Compatibility import Compatibility
from iscesys.Component.Component import Component, Port
from isceobj.Planet.Ellipsoid import Ellipsoid
from isceobj.Doppler.Doppler import Doppler
from isceobj.Orbit.Orbit import Orbit
#from iscesys.DateTimeUtil.DateTimeUtil import DateTimeUtil as DTU
from iscesys import DateTimeUtil as DTU
from iscesys.Component.Application import Application
from isce.applications.insarApp import SENSOR_NAME, DOPPLER_METHOD
from isceobj.Scene.Frame import FrameMixin
from isceobj.Util.decorators import port
SENSOR = Application.Facility('sensor',
public_name='sensor',
module='isceobj.Sensor',
factory='createSensor',
args=(SENSOR_NAME, ),
mandatory=True,
doc="Reference raw data component"
)
DOPPLER = Application.Facility('doppler',
public_name='doppler',
module='isceobj.Doppler',
factory='createDoppler',
args=(DOPPLER_METHOD, ),
mandatory=False,
doc="Reference Doppler calculation method"
)
class makeRawApp(Application):
parameter_list = (SENSOR_NAME, DOPPLER_METHOD)
facility_list = (SENSOR, DOPPLER)
def main(self):
self.make_raw.wireInputPort(name='doppler', object=self.doppler)
self.make_raw.wireInputPort(name='sensor', object=self.sensor)
self.make_raw.make_raw()
self.printInfo()
def printInfo(self):
print(self.make_raw.frame)
print(self.make_raw)
def __init__(self):
Application.__init__(self, "makeraw")
self.sensor = None
self.doppler = None
self.make_raw = make_raw()
def initFromArglist(self, arglist):
self.initFactory(arglist)
self.sensor = self.getComponent('Sensor')
self.doppler = self.getComponent('Doppler')
class make_raw(Component, FrameMixin):
def __init__(self):
self.sensor = None
self.doppler = None
self.dopplerValues = None
self.frame = None
# Derived Values
self.spacecraftHeight = 0.0
self.heightDt = 0.0
self.velocity = 0.0
self.squint = 0.0
self.iqImage = None
Component.__init__(self)
sensorPort = Port(name='sensor', method=self.addSensor)
dopplerPort = Port(name='doppler', method=self.addDoppler)
self._inputPorts.add(sensorPort)
self._inputPorts.add(dopplerPort)
self.logger = logging.getLogger("isce.make_raw")
return None
def __getstate__(self):
d = dict(self.__dict__)
del d['logger']
return d
def __setstate__(self, d):
self.__dict__.update(d)
self.logger = logging.getLogger("isce.make_raw")
return None
@port('extractImage')
def addSensor(self):
return None
@port('calculateDoppler')
def addDoppler(self):
return None
def getFrame(self):
return self.frame
def getIQImage(self):
return self.iqImage
def getDopplerValues(self):
return self.dopplerValues
def getSpacecraftHeight(self):
return self.spacecraftHeight
def getHeightDT(self):
return self.heightDt
def getVelocity(self):
return self.velocity
def getSquint(self):
return self.squint
def calculateHeightDt(self):
orbit = self.orbit
ellipsoid = self.ellipsoid
startTime = self.sensingStart
midTime = self.sensingMid
sv0 = orbit.interpolate(startTime)
sv1 = orbit.interpolate(midTime)
startHeight = sv0.calculateHeight(ellipsoid)
midHeight = sv1.calculateHeight(ellipsoid)
if ('uav' in self.sensor.family.lower()) and (hasattr(self.sensor, 'platformHeight')):
self.spacecraftHeight = self.sensor.platformHeight
else:
self.spacecraftHeight = startHeight
self.heightDt = (
(midHeight - startHeight)/
DTU.timeDeltaToSeconds(midTime - startTime)
)
def calculateVelocity(self):
import math
orbit = self.orbit
midTime = self.sensingMid
sv = orbit.interpolateOrbit(midTime)
vx1, vy1, vz1 = sv.velocity
self.velocity = math.sqrt(vx1**2 + vy1**2 + vz1**2)
def calculateSquint(self):
"""Calculate the squint angle
R0 is the starting range
h is the height at mid-swath
v is the velocity at mid-swath
"""
import math
startingRange = self.startingRange
prf = self.PRF
wavelength = self.radarWavelength
h = self.spacecraftHeight
try:
z = self.sensor.terrainHeight
except:
z = 0.0
v = self.velocity
if h - z > startingRange:
raise ValueError(
("Spacecraft Height - Terrain Height (%s) " +
"larger than starting Range (%s)") % (h-z, startingRange))
sinTheta = math.sqrt( 1 - ((h-z)/startingRange)**2 )
if 'a' in self.doppler.quadratic:
fd = self.doppler.quadratic['a']*prf
elif isinstance(self.doppler.quadratic, (list, tuple)):
####For UAVSAR
fd = self.doppler.quadratic[0]
else:
self.logger.error(
"make_raw doesn't handle doppler coefficient object type, ",
type(self.doppler.quadratic)
)
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 used later on from the frame; add it here
self.frame.squintAngle = math.radians(self.squint)
def make_raw(self):
from isceobj.Image import createRawImage, createSlcImage
self.activateInputPorts()
# Parse the image metadata and extract the image
self.logger.info('Extracting image')
try:
self.sensor.extractImage()
except NotImplementedError as strerr:
self.logger.error("%s" % (strerr))
self.logger.error(
"make_raw not implemented for %s" % self.sensor.__class__
)
raise NotImplementedError
#reset the global variable to empty so can go back to use default api
self.sensor.frame.image.renderVRT()
self.frame = self.sensor.frame
#jng NOTE if we pass just the sensor also in the case of raw image we
## can avoid the if
if isinstance(self.frame.image, createRawImage().__class__):
# Calculate the doppler fit
self.logger.info("Calculating Doppler Centroid")
try:
self.doppler.wireInputPort(name='frame',
object=self.frame)
except:
computeFlag = False
else:
computeFlag = True
if computeFlag:
self.doppler.wireInputPort(name='instrument',
object=self.frame.instrument)
self.doppler.wireInputPort(name='image',
object=self.frame.image)
self.doppler.calculateDoppler()
else:
self.doppler.wireInputPort(name='sensor', object=self.sensor)
self.doppler.calculateDoppler()
#new jng compute slc image size here
rangeSamplingRate = self.instrument.rangeSamplingRate
rangePulseDuration = self.instrument.pulseLength
goodBytes = self.frame.image.xmax - self.frame.image.xmin
try:
#check if the instrument implements it, if not set it to zero
chirpExtension = self.instrument.chirpExtension # Should probably be a percentage rather than a set number
except AttributeError:
chirpExtension = 0
chirpSize = int(rangeSamplingRate * rangePulseDuration)
self.frame.numberRangeBins = (int(goodBytes/2) -
chirpSize + chirpExtension)
elif isinstance(self.frame.image, createSlcImage().__class__):
# jng changed in view of the new tsx preproc from Howard
self.doppler.wireInputPort(name='sensor', object=self.sensor)
self.doppler.calculateDoppler()
#new jng compute slc image size here
self.frame.numberRangeBins = self.frame.image.width
else:
message = (
"Unrecognized image type %s" %
str(self.frame.image.__class__)
)
self.logger.error(message)
raise TypeError(message)
# Fit a polynomial to the doppler values. in the tsx case or every
# zero doppler case this function simple sets the a = fd b = 0, c = 0
self.doppler.fitDoppler()
# Create a doppler object
prf = self.frame.instrument.PRF
#coef = self.doppler.coeff_list
#for ii in range(len(coef), 4):
# coef.append(0.0)
if 'a' in self.doppler.quadratic:
coef = [self.doppler.quadratic['a']*prf,0.0,0.0,0.0]
elif isinstance(self.doppler.quadratic, (list, tuple)):
####For UAVSAR
coef = self.doppler.quadratic
else:
self.logger.error(
"make_raw doesn't handle doppler coefficient object type, ",
type(self.doppler.quadratic)
)
self.dopplerValues = Doppler(prf=prf)
self.dopplerValues.setDopplerCoefficients(coef, inHz=True)
if self.frame._dopplerVsPixel is None:
self.frame._dopplerVsPixel = [x*prf for x in coef]
# Calculate the height, height_dt, and velocity
self.logger.info("Calculating Spacecraft Velocity")
self.calculateHeightDt()
self.calculateVelocity()
# Calculate squint angle
self.logger.info("Calculating Squint Angle")
self.calculateSquint()
self.frame.image.numberGoodBytes = self.frame.image.xmax - self.frame.image.xmin
self.frame.image.coord1.coordStart = self.frame.image.xmin
self.createIQImage()
self.frame.image.renderHdr()
#just in case the Sensor does not compute the pulse timing
try:
self.adjustSensingStart()
except:
pass
return None
def createIQImage(self):
from isceobj.Image import createRawIQImage
#create an RawIQImage with appropriate values from the RawImage
self.iqImage = createRawIQImage()
self.iqImage.width = self.frame.image.width/2
self.iqImage.xmax = self.iqImage.width
self.iqImage.length = self.frame.image.length
self.iqImage.coord1.coordStart = int(self.frame.image.coord1.coordStart/2)
self.iqImage.numberGoodSamples = int(self.frame.image.numberGoodBytes/2)
self.iqImage.filename = self.frame.image.filename #the file is the same as for the raw
self.iqImage.inPhase = self.frame.instrument.getInPhaseValue()
self.iqImage.quadrature = self.frame.instrument.getQuadratureValue()
#change the name that will be used for the xml file
filename = self.frame.image.filename.replace('.raw','.iq.xml')
#just in case the extension was not .raw
if not filename.count('.iq'):
filename += '.iq.xml'
self.iqImage.renderHdr(filename)
#change the name that will be used for the vrt file
filename = filename.replace('.xml','.vrt')
self.iqImage.renderVRT(filename)
def adjustSensingStart(self, pulseTimingFilename=None, ext='.aux'):
pulseTimingFilename = (
pulseTimingFilename or
self.frame.image.filename + ext
)
import datetime as dt
import math
import struct
with open(pulseTimingFilename) as fp:
allF = fp.read()
pass
#use only a limited number of point from the first frame
lines = min(len(allF)/16, 10000)
allT = [0]*lines
d0 = struct.unpack('<d', allF[0:8])[0]
day0 = dt.timedelta(d0).days
sec = 0
for i in range(lines):
day, musec = struct.unpack('<dd', allF[i*16:(i+1)*16])
# note the musec are relative to the day, not to the second i.e.
# they are the total musec in the day
td = dt.timedelta(day, sec, musec)
allT[i] = (
(td.microseconds +
(td.seconds +
(td.days - day0) * 24 * 3600.0) * 10**6) / 10**6
)
pass
prf = self.frame.instrument.PRF
sumPart = [allT[i] - i/prf for i in xrange(len(allT))]
sum = math.fsum(sumPart)
sum /= len(allT)
day = day0
sec = math.floor(sum)
musec = (sum - sec)*10**6
sensingOld = self.frame.sensingStart
#day-1 since we start from jan 1 and not jan 0
newSensingStart = (
dt.datetime(sensingOld.year, 1, 1) +
dt.timedelta(day-1, sec, musec)
)
self.frame.setSensingStart(newSensingStart)
self.logger.info("Changing sensing start from %s to %s" %
(str(sensingOld), str(newSensingStart)))
def __str__(self):
retstr = "Velocity: (%s)\n"
retlst = (self.velocity, )
retstr += "HeightDt: (%s)\n"
retlst += (self.heightDt, )
retstr += "Squint: (%s)\n"
retlst += (self.squint, )
retstr += "Height: (%s)\n"
retlst += (self.spacecraftHeight, )
return retstr % retlst
pass
## JEB: added a main for script operation
def main():
return makeRawApp().run()
if __name__ == "__main__":
import sys
if (len(sys.argv) < 2):
print("Usage:%s <xml-parameter file>" % sys.argv[0])
sys.exit(1)
main()