<filename>components/isceobj/Alos2Proc/runPreprocessor.py
#
# Author: <NAME>
# Copyright 2015-present, NASA-JPL/Caltech
#

import os
import glob
import logging
import datetime
import numpy as np

import isceobj
import isceobj.Sensor.MultiMode as MultiMode
from isceobj.Planet.Planet import Planet
from isceobj.Alos2Proc.Alos2ProcPublic import runCmd
from isceobj.Alos2Proc.Alos2ProcPublic import getBboxRdr
from isceobj.Alos2Proc.Alos2ProcPublic import getBboxGeo

logger = logging.getLogger('isce.alos2insar.runPreprocessor')

def runPreprocessor(self):
    '''Extract images.
    '''
    catalog = isceobj.Catalog.createCatalog(self._insar.procDoc.name)


    #find files
    #actually no need to use absolute path any longer, since we are able to find file from vrt now. 27-JAN-2020, CRL.
    #denseoffset may still need absolute path when making links
    self.masterDir = os.path.abspath(self.masterDir)
    self.slaveDir = os.path.abspath(self.slaveDir)

    ledFilesMaster = sorted(glob.glob(os.path.join(self.masterDir, 'LED-ALOS2*-*-*')))
    imgFilesMaster = sorted(glob.glob(os.path.join(self.masterDir, 'IMG-{}-ALOS2*-*-*'.format(self.masterPolarization.upper()))))

    ledFilesSlave = sorted(glob.glob(os.path.join(self.slaveDir, 'LED-ALOS2*-*-*')))
    imgFilesSlave = sorted(glob.glob(os.path.join(self.slaveDir, 'IMG-{}-ALOS2*-*-*'.format(self.slavePolarization.upper()))))

    firstFrameMaster = ledFilesMaster[0].split('-')[-3][-4:]
    firstFrameSlave = ledFilesSlave[0].split('-')[-3][-4:]
    firstFrameImagesMaster = sorted(glob.glob(os.path.join(self.masterDir, 'IMG-{}-ALOS2*{}-*-*'.format(self.masterPolarization.upper(), firstFrameMaster))))
    firstFrameImagesSlave = sorted(glob.glob(os.path.join(self.slaveDir, 'IMG-{}-ALOS2*{}-*-*'.format(self.slavePolarization.upper(), firstFrameSlave))))


    #determin operation mode
    masterMode = os.path.basename(ledFilesMaster[0]).split('-')[-1][0:3]
    slaveMode = os.path.basename(ledFilesSlave[0]).split('-')[-1][0:3]
    spotlightModes = ['SBS']
    stripmapModes = ['UBS', 'UBD', 'HBS', 'HBD', 'HBQ', 'FBS', 'FBD', 'FBQ']
    scansarNominalModes = ['WBS', 'WBD', 'WWS', 'WWD']
    scansarWideModes = ['VBS', 'VBD']
    scansarModes = ['WBS', 'WBD', 'WWS', 'WWD', 'VBS', 'VBD']

    #usable combinations
    if (masterMode in spotlightModes) and (slaveMode in spotlightModes):
        self._insar.modeCombination = 0
    elif (masterMode in stripmapModes) and (slaveMode in stripmapModes):
        self._insar.modeCombination = 1
    elif (masterMode in scansarNominalModes) and (slaveMode in scansarNominalModes):
        self._insar.modeCombination = 21
    elif (masterMode in scansarWideModes) and (slaveMode in scansarWideModes):
        self._insar.modeCombination = 22
    elif (masterMode in scansarNominalModes) and (slaveMode in stripmapModes):
        self._insar.modeCombination = 31
    elif (masterMode in scansarWideModes) and (slaveMode in stripmapModes):
        self._insar.modeCombination = 32
    else:
        print('\n\nthis mode combination is not possible')
        print('note that for ScanSAR-stripmap, ScanSAR must be master\n\n')
        raise Exception('mode combination not supported')

# pixel size from real data processing. azimuth pixel size may change a bit as
# the antenna points to a different swath and therefore uses a different PRF.

#   MODE  RANGE PIXEL SIZE (LOOKS)       AZIMUTH PIXEL SIZE (LOOKS)
# -------------------------------------------------------------------
#   SPT    [SBS]
#          1.4304222392897463 (2)         0.9351804642158579 (4)
#   SM1    [UBS,UBD]
#          1.4304222392897463 (2)         1.8291988125114438 (2)
#   SM2    [HBS,HBD,HBQ]
#          2.8608444785794984 (2)         3.0672373839847196 (2)
#   SM3    [FBS,FBD,FBQ]
#          4.291266717869248  (2)         3.2462615913656667 (4)

#   WD1    [WBS,WBD] [WWS,WWD]
#          8.582533435738496  (1)         2.6053935830031887 (14)
#          8.582533435738496  (1)         2.092362043327227  (14)
#          8.582533435738496  (1)         2.8817632034495717 (14)
#          8.582533435738496  (1)         3.054362492601842  (14)
#          8.582533435738496  (1)         2.4582084463356977 (14)

