Adding land features including lidar

This commit is contained in:
2026-04-21 20:00:11 -07:00
parent 2396f94879
commit 8f052b6595
4 changed files with 600 additions and 3 deletions

249
CLAUDE.md
View File

@@ -8,7 +8,7 @@ with AMD AI chip R9-8945HS with 32 GB ram
# Project: C++ OpenGL Radar Simulation
**Environment:** Ubuntu Linux (Remote SSH from Windows)
**Tech Stack:** C++20, OpenGL 3.3 Core, GLFW, GLAD, FreeType
**Tech Stack:** C++20, OpenGL 3.3 Core, GLFW, GLAD, FreeType, GDAL (libgdal-dev)
The operating system is Linux (Ubuntu)
@@ -51,6 +51,15 @@ entire directory list is /home/maallyn/new-radar on the Geekom.
./LICENSE
./src
./src/CLAUDE.md
./map
./map/lidar_processed
./map/charts_enc
./map/charts_enc/US5WA45M.000
./map/charts_enc/n48_w123_1arc_v3.tif
./map/lidar_raw
./map/lidar_raw/wa2022_nooksack_dem_J1364940.zip
./map/lidar_raw/wa2016_west_dem_J1364939.zip
==================================================================
@@ -641,6 +650,16 @@ settings.h — tunable constants:
- Rain clutter filter: default (0.0 = off), minimum (0.0), maximum (1.0), keyboard step size
- Wave clutter filter: default (0.0 = off), minimum (0.0), maximum (1.0), keyboard step size
- Key-hold acceleration for gain, rain clutter, and wave clutter keys
- Terrain bounding box (lat/lon min/max) and processed cell size
- Terrain material σ° values (soil, rock, concrete, water calm/rough)
- Terrain material speckle/grain amplitudes (soil, rock, concrete)
- Terrain classification thresholds (rock elevation, rock slope)
- Terrain polar grid dimensions (range bins, bearing bins)
- Terrain clutter brightness scale for marine PPI
- Terrain boat recompute threshold (degrees bearing offset change)
- ATC terrain clutter suppressed flag (bool, default true)
- ATC terrain shadow enabled flag (bool, default true)
- LiDAR structure height threshold for man-made classification
==================================================================
@@ -1055,6 +1074,220 @@ in settings.h so they can be tuned without touching equation code.
==================================================================
TERRAIN AND LAND CLUTTER
==================================================================
DATA SOURCES
map/charts_enc/US5WA45M.000
S-57 Electronic Navigational Chart; coastline polygon,
pier/breakwater geometry, named landmarks. Parsed with GDAL/OGR.
map/charts_enc/n48_w123_1arc_v3.tif
USGS 1 arc-second (~30 m) DEM GeoTIFF. Terrain elevation for the
Bellingham area — Cascades, Chuckanut Mountain, coastal lowlands.
map/lidar_raw/wa2016_west_dem_J1364939.zip
2016 western WA LiDAR DEM. Resolves waterfront structures,
breakwaters, piers, Boulevard Park boardwalk, and other man-made
features at ~1 m resolution.
map/lidar_raw/wa2022_nooksack_dem_J1364940.zip
2022 Nooksack basin LiDAR DEM. Covers the delta and lowlands
northeast of Bellingham Bay; used for terrain shadowing from the
northeast quadrant.
All three sources are processed offline by terrain_preprocess into
binary grids in map/lidar_processed/. The raw files are never opened
at exhibit runtime. See TERRAIN PREPROCESSING section below.
GDAL (libgdal-dev) is a required build dependency for
terrain_preprocess. It is NOT linked into the main radar binary.
CLASSES
TerrainMap (Thread 1, read-only after init)
Loaded once at startup from map/lidar_processed/. Reads the four
binary grids and the metadata JSON. Provides:
- Elevation query by lat/lon
- Material query by lat/lon
- Pre-computed polar clutter grid (range × bearing bins) per
fixed radar location
- Line-of-sight shadow mask per radar location
Thread 1 only after init; no mutex required.
LandClutter (Thread 1)
Queries TerrainMap to generate clutter returns for each scope.
Called once per full sweep rotation, not once per frame.
- Marine A-Scope: produces amplitude samples along the current
antenna bearing for injection into the range trace.
- Marine PPI / ATC PPI: produces a polar texture (range × bearing)
uploaded to the GPU once per sweep period.
TERRAIN MATERIALS AND RCS
Each terrain cell is classified as one of four materials. Normalized
backscatter coefficient σ° (linear, m²/m²) is defined in settings.h:
TERRAIN_SIGMA0_SOIL ~0.010 (20 dB) — moist vegetated soil
TERRAIN_SIGMA0_ROCK ~0.032 (15 dB) — exposed rock, rough face
TERRAIN_SIGMA0_CONCRETE ~0.100 (10 dB) — smooth hard surface;
structures produce
corner-reflector effect
TERRAIN_SIGMA0_WATER_CALM ~0.0003 (35 dB) — open water, calm;
mostly specular, low
backscatter
TERRAIN_SIGMA0_WATER_ROUGH ~0.010 (20 dB) — choppy sea, sea clutter
Classification rules applied by terrain_preprocess at build time:
- Below sea level or inside S-57 coastline polygon → water
- Inside S-57 pier/breakwater/wharf feature → concrete
- Elevation > TERRAIN_ROCK_THRESHOLD_M AND slope
> TERRAIN_ROCK_SLOPE_THRESHOLD_DEG → rock
- All remaining land cells → soil
RADAR EQUATION FOR TERRAIN (area-extensive / clutter form)
P_r = (P_t ×× λ² × σ° × A_cell) / ((4π)³ × R⁴)
A_cell is the terrain cell area projected along the beam.
Evaluated CPU-side in LandClutter for each illuminated cell.
Result drives per-cell brightness in the clutter texture.
Same radar parameters (P_t, G, λ) used for point targets.
SHADOW / LINE-OF-SIGHT MASKING
Computed by terrain_preprocess for each fixed radar location and
stored in the processed data as uint8 shadow masks. Algorithm: march
outward from the radar along each azimuth radial, tracking the maximum
elevation angle seen. Any cell whose surface angle falls below that
maximum is shadowed — clutter amplitude = 0 and target returns through
that cell are attenuated proportionally.
Shadow masks stored per radar location:
map/lidar_processed/shadow_marine.u8 — marine bay platform
map/lidar_processed/shadow_atc.u8 — BLI ATC tower
Bearing offset (k/j keys on PPI scopes):
The k/j keys change display heading only — which direction appears
at the top of the scope. They do NOT move the radar geographically.
The shadow mask is unchanged. The terrain clutter shader receives
the offset as a rotation uniform and samples the polar texture at
the offset angle. Zero CPU overhead; no shadow recomputation.
Future boat scenario (not in v1):
If the radar antenna physically moves to a new lat/lon, the shadow
mask would be recomputed on a background thread while the display
continues with the previous mask.
PER-SCOPE TERRAIN BEHAVIOR
Marine A-Scope:
Land returns appear as stable blips at fixed ranges on the current
antenna bearing. Concrete structures give strong returns; soil/hills
give moderate returns; shadowed areas return nothing. Period
operators were trained to discard stable blips when searching for
moving targets.
Marine PPI (fixed platform and boat heading offset):
Land clutter is fully visible. Coastline, hills, piers, and
breakwaters paint exactly as on a real period marine radar. The
clutter texture updates once per 4-second sweep rotation.
ATC PPI:
Moving Target Indicator (MTI) cancellation suppresses land clutter.
Controlled by ATC_TERRAIN_CLUTTER_SUPPRESSED (default true) in
settings.h. Terrain shadowing of aircraft IS applied — controlled
by ATC_TERRAIN_SHADOW_ENABLED (default true). Aircraft approaching
behind a ridge appear at reduced amplitude or disappear until they
clear the ridge, as on period ASR equipment.
Chain Home A-Scope:
No terrain data applied. The exhibit scenario faces the English
Channel; the transmitter floodlights the sea. Land returns are
not simulated for this scope.
PAR:
No terrain clutter. PAR points at a single fixed approach path;
the narrow beam and short range make land returns negligible.
TERRAIN CLUTTER SHADER
terrain_clutter.vert / terrain_clutter.frag
Renders the polar clutter texture as a quad overlay on the PPI
scope. Converts screen coordinates to polar, samples the clutter
texture, and outputs P7-compatible phosphor color and alpha so
terrain returns decay on the same timescale as target echoes.
Uniforms:
u_bearingOffsetDeg — boat heading correction (default 0.0)
u_clutterSuppressed — bool; 1 = suppress (ATC mode)
u_maxRangeM — current scope max range in meters
u_clutterBrightness — TERRAIN_MARINE_CLUTTER_BRIGHTNESS scale
Used by MarinePPIScope and ATCPPIScope.
==================================================================
TERRAIN PREPROCESSING
==================================================================
The raw LiDAR zip files cannot be used at exhibit runtime. The offline
tool terrain_preprocess processes them once and writes ready-to-use
binary grids to map/lidar_processed/. Must be run before first launch
and re-run whenever TERRAIN_BBOX_* or TERRAIN_PROCESSED_CELL_DEG
constants in settings.h change.
TOOL
Build target: terrain_preprocess (separate CMake executable)
Source: src/terrain_preprocess.cpp
Links: GDAL only; NOT linked into the main radar binary
Run: ./terrain_preprocess (from build directory)
PIPELINE (runs in order)
1. Unzip both LiDAR archives to a temp directory.
2. Inventory tiles — enumerate .tif / .img files, check CRS and
native resolution of each.
3. Merge tiles within each survey into a GDAL VRT mosaic.
4. Warp both surveys to WGS84 (EPSG:4326) at
TERRAIN_PROCESSED_CELL_DEG resolution.
5. Crop to bounding box: TERRAIN_BBOX_LAT_MIN/MAX,
TERRAIN_BBOX_LON_MIN/MAX.
6. Merge the two surveys — where they overlap, the 2022 Nooksack
data wins over the 2016 western data (higher vintage / resolution).
7. Material classification:
Load S-57 ENC (US5WA45M.000) via GDAL/OGR.
Apply classification rules described in TERRAIN section above.
8. Compute shadow mask for marine platform and ATC tower using radial
elevation-angle march along each azimuth bearing.
9. Write to map/lidar_processed/:
elevation.f32 float32 row-major grid, meters, WGS84
material.u8 uint8 per cell (0=water 1=soil 2=rock 3=concrete)
shadow_marine.u8 uint8 visibility mask for marine radar
shadow_atc.u8 uint8 visibility mask for ATC radar
terrain_meta.json grid dimensions, lat/lon origin, cell size,
source file checksums, processing date
RUNTIME VALIDATION
TerrainMap reads terrain_meta.json at startup and compares the stored
bounding box and cell-size values against current settings.h constants.
If they differ it prints a warning and continues with stale data:
WARNING: terrain data was built with different bounding box or
cell size — re-run terrain_preprocess before exhibit launch.
The exhibit does not crash; it runs with the old grid.
OUTPUT FILES (map/lidar_processed/)
elevation.f32 — float32 elevation grid
material.u8 — uint8 material classification grid
shadow_marine.u8 — uint8 line-of-sight mask, marine radar
shadow_atc.u8 — uint8 line-of-sight mask, ATC radar
terrain_meta.json — metadata and provenance record
These five files are the only terrain inputs at runtime.
The raw zip archives in map/lidar_raw/ are never opened by
the exhibit binary.
==================================================================
FILE LAYOUT (COMPLETE — including additions)
==================================================================
@@ -1082,6 +1315,13 @@ src/
rpi_receiver.h / rpi_receiver.cpp
db_panel.h / db_panel.cpp — Dear ImGui DB management panel
(--database mode only)
terrain_map.h / terrain_map.cpp — DEM load, shadow mask, polar clutter
grid; read-only after init, Thread 1
land_clutter.h / land_clutter.cpp — per-sweep clutter arrays for A-scope
range trace and PPI clutter texture
terrain_preprocess.cpp — standalone offline preprocessing tool;
separate CMake target; links GDAL only;
NOT part of main radar binary
settings.h — all constexpr constants; no .cpp
imgui/ — Dear ImGui source, compiled in
@@ -1091,8 +1331,11 @@ src/
imgui_draw.cpp / imgui_tables.cpp / imgui_widgets.cpp
shaders/
phosphor.vert / phosphor.frag — P1 and P7 via uniforms
phosphor.vert / phosphor.frag — P1 and P7 via uniforms
graticule.vert / graticule.frag
text.vert / text.frag
sweep.vert / sweep.frag
bloom.vert / bloom.frag — FBO bloom post-processing
bloom.vert / bloom.frag — FBO bloom post-processing
terrain_clutter.vert / terrain_clutter.frag — polar clutter texture overlay
on PPI; P7-compatible decay;
bearing offset rotation uniform