ISCE_INSAR/contrib/issi/components/ISSI/FR.py

380 lines
17 KiB
Python

import os
import logging
import tempfile
from isceobj.Filter.Filter import Filter
from iscesys.DateTimeUtil.DateTimeUtil import DateTimeUtil as DTU
from isceobj.Util.mathModule import MathModule as MM
# Because of Fortran 77 quirks, this string needs to end with a $
# This allows the path to be passed into the code
# and then 'trimmed' to the correct size
# There are better ways to do this, but this works for now.
dataPath = os.path.join(os.path.split(os.path.abspath(__file__))[0],
'igrf_data$')
class FR(object):
def __init__(self,hhFile=None,hvFile=None,vhFile=None,vvFile=None,lines=None,samples=None,
frOutput=None, tecOutput=None, phaseOutput=None):
"""
Constructor
@param hhFile (\a string) the file name containing the HH polarity data
@param hvFile (\a string) the file name containing the HV polarity data
@param vhFile (\a string) the file name containing the VH polarity data
@param vvFile (\a string) the file name containing the VV polarity data
@param lines (\a int) the number of ranges lines in each of the data files
@param samples (\a int) the number of range bins in each line of each data file
@param frOutput (\a string) the output file name for the Faraday rotation image
@param tecOutput (\a string) the output file name for the Total Electron Count (TEC) image
@param phaseOutput (\a string) the output file name for the phase delay image
"""
self.hhFile = hhFile
self.hvFile = hvFile
self.vhFile = vhFile
self.vvFile = vvFile
self.frOutput = frOutput
self.tecOutput = tecOutput
self.phaseOutput = phaseOutput
self.lines = lines
self.samples = samples
self.averageFaradayRotation = None
# The ionospheric layer parameters
self.top = 691.65 # top of the ionosphere in km
self.bottom = 100.0 # bottom of the ionosphere in km
self.step = 10.0 # height increment in km
self.logger = logging.getLogger("contrib.ISSI")
def getAverageFaradayRotation(self):
return self.averageFaradayRotation
def polarimetricCorrection(self,transmit,receive):
"""
Apply the polarimetic calibration.
@param transmit (\a isceobj.Sensor.Polarimetry.Distortion) The transmission distortion parameters
@param receive (\a isceobj.Sensor.Polarimetry.Distortion) The reception distortion parameters
"""
from ctypes import cdll, c_int, c_char_p, c_float
lib = cdll.LoadLibrary(os.path.dirname(__file__) + '/issi.so')
hhOutFile = self.hhFile.replace('.slc','_cal.slc')
hvOutFile = self.hvFile.replace('.slc','_cal.slc')
vhOutFile = self.vhFile.replace('.slc','_cal.slc')
vvOutFile = self.vvFile.replace('.slc','_cal.slc')
hhFile_c = c_char_p(self.hhFile.encode("utf-8"))
hvFile_c = c_char_p(self.hvFile.encode("utf-8"))
vhFile_c = c_char_p(self.vhFile.encode("utf-8"))
vvFile_c = c_char_p(self.vvFile.encode("utf-8"))
hhOutFile_c = c_char_p(hhOutFile.encode("utf-8"))
hvOutFile_c = c_char_p(hvOutFile.encode("utf-8"))
vhOutFile_c = c_char_p(vhOutFile.encode("utf-8"))
vvOutFile_c = c_char_p(vvOutFile.encode("utf-8"))
# Unpack the transmit and receive distortion matrices
transmitCrossTalk1Real_c = c_float(transmit.getCrossTalk1().real)
transmitCrossTalk1Imag_c = c_float(transmit.getCrossTalk1().imag)
transmitCrossTalk2Real_c = c_float(transmit.getCrossTalk2().real)
transmitCrossTalk2Imag_c = c_float(transmit.getCrossTalk2().imag)
transmitChannelImbalanceReal_c = c_float(transmit.getChannelImbalance().real)
transmitChannelImbalanceImag_c = c_float(transmit.getChannelImbalance().imag)
receiveCrossTalk1Real_c = c_float(receive.getCrossTalk1().real)
receiveCrossTalk1Imag_c = c_float(receive.getCrossTalk1().imag)
receiveCrossTalk2Real_c = c_float(receive.getCrossTalk2().real)
receiveCrossTalk2Imag_c = c_float(receive.getCrossTalk2().imag)
receiveChannelImbalanceReal_c = c_float(receive.getChannelImbalance().real)
receiveChannelImbalanceImag_c = c_float(receive.getChannelImbalance().imag)
samples_c = c_int(self.samples)
lines_c = c_int(self.lines)
self.logger.info("Applying polarimetric correction")
lib.polcal(hhFile_c,hvFile_c,vhFile_c,vvFile_c,hhOutFile_c,hvOutFile_c,vhOutFile_c,vvOutFile_c,
transmitCrossTalk1Real_c, transmitCrossTalk2Real_c, transmitChannelImbalanceReal_c,
transmitCrossTalk1Imag_c, transmitCrossTalk2Imag_c, transmitChannelImbalanceImag_c,
receiveCrossTalk1Real_c, receiveCrossTalk2Real_c, receiveChannelImbalanceReal_c,
receiveCrossTalk1Imag_c, receiveCrossTalk2Imag_c, receiveChannelImbalanceImag_c,
self.samples,self.lines)
# Move change the reference files to the calibrated files
self.hhFile = hhOutFile
self.hvFile = hvOutFile
self.vhFile = vhOutFile
self.vvFile = vvOutFile
def calculateFaradayRotation(self,filter=False,filterSize=None,swap=True):
"""
Create a map of Faraday Rotation from quad-pol SAR data
@param filter (\a boolean) True if spatial filtering is desired, default is False
@param filterSize (\a tuple) a tuple containing the filter size in the range and azimuth direction specified by (range size, azimuth
size)
@param swap (\a boolean) enable byte-swapping, default is True
"""
from ctypes import cdll, c_int, c_float, c_char_p
lib = cdll.LoadLibrary(os.path.dirname(__file__) + '/issi.so')
input = self._combinePolarizations(swap=swap)
if (filter):
input = self._filterFaradayRotation(input,filter,filterSize)
self.logger.info("Calculating Faraday Rotation")
input_c = c_char_p(input.name)
output_c = c_char_p(self.frOutput)
samples_c = c_int(self.samples)
lines_c = c_int(self.lines)
lib.cfrToFr.restype = c_float
self.averageFaradayRotation = lib.cfrToFr(input_c,output_c,samples_c,lines_c)
# Create a resource file for the Faraday Rotation output file
rsc = ResourceFile(self.frOutput + '.rsc')
rsc.write('WIDTH',self.samples)
rsc.write('FILE_LENGTH',self.lines)
if (filter):
rsc.write('FILTER_SIZE_RANGE',filterSize[0])
rsc.write('FILTER_SIZE_AZIMUTH',filterSize[1])
rsc.close()
def frToTEC(self,date,corners,lookAngle,lookDirection,fc):
"""
Given a list of geodetic coordinates, a list of lookAngles and a list of lookDirections,
calculate the average value of the B-field in the radar line-of-sight. Look angles are
calculated in degrees from the nadir and look directions are calculated in degrees from
the perpendicular to the flight direction.
@param date (\a datetime.datetime) the date on which to calculate the B-field
@param corners (\a list) a list of Location.Coordinate objects specifying the corners of the radar image
@param lookAngle (\a list) a list of the look angles (in degrees) to each corner of the radar image
@param lookDirection (\a list) a list of the look directions (in degrees) to each corner of the radar image
@param fc (\a float) the radar carrier frequency in Hz
@return (\a float) the mean value of the B-field in the look direction of the radar in gauss
"""
kdotb = []
# Calculate the integrated B vector for each of the four corners of the interferogram
# Need to get the date from any of the Frame objects associated with one of the polarities
for i,coordinate in enumerate(corners):
k = self._calculateLookVector(lookAngle[i],lookDirection[i])
kdotb.append(self._integrateBVector(date,coordinate,k))
# Use this value to convert from Faraday rotation to TEC
meankdotb = MM.mean(kdotb)
self.logger.info("Creating TEC Map")
self._scaleFRToTEC(meankdotb,fc)
# Create a resource file for the TEC output file
rsc = ResourceFile(self.tecOutput + '.rsc')
rsc.write('WIDTH',self.samples)
rsc.write('FILE_LENGTH',self.lines)
rsc.write('MEAN_K_DOT_B',meankdotb)
rsc.write('LOOK_DIRECTION',lookDirection[0])
for i in range(len(corners)):
lattag = 'LAT_CORNER_' + str((i+1))
lontag = 'LON_CORNER_' + str((i+1))
looktag = 'LOOK_ANGLE_' + str((i+1))
rsc.write(lattag,corners[i].getLatitude())
rsc.write(lontag,corners[i].getLongitude())
rsc.write(looktag,lookAngle[i])
rsc.close()
return meankdotb
def tecToPhase(self,fc):
"""
Apply a scalar value to convert from Total Electron Count (TEC) to Phase in radians.
@param fc (\a float) the carrier frequency of the radar
"""
from ctypes import cdll, c_float, c_int,c_char_p
lib = cdll.LoadLibrary(os.path.dirname(__file__) + '/issi.so')
inFile_c = c_char_p(self.tecOutput)
outFile_c = c_char_p(self.phaseOutput)
width_c = c_int(self.samples)
fc_c = c_float(fc)
lib.convertToPhase(inFile_c,outFile_c,width_c,fc_c)
# Create a resource file for the phase output
rsc = ResourceFile(self.phaseOutput + '.rsc')
rsc.write('WIDTH',self.samples)
rsc.write('FILE_LENGTH',self.lines)
rsc.close()
def _combinePolarizations(self,swap=True):
"""
Combine the four polarizations using the method of Bickel & Bates (1965).
@note: Bickel, S. H., and R. H. T. Bates (1965), Effects of magneto-ionic propagation on the polarization scattering matrix,
pp. 1089--1091.
@param swap (\a boolean) enable byte-swapping, default is True
@return (\a string) the temporary file name containing the combined polarization channels
"""
from ctypes import cdll, c_int, c_char_p
self.logger.info("Combining Polarizations")
lib = cdll.LoadLibrary(os.path.dirname(__file__) + '/issi.so')
output = tempfile.NamedTemporaryFile()
output_c = c_char_p(output.name)
hhFile_c = c_char_p(self.hhFile)
hvFile_c = c_char_p(self.hvFile)
vhFile_c = c_char_p(self.vhFile)
vvFile_c = c_char_p(self.vvFile)
samples_c = c_int(self.samples)
lines_c = c_int(self.lines)
swap_c = None
if (swap):
self.logger.debug("Byte swapping")
swap_c = c_int(0) # 0 for byte swapping, 1 for no byte swapping
else:
self.logger.debug("Not Byte swapping")
swap_c = c_int(1)
lib.cfr(hhFile_c,hvFile_c,vhFile_c,vvFile_c,output_c,samples_c,lines_c,swap_c)
return output
def _filterFaradayRotation(self,infile,filterType,filterSize):
"""
Apply a filter to the intermediate Faraday rotation product.
@param infile (\a string) the file name containing the complex*8 data to be filtered
@param filterType (\a string) the filter type, may be 'median', 'gaussian', or 'mean'
@param filterSize (\a list) a list containing the range and azimuth filter sizes
@return (\a string) a file name containing the filtered complex*8 data
@throws NotImplementedError: if filterType is not implemented
"""
outfile = tempfile.NamedTemporaryFile()
filter = Filter(inFile=infile.name, outFile=outfile.name, width=self.samples, length=self.lines)
#2013-06-04 Kosal
filterType = filterType.title()
filterWidth, filterHeight = filterSize
if (filterType == 'Median'):
filter.medianFilter(filterWidth, filterHeight)
elif (filterType == 'Gaussian'):
width = filterWidth
sigma = ( ( (width - 1) / 2 ) / 3.0 )**2 # Thus "stretches" the Gaussian so that the 3-sigma level occurs at the edge of the filter
filter.gaussianFilter(filterWidth, filterHeight, sigma)
elif (filterType == 'Mean'):
filter.meanFilter(filterWidth, filterHeight)
else:
self.logger.error("Filter type %s is not currently supported" % (filterType))
raise NotImplementedError()
self.logger.info("%s Filtering with a %dx%d filter" % (filterType, filterWidth, filterHeight))
#Kosal
return outfile
def _scaleFRToTEC(self,meankdotb,fc):
"""
Apply a scalar value to convert from Faraday Rotation to Total Electron Count.
@param meankdotb (\a float) the mean value of the B-field in the look direction of the radar
@param fc (\a float) the carrier frequency of the radar in Hz
"""
from ctypes import cdll, c_float, c_int,c_char_p
lib = cdll.LoadLibrary(os.path.dirname(__file__) + '/issi.so')
inFile_c = c_char_p(self.frOutput)
outFile_c = c_char_p(self.tecOutput)
width_c = c_int(self.samples)
bdotk_c = c_float(meankdotb)
fc_c = c_float(fc)
lib.convertToTec(inFile_c,outFile_c,width_c,bdotk_c,fc_c)
def _integrateBVector(self,date,coordinate,k):
"""
Integrate the B-field estimates through the ionosphere at the specified date and location
@param date (\a datetime.datetime) date at which to calculate the B-field
@param coordinate (\a isceobj.Location.Coordinate) the coordinate at which to calculate the B-field.
@param k (\a list) the look vector of the radar
@return (\a float) the integrated value of the B-field at the specified date and location in gauss
"""
kdotb = []
n_altitude = int((self.top - self.bottom)/self.step) + 1
altitude = [self.bottom + i*self.step for i in range(n_altitude)]
for h in altitude:
coordinate.setHeight(h)
bvector = self._calculateBVector(date,coordinate)
kdotb.append(MM.dotProduct(k,bvector))
meankdotb = MM.mean(kdotb)
return meankdotb
def _calculateBVector(self,date,coordinate):
"""
Given a date, and a coordinate, calculate the value of the B-field.
@param date (\a float) the decimal year at which to calulate the B-field
@param coordinate (\a isceobj.Location.Coordinate) the location at which to calculate the B-field
@return (\a list) the north, east and down values of the B-field in gauss
"""
from ctypes import cdll, c_float, c_char_p
lib = cdll.LoadLibrary(os.path.dirname(__file__) + '/issi.so')
year = DTU.dateTimeToDecimalYear(date)
year_c = c_float(year)
lat_c = c_float(coordinate.getLatitude())
lon_c = c_float(coordinate.getLongitude())
alt_c = c_float(coordinate.getHeight())
beast_c = (c_float*1)(*[0.0])
bnorth_c = (c_float*1)(*[0.0])
bdown_c = (c_float*1)(*[0.0])
babs_c = (c_float*1)(*[0.0])
dataPath_c = c_char_p(dataPath) # Point to the library directory
lib.calculateBVector(year_c,lat_c,lon_c,alt_c,beast_c,bnorth_c,bdown_c,babs_c,dataPath_c)
beast = beast_c[0]
bnorth = bnorth_c[0]
bdown = bdown_c[0]
return [beast, bnorth, bdown]
def _calculateLookVector(self,lookAngle,lookDirection):
"""
Calculate the look vector of the radar from the look direction and look angle.
@param lookAngle (\a float) the look angle of the radar measured from the nadir direction in degrees
@param lookDirection (\a float) the look direction of the radar measured from the direction perpendicular to the flight direction in
degrees
@return (\a list) the cartesian look vector
"""
import math
x = math.sin(math.radians(lookAngle))*math.sin(math.radians(lookDirection))
y = math.sin(math.radians(lookAngle))*math.cos(math.radians(lookDirection))
z = -math.cos(math.radians(lookAngle))
return [x,y,z]
class ResourceFile(object):
"""A simple resource file generator"""
def __init__(self,filename):
"""
Constructor
@param filename (\a string) the resource file name
"""
self.file = open(filename,'w')
def close(self):
"""
Explicitly close the resource file
"""
self.file.close()
def write(self,keyword,value):
"""
Write a keyword-value pair into the resource file
@param keyword (\a string) a resource file keyword
@param value (\a string) a resource file value
"""
keyword = keyword.upper()
keyword = keyword.replace(' ','_')
value = str(value)
self.file.write(keyword + ' ' + value + "\n")