#   WD2    [VBS,VBD]
#          8.582533435738496  (1)         2.9215796012950728 (14)
#          8.582533435738496  (1)         3.088859074497863  (14)
#          8.582533435738496  (1)         2.8792293071133073 (14)
#          8.582533435738496  (1)         3.0592146044234854 (14)
#          8.582533435738496  (1)         2.8818767752199137 (14)
#          8.582533435738496  (1)         3.047038521027477  (14)
#          8.582533435738496  (1)         2.898816222039108  (14)

    #determine default number of looks:
    self._insar.numberRangeLooks1 = self.numberRangeLooks1
    self._insar.numberAzimuthLooks1 = self.numberAzimuthLooks1
    self._insar.numberRangeLooks2 = self.numberRangeLooks2
    self._insar.numberAzimuthLooks2 = self.numberAzimuthLooks2
    #the following two will be automatically determined by runRdrDemOffset.py
    self._insar.numberRangeLooksSim = self.numberRangeLooksSim
    self._insar.numberAzimuthLooksSim = self.numberAzimuthLooksSim
    self._insar.numberRangeLooksIon = self.numberRangeLooksIon
    self._insar.numberAzimuthLooksIon = self.numberAzimuthLooksIon

    if self._insar.numberRangeLooks1 == None:
        if masterMode in ['SBS']:
            self._insar.numberRangeLooks1 = 2
        elif masterMode in ['UBS', 'UBD']:
            self._insar.numberRangeLooks1 = 2
        elif masterMode in ['HBS', 'HBD', 'HBQ']:
            self._insar.numberRangeLooks1 = 2
        elif masterMode in ['FBS', 'FBD', 'FBQ']:
            self._insar.numberRangeLooks1 = 2
        elif masterMode in ['WBS', 'WBD']:
            self._insar.numberRangeLooks1 = 1
        elif masterMode in ['WWS', 'WWD']:
            self._insar.numberRangeLooks1 = 2
        elif masterMode in ['VBS', 'VBD']:
            self._insar.numberRangeLooks1 = 1
        else:
            raise Exception('unknow acquisition mode')

    if self._insar.numberAzimuthLooks1 == None:
        if masterMode in ['SBS']:
            self._insar.numberAzimuthLooks1 = 4
        elif masterMode in ['UBS', 'UBD']:
            self._insar.numberAzimuthLooks1 = 2
        elif masterMode in ['HBS', 'HBD', 'HBQ']:
            self._insar.numberAzimuthLooks1 = 2
        elif masterMode in ['FBS', 'FBD', 'FBQ']:
            self._insar.numberAzimuthLooks1 = 4
        elif masterMode in ['WBS', 'WBD']:
            self._insar.numberAzimuthLooks1 = 14
        elif masterMode in ['WWS', 'WWD']:
            self._insar.numberAzimuthLooks1 = 14
        elif masterMode in ['VBS', 'VBD']:
            self._insar.numberAzimuthLooks1 = 14
        else:
            raise Exception('unknow acquisition mode')

    if self._insar.numberRangeLooks2 == None:
        if masterMode in spotlightModes:
            self._insar.numberRangeLooks2 = 4
        elif masterMode in stripmapModes:
            self._insar.numberRangeLooks2 = 4
        elif masterMode in scansarModes:
            self._insar.numberRangeLooks2 = 5
        else:
            raise Exception('unknow acquisition mode')

    if self._insar.numberAzimuthLooks2 == None:
        if masterMode in spotlightModes:
            self._insar.numberAzimuthLooks2 = 4
        elif masterMode in stripmapModes:
            self._insar.numberAzimuthLooks2 = 4
        elif masterMode in scansarModes:
            self._insar.numberAzimuthLooks2 = 2
        else:
            raise Exception('unknow acquisition mode')

    if self._insar.numberRangeLooksIon == None:
        if masterMode in spotlightModes:
            self._insar.numberRangeLooksIon = 16
        elif masterMode in stripmapModes:
            self._insar.numberRangeLooksIon = 16
        elif masterMode in scansarModes:
            self._insar.numberRangeLooksIon = 40
        else:
            raise Exception('unknow acquisition mode')

    if self._insar.numberAzimuthLooksIon == None:
        if masterMode in spotlightModes:
            self._insar.numberAzimuthLooksIon = 16
        elif masterMode in stripmapModes:
            self._insar.numberAzimuthLooksIon = 16
        elif masterMode in scansarModes:
            self._insar.numberAzimuthLooksIon = 16
        else:
            raise Exception('unknow acquisition mode')


    #define processing file names
    self._insar.masterDate = os.path.basename(ledFilesMaster[0]).split('-')[2]
    self._insar.slaveDate = os.path.basename(ledFilesSlave[0]).split('-')[2]
    self._insar.setFilename(masterDate=self._insar.masterDate, slaveDate=self._insar.slaveDate, nrlks1=self._insar.numberRangeLooks1, nalks1=self._insar.numberAzimuthLooks1, nrlks2=self._insar.numberRangeLooks2, nalks2=self._insar.numberAzimuthLooks2)


    #find frame numbers
    if (self._insar.modeCombination == 31) or (self._insar.modeCombination == 32):
        if (self.masterFrames == None) or (self.slaveFrames == None):
            raise Exception('for ScanSAR-stripmap inteferometry, you must set master and slave frame numbers')
    #if not set, find frames automatically
    if self.masterFrames == None:
        self.masterFrames = []
        for led in ledFilesMaster:
            frameNumber = os.path.basename(led).split('-')[1][-4:]
            if frameNumber not in self.masterFrames:
                self.masterFrames.append(frameNumber)
    if self.slaveFrames == None:
        self.slaveFrames = []
        for led in ledFilesSlave:
            frameNumber = os.path.basename(led).split('-')[1][-4:]
            if frameNumber not in self.slaveFrames:
                self.slaveFrames.append(frameNumber)
    #sort frames
    self.masterFrames = sorted(self.masterFrames)
    self.slaveFrames = sorted(self.slaveFrames)
    #check number of frames
    if len(self.masterFrames) != len(self.slaveFrames):
        raise Exception('number of frames in master dir is not equal to number of frames \
            in slave dir. please set frame number manually')


    #find swath numbers (if not ScanSAR-ScanSAR, compute valid swaths)
    if (self._insar.modeCombination == 0) or (self._insar.modeCombination == 1):
        self.startingSwath = 1
        self.endingSwath = 1

    if self._insar.modeCombination == 21:
        if self.startingSwath == None:
            self.startingSwath = 1
        if self.endingSwath == None:
            self.endingSwath = 5

    if self._insar.modeCombination == 22:
        if self.startingSwath == None:
            self.startingSwath = 1
        if self.endingSwath == None:
            self.endingSwath = 7

    #determine starting and ending swaths for ScanSAR-stripmap, user's settings are overwritten
    #use first frame to check overlap
    if (self._insar.modeCombination == 31) or (self._insar.modeCombination == 32):
        if self._insar.modeCombination == 31:
            numberOfSwaths = 5
        else:
            numberOfSwaths = 7
        overlapSubswaths = []
        for i in range(numberOfSwaths):
            overlapRatio = check_overlap(ledFilesMaster[0], firstFrameImagesMaster[i], ledFilesSlave[0], firstFrameImagesSlave[0])
            if overlapRatio > 1.0 / 4.0:
                overlapSubswaths.append(i+1)
        if overlapSubswaths == []:
            raise Exception('There is no overlap area between the ScanSAR-stripmap pair')
        self.startingSwath = int(overlapSubswaths[0])
        self.endingSwath = int(overlapSubswaths[-1])

    #save the valid frames and swaths for future processing
    self._insar.masterFrames = self.masterFrames
    self._insar.slaveFrames = self.slaveFrames
    self._insar.startingSwath = self.startingSwath
    self._insar.endingSwath = self.endingSwath


    ##################################################
    #1. create directories and read data
    ##################################################
    self.master.configure()
    self.slave.configure()
    self.master.track.configure()
    self.slave.track.configure()
    for i, (masterFrame, slaveFrame) in enumerate(zip(self._insar.masterFrames, self._insar.slaveFrames)):
        #frame number starts with 1
        frameDir = 'f{}_{}'.format(i+1, masterFrame)
        if not os.path.exists(frameDir):
            os.makedirs(frameDir)
        os.chdir(frameDir)

        #attach a frame to master and slave
        frameObjMaster = MultiMode.createFrame()
        frameObjSlave = MultiMode.createFrame()
        frameObjMaster.configure()
        frameObjSlave.configure()
        self.master.track.frames.append(frameObjMaster)
        self.slave.track.frames.append(frameObjSlave)

        #swath number starts with 1
        for j in range(self._insar.startingSwath, self._insar.endingSwath+1):
            print('processing frame {} swath {}'.format(masterFrame, j))

            swathDir = 's{}'.format(j)
            if not os.path.exists(swathDir):
                os.makedirs(swathDir)
            os.chdir(swathDir)

            #attach a swath to master and slave
            swathObjMaster = MultiMode.createSwath()
            swathObjSlave = MultiMode.createSwath()
            swathObjMaster.configure()
            swathObjSlave.configure()
            self.master.track.frames[-1].swaths.append(swathObjMaster)
            self.slave.track.frames[-1].swaths.append(swathObjSlave)

            #setup master
            self.master.leaderFile = sorted(glob.glob(os.path.join(self.masterDir, 'LED-ALOS2*{}-*-*'.format(masterFrame))))[0]
            if masterMode in scansarModes:
                self.master.imageFile = sorted(glob.glob(os.path.join(self.masterDir, 'IMG-{}-ALOS2*{}-*-*-F{}'.format(self.masterPolarization.upper(), masterFrame, j))))[0]
            else:
                self.master.imageFile = sorted(glob.glob(os.path.join(self.masterDir, 'IMG-{}-ALOS2*{}-*-*'.format(self.masterPolarization.upper(), masterFrame))))[0]
            self.master.outputFile = self._insar.masterSlc
            self.master.useVirtualFile = self.useVirtualFile
            #read master
            (imageFDR, imageData)=self.master.readImage()
            (leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord)=self.master.readLeader()
            self.master.setSwath(leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord, imageFDR, imageData)
            self.master.setFrame(leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord, imageFDR, imageData)
            self.master.setTrack(leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord, imageFDR, imageData)

            #setup slave
            self.slave.leaderFile = sorted(glob.glob(os.path.join(self.slaveDir, 'LED-ALOS2*{}-*-*'.format(slaveFrame))))[0]
            if slaveMode in scansarModes:
                self.slave.imageFile = sorted(glob.glob(os.path.join(self.slaveDir, 'IMG-{}-ALOS2*{}-*-*-F{}'.format(self.slavePolarization.upper(), slaveFrame, j))))[0]
            else:
                self.slave.imageFile = sorted(glob.glob(os.path.join(self.slaveDir, 'IMG-{}-ALOS2*{}-*-*'.format(self.slavePolarization.upper(), slaveFrame))))[0]
            self.slave.outputFile = self._insar.slaveSlc
            self.slave.useVirtualFile = self.useVirtualFile
            #read slave
            (imageFDR, imageData)=self.slave.readImage()
            (leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord)=self.slave.readLeader()
            self.slave.setSwath(leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord, imageFDR, imageData)
            self.slave.setFrame(leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord, imageFDR, imageData)
            self.slave.setTrack(leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord, imageFDR, imageData)

            os.chdir('../')
        self._insar.saveProduct(self.master.track.frames[-1], self._insar.masterFrameParameter)
        self._insar.saveProduct(self.slave.track.frames[-1], self._insar.slaveFrameParameter)
        os.chdir('../')
    self._insar.saveProduct(self.master.track, self._insar.masterTrackParameter)
    self._insar.saveProduct(self.slave.track, self._insar.slaveTrackParameter)


    ##################################################
    #2. compute burst synchronization
    ##################################################
    #burst synchronization may slowly change along a track as a result of the changing relative speed of the two flights
    #in one frame, real unsynchronized time is the same for all swaths
    unsynTime = 0
    #real synchronized time/percentage depends on the swath burst length (synTime = burstlength - abs(unsynTime))
    #synTime = 0
    synPercentage = 0

    numberOfFrames = len(self._insar.masterFrames)
    numberOfSwaths = self._insar.endingSwath - self._insar.startingSwath + 1
    
    for i, frameNumber in enumerate(self._insar.masterFrames):
        for j, swathNumber in enumerate(range(self._insar.startingSwath, self._insar.endingSwath + 1)):
            masterSwath = self.master.track.frames[i].swaths[j]
            slaveSwath = self.slave.track.frames[i].swaths[j]
            #using Piyush's code for computing range and azimuth offsets
            midRange = masterSwath.startingRange + masterSwath.rangePixelSize * masterSwath.numberOfSamples * 0.5
            midSensingStart = masterSwath.sensingStart + datetime.timedelta(seconds = masterSwath.numberOfLines * 0.5 / masterSwath.prf)
            llh = self.master.track.orbit.rdr2geo(midSensingStart, midRange)
            slvaz, slvrng = self.slave.track.orbit.geo2rdr(llh)
            ###Translate to offsets
            #note that slave range pixel size and prf might be different from master, here we assume there is a virtual slave with same
            #range pixel size and prf
            rgoff = ((slvrng - slaveSwath.startingRange) / masterSwath.rangePixelSize) - masterSwath.numberOfSamples * 0.5
            azoff = ((slvaz - slaveSwath.sensingStart).total_seconds() * masterSwath.prf) - masterSwath.numberOfLines * 0.5

            #compute burst synchronization
            #burst parameters for ScanSAR wide mode not estimed yet
            if self._insar.modeCombination == 21:
                scburstStartLine = (masterSwath.burstStartTime - masterSwath.sensingStart).total_seconds() * masterSwath.prf + azoff
                #slave burst start times corresponding to master burst start times (100% synchronization)
                scburstStartLines = np.arange(scburstStartLine - 100000*masterSwath.burstCycleLength, \
                                              scburstStartLine + 100000*masterSwath.burstCycleLength, \
                                              masterSwath.burstCycleLength)
                dscburstStartLines = -((slaveSwath.burstStartTime - slaveSwath.sensingStart).total_seconds() * slaveSwath.prf - scburstStartLines)
                #find the difference with minimum absolute value
                unsynLines = dscburstStartLines[np.argmin(np.absolute(dscburstStartLines))]
                if np.absolute(unsynLines) >= slaveSwath.burstLength:
                    synLines = 0
                    if unsynLines > 0:
                        unsynLines = slaveSwath.burstLength
                    else:
                        unsynLines = -slaveSwath.burstLength
                else:
                    synLines = slaveSwath.burstLength - np.absolute(unsynLines)

                unsynTime += unsynLines / masterSwath.prf
                synPercentage += synLines / masterSwath.burstLength * 100.0

                catalog.addItem('burst synchronization of frame {} swath {}'.format(frameNumber, swathNumber), '%.1f%%'%(synLines / masterSwath.burstLength * 100.0), 'runPreprocessor')

            ############################################################################################
            #illustration of the sign of the number of unsynchronized lines (unsynLines)     
            #The convention is the same as ampcor offset, that is,
            #              slaveLineNumber = masterLineNumber + unsynLines
            #
            # |-----------------------|     ------------
            # |                       |        ^
            # |                       |        |
            # |                       |        |   unsynLines < 0
            # |                       |        |
            # |                       |       \ /
            # |                       |    |-----------------------|
            # |                       |    |                       |
            # |                       |    |                       |
            # |-----------------------|    |                       |
            #        Master Burst          |                       |
            #                              |                       |
            #                              |                       |
            #                              |                       |
            #                              |                       |
            #                              |-----------------------|
            #                                     Slave Burst
            #
            #
            ############################################################################################
 
            ##burst parameters for ScanSAR wide mode not estimed yet
            elif self._insar.modeCombination == 31:
                #scansar is master
                scburstStartLine = (masterSwath.burstStartTime - masterSwath.sensingStart).total_seconds() * masterSwath.prf + azoff
                #slave burst start times corresponding to master burst start times (100% synchronization)
                for k in range(-100000, 100000):
                    saz_burstx = scburstStartLine + masterSwath.burstCycleLength * k
                    st_burstx = slaveSwath.sensingStart + datetime.timedelta(seconds=saz_burstx / masterSwath.prf)
                    if saz_burstx >= 0.0 and saz_burstx <= slaveSwath.numberOfLines -1:
                        slaveSwath.burstStartTime = st_burstx
                        slaveSwath.burstLength = masterSwath.burstLength
                        slaveSwath.burstCycleLength = masterSwath.burstCycleLength
                        slaveSwath.swathNumber = masterSwath.swathNumber
                        break
                #unsynLines = 0
                #synLines = masterSwath.burstLength
                #unsynTime += unsynLines / masterSwath.prf
                #synPercentage += synLines / masterSwath.burstLength * 100.0
                catalog.addItem('burst synchronization of frame {} swath {}'.format(frameNumber, swathNumber), '%.1f%%'%(100.0), 'runPreprocessor')
            else:
                pass

        #overwrite original frame parameter file
        if self._insar.modeCombination == 31:
            frameDir = 'f{}_{}'.format(i+1, frameNumber)
            self._insar.saveProduct(self.slave.track.frames[i], os.path.join(frameDir, self._insar.slaveFrameParameter))

    #getting average
    if self._insar.modeCombination == 21:
        unsynTime /= numberOfFrames*numberOfSwaths
        synPercentage /= numberOfFrames*numberOfSwaths
    elif self._insar.modeCombination == 31:
        unsynTime = 0.
        synPercentage = 100.
    else:
        pass

    #record results
    if (self._insar.modeCombination == 21) or (self._insar.modeCombination == 31):
        self._insar.burstUnsynchronizedTime = unsynTime
        self._insar.burstSynchronization = synPercentage
        catalog.addItem('burst synchronization averaged', '%.1f%%'%(synPercentage), 'runPreprocessor')


    ##################################################
    #3. compute baseline
    ##################################################
    #only compute baseline at four corners and center of the master track
    bboxRdr = getBboxRdr(self.master.track)

    rangeMin = bboxRdr[0]
    rangeMax = bboxRdr[1]
    azimuthTimeMin = bboxRdr[2]
    azimuthTimeMax = bboxRdr[3]

    azimuthTimeMid = azimuthTimeMin+datetime.timedelta(seconds=(azimuthTimeMax-azimuthTimeMin).total_seconds()/2.0)
    rangeMid = (rangeMin + rangeMax) / 2.0

    points = [[azimuthTimeMin, rangeMin],
              [azimuthTimeMin, rangeMax],
              [azimuthTimeMax, rangeMin],
              [azimuthTimeMax, rangeMax],
              [azimuthTimeMid, rangeMid]]

    Bpar = []
    Bperp = []
    #modify Piyush's code for computing baslines
    refElp = Planet(pname='Earth').ellipsoid
    for x in points:
        masterSV = self.master.track.orbit.interpolate(x[0], method='hermite')
        target = self.master.track.orbit.rdr2geo(x[0], x[1])

        slvTime, slvrng = self.slave.track.orbit.geo2rdr(target)
        slaveSV = self.slave.track.orbit.interpolateOrbit(slvTime, method='hermite')

        targxyz = np.array(refElp.LLH(target[0], target[1], target[2]).ecef().tolist())
        mxyz = np.array(masterSV.getPosition())
        mvel = np.array(masterSV.getVelocity())
        sxyz = np.array(slaveSV.getPosition())

        aa = np.linalg.norm(sxyz-mxyz)
        costheta = (x[1]*x[1] + aa*aa - slvrng*slvrng)/(2.*x[1]*aa)

        Bpar.append(aa*costheta)

        perp = aa * np.sqrt(1 - costheta*costheta)
        direction = np.sign(np.dot( np.cross(targxyz-mxyz, sxyz-mxyz), mvel))
        Bperp.append(direction*perp)    

    catalog.addItem('parallel baseline at upperleft of master track', Bpar[0], 'runPreprocessor')
    catalog.addItem('parallel baseline at upperright of master track', Bpar[1], 'runPreprocessor')
    catalog.addItem('parallel baseline at lowerleft of master track', Bpar[2], 'runPreprocessor')
    catalog.addItem('parallel baseline at lowerright of master track', Bpar[3], 'runPreprocessor')
    catalog.addItem('parallel baseline at center of master track', Bpar[4], 'runPreprocessor')

    catalog.addItem('perpendicular baseline at upperleft of master track', Bperp[0], 'runPreprocessor')
    catalog.addItem('perpendicular baseline at upperright of master track', Bperp[1], 'runPreprocessor')
    catalog.addItem('perpendicular baseline at lowerleft of master track', Bperp[2], 'runPreprocessor')
    catalog.addItem('perpendicular baseline at lowerright of master track', Bperp[3], 'runPreprocessor')
    catalog.addItem('perpendicular baseline at center of master track', Bperp[4], 'runPreprocessor')


    ##################################################
    #4. compute bounding box
    ##################################################
    masterBbox = getBboxGeo(self.master.track)
    slaveBbox = getBboxGeo(self.slave.track)

    catalog.addItem('master bounding box', masterBbox, 'runPreprocessor')
    catalog.addItem('slave bounding box', slaveBbox, 'runPreprocessor')


    catalog.printToLog(logger, "runPreprocessor")
    self._insar.procDoc.addAllFromCatalog(catalog)



