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")