Overview
A Measurement Pipeline.
The Arduino Mega gathers sensor data in the field, checks whether a reading is usable for mapping, and writes both raw and filtered logs to the SD card. Afterward, Python scripts read the accepted points off that card and build the map products.
Workflow
From field reading to map output
- 1. Capture: GPS, sonar, temperature, IMU, UTC time.
- 2. Validate: Fix quality, depth stability, motion, spacing.
- 3. Log: Full record plus accepted mapping dataset.
- 4. Reconstruct: depth surfaces, contour maps, and Google Earth overlays.
Hardware
The Platform
The system runs on an Arduino Mega with a GPS module, a waterproof ultrasonic sensor, a DS18B20 water temperature sensor, a BNO085 IMU for pitch/roll orientation, an LCD display, and an SD card module for offline field logging.
Validation Logic
Measurement thresholds.
The logger collects sensor output and defines what counts as an acceptable mapping condition. Those choices show up directly in the constants and helper functions in the Arduino code.
| Parameter |
Current Value |
Role in the survey |
sonarCalSoundSpeed |
1500.0 m/s |
Baseline sound speed used when correcting sonar range. |
maxMapHdop |
1.8 |
Rejects GPS fixes with poorer horizontal precision. |
maxMapFixAgeMs |
2000 ms |
Rejects stale GPS fixes that no longer match the platform position closely. |
maxMapSpeedKmph |
8.0 km/h |
Limits mapping when the platform is moving too quickly. |
maxMapPitchDeg / maxMapRollDeg |
10.0° |
Rejects mapping points when platform tilt exceeds the allowed pitch/roll range. |
minMapSats |
5 |
Minimum satellite count required before a fix can produce a mapping point. |
minMapSpacingMetersBase |
2.0 m |
Base minimum spacing between accepted points; scales adaptively with speed (2–10 m) to prevent oversampling. |
maxDepthJumpCm |
50.0 cm |
Rejects isolated depth samples that jump too far from the smoothed value, suppressing transient sonar spikes. |
Geospatial Data
Depth becomes bathymetry when tied to position.
Depth and position are logged together, and that pairing is what turns a reading into bathymetry. On its own a depth value describes one moment at one spot; with coordinates attached it becomes part of a spatial pattern.
Context Attached
The map file records each accepted point with time, latitude, longitude, depth, temperature, satellite count, HDOP, speed, fix age, pitch, roll, and IMU accuracy. That structure keeps the measurement conditions attached to the point.
point,date_utc,time_utc,lat,lng,depth_cm,temp_c,satellites...
Raw vs Mapping Logs
The raw data log preserves the complete field record, including conditions that may not be suitable for mapping. The map log contains only accepted points. That separation makes the reconstruction step more predictable and keeps the record transparent.
Signal Conditioning
Cleaning the signal in the field.
Past the acceptance gates, the firmware conditions each reading before logging it. By the time the data reaches the Python scripts it has already been cleaned up.
Depth smoothing
Moving average and spike rejection
The firmware reads sonar range over UART with a checksum, then passes it through a 5-sample moving average. It discards samples that jump more than maxDepthJumpCm (50 cm) from the previous value, and marks depth invalid after a 2-second timeout with no valid return.
Sound-speed correction
Temperature-based, no salinity input
Acoustic depth depends on the speed of sound in water. The firmware adjusts the reported range from a 1500 m/s baseline using the measured DS18B20 temperature. It is a temperature-only model; the repository lists salinity-aware correction as a future improvement.
SD recovery
Reopen files after a write failure
If a log write fails or the card drops out, the logger retries SD.begin() and reopens both files on a 3-second interval, counting recoveries so a field session survives a transient card fault.
UTC time & GPS warmup
Trustworthy timestamps
All timestamps are logged in UTC to remove timezone ambiguity, and a 10-second GPS warmup window keeps early, unreliable fixes out of the mapping dataset.
Interpolation
Filling in the depth between measured points.
OMEGA uses inverse distance weighting to estimate values between measured points. The Python scripts build a grid over the survey area and estimate a depth for each grid cell. Nearby measured points receive more weight than distant ones.
scripts/csv_to_png_depth_interpolated.py
import numpy as np
def idw_interpolation(x, y, z, xi, yi, power=2.0, epsilon=1e-12):
"""
Inverse Distance Weighting (IDW) spatial interpolation.
"""
zi = np.zeros_like(xi, dtype=float)
# Iterate across the target 2D raster grid
for row in range(xi.shape[0]):
for col in range(xi.shape[1]):
dx = x - xi[row, col]
dy = y - yi[row, col]
dist = np.sqrt(dx * dx + dy * dy)
# Prevent division by zero
if np.any(dist < epsilon):
zi[row, col] = z[np.argmin(dist)]
continue
# Weight = 1 / (d^p)
weights = 1.0 / np.power(dist, power)
zi[row, col] = np.sum(weights * z) / np.sum(weights)
return zi
The Math: The weight of a known sonar point decreases as the distance increases. The scripts default to a power of 2.0 (inverse square), so a ping 2 meters away has 4 times less influence than a ping 1 meter away. The exponent is adjustable via --power, alongside --grid-size for interpolation density.
Data Pipelines
Offline Python Processing.
The same accepted points can become several different map products. OMEGA-bath includes a set of offline Python scripts that read a mapping CSV and produce interpolated depth surfaces, contour maps, and georeferenced Google Earth overlays.
csv_to_png_depth_points.py: Scatter view of the recorded points, colored by depth, for checking survey coverage before interpolation.
csv_to_png_depth_interpolated.py: Continuous depth surface estimated from the accepted points via IDW.
csv_to_png_depth_contours.py: Renders the interpolated surface as filled contour bands with sample points overlaid, emphasizing shape and slope.
csv_to_kml_points.py / csv_to_kml_points_colored.py: Export measured points to Google Earth, optionally colored by depth.
csv_to_kmz_depth_overlay.py: Places the interpolated surface over its geographic footprint in Google Earth, packaging the result into a compressed KMZ.
The repository documents the outputs as exploratory mapping products. The system uses single-beam depth sensing and IDW interpolation, so the reconstructed surface is a modeled estimate rather than a formal hydrographic survey.