def check_overlap(ldr_m, img_m, ldr_s, img_s):
    from isceobj.Constants import SPEED_OF_LIGHT

    rangeSamplingRateMaster, widthMaster, nearRangeMaster = read_param_for_checking_overlap(ldr_m, img_m)
    rangeSamplingRateSlave, widthSlave, nearRangeSlave = read_param_for_checking_overlap(ldr_s, img_s)

    farRangeMaster = nearRangeMaster + (widthMaster-1) * 0.5 * SPEED_OF_LIGHT / rangeSamplingRateMaster
    farRangeSlave = nearRangeSlave + (widthSlave-1) * 0.5 * SPEED_OF_LIGHT / rangeSamplingRateSlave

    #This should be good enough, although precise image offsets are not used.
    if farRangeMaster <= nearRangeSlave:
        overlapRatio = 0.0
    elif farRangeSlave <= nearRangeMaster:
        overlapRatio = 0.0
    else:
        #                     0                  1               2               3
        ranges = np.array([nearRangeMaster, farRangeMaster, nearRangeSlave, farRangeSlave])
        rangesIndex = np.argsort(ranges)
        overlapRatio = ranges[rangesIndex[2]]-ranges[rangesIndex[1]] / (farRangeMaster-nearRangeMaster)

    return overlapRatio


def read_param_for_checking_overlap(leader_file, image_file):
    from isceobj.Sensor import xmlPrefix
    import isceobj.Sensor.CEOS as CEOS

    #read from leader file
    fsampConst = { 104: 1.047915957140240E+08,
                   52: 5.239579785701190E+07,
                   34: 3.493053190467460E+07,
                   17: 1.746526595233730E+07 }

    fp = open(leader_file,'rb')
    leaderFDR = CEOS.CEOSDB(xml=os.path.join(xmlPrefix,'alos2_slc/leader_file.xml'),dataFile=fp)
    leaderFDR.parse()
    fp.seek(leaderFDR.getEndOfRecordPosition())
    sceneHeaderRecord = CEOS.CEOSDB(xml=os.path.join(xmlPrefix,'alos2_slc/scene_record.xml'),dataFile=fp)
    sceneHeaderRecord.parse()
    fp.seek(sceneHeaderRecord.getEndOfRecordPosition())

    fsamplookup = int(sceneHeaderRecord.metadata['Range sampling rate in MHz'])
    rangeSamplingRate = fsampConst[fsamplookup]
    fp.close()
    #print('{}'.format(rangeSamplingRate))

    #read from image file
    fp = open(image_file, 'rb')
    imageFDR = CEOS.CEOSDB(xml=os.path.join(xmlPrefix,'alos2_slc/image_file.xml'), dataFile=fp)
    imageFDR.parse()
    fp.seek(imageFDR.getEndOfRecordPosition())
    imageData = CEOS.CEOSDB(xml=os.path.join(xmlPrefix,'alos2_slc/image_record.xml'), dataFile=fp)
    imageData.parseFast()

    width = imageFDR.metadata['Number of pixels per line per SAR channel']
    near_range = imageData.metadata['Slant range to 1st data sample']
    fp.close()
    #print('{}'.format(width))
    #print('{}'.format(near_range))

    return (rangeSamplingRate, width, near_range)



<filename>components/isceobj/Alos2Proc/runPreprocessor.py
#
# Author: <NAME>
# Copyright 2015-present, NASA-JPL/Caltech
#

import os
import glob
import logging
import datetime
import numpy as np

import isceobj
import isceobj.Sensor.MultiMode as MultiMode
from isceobj.Planet.Planet import Planet
from isceobj.Alos2Proc.Alos2ProcPublic import runCmd
from isceobj.Alos2Proc.Alos2ProcPublic import getBboxRdr
from isceobj.Alos2Proc.Alos2ProcPublic import getBboxGeo

logger = logging.getLogger('isce.alos2insar.runPreprocessor')

def runPreprocessor(self):
    '''Extract images.
    '''
    catalog = isceobj.Catalog.createCatalog(self._insar.procDoc.name)


    #find files
    #actually no need to use absolute path any longer, since we are able to find file from vrt now. 27-JAN-2020, CRL.
    #denseoffset may still need absolute path when making links
    self.masterDir = os.path.abspath(self.masterDir)
    self.slaveDir = os.path.abspath(self.slaveDir)

    ledFilesMaster = sorted(glob.glob(os.path.join(self.masterDir, 'LED-ALOS2*-*-*')))
    imgFilesMaster = sorted(glob.glob(os.path.join(self.masterDir, 'IMG-{}-ALOS2*-*-*'.format(self.masterPolarization.upper()))))

    ledFilesSlave = sorted(glob.glob(os.path.join(self.slaveDir, 'LED-ALOS2*-*-*')))
    imgFilesSlave = sorted(glob.glob(os.path.join(self.slaveDir, 'IMG-{}-ALOS2*-*-*'.format(self.slavePolarization.upper()))))

    firstFrameMaster = ledFilesMaster[0].split('-')[-3][-4:]
    firstFrameSlave = ledFilesSlave[0].split('-')[-3][-4:]
    firstFrameImagesMaster = sorted(glob.glob(os.path.join(self.masterDir, 'IMG-{}-ALOS2*{}-*-*'.format(self.masterPolarization.upper(), firstFrameMaster))))
    firstFrameImagesSlave = sorted(glob.glob(os.path.join(self.slaveDir, 'IMG-{}-ALOS2*{}-*-*'.format(self.slavePolarization.upper(), firstFrameSlave))))


    #determin operation mode
    masterMode = os.path.basename(ledFilesMaster[0]).split('-')[-1][0:3]
    slaveMode = os.path.basename(ledFilesSlave[0]).split('-')[-1][0:3]
    spotlightModes = ['SBS']
    stripmapModes = ['UBS', 'UBD', 'HBS', 'HBD', 'HBQ', 'FBS', 'FBD', 'FBQ']
    scansarNominalModes = ['WBS', 'WBD', 'WWS', 'WWD']
    scansarWideModes = ['VBS', 'VBD']
    scansarModes = ['WBS', 'WBD', 'WWS', 'WWD', 'VBS', 'VBD']

    #usable combinations
    if (masterMode in spotlightModes) and (slaveMode in spotlightModes):
        self._insar.modeCombination = 0
    elif (masterMode in stripmapModes) and (slaveMode in stripmapModes):
        self._insar.modeCombination = 1
    elif (masterMode in scansarNominalModes) and (slaveMode in scansarNominalModes):
        self._insar.modeCombination = 21
    elif (masterMode in scansarWideModes) and (slaveMode in scansarWideModes):
        self._insar.modeCombination = 22
    elif (masterMode in scansarNominalModes) and (slaveMode in stripmapModes):
        self._insar.modeCombination = 31
    elif (masterMode in scansarWideModes) and (slaveMode in stripmapModes):
        self._insar.modeCombination = 32
    else:
        print('\n\nthis mode combination is not possible')
        print('note that for ScanSAR-stripmap, ScanSAR must be master\n\n')
        raise Exception('mode combination not supported')

