Fix errors

This commit is contained in:
2026-04-24 00:25:29 -07:00
parent f68524a6ae
commit 01c1db41db
34 changed files with 361 additions and 210 deletions

View File

@@ -661,11 +661,87 @@ PPIScope : public Scope (abstract) — shared PPI behavior:
- Cursor range/bearing readout under scope (white text)
- Bearing offset for boat mode (k/j)
- Cursor range clamped to max range
- Range rings and labels are beam-painted per sweep sector, NOT a static overlay;
the sweep shader evaluates ring radii for the CURRENT max range at the moment
each beam angle is rendered; the phosphor buffer retains whatever was last
written, so old ring positions fade naturally as the sweep overwrites them with
new geometry — no special transition code required
- Range rings are beam-painted per sweep sector with P7 persistence and decay;
however they are stored in the GAIN-INDEPENDENT G channel of the phosphor FBO
(see PHOSPHOR FBO ARCHITECTURE below) so operator gain does not dim the rings
- renderRingLabels() — virtual method (default no-op); concrete PPI scopes that
have labelled range rings override this to render mile-distance text labels in
P7 fresh-blue colour at a fixed bearing (RING_LABEL_BRG_DEG = 045°)
IMPLEMENTER CHECKLIST — required in every new PPIScope subclass:
1. computeRingRadii(): multiply each normalised ring radius by
GRAT_INNER_RING_FRAC (same as MarinePPIScope). Omitting this
places the outer ring at the scope boundary where it is clipped
and hidden behind the graticule. Target positions are scaled
automatically by PhosphorRenderer::update(); ring radii are not.
2. Override renderRingLabels() using the same pattern as
MarinePPIScope::renderRingLabels() but with the scope's own
ring-mile table. The base-class no-op produces no labels.
The p7Color() fix, two-channel FBO gain-separation, and target
position scaling are all automatic via the shared PhosphorRenderer
and shaders — no per-scope action required for those.
==================================================================
PHOSPHOR FBO ARCHITECTURE
==================================================================
The phosphor FBO is GL_RG32F (two independent float channels):
R channel — signal energy
Written by: target echoes in the sweep shader
Multiplied by: u_gain in the display shader
Effect: operator gain knob dims/brightens received echoes without
affecting the sweep beam or range rings
G channel — timing/geometry energy
Written by: range rings + sweep background glow in the sweep shader
NOT multiplied by gain in the display shader
Effect: rings always appear at a fixed brightness; the rotating
sweep-line glow is always visible even at minimum gain
Both channels decay at the same P7 rate (P7_DECAY_RATE in settings.h).
The display shader combines them: totalEnergy = max(R * gain, G).
This produces the correct visual priority: a strong target echo always
shows above the ring but a dim echo below gain threshold fades away
while the ring stays steady.
RANGE POSITION NORMALISATION
All ring radii and target range values are normalised so that
max-range maps to GRAT_INNER_RING_FRAC (0.915), NOT 1.0.
Normalised 1.0 is the outer edge of the phosphor circle (scope boundary).
The bearing graticule overlay occupies 0.915 to 0.985 of scope radius.
If max-range mapped to 1.0, the outer ring would sit at the scope
boundary — half-clipped by the sweep shader's rng > 1.0 early-exit and
visually hidden behind the graticule outer ring.
Mapping max-range → GRAT_INNER_RING_FRAC keeps all rings and targets
within the clean active display area inside the bearing scale overlay.
Scale is applied in two places:
1. PhosphorRenderer::update() — target range: × GRAT_INNER_RING_FRAC
2. computeRingRadii() in each concrete PPI scope — ring radii: × GRAT_INNER_RING_FRAC
P7 COLOUR FUNCTION
p7Color() in phosphor.frag is a piecewise linear ramp over [0, 1]:
e ≥ T_BLUE (0.82) → pure C_BLUE
[T_GREEN, T_BLUE) → mix(C_GREEN, C_BLUE, normalised within range)
[T_YGREE, T_GREEN) → mix(C_YGREE, C_GREEN, normalised within range)
[T_DARK, T_YGREE) → mix(C_YELLW, C_YGREE, normalised within range)
[0, T_DARK) → mix(C_BLACK, C_YELLW, normalised within range)
Each mix() factor is in [0, 1] and the function is continuous at every
threshold boundary. An earlier version had each branch using the formula
of the branch below it (off-by-one), which caused SWEEP_BACKGROUND_ENERGY
= 0.10 to render as saturated yellow (factor 3.33) instead of dim
yellow-green, producing an unwanted solid-yellow band behind the sweep.
==================================================================
MarinePPIScope : public PPIScope
- Sweep time: 4 seconds