From c36caafa24059bb57d644df3fe4511a9e6b97744 Mon Sep 17 00:00:00 2001 From: piyushrpt Date: Sun, 16 Jun 2019 18:59:42 -0700 Subject: [PATCH] Adding ICEYE_SLC Sensor --- components/isceobj/Sensor/ICEYE_SLC.py | 285 +++++++++++++++++++ components/isceobj/Sensor/SConscript | 2 +- components/isceobj/Sensor/__init__.py | 4 +- components/isceobj/StripmapProc/Factories.py | 6 +- 4 files changed, 292 insertions(+), 5 deletions(-) create mode 100755 components/isceobj/Sensor/ICEYE_SLC.py diff --git a/components/isceobj/Sensor/ICEYE_SLC.py b/components/isceobj/Sensor/ICEYE_SLC.py new file mode 100755 index 0000000..47b0146 --- /dev/null +++ b/components/isceobj/Sensor/ICEYE_SLC.py @@ -0,0 +1,285 @@ +#!/usr/bin/env python3 + +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Copyright 2013 California Institute of Technology. ALL RIGHTS RESERVED. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# United States Government Sponsorship acknowledged. This software is subject to +# U.S. export control laws and regulations and has been classified as 'EAR99 NLR' +# (No [Export] License Required except when exporting to an embargoed country, +# end user, or in support of a prohibited end use). By downloading this software, +# the user agrees to comply with all applicable U.S. export laws and regulations. +# The user has the responsibility to obtain export licenses, or other export +# authority as may be required before exporting this software to any 'EAR99' +# embargoed foreign country or citizen of those countries. +# +# Author: Piyush Agram +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + +import datetime +import logging +try: + import h5py +except ImportError: + raise ImportError( + "Python module h5py is required to process ICEYE data" + ) + +import isceobj +from isceobj.Scene.Frame import Frame +from isceobj.Orbit.Orbit import StateVector +from isceobj.Planet.Planet import Planet +from isceobj.Planet.AstronomicalHandbook import Const +from isceobj.Sensor import cosar +from iscesys.DateTimeUtil.DateTimeUtil import DateTimeUtil as DTU +from isceobj.Sensor import tkfunc,createAuxFile +from iscesys.Component.Component import Component + +HDF5 = Component.Parameter( + 'hdf5', + public_name='HDF5', + default=None, + type=str, + mandatory=True, + intent='input', + doc='ICEYE slc hdf5 input file' +) + +APPLY_SLANT_RANGE_PHASE = Component.Parameter( + 'applySlantRangePhase', + public_name='APPLY_SLANT_RANGE_PHASE', + default=False, + type=bool, + mandatory=True, + intent='input', + doc='Recenter spectra by applying range spectra shift' +) + +from .Sensor import Sensor +class ICEYE_SLC(Sensor): + """ + A class representing a Level1Product meta data. + Level1Product(hdf5=h5filename) will parse the hdf5 + file and produce an object with attributes for metadata. + """ + parameter_list = (HDF5, APPLY_SLANT_RANGE_PHASE) + Sensor.parameter_list + logging_name = 'isce.Sensor.ICEYE_SLC' + family = 'iceye_slc' + + def __init__(self,family='',name=''): + super(ICEYE_SLC,self).__init__(family if family else self.__class__.family, name=name) + self.frame = Frame() + self.frame.configure() + # Some extra processing parameters unique to CSK SLC (currently) + self.dopplerRangeTime = [] + self.dopplerAzimuthTime = [] + self.azimuthRefTime = None + self.rangeRefTime = None + self.rangeFirstTime = None + self.rangeLastTime = None + + + self.lookMap = {'RIGHT': -1, + 'LEFT': 1} + return + + def __getstate__(self): + d = dict(self.__dict__) + del d['logger'] + return d + + def __setstate__(self,d): + self.__dict__.update(d) + self.logger = logging.getLogger('isce.Sensor.ICEYE_SLC') + return + + + def getFrame(self): + return self.frame + + def parse(self): + try: + fp = h5py.File(self.hdf5,'r') + except Exception as strerr: + self.logger.error("IOError: %s" % strerr) + return None + + self.populateMetadata(fp) + fp.close() + + def populateMetadata(self, file): + """ + Populate our Metadata objects + """ + + self._populatePlatform(file) + self._populateInstrument(file) + self._populateFrame(file) + self._populateOrbit(file) + self._populateExtras(file) + + + def _populatePlatform(self, file): + platform = self.frame.getInstrument().getPlatform() + + platform.setMission(file['satellite_name'][()]) + platform.setPointingDirection(self.lookMap[file['look_side'][()].upper()]) + platform.setPlanet(Planet(pname="Earth")) + + ####This is an approximation for spotlight mode + ####In spotlight mode, antenna length changes with azimuth position + platform.setAntennaLength(2 * file['azimuth_ground_spacing'][()]) + + assert( file['range_looks'][()] == 1) + assert( file['azimuth_looks'][()] == 1) + + def _populateInstrument(self, file): + instrument = self.frame.getInstrument() + + rangePixelSize = file['slant_range_spacing'][()] + instrument.setRadarWavelength(Const.c / file['carrier_frequency'][()]) + instrument.setPulseRepetitionFrequency(file['processing_prf'][()]) + instrument.setRangePixelSize(rangePixelSize) + instrument.setPulseLength(file['chirp_duration'][()]) + instrument.setChirpSlope(file['chirp_bandwidth'][()]/ file['chirp_duration'][()]) + instrument.setRangeSamplingRate(file['range_sampling_rate'][()]) + + incangle = file['local_incidence_angle'] + instrument.setIncidenceAngle(incangle[incangle.size//2]) + + + def _populateFrame(self, file): + + rft = file['first_pixel_time'][()] + slantRange = rft*Const.c/2.0 + self.frame.setStartingRange(slantRange) + + + sensingStart = datetime.datetime.strptime(file['zerodoppler_start_utc'][()].decode('utf-8'),'%Y-%m-%dT%H:%M:%S.%f') + sensingStop = datetime.datetime.strptime(file['zerodoppler_end_utc'][()].decode('utf-8'),'%Y-%m-%dT%H:%M:%S.%f') + sensingMid = sensingStart + 0.5 * (sensingStop - sensingStart) + + self.frame.setPassDirection(file['orbit_direction'][()]) + self.frame.setOrbitNumber(file['orbit_absolute_number'][()]) + self.frame.setProcessingFacility('ICEYE') + self.frame.setProcessingSoftwareVersion(str(file['processor_version'][()])) + self.frame.setPolarization(file['polarization'][()]) + self.frame.setNumberOfLines(file['number_of_azimuth_samples'][()]) + self.frame.setNumberOfSamples(file['number_of_range_samples'][()]) + self.frame.setSensingStart(sensingStart) + self.frame.setSensingMid(sensingMid) + self.frame.setSensingStop(sensingStop) + + rangePixelSize = self.frame.getInstrument().getRangePixelSize() + farRange = slantRange + (self.frame.getNumberOfSamples()-1)*rangePixelSize + self.frame.setFarRange(farRange) + + def _populateOrbit(self,file): + import numpy as np + orbit = self.frame.getOrbit() + + orbit.setReferenceFrame('ECR') + orbit.setOrbitSource('Header') + t = file['state_vector_time_utc'][:] + position = np.zeros((t.size,3)) + position[:,0] = file['posX'][:] + position[:,1] = file['posY'][:] + position[:,2] = file['posZ'][:] + + velocity = np.zeros((t.size,3)) + velocity[:,0] = file['velX'][:] + velocity[:,1] = file['velY'][:] + velocity[:,2] = file['velZ'][:] + + for ii in range(t.size): + vec = StateVector() + vec.setTime(datetime.datetime.strptime(t[ii][0].decode('utf-8'), '%Y-%m-%dT%H:%M:%S.%f')) + vec.setPosition([position[ii,0],position[ii,1],position[ii,2]]) + vec.setVelocity([velocity[ii,0],velocity[ii,1],velocity[ii,2]]) + orbit.addStateVector(vec) + + + def _populateExtras(self, file): + """ + Populate some of the extra fields unique to processing TSX data. + In the future, other sensors may need this information as well, + and a re-organization may be necessary. + """ + import numpy as np + self.dcpoly = np.mean(file['dc_estimate_coeffs'][:], axis=0) + + def extractImage(self): + import numpy as np + import h5py + + self.parse() + + fid = h5py.File(self.hdf5, 'r') + + si = fid['s_i'] + sq = fid['s_q'] + + nLines = si.shape[0] + spectralShift = 2 * self.frame.getInstrument().getRangePixelSize() / self.frame.getInstrument().getRadarWavelength() + spectralShift -= np.floor(spectralShift) + phsShift = np.exp(-1j * 2 * np.pi * spectralShift * np.arange(si.shape[1])) + with open(self.output, 'wb') as fout: + for ii in range(nLines): + line = (si[ii,:] + 1j*sq[ii,:]) + if self.applySlantRangePhase: + line *= phsShift + line.astype(np.complex64).tofile(fout) + + fid.close() + + slcImage = isceobj.createSlcImage() + slcImage.setFilename(self.output) + slcImage.setXmin(0) + slcImage.setXmax(self.frame.getNumberOfSamples()) + slcImage.setWidth(self.frame.getNumberOfSamples()) + slcImage.setAccessMode('r') + self.frame.setImage(slcImage) + + def extractDoppler(self): + """ + Return the doppler centroid as defined in the HDF5 file. + """ + import numpy as np + + quadratic = {} + + rangePixelSize = self.frame.getInstrument().getRangePixelSize() + rt0 = self.frame.getStartingRange() / (2 * Const.c) + rt1 = rt0 +((self.frame.getNumberOfSamples()-1)*rangePixelSize) / (2 * Const.c) + + + ####insarApp style + quadratic['a'] = np.polyval( self.dcpoly, 0.5 * (rt0 + rt1)) / self.frame.PRF + quadratic['b'] = 0. + quadratic['c'] = 0. + + + ####For roiApp more accurate + ####Convert stuff to pixel wise coefficients + x = np.linspace(rt0, rt1, num=len(self.dcpoly)+1) + pix = np.linspace(0, self.frame.getNumberOfSamples(), num=len(self.dcpoly)+1) + evals = np.polyval(self.dcpoly, x) + fit = np.polyfit(pix, evals, len(self.dcpoly)-1) + self.frame._dopplerVsPixel = list(fit[::-1]) + print('Doppler Fit: ', self.frame._dopplerVsPixel) + + return quadratic diff --git a/components/isceobj/Sensor/SConscript b/components/isceobj/Sensor/SConscript index f512f62..32d6817 100644 --- a/components/isceobj/Sensor/SConscript +++ b/components/isceobj/Sensor/SConscript @@ -25,7 +25,7 @@ listFiles = ['ALOS.py','CEOS.py','COSMO_SkyMed.py','COSMO_SkyMed_SLC.py', 'Radarsat2.py','TerraSARX.py','Polarimetry.py','Sensor.py', 'ROI_PAC.py','Sentinel1.py','TanDEMX.py','KOMPSAT5.py', 'Risat1.py', 'Risat1_SLC.py', 'UAVSAR_RPI.py', 'UAVSAR_Stack.py', - 'UAVSAR_Polsar.py', 'ERS_EnviSAT.py', + 'UAVSAR_Polsar.py', 'ERS_EnviSAT.py', 'ICEYE_SLC.py', 'ALOS2.py', 'ERS_SLC.py', 'ALOS_SLC.py', 'EnviSAT_SLC.py', 'ERS_EnviSAT_SLC.py', 'SICD_RGZERO.py','__init__.py'] diff --git a/components/isceobj/Sensor/__init__.py b/components/isceobj/Sensor/__init__.py index cb63767..39fe672 100755 --- a/components/isceobj/Sensor/__init__.py +++ b/components/isceobj/Sensor/__init__.py @@ -96,6 +96,7 @@ createEnviSAT_SLC = partial(factory_template, 'EnviSAT_SLC') createERS_ENVISAT = partial(factory_template, 'ERS_EnviSAT') createERS_EnviSAT_SLC = partial(factory_template, 'ERS_EnviSAT_SLC') createSICD_RGZERO = partial(factory_template, 'SICD_RGZERO') +createICEYE_SLC = partial(factory_template, 'ICEYE_SLC') SENSORS = {'ALOS' : createALOS, 'ALOS_SLC' : createALOS_SLC, @@ -119,7 +120,8 @@ SENSORS = {'ALOS' : createALOS, 'ENVISAT_SLC': createEnviSAT_SLC, 'ERS_ENVISAT' : createERS_ENVISAT, 'ERS_ENVISAT_SLC' : createERS_EnviSAT_SLC, - 'SICD_RGZERO' : createSICD_RGZERO} + 'SICD_RGZERO' : createSICD_RGZERO, + 'ICEYE_SLC' : createICEYE_SLC} #These are experimental and can be added in as they become ready # 'JERS': createJERS, diff --git a/components/isceobj/StripmapProc/Factories.py b/components/isceobj/StripmapProc/Factories.py index 9f65fc1..c465f4c 100644 --- a/components/isceobj/StripmapProc/Factories.py +++ b/components/isceobj/StripmapProc/Factories.py @@ -52,7 +52,7 @@ def isRawSensor(sensor): ''' Check if input data is raw / slc. ''' - if str(sensor).lower() in ["terrasarx","cosmo_skymed_slc","radarsat2",'tandemx', 'kompsat5','risat1_slc','sentinel1', 'alos2','ers_slc','alos_slc','envisat_slc', 'uavsar_rpi','ers_envisat_slc','sicd_rgzero']: + if str(sensor).lower() in ["terrasarx","cosmo_skymed_slc","radarsat2",'tandemx', 'kompsat5','risat1_slc','sentinel1', 'alos2','ers_slc','alos_slc','envisat_slc', 'uavsar_rpi','ers_envisat_slc','sicd_rgzero', 'iceye_slc']: return False else: return True @@ -63,7 +63,7 @@ def isZeroDopplerSLC(sensor): Check if SLC is zero doppler / native doppler. ''' - if str(sensor).lower() in ["terrasarx","cosmo_skymed_slc","radarsat2",'tandemx', 'kompsat5','risat1_slc','sentinel1', 'alos2','ers_slc','envisat_slc','ers_envisat_slc','sicd_rgzero']: + if str(sensor).lower() in ["terrasarx","cosmo_skymed_slc","radarsat2",'tandemx', 'kompsat5','risat1_slc','sentinel1', 'alos2','ers_slc','envisat_slc','ers_envisat_slc','sicd_rgzero', 'iceye_slc']: return True elif sensor.lower() in ['alos_slc', 'uavsar_rpi']: return False @@ -76,7 +76,7 @@ def getDopplerMethod(sensor): Return appropriate doppler method based on user input. ''' - if str(sensor).lower() in ["terrasarx","cosmo_skymed_slc","radarsat2",'tandemx', 'kompsat5','risat1_slc','sentinel1', 'alos2','ers_slc','alos_slc','envisat_slc', 'uavsar_rpi','cosmo_skymed','ers_envisat_slc','sicd_rgzero']: + if str(sensor).lower() in ["terrasarx","cosmo_skymed_slc","radarsat2",'tandemx', 'kompsat5','risat1_slc','sentinel1', 'alos2','ers_slc','alos_slc','envisat_slc', 'uavsar_rpi','cosmo_skymed','ers_envisat_slc','sicd_rgzero', 'iceye_slc']: res = 'useDEFAULT' else: res = 'useDOPIQ'