# pixel size from real data processing. azimuth pixel size may change a bit as
# the antenna points to a different swath and therefore uses a different PRF.

#   MODE  RANGE PIXEL SIZE (LOOKS)       AZIMUTH PIXEL SIZE (LOOKS)
# -------------------------------------------------------------------
#   SPT    [SBS]
#          1.4304222392897463 (2)         0.9351804642158579 (4)
#   SM1    [UBS,UBD]
#          1.4304222392897463 (2)         1.8291988125114438 (2)
#   SM2    [HBS,HBD,HBQ]
#          2.8608444785794984 (2)         3.0672373839847196 (2)
#   SM3    [FBS,FBD,FBQ]
#          4.291266717869248  (2)         3.2462615913656667 (4)

#   WD1    [WBS,WBD] [WWS,WWD]
#          8.582533435738496  (1)         2.6053935830031887 (14)
#          8.582533435738496  (1)         2.092362043327227  (14)
#          8.582533435738496  (1)         2.8817632034495717 (14)
#          8.582533435738496  (1)         3.054362492601842  (14)
#          8.582533435738496  (1)         2.4582084463356977 (14)

#   WD2    [VBS,VBD]
#          8.582533435738496  (1)         2.9215796012950728 (14)
#          8.582533435738496  (1)         3.088859074497863  (14)
#          8.582533435738496  (1)         2.8792293071133073 (14)
#          8.582533435738496  (1)         3.0592146044234854 (14)
#          8.582533435738496  (1)         2.8818767752199137 (14)
#          8.582533435738496  (1)         3.047038521027477  (14)
#          8.582533435738496  (1)         2.898816222039108  (14)

    #determine default number of looks:
    self._insar.numberRangeLooks1 = self.numberRangeLooks1
    self._insar.numberAzimuthLooks1 = self.numberAzimuthLooks1
    self._insar.numberRangeLooks2 = self.numberRangeLooks2
    self._insar.numberAzimuthLooks2 = self.numberAzimuthLooks2
    #the following two will be automatically determined by runRdrDemOffset.py
    self._insar.numberRangeLooksSim = self.numberRangeLooksSim
    self._insar.numberAzimuthLooksSim = self.numberAzimuthLooksSim
    self._insar.numberRangeLooksIon = self.numberRangeLooksIon
    self._insar.numberAzimuthLooksIon = self.numberAzimuthLooksIon

    if self._insar.numberRangeLooks1 == None:
        if masterMode in ['SBS']:
            self._insar.numberRangeLooks1 = 2
        elif masterMode in ['UBS', 'UBD']:
            self._insar.numberRangeLooks1 = 2
        elif masterMode in ['HBS', 'HBD', 'HBQ']:
            self._insar.numberRangeLooks1 = 2
        elif masterMode in ['FBS', 'FBD', 'FBQ']:
            self._insar.numberRangeLooks1 = 2
        elif masterMode in ['WBS', 'WBD']:
            self._insar.numberRangeLooks1 = 1
        elif masterMode in ['WWS', 'WWD']:
            self._insar.numberRangeLooks1 = 2
        elif masterMode in ['VBS', 'VBD']:
            self._insar.numberRangeLooks1 = 1
        else:
            raise Exception('unknow acquisition mode')

    if self._insar.numberAzimuthLooks1 == None:
        if masterMode in ['SBS']:
            self._insar.numberAzimuthLooks1 = 4
        elif masterMode in ['UBS', 'UBD']:
            self._insar.numberAzimuthLooks1 = 2
        elif masterMode in ['HBS', 'HBD', 'HBQ']:
            self._insar.numberAzimuthLooks1 = 2
        elif masterMode in ['FBS', 'FBD', 'FBQ']:
            self._insar.numberAzimuthLooks1 = 4
        elif masterMode in ['WBS', 'WBD']:
            self._insar.numberAzimuthLooks1 = 14
        elif masterMode in ['WWS', 'WWD']:
            self._insar.numberAzimuthLooks1 = 14
        elif masterMode in ['VBS', 'VBD']:
            self._insar.numberAzimuthLooks1 = 14
        else:
            raise Exception('unknow acquisition mode')

    if self._insar.numberRangeLooks2 == None:
        if masterMode in spotlightModes:
            self._insar.numberRangeLooks2 = 4
        elif masterMode in stripmapModes:
            self._insar.numberRangeLooks2 = 4
        elif masterMode in scansarModes:
            self._insar.numberRangeLooks2 = 5
        else:
            raise Exception('unknow acquisition mode')

    if self._insar.numberAzimuthLooks2 == None:
        if masterMode in spotlightModes:
            self._insar.numberAzimuthLooks2 = 4
        elif masterMode in stripmapModes:
            self._insar.numberAzimuthLooks2 = 4
        elif masterMode in scansarModes:
            self._insar.numberAzimuthLooks2 = 2
        else:
            raise Exception('unknow acquisition mode')

    if self._insar.numberRangeLooksIon == None:
        if masterMode in spotlightModes:
            self._insar.numberRangeLooksIon = 16
        elif masterMode in stripmapModes:
            self._insar.numberRangeLooksIon = 16
        elif masterMode in scansarModes:
            self._insar.numberRangeLooksIon = 40
        else:
            raise Exception('unknow acquisition mode')

    if self._insar.numberAzimuthLooksIon == None:
        if masterMode in spotlightModes:
            self._insar.numberAzimuthLooksIon = 16
        elif masterMode in stripmapModes:
            self._insar.numberAzimuthLooksIon = 16
        elif masterMode in scansarModes:
            self._insar.numberAzimuthLooksIon = 16
        else:
            raise Exception('unknow acquisition mode')


    #define processing file names
    self._insar.masterDate = os.path.basename(ledFilesMaster[0]).split('-')[2]
    self._insar.slaveDate = os.path.basename(ledFilesSlave[0]).split('-')[2]
    self._insar.setFilename(masterDate=self._insar.masterDate, slaveDate=self._insar.slaveDate, nrlks1=self._insar.numberRangeLooks1, nalks1=self._insar.numberAzimuthLooks1, nrlks2=self._insar.numberRangeLooks2, nalks2=self._insar.numberAzimuthLooks2)


    #find frame numbers
    if (self._insar.modeCombination == 31) or (self._insar.modeCombination == 32):
        if (self.masterFrames == None) or (self.slaveFrames == None):
            raise Exception('for ScanSAR-stripmap inteferometry, you must set master and slave frame numbers')
    #if not set, find frames automatically
    if self.masterFrames == None:
        self.masterFrames = []
        for led in ledFilesMaster:
            frameNumber = os.path.basename(led).split('-')[1][-4:]
            if frameNumber not in self.masterFrames:
                self.masterFrames.append(frameNumber)
    if self.slaveFrames == None:
        self.slaveFrames = []
        for led in ledFilesSlave:
            frameNumber = os.path.basename(led).split('-')[1][-4:]
            if frameNumber not in self.slaveFrames:
                self.slaveFrames.append(frameNumber)
    #sort frames
    self.masterFrames = sorted(self.masterFrames)
    self.slaveFrames = sorted(self.slaveFrames)
    #check number of frames
    if len(self.masterFrames) != len(self.slaveFrames):
        raise Exception('number of frames in master dir is not equal to number of frames \
            in slave dir. please set frame number manually')


    #find swath numbers (if not ScanSAR-ScanSAR, compute valid swaths)
    if (self._insar.modeCombination == 0) or (self._insar.modeCombination == 1):
        self.startingSwath = 1
        self.endingSwath = 1

    if self._insar.modeCombination == 21:
        if self.startingSwath == None:
            self.startingSwath = 1
        if self.endingSwath == None:
            self.endingSwath = 5

    if self._insar.modeCombination == 22:
        if self.startingSwath == None:
            self.startingSwath = 1
        if self.endingSwath == None:
            self.endingSwath = 7

    #determine starting and ending swaths for ScanSAR-stripmap, user's settings are overwritten
    #use first frame to check overlap
    if (self._insar.modeCombination == 31) or (self._insar.modeCombination == 32):
        if self._insar.modeCombination == 31:
            numberOfSwaths = 5
        else:
            numberOfSwaths = 7
        overlapSubswaths = []
        for i in range(numberOfSwaths):
            overlapRatio = check_overlap(ledFilesMaster[0], firstFrameImagesMaster[i], ledFilesSlave[0], firstFrameImagesSlave[0])
            if overlapRatio > 1.0 / 4.0:
                overlapSubswaths.append(i+1)
        if overlapSubswaths == []:
            raise Exception('There is no overlap area between the ScanSAR-stripmap pair')
        self.startingSwath = int(overlapSubswaths[0])
        self.endingSwath = int(overlapSubswaths[-1])

    #save the valid frames and swaths for future processing
    self._insar.masterFrames = self.masterFrames
    self._insar.slaveFrames = self.slaveFrames
    self._insar.startingSwath = self.startingSwath
    self._insar.endingSwath = self.endingSwath


    ##################################################
    #1. create directories and read data
    ##################################################
    self.master.configure()
    self.slave.configure()
    self.master.track.configure()
    self.slave.track.configure()
    for i, (masterFrame, slaveFrame) in enumerate(zip(self._insar.masterFrames, self._insar.slaveFrames)):
        #frame number starts with 1
        frameDir = 'f{}_{}'.format(i+1, masterFrame)
        if not os.path.exists(frameDir):
            os.makedirs(frameDir)
        os.chdir(frameDir)

        #attach a frame to master and slave
        frameObjMaster = MultiMode.createFrame()
        frameObjSlave = MultiMode.createFrame()
        frameObjMaster.configure()
        frameObjSlave.configure()
        self.master.track.frames.append(frameObjMaster)
        self.slave.track.frames.append(frameObjSlave)

        #swath number starts with 1
        for j in range(self._insar.startingSwath, self._insar.endingSwath+1):
            print('processing frame {} swath {}'.format(masterFrame, j))

            swathDir = 's{}'.format(j)
            if not os.path.exists(swathDir):
                os.makedirs(swathDir)
            os.chdir(swathDir)

            #attach a swath to master and slave
            swathObjMaster = MultiMode.createSwath()
            swathObjSlave = MultiMode.createSwath()
            swathObjMaster.configure()
            swathObjSlave.configure()
            self.master.track.frames[-1].swaths.append(swathObjMaster)
            self.slave.track.frames[-1].swaths.append(swathObjSlave)

            #setup master
            self.master.leaderFile = sorted(glob.glob(os.path.join(self.masterDir, 'LED-ALOS2*{}-*-*'.format(masterFrame))))[0]
            if masterMode in scansarModes:
                self.master.imageFile = sorted(glob.glob(os.path.join(self.masterDir, 'IMG-{}-ALOS2*{}-*-*-F{}'.format(self.masterPolarization.upper(), masterFrame, j))))[0]
            else:
                self.master.imageFile = sorted(glob.glob(os.path.join(self.masterDir, 'IMG-{}-ALOS2*{}-*-*'.format(self.masterPolarization.upper(), masterFrame))))[0]
            self.master.outputFile = self._insar.masterSlc
            self.master.useVirtualFile = self.useVirtualFile
            #read master
            (imageFDR, imageData)=self.master.readImage()
            (leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord)=self.master.readLeader()
            self.master.setSwath(leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord, imageFDR, imageData)
            self.master.setFrame(leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord, imageFDR, imageData)
            self.master.setTrack(leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord, imageFDR, imageData)

            #setup slave
            self.slave.leaderFile = sorted(glob.glob(os.path.join(self.slaveDir, 'LED-ALOS2*{}-*-*'.format(slaveFrame))))[0]
            if slaveMode in scansarModes:
                self.slave.imageFile = sorted(glob.glob(os.path.join(self.slaveDir, 'IMG-{}-ALOS2*{}-*-*-F{}'.format(self.slavePolarization.upper(), slaveFrame, j))))[0]
            else:
                self.slave.imageFile = sorted(glob.glob(os.path.join(self.slaveDir, 'IMG-{}-ALOS2*{}-*-*'.format(self.slavePolarization.upper(), slaveFrame))))[0]
            self.slave.outputFile = self._insar.slaveSlc
            self.slave.useVirtualFile = self.useVirtualFile
            #read slave
            (imageFDR, imageData)=self.slave.readImage()
            (leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord)=self.slave.readLeader()
            self.slave.setSwath(leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord, imageFDR, imageData)
            self.slave.setFrame(leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord, imageFDR, imageData)
            self.slave.setTrack(leaderFDR, sceneHeaderRecord, platformPositionRecord, facilityRecord, imageFDR, imageData)

            os.chdir('../')
        self._insar.saveProduct(self.master.track.frames[-1], self._insar.masterFrameParameter)
        self._insar.saveProduct(self.slave.track.frames[-1], self._insar.slaveFrameParameter)
        os.chdir('../')
    self._insar.saveProduct(self.master.track, self._insar.masterTrackParameter)
    self._insar.saveProduct(self.slave.track, self._insar.slaveTrackParameter)


    ##################################################
    #2. compute burst synchronization
    ##################################################
    #burst synchronization may slowly change along a track as a result of the changing relative speed of the two flights
    #in one frame, real unsynchronized time is the same for all swaths
    unsynTime = 0
    #real synchronized time/percentage depends on the swath burst length (synTime = burstlength - abs(unsynTime))
    #synTime = 0
    synPercentage = 0

    numberOfFrames = len(self._insar.masterFrames)
    numberOfSwaths = self._insar.endingSwath - self._insar.startingSwath + 1
    
    for i, frameNumber in enumerate(self._insar.masterFrames):
        for j, swathNumber in enumerate(range(self._insar.startingSwath, self._insar.endingSwath + 1)):
            masterSwath = self.master.track.frames[i].swaths[j]
            slaveSwath = self.slave.track.frames[i].swaths[j]
            #using Piyush's code for computing range and azimuth offsets
            midRange = masterSwath.startingRange + masterSwath.rangePixelSize * masterSwath.numberOfSamples * 0.5
            midSensingStart = masterSwath.sensingStart + datetime.timedelta(seconds = masterSwath.numberOfLines * 0.5 / masterSwath.prf)
            llh = self.master.track.orbit.rdr2geo(midSensingStart, midRange)
            slvaz, slvrng = self.slave.track.orbit.geo2rdr(llh)
            ###Translate to offsets
            #note that slave range pixel size and prf might be different from master, here we assume there is a virtual slave with same
            #range pixel size and prf
            rgoff = ((slvrng - slaveSwath.startingRange) / masterSwath.rangePixelSize) - masterSwath.numberOfSamples * 0.5
            azoff = ((slvaz - slaveSwath.sensingStart).total_seconds() * masterSwath.prf) - masterSwath.numberOfLines * 0.5

            #compute burst synchronization
            #burst parameters for ScanSAR wide mode not estimed yet
            if self._insar.modeCombination == 21:
                scburstStartLine = (masterSwath.burstStartTime - masterSwath.sensingStart).total_seconds() * masterSwath.prf + azoff
                #slave burst start times corresponding to master burst start times (100% synchronization)
                scburstStartLines = np.arange(scburstStartLine - 100000*masterSwath.burstCycleLength, \
                                              scburstStartLine + 100000*masterSwath.burstCycleLength, \
                                              masterSwath.burstCycleLength)
                dscburstStartLines = -((slaveSwath.burstStartTime - slaveSwath.sensingStart).total_seconds() * slaveSwath.prf - scburstStartLines)
                #find the difference with minimum absolute value
                unsynLines = dscburstStartLines[np.argmin(np.absolute(dscburstStartLines))]
                if np.absolute(unsynLines) >= slaveSwath.burstLength:
                    synLines = 0
                    if unsynLines > 0:
                        unsynLines = slaveSwath.burstLength
                    else:
                        unsynLines = -slaveSwath.burstLength
                else:
                    synLines = slaveSwath.burstLength - np.absolute(unsynLines)

                unsynTime += unsynLines / masterSwath.prf
                synPercentage += synLines / masterSwath.burstLength * 100.0

                catalog.addItem('burst synchronization of frame {} swath {}'.format(frameNumber, swathNumber), '%.1f%%'%(synLines / masterSwath.burstLength * 100.0), 'runPreprocessor')

            ############################################################################################
            #illustration of the sign of the number of unsynchronized lines (unsynLines)     
            #The convention is the same as ampcor offset, that is,
            #              slaveLineNumber = masterLineNumber + unsynLines
            #
            # |-----------------------|     ------------
            # |                       |        ^
            # |                       |        |
            # |                       |        |   unsynLines < 0
            # |                       |        |
            # |                       |       \ /
            # |                       |    |-----------------------|
            # |                       |    |                       |
            # |                       |    |                       |
            # |-----------------------|    |                       |
            #        Master Burst          |                       |
            #                              |                       |
            #                              |                       |
            #                              |                       |
            #                              |                       |
            #                              |-----------------------|
            #                                     Slave Burst
            #
            #
            ############################################################################################
 
            ##burst parameters for ScanSAR wide mode not estimed yet
            elif self._insar.modeCombination == 31:
                #scansar is master
                scburstStartLine = (masterSwath.burstStartTime - masterSwath.sensingStart).total_seconds() * masterSwath.prf + azoff
                #slave burst start times corresponding to master burst start times (100% synchronization)
                for k in range(-100000, 100000):
                    saz_burstx = scburstStartLine + masterSwath.burstCycleLength * k
                    st_burstx = slaveSwath.sensingStart + datetime.timedelta(seconds=saz_burstx / masterSwath.prf)
                    if saz_burstx >= 0.0 and saz_burstx <= slaveSwath.numberOfLines -1:
                        slaveSwath.burstStartTime = st_burstx
                        slaveSwath.burstLength = masterSwath.burstLength
                        slaveSwath.burstCycleLength = masterSwath.burstCycleLength
                        slaveSwath.swathNumber = masterSwath.swathNumber
                        break
                #unsynLines = 0
                #synLines = masterSwath.burstLength
                #unsynTime += unsynLines / masterSwath.prf
                #synPercentage += synLines / masterSwath.burstLength * 100.0
                catalog.addItem('burst synchronization of frame {} swath {}'.format(frameNumber, swathNumber), '%.1f%%'%(100.0), 'runPreprocessor')
            else:
                pass

        #overwrite original frame parameter file
        if self._insar.modeCombination == 31:
            frameDir = 'f{}_{}'.format(i+1, frameNumber)
            self._insar.saveProduct(self.slave.track.frames[i], os.path.join(frameDir, self._insar.slaveFrameParameter))

    #getting average
    if self._insar.modeCombination == 21:
        unsynTime /= numberOfFrames*numberOfSwaths
        synPercentage /= numberOfFrames*numberOfSwaths
    elif self._insar.modeCombination == 31:
        unsynTime = 0.
        synPercentage = 100.
    else:
        pass

    #record results
    if (self._insar.modeCombination == 21) or (self._insar.modeCombination == 31):
        self._insar.burstUnsynchronizedTime = unsynTime
        self._insar.burstSynchronization = synPercentage
        catalog.addItem('burst synchronization averaged', '%.1f%%'%(synPercentage), 'runPreprocessor')


    ##################################################
    #3. compute baseline
    ##################################################
    #only compute baseline at four corners and center of the master track
    bboxRdr = getBboxRdr(self.master.track)

    rangeMin = bboxRdr[0]
    rangeMax = bboxRdr[1]
    azimuthTimeMin = bboxRdr[2]
    azimuthTimeMax = bboxRdr[3]

    azimuthTimeMid = azimuthTimeMin+datetime.timedelta(seconds=(azimuthTimeMax-azimuthTimeMin).total_seconds()/2.0)
    rangeMid = (rangeMin + rangeMax) / 2.0

    points = [[azimuthTimeMin, rangeMin],
              [azimuthTimeMin, rangeMax],
              [azimuthTimeMax, rangeMin],
              [azimuthTimeMax, rangeMax],
              [azimuthTimeMid, rangeMid]]

    Bpar = []
    Bperp = []
    #modify Piyush's code for computing baslines
    refElp = Planet(pname='Earth').ellipsoid
    for x in points:
        masterSV = self.master.track.orbit.interpolate(x[0], method='hermite')
        target = self.master.track.orbit.rdr2geo(x[0], x[1])

        slvTime, slvrng = self.slave.track.orbit.geo2rdr(target)
        slaveSV = self.slave.track.orbit.interpolateOrbit(slvTime, method='hermite')

        targxyz = np.array(refElp.LLH(target[0], target[1], target[2]).ecef().tolist())
        mxyz = np.array(masterSV.getPosition())
        mvel = np.array(masterSV.getVelocity())
        sxyz = np.array(slaveSV.getPosition())

        aa = np.linalg.norm(sxyz-mxyz)
        costheta = (x[1]*x[1] + aa*aa - slvrng*slvrng)/(2.*x[1]*aa)

        Bpar.append(aa*costheta)

        perp = aa * np.sqrt(1 - costheta*costheta)
        direction = np.sign(np.dot( np.cross(targxyz-mxyz, sxyz-mxyz), mvel))
        Bperp.append(direction*perp)    

    catalog.addItem('parallel baseline at upperleft of master track', Bpar[0], 'runPreprocessor')
    catalog.addItem('parallel baseline at upperright of master track', Bpar[1], 'runPreprocessor')
    catalog.addItem('parallel baseline at lowerleft of master track', Bpar[2], 'runPreprocessor')
    catalog.addItem('parallel baseline at lowerright of master track', Bpar[3], 'runPreprocessor')
    catalog.addItem('parallel baseline at center of master track', Bpar[4], 'runPreprocessor')

    catalog.addItem('perpendicular baseline at upperleft of master track', Bperp[0], 'runPreprocessor')
    catalog.addItem('perpendicular baseline at upperright of master track', Bperp[1], 'runPreprocessor')
    catalog.addItem('perpendicular baseline at lowerleft of master track', Bperp[2], 'runPreprocessor')
    catalog.addItem('perpendicular baseline at lowerright of master track', Bperp[3], 'runPreprocessor')
    catalog.addItem('perpendicular baseline at center of master track', Bperp[4], 'runPreprocessor')


    ##################################################
    #4. compute bounding box
    ##################################################
    masterBbox = getBboxGeo(self.master.track)
    slaveBbox = getBboxGeo(self.slave.track)

    catalog.addItem('master bounding box', masterBbox, 'runPreprocessor')
    catalog.addItem('slave bounding box', slaveBbox, 'runPreprocessor')


    catalog.printToLog(logger, "runPreprocessor")
    self._insar.procDoc.addAllFromCatalog(catalog)



