PyGeoFetch 1.9.6__tar.gz → 1.9.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/CHANGELOG.md +6 -6
- {pygeofetch-1.9.6/pygeofetch.egg-info → pygeofetch-1.9.7}/PKG-INFO +1 -1
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/__init__.py +2 -2
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/cli/main.py +2 -2
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/esd.py +45 -3
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/flatearth.py +34 -1
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/interferogram.py +143 -9
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/meta.yaml +1 -1
- {pygeofetch-1.9.6 → pygeofetch-1.9.7/pygeofetch.egg-info}/PKG-INFO +1 -1
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pyproject.toml +1 -1
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_flat_earth_phase.py +84 -14
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/LICENSE +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/MANIFEST.in +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/README.md +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/cli/__init__.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/cli/auth_commands.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/cli/config_commands.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/cli/download_commands.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/cli/index_commands.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/cli/pipeline_process_commands.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/cli/postprocess_commands.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/cli/preprocess_commands.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/cli/sar_commands.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/cli/search_commands.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/config/__init__.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/config/defaults.yaml +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/config/settings.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/core/__init__.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/core/authenticator.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/core/cache_manager.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/core/downloader.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/core/engine.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/core/logging.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/core/orbits.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/core/scheduler.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/core/searcher.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/__init__.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/annotation.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/atmosphere.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/coregister.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/deburst.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/extraction.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/geolocation.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/gpu.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/ionosphere.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/splitspectrum_diagnostic.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/synthetic.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/timeseries.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/unwrap.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/validate.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/visualize.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/insar/workflow.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/models/__init__.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/models/download_task.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/models/satellite_data.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/models/search_query.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/models/user_auth.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/processing/__init__.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/processing/base.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/processing/batch.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/processing/indices.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/processing/pipeline.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/processing/postprocessor.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/processing/preprocessor.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/processing/sar.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/processor/__init__.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/processor/indices.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/processor/landsat.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/processor/loader.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/processor/stacker.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/processor/timeseries.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/__init__.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/airbus_oneatlas.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/alaska_satellite_facility.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/aws_earth.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/base.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/copernicus.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/digitalglobe.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/earth_explorer_additional.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/element84.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/eodag_provider.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/esa_scihub.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/geoserver_generic.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/google_earth_engine.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/inpe_cbers.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/isro_bhuvan.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/jaxa_earth.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/maxar_gbdx.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/nasa_earthdata.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/nasa_earthdata_cloud.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/noaa_big_data.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/opentopography.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/planet.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/planetary_computer.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/sentinel_hub.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/terrabotics.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/providers/usgs.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/sar/__init__.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/sar/_native.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/sar/_ost.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/sar/_sarxarray.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/sar/extraction.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/sar/processor.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/utils/__init__.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/utils/file_utils.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/utils/gcp_recovery.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/utils/geo_utils.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/utils/logging_setup.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/utils/retry_handler.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/utils/validators.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/viz/__init__.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/viz/map.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch/viz/plot.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch.egg-info/SOURCES.txt +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch.egg-info/dependency_links.txt +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch.egg-info/entry_points.txt +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch.egg-info/not-zip-safe +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch.egg-info/requires.txt +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/pygeofetch.egg-info/top_level.txt +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/setup.cfg +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_atmospheric_circular_regression.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_burst_metadata.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_burst_processing_integration.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_era5_atmospheric_correction.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_extraction.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_gcp_reprojection.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_insar.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_ionosphere.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_models.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_pgv_integration_contract.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_provider_geometry_audit (1).py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_provider_geometry_audit.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_provider_scene_disambiguation.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_providers.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_search_download.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_splitspectrum_diagnostic.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_utils.py +0 -0
- {pygeofetch-1.9.6 → pygeofetch-1.9.7}/tests/test_viz_plot.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
All notable changes to PyGeoFetch are documented here.
|
|
4
|
-
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.9.
|
|
4
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.9.7/).
|
|
5
5
|
|
|
6
|
-
## [1.9.
|
|
6
|
+
## [1.9.7] — 2026-07-29
|
|
7
7
|
|
|
8
8
|
### Added
|
|
9
9
|
- Major SNAPHU unwrapping improvements for urban InSAR applications
|
|
@@ -51,7 +51,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.9.6/).
|
|
|
51
51
|
|
|
52
52
|
### Migration Guide
|
|
53
53
|
```python
|
|
54
|
-
# Before (v1.9.
|
|
54
|
+
# Before (v1.9.7)
|
|
55
55
|
unwrapper = PhaseUnwrapper(cost_mode="defo", init_method="mcf")
|
|
56
56
|
unwrapped = unwrapper.unwrap(phase, coherence, nlooks=2.0)
|
|
57
57
|
|
|
@@ -72,7 +72,7 @@ unwrapped = unwrapper.unwrap(
|
|
|
72
72
|
|
|
73
73
|
---
|
|
74
74
|
|
|
75
|
-
## [1.9.
|
|
75
|
+
## [1.9.7] — 2026-07-12
|
|
76
76
|
|
|
77
77
|
### Added
|
|
78
78
|
- Federated search across 22+ satellite data providers
|
|
@@ -188,8 +188,8 @@ unwrapped = unwrapper.unwrap(
|
|
|
188
188
|
|
|
189
189
|
| Version | Date | Key Changes |
|
|
190
190
|
|---------|------------|-------------|
|
|
191
|
-
| **1.9.
|
|
192
|
-
| **1.9.
|
|
191
|
+
| **1.9.7** | 2026-07-29 | SNAPHU unwrapping fixes, 87.1% reliable pixels |
|
|
192
|
+
| **1.9.7** | 2026-07-12 | 22 providers, 17 indices, Sentinel-1C/D |
|
|
193
193
|
| **1.9.2** | 2026-06-15 | Batch processing, parallel downloads |
|
|
194
194
|
| **1.9.1** | 2026-05-20 | 3D terrain, SNAPHU integration, time series |
|
|
195
195
|
| **1.9.0** | 2026-04-25 | Initial public release |
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
PyGeoFetch v1.9.
|
|
2
|
+
PyGeoFetch v1.9.7 — Universal Satellite Data Pipeline.
|
|
3
3
|
|
|
4
4
|
Unified access to 22+ satellite data providers with a consistent
|
|
5
5
|
Python API and CLI. Supports authentication management, federated search,
|
|
@@ -30,7 +30,7 @@ CLI::
|
|
|
30
30
|
pygeofetch doctor
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
|
-
__version__ = "1.9.
|
|
33
|
+
__version__ = "1.9.7"
|
|
34
34
|
__author__ = "PyGeoFetch Contributors"
|
|
35
35
|
__license__ = "MIT"
|
|
36
36
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
PyGeoFetch CLI v1.9.
|
|
2
|
+
PyGeoFetch CLI v1.9.7 main entry point.
|
|
3
3
|
|
|
4
4
|
Full command reference:
|
|
5
5
|
pygeofetch auth — credential management
|
|
@@ -71,7 +71,7 @@ def cli(
|
|
|
71
71
|
) -> None:
|
|
72
72
|
"""
|
|
73
73
|
\b
|
|
74
|
-
PyGeoFetch v1.9.
|
|
74
|
+
PyGeoFetch v1.9.7 — Universal Satellite Data Pipeline
|
|
75
75
|
===========================================================
|
|
76
76
|
Unified access to 22+ satellite data providers.
|
|
77
77
|
|
|
@@ -172,6 +172,20 @@ def estimate_esd_shift_per_burst_overlap(
|
|
|
172
172
|
overlaps = compute_overlap_row_ranges(swath_timing, azimuth_time_interval_s)
|
|
173
173
|
lines_per_burst = swath_timing.lines_per_burst
|
|
174
174
|
per_overlap_shifts: List[Optional[float]] = []
|
|
175
|
+
# Real, confirmed diagnostic gap fixed here: the two real, genuinely
|
|
176
|
+
# different reasons an overlap gets skipped (it fell entirely outside
|
|
177
|
+
# the given, possibly-cropped arrays -- an AOI/cropping issue -- vs.
|
|
178
|
+
# it was inside the arrays but below coherence_threshold -- a real
|
|
179
|
+
# decorrelation issue, e.g. vegetated/mountainous terrain) were
|
|
180
|
+
# previously indistinguishable from the outside: individual reasons
|
|
181
|
+
# were only logged at DEBUG level, and the final summary warning
|
|
182
|
+
# collapsed both into one "outside the given arrays, or were below
|
|
183
|
+
# coherence_threshold" message. That ambiguity is exactly what made
|
|
184
|
+
# it impossible to tell, from a normal INFO-level log, whether a
|
|
185
|
+
# given real run's ESD failure was fixable by widening the AOI or
|
|
186
|
+
# was a genuine reflection of the terrain -- tracked explicitly here
|
|
187
|
+
# instead of guessed at after the fact.
|
|
188
|
+
skip_reasons: List[str] = []
|
|
175
189
|
|
|
176
190
|
for i, ((bw_start, bw_end), (fw_start, fw_end)) in enumerate(overlaps):
|
|
177
191
|
bw_full_scene_start = i * lines_per_burst - row_offset + bw_start
|
|
@@ -184,11 +198,13 @@ def estimate_esd_shift_per_burst_overlap(
|
|
|
184
198
|
|
|
185
199
|
if bw_clip_start >= bw_clip_end or fw_clip_start >= fw_clip_end:
|
|
186
200
|
per_overlap_shifts.append(None)
|
|
201
|
+
skip_reasons.append("outside_array_bounds")
|
|
187
202
|
continue # this overlap falls entirely outside the real, given (possibly cropped) arrays
|
|
188
203
|
|
|
189
204
|
n_common = min(bw_clip_end - bw_clip_start, fw_clip_end - fw_clip_start)
|
|
190
205
|
if n_common < 1:
|
|
191
206
|
per_overlap_shifts.append(None)
|
|
207
|
+
skip_reasons.append("outside_array_bounds")
|
|
192
208
|
continue
|
|
193
209
|
|
|
194
210
|
ref_bw = ref_complex[bw_clip_start:bw_clip_start + n_common]
|
|
@@ -256,19 +272,45 @@ def estimate_esd_shift_per_burst_overlap(
|
|
|
256
272
|
i, i + 1, 100 * valid.mean(), coherence_threshold,
|
|
257
273
|
)
|
|
258
274
|
per_overlap_shifts.append(None)
|
|
275
|
+
skip_reasons.append(f"low_coherence({100 * valid.mean():.0f}%)")
|
|
259
276
|
continue
|
|
260
277
|
|
|
261
278
|
double_diff = igram_fw * np.conj(igram_bw)
|
|
262
279
|
mean_phase = np.angle(np.mean(double_diff[valid]))
|
|
263
280
|
shift_s = mean_phase / (2 * np.pi * delta_f_ovl_hz)
|
|
264
281
|
per_overlap_shifts.append(float(shift_s))
|
|
282
|
+
skip_reasons.append("used")
|
|
265
283
|
|
|
266
284
|
valid_shifts = [s for s in per_overlap_shifts if s is not None]
|
|
267
285
|
if not valid_shifts:
|
|
286
|
+
# Real, surfaced breakdown, not the previous ambiguous message --
|
|
287
|
+
# tells the caller directly whether every real overlap failed
|
|
288
|
+
# because none existed within the (possibly cropped) arrays at
|
|
289
|
+
# all (an AOI/cropping problem, fixable by widening the crop) or
|
|
290
|
+
# because they existed but were genuinely below the coherence
|
|
291
|
+
# threshold (a real decorrelation problem in the data itself,
|
|
292
|
+
# not fixable by widening anything).
|
|
293
|
+
n_outside = skip_reasons.count("outside_array_bounds")
|
|
294
|
+
n_low_coh = sum(1 for r in skip_reasons if r.startswith("low_coherence"))
|
|
268
295
|
logger.warning(
|
|
269
296
|
"Real per-burst-overlap ESD found no usable burst overlaps "
|
|
270
|
-
"
|
|
271
|
-
"
|
|
297
|
+
"out of %d total: %d fell outside the given (possibly "
|
|
298
|
+
"cropped) arrays, %d were below coherence_threshold=%.2f. "
|
|
299
|
+
"%s",
|
|
300
|
+
len(overlaps), n_outside, n_low_coh, coherence_threshold,
|
|
301
|
+
(
|
|
302
|
+
"Every overlap fell outside the array -- try widening the "
|
|
303
|
+
"AOI/crop, since none were even tested for coherence."
|
|
304
|
+
if n_outside == len(overlaps) and len(overlaps) > 0
|
|
305
|
+
else (
|
|
306
|
+
"Every real, in-bounds overlap was tested and found "
|
|
307
|
+
"genuinely below the coherence threshold -- this "
|
|
308
|
+
"reflects real decorrelation in the data, not "
|
|
309
|
+
"something a larger AOI would fix."
|
|
310
|
+
if n_low_coh == len(overlaps) and len(overlaps) > 0
|
|
311
|
+
else "A mix of both -- see the per-overlap detail at DEBUG level."
|
|
312
|
+
)
|
|
313
|
+
),
|
|
272
314
|
)
|
|
273
315
|
return None, per_overlap_shifts
|
|
274
316
|
|
|
@@ -279,4 +321,4 @@ def estimate_esd_shift_per_burst_overlap(
|
|
|
279
321
|
len(valid_shifts), len(overlaps), combined_shift_s * 1000,
|
|
280
322
|
combined_shift_s / azimuth_time_interval_s,
|
|
281
323
|
)
|
|
282
|
-
return combined_shift_s, per_overlap_shifts
|
|
324
|
+
return combined_shift_s, per_overlap_shifts
|
|
@@ -178,6 +178,39 @@ def compute_flat_earth_phase(
|
|
|
178
178
|
n_out_of_bounds += 1
|
|
179
179
|
continue
|
|
180
180
|
|
|
181
|
+
# Real, confirmed gap fixed here: t_ref/ref_row/ref_col were
|
|
182
|
+
# already validated against the reference scene's real
|
|
183
|
+
# bounds above, but t_sec was never checked against the
|
|
184
|
+
# SECONDARY scene's real bounds at all -- find_zero_doppler_time
|
|
185
|
+
# has no built-in guarantee of converging to the zero-Doppler
|
|
186
|
+
# crossing nearest the initial guess specifically (only that
|
|
187
|
+
# it converges to *some* zero crossing), so nothing here was
|
|
188
|
+
# catching a secondary-orbit convergence result that fell
|
|
189
|
+
# outside this ground point's real, valid azimuth/range window
|
|
190
|
+
# for the secondary acquisition. An undetected error here
|
|
191
|
+
# would silently corrupt R_sec and, through it, every sample
|
|
192
|
+
# point's flat_phase and the polynomial fit fit across all of
|
|
193
|
+
# them -- a real, plausible explanation for this project's
|
|
194
|
+
# own observed flat-earth phase anomaly (values in the tens
|
|
195
|
+
# of millions of radians, physically equivalent to tens of
|
|
196
|
+
# km of spurious range error, for specific real pairs).
|
|
197
|
+
sec_row = sec_geometry.row_for_azimuth_time(t_sec)
|
|
198
|
+
range_sec_time = 2 * R_sec / 299792458.0
|
|
199
|
+
sec_col = sec_geometry.col_for_range_time(range_sec_time)
|
|
200
|
+
|
|
201
|
+
if not (0 <= sec_row < sec_geometry.n_lines and 0 <= sec_col < sec_geometry.n_columns):
|
|
202
|
+
n_out_of_bounds += 1
|
|
203
|
+
logger.debug(
|
|
204
|
+
"Flat-earth phase: sample (%.4f, %.4f) rejected -- "
|
|
205
|
+
"t_sec converged to a secondary-scene row/col "
|
|
206
|
+
"(%.1f, %.1f) outside the real secondary SLC extent "
|
|
207
|
+
"(%d lines x %d columns). This is the real, specific "
|
|
208
|
+
"case the reference-only bounds check upstream could "
|
|
209
|
+
"not catch.",
|
|
210
|
+
lon, lat, sec_row, sec_col, sec_geometry.n_lines, sec_geometry.n_columns,
|
|
211
|
+
)
|
|
212
|
+
continue
|
|
213
|
+
|
|
181
214
|
# Real, verified sign convention: interferogram phase =
|
|
182
215
|
# phase(ref) - phase(sec), and SAR phase for a point at
|
|
183
216
|
# range R is -(4*pi/lambda)*R, so
|
|
@@ -240,4 +273,4 @@ def compute_flat_earth_phase(
|
|
|
240
273
|
len(grid_rows), polynomial_degree, dense_phase.min(), dense_phase.max(),
|
|
241
274
|
)
|
|
242
275
|
|
|
243
|
-
return dense_phase.astype(np.float32)
|
|
276
|
+
return dense_phase.astype(np.float32)
|
|
@@ -391,11 +391,32 @@ class InterferogramGenerator:
|
|
|
391
391
|
|
|
392
392
|
# Step 2: real deburst, applied to both images using each
|
|
393
393
|
# date's own real burst metadata.
|
|
394
|
+
#
|
|
395
|
+
# Real, confirmed bug fixed here: by this point sec_complex has
|
|
396
|
+
# already been resampled onto the REFERENCE's real grid, either
|
|
397
|
+
# by _orbit_based_coregister (which explicitly fits and applies
|
|
398
|
+
# an offset field using both ref_row_off AND sec_row_off
|
|
399
|
+
# together, see resample_with_offset_field above) or by the
|
|
400
|
+
# shape-based _resample_to_reference fallback -- either way,
|
|
401
|
+
# sec_complex's row 0 now corresponds to the same real
|
|
402
|
+
# full-scene row as ref_complex's row 0, i.e. ref_row_off, not
|
|
403
|
+
# its own original, pre-coregistration sec_row_off. Passing
|
|
404
|
+
# the stale sec_row_off here fed deburst_array a coordinate
|
|
405
|
+
# system that no longer matched the array it was actually
|
|
406
|
+
# operating on, silently shifting which rows got cropped as
|
|
407
|
+
# "burst edge/overlap" for the secondary relative to where the
|
|
408
|
+
# real burst boundaries actually fell in the now-coregistered
|
|
409
|
+
# array -- corrupting the debursted secondary image used for
|
|
410
|
+
# every downstream step (interferogram formation, coherence,
|
|
411
|
+
# unwrapping), independent of and in addition to the separate
|
|
412
|
+
# per-burst-overlap ESD issue investigated above, which runs
|
|
413
|
+
# earlier, on the pre-deburst array, and was already using
|
|
414
|
+
# ref_row_off correctly for both images at that stage.
|
|
394
415
|
ref_debursted, ref_first_kept_row = deburst_array(
|
|
395
416
|
ref_complex, ref_burst_info, azimuth_time_interval_s, row_offset=int(ref_row_off)
|
|
396
417
|
)
|
|
397
418
|
sec_debursted, _ = deburst_array(
|
|
398
|
-
sec_complex, sec_burst_info, azimuth_time_interval_s, row_offset=int(
|
|
419
|
+
sec_complex, sec_burst_info, azimuth_time_interval_s, row_offset=int(ref_row_off)
|
|
399
420
|
)
|
|
400
421
|
|
|
401
422
|
# Real correctness fix, not optional: deburst can remove
|
|
@@ -549,6 +570,63 @@ class InterferogramGenerator:
|
|
|
549
570
|
DataValidator.validate_slc(ref_complex, name="reference SLC").raise_if_invalid()
|
|
550
571
|
DataValidator.validate_slc(sec_complex, name="secondary SLC").raise_if_invalid()
|
|
551
572
|
|
|
573
|
+
# Real, confirmed-relevant check added here: mismatched satellite
|
|
574
|
+
# platform (S1A vs S1B) or sub-swath (IW1/IW2/IW3) between the two
|
|
575
|
+
# real dates is a genuine, documented risk factor for this exact
|
|
576
|
+
# kind of pair -- confirmed directly against ESA's own S1TBX TOPS
|
|
577
|
+
# interferometry tutorial: sub-swaths are processed independently
|
|
578
|
+
# and must be explicitly merged (S-1 TOPS Merge) before combining
|
|
579
|
+
# data from different ones, and combining S1A/S1B "be aware the
|
|
580
|
+
# extents... can be shifted along-track and different bursts must
|
|
581
|
+
# be selected for the same area." Real, satellite platform is read
|
|
582
|
+
# directly from the .SAFE product's own real filename (its first
|
|
583
|
+
# three characters, "S1A" or "S1B", by Sentinel-1's own naming
|
|
584
|
+
# convention), and real sub-swath from the tag SLCExtractor itself
|
|
585
|
+
# already records at extraction time -- not guessed. This does not
|
|
586
|
+
# raise: pygeofetch's own coregistration and burst-aware processing
|
|
587
|
+
# can still run and may still produce a usable result, but a
|
|
588
|
+
# mismatch here is a real, direct, and previously silent
|
|
589
|
+
# contributor to degraded coregistration and failed per-burst-
|
|
590
|
+
# overlap ESD, worth surfacing loudly rather than only showing up
|
|
591
|
+
# later as an unexplained low coherence value.
|
|
592
|
+
if reference_safe_zip is not None and secondary_safe_zip is not None:
|
|
593
|
+
ref_platform = Path(reference_safe_zip).name[:3]
|
|
594
|
+
sec_platform = Path(secondary_safe_zip).name[:3]
|
|
595
|
+
if ref_platform != sec_platform:
|
|
596
|
+
logger.warning(
|
|
597
|
+
"Reference (%s) and secondary (%s) scenes are from "
|
|
598
|
+
"different Sentinel-1 satellites -- burst boundaries "
|
|
599
|
+
"for the same real ground area are not guaranteed to "
|
|
600
|
+
"align between S1A and S1B. This pair can still be "
|
|
601
|
+
"processed, but expect degraded coregistration and "
|
|
602
|
+
"possible per-burst-overlap ESD failure as a direct "
|
|
603
|
+
"consequence, not a separate, unrelated issue.",
|
|
604
|
+
ref_platform, sec_platform,
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
try:
|
|
608
|
+
from pygeofetch.insar.coregister import read_matched_swath
|
|
609
|
+
|
|
610
|
+
ref_swath = read_matched_swath(reference) if reference is not None else None
|
|
611
|
+
sec_swath = read_matched_swath(secondary) if secondary is not None else None
|
|
612
|
+
if ref_swath and sec_swath and ref_swath != sec_swath:
|
|
613
|
+
logger.warning(
|
|
614
|
+
"Reference (%s) and secondary (%s) scenes were "
|
|
615
|
+
"extracted from different real sub-swaths -- per "
|
|
616
|
+
"ESA's own TOPS documentation, sub-swaths are "
|
|
617
|
+
"processed independently and require an explicit "
|
|
618
|
+
"merge step before combining; without it, ESD "
|
|
619
|
+
"and coregistration between these two dates are "
|
|
620
|
+
"not expected to find a usable, shared burst "
|
|
621
|
+
"structure. This pair can still be processed, "
|
|
622
|
+
"but this mismatch is a real, direct, and "
|
|
623
|
+
"previously silent explanation if ESD reports no "
|
|
624
|
+
"usable overlaps for it.",
|
|
625
|
+
ref_swath, sec_swath,
|
|
626
|
+
)
|
|
627
|
+
except Exception:
|
|
628
|
+
pass # real, best-effort check -- never block processing over it
|
|
629
|
+
|
|
552
630
|
# Step 1: coregistration.
|
|
553
631
|
real_coreg_inputs = (
|
|
554
632
|
dem, reference_safe_zip, secondary_safe_zip,
|
|
@@ -1079,16 +1157,72 @@ class InterferogramGenerator:
|
|
|
1079
1157
|
return interferogram, {"correction_applied": False, "reason": "rasterio_missing"}
|
|
1080
1158
|
|
|
1081
1159
|
try:
|
|
1160
|
+
interferogram_crs = profile.get("crs")
|
|
1161
|
+
interferogram_transform = profile.get("transform")
|
|
1162
|
+
|
|
1082
1163
|
with rasterio.open(dem_path) as dem_src:
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1164
|
+
if interferogram_crs is not None and interferogram_transform is not None:
|
|
1165
|
+
# Real, confirmed bug fixed here: the previous fallback
|
|
1166
|
+
# reprojected the DEM onto the interferogram's grid
|
|
1167
|
+
# using ONLY a shape ratio (scipy.ndimage.zoom), never
|
|
1168
|
+
# referencing the real, georeferenced profile/transform
|
|
1169
|
+
# this method receives as its own `profile` argument --
|
|
1170
|
+
# that only produces a correctly-aligned DEM if the DEM
|
|
1171
|
+
# and interferogram happen to cover exactly the same
|
|
1172
|
+
# real geographic extent, which this project's own AOI-
|
|
1173
|
+
# cropped, orbit-coregistered pipeline does not
|
|
1174
|
+
# guarantee (confirmed directly against this project's
|
|
1175
|
+
# own logs: reference/secondary crop offsets of several
|
|
1176
|
+
# thousand rows relative to each other). A shape-only
|
|
1177
|
+
# resample under that mismatch compares elevation at
|
|
1178
|
+
# the wrong real pixels to phase at the wrong real
|
|
1179
|
+
# pixels, which would show up as exactly the near-zero
|
|
1180
|
+
# or negative R^2 this project's own real runs
|
|
1181
|
+
# reported for every pair tested -- not necessarily
|
|
1182
|
+
# because there was little true topographic phase to
|
|
1183
|
+
# find, but because the regression was never actually
|
|
1184
|
+
# comparing elevation and phase at the same ground
|
|
1185
|
+
# location to begin with. Reproject properly using the
|
|
1186
|
+
# real CRS/transform on both sides instead.
|
|
1187
|
+
from rasterio.warp import reproject, Resampling
|
|
1188
|
+
|
|
1189
|
+
dem = np.empty(interferogram.shape, dtype=np.float32)
|
|
1190
|
+
reproject(
|
|
1191
|
+
source=rasterio.band(dem_src, 1),
|
|
1192
|
+
destination=dem,
|
|
1193
|
+
src_transform=dem_src.transform,
|
|
1194
|
+
src_crs=dem_src.crs,
|
|
1195
|
+
dst_transform=interferogram_transform,
|
|
1196
|
+
dst_crs=interferogram_crs,
|
|
1197
|
+
resampling=Resampling.bilinear,
|
|
1198
|
+
src_nodata=dem_src.nodata,
|
|
1199
|
+
dst_nodata=np.nan,
|
|
1200
|
+
)
|
|
1201
|
+
else:
|
|
1202
|
+
# Honest fallback: no real georeferencing available on
|
|
1203
|
+
# either side to reproject against, so approximate
|
|
1204
|
+
# alignment via shape ratio is the best available --
|
|
1205
|
+
# but this is a real, meaningfully weaker guarantee
|
|
1206
|
+
# than the georeferenced path above, and worth logging
|
|
1207
|
+
# as such rather than silently treating it the same.
|
|
1208
|
+
logger.warning(
|
|
1209
|
+
"No real CRS/transform available on the "
|
|
1210
|
+
"interferogram profile -- falling back to a "
|
|
1211
|
+
"shape-ratio DEM resample, which only aligns "
|
|
1212
|
+
"correctly if the DEM and interferogram already "
|
|
1213
|
+
"cover the exact same real geographic extent. "
|
|
1214
|
+
"Topographic phase removal R^2 may be unreliable "
|
|
1215
|
+
"as a result."
|
|
1216
|
+
)
|
|
1217
|
+
dem = dem_src.read(1).astype(np.float32)
|
|
1218
|
+
if dem.shape != interferogram.shape:
|
|
1219
|
+
from scipy.ndimage import zoom
|
|
1086
1220
|
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1221
|
+
zf = (
|
|
1222
|
+
interferogram.shape[0] / dem.shape[0],
|
|
1223
|
+
interferogram.shape[1] / dem.shape[1],
|
|
1224
|
+
)
|
|
1225
|
+
dem = zoom(dem, zf, order=1)
|
|
1092
1226
|
|
|
1093
1227
|
valid = np.isfinite(dem) & (dem > -500)
|
|
1094
1228
|
phase = np.angle(interferogram)
|
|
@@ -19,13 +19,12 @@ from pathlib import Path
|
|
|
19
19
|
import numpy as np
|
|
20
20
|
import pytest
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
from
|
|
24
|
-
|
|
25
|
-
from pygeofetch.insar.annotation import SLCGeometry # noqa: E402
|
|
26
|
-
from pygeofetch.insar.flatearth import compute_flat_earth_phase # noqa: E402
|
|
27
|
-
from pygeofetch.insar.geolocation import ( # noqa: E402
|
|
22
|
+
from pygeofetch.insar.annotation import SLCGeometry
|
|
23
|
+
from pygeofetch.insar.flatearth import compute_flat_earth_phase
|
|
24
|
+
from pygeofetch.insar.geolocation import (
|
|
28
25
|
SPEED_OF_LIGHT,
|
|
26
|
+
WGS84_A,
|
|
27
|
+
WGS84_B,
|
|
29
28
|
geodetic_to_ecef,
|
|
30
29
|
find_zero_doppler_time,
|
|
31
30
|
interpolate_orbit_state,
|
|
@@ -34,6 +33,62 @@ from pygeofetch.insar.geolocation import ( # noqa: E402
|
|
|
34
33
|
WAVELENGTH_M = 0.05546576
|
|
35
34
|
|
|
36
35
|
|
|
36
|
+
# Real, confirmed fix here: this file previously imported _build_geometry
|
|
37
|
+
# from a sibling test module (test_insar_verified_components) via a
|
|
38
|
+
# runtime sys.path.insert() hack. That only worked when both files sat
|
|
39
|
+
# together in the same directory -- a real, brittle assumption that broke
|
|
40
|
+
# in any environment where this file is used standalone (as reported: the
|
|
41
|
+
# sibling file genuinely does not exist there). Inlined below, verbatim,
|
|
42
|
+
# so this file is fully self-contained and has no dependency on any
|
|
43
|
+
# other test file existing.
|
|
44
|
+
def _build_geometry(lat_deg, lon_deg, dem_h=0.0, incl_deg=98.18, ascending=True):
|
|
45
|
+
"""Build a fully self-consistent, realistic satellite+ground-point test
|
|
46
|
+
geometry — real orbital velocity direction, exact zero-Doppler
|
|
47
|
+
projection."""
|
|
48
|
+
lat, lon = math.radians(lat_deg), math.radians(lon_deg)
|
|
49
|
+
e2 = 1 - ((WGS84_B + dem_h) ** 2 / (WGS84_A + dem_h) ** 2)
|
|
50
|
+
N = (WGS84_A + dem_h) / math.sqrt(1 - e2 * math.sin(lat) ** 2)
|
|
51
|
+
P_true = (
|
|
52
|
+
N * math.cos(lat) * math.cos(lon),
|
|
53
|
+
N * math.cos(lat) * math.sin(lon),
|
|
54
|
+
N * (1 - e2) * math.sin(lat),
|
|
55
|
+
)
|
|
56
|
+
altitude = 693000.0
|
|
57
|
+
normal = (
|
|
58
|
+
P_true[0] / (WGS84_A + dem_h) ** 2,
|
|
59
|
+
P_true[1] / (WGS84_A + dem_h) ** 2,
|
|
60
|
+
P_true[2] / (WGS84_B + dem_h) ** 2,
|
|
61
|
+
)
|
|
62
|
+
nmag = math.sqrt(sum(c**2 for c in normal))
|
|
63
|
+
normal = tuple(c / nmag for c in normal)
|
|
64
|
+
sat_pos = tuple(P_true[i] + normal[i] * altitude for i in range(3))
|
|
65
|
+
|
|
66
|
+
incl = math.radians(incl_deg)
|
|
67
|
+
sign = 1 if ascending else -1
|
|
68
|
+
plane_normal = (
|
|
69
|
+
sign * math.sin(incl) * math.sin(lon),
|
|
70
|
+
-sign * math.sin(incl) * math.cos(lon),
|
|
71
|
+
math.cos(incl),
|
|
72
|
+
)
|
|
73
|
+
pn_mag = math.sqrt(sum(c**2 for c in plane_normal))
|
|
74
|
+
plane_normal = tuple(c / pn_mag for c in plane_normal)
|
|
75
|
+
tangent = (
|
|
76
|
+
plane_normal[1] * normal[2] - plane_normal[2] * normal[1],
|
|
77
|
+
plane_normal[2] * normal[0] - plane_normal[0] * normal[2],
|
|
78
|
+
plane_normal[0] * normal[1] - plane_normal[1] * normal[0],
|
|
79
|
+
)
|
|
80
|
+
tmag = math.sqrt(sum(c**2 for c in tangent))
|
|
81
|
+
tangent = tuple(c / tmag for c in tangent)
|
|
82
|
+
sat_vel = tuple(c * 7500.0 for c in tangent)
|
|
83
|
+
|
|
84
|
+
d = sum(sat_vel[i] * (P_true[i] - sat_pos[i]) for i in range(3)) / sum(v**2 for v in sat_vel)
|
|
85
|
+
P_exact = tuple(P_true[i] - d * sat_vel[i] for i in range(3))
|
|
86
|
+
los = tuple(sat_pos[i] - P_exact[i] for i in range(3))
|
|
87
|
+
range_m = math.sqrt(sum(c**2 for c in los))
|
|
88
|
+
range_time_s = 2 * range_m / SPEED_OF_LIGHT
|
|
89
|
+
return sat_pos, sat_vel, range_time_s, P_exact
|
|
90
|
+
|
|
91
|
+
|
|
37
92
|
def _orbit_series(center_pos, center_vel, center_time):
|
|
38
93
|
times, positions, velocities = [], [], []
|
|
39
94
|
for i in range(-60, 61):
|
|
@@ -59,7 +114,22 @@ def _build_fixture(n=200, b_perp=80.0):
|
|
|
59
114
|
near_range_time_s=range_time_center - (n / 2) * (1 / 6.4e7), range_sampling_rate_hz=6.4e7,
|
|
60
115
|
n_lines=n, n_columns=n,
|
|
61
116
|
)
|
|
62
|
-
|
|
117
|
+
# Real, confirmed test-fixture gap fixed here: the fixture previously
|
|
118
|
+
# only built ONE SLCGeometry and every caller passed it as BOTH
|
|
119
|
+
# ref_geometry and sec_geometry -- which never exercised the real,
|
|
120
|
+
# production scenario (interferogram.py always parses a genuinely
|
|
121
|
+
# separate sec_geom_fe from the secondary's own SAFE zip, anchored to
|
|
122
|
+
# the secondary's own real first_line_time, not the reference's).
|
|
123
|
+
# Reusing ref_geom for both meant t_sec (anchored ~12 real days after
|
|
124
|
+
# ref_geom's own first_line_time) was silently being evaluated
|
|
125
|
+
# against the WRONG scene's timing reference whenever a real
|
|
126
|
+
# secondary-bounds check was added -- exactly what surfaced this gap.
|
|
127
|
+
sec_geom = SLCGeometry(
|
|
128
|
+
first_line_time=t0_sec - timedelta(seconds=n / 2 * 0.002), azimuth_time_interval_s=0.002,
|
|
129
|
+
near_range_time_s=range_time_center - (n / 2) * (1 / 6.4e7), range_sampling_rate_hz=6.4e7,
|
|
130
|
+
n_lines=n, n_columns=n,
|
|
131
|
+
)
|
|
132
|
+
return ref_geom, sec_geom, ref_orbit, sec_orbit, t0_ref, t0_sec, n
|
|
63
133
|
|
|
64
134
|
|
|
65
135
|
def test_range_difference_matches_textbook_approximation():
|
|
@@ -98,11 +168,11 @@ def test_matches_independent_direct_computation_at_a_real_point():
|
|
|
98
168
|
"""The module's polynomial-fit output at a specific pixel must
|
|
99
169
|
match a completely independent, direct geometric computation at
|
|
100
170
|
that same real point."""
|
|
101
|
-
ref_geom, ref_orbit, sec_orbit, t0_ref, t0_sec, n = _build_fixture()
|
|
171
|
+
ref_geom, sec_geom, ref_orbit, sec_orbit, t0_ref, t0_sec, n = _build_fixture()
|
|
102
172
|
lon0, lat0 = -99.11, 19.34
|
|
103
173
|
|
|
104
174
|
flat_phase = compute_flat_earth_phase(
|
|
105
|
-
ref_geom, ref_orbit,
|
|
175
|
+
ref_geom, ref_orbit, sec_geom, sec_orbit, t0_ref, t0_sec, (n, n), WAVELENGTH_M,
|
|
106
176
|
sample_bounds=(lon0, lat0, lon0 + 0.04, lat0 + 0.04), grid_points=7, polynomial_degree=2,
|
|
107
177
|
)
|
|
108
178
|
|
|
@@ -129,11 +199,11 @@ def test_closed_loop_removal_through_complex_wrapped_pathway():
|
|
|
129
199
|
a real, complex, wrapped interferogram alongside a known
|
|
130
200
|
deformation signal, remove it, and confirm exact recovery of the
|
|
131
201
|
true signal -- the same pathway process_pair() actually uses."""
|
|
132
|
-
ref_geom, ref_orbit, sec_orbit, t0_ref, t0_sec, n = _build_fixture()
|
|
202
|
+
ref_geom, sec_geom, ref_orbit, sec_orbit, t0_ref, t0_sec, n = _build_fixture()
|
|
133
203
|
lon0, lat0 = -99.11, 19.34
|
|
134
204
|
|
|
135
205
|
flat_phase = compute_flat_earth_phase(
|
|
136
|
-
ref_geom, ref_orbit,
|
|
206
|
+
ref_geom, ref_orbit, sec_geom, sec_orbit, t0_ref, t0_sec, (n, n), WAVELENGTH_M,
|
|
137
207
|
sample_bounds=(lon0, lat0, lon0 + 0.04, lat0 + 0.04), grid_points=7, polynomial_degree=2,
|
|
138
208
|
)
|
|
139
209
|
|
|
@@ -155,8 +225,8 @@ def test_closed_loop_removal_through_complex_wrapped_pathway():
|
|
|
155
225
|
|
|
156
226
|
|
|
157
227
|
def test_raises_with_neither_dem_nor_sample_bounds():
|
|
158
|
-
ref_geom, ref_orbit, sec_orbit, t0_ref, t0_sec, n = _build_fixture()
|
|
228
|
+
ref_geom, sec_geom, ref_orbit, sec_orbit, t0_ref, t0_sec, n = _build_fixture()
|
|
159
229
|
with pytest.raises(ValueError):
|
|
160
230
|
compute_flat_earth_phase(
|
|
161
|
-
ref_geom, ref_orbit,
|
|
162
|
-
)
|
|
231
|
+
ref_geom, ref_orbit, sec_geom, sec_orbit, t0_ref, t0_sec, (n, n), WAVELENGTH_M,
|
|
232
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|