Skip to content

fix: anchor temperature / power / ANE sparklines to fixed Y-axis ranges in the GPU Metrics box and summary bars #236

Description

@inureyes

Problem

In the TUI, several header/summary sparklines (most visibly the "GPU Metrics" box rendered by src/ui/gpu_sparkline_panel.rs) auto-scale their Y-axis to the per-frame data window's [min, max]. Because the braille sparkline only has 4 vertical levels, this auto-ranging makes the graphs effectively meaningless:

  1. Noise amplification — a steady temperature wiggling 45→47°C gets a range of [45, 47] and fills the full height, looking like violent oscillation.
  2. Non-stationary baseline — the min/max are recomputed every frame as the 100-sample window (AppConfig::HISTORY_MAX_ENTRIES = 100) slides, so the same 46°C sample lands at the bottom one frame and the top the next. Time-axis comparison is impossible.
  3. Absolute level lost — when the data is nearly constant, max <= min triggers the "constant input" path in src/ui/braille.rs and every cell renders at the bottom row (). A blazing 90°C and a cool 35°C look identical.

The GPU Metrics box also prints a min_max_badge (window min/max, gpu_sparkline_panel.rs) next to each row, and that number jitters frame to frame too, reinforcing the confusion.

Affected graphs (audited)

All sparkline_braille(...) call sites were reviewed:

Location Metric Current range Status
gpu_sparkline_panel.rs (GPU Metrics box) GPU Util / GPU Mem (0,100) fixed OK
GPU Temp None (auto) BUG
ANE None (auto) BUG
Pkg Power None (auto) BUG
local_header.rs (summary bar) CPU% / GPU% / RAM% (0,100) fixed OK
Tmp (system/CPU temp) None (auto) BUG
Pwr (0, window-max) dynamic BUG
remote_sparkline_panel.rs (remote Live Statistics) Util / Mem (GPU·CPU) (0,100) fixed OK
GPU Temp / CPU Temp None (auto) BUG
activity_panel.rs (CPU cores) per-core bars 100.0 fixed OK

Note: CPU-utilization and memory sparklines are already pinned to (0,100) everywhere, so they are not subject to the moving-axis bug. The genuinely affected metrics are temperature, package power, and ANE.

Proposed solution

Replace per-window auto-ranging with fixed, domain-meaningful Y-axis ranges ("smart fixed range"):

  • Temperature (system + GPU) → (30, ceil). Floor 30°C (idle baseline). Ceiling = first reported GPU thermal threshold (temperature_threshold_slowdowntemperature_threshold_max_operatingtemperature_threshold_shutdown), else fallback 100°C. CPU temperature has no threshold fields, so it uses the 100°C fallback. Bar height then means "how close to throttling".
  • Package power (Pkg Power / summary Pwr) → (0, ceil). Ceiling = enforced power limit from gpu.detail (power_limit_currentpower_limit_maxpower_limit_default, in W; available for NVIDIA/Gaudi), else nice_ceil(window_peak) with a 10W floor. Bar then means "fraction of power budget".
  • ANE (Apple) → (0, nice_ceil(max(window_peak, 8W))). No published cap; an 8W floor keeps an idle Neural Engine reading near-empty.
  • nice_ceil(v) rounds up to a pleasant 1/2/5 × 10ⁿ so small fluctuations don't shift the fallback ceiling.
  • min-max badge (GPU Metrics box): show the fixed axis in use (e.g. 30-83, 0-350) instead of the jittery observed window min/max, so the badge becomes a stable scale legend. The "latest value" column already shows the current reading.
  • CPU% / Mem% / per-core bars: no change.

Implementation scope

  • New src/ui/scale.rs with nice_ceil(), temp_range(gpu), power_range(gpu, history), ane_range(history), scale_badge(min,max), plus constants TEMP_FLOOR_C=30, TEMP_FALLBACK_CEIL_C=100, ANE_MIN_CEIL_W=8, POWER_MIN_CEIL_W=10. Register pub mod scale; in src/ui/mod.rs.
  • Wire the helpers into the None/dynamic ranges in gpu_sparkline_panel.rs, local_header.rs, and remote_sparkline_panel.rs. sparkline_braille itself already supports an explicit range and needs no change.
  • Switch the GPU Metrics box badge to the fixed-axis form (replaces the existing min_max_badge).
  • Unit tests for the new helpers (axis stability under window shift, nice_ceil rounding, threshold/power-limit fallbacks).

Acceptance criteria

  • Temperature, power, and ANE sparklines use fixed anchored ranges; their shape no longer rescales as the window slides, and a flat hot reading is visually distinct from a flat cool reading.
  • NVIDIA temperature/power axes use reported thresholds / power limits when available; Apple Silicon and others fall back to nice_ceil / constants.
  • GPU Metrics box badge shows the fixed axis and is stable.
  • Changes are integrated into all three panels (gpu_sparkline_panel.rs, local_header.rs, remote_sparkline_panel.rs) — not just standalone helpers — so the running TUI reflects the fix.
  • cargo fmt --check, cargo clippy, and cargo test pass.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions