Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
125 commits
Select commit Hold shift + click to select a range
d504b90
Messing around
Orcasphynx Nov 1, 2025
4004b81
Remove symlink and add bash script
Nov 1, 2025
f651cdd
Modifications
Nov 1, 2025
27ef192
Docker edits
Nov 1, 2025
17328b8
Ease of fuse
Nov 1, 2025
d16a266
Pre-creating
Nov 1, 2025
47ab394
Merge branch 'feature/dataviz' into feature/docker
TheGreatPintoJ Nov 1, 2025
1d31b73
Switched to use ingest_library
Nov 5, 2025
7bc170b
2024 and 2025 versions of owlet for macOS
Nov 5, 2025
7cbb7af
Added .DS_Store
Nov 5, 2025
a43e106
Updating owlet
Nov 5, 2025
0596df6
Updated to use correct owlet executable based of OS
Nov 5, 2025
2e834d7
Added script to ingest the telemetry/ directory
Nov 5, 2025
323d33b
Merge branch 'feature/dataviz' into feature/docker
TheGreatPintoJ Nov 5, 2025
8a29921
multipage app setup
Orcasphynx Nov 8, 2025
ea3386f
Add force exclude (gitignore)
Nov 8, 2025
76723d7
Messin around
Nov 8, 2025
6fb374e
Sidebar Filters
Nov 9, 2025
817605f
Merge branch 'feature/dataviz' of https://github.com/IgniteRobotics/f…
Nov 9, 2025
78d29fd
Filter log options based off event
Nov 9, 2025
72aaeba
Refreshing works?
Nov 10, 2025
53050b8
Organization and fix refresh button
Nov 12, 2025
487eab7
Merge pull request #7 from IgniteRobotics/feature/docker
TheGreatPintoJ Nov 12, 2025
52a9673
Update to ingest with ingest_dir.py
Nov 12, 2025
b1eca48
Current/match tab
Nov 12, 2025
0901067
Add parsing as integers to several columns
Nov 12, 2025
6ed2aa8
Update opening command for windows
Nov 19, 2025
f97b0ea
Windows advantage scope command working
Orcasphynx Nov 22, 2025
a58c80c
removed print statements
Orcasphynx Nov 22, 2025
66de432
Updated linux exec
Nov 22, 2025
1c5b47c
End to end fixes
Nov 22, 2025
6f33c6c
Update linux path (advscope)
Nov 22, 2025
974b593
Filters work now, you have to use refresh
Orcasphynx Dec 3, 2025
66eb18a
Slight progress (BROKEN)
Dec 6, 2025
acf39a2
Remove duplicate entry
Dec 10, 2025
b07cdf0
Changed some database data entries to int
Orcasphynx Dec 10, 2025
0e68e40
Merge branch 'feature/dataviz' of https://github.com/IgniteRobotics/f…
Orcasphynx Dec 20, 2025
33b5560
AScope Broken
Jan 7, 2026
82438ad
Add/update owlet and references
TheGreatPintoJ Feb 12, 2026
ae5c139
Cache pip install command (thank goodness no 10min buid times)
TheGreatPintoJ Feb 18, 2026
c89e341
Update more to 2026
TheGreatPintoJ Feb 18, 2026
eba2afa
Temp comments
TheGreatPintoJ Apr 1, 2026
3dabb9b
Rename ingest to puller
TheGreatPintoJ Apr 2, 2026
c903658
Added dedicated importer
TheGreatPintoJ Apr 3, 2026
933eaf2
Add motor power analysis design spec
Apr 15, 2026
2c5f003
Update motor analysis spec with project/env setup and code conventions
Apr 15, 2026
3b9d6b6
Add motor power analysis implementation plan
Apr 15, 2026
be64963
feat: scaffold utils package and test infrastructure
Apr 15, 2026
2ec4d01
fix: add type hints and pytest markers to scaffold
Apr 15, 2026
d35d373
feat: add MotorData and Match dataclasses
Apr 15, 2026
fb2aaf2
feat: add loader — filename grouping and CSV merging
Apr 15, 2026
faf65df
feat: add trimmer — voltage-threshold match window detection
Apr 15, 2026
1700d6d
feat: add analyzer — normalize, power/energy math, Match construction
Apr 15, 2026
1055f21
feat: add plotter — matplotlib figure generators
Apr 15, 2026
ddda4db
feat: add reporter — PDF assembly via matplotlib PdfPages
Apr 15, 2026
c014784
feat: add CLI entry point and integration smoke test
Apr 15, 2026
548bcc6
fix: handle 32-char hex device IDs in filename grouping
Apr 15, 2026
d24e568
reports
Apr 17, 2026
f8ec180
docs: add viz improvements design spec
Apr 21, 2026
6224e53
feat: replace noisy line charts with heatmap and smoothed total power
Apr 21, 2026
ff2193b
docs: add supply current comparison and motor naming design spec
Apr 21, 2026
b478585
docs: update spec with interleaved multi-match layout and shared cover
Apr 21, 2026
c76c7f9
docs: add implementation plan for supply current, motor naming, inter…
Apr 21, 2026
d313067
feat: add motor name loading and per-match resolution
Apr 21, 2026
c0b4a48
fix: use longest-match prefix in resolve(), add ambiguity test
Apr 21, 2026
7a537fa
feat: add _label helper and motor_names to plot_heatmap
Apr 21, 2026
e1a963f
feat: add motor_names to plot_cumulative_energy and plot_per_motor
Apr 21, 2026
71dfa8e
feat: add _multi_cover_page, _resolve_names, motor_names to stat tabl…
Apr 21, 2026
fa93bc7
feat: interleaved multi-match layout, shared cover, supply current co…
Apr 21, 2026
7f9322e
fix: remove inline imports and section comments from reporter
Apr 21, 2026
b6bc6a7
feat: add --motor-names CLI flag for TOML display name config
Apr 21, 2026
37b0cd9
fix: sum supply_current across motors in compute_totals
Apr 21, 2026
1e2458f
feat: smooth supply_current comparison graph
Apr 21, 2026
4b398d6
fix: match section keys as competition code suffix (e.g. COL matches …
Apr 21, 2026
114d36e
feat: filter unnamed motors from graphs/tables, group by function in …
Apr 22, 2026
8cfd41f
fix: group motors by function in display order, stripping position pr…
Apr 22, 2026
0980d98
feat: add P95 peak markers to supply current comparison graph
Apr 22, 2026
f436839
motors file
Apr 22, 2026
88aa315
docs: add hoot power report automation design spec
Apr 22, 2026
715e8d9
docs: add hoot power report implementation plan
Apr 22, 2026
bd52bb3
feat: add hoot_loader stub with cache-hit path
Apr 22, 2026
431f482
fix: anchor owlet path to repo root, remove unused stub imports
Apr 22, 2026
ddbaf13
feat: implement _pivot_records for hoot → wide-format DataFrame
Apr 22, 2026
7521cbd
test: add interleaved records test and document timestamp collision b…
Apr 22, 2026
cd05887
feat: implement full hoot conversion path via owlet + DataLogReader
Apr 22, 2026
62413cf
fix: close mmap with context manager in _wpilog_to_df
Apr 22, 2026
dfef538
test: add error handling tests for hoot_loader
Apr 22, 2026
9f1459e
test: tighten exit code assertion and use robust corrupt-cache bytes
Apr 22, 2026
e27a91a
feat: add --cache-dir arg and hoot routing to utils CLI
Apr 22, 2026
0ccf1cb
fix: warn when hoot conversion produces no usable data
Apr 22, 2026
906f7ba
test: add coverage for data-before-start drop and missing owlet execu…
Apr 22, 2026
9d07ce6
fix: treat owlet non-zero exit as warning if wpilog was written (trun…
Apr 23, 2026
4c12d2f
hoot file support
Apr 23, 2026
605fe81
docs: add supply watts per RPS design spec
Apr 23, 2026
0bb200e
docs: add supply watts per RPS implementation plan
Apr 23, 2026
72b06e7
feat: expand MOTOR_COL_PATTERN to capture TalonFX Velocity signal
Apr 23, 2026
65729f1
feat: add rotor_velocity field to MotorData
Apr 23, 2026
923a54e
feat: capture TalonFX Velocity signal in compute_motor_data
Apr 23, 2026
09418b0
feat: add plot_watts_per_rps — supply watts per RPS per motor
Apr 23, 2026
bbd4bf1
fix: add constant comments and correct fixture duration for W/RPS smo…
Apr 23, 2026
f35f69a
feat: wire plot_watts_per_rps into reporter for single and multi-matc…
Apr 23, 2026
7113836
feat: supply watts vs RPS scatter plots, paginated 6/page in 2-col grid
Apr 23, 2026
a175296
added w/rps graphs
Apr 23, 2026
1ad799b
docs: add motor table redesign spec and implementation plan
Apr 23, 2026
228a48c
feat: add DeviceTemp to MOTOR_COL_PATTERN
Apr 23, 2026
7485693
feat: add device_temp field to MotorData, extract DeviceTemp in compu…
Apr 23, 2026
8a26068
feat: migrate to hoot-only input, move MOTOR_COL_PATTERN and helpers …
Apr 23, 2026
cb5855b
refactor: consolidate imports at top of test_hoot_loader.py
Apr 23, 2026
6018bc0
feat: combine output/supply tables, add device temp with color flagging
Apr 23, 2026
84ca994
test: strengthen cell color assertion and add max_temp boundary test
Apr 23, 2026
72b09a0
docs: fix stale CSV reference in MotorData comment
Apr 23, 2026
a281bcf
docs: add static site generator design spec
Apr 24, 2026
13e6adf
docs: add static site generator implementation plan
Apr 24, 2026
3aa80fb
chore: ignore .worktrees directory
Apr 24, 2026
6c30e80
fix: safe max reduction and motor name guard in stats tab
Apr 24, 2026
164c440
docs: add offline deps design spec
Apr 25, 2026
775e1ce
docs: add offline deps implementation plan
Apr 25, 2026
c136df5
logo
Apr 26, 2026
665e2c1
docs: add logo/header + hoot-dir design spec
Apr 26, 2026
13a4a06
docs: add logo/header + hoot-dir implementation plan
Apr 26, 2026
5598702
chore: bring site_builder, templates, and static assets from feature/…
Apr 26, 2026
e83dcb5
feat: add Flashpoint title to site topbar
Apr 26, 2026
95a980d
feat: add --hoot-dir and --clean flags to CLI
Apr 26, 2026
32648d6
fix: correct --clean help text and move shutil import to module level
Apr 26, 2026
af97804
fix: warn when --clean is used without --site
Apr 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"permissions": {
"allow": [
"Bash(pyenv versions *)",
"Bash(poetry install *)",
"Bash(poetry run *)",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(python *)",
"Bash(tee /tmp/pytest_output.log)",
"Bash(open /tmp/report.pdf)"
]
}
}
5 changes: 4 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
**/docker/
**/telemetry/
Dockerfile-ingest
**/db/
**/converted_data/

Dockerfile
docker-compose.yml
.venv
.env
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,11 @@ faketele/
old_2024_data/
imported_data/

# macOS
.DS_Store

# worktrees
.worktrees/

# add .! to start to manually exclude
.!*.superpowers/
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
flashpoint
5 changes: 5 additions & 0 deletions .streamlit/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[server]
showEmailPrompt = false

[browser]
gatherUsageStats = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<h2>Heatmap color scale — global or per-motor?</h2>
<p class="subtitle">How should color intensity be assigned across motors?</p>

<div class="split">
<div class="mockup">
<div class="mockup-header" style="cursor:pointer;user-select:none" data-choice="global" onclick="toggleSelect(this)">
A — Global scale (click to select)
</div>
<div class="mockup-body" style="padding:16px">
<p style="font-size:12px;color:#aaa;margin-bottom:12px">All motors share one color scale. Red = highest W on the robot. A motor that barely ran stays blue even at its personal peak.</p>
<svg width="100%" height="120" viewBox="0 0 360 120">
<!-- row labels -->
<text x="2" y="18" fill="#888" font-size="9">TFX-1 (drive)</text>
<text x="2" y="36" fill="#888" font-size="9">TFX-2 (drive)</text>
<text x="2" y="54" fill="#888" font-size="9">TFX-3 (drive)</text>
<text x="2" y="72" fill="#888" font-size="9">TFX-4 (intake)</text>
<text x="2" y="90" fill="#888" font-size="9">TFX-5 (arm)</text>

<!-- TFX-1: heavy drive motor, lots of red -->
<rect x="80" y="7" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="104" y="7" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="128" y="7" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="152" y="7" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="176" y="7" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="200" y="7" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="224" y="7" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="248" y="7" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="272" y="7" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="296" y="7" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="320" y="7" width="22" height="13" fill="#e74c3c" rx="1"/>

<!-- TFX-2: similar drive motor -->
<rect x="80" y="25" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="104" y="25" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="128" y="25" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="152" y="25" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="176" y="25" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="200" y="25" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="224" y="25" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="248" y="25" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="272" y="25" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="296" y="25" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="320" y="25" width="22" height="13" fill="#c0392b" rx="1"/>

<!-- TFX-3: drive motor -->
<rect x="80" y="43" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="104" y="43" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="128" y="43" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="152" y="43" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="176" y="43" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="200" y="43" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="224" y="43" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="248" y="43" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="272" y="43" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="296" y="43" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="320" y="43" width="22" height="13" fill="#c0392b" rx="1"/>

<!-- TFX-4: intake, low power — all blue on global scale -->
<rect x="80" y="61" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="104" y="61" width="22" height="13" fill="#1e3f70" rx="1"/>
<rect x="128" y="61" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="152" y="61" width="22" height="13" fill="#1e3f70" rx="1"/>
<rect x="176" y="61" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="200" y="61" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="224" y="61" width="22" height="13" fill="#1e3f70" rx="1"/>
<rect x="248" y="61" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="272" y="61" width="22" height="13" fill="#1e3f70" rx="1"/>
<rect x="296" y="61" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="320" y="61" width="22" height="13" fill="#1a3a6b" rx="1"/>

<!-- TFX-5: arm, medium -->
<rect x="80" y="79" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="104" y="79" width="22" height="13" fill="#2e5f8a" rx="1"/>
<rect x="128" y="79" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="152" y="79" width="22" height="13" fill="#2e5f8a" rx="1"/>
<rect x="176" y="79" width="22" height="13" fill="#4a7fb5" rx="1"/>
<rect x="200" y="79" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="224" y="79" width="22" height="13" fill="#2e5f8a" rx="1"/>
<rect x="248" y="79" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="272" y="79" width="22" height="13" fill="#4a7fb5" rx="1"/>
<rect x="296" y="79" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="320" y="79" width="22" height="13" fill="#2e5f8a" rx="1"/>

<!-- colorbar -->
<defs>
<linearGradient id="heatA" x1="0" x2="1" y1="0" y2="0">
<stop offset="0%" stop-color="#1a3a6b"/>
<stop offset="50%" stop-color="#c0392b"/>
<stop offset="100%" stop-color="#e74c3c"/>
</linearGradient>
</defs>
<rect x="80" y="100" width="200" height="8" fill="url(#heatA)" rx="2"/>
<text x="80" y="116" fill="#888" font-size="8">0 W</text>
<text x="260" y="116" fill="#888" font-size="8">max W (robot)</text>
</svg>
<p style="font-size:11px;color:#e67e22;margin-top:8px">Note: intake row stays blue — you can tell it drew less than the drivetrain.</p>
</div>
</div>

<div class="mockup">
<div class="mockup-header" style="cursor:pointer;user-select:none" data-choice="per-motor" onclick="toggleSelect(this)">
B — Per-motor scale (click to select)
</div>
<div class="mockup-body" style="padding:16px">
<p style="font-size:12px;color:#aaa;margin-bottom:12px">Each motor's color spans its own min–max. Red = that motor's personal peak. Shows temporal pattern for every motor equally — but you can't compare intensity across rows.</p>
<svg width="100%" height="120" viewBox="0 0 360 120">
<text x="2" y="18" fill="#888" font-size="9">TFX-1 (drive)</text>
<text x="2" y="36" fill="#888" font-size="9">TFX-2 (drive)</text>
<text x="2" y="54" fill="#888" font-size="9">TFX-3 (drive)</text>
<text x="2" y="72" fill="#888" font-size="9">TFX-4 (intake)</text>
<text x="2" y="90" fill="#888" font-size="9">TFX-5 (arm)</text>

<!-- All motors now use full range — intake looks just as "active" as drive -->
<rect x="80" y="7" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="104" y="7" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="128" y="7" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="152" y="7" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="176" y="7" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="200" y="7" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="224" y="7" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="248" y="7" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="272" y="7" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="296" y="7" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="320" y="7" width="22" height="13" fill="#e74c3c" rx="1"/>

<rect x="80" y="25" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="104" y="25" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="128" y="25" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="152" y="25" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="176" y="25" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="200" y="25" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="224" y="25" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="248" y="25" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="272" y="25" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="296" y="25" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="320" y="25" width="22" height="13" fill="#c0392b" rx="1"/>

<rect x="80" y="43" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="104" y="43" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="128" y="43" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="152" y="43" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="176" y="43" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="200" y="43" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="224" y="43" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="248" y="43" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="272" y="43" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="296" y="43" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="320" y="43" width="22" height="13" fill="#c0392b" rx="1"/>

<!-- TFX-4 intake: per-motor scale so it ALSO looks red even though absolute W is low -->
<rect x="80" y="61" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="104" y="61" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="128" y="61" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="152" y="61" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="176" y="61" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="200" y="61" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="224" y="61" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="248" y="61" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="272" y="61" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="296" y="61" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="320" y="61" width="22" height="13" fill="#1a3a6b" rx="1"/>

<rect x="80" y="79" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="104" y="79" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="128" y="79" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="152" y="79" width="22" height="13" fill="#922b21" rx="1"/>
<rect x="176" y="79" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="200" y="79" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="224" y="79" width="22" height="13" fill="#e74c3c" rx="1"/>
<rect x="248" y="79" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="272" y="79" width="22" height="13" fill="#c0392b" rx="1"/>
<rect x="296" y="79" width="22" height="13" fill="#1a3a6b" rx="1"/>
<rect x="320" y="79" width="22" height="13" fill="#922b21" rx="1"/>

<defs>
<linearGradient id="heatB" x1="0" x2="1" y1="0" y2="0">
<stop offset="0%" stop-color="#1a3a6b"/>
<stop offset="50%" stop-color="#c0392b"/>
<stop offset="100%" stop-color="#e74c3c"/>
</linearGradient>
</defs>
<rect x="80" y="100" width="200" height="8" fill="url(#heatB)" rx="2"/>
<text x="80" y="116" fill="#888" font-size="8">motor min</text>
<text x="248" y="116" fill="#888" font-size="8">motor max</text>
</svg>
<p style="font-size:11px;color:#e67e22;margin-top:8px">Note: intake row is equally vivid — you can see its duty cycle but not that it barely draws power.</p>
</div>
</div>
</div>
Loading