-
Notifications
You must be signed in to change notification settings - Fork 10
Single‐Sensor Magnetometer Weight And Distance Estimation Script
This document describes a method for estimating the distance, depth, and approximate weight of magnetic targets using single-sensor magnetic data.
The method uses the 2D analytic signal of the magnetic field to locate anomaly peaks and evaluate their shape. From this, the script estimates the sensor-to-source distance, target depth below ground, and an approximate weight.
It is intended for rapid interpretation of marked magnetic anomalies when only one sensor is available. The method is most useful for UXO detection and buried-metal target surveys, but can be used for other geophysical applications where a practical automated estimate is needed for semi-small targets.
For each detected anomaly, the script produces:
- an estimated distance,
- an estimated weight,
- and corresponding minimum, maximum, and central estimate values.
The following sections describe the input requirements, physical model, algorithm, and outputs in detail.
- Anomalies must be marked in the data file before running this script (
Mark = 1). More information about setting marks can be found here and here. - The script exits with a message if no marked rows are found.
Standard MagNIMBUS CSV file with the following required columns:
| Column | Default name | Description |
|---|---|---|
| Timestamp |
Date + Time (or Timestamp) |
Used to split flight lines |
| Magnetometer field | TMI |
Total magnetic intensity (nT) |
| Altitude AGL | Altitude AGL |
Height above ground level (metres) |
| Latitude / Longitude |
Latitude / Longitude
|
Position (decimal degrees) |
| Mark | Mark |
Anomaly mark (1 = marked row) |
All column names are configurable via command-line arguments (see Section 6).
The script performs spatial lowpass filtering per flight line:
- The TMI profile is resampled onto a uniform spatial grid (0.5 m step).
- A 4th-order zero-phase Butterworth filter with cut-off wavelength
LOWPASS_WAVELENGTH_M = 15 m(f_cut = 1/λcycles/m) is applied to obtain the smooth background. - A rolling spatial-median pass (window =
λ / 0.5 m = 30 samples) is applied after the LP filter for robustness against residual spikes. - The background is interpolated back to the original sample positions, stored in the
TMI_LPFcolumn, and subtracted fromTMIto obtain the residual anomaly used by all subsequent steps.
If a flight line is shorter than λ/2 = 7.5 m or contains fewer than 4 samples, the algorithm falls back to using the line mean as the background estimate.
A legacy
"linear"mode (polynomial trend fit to unmarked rows,TREND_DEGREE = 1) remains available by settingBACKGROUND_METHOD = "linear"in the script header.
No pre-processing of the background field is required before running the script.
The magnetic source is modelled as a point magnetic dipole (compact object, dimensions much smaller than depth). This is a reasonable approximation for UXO and similar metallic objects at depths greater than ~2× the object's largest dimension.
The script uses the 2D gradient analytic signal (Roest et al. 1992) computed on a regular 2D grid:
where:
-
$B(x, y)$ is the detrended magnetic field gridded onto a regular mesh, -
$\partial B/\partial x$ ,$\partial B/\partial y$ are horizontal gradients computed via a 5-point finite-difference stencil, -
$\partial B/\partial z$ is the vertical derivative computed via 2D FFT (multiplication by the wavenumber$|k| = \sqrt{k_x^2 + k_y^2}$ in the frequency domain).
Physical meaning:
Both methods are computed for every anomaly and their results are combined by the selection policy in Section 3, Step 12.
The full width at half maximum (FWHM) of the local
where
Before computing ASYMMETRY_CAP_RATIO = 1.20, the longer side is trimmed to 1.20 × shorter side. This corrects geological gradients that inflate one side of the AS profile.
MAX_DISTANCE_M = 20 m.
Both
Physical meaning: for a point dipole, the ratio
MIN_AS_THRESHOLD = 1.0 nT/m, MAX_DISTANCE_M.
For a dipole of moment
-
Peak TMI:
$|B_{\max}| = \frac{\mu_0}{4\pi} \frac{2m}{D^3}$ -
Peak AS:
$A_{\max} = \frac{\mu_0}{4\pi} \frac{6m}{D^4} = \frac{3|B_{\max}|}{D}$
From these:
The theoretical FWHM of
where $\bar{h}{\mathrm{AGL}}$ is the mean altitude AGL over the aggregated bins of the anomaly group. If $d{\mathrm{ground}} < 0$ the value is clipped to 0.
Weight estimation runs immediately after depth estimation for each anomaly group (Steps 14–18). It reuses the same data, grid, and anomaly groups already computed during the depth-estimation run.
The field value used for weight estimation is the residual TMI at the AS-peak position:
If this value is unavailable (Method B was skipped due to peak_mismatch), the fallback is:
covering all bins within ±AS_PEAK_SEARCH_RADIUS_M = 5 m of the mark centre. This handles cases where the operator pressed the mark button before crossing the anomaly peak.
where
Important: The formula uses the sensor-to-source distance
$D_\mathrm{weight}$ , not the depth below ground. Using ground depth instead would underestimate weight by up to$(\bar{h}_\mathrm{AGL} / D)^3$ .
where
Sphere geometry (only supported geometry): the calibrated effective magnetisation is
Min and Max weight estimates are computed by offsetting the central weight distance by WEIGHT_SPREAD_D_FRAC
Because
Using
dist_min/dist_maxdirectly for the weight range would amplify a wide distance band by$D^3$ , producing a thousands-of-percent weight spread when the two methods disagree. Anchoring onEstimated_Distance_Harmonicwith a fixed ±4.4 % offset gives a stable, meaningful weight bracket regardless of method disagreement.
- Read input CSV.
- Verify required columns (
TMIor configured name,Latitude,Longitude,Mark). - Strip previously computed output columns (
_SCRIPT_OUTPUT_COLS) to prevent stale values from a prior run. - Exit cleanly if no marks are found.
Consecutive rows separated by more than LINE_GAP_SECONDS = 30 s are treated as separate flight lines. Each line is processed independently.
Default path (BACKGROUND_METHOD = "lowpass"):
- Resample TMI profile to a uniform spatial grid (0.5 m step).
- Apply 4th-order zero-phase Butterworth lowpass filter with
LOWPASS_WAVELENGTH_M = 15 m. - Apply rolling spatial-median pass (window = 30 samples) for spike robustness.
- Interpolate background back to original sample positions; store as
TMI_LPF; subtract fromTMI.
Degenerate cases (line too short or too few samples) subtract the line mean instead.
Legacy "linear" path: fit a degree-TREND_DEGREE polynomial to unmarked rows; subtract.
Convert latitude/longitude to local Cartesian (equirectangular, centred on data midpoint):
x_m = R · (lon − lon₀) · cos(lat₀) [East, metres]
y_m = R · (lat − lat₀) [North, metres]
Along-track distance is computed via the haversine formula for use in aggregation and halfwidth calculations.
If DEFAULT_CELL_SIZE_M is None (default):
- Per-line median GPS step: compute all consecutive point-to-point distances per line (ignoring steps < 0.01 m); take the median across lines.
-
Validity check: if outside
[0.5 m, 10 m], fall back toextent / 168. -
Snap to 0.1 m:
max(0.5, round(cs, 1))— prevents grid-alignment aliasing from sub-0.1 m cell-size variations.
-
Grid dimensions:
gridWidth = max(2, int((x_max − x_min) / cell_size)),gridHeight = max(2, int((y_max − y_min) / cell_size)). - Bin and median: multiple observations in the same cell are reduced to their median.
-
Fill gaps: empty cells within
blanking_distance(default10 × cell_size) are filled via thin-plate-spline interpolation (RBFInterpolator(kernel='thin_plate_spline')) from the ≥ 10 nearest known neighbours. Regularisationsmoothing = var(vals) × 10⁻³. Falls back to lineargriddataon solver failure or remaining NaNs. - Clip: interpolated cells are clamped to ±100 % of the measured data range to prevent TPS overshoot at grid edges.
-
Circular blank mask: cells further than
blanking_distancefrom any data cell are set to NaN.
- Fill remaining NaN with an inverse-distance-weighted priority-queue algorithm (exact reproduction of Java
GridInterpolator.interpolate()). - Horizontal gradients
$\partial B/\partial x$ ,$\partial B/\partial y$ : 5-point finite-difference stencil (falls back to 3-point central, then forward/backward at edges). - Vertical derivative
$\partial B/\partial z$ : 2D FFT multiplication by$|k|$ :G = fft2(B) # NaN → 0 before FFT G *= sqrt(kx² + ky²) dz = real(ifft2(G)) - AS grid:
$A = \sqrt{(\partial B/\partial x)^2 + (\partial B/\partial y)^2 + (\partial B/\partial z)^2}$ . - Cells that were NaN before fill are set to NaN in the AS output.
Bilinearly interpolate the AS grid at each original data point's
Bin each flight line into spatial bins of SPATIAL_BIN_M = 0.5 m along the along-track distance. Per bin:
- TMI and AS: mean (NaN excluded).
- AGL: mean of valid values.
- Mark: OR (a bin is marked if any row in it is marked).
The same bin keys are reused to aggregate the AS values, ensuring identical spatial grouping.
Contiguous runs of marked bins are identified. Each run is one anomaly group.
For each anomaly group at aggregated positions
-
Window:
$[x_s - 20,\mathrm{m},\ x_e + 20,\mathrm{m}]$ - Require ≥
MIN_WINDOW_SAMPLES = 20bins; otherwise skip. -
Edge trim: exclude the outermost 10 % of bins from each end (
edge = max(1, int(n × 0.10))) to remove FFT wrap-around artefacts.
Local AS peak search:
- Restrict to bins within ±
ANOMALY_CENTER_RADIUS_M = 15 mof the mark centre. This larger radius handles cases where the operator placed the mark several metres before crossing the actual anomaly centre. - Find the bin with the highest
$A$ in that sub-range. - Fall back to the global window maximum if no bins fall within the search radius.
Method A — Halfwidth:
- Search outward from the AS peak for the first crossing where
$A \leq A_{\max}/2$ (linear interpolation). - Apply asymmetry cap: if one half exceeds the other by more than
ASYMMETRY_CAP_RATIO = 1.20, trim the longer to1.20 × shorter. -
$W = x_{\mathrm{right}} - x_{\mathrm{left}}$ ,$\quad d_A = K \cdot W$ with$K = 1.03$ .
MIN_FWHM_M = 0.5 m, MAX_FWHM_M = 100 m, or MAX_DISTANCE_M = 20 m.
Method B — B/AS ratio:
Both field value and AS are taken at the AS-peak bin:
MIN_AS_THRESHOLD = 1.0 nT/m, MAX_DISTANCE_M.
Dipole detection:
Within ±DIPOLE_CHECK_RADIUS_M = 15 m of the mark centre, flag dipole_anomaly if
Estimated_Distance_Min = Estimated_Distance_Max =
Estimated_Distance_Harmonic (central best estimate) is selected by the following rule-based policy, applied in order:
| Condition | Rule | Action |
|---|---|---|
|
|
Rule 1 | Use |
LARGE_FWHM_RATIO |
Rule 2 | Use |
|
|
Rule 3 | Use |
| Both methods agree and are physically valid | Fallback | Harmonic mean: |
| Only one method produced a valid result | — | Use that result directly |
The harmonic mean pulls the central estimate toward the smaller value compared with the arithmetic mean. When both methods agree (
Estimated_Depth_Harmonic =
Fill out_dist_min / max / avg and out_depth_min / max / avg arrays for all marked rows of the group. Proceed to Step 14.
Primary: the residual TMI at the AS-peak position (already found in Step 11):
Fallback (when the primary value is unavailable due to peak_mismatch): maximum absolute value within ±AS_PEAK_SEARCH_RADIUS_M = 5 m of the mark centre, or the marked bins if no window bins fall within 5 m.
Weight distance (
When Rule 3 fired (both distances valid,
Otherwise:
The AGL floor ensures a physically valid distance when the depth estimator returns 0.
Sphere geometry (only supported geometry): the calibrated effective magnetisation is
where WEIGHT_SPREAD_D_FRAC, equal to
Append all result columns to marked rows of the group. Write source CSV and targets CSV. See Section 4.
| Column | Description | Unit |
|---|---|---|
TMI_LPF |
Lowpass-filtered background subtracted in Step 3 | nT |
Estimated_Distance_Min |
Smaller of |
m |
Estimated_Distance_Max |
Larger of |
m |
Estimated_Distance_Harmonic |
Best-estimate distance — rule-based (Step 12) | m |
Estimated_Depth_Min |
Depth fromDistance_Min, clipped to 0 |
m |
Estimated_Depth_Max |
Depth fromDistance_Max, clipped to 0 |
m |
Estimated_Depth_Harmonic |
Depth fromDistance_Harmonic, clipped to 0 |
m |
Estimated_Weight_Min |
Lower weight estimate from |
kg |
Estimated_Weight_Max |
Upper weight estimate from |
kg |
Estimated_Weight_Harmonic |
Central weight estimate from |
kg |
All columns are empty ("") for rows outside any anomaly window.
One row per marked row. Identical estimation values are written to all marked rows of the same anomaly group.
Contains all original source CSV columns (excluding previously computed output columns) plus all 10 output columns listed in Section 4.1 (TMI_LPF plus the 9 distance/depth/weight estimation columns).
Not exposed as CLI arguments; edit the script header to change them.
| Constant | Value | Description |
|---|---|---|
K_FACTOR |
1.03 |
FWHM → sensor-to-source distance (Method A); calibrated on Brandenburg 2024 dataset |
CB_FACTOR |
3.0 |
B/AS ratio → sensor-to-source distance (Method B); exact theoretical value |
WINDOW_PADDING_M |
20.0 |
Window extends this far beyond each mark group (m) |
SPATIAL_BIN_M |
0.5 |
Spatial bin size for along-track aggregation (m) |
DEFAULT_CELL_SIZE_M |
None |
Override grid cell size (None = auto-estimated) |
MIN_WINDOW_SAMPLES |
20 |
Minimum bins in window for processing |
ANOMALY_CENTER_RADIUS_M |
15.0 |
Radius around mark centre for AS peak search (m); large to handle offset marks |
AS_PEAK_SEARCH_RADIUS_M |
5.0 |
Radius around mark centre for |
DIPOLE_CHECK_RADIUS_M |
15.0 |
Radius for dipole sign-change detection (m) |
METHOD_DISAGREE_THRESHOLD |
0.35 |
Relative distance difference to triggermethod_disagreement flag |
LARGE_FWHM_RATIO |
2.5 |
|
ASYMMETRY_CAP_RATIO |
1.20 |
Max allowed ratio between left and right AS half-widths before trimming the longer side |
MIN_FWHM_M |
0.5 |
Minimum valid full FWHM (m) |
MAX_FWHM_M |
100.0 |
Maximum valid full FWHM (m);wide_anomaly flag triggers when |
MIN_AS_THRESHOLD |
1.0 |
Minimum AS at AS-peak for Method B to be valid (nT/m) |
MAX_DISTANCE_M |
20.0 |
Maximum plausible sensor-to-source distance; results above this are discarded (m) |
BACKGROUND_METHOD |
"lowpass" |
Background removal:"lowpass" (Butterworth LP) or "linear" (polynomial fit) |
LOWPASS_WAVELENGTH_M |
15.0 |
Spatial cut-off wavelength for lowpass background filter (m) |
TREND_DEGREE |
1 |
Polynomial degree for linear detrending (legacy mode) |
MIN_TREND_SAMPLES |
10 |
Minimum rows needed for linear background removal (legacy mode) |
LINE_GAP_SECONDS |
30 |
Time gap that splits data into separate flight lines (s) |
CSV_SEPARATOR |
"," |
CSV field delimiter |
MARK_COLUMN |
"Mark" |
Hard-coded mark column name |
K_DIPOLE |
5e-3 |
Dipole moment coefficient A·m²·nT⁻¹·m⁻³ (exact: |
J_EFF\_SPHERE\_AM |
2112.0 |
Calibrated effective magnetisation, sphere geometry (A/m); ≈ 17.7× induced-only value |
WEIGHT_SPREAD_D_FRAC |
0.044 |
Distance offset for weight Min/Max: ±4.4 % in |
DENSITY_STEEL |
7800.0 |
Material density (kg/m³) |
| Argument | Default | Description |
|---|---|---|
file_path |
(required) | Input CSV file path |
--mag-column |
TMI |
Magnetometer column name |
--altitude-agl-column |
Altitude AGL |
Altitude AGL column (used for depth = distance − AGL, and for weight distance floor) |
--output-dir |
(same as input) | Directory for targets CSV |
- MagNIMBUS magnetometer data processing
- SENSYS MagDrone R1 magnetometer data processing
- SENSYS MagDrone R3 and R4 magnetometer data processing