def check_overlap(ldr_m, img_m, ldr_s, img_s):
    from isceobj.Constants import SPEED_OF_LIGHT

    rangeSamplingRateMaster, widthMaster, nearRangeMaster = read_param_for_checking_overlap(ldr_m, img_m)
    rangeSamplingRateSlave, widthSlave, nearRangeSlave = read_param_for_checking_overlap(ldr_s, img_s)

    farRangeMaster = nearRangeMaster + (widthMaster-1) * 0.5 * SPEED_OF_LIGHT / rangeSamplingRateMaster
    farRangeSlave = nearRangeSlave + (widthSlave-1) * 0.5 * SPEED_OF_LIGHT / rangeSamplingRateSlave

    #This should be good enough, although precise image offsets are not used.
    if farRangeMaster <= nearRangeSlave:
        overlapRatio = 0.0
    elif farRangeSlave <= nearRangeMaster:
        overlapRatio = 0.0
    else:
        #                     0                  1               2               3
        ranges = np.array([nearRangeMaster, farRangeMaster, nearRangeSlave, farRangeSlave])
        rangesIndex = np.argsort(ranges)
        overlapRatio = ranges[rangesIndex[2]]-ranges[rangesIndex[1]] / (farRangeMaster-nearRangeMaster)

    return overlapRatio


