A package for cross-checking radargram layers using dot product similarity.
Project description
XCheck — Consistency-Based Validation of Englacial Layer Annotations
A Python implementation of the radargram layer-matching framework from:
Hassan, Muhammad Behroze; Tama, Bayu Adhi; Purushotham, Sanjay; Janeja, Vandana P. XCheck: A Consistency-Based Validation Framework for Englacial Layers Annotations. https://par.nsf.gov/biblio/10673248 · DOI: 10.1109/ICDMW69685.2025.00015
XCheck validates whether annotated englacial layers are consistent between two radargrams that observe the same ice — either two consecutive flight segments (overlap found automatically via GPS time) or two intersecting flight paths (crossing point supplied via a CSV).
Features
- GPS Overlap Detection: Automatically finds temporally overlapping segments between two radargrams using log-transformed GPS timestamps.
- Data Loading & Masking: Loads
.matradargram and ground truth layer files and converts annotated layers into binary depth masks. - Geometric Alignment: Adjusts radargram columns by surface elevation before comparison to correct for topographic variation.
- Layer Continuity Matching: Dot-product similarity algorithm that identifies corresponding layers between two radargrams at their crossing or overlap point.
- Flexible Input: Accepts a single radargram pair via CLI flags, a batch CSV of pairs, or a pre-computed intersections CSV.
- Visualization: Map plots of GPS overlap regions and side-by-side mask views with matched layer annotations.
Installation
pip install xcheck
NDH_PythonTools (required)
XCheck uses NDH_PythonTools
for .mat file loading and radar data processing. Because that repository
is not a standard pip package, it must be installed manually:
git clone https://github.com/nholschuh/NDH_PythonTools.git
Then add its parent directory to PYTHONPATH before running xcheck:
# Linux / macOS
export PYTHONPATH="/path/to/parent/of/NDH_PythonTools"
# Windows PowerShell
$env:PYTHONPATH = "C:\path\to\parent\of\NDH_PythonTools"
Quick Start
Layer matching only (no NDH_PythonTools needed)
import numpy as np
from xcheck import match_layers_with_dot_product
m1 = np.zeros((6, 5)); m1[[1, 3, 5], :] = 1
m2 = np.zeros((6, 5)); m2[[1, 3, 5], :] = 1
matches = match_layers_with_dot_product(m1, m2, max_distance=1,
window_size=3, dp_threshold=1)
print(matches) # [(1, 1), (3, 3), (5, 5)]
Python API (requires NDH_PythonTools + data)
from xcheck import (
load_and_process_radargram,
load_log_gps_time,
find_overlapping_intervals,
extract_and_adjust_mask_columns_for_location,
match_layers_with_dot_product,
)
# Load two consecutive radargrams
r1, m1, d1 = load_and_process_radargram("Data_20120429_01_026.mat",
layer_dir="./Nick-layer-data-mat/",
radar_dir="./Nick-raw-radargram-mat/")
r2, m2, d2 = load_and_process_radargram("Data_20120429_01_027.mat",
layer_dir="./Nick-layer-data-mat/",
radar_dir="./Nick-raw-radargram-mat/")
# Find GPS overlap and derive crossing point from midpoint
import numpy as np
log_t1 = load_log_gps_time("./Nick-raw-radargram-mat/Data_20120429_01_026.mat")
log_t2 = load_log_gps_time("./Nick-raw-radargram-mat/Data_20120429_01_027.mat")
overlaps1, overlaps2 = find_overlapping_intervals(log_t1, log_t2)
for iv1, iv2 in zip(overlaps1, overlaps2):
mid1 = (iv1[0] + iv1[1]) // 2
mid2 = (iv2[0] + iv2[1]) // 2
mc1 = extract_and_adjust_mask_columns_for_location(
r1['Longitude'][mid1], r1['Latitude'][mid1], r1, m1, d1, 20)
mc2 = extract_and_adjust_mask_columns_for_location(
r2['Longitude'][mid2], r2['Latitude'][mid2], r2, m2, d2, 20)
matches = match_layers_with_dot_product(mc1, mc2)
print(f"Matched layers: {matches}")
CLI
Single pair — consecutive (no crossing CSV needed)
xcheck --r1 Data_20120429_01_026.mat --r2 Data_20120429_01_027.mat \
--layer_dir ./Nick-layer-data-mat/ \
--radar_dir ./Nick-raw-radargram-mat/
Single pair — intersecting (crossing looked up from CSV)
xcheck --r1 Data_20120330_01_004.mat --r2 Data_20120511_01_054.mat \
--intersections_csv ./Intersections_2012.csv \
--layer_dir ./Nick-layer-data-mat/ \
--radar_dir ./Nick-raw-radargram-mat/
Single pair — intersecting with explicit crossing coordinates
xcheck --r1 Data_20120330_01_004.mat --r2 Data_20120511_01_054.mat \
--lat 78.909416 --lon -61.804612 \
--layer_dir ./Nick-layer-data-mat/ \
--radar_dir ./Nick-raw-radargram-mat/
Batch mode (all pairs in a CSV)
xcheck --intersections_csv ./Intersections_2012.csv \
--layer_dir ./Nick-layer-data-mat/ \
--radar_dir ./Nick-raw-radargram-mat/
All CLI arguments
| Argument | Default | Description |
|---|---|---|
--r1, --r2 |
— | Single-pair mode: filenames of the two radargrams |
--lat, --lon |
— | Crossing coordinates for single-pair mode |
--csv_path |
— | Batch CSV with columns Radargram 1, Radargram 2, Latitude, Longitude |
--intersections_csv |
— | Pre-computed intersections CSV (used alone or to look up coordinates) |
--layer_dir |
required | Directory of layer .mat files |
--radar_dir |
required | Directory of raw radargram .mat files |
--layer_prefix |
Layer_ |
Filename prefix for layer files |
--cols_side |
20 / 3 | Columns extracted each side of crossing (consecutive / non-consecutive) |
--max_distance |
4 / 3 | Max depth-pixel gap between candidate layer rows |
--window_size |
7 / 3 | Dot product window width (must be odd) |
--dp_threshold |
4 / 1 | Minimum dot product score to confirm a match |
--plot_overlaps |
off | Plot GPS overlap regions on a map |
Default pairs shown as consecutive / non-consecutive.
Default thresholds by radargram type
| Parameter | Consecutive | Non-consecutive |
|---|---|---|
cols_side |
20 (41 columns) | 3 (7 columns) |
max_distance |
4 px | 3 px |
window_size |
7 | 3 |
dp_threshold |
4 | 1 |
These are selected automatically. Override any of them via CLI flags.
Methodological note
The GPS overlap detection compares log(GPS_time) between two
radargrams with an absolute tolerance of 1e-8. Because
d(log t) ≈ dt/t and GPS times are ~1×10⁹ seconds, this tolerance
corresponds to matching raw timestamps within roughly 10 seconds.
Consecutive flight segments (e.g. _026.mat → _027.mat) share a
small tail/head of overlapping traces, which is where the crossing point
is derived. Non-consecutive intersecting radargrams do not share GPS
timestamps; their crossing point must be supplied via --intersections_csv
or --lat/--lon.
Project structure
src/xcheck/
xcheck.py Core algorithms: loading, masking, GPS overlap,
column extraction, layer matching, plotting, CLI
__init__.py Public API exports
pyproject.toml Build metadata and dependencies
README.md
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file xcheck-0.1.0.tar.gz.
File metadata
- Download URL: xcheck-0.1.0.tar.gz
- Upload date:
- Size: 11.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
565422afeb4baf43ac2e0b8ff426152d1493b72c126f5993974839890f8208ab
|
|
| MD5 |
acf0f87068869062683a986205ec700a
|
|
| BLAKE2b-256 |
2a9b19c482b9f0c655e7f3bff67846bdc02971851b136d30389118d5d0ec486b
|
File details
Details for the file xcheck-0.1.0-py3-none-any.whl.
File metadata
- Download URL: xcheck-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b7f9d946bc8056692f4f197b8326c38f779474e0441c0f4470fa0d9495d76aa
|
|
| MD5 |
a79ebbda8d5cee78ca909816a775660e
|
|
| BLAKE2b-256 |
a07ab7d4aa02f3f13c6a99e63256f4c102890ee78b5ede31d6ae0946dba42c53
|