PRIZM 4.0DSTA
Viewing C3 design language
RC3 · Components

Telemetry HUD

Operational telemetry for a UXV — aerial, ground, surface, or underwater. The same organism, four domain dialects. Two postures: a compact strip for edge overlays, and a four-edge frame for centred map / 3D / video viewports.

Live · frame mode

Four-edge layout around a centred viewport. The consumer's map, 3D view, or video sits in the children slot; telemetry pins to the edges. Top: heading + platform. Left: speed + roll. Right: vertical + pitch. Bottom: battery, vertical rate, and any domain-specific cells. The centre stays clear.

UAV-09
HDG048°
SPD22 knROLL-3°
ALT4000 ft AGLPITCH+2°
BATT78%V/S+1.5 m/s

Live · domains · strip mode

Four domains, four label dialects. The vertical concept changes — `ALT` for aerial, `ELEV` for ground, `DEPTH` for surface and underwater. Vertical rate reads `V/S` for aerial, `DIVE` for underwater. Domain-specific cells (`SLOPE` for ground, `BOT` for underwater) surface only when relevant.

AERIALAerial — airspeed in knots, altitude AGL in feet, full attitude and climb rate. ALT label resolves from the aerial domain.
UAV-09SPD22 knALT4000 ft AGLHDG048°BATT78%ROLL-3°PITCH+2°V/S+1.5 m/s
GROUNDGround — ELEV reads instead of ALT. Slope cell carries the terrain grade. Battery surfaces in danger colour at 18%.
UGV-04SPD3.2 m/sELEV142 mHDG178°BATT18%ROLL-1°PITCH+4°SLOPE+8°
SURFACESurface — DEPTH reads (below-keel sounder); fuel substitutes for battery on a combustion platform.
USV-02SPD14 knDEPTH32 m BLWHDG270°FUEL42%ROLL-4°PITCH+1°
UNDERWATERUnderwater — DEPTH reads sub-surface; DIVE rate replaces V/S; BOT shows altitude above seabed.
UUV-11SPD2.4 m/sDEPTH85 mHDG092°BATT64%ROLL+0°PITCH-8°DIVE+0.3 m/sBOT12 m

Live · stale field

Heading has stopped updating. Rather than presenting a frozen `174°` as if it were live, the cell dims its value and carries a `STALE` age tag — the operator sees the degradation before acting on it. Fresh cells around it are unaffected. This is invariant 5 in the organism itself: mark stale, never hold a silent last-good value.

UGV-04SPD3.2 m/sHDG174°STALE 4sBATT87%

Anatomy

Same cells, two postures. The cell set is determined by what the consumer passes; the posture is determined by `mode`. Labels resolve from `domain`.

Domain

Aerial / ground / surface / underwater. Resolves the vertical concept (ALT / ELEV / DEPTH) and the vertical rate (V/S / DIVE) without forking the organism.

Platform marker

Optional leading cell. Ember dot + mono identifier. Marks 'this is the active RC3 platform.' Drop when the HUD overlays a video tile that already names the source.

Strip mode

Inline horizontal row. Cells flow with hairline dividers, flex-wraps on narrow viewports. Best for edge overlays and side panels.

Frame mode

Four-edge container with the consumer's viewport in the centre. Top: heading + platform. Left: speed + roll. Right: vertical + pitch. Bottom: charge + rate + domain cells.

Battery / fuel colour

Success > 50%, warning 20–50%, danger < 20%. Operators read state before reading the number.

Stale marker

A field passed in `stale` dims its value and gains a STALE age tag. The frozen reading stays visible but unmistakably degraded — never silently fresh-looking.

Domain-specific cells

SLOPE (ground only) and BOT — altitude above bottom — surface where the platform's domain makes them meaningful.

Props

PropTypeDefaultDescription
domain"aerial" | "ground" | "surface" | "underwater""aerial"UXV domain. Drives label semantics: vertical reads as ALT / ELEV / DEPTH; verticalRate reads as V/S / DIVE. Domain-specific cells (slope, altitudeAboveBottom) surface only when meaningful.
mode"strip" | "frame""strip"Layout. strip is an inline horizontal row, suited to edge overlays and side panels. frame is a four-edge container around a centred viewport (map, 3D, video).
platformstringOptional platform identifier — e.g. "UGV-04". Renders as the leading Ember-dotted cell.
speednumberSpeed value. Unit defaults to m/s.
speedUnit"m/s" | "km/h" | "kn""m/s"Speed unit. m/s rounds to one decimal; km/h and kn round to integer.
verticalnumberVertical position. Aerial: altitude. Ground: elevation. Surface / underwater: depth. Label resolves from domain.
verticalUnit"m" | "ft""m"Vertical unit.
verticalRefstringOptional reference marker appended to the vertical cell — e.g. "AGL" / "MSL" / "BLW".
verticalRatenumberVertical rate in m/s. Signed. Label resolves from domainV/S for aerial, DIVE for underwater.
headingnumberHeading in degrees (0–359). Rendered three-digit padded — HDG 048°.
batterynumberBattery state of charge (0–100). Colour-coded — success > 50, warning 20–50, danger < 20.
fuelnumberFuel level (0–100). Mutually exclusive with battery; if both are passed, battery wins.
rollnumberRoll angle in degrees. Signed.
pitchnumberPitch angle in degrees. Signed.
slopenumberGround-only — slope / grade in degrees. Signed. Renders only when domain="ground".
altitudeAboveBottomnumberDistance to bottom in metres. Sounder reading; meaningful for underwater and deep-terrain ground platforms.
stalePartial<Record<TelemetryField, number>>Per-field staleness, in seconds since the last fresh update. A listed field keeps its last-known value but renders dimmed with a STALE age tag — a frozen reading never passes for live. Honours invariant 5.
childrenReactNodeRendered inside the frame centre when mode="frame". Ignored in strip mode.
classNamestringForwarded to the root container.