def read_param_for_checking_overlap(leader_file, image_file):
    from isceobj.Sensor import xmlPrefix
    import isceobj.Sensor.CEOS as CEOS

    #read from leader file
    fsampConst = { 104: 1.047915957140240E+08,
                   52: 5.239579785701190E+07,
                   34: 3.493053190467460E+07,
                   17: 1.746526595233730E+07 }

    fp = open(leader_file,'rb')
    leaderFDR = CEOS.CEOSDB(xml=os.path.join(xmlPrefix,'alos2_slc/leader_file.xml'),dataFile=fp)
    leaderFDR.parse()
    fp.seek(leaderFDR.getEndOfRecordPosition())
    sceneHeaderRecord = CEOS.CEOSDB(xml=os.path.join(xmlPrefix,'alos2_slc/scene_record.xml'),dataFile=fp)
    sceneHeaderRecord.parse()
    fp.seek(sceneHeaderRecord.getEndOfRecordPosition())

    fsamplookup = int(sceneHeaderRecord.metadata['Range sampling rate in MHz'])
    rangeSamplingRate = fsampConst[fsamplookup]
    fp.close()
    #print('{}'.format(rangeSamplingRate))

    #read from image file
    fp = open(image_file, 'rb')
    imageFDR = CEOS.CEOSDB(xml=os.path.join(xmlPrefix,'alos2_slc/image_file.xml'), dataFile=fp)
    imageFDR.parse()
    fp.seek(imageFDR.getEndOfRecordPosition())
    imageData = CEOS.CEOSDB(xml=os.path.join(xmlPrefix,'alos2_slc/image_record.xml'), dataFile=fp)
    imageData.parseFast()

    width = imageFDR.metadata['Number of pixels per line per SAR channel']
    near_range = imageData.metadata['Slant range to 1st data sample']
    fp.close()
    #print('{}'.format(width))
    #print('{}'.format(near_range))

    return (rangeSamplingRate, width, near_range)



