Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3686ba4
Carrier phase functons & pybinds
Tyler-g-hudson Feb 26, 2024
23e741a
Addbindings templates
Tyler-g-hudson Feb 27, 2024
e3d32ba
call addbindings temlates in image.cpp pybind
Tyler-g-hudson Feb 27, 2024
b7d9f54
template in resample.h pybind
Tyler-g-hudson Feb 27, 2024
f6f4ae6
Added modulate/demodulate functions for benchmarking
Tyler-g-hudson Feb 29, 2024
bb5b02d
Parameter name fix
Tyler-g-hudson Feb 29, 2024
45a5bf3
Incorporated into resample_slc_blocks
Tyler-g-hudson Feb 29, 2024
1d36c6a
clearer input parameters for modulate() on resample_to_coords
Tyler-g-hudson Feb 29, 2024
524a843
Docstring update
Tyler-g-hudson Mar 1, 2024
7730f93
Changed parameter and function names
Tyler-g-hudson Mar 11, 2024
3bc4beb
Made input types consistent
Tyler-g-hudson Mar 21, 2024
6656baf
Name changes in resample_slc
Tyler-g-hudson Mar 27, 2024
ecb4356
docstrings and fixes for modulate.py
Tyler-g-hudson Mar 27, 2024
36623e2
C++ documentation changes
Tyler-g-hudson Mar 27, 2024
15c5b9d
output remodulation fix in resample_slc
Tyler-g-hudson Mar 27, 2024
e6e7b86
Moved modulate bindings to their own python submodule
Tyler-g-hudson Mar 27, 2024
eb27a85
Used radar grid blocking, moved Modulate functions into their own nam…
Tyler-g-hudson Mar 27, 2024
85bb655
Made the "at coords" functions private in C++
Tyler-g-hudson Mar 27, 2024
b75ceb2
Separated the modulation code into its own namespaces and modules
Tyler-g-hudson Mar 27, 2024
5ae20e7
Removed an excess function declaration from pybind Resample.h
Tyler-g-hudson Mar 27, 2024
22d62f3
Fixed a C++ bug that would cause confusing errors on out-of-frame ind…
Tyler-g-hudson Apr 1, 2024
5fe5b81
Added a battery of tests that check each of the modulation functions.
Tyler-g-hudson Apr 1, 2024
8795217
Added a fill_value parameter to the functions to avoid incomplete mod…
Tyler-g-hudson Apr 1, 2024
57be653
Commenting, docstring update, switched to use kwargs
Tyler-g-hudson Apr 2, 2024
bad39fb
Further annotation improvements in unit tests
Tyler-g-hudson Apr 2, 2024
2650225
Fixed an error
Tyler-g-hudson Apr 2, 2024
eb831bd
Improved docstring description for "fill_value" argument
Tyler-g-hudson Apr 2, 2024
98b4dbe
Added a range carrier to the tests to test in both dimensions
Tyler-g-hudson Apr 2, 2024
70ef196
small additional change to interface
Tyler-g-hudson Apr 2, 2024
2398ea4
rebasing fixes
Tyler-g-hudson Oct 7, 2024
fdd27c0
conform modulate function interfaces to be similar to resample_to_coords
Tyler-g-hudson Oct 10, 2024
99c2929
Small documentation and error reporting improvements
Tyler-g-hudson Oct 10, 2024
9d02755
modulate -> modulate_carrier_phase, get_modulation_phase -> get_carri…
Tyler-g-hudson Feb 19, 2025
b942af2
Merge branch 'isce-framework:develop' into resamp-modulation
Tyler-g-hudson Mar 16, 2026
6dc38a8
Fix error from rebase: `output_block` not correctly declared
Tyler-g-hudson Mar 16, 2026
1e12dd6
Fix broken import in test
Tyler-g-hudson Mar 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cxx/isce3/Headers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ geometry/metadataCubes.h
geogrid/getRadarGrid.h
geogrid/relocateRaster.h
image/forward.h
image/Modulate.h
image/Resample.h
image/ResampSlc.h
image/ResampSlc.icc
Expand Down
1 change: 1 addition & 0 deletions cxx/isce3/Sources.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ geometry/TopoLayers.cpp
geometry/metadataCubes.cpp
geogrid/getRadarGrid.cpp
geogrid/relocateRaster.cpp
image/Modulate.cpp
image/Resample.cpp
image/ResampSlc.cpp
io/gdal/Dataset.cpp
Expand Down
11 changes: 11 additions & 0 deletions cxx/isce3/core/Poly2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,17 @@ class isce3::core::Poly2d {
/**Get coefficient by indices*/
inline double getCoeff(int row, int col) const;

/**
* Check if point resides in domain of Poly2d. Added for interoperability with
* LUT2d, but always assumed to be true.
*/
inline bool contains(double y, double x) const {
if (std::isnan(y) || std::isnan(x)){
return false;
}
return true;
}

/**Evaluate polynomial at given y/azimuth/row ,x/range/col*/
double eval(double y, double x) const;

Expand Down
250 changes: 250 additions & 0 deletions cxx/isce3/image/Modulate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
#include "Modulate.h"

#include <isce3/core/Constants.h>
#include <isce3/core/LUT2d.h>
#include <isce3/core/Poly2d.h>

namespace isce3::image::modulate {


template<typename AzRgFunc = isce3::core::Poly2d>
std::complex<double> _getPixelCarrierPhase(
const double azimuth,
const double range,
const AzRgFunc& carrier_phase,
bool conjugate = false
) {
// Evaluate the pixel's carrier phase
// unit: phase (radians)
const double phase = carrier_phase.eval(azimuth, range);

// Convert the carrier phase into a unit phasor (i.e. an angle on the unit
// circle in the complex plane).
// unit: unitless (complex)
if(conjugate){
return std::complex<double>(std::cos(phase), -std::sin(phase));
} else {
return std::complex<double>(std::cos(phase), std::sin(phase));
}
}


template<typename AzRgFunc>
void getCarrierPhase(
ArrayRef2D<std::complex<float>> out,
const AzRgFunc& carrier_phase,
const isce3::product::RadarGridParameters& radar_grid,
const bool conjugate,
const std::complex<float> fill_value
)
{
// unit: azimuth row indices (int)
const size_t phase_block_length = out.rows();
// unit: range column indices (int)
const size_t phase_block_width = out.cols();

// remove carrier from radar data
#pragma omp parallel for collapse(2)
for (size_t az_index = 0; az_index < phase_block_length; ++az_index) {
for (size_t rg_index = 0; rg_index < phase_block_width; ++rg_index) {
// unit: time (seconds)
const double azimuth =
radar_grid.sensingStart() + az_index / radar_grid.prf();

// unit: distance (meters)
const double range = radar_grid.startingRange() +
rg_index * radar_grid.rangePixelSpacing();

if(not carrier_phase.contains(azimuth, range)){
out(az_index, rg_index) = fill_value;
continue;
}

// Get the carrier unit phasor for this pixel
const auto phasor = _getPixelCarrierPhase(
azimuth, range, carrier_phase, conjugate
);

// Write the phasor into the output data block
out(az_index, rg_index) = phasor;
}
} // end multithreaded block
}


template<typename AzRgFunc>
void _getCarrierPhaseAtCoords(
ArrayRef2D<std::complex<float>> out,
const AzRgFunc& carrier_phase,
const isce3::product::RadarGridParameters& radar_grid,
const ConstArrayRef2D<double> azimuth_indices,
const ConstArrayRef2D<double> range_indices,
const bool conjugate,
const std::complex<float> fill_value
)
{
const size_t outWidth = out.cols();
const size_t outLength = out.rows();

#pragma omp parallel for collapse(2)
for (size_t az_index = 0; az_index < outLength; ++az_index){
for (size_t rg_index = 0; rg_index < outWidth; ++rg_index){

// Get the indices on the output grid corresponding to these indices
// on the input grid overall.
const double az_carrier_index = azimuth_indices(az_index, rg_index);
const double rg_carrier_index = range_indices(az_index, rg_index);

// Azimuth time at the current output pixel
const double azimuth = radar_grid.sensingStart() + az_carrier_index /
radar_grid.prf();

// Slant Range at the current output pixel
const double range = radar_grid.startingRange() + rg_carrier_index *
radar_grid.rangePixelSpacing();

if(not carrier_phase.contains(azimuth, range)){
out(az_index, rg_index) = fill_value;
continue;
}

// Get the carrier phasor for this pixel
const auto phasor = _getPixelCarrierPhase(
azimuth, range, carrier_phase, conjugate
);

// Write the phasor into the output data block
out(az_index, rg_index) = phasor;
}
} // end multithreaded block
}


template<typename AzRgFunc>
void modulateCarrierPhase(
ArrayRef2D<std::complex<float>> slc_data_block,
const AzRgFunc& carrier_phase,
const isce3::product::RadarGridParameters& radar_grid,
const bool conjugate,
const std::complex<float> fill_value
)
{
// unit: azimuth row indices (int)
const size_t phase_block_length = slc_data_block.rows();
// unit: range column indices (int)
const size_t phase_block_width = slc_data_block.cols();

// remove carrier from radar data
#pragma omp parallel for collapse(2)
for ( size_t az_index = 0; az_index < phase_block_length; ++az_index) {
for (size_t rg_index = 0; rg_index < phase_block_width; ++rg_index) {
// unit: time (seconds)
const double azimuth =
radar_grid.sensingStart() + az_index / radar_grid.prf();

// unit: distance (meters)
const double range = radar_grid.startingRange() +
rg_index * radar_grid.rangePixelSpacing();

if(not carrier_phase.contains(azimuth, range)){
slc_data_block(az_index, rg_index) = fill_value;
continue;
}

// Get the carrier phasor for this pixel
const auto phasor = _getPixelCarrierPhase(
azimuth, range, carrier_phase, conjugate
);

// Modulate the phasor into the output data block
slc_data_block(az_index, rg_index) *= phasor;
}
} // end multithreaded block
}


template<typename AzRgFunc>
void _modulateCarrierPhaseAtCoords(
ArrayRef2D<std::complex<float>> slc_data_block,
const AzRgFunc& carrier_phase,
const isce3::product::RadarGridParameters& radar_grid,
const ConstArrayRef2D<double> azimuth_indices,
const ConstArrayRef2D<double> range_indices,
const bool conjugate,
const std::complex<float> fill_value
)
{
const size_t outWidth = slc_data_block.cols();
const size_t outLength = slc_data_block.rows();

#pragma omp parallel for collapse(2)
for (size_t az_index = 0; az_index < outLength; ++az_index){
for (size_t rg_index = 0; rg_index < outWidth; ++rg_index){

// Get the indices on the output grid corresponding to these indices
// on the input grid overall.
const double az_carrier_index = azimuth_indices(az_index, rg_index);
const double rg_carrier_index = range_indices(az_index, rg_index);

// Azimuth time at the current output pixel
const double azimuth = radar_grid.sensingStart() + az_carrier_index /
radar_grid.prf();

// Slant Range at the current output pixel
const double range = radar_grid.startingRange() + rg_carrier_index *
radar_grid.rangePixelSpacing();

if(not carrier_phase.contains(azimuth, range)){
slc_data_block(az_index, rg_index) = fill_value;
continue;
}

// Get the carrier phasor for this pixel
const auto phasor = _getPixelCarrierPhase(
azimuth, range, carrier_phase, conjugate
);

// Modulate the phasor into the output data block
slc_data_block(az_index, rg_index) *= phasor;
}
} // end multithreaded block
}


#define EXPLICIT_INSTANTIATION(AzRgFunc) \
template void getCarrierPhase( \
ArrayRef2D<std::complex<float>> out, \
const AzRgFunc& carrier_phase, \
const isce3::product::RadarGridParameters& radar_grid, \
const bool conjugate, \
const std::complex<float> fill_value \
); \
template void _getCarrierPhaseAtCoords( \
ArrayRef2D<std::complex<float>> out, \
const AzRgFunc& carrier_phase, \
const isce3::product::RadarGridParameters& radar_grid, \
const ConstArrayRef2D<double> azimuth_indices, \
const ConstArrayRef2D<double> range_indices, \
const bool conjugate, \
const std::complex<float> fill_value \
); \
template void modulateCarrierPhase( \
ArrayRef2D<std::complex<float>> slc_data_block, \
const AzRgFunc& carrier_phase, \
const isce3::product::RadarGridParameters& radar_grid, \
const bool conjugate, \
const std::complex<float> fill_value \
); \
template void _modulateCarrierPhaseAtCoords( \
ArrayRef2D<std::complex<float>> slc_data_block, \
const AzRgFunc& carrier_phase, \
const isce3::product::RadarGridParameters& radar_grid, \
const ConstArrayRef2D<double> azimuth_indices, \
const ConstArrayRef2D<double> range_indices, \
const bool conjugate, \
const std::complex<float> fill_value \
)
EXPLICIT_INSTANTIATION(isce3::core::LUT2d<double>);
EXPLICIT_INSTANTIATION(isce3::core::Poly2d);

} // end namespace isce3::image::modulate
Loading
Loading