remove comparisons, move panel

This commit is contained in:
legop3
2026-06-19 17:06:57 -04:00
parent 3bad454e0d
commit 36ff14824e
7 changed files with 13 additions and 121 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -78,8 +78,8 @@
<script defer src="https://analytics.otter.land/script.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land"></script> <script defer src="https://analytics.otter.land/script.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land"></script>
<script defer src="https://analytics.otter.land/recorder.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land" data-sample-rate="0.15" data-mask-level="moderate" data-max-duration="300000"></script> <script defer src="https://analytics.otter.land/recorder.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land" data-sample-rate="0.15" data-mask-level="moderate" data-max-duration="300000"></script>
<title>Roomba Rover</title> <title>Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-DXjwvKfx.js"></script> <script type="module" crossorigin src="/assets/index-CNhxRpdM.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BcP4x_Qk.css"> <link rel="stylesheet" crossorigin href="/assets/index-CxLsehkZ.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
@@ -35,19 +35,6 @@ function safeNumber(value, fallback = 0) {
return Number.isFinite(numeric) ? numeric : fallback; return Number.isFinite(numeric) ? numeric : fallback;
} }
function createInitialComparison() {
return {
sampleCount: 0,
missingDistanceSamples: 0,
ignoredSamples: 0,
encoderSessionMm: 0,
distancePacketSessionMm: 0,
signedEncoderSessionMm: 0,
signedDistancePacketSessionMm: 0,
last: null,
};
}
function normalizeStoredEntry(entry = {}) { function normalizeStoredEntry(entry = {}) {
const totalMm = Math.max(0, safeNumber(entry.totalMm, 0)); const totalMm = Math.max(0, safeNumber(entry.totalMm, 0));
const calibrationMultiplier = Math.max(0.01, safeNumber(entry.calibrationMultiplier, DEFAULT_CALIBRATION_MULTIPLIER)); const calibrationMultiplier = Math.max(0.01, safeNumber(entry.calibrationMultiplier, DEFAULT_CALIBRATION_MULTIPLIER));
@@ -82,7 +69,6 @@ function ensureLoaded() {
ignoredSamples: 0, ignoredSamples: 0,
status: 'waiting', status: 'waiting',
statusReason: 'waiting for encoder sample', statusReason: 'waiting for encoder sample',
comparison: createInitialComparison(),
lastEmittedAt: 0, lastEmittedAt: 0,
lastEmittedStatus: null, lastEmittedStatus: null,
updatedAt: normalized.updatedAt, updatedAt: normalized.updatedAt,
@@ -114,7 +100,6 @@ function ensureState(roverId) {
ignoredSamples: 0, ignoredSamples: 0,
status: 'waiting', status: 'waiting',
statusReason: 'waiting for encoder sample', statusReason: 'waiting for encoder sample',
comparison: createInitialComparison(),
lastEmittedAt: 0, lastEmittedAt: 0,
lastEmittedStatus: null, lastEmittedStatus: null,
updatedAt: null, updatedAt: null,
@@ -186,28 +171,6 @@ function saveNow() {
} }
} }
function percentDifference(differenceMm, referenceMm) {
const reference = Math.abs(safeNumber(referenceMm, 0));
if (reference < 1) return null;
return Math.round((Math.abs(differenceMm) / reference) * 1000) / 10;
}
function snapshotComparison(comparison = createInitialComparison()) {
const sessionDifferenceMm = comparison.distancePacketSessionMm - comparison.encoderSessionMm;
return {
sampleCount: comparison.sampleCount,
missingDistanceSamples: comparison.missingDistanceSamples,
ignoredSamples: comparison.ignoredSamples,
encoderSessionMm: Math.round(comparison.encoderSessionMm),
distancePacketSessionMm: Math.round(comparison.distancePacketSessionMm),
signedEncoderSessionMm: Math.round(comparison.signedEncoderSessionMm),
signedDistancePacketSessionMm: Math.round(comparison.signedDistancePacketSessionMm),
sessionDifferenceMm: Math.round(sessionDifferenceMm),
sessionDifferencePct: percentDifference(sessionDifferenceMm, comparison.encoderSessionMm),
last: comparison.last,
};
}
function snapshotState(state) { function snapshotState(state) {
if (!state) return null; if (!state) return null;
return { return {
@@ -226,52 +189,12 @@ function snapshotState(state) {
lastDelta: state.lastDelta, lastDelta: state.lastDelta,
rolloverEvents: state.rolloverEvents, rolloverEvents: state.rolloverEvents,
ignoredSamples: state.ignoredSamples, ignoredSamples: state.ignoredSamples,
comparison: snapshotComparison(state.comparison),
status: state.status, status: state.status,
statusReason: state.statusReason, statusReason: state.statusReason,
updatedAt: state.updatedAt, updatedAt: state.updatedAt,
}; };
} }
function updateDistancePacketComparison(state, sensors, encoderCenterMm, encoderDistanceMm, options = {}) {
const comparison = state.comparison || createInitialComparison();
state.comparison = comparison;
const packetMm = Number(sensors?.distanceMm);
const ignored = Boolean(options.ignored);
if (!Number.isFinite(packetMm)) {
comparison.missingDistanceSamples += 1;
return;
}
const packetDistanceMm = Math.abs(packetMm);
const signedDifferenceMm = packetMm - encoderCenterMm;
const distanceDifferenceMm = packetDistanceMm - encoderDistanceMm;
comparison.sampleCount += 1;
if (ignored) {
// Ignored encoder deltas are still recorded as a point-in-time diagnostic,
// but they are not folded into the session totals. That keeps reconnect
// edges from making the distance-packet comparison look worse than the
// odometer's own accepted movement stream.
comparison.ignoredSamples += 1;
} else {
comparison.encoderSessionMm += encoderDistanceMm;
comparison.distancePacketSessionMm += packetDistanceMm;
comparison.signedEncoderSessionMm += encoderCenterMm;
comparison.signedDistancePacketSessionMm += packetMm;
}
comparison.last = {
encoderCenterMm: Math.round(encoderCenterMm),
encoderDistanceMm: Math.round(encoderDistanceMm),
distancePacketMm: Math.round(packetMm),
distancePacketAbsMm: Math.round(packetDistanceMm),
signedDifferenceMm: Math.round(signedDifferenceMm),
distanceDifferenceMm: Math.round(distanceDifferenceMm),
signedDifferencePct: percentDifference(signedDifferenceMm, encoderCenterMm),
distanceDifferencePct: percentDifference(distanceDifferenceMm, encoderDistanceMm),
ignored,
};
}
function emitSnapshot(state, snapshot, options = {}) { function emitSnapshot(state, snapshot, options = {}) {
const force = Boolean(options.force); const force = Boolean(options.force);
const emittedRecently = nowMs() - safeNumber(state.lastEmittedAt, 0) < EMIT_THROTTLE_MS; const emittedRecently = nowMs() - safeNumber(state.lastEmittedAt, 0) < EMIT_THROTTLE_MS;
@@ -359,7 +282,6 @@ function processSensorFrame(roverId, sensors = {}) {
elapsedMs, elapsedMs,
ignored: true, ignored: true,
}; };
updateDistancePacketComparison(state, sensors, centerMm, distanceMm, { ignored: true });
state.updatedAt = sampleAt; state.updatedAt = sampleAt;
const snapshot = snapshotState(state); const snapshot = snapshotState(state);
emitSnapshot(state, snapshot); emitSnapshot(state, snapshot);
@@ -368,7 +290,6 @@ function processSensorFrame(roverId, sensors = {}) {
state.totalMm += distanceMm; state.totalMm += distanceMm;
state.sessionMm += distanceMm; state.sessionMm += distanceMm;
updateDistancePacketComparison(state, sensors, centerMm, distanceMm);
state.lastIntegratedAt = sampleAt; state.lastIntegratedAt = sampleAt;
state.status = 'tracking'; state.status = 'tracking';
state.statusReason = distanceMm > 0 ? 'integrated encoder delta' : 'no movement'; state.statusReason = distanceMm > 0 ? 'integrated encoder delta' : 'no movement';
@@ -393,7 +314,6 @@ function resetSession(roverId) {
const state = ensureState(roverId); const state = ensureState(roverId);
if (!state) return null; if (!state) return null;
state.sessionMm = 0; state.sessionMm = 0;
state.comparison = createInitialComparison();
state.updatedAt = nowMs(); state.updatedAt = nowMs();
const snapshot = snapshotState(state); const snapshot = snapshotState(state);
emitSnapshot(state, snapshot, { force: true }); emitSnapshot(state, snapshot, { force: true });
+1 -1
View File
@@ -193,8 +193,8 @@ function MobileFeatureTabs({
{/* activities tab */} {/* activities tab */}
<TabPanel id="activities"> <TabPanel id="activities">
<div className={`flex flex-col ${themeGapClass}`}> <div className={`flex flex-col ${themeGapClass}`}>
<OdometerPanel />
<BarcodeGamesPanel /> <BarcodeGamesPanel />
<OdometerPanel />
<ButtonBoxPanel /> <ButtonBoxPanel />
<KinectPanel /> <KinectPanel />
</div> </div>
+1 -29
View File
@@ -36,17 +36,6 @@ function formatCount(value) {
return Number.isFinite(Number(value)) ? String(Math.round(Number(value))) : '--'; return Number.isFinite(Number(value)) ? String(Math.round(Number(value))) : '--';
} }
function formatSignedDistance(mm) {
const value = Number(mm);
if (!Number.isFinite(value)) return '--';
const sign = value > 0 ? '+' : '';
return `${sign}${value < 0 ? '-' : ''}${formatDistance(Math.abs(value))}`;
}
function formatPercent(value) {
return Number.isFinite(Number(value)) ? `${Number(value).toFixed(1)}%` : '--';
}
function formatAge(timestamp, now) { function formatAge(timestamp, now) {
const value = Number(timestamp); const value = Number(timestamp);
if (!Number.isFinite(value)) return '--'; if (!Number.isFinite(value)) return '--';
@@ -124,9 +113,8 @@ export default function OdometerPanel() {
) : ( ) : (
<> <>
<OdometerSummary odometer={primary} rover={primaryRover} now={now} /> <OdometerSummary odometer={primary} rover={primaryRover} now={now} />
<div className="grid gap-0.5 md:grid-cols-2 xl:grid-cols-3"> <div className="grid gap-0.5 md:grid-cols-2">
<EncoderDetails odometer={primary} /> <EncoderDetails odometer={primary} />
<DistancePacketComparison odometer={primary} />
<CalibrationDetails odometer={primary} now={now} /> <CalibrationDetails odometer={primary} now={now} />
</div> </div>
<RoverOdometerList rows={visibleRows} primaryRoverId={primaryRoverId} /> <RoverOdometerList rows={visibleRows} primaryRoverId={primaryRoverId} />
@@ -178,22 +166,6 @@ function EncoderDetails({ odometer }) {
); );
} }
function DistancePacketComparison({ odometer }) {
const comparison = odometer?.comparison || null;
const last = comparison?.last || null;
return (
<DetailCard title="Distance packet comparison">
<ValueRow label="Last encoder" value={formatSignedDistance(last?.encoderCenterMm)} />
<ValueRow label="Last packet" value={formatSignedDistance(last?.distancePacketMm)} />
<ValueRow label="Last signed diff" value={`${formatSignedDistance(last?.signedDifferenceMm)} · ${formatPercent(last?.signedDifferencePct)}`} />
<ValueRow label="Session encoder" value={formatDistance(comparison?.encoderSessionMm)} />
<ValueRow label="Session packet" value={formatDistance(comparison?.distancePacketSessionMm)} />
<ValueRow label="Session diff" value={`${formatSignedDistance(comparison?.sessionDifferenceMm)} · ${formatPercent(comparison?.sessionDifferencePct)}`} />
<ValueRow label="Samples" value={`${formatCount(comparison?.sampleCount)} compared · ${formatCount(comparison?.missingDistanceSamples)} missing`} />
</DetailCard>
);
}
function CalibrationDetails({ odometer, now }) { function CalibrationDetails({ odometer, now }) {
return ( return (
<DetailCard title="Conversion"> <DetailCard title="Conversion">
+1 -1
View File
@@ -366,8 +366,8 @@ export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
{/* activities tab */} {/* activities tab */}
<TabPanel id="activities"> <TabPanel id="activities">
<div className={`flex flex-col ${themeGapClass}`}> <div className={`flex flex-col ${themeGapClass}`}>
<OdometerPanel />
<BarcodeGamesPanel /> <BarcodeGamesPanel />
<OdometerPanel />
<ButtonBoxPanel /> <ButtonBoxPanel />
<KinectPanel /> <KinectPanel />
</div> </div>