This commit is contained in:
2026-06-17 10:35:08 -07:00
parent e3b09170a4
commit 5355a9eded
9 changed files with 1836 additions and 0 deletions

453
src/01_classes.cpp Normal file
View File

@@ -0,0 +1,453 @@
/*
* MIT License
*
* Copyright (c) 2026 Mark Allyn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/*
* 01_classes.cpp
* Author: Mark Allyn
* Component: 01_classes
*
* Stub implementations for all four radar subclasses.
* Each renders description text in the left panel, the radar name in the
* scope panel, and current control values in the status bar.
* No real radar simulation occurs here; this exists to test the
* initialization and radar-management/selection flow.
*/
#include "01_classes.h"
#include "02_text_description.h"
#include "data_structure_and_constants.h"
#include "settings.h"
#include <cstdio>
#include <string>
/* ------------------------------------------------------------------ */
/* Global instances */
/* ------------------------------------------------------------------ */
ChainHomeRadar g_chain_home;
MarineAScopeRadar g_marine_ascope;
MarineTrafficRadar g_marine_traffic;
PoliceboatRadar g_police_boat;
/* ------------------------------------------------------------------ */
/* Colour constants (RGB, 0.01.0) */
/* ------------------------------------------------------------------ */
static const float COL_WHITE[3] = { 1.00f, 1.00f, 1.00f };
static const float COL_RED[3] = { 1.00f, 0.25f, 0.25f };
static const float COL_PINK[3] = { 1.00f, 0.60f, 0.80f };
static const float COL_YELLOW[3] = { 1.00f, 1.00f, 0.00f };
static const float COL_GREEN_DIM[3] = { 0.00f, 0.85f, 0.30f };
static const float COL_GREEN_LO[3] = { 0.00f, 0.55f, 0.18f };
/* ------------------------------------------------------------------ */
/* Layout helpers */
/* ------------------------------------------------------------------ */
static const float SCALE_TITLE = 1.25f;
static const float SCALE_NORMAL = 1.00f;
static const float SCALE_SMALL = 0.85f;
static const float MARGIN = static_cast<float>(TEXT_MARGIN);
static const float LH = LINE_HEIGHT; /* line height at scale 1.0 */
/* Render one line of text and advance y downward by one line. */
static void text_line(const std::string& text, float x, float& y,
float scale,
float cr, float cg, float cb) {
g_text_renderer.render_text(text, x, y, scale, cr, cg, cb);
y -= LH * scale;
}
/* Render a blank spacer line. */
static void blank_line(float& y, float scale) {
y -= LH * scale * 0.6f;
}
/* Render one control entry: label in red, keys in pink, on the same line.
* Format: " label_text key_lo / key_hi"
* Returns the new y after advancing one line. */
static void control_line(const std::string& label,
const std::string& key_lo, const std::string& key_hi,
float x, float& y, float scale) {
float xp = x + 6.0f;
/* Label in red */
g_text_renderer.render_text(label, xp, y, scale,
COL_RED[0], COL_RED[1], COL_RED[2]);
xp += g_text_renderer.get_text_width(label, scale);
/* Lower key in pink */
g_text_renderer.render_text(key_lo, xp, y, scale,
COL_PINK[0], COL_PINK[1], COL_PINK[2]);
xp += g_text_renderer.get_text_width(key_lo, scale);
/* Separator in red */
g_text_renderer.render_text(" / ", xp, y, scale,
COL_RED[0], COL_RED[1], COL_RED[2]);
xp += g_text_renderer.get_text_width(" / ", scale);
/* Upper key in pink */
g_text_renderer.render_text(key_hi, xp, y, scale,
COL_PINK[0], COL_PINK[1], COL_PINK[2]);
y -= LH * scale;
}
/* ------------------------------------------------------------------ */
/* Scope helper: render centred name + stub subtitle */
/* ------------------------------------------------------------------ */
static void render_scope_name(const std::string& radar_name) {
const float scope_w = static_cast<float>(SCOPE_WIDTH);
const float scope_h = static_cast<float>(SCOPE_HEIGHT);
float name_w = g_text_renderer.get_text_width(radar_name, 2.0f);
float nx = (scope_w - name_w) / 2.0f;
float ny = scope_h / 2.0f + 20.0f;
g_text_renderer.render_text(radar_name, nx, ny, 2.0f,
COL_GREEN_DIM[0], COL_GREEN_DIM[1], COL_GREEN_DIM[2]);
std::string sub = "[ stub - initialization test only ]";
float sub_w = g_text_renderer.get_text_width(sub, SCALE_NORMAL);
float sx = (scope_w - sub_w) / 2.0f;
float sy = ny - LH * 2.0f;
g_text_renderer.render_text(sub, sx, sy, SCALE_NORMAL,
COL_GREEN_LO[0], COL_GREEN_LO[1], COL_GREEN_LO[2]);
}
/* ------------------------------------------------------------------ */
/* Status bar helper: common controls (intensity, sensitivity, STC) */
/* ------------------------------------------------------------------ */
static void render_common_status(float x, float& y, float scale) {
char buf[320];
snprintf(buf, sizeof(buf),
"Intensity: %.2f | Sensitivity: %.2f | STC Sens: %.2f | STC Range: %.2f",
uniform_intensity, uniform_sensitivity,
uniform_stc_sensitivity, uniform_stc_range);
g_text_renderer.render_text(buf, x, y, scale,
COL_YELLOW[0], COL_YELLOW[1], COL_YELLOW[2]);
y -= LH * scale;
}
/* ================================================================== */
/* ChainHomeRadar */
/* ================================================================== */
void ChainHomeRadar::render_left_panel() {
float y = static_cast<float>(WINDOW_HEIGHT) - MARGIN - LH * SCALE_TITLE;
float x = MARGIN;
text_line("CHAIN HOME RADAR", x, y, SCALE_TITLE,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("Location: Ventnor, Isle of Wight", x, y, SCALE_SMALL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("The UK developed Chain Home in the 1940s", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("to detect German aircraft attacking across", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("the English Channel. It was one of the", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("first operational radar systems ever built.", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("The scope shows range on the horizontal", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("axis and echo amplitude on the vertical.", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("Bearing is found with the radiogoniometer:", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("rotate the search coil until the echo", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("amplitude reaches a minimum (null point).", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
/* Controls header */
text_line("--- CONTROLS ---", x, y, SCALE_NORMAL,
COL_RED[0], COL_RED[1], COL_RED[2]);
control_line("Intensity: ", "3", "4", x, y, SCALE_NORMAL);
control_line("Sensitivity: ", "5", "6", x, y, SCALE_NORMAL);
control_line("STC Sensitivity: ", "Q", "W", x, y, SCALE_NORMAL);
control_line("STC Range: ", "E", "R", x, y, SCALE_NORMAL);
control_line("Radiogoniometer: ", "U", "I", x, y, SCALE_NORMAL);
blank_line(y, SCALE_NORMAL);
text_line("1 - Return to Radar Selection", x, y, SCALE_NORMAL,
COL_YELLOW[0], COL_YELLOW[1], COL_YELLOW[2]);
}
void ChainHomeRadar::render_scope() {
render_scope_name("CHAIN HOME RADAR");
}
void ChainHomeRadar::render_status_bar() {
float x = MARGIN;
float y = static_cast<float>(STATUS_BAR_HEIGHT) - MARGIN - LH;
text_line("CHAIN HOME RADAR (Ventnor, Isle of Wight) | Fixed range: 200 km",
x, y, SCALE_NORMAL, COL_YELLOW[0], COL_YELLOW[1], COL_YELLOW[2]);
render_common_status(x, y, SCALE_NORMAL);
char buf[200];
snprintf(buf, sizeof(buf), "Radiogoniometer: %.1f deg", uniform_radiogoniometer);
g_text_renderer.render_text(buf, x, y, SCALE_NORMAL,
COL_YELLOW[0], COL_YELLOW[1], COL_YELLOW[2]);
}
/* ================================================================== */
/* MarineAScopeRadar */
/* ================================================================== */
void MarineAScopeRadar::render_left_panel() {
float y = static_cast<float>(WINDOW_HEIGHT) - MARGIN - LH * SCALE_TITLE;
float x = MARGIN;
text_line("MARINE A-SCOPE RADAR", x, y, SCALE_TITLE,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("Location: Community Boating Center,", x, y, SCALE_SMALL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("Bellingham Bay, Washington", x, y, SCALE_SMALL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("Microwave radar was developed after", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("Chain Home. Its shorter wavelengths", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("could detect small targets such as a", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("submarine periscope or conning tower.", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("A steerable dish is connected to an", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("oscilloscope. Bearing is determined by", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("rotating the dish with the servo control.", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("--- CONTROLS ---", x, y, SCALE_NORMAL,
COL_RED[0], COL_RED[1], COL_RED[2]);
control_line("Intensity: ", "3", "4", x, y, SCALE_NORMAL);
control_line("Sensitivity: ", "5", "6", x, y, SCALE_NORMAL);
control_line("STC Sensitivity: ", "Q", "W", x, y, SCALE_NORMAL);
control_line("STC Range: ", "E", "R", x, y, SCALE_NORMAL);
control_line("Max Range: ", "O", "P", x, y, SCALE_NORMAL);
control_line("Antenna Rotation: ", "G", "H", x, y, SCALE_NORMAL);
blank_line(y, SCALE_NORMAL);
text_line("1 - Return to Radar Selection", x, y, SCALE_NORMAL,
COL_YELLOW[0], COL_YELLOW[1], COL_YELLOW[2]);
}
void MarineAScopeRadar::render_scope() {
render_scope_name("MARINE A-SCOPE RADAR");
}
void MarineAScopeRadar::render_status_bar() {
float x = MARGIN;
float y = static_cast<float>(STATUS_BAR_HEIGHT) - MARGIN - LH;
text_line("MARINE A-SCOPE RADAR (Community Boating Center, Bellingham Bay)",
x, y, SCALE_NORMAL, COL_YELLOW[0], COL_YELLOW[1], COL_YELLOW[2]);
render_common_status(x, y, SCALE_NORMAL);
char buf[200];
snprintf(buf, sizeof(buf),
"Max Range (normalized): %.2f | Antenna Bearing: %.1f deg",
uniform_max_range, uniform_marine_a_bearing);
g_text_renderer.render_text(buf, x, y, SCALE_NORMAL,
COL_YELLOW[0], COL_YELLOW[1], COL_YELLOW[2]);
}
/* ================================================================== */
/* MarineTrafficRadar */
/* ================================================================== */
void MarineTrafficRadar::render_left_panel() {
float y = static_cast<float>(WINDOW_HEIGHT) - MARGIN - LH * SCALE_TITLE;
float x = MARGIN;
text_line("MARINE TRAFFIC CONTROL RADAR", x, y, SCALE_TITLE,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("Location: Center of Bellingham Bay,", x, y, SCALE_SMALL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("Washington", x, y, SCALE_SMALL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("The Plan Position Indicator (PPI) radar", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("sweeps a beam clockwise from north.", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("Range is the distance of a blip from", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("centre; bearing is the beam direction.", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("All modern marine radars use this type", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("of display. Coastal guard stations use", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("them to manage harbour traffic.", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("--- CONTROLS ---", x, y, SCALE_NORMAL,
COL_RED[0], COL_RED[1], COL_RED[2]);
control_line("Intensity: ", "3", "4", x, y, SCALE_NORMAL);
control_line("Sensitivity: ", "5", "6", x, y, SCALE_NORMAL);
control_line("STC Sensitivity: ", "Q", "W", x, y, SCALE_NORMAL);
control_line("STC Range: ", "E", "R", x, y, SCALE_NORMAL);
control_line("Rain Noise Filter: ", "T", "Y", x, y, SCALE_NORMAL);
control_line("Max Range: ", "O", "P", x, y, SCALE_NORMAL);
control_line("Range Cursor: ", "A", "S", x, y, SCALE_NORMAL);
control_line("Bearing Cursor: ", "D", "F", x, y, SCALE_NORMAL);
blank_line(y, SCALE_NORMAL);
text_line("1 - Return to Radar Selection", x, y, SCALE_NORMAL,
COL_YELLOW[0], COL_YELLOW[1], COL_YELLOW[2]);
}
void MarineTrafficRadar::render_scope() {
render_scope_name("MARINE TRAFFIC CONTROL RADAR");
}
void MarineTrafficRadar::render_status_bar() {
float x = MARGIN;
float y = static_cast<float>(STATUS_BAR_HEIGHT) - MARGIN - LH;
text_line("MARINE TRAFFIC CONTROL RADAR (Bellingham Bay)",
x, y, SCALE_NORMAL, COL_YELLOW[0], COL_YELLOW[1], COL_YELLOW[2]);
render_common_status(x, y, SCALE_NORMAL);
char buf[280];
snprintf(buf, sizeof(buf),
"Noise Filter: %.2f | Max Range (norm): %.2f | "
"Range Cursor: %.2f | Bearing Cursor: %.1f deg",
uniform_noise_filter, uniform_max_range,
uniform_range_cursor, uniform_bearing_cursor);
g_text_renderer.render_text(buf, x, y, SCALE_NORMAL,
COL_YELLOW[0], COL_YELLOW[1], COL_YELLOW[2]);
}
/* ================================================================== */
/* PoliceboatRadar */
/* ================================================================== */
void PoliceboatRadar::render_left_panel() {
float y = static_cast<float>(WINDOW_HEIGHT) - MARGIN - LH * SCALE_TITLE;
float x = MARGIN;
text_line("POLICE BOAT RADAR", x, y, SCALE_TITLE,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("Starting location: Taylor Dock /", x, y, SCALE_SMALL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("Boulevard Park, Bellingham Bay", x, y, SCALE_SMALL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("This PPI radar is mounted on a moving", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("police boat patrolling Bellingham Bay.", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("The sweep rotates clockwise relative to", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("the boat's heading, not true north. A", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("small indicator shows where north is.", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("Watch how the display changes as the", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
text_line("boat moves and turns.", x, y, SCALE_NORMAL,
COL_WHITE[0], COL_WHITE[1], COL_WHITE[2]);
blank_line(y, SCALE_NORMAL);
text_line("--- CONTROLS ---", x, y, SCALE_NORMAL,
COL_RED[0], COL_RED[1], COL_RED[2]);
control_line("Intensity: ", "3", "4", x, y, SCALE_NORMAL);
control_line("Sensitivity: ", "5", "6", x, y, SCALE_NORMAL);
control_line("STC Sensitivity: ", "Q", "W", x, y, SCALE_NORMAL);
control_line("STC Range: ", "E", "R", x, y, SCALE_NORMAL);
control_line("Rain Noise Filter: ", "T", "Y", x, y, SCALE_NORMAL);
control_line("Max Range: ", "O", "P", x, y, SCALE_NORMAL);
control_line("Range Cursor: ", "A", "S", x, y, SCALE_NORMAL);
control_line("Bearing Cursor: ", "D", "F", x, y, SCALE_NORMAL);
blank_line(y, SCALE_NORMAL);
text_line("1 - Return to Radar Selection", x, y, SCALE_NORMAL,
COL_YELLOW[0], COL_YELLOW[1], COL_YELLOW[2]);
}
void PoliceboatRadar::render_scope() {
render_scope_name("POLICE BOAT RADAR");
}
void PoliceboatRadar::render_status_bar() {
float x = MARGIN;
float y = static_cast<float>(STATUS_BAR_HEIGHT) - MARGIN - LH;
text_line("POLICE BOAT RADAR (Bellingham Bay - moving)",
x, y, SCALE_NORMAL, COL_YELLOW[0], COL_YELLOW[1], COL_YELLOW[2]);
render_common_status(x, y, SCALE_NORMAL);
char buf[280];
snprintf(buf, sizeof(buf),
"Noise Filter: %.2f | Max Range (norm): %.2f | "
"Range Cursor: %.2f | Bearing Cursor: %.1f deg",
uniform_noise_filter, uniform_max_range,
uniform_range_cursor, uniform_bearing_cursor);
g_text_renderer.render_text(buf, x, y, SCALE_NORMAL,
COL_YELLOW[0], COL_YELLOW[1], COL_YELLOW[2]);
}