OMEGA decides whether a point is acceptable before it joins the map dataset.
Bathymetry depends on where a depth was measured, how stable the platform was at that moment, and whether the sensor values were still trustworthy. The validation logic makes those conditions explicit.
getMapStatus()
const char* getMapStatus(unsigned long currentMillis) {
if (millis() - startupMillis < gpsWarmupTime) return "WARMUP";
if (!mapReady || !sdReady || !mapFile) return "NOFILE";
if (!gpsDataPresent(currentMillis)) return "NOGPS";
if (!gps.location.isValid()) return "NOFIX";
if (!gps.date.isValid() || !gps.time.isValid()) return "NOTIME";
if (isnan(depthCm)) return "NODEP";
if (depthSamplesCollected < 3) return "WARM";
if (!gps.satellites.isValid()) return "SAT";
if (gps.satellites.value() < minMapSats) return "SAT";
if (!gps.hdop.isValid()) return "HDOP";
if (gps.hdop.hdop() > maxMapHdop) return "HDOP";
if (gps.location.age() > maxMapFixAgeMs) return "STALE";
if (!isnan(smoothedSpeed) && smoothedSpeed > maxMapSpeedKmph) return "FAST";
if (requireLevelForMapping) {
if (!imuReady) return "NOIMU";
if (!imuOrientationValid) return "IMU?";
if (imuAccuracy < minImuAccuracyForMapping) return "CAL";
if (fabs(imuPitchDeg) > maxMapPitchDeg || fabs(imuRollDeg) > maxMapRollDeg) return "UNLVL";
}
return "ARMED";
}
The returned status tells the rest of the system whether the logger is still warming up, missing required data,
or fully ready to write a mapping-quality point. The accepted state is ARMED.
The validation rules are really rules about evidence.
Each check asks a different question about the point. Does the system know where it is? Is the fix current? Is the depth stable? Is the vehicle moving too fast or tilted too far? Those questions shape the survey.
| Status | What it means | Why it affects mapping |
|---|---|---|
NOFIX | No valid GPS position | A depth without a reliable location cannot be placed correctly in the bathymetric surface. |
HDOP | Horizontal precision is missing or too poor | Even a good depth can distort the map if the position uncertainty is too large. |
STALE | The GPS fix is too old | The platform may have moved since the last valid position was reported. |
NODEP / WARM | Depth is unavailable or not yet stabilized | The logger waits until multiple readings have built a stable working depth. |
FAST | The platform is moving faster than the mapping limit | Rapid motion reduces how well each depth represents the bottom directly beneath the sensor. |
UNLVL | Pitch or roll exceeds the allowed limit | Excess tilt changes the geometry of the measurement and weakens the assumption of a near-vertical depth reading. |
ARMED | All required conditions are currently met | The point is eligible to enter the mapping dataset. |
OMEGA smooths depth and rejects sharp spikes.
The logger uses a moving average buffer to keep the working depth from jumping around. It also rejects isolated spikes when a new average would jump too far from the previous accepted depth.
void addDepthSample(float sample) {
depthReadingsTotal -= depthReadingsBuffer[depthBufferIndex];
depthReadingsBuffer[depthBufferIndex] = sample;
depthReadingsTotal += sample;
depthBufferIndex = (depthBufferIndex + 1) % DEPTH_BUFFER_SIZE;
if (depthSamplesCollected < DEPTH_BUFFER_SIZE) {
depthSamplesCollected++;
}
float newDepth = depthReadingsTotal / depthSamplesCollected;
// Reject a jump that is too large to trust as the next point.
if (!isnan(lastDepthCm) && fabs(newDepth - lastDepthCm) > maxDepthJumpCm) {
return;
}
depthCm = newDepth;
lastDepthCm = depthCm;
}Instead of letting every raw pulse become a survey point, the logger forms a short memory of recent readings and compares the new value to the previous accepted depth. That reduces noise without erasing genuine depth variation.
The logger also avoids collecting too many nearly identical points.
OMEGA checks the distance from the last accepted map point and can hold the next one if the platform has not moved far enough. The minimum spacing is adaptive: it grows with speed inside a constrained range.
if (hasLastMapPoint) {
double distanceMeters = TinyGPSPlus::distanceBetween(
gps.location.lat(), gps.location.lng(),
lastMapLat, lastMapLng
);
float minSpacing = minMapSpacingMetersBase;
if (!isnan(smoothedSpeed)) {
minSpacing = constrain(smoothedSpeed * 0.5f, 2.0f, 10.0f);
}
if (distanceMeters < minSpacing) return "HOLD";
}A map built from a tight cluster of nearly duplicate points is less useful than a map built from points that are spread across the survey path. Spacing helps distribute control points through the area instead of oversampling one small stretch.