#!/usr/bin/env python3 # # Author: Cunren Liang # Copyright 2015-present, NASA-JPL/Caltech # import os import glob import shutil import datetime import numpy as np import xml.etree.ElementTree as ET import isce, isceobj from isceobj.Alos2Proc.runIonFilt import computeIonosphere from isceobj.Alos2Proc.runIonFilt import gaussian #from isceobj.Alos2Proc.runIonFilt import least_sqares from isceobj.Alos2Proc.runIonFilt import polyfit_2d from isceobj.Alos2Proc.runIonFilt import adaptive_gaussian from isceobj.Alos2Proc.runIonFilt import reformatMaskedAreas from StackPulic import loadTrack from StackPulic import createObject from StackPulic import stackDateStatistics from StackPulic import acquisitionModesAlos2 from StackPulic import subbandParameters from compute_burst_sync import computeBurstSynchronization def ionFilt(self, referenceTrack, catalog=None): from isceobj.Alos2Proc.runIonSubband import defineIonDir ionDir = defineIonDir() subbandPrefix = ['lower', 'upper'] ionCalDir = os.path.join(ionDir['ion'], ionDir['ionCal']) os.makedirs(ionCalDir, exist_ok=True) os.chdir(ionCalDir) log = '' ############################################################ # STEP 1. compute ionospheric phase ############################################################ from isceobj.Constants import SPEED_OF_LIGHT from isceobj.Alos2Proc.Alos2ProcPublic import create_xml ################################### #SET PARAMETERS HERE #THESE SHOULD BE GOOD ENOUGH, NO NEED TO SET IN setup(self) corThresholdAdj = 0.97 corOrderAdj = 20 ################################### print('\ncomputing ionosphere') #get files ml2 = '_{}rlks_{}alks'.format(self._insar.numberRangeLooks1*self._insar.numberRangeLooksIon, self._insar.numberAzimuthLooks1*self._insar.numberAzimuthLooksIon) lowerUnwfile = subbandPrefix[0]+ml2+'.unw' upperUnwfile = subbandPrefix[1]+ml2+'.unw' corfile = 'diff'+ml2+'.cor' #use image size from lower unwrapped interferogram img = isceobj.createImage() img.load(lowerUnwfile + '.xml') width = img.width length = img.length lowerUnw = (np.fromfile(lowerUnwfile, dtype=np.float32).reshape(length*2, width))[1:length*2:2, :] upperUnw = (np.fromfile(upperUnwfile, dtype=np.float32).reshape(length*2, width))[1:length*2:2, :] cor = (np.fromfile(corfile, dtype=np.float32).reshape(length*2, width))[1:length*2:2, :] #amp = (np.fromfile(corfile, dtype=np.float32).reshape(length*2, width))[0:length*2:2, :] #masked out user-specified areas if self.maskedAreasIon != None: maskedAreas = reformatMaskedAreas(self.maskedAreasIon, length, width) for area in maskedAreas: lowerUnw[area[0]:area[1], area[2]:area[3]] = 0 upperUnw[area[0]:area[1], area[2]:area[3]] = 0 cor[area[0]:area[1], area[2]:area[3]] = 0 #remove possible wired values in coherence cor[np.nonzero(cor<0)] = 0.0 cor[np.nonzero(cor>1)] = 0.0 #remove water body wbd = np.fromfile('wbd'+ml2+'.wbd', dtype=np.int8).reshape(length, width) cor[np.nonzero(wbd==-1)] = 0.0 #remove small values cor[np.nonzero(cor size_max: print('\n\nWARNING: minimum window size for filtering ionosphere phase {} > maximum window size {}'.format(size_min, size_max)) print(' re-setting maximum window size to {}\n\n'.format(size_min)) size_max = size_min if size_secondary % 2 != 1: size_secondary += 1 print('window size of secondary filtering of ionosphere phase should be odd, window size changed to {}'.format(size_secondary)) #coherence threshold for fitting a polynomial corThresholdFit = 0.25 #ionospheric phase standard deviation after filtering std_out0 = self.filterStdIon #std_out0 = 0.1 ################################################# print('\nfiltering ionosphere') #input files ionfile = 'ion'+ml2+'.ion' #corfile = 'diff'+ml2+'.cor' corLowerfile = subbandPrefix[0]+ml2+'.cor' corUpperfile = subbandPrefix[1]+ml2+'.cor' #output files ionfiltfile = 'filt_ion'+ml2+'.ion' stdfiltfile = 'filt_ion'+ml2+'.std' windowsizefiltfile = 'filt_ion'+ml2+'.win' #read data img = isceobj.createImage() img.load(ionfile + '.xml') width = img.width length = img.length ion = np.fromfile(ionfile, dtype=np.float32).reshape(length, width) corLower = (np.fromfile(corLowerfile, dtype=np.float32).reshape(length*2, width))[1:length*2:2, :] corUpper = (np.fromfile(corUpperfile, dtype=np.float32).reshape(length*2, width))[1:length*2:2, :] cor = (corLower + corUpper) / 2.0 index = np.nonzero(np.logical_or(corLower==0, corUpper==0)) cor[index] = 0 del corLower, corUpper #masked out user-specified areas if self.maskedAreasIon != None: maskedAreas = reformatMaskedAreas(self.maskedAreasIon, length, width) for area in maskedAreas: ion[area[0]:area[1], area[2]:area[3]] = 0 cor[area[0]:area[1], area[2]:area[3]] = 0 #remove possible wired values in coherence cor[np.nonzero(cor<0)] = 0.0 cor[np.nonzero(cor>1)] = 0.0 #remove water body. Not helpful, just leave it here wbd = np.fromfile('wbd'+ml2+'.wbd', dtype=np.int8).reshape(length, width) cor[np.nonzero(wbd==-1)] = 0.0 # #applying water body mask here # waterBodyFile = 'wbd'+ml2+'.wbd' # if os.path.isfile(waterBodyFile): # print('applying water body mask to coherence used to compute ionospheric phase') # wbd = np.fromfile(waterBodyFile, dtype=np.int8).reshape(length, width) # cor[np.nonzero(wbd!=0)] = 0.00001 #minimize the effect of low coherence pixels #cor[np.nonzero( (cor<0.85)*(cor!=0) )] = 0.00001 #filt = adaptive_gaussian(ion, cor, size_max, size_min) #cor**14 should be a good weight to use. 22-APR-2018 #filt = adaptive_gaussian_v0(ion, cor**corOrderFilt, size_max, size_min) #1. compute number of looks azimuthBandwidth = 0 for i, frameNumber in enumerate(self._insar.referenceFrames): for j, swathNumber in enumerate(range(self._insar.startingSwath, self._insar.endingSwath + 1)): #azimuthBandwidth += 2270.575 * 0.85 azimuthBandwidth += referenceTrack.frames[i].swaths[j].azimuthBandwidth azimuthBandwidth = azimuthBandwidth / (len(self._insar.referenceFrames)*(self._insar.endingSwath-self._insar.startingSwath+1)) #azimuth number of looks should also apply to burst mode #assume range bandwidth of subband image is 1/3 of orginal range bandwidth, as in runIonSubband.py!!! numberOfLooks = referenceTrack.azimuthLineInterval * self._insar.numberAzimuthLooks1*self._insar.numberAzimuthLooksIon / (1.0/azimuthBandwidth) *\ referenceTrack.frames[0].swaths[0].rangeBandwidth / 3.0 / referenceTrack.rangeSamplingRate * self._insar.numberRangeLooks1*self._insar.numberRangeLooksIon #consider also burst characteristics. In ScanSAR-stripmap interferometry, azimuthBandwidth is from referenceTrack (ScanSAR) if self._insar.modeCombination in [21, 31]: numberOfLooks /= 5.0 if self._insar.modeCombination in [22, 32]: numberOfLooks /= 7.0 if self._insar.modeCombination in [21]: numberOfLooks *= (self._insar.burstSynchronization/100.0) #numberOfLooks checked print('number of looks to be used for computing subband interferogram standard deviation: {}'.format(numberOfLooks)) if catalog is not None: catalog.addItem('number of looks of subband interferograms', numberOfLooks, 'runIonFilt') log += 'number of looks of subband interferograms: {}\n'.format(numberOfLooks) #2. compute standard deviation of the raw ionospheric phase #f0 same as in runIonSubband.py!!! def ion_std(fl, fu, numberOfLooks, cor): ''' compute standard deviation of ionospheric phase fl: lower band center frequency fu: upper band center frequency cor: coherence, must be numpy array ''' f0 = (fl + fu) / 2.0 interferogramVar = (1.0 - cor**2) / (2.0 * numberOfLooks * cor**2 + (cor==0)) std = fl*fu/f0/(fu**2-fl**2)*np.sqrt(fu**2*interferogramVar+fl**2*interferogramVar) std[np.nonzero(cor==0)] = 0 return std std = ion_std(fl, fu, numberOfLooks, cor) #3. compute minimum filter window size for given coherence and standard deviation of filtered ionospheric phase cor2 = np.linspace(0.1, 0.9, num=9, endpoint=True) std2 = ion_std(fl, fu, numberOfLooks, cor2) std_out2 = np.zeros(cor2.size) win2 = np.zeros(cor2.size, dtype=np.int32) for i in range(cor2.size): for size in range(9, 10001, 2): #this window must be the same as those used in adaptive_gaussian!!! gw = gaussian(size, size/2.0, scale=1.0) scale = 1.0 / np.sum(gw / std2[i]**2) std_out2[i] = scale * np.sqrt(np.sum(gw**2 / std2[i]**2)) win2[i] = size if std_out2[i] <= std_out0: break print('if ionospheric phase standard deviation <= {} rad, minimum filtering window size required:'.format(std_out0)) print('coherence window size') print('************************') for x, y in zip(cor2, win2): print(' %5.2f %5d'%(x, y)) print() if catalog is not None: catalog.addItem('coherence value', cor2, 'runIonFilt') catalog.addItem('minimum filter window size', win2, 'runIonFilt') log += 'coherence value: {}\n'.format(cor2) log += 'minimum filter window size: {}\n'.format(win2) #4. filter interferogram #fit ionosphere if fit: #prepare weight wgt = std**2 wgt[np.nonzero(cor