# Radar Exhibit — Design Document ## Hardware target - Geekom A8 Max: AMD Ryzen 9 8945HS, Radeon 780M (Mesa/RADV), 32 GB RAM - Ubuntu 25.10, g++ 15.2.0, C++20, OpenGL 4.3 Core --- ## Screen layout Three fixed regions rendered with `glViewport` + `glScissor`: | Region | Content | |--------|---------| | Left panel | Text description, control list (white text; red labels; pink keystrokes) | | Right panel — upper | Radar scope | | Right panel — lower | Status bar: range, bearing, scope ID (yellow text) | --- ## Scope sequence (cycled with keys 1 / 2) 1. Exhibit introduction (text only) 2. Chain Home A-Scope (1940s) 3. Marine A-Scope (1940s) 4. PPI — stationary marine traffic control 5. PPI — on-board boat Each scope's control state is **independent** and **resets on re-entry**. --- ## Radar equation $$P_r = \frac{P_t G^2 \lambda^2 \sigma}{(4\pi)^3 R^4}$$ Signal strength falls off with $1/R^4$. Each radar type has fixed hardware loop gain baked into its shader set as a constant. --- ## Controls (keyboard until physical panel is built) | Key | Action | |-----|--------| | 1 / 2 | Next / previous scope | | 3 / 4 | Intensity down / up | | 5 / 6 | Receiver sensitivity down / up | | q / w | STC sensitivity down / up | | e / r | STC range down / up | | t / y | Radiogoniometer left / right (Chain Home only) | | u / i | Max range down / up (all except Chain Home) | | o / p | Range cursor down / up (PPI only) | | a / s | Bearing cursor CCW / CW (PPI only) | | d / f | Antenna bearing CCW / CW (Marine A-Scope only) | Notes: - Radiogoniometer, marine antenna bearing, and PPI bearing cursor share one physical knob on the eventual panel; kept separate in software. - Range cursor ≠ max range: max range is the radar's selected range setting; range cursor is the measurement cursor on the PPI display. --- ## Scope specifications ### Chain Home A-Scope - Fixed range: 200 miles - No graticule; crystal-oscillator range pips every 20 miles (operator cannot change) - Bearing via radiogoniometer simulation: operator finds null point; bearing shown as text below scope - Two perpendicular antenna sets (N-S, E-W); radiogoniometer angle determines which target pip goes to null ### Marine A-Scope - Ranges: 1.5 / 3.0 / 6.0 / 12.0 miles - Range pips: every 0.25 / 0.5 / 1.0 / 2.0 miles respectively (fixed oscillator; not affected by range setting) - No graticule - Bearing by rotating simulated dish/horn antenna (d/f keys) - Pip shape: finite rise, fixed-width pulse, finite fall — curved waveform, not a vertical line ### PPI scopes (both) - 360° sweep with phosphor persistence via FBO (semi-transparent black quad each frame; no full clear) - Range cursor and bearing cursor overlay #### Stationary (marine traffic control) - Fixed observer position; targets move relative to scope center #### On-boat - Observer is scope center; heading-up display — world rotates as boat heading changes - Boat position/heading supplied from GDAL/PostgreSQL data --- ## Architecture ### Class hierarchy (State pattern) ``` BaseScope — intensity, sensitivity, STC vars, virtual render/update ├── AScope — horizontal sweep, curved pip waveform │ ├── ChainHomeScope — radiogoniometer, fixed 200 mi range, 20 mi pips │ └── MarineAScope — antenna bearing knob, 4 range settings └── PPIScope — 360° sweep, FBO persistence, range/bearing cursors ├── StationaryPPI └── OnBoatPPI — heading offset passed into shader ``` ### Threading model | Thread | Responsibility | |--------|---------------| | Render (main) | OpenGL loop, GLFW input, all `glDraw*` calls — never blocks | | Simulation | Polls `target_data` (PostgreSQL) and GDAL; updates shared `std::vector` under `std::mutex` | ### FBO phosphor decay - Render sweep into FBO each frame - Blit FBO to screen - Do **not** clear color buffer between frames; draw full-screen quad at very low alpha (~0.05) to decay old sweeps ### Shader notes - GL_DEBUG_OUTPUT + `glDebugMessageCallback` enabled at startup (GPU robustness protocol) - No NVIDIA-specific extensions; Mesa/RADV only - $1/R^4$ attenuation computed in fragment shader; hardware loop gain is a per-scope `uniform` constant --- ## File structure ``` new-radar/ ├── CMakeLists.txt ├── src/ — .cpp source files ├── include/ — .h/.hpp headers │ ├── glad/ │ └── KHR/ ├── glad/src/ — glad.c (bundled) ├── shaders/ — .vert / .frag files ├── data/ — patrol_route.json, etc. └── map/ ├── charts_enc/ — ENC .000 and GeoTIFF files └── lidar_raw/ — LIDAR zips ``` --- ## Database - PostgreSQL, database `radar`, user `radar`, password `radar` - Table `target_data`: polled by simulation thread for live target positions