486 lines
14 KiB
JavaScript
486 lines
14 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { after, before, test } from "node:test";
|
|
|
|
import { createServer } from "vite";
|
|
|
|
let server;
|
|
let parseLidarLocalSurfaceCatalog;
|
|
let parseLidarLocalSurfaceFrame;
|
|
let parseLidarLocalSurfaceTimeline;
|
|
let parseLidarLocalSurfaceReview;
|
|
let fetchLidarLocalSurfaceReview;
|
|
let fetchLidarLocalSurfaceTimeline;
|
|
let LidarLocalSurfaceContractError;
|
|
|
|
const modelId = `k1-local-surface-${"a".repeat(64)}`;
|
|
const sourcePackId = `e10-lidar-pack-${"b".repeat(64)}`;
|
|
|
|
function distribution(value) {
|
|
return {
|
|
sample_count: 10,
|
|
minimum: value,
|
|
mean: value,
|
|
p50: value,
|
|
p95: value,
|
|
maximum: value,
|
|
};
|
|
}
|
|
|
|
function policy(overrides = {}) {
|
|
return {
|
|
observed_surface_class: 1,
|
|
observed_occupied_class: 2,
|
|
below_surface_or_negative_outlier_class: 3,
|
|
absence_of_points_means_free: false,
|
|
unknown_is_traversable: false,
|
|
persistent_reconstruction_mutated: false,
|
|
dynamic_object_layer_available: false,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function model(overrides = {}) {
|
|
return {
|
|
model_id: modelId,
|
|
display_name: "RAVNOVES00 · динамическая локальная поверхность K1",
|
|
session_id: "20260720T065719Z_viewer_live",
|
|
source_pack_id: sourcePackId,
|
|
status: "diagnostic-only",
|
|
source: {
|
|
representation: "legacy-e10-vendor-map-with-pose",
|
|
immutable: true,
|
|
passive_processing_only: true,
|
|
firmware_or_device_commands_used: false,
|
|
frame_count: 12,
|
|
available_lidar_frames: 10,
|
|
point_count: 100,
|
|
timeline_start_seconds: 1,
|
|
timeline_end_seconds: 2,
|
|
},
|
|
surface_model: {},
|
|
occupancy_policy: policy(),
|
|
metrics: {
|
|
frames: {
|
|
total: 12,
|
|
source_available: 10,
|
|
valid: 10,
|
|
source_unavailable: 2,
|
|
pose_stale: 0,
|
|
insufficient_surface: 0,
|
|
fit_failed: 0,
|
|
},
|
|
sensor_height_m: distribution(1.3),
|
|
slope_deg: distribution(2),
|
|
roughness_m: distribution(0.04),
|
|
confidence: distribution(0.8),
|
|
pose_binding_age_ms: distribution(7),
|
|
surface_max_age_ms: distribution(1000),
|
|
temporal_qualification: {
|
|
prediction: {
|
|
current_frame_excluded: true,
|
|
sample_count: 9,
|
|
residual_p50_m: distribution(0.04),
|
|
residual_p95_m: distribution(0.2),
|
|
inlier_fraction: distribution(0.95),
|
|
},
|
|
stability: {
|
|
sample_count: 9,
|
|
height_delta_m: distribution(0.01),
|
|
slope_delta_deg: distribution(0.1),
|
|
roughness_delta_m: distribution(0.002),
|
|
jump_count: 1,
|
|
},
|
|
step_candidates: {
|
|
is_ground_truth: false,
|
|
frames_with_candidates: 8,
|
|
cell_count: distribution(20),
|
|
point_count: distribution(12),
|
|
},
|
|
},
|
|
build_elapsed_ms: 20,
|
|
},
|
|
anchors: [{
|
|
key: "long-street",
|
|
label: "Длинная улица",
|
|
frame_index: 2,
|
|
source_frame_index: 1350,
|
|
session_seconds: 1.5,
|
|
valid: true,
|
|
}],
|
|
decision: {
|
|
status: "replay-experiment-only",
|
|
production_promotion: false,
|
|
next_gate: "shadow",
|
|
},
|
|
created_at_utc: "2026-07-25T00:00:00Z",
|
|
ground_truth: false,
|
|
authority: {
|
|
commands_enabled: false,
|
|
navigation_or_safety_accepted: false,
|
|
},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function catalog(overrides = {}) {
|
|
return {
|
|
schema_version: "missioncore.k1-local-surface-catalog/v1",
|
|
configured: true,
|
|
items: [model()],
|
|
valid_total: 1,
|
|
invalid_total: 0,
|
|
access: "read-only",
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function frame(overrides = {}) {
|
|
return {
|
|
schema_version: "missioncore.k1-local-surface-frame/v2",
|
|
model_id: modelId,
|
|
source_pack_id: sourcePackId,
|
|
session_id: "20260720T065719Z_viewer_live",
|
|
frame_index: 2,
|
|
frame_count: 12,
|
|
source_frame_index: 1350,
|
|
session_seconds: 1.5,
|
|
source_available: true,
|
|
valid: true,
|
|
failure_code: 0,
|
|
point_count: 4,
|
|
coordinate_frame: "map",
|
|
distance_unit: "m",
|
|
points_xyz_m: [[0, 0, 0], [1, 0, 0], [1, 1, 0.7], [2, 0, -0.3]],
|
|
point_class: [1, 1, 2, 3],
|
|
point_height_m: [0, 0.02, 0.7, -0.3],
|
|
point_step_candidate: [0, 1, 0, 0],
|
|
pose: {
|
|
position_xyz_m: [0, 0, 1.3],
|
|
orientation_xyzw: [0, 0, 0, 1],
|
|
binding_age_ms: 7,
|
|
},
|
|
surface: {
|
|
plane_coefficients_map: [0, 0, 1, 0],
|
|
local_radius_m: 10,
|
|
sensor_height_m: 1.3,
|
|
slope_deg: 0,
|
|
roughness_m: 0.02,
|
|
confidence: 0.8,
|
|
surface_max_age_ms: 1000,
|
|
cell_count: 40,
|
|
inlier_cell_count: 35,
|
|
},
|
|
counts: {
|
|
classified: 4,
|
|
surface: 2,
|
|
occupied: 1,
|
|
below_surface: 1,
|
|
step_candidate: 1,
|
|
},
|
|
prediction: {
|
|
available: true,
|
|
current_frame_excluded: true,
|
|
cell_count: 2,
|
|
residual_p50_m: 0.04,
|
|
residual_p95_m: 0.2,
|
|
inlier_fraction: 0.95,
|
|
evidence: {
|
|
available: true,
|
|
basis: "current-lower-cell-observations",
|
|
coordinate_frame: "map",
|
|
distance_unit: "m",
|
|
current_frame_excluded_from_plane: true,
|
|
surface_inlier_band_m: 0.16,
|
|
prior_plane_coefficients_map: [0, 0, 1, 0],
|
|
cell_points_xyz_m: [[0.25, 0.25, 0.02], [1.25, 0.25, 0.18]],
|
|
cell_signed_residual_m: [0.02, 0.18],
|
|
cell_inlier: [1, 0],
|
|
ground_truth: false,
|
|
},
|
|
},
|
|
temporal: {
|
|
compared: true,
|
|
height_delta_m: 0.01,
|
|
slope_delta_deg: 0.1,
|
|
roughness_delta_m: 0.002,
|
|
jump: false,
|
|
step_candidate_cell_count: 5,
|
|
},
|
|
classes: {},
|
|
occupancy_policy: policy(),
|
|
ground_truth: false,
|
|
access: "read-only",
|
|
authority: {
|
|
commands_enabled: false,
|
|
navigation_or_safety_accepted: false,
|
|
},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function timeline(overrides = {}) {
|
|
return {
|
|
schema_version: "missioncore.k1-local-surface-timeline/v1",
|
|
model_id: modelId,
|
|
source_pack_id: sourcePackId,
|
|
session_id: "20260720T065719Z_viewer_live",
|
|
frame_count: 4,
|
|
source_frame_index: [1000, 1001, 1002, 1003],
|
|
session_seconds: [0, 0.1, 0.2, 0.3],
|
|
source_available: [1, 1, 1, 1],
|
|
valid: [1, 1, 1, 1],
|
|
prediction_available: [0, 1, 1, 1],
|
|
prediction_residual_p50_m: [0, 0.04, 0.05, 0.03],
|
|
prediction_residual_p95_m: [0, 0.2, 0.3, 0.15],
|
|
prediction_inlier_fraction: [0, 0.95, 0.92, 0.97],
|
|
sensor_height_m: [1.3, 1.31, 1.3, 1.29],
|
|
slope_deg: [1, 1.1, 1.2, 1],
|
|
roughness_m: [0.04, 0.04, 0.05, 0.04],
|
|
confidence: [0.8, 0.8, 0.75, 0.82],
|
|
temporal_compared: [0, 1, 1, 1],
|
|
height_delta_m: [0, 0.01, 0.01, 0.01],
|
|
slope_delta_deg: [0, 0.1, 0.1, 0.2],
|
|
roughness_delta_m: [0, 0, 0.01, 0.01],
|
|
temporal_jump: [0, 0, 1, 0],
|
|
step_candidate_point_count: [1, 2, 3, 4],
|
|
ground_truth: false,
|
|
access: "read-only",
|
|
authority: {
|
|
commands_enabled: false,
|
|
navigation_or_safety_accepted: false,
|
|
},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function review(overrides = {}) {
|
|
return {
|
|
schema_version: "missioncore.k1-local-surface-review/v1",
|
|
review_profile_id: "missioncore-local-surface-attention/v1",
|
|
model_id: modelId,
|
|
source_pack_id: sourcePackId,
|
|
session_id: "20260720T065719Z_viewer_live",
|
|
available: true,
|
|
criteria: {
|
|
prediction_tail_residual_p95_m: 0.45,
|
|
prediction_inlier_fraction_floor: 0.85,
|
|
surface_height_jump_m: 0.03,
|
|
surface_slope_jump_deg: 0.5,
|
|
surface_roughness_jump_m: 0.015,
|
|
high_attention_score: 2,
|
|
episode_max_frame_gap: 2,
|
|
},
|
|
summary: {
|
|
item_count: 2,
|
|
episode_count: 2,
|
|
high_attention_count: 1,
|
|
review_attention_count: 1,
|
|
reason_counts: {
|
|
"prediction-tail": 1,
|
|
"prediction-inlier-drop": 1,
|
|
"surface-height-jump": 1,
|
|
"surface-slope-jump": 0,
|
|
"surface-roughness-jump": 0,
|
|
},
|
|
},
|
|
items: [
|
|
{
|
|
rank: 1,
|
|
frame_index: 2,
|
|
source_frame_index: 1002,
|
|
session_seconds: 0.2,
|
|
episode_id: "episode-02",
|
|
attention: "high",
|
|
attention_score: 2.4,
|
|
reasons: ["prediction-tail", "prediction-inlier-drop"],
|
|
prediction: {
|
|
available: true,
|
|
residual_p50_m: 0.05,
|
|
residual_p95_m: 1.08,
|
|
inlier_fraction: 0.64,
|
|
},
|
|
temporal: {
|
|
compared: true,
|
|
height_delta_m: 0.01,
|
|
slope_delta_deg: 0.1,
|
|
roughness_delta_m: 0.002,
|
|
},
|
|
surface: {
|
|
sensor_height_m: 1.3,
|
|
slope_deg: 2,
|
|
roughness_m: 0.04,
|
|
confidence: 0.8,
|
|
},
|
|
step_candidate_point_count: 13,
|
|
},
|
|
{
|
|
rank: 2,
|
|
frame_index: 1,
|
|
source_frame_index: 1001,
|
|
session_seconds: 0.1,
|
|
episode_id: "episode-01",
|
|
attention: "review",
|
|
attention_score: 1.2,
|
|
reasons: ["surface-height-jump"],
|
|
prediction: {
|
|
available: true,
|
|
residual_p50_m: 0.04,
|
|
residual_p95_m: 0.2,
|
|
inlier_fraction: 0.95,
|
|
},
|
|
temporal: {
|
|
compared: true,
|
|
height_delta_m: 0.036,
|
|
slope_delta_deg: 0.1,
|
|
roughness_delta_m: 0.002,
|
|
},
|
|
surface: {
|
|
sensor_height_m: 1.34,
|
|
slope_deg: 2,
|
|
roughness_m: 0.04,
|
|
confidence: 0.8,
|
|
},
|
|
step_candidate_point_count: 20,
|
|
},
|
|
],
|
|
ground_truth: false,
|
|
access: "read-only",
|
|
authority: {
|
|
commands_enabled: false,
|
|
navigation_or_safety_accepted: false,
|
|
},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
before(async () => {
|
|
server = await createServer({
|
|
appType: "custom",
|
|
logLevel: "silent",
|
|
server: { middlewareMode: true },
|
|
});
|
|
({
|
|
parseLidarLocalSurfaceCatalog,
|
|
parseLidarLocalSurfaceFrame,
|
|
parseLidarLocalSurfaceTimeline,
|
|
parseLidarLocalSurfaceReview,
|
|
fetchLidarLocalSurfaceReview,
|
|
fetchLidarLocalSurfaceTimeline,
|
|
LidarLocalSurfaceContractError,
|
|
} = await server.ssrLoadModule("/src/core/lidar/localSurface.ts"));
|
|
});
|
|
|
|
after(async () => {
|
|
await server?.close();
|
|
});
|
|
|
|
test("decodes passive local-surface evidence", () => {
|
|
const decoded = parseLidarLocalSurfaceCatalog(catalog());
|
|
assert.equal(decoded.items[0].source.passiveProcessingOnly, true);
|
|
assert.equal(decoded.items[0].metrics.sensorHeightM.p50, 1.3);
|
|
assert.equal(decoded.items[0].anchors[0].sourceFrameIndex, 1350);
|
|
assert.equal(
|
|
decoded.items[0].metrics.temporalQualification.prediction.sampleCount,
|
|
9,
|
|
);
|
|
|
|
const decodedFrame = parseLidarLocalSurfaceFrame(frame());
|
|
assert.equal(decodedFrame.counts.occupied, 1);
|
|
assert.equal(decodedFrame.counts.stepCandidate, 1);
|
|
assert.equal(decodedFrame.prediction.currentFrameExcluded, true);
|
|
assert.equal(decodedFrame.prediction.evidence.available, true);
|
|
assert.equal(decodedFrame.prediction.evidence.cellPointsXyzM.length, 2);
|
|
assert.deepEqual(decodedFrame.prediction.evidence.cellInlier, [1, 0]);
|
|
assert.equal(decodedFrame.surface.sensorHeightM, 1.3);
|
|
|
|
const decodedTimeline = parseLidarLocalSurfaceTimeline(timeline());
|
|
assert.equal(decodedTimeline.frameCount, 4);
|
|
assert.deepEqual(decodedTimeline.temporalJump, [0, 0, 1, 0]);
|
|
assert.equal(decodedTimeline.predictionResidualP50M[2], 0.05);
|
|
|
|
const decodedReview = parseLidarLocalSurfaceReview(review());
|
|
assert.equal(decodedReview.summary.itemCount, 2);
|
|
assert.equal(decodedReview.items[0].attention, "high");
|
|
assert.deepEqual(decodedReview.items[0].reasons, [
|
|
"prediction-tail",
|
|
"prediction-inlier-drop",
|
|
]);
|
|
});
|
|
|
|
test("rejects inferred free space", () => {
|
|
assert.throws(
|
|
() => parseLidarLocalSurfaceCatalog(catalog({
|
|
items: [model({
|
|
occupancy_policy: policy({ absence_of_points_means_free: true }),
|
|
})],
|
|
})),
|
|
LidarLocalSurfaceContractError,
|
|
);
|
|
});
|
|
|
|
test("rejects command authority", () => {
|
|
assert.throws(
|
|
() => parseLidarLocalSurfaceFrame(frame({
|
|
authority: {
|
|
commands_enabled: true,
|
|
navigation_or_safety_accepted: false,
|
|
},
|
|
})),
|
|
LidarLocalSurfaceContractError,
|
|
);
|
|
});
|
|
|
|
test("rejects forged prediction residual evidence", () => {
|
|
const forged = frame();
|
|
forged.prediction.evidence.cell_inlier = [0, 0];
|
|
assert.throws(
|
|
() => parseLidarLocalSurfaceFrame(forged),
|
|
LidarLocalSurfaceContractError,
|
|
);
|
|
});
|
|
|
|
test("fetches the complete local-surface timeline read-only", async () => {
|
|
const requests = [];
|
|
const decoded = await fetchLidarLocalSurfaceTimeline(modelId, {
|
|
fetcher: async (input, init) => {
|
|
requests.push({ input: String(input), method: init?.method });
|
|
return new Response(JSON.stringify(timeline()), {
|
|
status: 200,
|
|
headers: { "Content-Type": "application/json" },
|
|
});
|
|
},
|
|
});
|
|
assert.deepEqual(requests, [{
|
|
input: `/api/v1/lidar/local-surfaces/${modelId}/timeline`,
|
|
method: "GET",
|
|
}]);
|
|
assert.equal(decoded.frameCount, 4);
|
|
});
|
|
|
|
test("fetches a deterministic local-surface review queue read-only", async () => {
|
|
const requests = [];
|
|
const decoded = await fetchLidarLocalSurfaceReview(modelId, {
|
|
fetcher: async (input, init) => {
|
|
requests.push({ input: String(input), method: init?.method });
|
|
return new Response(JSON.stringify(review()), {
|
|
status: 200,
|
|
headers: { "Content-Type": "application/json" },
|
|
});
|
|
},
|
|
});
|
|
assert.deepEqual(requests, [{
|
|
input: `/api/v1/lidar/local-surfaces/${modelId}/review`,
|
|
method: "GET",
|
|
}]);
|
|
assert.equal(decoded.items[0].sourceFrameIndex, 1002);
|
|
});
|
|
|
|
test("rejects a review queue with forged priority", () => {
|
|
const forged = review();
|
|
forged.items[0].attention = "review";
|
|
assert.throws(
|
|
() => parseLidarLocalSurfaceReview(forged),
|
|
LidarLocalSurfaceContractError,
|
|
);
|
|
});
|