Types

UxvDomain

UXV domain. Drives label semantics for the vertical concept and vertical rate.

ts
type UxvDomain = "aerial" | "ground" | "surface" | "underwater";
SpeedUnit

Speed unit. Use `kn` for aviation / surface, `m/s` for ground / underwater.

ts
type SpeedUnit = "m/s" | "km/h" | "kn";
VerticalUnit

Vertical unit. `ft` typical for aviation; `m` for everything else.

ts
type VerticalUnit = "m" | "ft";
HudMode

Layout posture.

ts
type HudMode = "strip" | "frame";
TelemetryField

The fields that can be marked stale via the `stale` prop. Keyed by the feeding prop.

ts
type TelemetryField =
  | "speed" | "vertical" | "heading"
  | "battery" | "fuel" | "roll" | "pitch"
  | "verticalRate" | "slope" | "altitudeAboveBottom";

Wiring

Read-only — pass whichever fields the platform surfaces. When a field stops updating, mark it stale via `stale` rather than letting a frozen value look live. Set `domain` to the platform class.

tsx
// Aerial — strip mode beside a video tile
<TelemetryHud
  domain="aerial"
  platform="UAV-09"
  speed={state.airspeed}
  speedUnit="kn"
  vertical={state.altitudeFt}
  verticalUnit="ft"
  verticalRef="AGL"
  heading={state.heading}
  battery={state.batteryPct}
  roll={state.attitude.roll}
  pitch={state.attitude.pitch}
  verticalRate={state.climbRate}
/>
tsx
// Underwater — DEPTH and DIVE labels resolve from domain
<TelemetryHud
  domain="underwater"
  platform="UUV-11"
  speed={state.speed}
  vertical={state.depth}
  heading={state.heading}
  battery={state.batteryPct}
  pitch={state.diveAngle}
  verticalRate={state.diveRate}
  altitudeAboveBottom={state.sounderM}
/>
tsx
// Frame mode — four-edge overlay around a map / 3D viewport
<TelemetryHud mode="frame" domain="aerial" ...telemetry>
  <YourMapEngine />
</TelemetryHud>
tsx
// Mark a field stale when it stops updating — invariant 5
<TelemetryHud
  domain="ground"
  platform="UGV-04"
  speed={state.speed}
  heading={state.lastHeading}
  battery={state.batteryPct}
  stale={{ heading: secondsSince(state.headingUpdatedAt) }}
/>

Behavioural rule

Invariant Five

Telemetry never silently stale

When a field stops updating, mark it stale via `stale` — the value dims and gains a STALE age tag so a frozen reading can never pass for live. A degraded battery surfaces in danger before it becomes critical; a stalled heading reads as stale, not as a fresh fix.

Read invariant

Accessibility

Live regionThe HUD carries `role="status"` and `aria-live="polite"`. Screen readers announce telemetry transitions without interrupting the operator.
Labelled by platformThe root `aria-label` is of the form `Telemetry for {platform}` when a platform identifier is present, otherwise just `Telemetry`.
Decorative partsThe Ember dot and the cell dividers are `aria-hidden`. Cell labels and values carry the meaning.
Colour and meaningBattery / fuel state colour is paired with the numeric value — operators with colour-vision differences still read the percentage. The colour amplifies, never replaces, the signal.
Stale not colour-onlyA stale field carries the literal `STALE {age}` text alongside the dimmed value — the degradation is legible without relying on the danger colour. The `aria-live` region announces the change.
Tabular numsValues use `tabular-nums` so digits do not shift width as values change. The strip stays scannable when the operator's eye returns to the same cell repeatedly.

JavaFX

Ships in the PRIZM JavaFX library for thick-client C3 apps as Rc3TelemetryHud (extends StackPane). Run the gallery to see it natively.

java
import design.prizm.fx.rc3.Rc3TelemetryHud;

Rc3TelemetryHud()
MemberTypeDefaultDescription
UxvDomainenumAERIAL / GROUND / SURFACE / UNDERWATER — drives ALT / ELEV / DEPTH and V/S / DIVE labels.
HudModeenumSTRIPSTRIP (inline row) or FRAME (four-edge overlay around a viewport).
SpeedUnit / VerticalUnitenumM_S / KM_H / KN and M / FT.
TelemetryFieldenumKeys for per-field staleness.
setDomain / setMode / setPlatform→ voidDomain, posture, and the Ember-dotted platform cell.
setSpeed / setVertical / setVerticalRate / setHeading / setBattery / setFuel / setRoll / setPitch / setSlope / setAltitudeAboveBottom→ voidDomain-tuned cells (slope is ground-only).
setStale(Map<TelemetryField, Integer>) → voidSeconds since last fresh; the cell dims + shows a STALE tag.
setContent(Node) → voidCentred viewport in FRAME mode.

Honours invariant 5 — a stale field never passes for live. Battery / fuel use semantic tones; the platform marker is Ember. Mirrors components/rc3/telemetry-hud.tsx.

Usage

Set `domain` to the platform class. Pass whichever fields the platform surfaces — labels resolve automatically. Drop the platform marker when the HUD overlays a video tile that already identifies the source. For map and 3D viewports, prefer `mode="frame"` and let the edges hold the telemetry while the centre stays clear. Keep the HUD honest: when a field stops updating, mark it stale via `stale` rather than letting a frozen value look live.