///|
pub(all) enum HazardClass {
Clear
Caution
Blocked
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct TerrainGrid {
tile_id : String
rows : Int
cols : Int
cell_size_m : Double
source_manifest : TerrainSourceManifest
cells : Array[@core.TerrainCell]
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct TerrainMetrics {
min_elevation_m : Double
max_elevation_m : Double
elevation_range_m : Double
max_neighbor_grade : Double
roughness_m : Double
hazard_class : HazardClass
hazard_reason : String
provenance : @core.Provenance
uncertainty : @core.Uncertainty
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) enum TerrainFixtureValidationStatus {
Verified
FingerprintMismatch
MissingFingerprint
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct TerrainFixtureValidation {
dataset_id : @core.DatasetId
expected_fingerprint : String
actual_fingerprint : String
status : TerrainFixtureValidationStatus
note : String
} derive(Debug, Eq, ToJson, FromJson)
///|
pub fn hazard_label(hazard : HazardClass) -> String {
match hazard {
Clear => "clear"
Caution => "caution"
Blocked => "blocked"
}
}
///|
pub fn terrain_fixture_validation_status_label(
status : TerrainFixtureValidationStatus,
) -> String {
match status {
Verified => "verified"
FingerprintMismatch => "fingerprint-mismatch"
MissingFingerprint => "missing-fingerprint"
}
}
///|
fn abs_double(value : Double) -> Double {
if value < 0.0 {
0.0 - value
} else {
value
}
}
///|
fn cell_at(grid : TerrainGrid, row : Int, col : Int) -> @core.TerrainCell? {
if row < 0 || col < 0 || row >= grid.rows || col >= grid.cols {
None
} else {
grid.cells.get(row * grid.cols + col)
}
}
///|
fn first_cell(grid : TerrainGrid) -> @core.TerrainCell {
grid.cells.get(0).unwrap()
}
///|
fn elevation_range(grid : TerrainGrid) -> (Double, Double) {
let first = first_cell(grid)
let mut min_value = first.elevation_m
let mut max_value = first.elevation_m
for cell in grid.cells {
if cell.elevation_m < min_value {
min_value = cell.elevation_m
}
if cell.elevation_m > max_value {
max_value = cell.elevation_m
}
}
(min_value, max_value)
}
///|
fn neighbor_pairs(grid : TerrainGrid) -> Array[(Double, Double)] {
let pairs : Array[(Double, Double)] = []
for row in 0.. pairs.push((cell.elevation_m, east.elevation_m))
None => ()
}
match cell_at(grid, row + 1, col) {
Some(south) => pairs.push((cell.elevation_m, south.elevation_m))
None => ()
}
}
}
pairs
}
///|
fn classify_hazard(
max_grade : Double,
roughness_m : Double,
) -> (HazardClass, String) {
if max_grade >= 0.35 || roughness_m >= 2.5 {
(Blocked, "neighbor grade or roughness exceeds early rover traverse limits")
} else if max_grade >= 0.18 || roughness_m >= 1.2 {
(Caution, "terrain needs operator review before traverse planning")
} else {
(Clear, "terrain is smooth enough for the first trusted-square fixture")
}
}
///|
pub fn analyze(grid : TerrainGrid) -> TerrainMetrics {
let (min_elevation_m, max_elevation_m) = elevation_range(grid)
let pairs = neighbor_pairs(grid)
let mut max_delta = 0.0
let mut total_delta = 0.0
for pair in pairs {
let delta = abs_double(pair.0 - pair.1)
total_delta += delta
if delta > max_delta {
max_delta = delta
}
}
let roughness_m = if pairs.is_empty() {
0.0
} else {
total_delta / pairs.length().to_double()
}
let max_neighbor_grade = if grid.cell_size_m <= 0.0 {
0.0
} else {
max_delta / grid.cell_size_m
}
let (hazard_class, hazard_reason) = classify_hazard(
max_neighbor_grade, roughness_m,
)
let first = first_cell(grid)
{
min_elevation_m,
max_elevation_m,
elevation_range_m: max_elevation_m - min_elevation_m,
max_neighbor_grade,
roughness_m,
hazard_class,
hazard_reason,
provenance: @core.derived_provenance(
first.provenance,
"moonmoon/src/terrain.analyze",
"terrain-metrics.v1",
),
uncertainty: @core.uncertainty(
first.uncertainty.confidence * 0.92,
"Derived from a tiny fixture; useful for software proof, not mission certification.",
),
}
}
///|
pub fn grid_fingerprint(grid : TerrainGrid) -> String {
let first = first_cell(grid)
let last = grid.cells.get(grid.cells.length() - 1).unwrap()
"inline-grid-v1:tile=\{grid.tile_id}:rows=\{grid.rows}:cols=\{grid.cols}:cell-size-m=\{grid.cell_size_m}:cells=\{grid.cells.length()}:first=\{first.elevation_m}:last=\{last.elevation_m}"
}
///|
pub fn validate_grid_source(grid : TerrainGrid) -> TerrainFixtureValidation {
let expected_fingerprint = grid.source_manifest.checksum
let actual_fingerprint = grid_fingerprint(grid)
let status = if expected_fingerprint == "" || actual_fingerprint == "" {
MissingFingerprint
} else if expected_fingerprint == actual_fingerprint {
Verified
} else {
FingerprintMismatch
}
{
dataset_id: grid.source_manifest.dataset_id,
expected_fingerprint,
actual_fingerprint,
status,
note: match status {
Verified => "terrain fixture fingerprint matches manifest"
FingerprintMismatch => "terrain fixture fingerprint differs from manifest"
MissingFingerprint =>
"terrain fixture manifest or generated grid fingerprint is missing"
},
}
}
///|
pub fn render_grid_markdown(grid : TerrainGrid) -> String {
let metrics = analyze(grid)
let manifest = grid.source_manifest
let validation = validate_grid_source(grid)
let mut text = "# Terrain Fixture: \{grid.tile_id}\n\n"
text += "## Source\n\n"
text += "- dataset: \{manifest.dataset_id}\n"
text += "- title: \{manifest.title}\n"
text += "- trust: \{terrain_source_trust_label(manifest.trust)}\n"
text += "- claim: \{@core.claim_kind_label(manifest.claim_kind)}\n"
text += "- resolution: \{manifest.resolution_m} m\n"
text += "- source path: \{manifest.source_path}\n"
text += "- source sha256: \{manifest.source_sha256}\n"
text += "- extractor: \{manifest.extractor}\n"
text += "- checksum kind: \{terrain_source_checksum_kind_label(manifest.checksum_kind)}\n"
text += "- checksum: \{manifest.checksum}\n\n"
text += "## Terrain Fixture Validation\n\n"
text += "- status: \{terrain_fixture_validation_status_label(validation.status)}\n"
text += "- actual: \{validation.actual_fingerprint}\n"
text += "- note: \{validation.note}\n\n"
text += "## Grid\n\n"
text += "- rows: \{grid.rows}\n"
text += "- cols: \{grid.cols}\n"
text += "- cell size: \{grid.cell_size_m} m\n"
text += "- cells: \{grid.cells.length()}\n\n"
text += "## Derived Metrics\n\n"
text += "- elevation range: \{metrics.elevation_range_m} m (\{metrics.min_elevation_m} to \{metrics.max_elevation_m})\n"
text += "- max neighbor grade: \{metrics.max_neighbor_grade}\n"
text += "- roughness: \{metrics.roughness_m} m\n"
text += "- hazard: \{hazard_label(metrics.hazard_class)} - \{metrics.hazard_reason}\n"
text
}
///|
pub fn first_trusted_square_fixture_markdown() -> String {
render_grid_markdown(first_trusted_square_grid())
}
///|
pub fn render_grid_json(grid : TerrainGrid) -> Json {
grid.to_json()
}
///|
pub fn first_trusted_square_fixture_json() -> Json {
render_grid_json(first_trusted_square_grid())
}