/* * 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.sentinel1.gpf.experimental; import com.bc.ceres.core.ProgressMonitor; import org.esa.s1tbx.insar.gpf.support.Sentinel1Utils; import org.esa.snap.core.datamodel.Band; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.datamodel.ProductData; import org.esa.snap.core.gpf.Operator; import org.esa.snap.core.gpf.OperatorException; import org.esa.snap.core.gpf.OperatorSpi; import org.esa.snap.core.gpf.Tile; import org.esa.snap.core.gpf.annotations.OperatorMetadata; import org.esa.snap.core.gpf.annotations.SourceProduct; import org.esa.snap.core.gpf.annotations.TargetProduct; import org.esa.snap.engine_utilities.eo.Constants; import org.esa.snap.engine_utilities.gpf.InputProductValidator; import org.esa.snap.engine_utilities.gpf.OperatorUtils; import org.esa.snap.engine_utilities.gpf.TileIndex; import java.awt.*; import java.util.Map; /** * Compute deramp and demodulation phases. */ @OperatorMetadata(alias = "Deramp-Demod-Phase", category = "Radar/Coregistration/S-1 TOPS Coregistration", authors = "Jun Lu, Luis Veci", version = "1.0", copyright = "Copyright (C) 2014 by Array Systems Computing Inc.", description = "Compute Deramp and Demodulation Phases", internal = true) public final class DerampDemodPhaseOp extends Operator { @SourceProduct(alias = "source") private Product sourceProduct; @TargetProduct private Product targetProduct; private Sentinel1Utils su = null; private Sentinel1Utils.SubSwathInfo[] subSwath = null; private int subSwathIndex = 0; private int sourceImageWidth = 0; private int sourceImageHeight = 0; /** * Default constructor. The graph processing framework * requires that an operator has a default constructor. */ public DerampDemodPhaseOp() { } /** * Compute range dependent Doppler rate Kt(r) for given burst. * @return The Doppler rate array. */ public float[] computeDopplerRate(final int burstIndex) throws Exception { float[] kt = new float[sourceImageWidth]; for (int x = 0; x < sourceImageWidth; x++) { kt[x] = (float)subSwath[0].dopplerRate[burstIndex][x]; } return kt; } /** * Compute range dependent Doppler centroid fDC(r) for given burst. * @return The Doppler centroid array. */ public float[] computeDopplerCentroid(final int burstIndex) throws Exception { float[] fdc = new float[sourceImageWidth]; for (int x = 0; x < sourceImageWidth; x++) { fdc[x] = (float)subSwath[0].dopplerCentroid[burstIndex][x]; } return fdc; } /** * Compute slant range. * @return The slant range array. */ public float[] computeSlantRange() throws Exception { float[] slr = new float[sourceImageWidth]; for (int x = 0; x < sourceImageWidth; x++) { slr[x] = (float)(subSwath[0].slrTimeToFirstPixel * Constants.lightSpeed + x * subSwath[subSwathIndex - 1].rangePixelSpacing); } return slr; } /** * Compute reference time. * @return The reference time array. */ public float[] computeReferenceTime(final int burstIndex) throws Exception { float[] tref = new float[sourceImageWidth]; for (int x = 0; x < sourceImageWidth; x++) { tref[x] = (float)subSwath[0].referenceTime[burstIndex][x]; } return tref; } /** * Initializes this operator and sets the one and only target product. * <p>The target product can be either defined by a field of type {@link Product} annotated with the * {@link TargetProduct TargetProduct} annotation or * by calling {@link #setTargetProduct} method.</p> * <p>The framework calls this method after it has created this operator. * Any client code that must be performed before computation of tile data * should be placed here.</p> * * @throws OperatorException If an error occurs during operator initialisation. * @see #getTargetProduct() */ @Override public void initialize() throws OperatorException { try { final InputProductValidator validator = new InputProductValidator(sourceProduct); validator.checkIfSARProduct(); validator.checkIfSentinel1Product(); validator.checkIfSLC(); su = new Sentinel1Utils(sourceProduct); su.computeDopplerRate(); su.computeReferenceTime(); subSwath = su.getSubSwath(); final String[] subSwathNames = su.getSubSwathNames(); if (subSwathNames.length != 1) { throw new OperatorException("Split product is expected."); } subSwathIndex = 1; // subSwathIndex is always 1 because of split product final String[] polarizations = su.getPolarizations(); if (polarizations.length != 1) { throw new OperatorException("Split product with one polarization is expected."); } createTargetProduct(); } catch (Throwable e) { throw new OperatorException(e.getMessage()); } } /** * Create target product. */ private void createTargetProduct() { sourceImageWidth = sourceProduct.getSceneRasterWidth(); sourceImageHeight = sourceProduct.getSceneRasterHeight(); targetProduct = new Product( sourceProduct.getName(), sourceProduct.getProductType(), sourceImageWidth, sourceImageHeight); final Band derampPhaseBand = new Band( "derampPhase", ProductData.TYPE_FLOAT32, sourceImageWidth, sourceImageHeight); derampPhaseBand.setUnit("radian"); targetProduct.addBand(derampPhaseBand); final Band demodPhaseBand = new Band( "demodPhase", ProductData.TYPE_FLOAT32, sourceImageWidth, sourceImageHeight); demodPhaseBand.setUnit("radian"); targetProduct.addBand(demodPhaseBand); } /** * 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 targetTileMap The target tiles associated with all target bands to be computed. * @param targetRectangle The rectangle of target tile. * @param pm A progress monitor which should be used to determine computation cancelation requests. * @throws OperatorException * If an error occurs during computation of the target raster. */ @Override public void computeTileStack(Map<Band, Tile> targetTileMap, Rectangle targetRectangle, ProgressMonitor pm) throws OperatorException { try { final int tx0 = targetRectangle.x; final int ty0 = targetRectangle.y; final int tw = targetRectangle.width; final int th = targetRectangle.height; final int tyMax = ty0 + th; //System.out.println("tx0 = " + tx0 + ", ty0 = " + ty0 + ", tw = " + tw + ", th = " + th); for (int burstIndex = 0; burstIndex < subSwath[subSwathIndex - 1].numOfBursts; burstIndex++) { final int firstLineIdx = burstIndex*subSwath[subSwathIndex - 1].linesPerBurst; final int lastLineIdx = firstLineIdx + subSwath[subSwathIndex - 1].linesPerBurst - 1; if (tyMax <= firstLineIdx || ty0 > lastLineIdx) { continue; } final int ntx0 = tx0; final int ntw = tw; final int nty0 = Math.max(ty0, firstLineIdx); final int ntyMax = Math.min(tyMax, lastLineIdx + 1); final int nth = ntyMax - nty0; final Rectangle subTargetRectangle = new Rectangle(ntx0, nty0, ntw, nth); //System.out.println("burstIndex = " + burstIndex + ": ntx0 = " + ntx0 + ", nty0 = " + nty0 + ", ntw = " + ntw + ", nth = " + nth); computeDerampDemodPhase( subSwathIndex, burstIndex, subTargetRectangle, targetTileMap, pm); } } catch (Throwable e) { throw new OperatorException(e.getMessage()); } } private void computeDerampDemodPhase(final int subSwathIndex, final int burstIndex, final Rectangle rectangle, final Map<Band, Tile> targetTileMap, ProgressMonitor pm) throws Exception { try { final int x0 = rectangle.x; final int y0 = rectangle.y; final int w = rectangle.width; final int h = rectangle.height; final int xMax = x0 + w; final int yMax = y0 + h; final double[][] derampPhase = su.computeDerampPhase(subSwath, subSwathIndex, burstIndex, rectangle); final double[][] demodPhase = su.computeDemodPhase(subSwath, subSwathIndex, burstIndex, rectangle); final Band derampPhaseBand = targetProduct.getBand("derampPhase"); final Band demodPhaseBand = targetProduct.getBand("demodPhase"); final Tile tgtTileDerampPhase = targetTileMap.get(derampPhaseBand); final Tile tgtTileDemodPhase = targetTileMap.get(demodPhaseBand); final ProductData tgtBufferDerampPhase = tgtTileDerampPhase.getDataBuffer(); final ProductData tgtBufferDemodPhase = tgtTileDemodPhase.getDataBuffer(); final TileIndex tgtIndex = new TileIndex(tgtTileDerampPhase); for (int y = y0; y < yMax; y++) { final int yy = y - y0; tgtIndex.calculateStride(y); for (int x = x0; x < xMax; x++) { final int xx = x - x0; final int idx = tgtIndex.getIndex(x); tgtBufferDerampPhase.setElemFloatAt(idx, (float)derampPhase[yy][xx]); tgtBufferDemodPhase.setElemFloatAt(idx, (float)demodPhase[yy][xx]); } } } catch (Throwable e) { OperatorUtils.catchOperatorException("computeDerampDemodPhase", e); } } /** * The SPI is used to register this operator in the graph processing framework * via the SPI configuration file * {@code META-INF/services/org.esa.snap.core.gpf.OperatorSpi}. * This class may also serve as a factory for new operator instances. * * @see OperatorSpi#createOperator() * @see OperatorSpi#createOperator(java.util.Map, java.util.Map) */ public static class Spi extends OperatorSpi { public Spi() { super(DerampDemodPhaseOp.class); } } }