Java tutorial
/* * Copyright (C) 2014 by Array Systems Computing Inc. http://www.array.ca * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package org.esa.s1tbx.calibration.gpf; import com.bc.ceres.core.ProgressMonitor; import org.apache.commons.math3.util.FastMath; import org.esa.s1tbx.calibration.gpf.support.BaseCalibrator; import org.esa.s1tbx.calibration.gpf.support.Calibrator; import org.esa.snap.dataio.envisat.EnvisatAuxReader; import org.esa.snap.datamodel.AbstractMetadata; import org.esa.snap.datamodel.OrbitStateVector; import org.esa.snap.datamodel.Unit; import org.esa.snap.eo.Constants; import org.esa.snap.eo.GeoUtils; import org.esa.snap.framework.datamodel.Band; import org.esa.snap.framework.datamodel.MetadataAttribute; import org.esa.snap.framework.datamodel.MetadataElement; import org.esa.snap.framework.datamodel.Product; import org.esa.snap.framework.datamodel.ProductData; import org.esa.snap.framework.datamodel.TiePointGrid; import org.esa.snap.framework.gpf.Operator; import org.esa.snap.framework.gpf.OperatorException; import org.esa.snap.framework.gpf.Tile; import org.esa.snap.gpf.OperatorUtils; import org.esa.snap.gpf.TileIndex; import org.esa.snap.util.Maths; import org.esa.snap.util.ResourceInstaller; import org.esa.snap.util.Settings; import java.awt.Rectangle; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.text.SimpleDateFormat; import java.util.Date; /** * Calibration for ASAR data products. */ public class ASARCalibrator extends BaseCalibrator implements Calibrator { private File externalAuxFile = null; private String auxFile = null; private String productType = null; private String oldXCAFileName = null; // the old XCA file private String newXCAFileName = null; // XCA file for radiometric calibration private String newXCAFilePath = null; // absolute path for XCA file private String[] mdsPolar; // polarizations for the two bands in the product private TiePointGrid incidenceAngle = null; private TiePointGrid slantRangeTime = null; private TiePointGrid latitude = null; private boolean srgrFlag = false; private boolean multilookFlag = false; private boolean antElevCorrFlag = false; private boolean rangeSpreadCompFlag = false; private boolean wideSwathProductFlag = false; private boolean retroCalibrationFlag = false; private boolean applyAntennaPatternCorr = false; private boolean applyRangeSpreadingCorr = false; private double firstLineUTC = 0.0; // in days private double lineTimeInterval = 0.0; // in days private double avgSceneHeight = 0.0; // in m private double rangeSpacing = 0.0; // in m private double azimuthSpacing = 0.0; // in m private double rangeSpreadingCompPower = 0.0; // power in range spreading loss compensation calculation private double halfRangeSpreadingCompPower = 0.0; private double latMax = 0.0; private double delLat = 0.0; private double[] earthRadius = null; // Earth radius for all range lines, in m private final double[] newCalibrationConstant = new double[2]; private double[] oldRefElevationAngle = null; // reference elevation angle for given swath in old aux file, in degree private double[] newRefElevationAngle = null; // reference elevation angle for given swath in new aux file, in degree private float[][] oldAntennaPatternSingleSwath = null; // old antenna pattern gains for single swath product, in dB private float[][] oldAntennaPatternWideSwath = null; // old antenna pattern gains for single swath product, in dB private float[][] newAntennaPatternSingleSwath = null; // new antenna pattern gains for single swath product, in dB private float[][] newAntennaPatternWideSwath = null; // new antenna pattern gains for single swath product, in dB private TiePointInterpolator incidenceTPGInterp = null; private TiePointInterpolator slantRangeTPGInterp = null; private int numMPPRecords; // number of MPP ADSR records private String swath; private OrbitStateVector[] orbitStateVectors = null; private AbstractMetadata.SRGRCoefficientList[] srgrConvParams = null; private static final int numOfGains = 201; // number of antenna pattern gain values for a given swath and // polarization in the aux file // private double refSlantRange = 800000.0; // m // private double halfLightSpeedByRefSlantRange = Constants.halfLightSpeed / refSlantRange; private static final double refSlantRange800km = 800000.0; // m private static final int INVALID_SUB_SWATH_INDEX = -1; public ASARCalibrator() { } /** * Set external auxiliary file. */ @Override public void setExternalAuxFile(File file) { externalAuxFile = file; } /** * Set auxiliary file flag. */ @Override public void setAuxFileFlag(String file) { auxFile = file; } /** * @param op the Calibration Operator * @param srcProduct The source product. * @param tgtProduct The target product. * @param mustPerformRetroCalibration If true, retro-calibration must be performed if it is applicable. * @throws OperatorException The exception. */ @Override public void initialize(final Operator op, final Product srcProduct, final Product tgtProduct, final boolean mustPerformRetroCalibration, final boolean mustUpdateMetadata) throws OperatorException { try { calibrationOp = op; sourceProduct = srcProduct; targetProduct = tgtProduct; absRoot = AbstractMetadata.getAbstractedMetadata(sourceProduct); getProductType(); getSRGRFlag(); getCalibrationFlags(); getMultilookFlag(); getProductSwath(); setCalibrationFlags(); mdsPolar = OperatorUtils.getProductPolarization(absRoot); getRangeAzimuthSpacing(); numMPPRecords = getNumOfRecordsInMainProcParam(sourceProduct); //??? getTiePointGridData(sourceProduct); checkXCAFileExsitence(mustPerformRetroCalibration); if (srgrFlag) { getSrgrCoeff(); } if (retroCalibrationFlag) { getOldAntennaPattern(); } if (applyAntennaPatternCorr) { getFirstLineTime(); getLineTimeInterval(); getAverageSceneHeight(); getOrbitStateVectors(); getNewAntennaPattern(); computeEarthRadius(); } getNewCalibrationFactor(); setRangeSpreadingLossCompPower(); if (mustUpdateMetadata) { updateTargetProductMetadata(); } } catch (Exception e) { throw new OperatorException(e); } } /** * Get Product ID from MPH. * * @throws OperatorException The exceptions. */ private void getProductType() throws OperatorException { productType = sourceProduct.getProductType(); // product type could be 1P or 1C if (!productType.contains("ASA_IMP_1") && !productType.contains("ASA_IMM_1") && !productType.contains("ASA_APP_1") && !productType.contains("ASA_APM_1") && !productType.contains("ASA_WSM_1") && !productType.contains("ASA_IMG_1") && !productType.contains("ASA_APG_1") && !productType.contains("ASA_IMS_1") && !productType.contains("ASA_APS_1") && !productType.contains("ASA_GM") && !productType.contains("ASA_WSS_1")) { throw new OperatorException(productType + " is not a valid ASAR product type for calibration."); } } /** * Get antenna elevation correction flag and range spreading compensation flag from Metadata. * * @throws Exception The exceptions. */ private void getCalibrationFlags() throws Exception { if (AbstractMetadata.getAttributeBoolean(absRoot, AbstractMetadata.abs_calibration_flag)) { throw new OperatorException("The product has already been calibrated."); } antElevCorrFlag = AbstractMetadata.getAttributeBoolean(absRoot, AbstractMetadata.ant_elev_corr_flag); rangeSpreadCompFlag = AbstractMetadata.getAttributeBoolean(absRoot, AbstractMetadata.range_spread_comp_flag); } /** * Get SRGR flag from the abstracted metadata. * * @throws Exception The exceptions. */ private void getSRGRFlag() throws Exception { srgrFlag = AbstractMetadata.getAttributeBoolean(absRoot, AbstractMetadata.srgr_flag); } /** * Get multilook flag from the abstracted metadata. * * @throws Exception The exceptions. */ private void getMultilookFlag() throws Exception { multilookFlag = AbstractMetadata.getAttributeBoolean(absRoot, AbstractMetadata.multilook_flag); } /** * Get product swath. */ private void getProductSwath() { swath = absRoot.getAttributeString(AbstractMetadata.SWATH); wideSwathProductFlag = swath.contains("WS"); } /** * Set calibration flags. */ private void setCalibrationFlags() { if (antElevCorrFlag) { if (multilookFlag || auxFile != null && auxFile.contains(CalibrationOp.PRODUCT_AUX)) { retroCalibrationFlag = false; //System.out.println("Only constant and incidence angle corrections will be performed for radiometric calibration"); } else { retroCalibrationFlag = true; } } if (auxFile != null && auxFile.contains(CalibrationOp.PRODUCT_AUX)) { applyAntennaPatternCorr = false; } else { applyAntennaPatternCorr = !srgrFlag || retroCalibrationFlag || !antElevCorrFlag; } applyRangeSpreadingCorr = !rangeSpreadCompFlag; } /** * Get SRGR conversion parameters. */ private void getSrgrCoeff() { srgrConvParams = AbstractMetadata.getSRGRCoefficients(absRoot); } /** * Get range spacing from the abstracted metadata. * * @throws Exception The exceptions. */ private void getRangeAzimuthSpacing() throws Exception { rangeSpacing = AbstractMetadata.getAttributeDouble(absRoot, AbstractMetadata.range_spacing); azimuthSpacing = AbstractMetadata.getAttributeDouble(absRoot, AbstractMetadata.azimuth_spacing); } /** * Get XCA file used for the original radiometric calibration. * * @return The complete path to the XCA file. */ private String getOldXCAFile() { oldXCAFileName = absRoot.getAttributeString(AbstractMetadata.external_calibration_file); return Settings.instance().get("AuxData.envisatAuxDataPath") + File.separator + oldXCAFileName; } /** * Get old antenna pattern gains. */ private void getOldAntennaPattern() { final String xcaFilePath = Settings.instance().get("AuxData.envisatAuxDataPath") + File.separator + oldXCAFileName; if (wideSwathProductFlag) { oldRefElevationAngle = new double[5]; // reference elevation angles for 5 sub swathes oldAntennaPatternWideSwath = new float[5][numOfGains]; // antenna pattern gain for 5 sub swathes getWideSwathAntennaPatternGainFromAuxData(xcaFilePath, mdsPolar[0], numOfGains, oldRefElevationAngle, oldAntennaPatternWideSwath); } else { oldRefElevationAngle = new double[1]; // reference elevation angle for 1 swath oldAntennaPatternSingleSwath = new float[2][numOfGains]; // antenna pattern gain for 2 bands getSingleSwathAntennaPatternGainFromAuxData(xcaFilePath, swath, mdsPolar, numOfGains, oldRefElevationAngle, oldAntennaPatternSingleSwath); } } /** * Get number of records in Main Processing Params data set. * * @param sourceProduct The source prodict. * @return The number of records. * @throws org.esa.snap.framework.gpf.OperatorException The exceptions. */ static int getNumOfRecordsInMainProcParam(Product sourceProduct) throws OperatorException { final MetadataElement origRoot = AbstractMetadata.getOriginalProductMetadata(sourceProduct); final MetadataElement dsd = origRoot.getElement("DSD").getElement("DSD.3"); if (dsd == null) { throw new OperatorException("DSD not found"); } final MetadataAttribute numRecordsAttr = dsd.getAttribute("num_records"); if (numRecordsAttr == null) { throw new OperatorException("num_records not found"); } int numMPPRecords = numRecordsAttr.getData().getElemInt(); if (numMPPRecords < 1) { throw new OperatorException("Invalid num_records."); } //System.out.println("The number of Main Processing Params records is " + numMPPRecords); return numMPPRecords; } /** * Get incidence angle and slant range time tie point grids. * * @param sourceProduct the source */ private void getTiePointGridData(Product sourceProduct) { slantRangeTime = OperatorUtils.getSlantRangeTime(sourceProduct); incidenceAngle = OperatorUtils.getIncidenceAngle(sourceProduct); latitude = OperatorUtils.getLatitude(sourceProduct); incidenceTPGInterp = new TiePointInterpolator(incidenceAngle); slantRangeTPGInterp = new TiePointInterpolator(slantRangeTime); } /** * Check if old or new XCA file exists. * * @param mustPerformRetroCalibration If true, retro-calibration must be performed if it is applicable. * @throws Exception The exception. */ private void checkXCAFileExsitence(boolean mustPerformRetroCalibration) throws Exception { String oldXCAFilePath = null; if (retroCalibrationFlag) { oldXCAFilePath = getOldXCAFile(); if (!isFileExisting(oldXCAFilePath)) { if (mustPerformRetroCalibration) { throw new OperatorException("Cannot find XCA file: " + oldXCAFilePath); } else { retroCalibrationFlag = false; applyAntennaPatternCorr = false; // todo should warn the user that retro-calibration will not be performed } } } if (applyAntennaPatternCorr) { getNewXCAFile(); if (!isFileExisting(newXCAFilePath)) { throw new OperatorException("Cannot find XCA file: " + newXCAFilePath); } if (retroCalibrationFlag && !mustPerformRetroCalibration && newXCAFilePath != null && (oldXCAFilePath.contains(newXCAFilePath) || newXCAFilePath.contains(oldXCAFilePath))) { retroCalibrationFlag = false; applyAntennaPatternCorr = false; } } } /** * Return true if a given file exists, otherwise, false. * * @param filePath The complete path to the given file. * @return Return true if the file exists, false otherwise. */ private boolean isFileExisting(String filePath) { File file = null; final String[] exts = new String[] { "", ".gz", ".zip" }; for (String ext : exts) { file = new File(filePath + ext); if (file.exists()) { return true; } } return false; } private void getNewXCAFile() throws Exception { if (auxFile != null && auxFile.contains(CalibrationOp.EXTERNAL_AUX)) { if (externalAuxFile != null && externalAuxFile.exists()) { if (!externalAuxFile.getName().contains("ASA_XCA")) { throw new OperatorException("Invalid XCA file for ASAR product"); } newXCAFileName = externalAuxFile.getName(); newXCAFilePath = externalAuxFile.getAbsolutePath(); } else { throw new OperatorException("No external auxiliary file is specified."); } } else { final Date startDate = sourceProduct.getStartTime().getAsDate(); final Date endDate = sourceProduct.getEndTime().getAsDate(); final Path moduleBasePath = ResourceInstaller.findModuleCodeBasePath(this.getClass()); final Path xcaFileDir = moduleBasePath.resolve("org/esa/s1tbx/auxdata/envisat"); newXCAFileName = findXCAFile(xcaFileDir.toFile(), startDate, endDate); newXCAFilePath = xcaFileDir.toString() + File.separator + newXCAFileName; } } /** * Find the latest XVA file available. * * @param xcaFileDir The complete path to the XCA file directory. * @param productStartDate The product start date. * @param productEndDate The product end data. * @return The name of the XCA file found. * @throws Exception The exceptions. */ public static String findXCAFile(File xcaFileDir, Date productStartDate, Date productEndDate) throws Exception { final File[] list = xcaFileDir.listFiles(); if (list == null) { return null; } final SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMdd_HHmmss"); Date latestCreationDate = dateformat.parse("19000101_000000"); String xcaFileName = null; for (File f : list) { final String fileName = f.getName(); if (fileName.length() < 61 || !fileName.substring(0, 10).equals("ASA_XCA_AX")) { continue; } final Date creationDate = dateformat.parse(fileName.substring(14, 29)); final Date validStartDate = dateformat.parse(fileName.substring(30, 45)); final Date validStopDate = dateformat.parse(fileName.substring(46, 61)); if (productStartDate.after(validStartDate) && productEndDate.before(validStopDate) && latestCreationDate.before(creationDate)) { latestCreationDate = creationDate; xcaFileName = fileName; } } return xcaFileName; } /** * Get calibration factor. */ private void getNewCalibrationFactor() { if (newXCAFilePath != null) { getCalibrationFactorFromExternalAuxFile(newXCAFilePath, swath, mdsPolar, productType, newCalibrationConstant); } else { getCalibrationFactorFromMetadata(); } } /** * Get calibration factor from user specified auxiliary data. * * @param auxFilePath The absolute path to the aux file. * @param swath The product swath. * @param mdsPolar The product polarizations. * @param productType The product type. * @param calibrationFactor The calibration factors. */ public static void getCalibrationFactorFromExternalAuxFile(String auxFilePath, String swath, String[] mdsPolar, String productType, double[] calibrationFactor) { final EnvisatAuxReader reader = new EnvisatAuxReader(); try { reader.readProduct(auxFilePath); final int numOfSwaths = 7; String calibrationFactorName; for (int i = 0; i < 2 && mdsPolar[i] != null && mdsPolar[i].length() != 0; i++) { calibrationFactor[i] = 0; if (productType.contains("ASA_IMP_1")) { calibrationFactorName = "ext_cal_im_pri_" + mdsPolar[i]; } else if (productType.contains("ASA_IMM_1")) { calibrationFactorName = "ext_cal_im_med_" + mdsPolar[i]; } else if (productType.contains("ASA_APP_1")) { calibrationFactorName = "ext_cal_ap_pri_" + mdsPolar[i]; } else if (productType.contains("ASA_APM_1")) { calibrationFactorName = "ext_cal_ap_med_" + mdsPolar[i]; } else if (productType.contains("ASA_WS")) { calibrationFactorName = "ext_cal_ws_" + mdsPolar[i]; } else if (productType.contains("ASA_GM1_1")) { calibrationFactorName = "ext_cal_gm_" + mdsPolar[i]; } else if (productType.contains("ASA_IMG_1")) { calibrationFactorName = "ext_cal_im_geo_" + mdsPolar[i]; } else if (productType.contains("ASA_APG_1")) { calibrationFactorName = "ext_cal_ap_geo_" + mdsPolar[i]; } else if (productType.contains("ASA_IMS_1")) { calibrationFactorName = "ext_cal_im_" + mdsPolar[i]; } else if (productType.contains("ASA_APS_1")) { calibrationFactorName = "ext_cal_ap_" + mdsPolar[i]; } else { throw new OperatorException("Invalid ASAR product type."); } final ProductData factorData = reader.getAuxData(calibrationFactorName); final float[] factors = (float[]) factorData.getElems(); if (productType.contains("ASA_WS") || productType.contains("ASA_GM1")) { calibrationFactor[i] = factors[0]; } else { if (factors.length != numOfSwaths) { throw new OperatorException("Incorrect array length for " + calibrationFactorName); } if (swath.contains("S1")) { calibrationFactor[i] = factors[0]; } else if (swath.contains("S2")) { calibrationFactor[i] = factors[1]; } else if (swath.contains("S3")) { calibrationFactor[i] = factors[2]; } else if (swath.contains("S4")) { calibrationFactor[i] = factors[3]; } else if (swath.contains("S5")) { calibrationFactor[i] = factors[4]; } else if (swath.contains("S6")) { calibrationFactor[i] = factors[5]; } else if (swath.contains("S7")) { calibrationFactor[i] = factors[6]; } else { throw new OperatorException("Invalid swath"); } } } } catch (IOException e) { throw new OperatorException(e); } if (Double.compare(calibrationFactor[0], 0.0) == 0 && Double.compare(calibrationFactor[1], 0.0) == 0) { throw new OperatorException("Calibration factors in user provided auxiliary file are zero"); } } /** * Get calibration factors from Metadata for each band in the product. * Here it is assumed that the calibration factor values do not change in case that there are * multiple records in the Main Processing Parameters data set. */ private void getCalibrationFactorFromMetadata() { final MetadataElement origRoot = AbstractMetadata.getOriginalProductMetadata(sourceProduct); MetadataElement ads; if (numMPPRecords == 1) { ads = origRoot.getElement("MAIN_PROCESSING_PARAMS_ADS"); } else { ads = origRoot.getElement("MAIN_PROCESSING_PARAMS_ADS").getElement("MAIN_PROCESSING_PARAMS_ADS.1"); if (ads == null) ads = origRoot.getElement("MAIN_PROCESSING_PARAMS_ADS"); } if (ads == null) { throw new OperatorException("MAIN_PROCESSING_PARAMS_ADS not found"); } MetadataAttribute calibrationFactorsAttr = ads .getAttribute("ASAR_Main_ADSR.sd/calibration_factors.1.ext_cal_fact"); if (calibrationFactorsAttr == null) { throw new OperatorException("calibration_factors.1.ext_cal_fact not found"); } newCalibrationConstant[0] = (double) calibrationFactorsAttr.getData().getElemFloat(); calibrationFactorsAttr = ads.getAttribute("ASAR_Main_ADSR.sd/calibration_factors.2.ext_cal_fact"); if (calibrationFactorsAttr == null) { throw new OperatorException("calibration_factors.2.ext_cal_fact not found"); } newCalibrationConstant[1] = (double) calibrationFactorsAttr.getData().getElemFloat(); if (Double.compare(newCalibrationConstant[0], 0.0) == 0 && Double.compare(newCalibrationConstant[1], 0.0) == 0) { throw new OperatorException("Calibration factors in metadata are zero"); } //System.out.println("calibration factor for band 1 is " + calibrationFactor[0]); //System.out.println("calibration factor for band 2 is " + calibrationFactor[1]); } /** * Get orbit state vectors from the abstracted metadata. */ private void getOrbitStateVectors() { orbitStateVectors = AbstractMetadata.getOrbitStateVectors(absRoot); } /** * Get first line time from the abstracted metadata (in days). */ private void getFirstLineTime() { firstLineUTC = absRoot.getAttributeUTC(AbstractMetadata.first_line_time).getMJD(); // in days } /** * Get line time interval from the abstracted metadata (in days). */ private void getLineTimeInterval() { lineTimeInterval = absRoot.getAttributeDouble(AbstractMetadata.line_time_interval) / Constants.secondsInDay; // s to day } /** * Get average scene height from abstracted metadata. * * @throws Exception The exceptions. */ private void getAverageSceneHeight() throws Exception { avgSceneHeight = AbstractMetadata.getAttributeDouble(absRoot, AbstractMetadata.avg_scene_height); } /** * Get the new antenna pattern gain from the latest XCA file available. */ private void getNewAntennaPattern() { if (wideSwathProductFlag) { newRefElevationAngle = new double[5]; // reference elevation angles for 5 sub swathes newAntennaPatternWideSwath = new float[5][numOfGains]; // antenna pattern gain for 5 sub swathes getWideSwathAntennaPatternGainFromAuxData(newXCAFilePath, mdsPolar[0], numOfGains, newRefElevationAngle, newAntennaPatternWideSwath); } else { newRefElevationAngle = new double[1]; // reference elevation angle for 1 swath newAntennaPatternSingleSwath = new float[2][numOfGains]; // antenna pattern gain for 2 bands getSingleSwathAntennaPatternGainFromAuxData(newXCAFilePath, swath, mdsPolar, numOfGains, newRefElevationAngle, newAntennaPatternSingleSwath); } } /** * Get reference elevation angle and antenna pattern gain from auxiliary file for single swath product. * * @param fileName The auxiliary data file name * @param swath The swath name. * @param pol The polarizations for 2 bands. * @param numOfGains The number of gains for given swath and polarization (201). * @param refElevAngle The reference elevation angle array. * @param antPatArray The antenna pattern array. * @throws org.esa.snap.framework.gpf.OperatorException The IO exception. */ public static void getSingleSwathAntennaPatternGainFromAuxData(String fileName, String swath, String[] pol, int numOfGains, double[] refElevAngle, float[][] antPatArray) throws OperatorException { final EnvisatAuxReader reader = new EnvisatAuxReader(); try { reader.readProduct(fileName); String swathName; if (swath.contains("S1")) { swathName = "is1"; } else if (swath.contains("S2")) { swathName = "is2"; } else if (swath.contains("S3")) { swathName = "is3_ss2"; } else if (swath.contains("S4")) { swathName = "is4_ss3"; } else if (swath.contains("S5")) { swathName = "is5_ss4"; } else if (swath.contains("S6")) { swathName = "is6_ss5"; } else if (swath.contains("S7")) { swathName = "is7"; } else { throw new OperatorException("Invalid swath"); } final String refElevAngleName = "elev_ang_" + swathName; final ProductData refElevAngleData = reader.getAuxData(refElevAngleName); refElevAngle[0] = (double) refElevAngleData.getElemFloat(); final String patternName = "pattern_" + swathName; final ProductData patternData = reader.getAuxData(patternName); final float[] pattern = ((float[]) patternData.getElems()); if (pattern.length != 804) { throw new OperatorException("Incorret array length for " + patternName); } for (int i = 0; i < 2 && pol[i] != null && pol[i].length() != 0; i++) { if (pol[i].contains("hh")) { System.arraycopy(pattern, 0, antPatArray[i], 0, numOfGains); } else if (pol[i].contains("vv")) { System.arraycopy(pattern, numOfGains, antPatArray[i], 0, numOfGains); } else if (pol[i].contains("hv")) { System.arraycopy(pattern, 2 * numOfGains, antPatArray[i], 0, numOfGains); } else if (pol[i].contains("vh")) { System.arraycopy(pattern, 3 * numOfGains, antPatArray[i], 0, numOfGains); } } } catch (IOException e) { throw new OperatorException(e); } } /** * Get reference elevation angle and antenna pattern gain from auxiliary file for wide swath product. * * @param fileName The auxiliary data file name * @param pol The polarization. * @param numOfGains The number of gains for given swath and polarization (201). * @param refElevAngle The reference elevation angle array. * @param antPatArray The antenna pattern array. * @throws org.esa.snap.framework.gpf.OperatorException The IO exception. */ public static void getWideSwathAntennaPatternGainFromAuxData(String fileName, String pol, int numOfGains, double[] refElevAngle, float[][] antPatArray) throws OperatorException { final EnvisatAuxReader reader = new EnvisatAuxReader(); try { reader.readProduct(fileName); final String[] swathName = { "ss1", "is3_ss2", "is4_ss3", "is5_ss4", "is6_ss5" }; for (int i = 0; i < swathName.length; i++) { // read elevation angles final String refElevAngleName = "elev_ang_" + swathName[i]; final ProductData refElevAngleData = reader.getAuxData(refElevAngleName); refElevAngle[i] = (double) refElevAngleData.getElemFloat(); // read antenna pattern gains final String patternName = "pattern_" + swathName[i]; final ProductData patternData = reader.getAuxData(patternName); final float[] pattern = ((float[]) patternData.getElems()); if (pattern.length != 804) { throw new OperatorException("Incorret array length for " + patternName); } if (pol.contains("hh")) { System.arraycopy(pattern, 0, antPatArray[i], 0, numOfGains); } else if (pol.contains("vv")) { System.arraycopy(pattern, numOfGains, antPatArray[i], 0, numOfGains); } else if (pol.contains("hv")) { System.arraycopy(pattern, 2 * numOfGains, antPatArray[i], 0, numOfGains); } else if (pol.contains("vh")) { System.arraycopy(pattern, 3 * numOfGains, antPatArray[i], 0, numOfGains); } } } catch (IOException e) { throw new OperatorException(e); } } /** * Set power coefficient used in range spreading loss compensation computation for slant range images. */ private void setRangeSpreadingLossCompPower() { rangeSpreadingCompPower = 3.0; if (productType.contains("ASA_APS_1") || productType.contains("ASA_WSS")) { rangeSpreadingCompPower = 4.0; } halfRangeSpreadingCompPower = rangeSpreadingCompPower / 2.0; } /** * Update the metadata in the target product. */ public void updateTargetProductMetadata() { final MetadataElement tgtAbsRoot = AbstractMetadata.getAbstractedMetadata(targetProduct); if (applyAntennaPatternCorr) { AbstractMetadata.setAttribute(tgtAbsRoot, AbstractMetadata.ant_elev_corr_flag, 1); } if (applyRangeSpreadingCorr) { AbstractMetadata.setAttribute(tgtAbsRoot, AbstractMetadata.range_spread_comp_flag, 1); } AbstractMetadata.setAttribute(tgtAbsRoot, AbstractMetadata.abs_calibration_flag, 1); if (newXCAFileName != null) { AbstractMetadata.setAttribute(tgtAbsRoot, AbstractMetadata.external_calibration_file, newXCAFileName); } AbstractMetadata.setAttribute(tgtAbsRoot, AbstractMetadata.calibration_factor, newCalibrationConstant[0]); } /** * Called by the framework in order to compute a tile for the given target band. * <p>The default implementation throws a runtime exception with the message "not implemented".</p> * * @param targetBand The target band. * @param targetTile The current tile associated with the target band to be computed. * @param pm A progress monitor which should be used to determine computation cancelation requests. * @throws org.esa.snap.framework.gpf.OperatorException If an error occurs during computation of the target raster. */ @Override public void computeTile(Band targetBand, Tile targetTile, ProgressMonitor pm) throws OperatorException { final Rectangle targetTileRectangle = targetTile.getRectangle(); final int x0 = targetTileRectangle.x; final int y0 = targetTileRectangle.y; final int w = targetTileRectangle.width; final int h = targetTileRectangle.height; //System.out.println("x0 = " + x0 + ", y0 = " + y0 + ", w = " + w + ", h = " + h); Band sourceBand1; Tile sourceRaster1; ProductData srcData1; ProductData srcData2 = null; final String[] srcBandNames = targetBandNameToSourceBandName.get(targetBand.getName()); if (srcBandNames.length == 1) { sourceBand1 = sourceProduct.getBand(srcBandNames[0]); sourceRaster1 = calibrationOp.getSourceTile(sourceBand1, targetTileRectangle); srcData1 = sourceRaster1.getDataBuffer(); } else { sourceBand1 = sourceProduct.getBand(srcBandNames[0]); final Band sourceBand2 = sourceProduct.getBand(srcBandNames[1]); sourceRaster1 = calibrationOp.getSourceTile(sourceBand1, targetTileRectangle); final Tile sourceRaster2 = calibrationOp.getSourceTile(sourceBand2, targetTileRectangle); srcData1 = sourceRaster1.getDataBuffer(); srcData2 = sourceRaster2.getDataBuffer(); } final Unit.UnitType bandUnit = Unit.getUnitType(sourceBand1); // copy band if unit is phase if (bandUnit == Unit.UnitType.PHASE) { targetTile.setRawSamples(sourceRaster1.getRawSamples()); return; } final String pol = OperatorUtils.getBandPolarization(srcBandNames[0], absRoot); int prodBand = 0; if (pol != null && mdsPolar[1] != null && mdsPolar[1].contains(pol)) { prodBand = 1; } final ProductData trgData = targetTile.getDataBuffer(); final TileIndex srcIndex = new TileIndex(sourceRaster1); final TileIndex tgtIndex = new TileIndex(targetTile); final int maxY = y0 + h; final int maxX = x0 + w; final double[] incidenceAnglesArray = new double[w]; final double[] slantRangeTimeArray = new double[w]; double[][] targetTileOldAntPat = null; // old antenna pattern gains for row pixels in a tile, in linear scale double[][] targetTileNewAntPat = null; // new antenna pattern gains for row pixels in a tile, in linear scale double[][] targetTileSlantRange = null; // slant range for pixels in a tile, in m if (applyAntennaPatternCorr) { targetTileNewAntPat = new double[h][w]; targetTileSlantRange = new double[h][w]; if (retroCalibrationFlag) { targetTileOldAntPat = new double[h][w]; } if (wideSwathProductFlag) { computeWideSwathAntennaPatternForCurrentTile(x0, y0, w, h, targetTileOldAntPat, targetTileNewAntPat, targetTileSlantRange, slantRangeTPGInterp); } else { computeSingleSwathAntennaPatternForCurrentTile(x0, y0, w, h, targetTileOldAntPat, targetTileNewAntPat, targetTileSlantRange, prodBand, slantRangeTPGInterp); } } double sigma, dn = 0, i, q; final double theCalibrationFactor = newCalibrationConstant[prodBand]; int srcIdx, tgtIdx; for (int y = y0, yy = 0; y < maxY; ++y, ++yy) { srcIndex.calculateStride(y); tgtIndex.calculateStride(y); incidenceTPGInterp.getPixels(x0, y, w, 1, incidenceAnglesArray, pm, TiePointInterpolator.InterpMode.QUADRATIC); if (applyRangeSpreadingCorr) { slantRangeTPGInterp.getPixels(x0, y, w, 1, slantRangeTimeArray, pm, TiePointInterpolator.InterpMode.QUADRATIC); } for (int x = x0, xx = 0; x < maxX; ++x, ++xx) { srcIdx = srcIndex.getIndex(x); tgtIdx = tgtIndex.getIndex(x); if (bandUnit == Unit.UnitType.AMPLITUDE) { dn = srcData1.getElemDoubleAt(srcIdx); sigma = dn * dn; } else if (bandUnit == Unit.UnitType.INTENSITY) { sigma = srcData1.getElemDoubleAt(srcIdx); } else if (bandUnit == Unit.UnitType.REAL || bandUnit == Unit.UnitType.IMAGINARY) { i = srcData1.getElemDoubleAt(srcIdx); q = srcData2.getElemDoubleAt(srcIdx); sigma = i * i + q * q; } else { throw new OperatorException("ASAR Calibration: unhandled unit"); } if (retroCalibrationFlag) { // remove old antenna pattern gain sigma *= targetTileOldAntPat[yy][xx]; // see Andrea's email dated Nov. 11, 2008 } // apply calibration constant and incidence angle corrections sigma *= FastMath.sin(incidenceAnglesArray[xx] * Constants.DTOR) / theCalibrationFactor; if (applyRangeSpreadingCorr && targetTileSlantRange != null) { // apply range spreading loss compensation /* time = slantRangeTimeArray[xx] / 1000000000.0; //convert ns to s sigma *= Math.pow(time * halfLightSpeedByRefSlantRange, rangeSpreadingCompPower); */ sigma *= FastMath.pow(targetTileSlantRange[yy][xx] / refSlantRange800km, rangeSpreadingCompPower); } if (applyAntennaPatternCorr) { // apply antenna pattern correction sigma /= targetTileNewAntPat[yy][xx]; // see Andrea's email dated Nov. 11, 2008 } if (outputImageScaleInDb) { // convert calibration result to dB if (sigma < underFlowFloat) { sigma = -underFlowFloat; } else { sigma = 10.0 * Math.log10(sigma); } } trgData.setElemDoubleAt(tgtIdx, sigma); } } } /** * Compute antenna pattern for the middle row of the given tile for single swath product. * Here it is assumed that the elevation angles for pixels in the same column are the same. * * @param x0 The x coordinate of the upper left point in the current tile. * @param y0 The y coordinate of the upper left point in the current tile. * @param w The width of the current tile. * @param h The height of the current tile. * @param band The band index. */ private void computeSingleSwathAntennaPatternForCurrentTile(final int x0, final int y0, final int w, final int h, final double[][] targetTileOldAntPat, final double[][] targetTileNewAntPat, final double[][] targetTileSlantRange, final int band, final TiePointInterpolator slantRangeTPGInterp) { final int yMax = y0 + h; for (int y = y0; y < yMax; y++) { final double zeroDopplerTime = firstLineUTC + y * lineTimeInterval; final double satelliteHeight = computeSatelliteHeight(zeroDopplerTime, orbitStateVectors); AbstractMetadata.SRGRCoefficientList srgrConvParam = null; if (srgrFlag) { srgrConvParam = getSRGRCoefficientsForARangeLine(zeroDopplerTime); } final int yy = y - y0; final int xMax = x0 + w; for (int x = x0; x < xMax; x++) { final int xx = x - x0; targetTileSlantRange[yy][xx] = computeSlantRange(x, y, srgrConvParam, slantRangeTPGInterp); // in m final double localEarthRadius = getEarthRadius(x, y); final double theta = computeElevationAngle(targetTileSlantRange[yy][xx], satelliteHeight, avgSceneHeight + localEarthRadius); // in degree /* double alpha = incidenceAngle.getPixelDouble(x, y); // in degree double gamma = Math.asin(targetTileSlantRange[yy][xx]*Math.sin(alpha*MathUtils.DTOR)/satelitteHeight)*MathUtils.RTOD; // in degree double theta = alpha - gamma; // in degree */ targetTileNewAntPat[yy][xx] = computeAntPatGain(theta, newRefElevationAngle[0], newAntennaPatternSingleSwath[band]); if (retroCalibrationFlag) { targetTileOldAntPat[yy][xx] = computeAntPatGain(theta, oldRefElevationAngle[0], oldAntennaPatternSingleSwath[band]); } } } } /** * Compute antenna pattern for the middle row of the given tile for wide swath product. * Here it is assumed that the elevation angles for pixels in the same column are the same. * * @param x0 The x coordinate of the upper left point in the current tile. * @param y0 The y coordinate of the upper left point in the current tile. * @param w The width of the current tile. * @param h The height of the current tile. */ private void computeWideSwathAntennaPatternForCurrentTile(final int x0, final int y0, final int w, final int h, final double[][] targetTileOldAntPat, final double[][] targetTileNewAntPat, final double[][] targetTileSlantRange, final TiePointInterpolator slantRangeTPGInterp) { final int yMax = y0 + h; for (int y = y0; y < yMax; y++) { final double zeroDopplerTime = firstLineUTC + y * lineTimeInterval; final double satelitteHeight = computeSatelliteHeight(zeroDopplerTime, orbitStateVectors); AbstractMetadata.SRGRCoefficientList srgrConvParam = null; if (srgrFlag) { srgrConvParam = getSRGRCoefficientsForARangeLine(zeroDopplerTime); } final int yy = y - y0; final int xMax = x0 + w; for (int x = x0; x < xMax; x++) { final int xx = x - x0; targetTileSlantRange[yy][xx] = computeSlantRange(x, y, srgrConvParam, slantRangeTPGInterp); // in m final double localEarthRadius = getEarthRadius(x, y); final double theta = computeElevationAngle(targetTileSlantRange[yy][xx], satelitteHeight, avgSceneHeight + localEarthRadius); // in degree int subSwathIndex = findSubSwath(theta, newRefElevationAngle); targetTileNewAntPat[yy][xx] = computeAntPatGain(theta, newRefElevationAngle[subSwathIndex], newAntennaPatternWideSwath[subSwathIndex]); if (retroCalibrationFlag) { subSwathIndex = findSubSwath(theta, oldRefElevationAngle); targetTileOldAntPat[yy][xx] = computeAntPatGain(theta, oldRefElevationAngle[subSwathIndex], oldAntennaPatternWideSwath[subSwathIndex]); } } } } /** * Get Earth radius (in m) for given pixel. * * @param x The x coordinate of the given pixel. * @param y The y coordinate of the given pixel. * @return The earth radius. */ private double getEarthRadius(final int x, final int y) { // use linear rather than quadratic interpolation for subset because quadratic interpolation // does not give accurate result in this case int i = (int) ((latMax - latitude.getPixelDouble(x, y)) / delLat + 0.5); if (i < 0) { i = 0; } else if (i >= earthRadius.length) { i = earthRadius.length - 1; } return earthRadius[i]; } /** * Find the sub swath index for given elevation angle. * * @param theta The elevation angle. * @param refElevationAngle The reference elevation array. * @return The sub swath index. */ public static int findSubSwath(double theta, double[] refElevationAngle) { // The method below finds the nearest reference elevation angle to the given elevation angle theta. // The method is equivalent to the one proposed by Romain in his email dated April 28, 2009, in which // middle point of the overlapped area of two adjacent sub swathes is used as boundary of sub swath. int idx = -1; double min = 360.0; for (int i = 0; i < refElevationAngle.length; i++) { double d = Math.abs(theta - refElevationAngle[i]); if (d < min) { min = d; idx = i; } } return idx; } /** * Compute antenna pattern gains for the given elevation angle using linear interpolation. * * @param elevAngle The elevation angle (in degree) of a given pixel. * @param refElevationAngle The reference elevation angle (in degree). * @param antPatArray The antenna pattern array. * @return The antenna pattern gain (in linear scale). */ public static double computeAntPatGain(double elevAngle, double refElevationAngle, float[] antPatArray) { final double delta = 0.05; int k0 = (int) ((elevAngle - refElevationAngle + 5.0) / delta); if (k0 < 0) { k0 = 0; } else if (k0 >= antPatArray.length - 1) { k0 = antPatArray.length - 2; } final double theta0 = refElevationAngle - 5.0 + k0 * delta; final double theta1 = theta0 + delta; final double gain0 = FastMath.pow(10, (double) antPatArray[k0] / 10.0); // convert dB to linear scale final double gain1 = FastMath.pow(10, (double) antPatArray[k0 + 1] / 10.0); final double mu = (elevAngle - theta0) / (theta1 - theta0); return Maths.interpolationLinear(gain0, gain1, mu); } //============================================================================================================ /** * Get the SRGR coefficients for given zero Doppler time. * * @param zeroDopplerTime The zero Doppler time in MJD. * @return The SRGR coefficients. */ private AbstractMetadata.SRGRCoefficientList getSRGRCoefficientsForARangeLine(final double zeroDopplerTime) { if (srgrConvParams.length == 1) { return srgrConvParams[0]; } int idx = 0; for (int i = 0; i < srgrConvParams.length && zeroDopplerTime >= srgrConvParams[i].timeMJD; i++) { idx = i; } if (idx == srgrConvParams.length - 1) { idx--; } AbstractMetadata.SRGRCoefficientList srgrConvParam = new AbstractMetadata.SRGRCoefficientList(); srgrConvParam.timeMJD = zeroDopplerTime; srgrConvParam.ground_range_origin = srgrConvParams[idx].ground_range_origin; srgrConvParam.coefficients = new double[srgrConvParams[idx].coefficients.length]; final double mu = (zeroDopplerTime - srgrConvParams[idx].timeMJD) / (srgrConvParams[idx + 1].timeMJD - srgrConvParams[idx].timeMJD); for (int i = 0; i < srgrConvParam.coefficients.length; i++) { srgrConvParam.coefficients[i] = Maths.interpolationLinear(srgrConvParams[idx].coefficients[i], srgrConvParams[idx + 1].coefficients[i], mu); } return srgrConvParam; } /** * Compute slant range for given pixel. * * @param x The x coordinate of the pixel in the source image. * @param y The y coordinate of the pixel in the source image. * @param srgrConvParam The SRGR coefficients. * @return The slant range (in meters). */ private double computeSlantRange(int x, int y, AbstractMetadata.SRGRCoefficientList srgrConvParam, final TiePointInterpolator slantRangeTPGInterp) { if (srgrFlag) { // for ground detected product, compute slant range from SRGR coefficients return Maths.computePolynomialValue(x * rangeSpacing + srgrConvParam.ground_range_origin, srgrConvParam.coefficients); } else { // for slant range product, compute slant range from slant range time final double time = slantRangeTPGInterp.getPixelDouble(x, y, TiePointInterpolator.InterpMode.QUADRATIC) / Constants.oneBillion; //convert ns to s return time * Constants.halfLightSpeed; // in m } } /** * Compute distance from satelitte to the Earth centre (in meters). * * @param zeroDopplerTime The zero Doppler time (in days). * @param orbitStateVectors The orbit state vectors. * @return The distance. */ public static double computeSatelliteHeight(final double zeroDopplerTime, final OrbitStateVector[] orbitStateVectors) { // todo should use the 3rd state vector as suggested by the doc? int idx = 0; for (int i = 0; i < orbitStateVectors.length && zeroDopplerTime >= orbitStateVectors[i].time_mjd; i++) { idx = i; } final double xPos = orbitStateVectors[idx].x_pos; final double yPos = orbitStateVectors[idx].y_pos; final double zPos = orbitStateVectors[idx].z_pos; return Math.sqrt(xPos * xPos + yPos * yPos + zPos * zPos); } /** * Compute earth radius for all range lines (in m). */ private void computeEarthRadius() { final OperatorUtils.ImageGeoBoundary imageGeoBoundary = OperatorUtils .computeImageGeoBoundary(sourceProduct); latMax = imageGeoBoundary.latMax; final double latMin = imageGeoBoundary.latMin; final double minSpacing = Math.min(rangeSpacing, azimuthSpacing); double minAbsLat; if (latMin * latMax > 0) { minAbsLat = Math.min(Math.abs(latMin), Math.abs(latMax)) * Constants.DTOR; } else { minAbsLat = 0.0; } delLat = minSpacing / Constants.MeanEarthRadius * Constants.RTOD; final double delLon = minSpacing / (Constants.MeanEarthRadius * FastMath.cos(minAbsLat)) * Constants.RTOD; delLat = Math.min(delLat, delLon); final int h = (int) ((latMax - latMin) / delLat) + 1; earthRadius = new double[h + 1]; for (int i = 0; i <= h; i++) { earthRadius[i] = computeEarthRadius((float) (latMax - i * delLat), 0.0f); } } /** * Compute Earth radius (in meters) for given pixel in source image. * * @param lat The latitude of a given pixel in source image. * @param lon The longitude of a given pixel in source image. * @return The Earth radius. */ private static double computeEarthRadius(float lat, float lon) { final double[] xyz = new double[3]; GeoUtils.geo2xyz(lat, lon, 0.0, xyz, GeoUtils.EarthModel.WGS84); return Math.sqrt(xyz[0] * xyz[0] + xyz[1] * xyz[1] + xyz[2] * xyz[2]); } /** * Compute elevation angle (in degree). * * @param slantRange The slant range (in meters). * @param satelliteHeight The distance from satelitte to the Earth centre (in meters). * @param sceneToEarthCentre The distance from the backscatter element to the Earth centre (in meters). * @return The elevation angle. */ public static double computeElevationAngle(final double slantRange, final double satelliteHeight, final double sceneToEarthCentre) { return FastMath .acos((slantRange * slantRange + satelliteHeight * satelliteHeight - sceneToEarthCentre * sceneToEarthCentre) / (2 * slantRange * satelliteHeight)) * Constants.RTOD; } /** * Set the XCA file name. * This function is used by unit test only. * * @param xcaFileName The XCA file name. */ public void setExternalAntennaPatternFile(String xcaFileName) { String path = Settings.instance().get("AuxData.envisatAuxDataPath") + File.separator + xcaFileName; externalAuxFile = new File(path); if (!externalAuxFile.exists()) { throw new OperatorException("External antenna pattern file for unit test does not exist"); } } //==================================== pixel calibration used by RD ====================================== /** * Remove the antenna pattern compensation and range spreading loss applied to the pixel. * * @param x The x coordinate of the pixel in the source image. * @param y The y coordinate of the pixel in the source image. * @param v The pixel value. * @param bandPolar The polarization of the source band. * @param bandUnit The source band unit. * @param subSwathIndex The sub swath index for current pixel for wide swath product case. * @return The pixel value with antenna pattern compensation and range spreading loss correction removed. */ public double applyRetroCalibration(int x, int y, double v, String bandPolar, final Unit.UnitType bandUnit, int[] subSwathIndex) { if (!retroCalibrationFlag) { return v; } int bandPolarIdx = 0; if (bandPolar != null && mdsPolar[1] != null && mdsPolar[1].contains(bandPolar)) { bandPolarIdx = 1; } final double zeroDopplerTime = firstLineUTC + y * lineTimeInterval; final double satelitteHeight = computeSatelliteHeight(zeroDopplerTime, orbitStateVectors); AbstractMetadata.SRGRCoefficientList srgrConvParam = null; if (srgrFlag) { srgrConvParam = getSRGRCoefficientsForARangeLine(zeroDopplerTime); } final TiePointInterpolator slantRangeTPGInterp = new TiePointInterpolator(slantRangeTime); final double slantRange = computeSlantRange(x, y, srgrConvParam, slantRangeTPGInterp); // in m final double elevationAngle = computeElevationAngle(slantRange, satelitteHeight, avgSceneHeight + getEarthRadius(x, y)); double gain = 0.0; if (wideSwathProductFlag) { gain = getAntennaPatternGain(elevationAngle, bandPolarIdx, oldRefElevationAngle, oldAntennaPatternWideSwath, true, subSwathIndex); } else { gain = computeAntPatGain(elevationAngle, oldRefElevationAngle[0], oldAntennaPatternSingleSwath[bandPolarIdx]); } if (bandUnit == Unit.UnitType.AMPLITUDE) { return v * Math.sqrt(gain) * FastMath.pow(refSlantRange800km / slantRange, halfRangeSpreadingCompPower); // amplitude } else if (bandUnit == Unit.UnitType.AMPLITUDE_DB) { return 10.0 * Math.log10(FastMath.pow(10, v / 10.0) * Math.sqrt(gain) * FastMath.pow(refSlantRange800km / slantRange, halfRangeSpreadingCompPower)); } else if (bandUnit == Unit.UnitType.INTENSITY || bandUnit == Unit.UnitType.REAL || bandUnit == Unit.UnitType.IMAGINARY) { return v * gain * FastMath.pow(refSlantRange800km / slantRange, rangeSpreadingCompPower); // intensity } else if (bandUnit == Unit.UnitType.INTENSITY_DB) { return 10.0 * Math.log10(FastMath.pow(10, v / 10.0) * gain * FastMath.pow(refSlantRange800km / slantRange, rangeSpreadingCompPower)); } else { throw new OperatorException("Unknown band unit"); } } /** * Get antenna pattern gain value for given elevation angle. * * @param elevationAngle The elevation angle (in degree). * @param bandPolar The source band polarization index. * @param refElevationAngle The reference elevation angles for different swathes or sub swathes. * @param antennaPattern The antenna pattern array. For single swath product, it contains two 201-length arrays * corresponding to the two bands of different polarizations. For wide swath product, it * contains five 201-length arrays with each for a sub swath. * @param compSubSwathIdx The boolean flag indicating if sub swath index should be computed. * @param subSwathIndex The sub swath index for current pixel for wide swath product case. * @return The antenna pattern gain value. */ private static double getAntennaPatternGain(double elevationAngle, int bandPolar, double[] refElevationAngle, float[][] antennaPattern, boolean compSubSwathIdx, int[] subSwathIndex) { if (refElevationAngle.length == 1) { // single swath return computeAntPatGain(elevationAngle, refElevationAngle[0], antennaPattern[bandPolar]); } else { // wide swath if (compSubSwathIdx || subSwathIndex[0] == INVALID_SUB_SWATH_INDEX) { subSwathIndex[0] = findSubSwath(elevationAngle, refElevationAngle); } return computeAntPatGain(elevationAngle, refElevationAngle[subSwathIndex[0]], antennaPattern[subSwathIndex[0]]); } } /** * Apply calibrations to the given point. The following calibrations are included: calibration constant, * antenna pattern compensation, range spreading loss correction and incidence angle correction. * * @param v The pixel value. * @param slantRange The slant range (in m). * @param satelliteHeight The distance from satellite to earth centre (in m). * @param sceneToEarthCentre The distance from the backscattering element position to earth centre (in m). * @param localIncidenceAngle The local incidence angle (in degrees). * @param bandPolar The source band polarization index. * @param bandUnit The source band unit. * @param subSwathIndex The sub swath index for current pixel for wide swath product case. * @return The calibrated pixel value. */ public double applyCalibration(final double v, final double rangeIndex, final double azimuthIndex, final double slantRange, final double satelliteHeight, final double sceneToEarthCentre, final double localIncidenceAngle, final String bandPolar, final Unit.UnitType bandUnit, int[] subSwathIndex) { int bandPolarIdx = 0; if (bandPolar != null && mdsPolar[1] != null && mdsPolar[1].contains(bandPolar)) { bandPolarIdx = 1; } double sigma = 0.0; if (bandUnit == Unit.UnitType.AMPLITUDE) { sigma = v * v; } else if (bandUnit == Unit.UnitType.AMPLITUDE_DB) { sigma = FastMath.pow(10, v / 5.0); // convert dB to linear scale, then square } else if (bandUnit == Unit.UnitType.INTENSITY || bandUnit == Unit.UnitType.REAL || bandUnit == Unit.UnitType.IMAGINARY) { sigma = v; } else if (bandUnit == Unit.UnitType.INTENSITY_DB) { sigma = FastMath.pow(10, v / 10.0); // convert dB to linear scale } else { throw new OperatorException("Unknown band unit"); } sigma *= FastMath.sin(Math.abs(localIncidenceAngle) * Constants.DTOR) / newCalibrationConstant[bandPolarIdx]; if (multilookFlag && antElevCorrFlag) { // calibration constant and incidence angle corrections only return sigma; } if (auxFile == null || !auxFile.contains(CalibrationOp.PRODUCT_AUX)) { sigma *= FastMath.pow(slantRange / refSlantRange800km, rangeSpreadingCompPower); } if (applyAntennaPatternCorr) { final double elevationAngle = computeElevationAngle(slantRange, satelliteHeight, sceneToEarthCentre); // in degrees double gain; if (wideSwathProductFlag) { if (subSwathIndex[0] == INVALID_SUB_SWATH_INDEX) { // Rem(AP+RSL)/ApplyADC Op is used final TiePointInterpolator slantRangeTPGInterp = new TiePointInterpolator(slantRangeTime); computeSubSwathIndex(rangeIndex, azimuthIndex, newRefElevationAngle, subSwathIndex, slantRangeTPGInterp); } gain = getAntennaPatternGain(elevationAngle, bandPolarIdx, newRefElevationAngle, newAntennaPatternWideSwath, false, subSwathIndex); } else { gain = computeAntPatGain(elevationAngle, newRefElevationAngle[0], newAntennaPatternSingleSwath[bandPolarIdx]); } sigma /= gain; } return sigma; } private void computeSubSwathIndex(final double rangeIndex, final double azimuthIndex, final double[] refElevationAngle, int[] subSwathIndex, final TiePointInterpolator slantRangeTPGInterp) { final int x = (int) (rangeIndex + 0.5); final int y = (int) (azimuthIndex + 0.5); final double zeroDopplerTime = firstLineUTC + y * lineTimeInterval; final double satelitteHeight = computeSatelliteHeight(zeroDopplerTime, orbitStateVectors); AbstractMetadata.SRGRCoefficientList srgrConvParam = null; if (srgrFlag) { srgrConvParam = getSRGRCoefficientsForARangeLine(zeroDopplerTime); } final double slantRange = computeSlantRange(x, y, srgrConvParam, slantRangeTPGInterp); // in m final double elevationAngle = computeElevationAngle(slantRange, satelitteHeight, avgSceneHeight + getEarthRadius(x, y)); subSwathIndex[0] = findSubSwath(elevationAngle, refElevationAngle); } public void removeFactorsForCurrentTile(Band targetBand, Tile targetTile, String srcBandName) throws OperatorException { if (!srgrFlag) { return; } final Rectangle targetTileRectangle = targetTile.getRectangle(); final int x0 = targetTileRectangle.x; final int y0 = targetTileRectangle.y; final int w = targetTileRectangle.width; final int h = targetTileRectangle.height; //System.out.println("x0 = " + x0 + ", y0 = " + y0 + ", w = " + w + ", h = " + h); final Band sourceBand = sourceProduct.getBand(srcBandName); final Tile sourceTile = calibrationOp.getSourceTile(sourceBand, targetTileRectangle); final ProductData srcData = sourceTile.getDataBuffer(); final Unit.UnitType bandUnit = Unit.getUnitType(sourceBand); final ProductData trgData = targetTile.getDataBuffer(); final String pol = OperatorUtils.getBandPolarization(targetBand.getName(), absRoot); int prodBand = 0; if (pol != null && mdsPolar[1] != null && mdsPolar[1].contains(pol)) { prodBand = 1; } final double[][] targetTileNewAntPat = new double[h][w]; final double[][] targetTileSlantRange = new double[h][w]; final double[][] targetTileOldAntPat = new double[h][w]; final TiePointInterpolator slantRangeTPGInterp = new TiePointInterpolator(slantRangeTime); if (wideSwathProductFlag) { computeWideSwathAntennaPatternForCurrentTile(x0, y0, w, h, targetTileOldAntPat, targetTileNewAntPat, targetTileSlantRange, slantRangeTPGInterp); } else { computeSingleSwathAntennaPatternForCurrentTile(x0, y0, w, h, targetTileOldAntPat, targetTileNewAntPat, targetTileSlantRange, prodBand, slantRangeTPGInterp); } final int maxY = y0 + h; final int maxX = x0 + w; double gain, slantRange, v; for (int y = y0, yy = 0; y < maxY; ++y, ++yy) { for (int x = x0, xx = 0; x < maxX; ++x, ++xx) { v = srcData.getElemDoubleAt(sourceTile.getDataBufferIndex(x, y)); gain = targetTileOldAntPat[yy][xx]; slantRange = targetTileSlantRange[yy][xx]; if (bandUnit == Unit.UnitType.AMPLITUDE) { v *= Math.sqrt(gain) * FastMath.pow(refSlantRange800km / slantRange, 0.5 * rangeSpreadingCompPower); } else if (bandUnit == Unit.UnitType.AMPLITUDE_DB) { v = FastMath.pow(10, v / 10.0) * Math.sqrt(gain) * FastMath.pow(refSlantRange800km / slantRange, 0.5 * rangeSpreadingCompPower); v = 10.0 * Math.log10(v); } else if (bandUnit == Unit.UnitType.INTENSITY) { v *= gain * FastMath.pow(refSlantRange800km / slantRange, rangeSpreadingCompPower); } else if (bandUnit == Unit.UnitType.INTENSITY_DB) { v = FastMath.pow(10, v / 10.0) * gain * FastMath.pow(refSlantRange800km / slantRange, rangeSpreadingCompPower); v = 10.0 * Math.log10(v); } else { throw new OperatorException("Unknown band unit"); } trgData.setElemDoubleAt(targetTile.getDataBufferIndex(x, y), v); } } } }