# # Author: <NAME> # Copyright 2015-present, NASA-JPL/Caltech # Extract images. #find files #actually no need to use absolute path any longer, since we are able to find file from vrt now. 27-JAN-2020, CRL. #denseoffset may still need absolute path when making links #determin operation mode #usable combinations # pixel size from real data processing. azimuth pixel size may change a bit as # the antenna points to a different swath and therefore uses a different PRF. #   MODE  RANGE PIXEL SIZE (LOOKS)       AZIMUTH PIXEL SIZE (LOOKS) # ------------------------------------------------------------------- #   SPT    [SBS] #          1.4304222392897463 (2)         0.9351804642158579 (4) #   SM1    [UBS,UBD] #          1.4304222392897463 (2)         1.8291988125114438 (2) #   SM2    [HBS,HBD,HBQ] #          2.8608444785794984 (2)         3.0672373839847196 (2) #   SM3    [FBS,FBD,FBQ] #          4.291266717869248  (2)         3.2462615913656667 (4) #   WD1    [WBS,WBD] [WWS,WWD] #          8.582533435738496  (1)         2.6053935830031887 (14) #          8.582533435738496  (1)         2.092362043327227  (14) #          8.582533435738496  (1)         2.8817632034495717 (14) #          8.582533435738496  (1)         3.054362492601842  (14) #          8.582533435738496  (1)         2.4582084463356977 (14) #   WD2    [VBS,VBD] #          8.582533435738496  (1)         2.9215796012950728 (14) #          8.582533435738496  (1)         3.088859074497863  (14) #          8.582533435738496  (1)         2.8792293071133073 (14) #          8.582533435738496  (1)         3.0592146044234854 (14) #          8.582533435738496  (1)         2.8818767752199137 (14) #          8.582533435738496  (1)         3.047038521027477  (14) #          8.582533435738496  (1)         2.898816222039108  (14) #determine default number of looks: #the following two will be automatically determined by runRdrDemOffset.py #define processing file names #find frame numbers #if not set, find frames automatically #sort frames #check number of frames #find swath numbers (if not ScanSAR-ScanSAR, compute valid swaths) #determine starting and ending swaths for ScanSAR-stripmap, user's settings are overwritten #use first frame to check overlap #save the valid frames and swaths for future processing ################################################## #1. create directories and read data ################################################## #frame number starts with 1 #attach a frame to master and slave #swath number starts with 1 #attach a swath to master and slave #setup master #read master #setup slave #read slave ################################################## #2. compute burst synchronization ################################################## #burst synchronization may slowly change along a track as a result of the changing relative speed of the two flights #in one frame, real unsynchronized time is the same for all swaths #real synchronized time/percentage depends on the swath burst length (synTime = burstlength - abs(unsynTime)) #synTime = 0 #using Piyush's code for computing range and azimuth offsets ###Translate to offsets #note that slave range pixel size and prf might be different from master, here we assume there is a virtual slave with same #range pixel size and prf #compute burst synchronization #burst parameters for ScanSAR wide mode not estimed yet #slave burst start times corresponding to master burst start times (100% synchronization) #find the difference with minimum absolute value ############################################################################################ #illustration of the sign of the number of unsynchronized lines (unsynLines) #The convention is the same as ampcor offset, that is, #              slaveLineNumber = masterLineNumber + unsynLines # # |-----------------------|     ------------ # |                       |        ^ # |                       |        | # |                       |        |   unsynLines < 0 # |                       |        | # |                       |       \ / # |                       |    |-----------------------| # |                       |    |                       | # |                       |    |                       | # |-----------------------|    |                       | #        Master Burst          |                       | #                              |                       | #                              |                       | #                              |                       | #                              |                       | #                              |-----------------------| #                                     Slave Burst # # ############################################################################################ ##burst parameters for ScanSAR wide mode not estimed yet #scansar is master #slave burst start times corresponding to master burst start times (100% synchronization) #unsynLines = 0 #synLines = masterSwath.burstLength #unsynTime += unsynLines / masterSwath.prf #synPercentage += synLines / masterSwath.burstLength * 100.0 #overwrite original frame parameter file #getting average #record results ################################################## #3. compute baseline ################################################## #only compute baseline at four corners and center of the master track #modify Piyush's code for computing baslines ################################################## #4. compute bounding box ################################################## #This should be good enough, although precise image offsets are not used. #                     0                  1               2               3 #read from leader file #print('{}'.format(rangeSamplingRate)) #read from image file #print('{}'.format(width)) #print('{}'.format(near_range))