Skip to content

Project the parked drain from odometer-steady samples - #85

Merged
dzid26 merged 3 commits into
mainfrom
work/projection-horizon
Oct 9, 2026
Merged

dzid26 merged 3 commits into
mainfrom
work/projection-horizon

Conversation

@dzid26

@dzid26 dzid26 commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Bases the discharge projection on the parked drain only, using the DriveState odometer (#149) to tell parked stretches from drives. One commit on top of main.

What counts as parked

  • A pair of consecutive battery samples is parked when neither is charging and the odometer did not move between them.
  • Each sample takes its odometer from the drive sample nearest to it in time, within 2 minutes. A drive read follows every charge read, both on the car's clock.
  • A sample with no drive sample in that window (or one without an odometer) isn't known to be parked, so its pairs are left out.
  • A long unobserved gap still counts when the odometer is unchanged across it. That keeps the overnight drop up to the wake-up read when the door opens. A drive no phone saw shows up as an odometer change and is left out.
  • The rate is the summed signed percent change over the summed parked time, so a parked rounding blip cancels instead of inflating the drop.

Horizon and chart

  • The projection looks half the selected range ahead (6 h → 3 h, 24 h → 12 h, 7 d → 3.5 d, All → half the span).
  • The chart's x-axis expands by that horizon, so the dashed line keeps its slope.

Sparse samples (#161). Battery reads now come every 20 min while parked and awake, and not at all while asleep. I checked the two rules:

  • Under 10 minutes of parked time: unchanged. It applies to the summed parked time, which hour-long gaps easily clear.
  • Last sample stale: changed. The old bound (a quarter of the range, 30 min to 6 h) hid the projection for most of a night parked, since a sleeping car produces no reads. The projection now stays until its whole line lies in the past, i.e. the last sample is older than the horizon. A 24 h chart keeps it for 12 h after the last read.

Wiring.

  • The car screen feeds the selected car's drive samples into the projection.
  • MainScreen takes battery and drive samples as one HistorySamples value. That keeps it at 14 parameters, under detekt's limit, without raising the threshold.
  • The demo history now seeds a DriveState next to each charge record. Its odometer moves only across the seeded drives and ends at the simulated car's live odometer, so the demo still shows the parked drain.

Type

  • Bug fix
  • Feature
  • Protocol / crypto change
  • Docs / CI / tooling
  • Refactor (no behavior change)

Testing

  • ./gradlew :core:test. DischargeProjectionTest (kotlin.test, 13 cases) covers:
    • a long overnight gap with an unchanged odometer counts;
    • a gap with an odometer change doesn't;
    • a charging pair doesn't;
    • a missing drive sample excludes its pairs (including one just past the 2-minute tolerance, and none at all);
    • a rounding blip cancels;
    • the horizon is half the range;
    • a sleeping car keeps its projection until the horizon passes;
    • the null cases.
  • ./gradlew :app:testDebugUnitTest
  • ./gradlew :app:lintDebug
  • ./gradlew :app:assembleDebug, ktlintCheck, detekt. The full build from the brief is green locally.
  • Real car: not tested. Worth checking on the car that the overnight drop shows up after the morning wake-up read.

Checklist

  • Protocol changes ship test vectors; expected.txt regenerated, not hand-edited (n/a: no protocol change)
  • No VINs, keys, or decrypted payloads in logs
  • Docs/master-plan updated where relevant: README, master-plan projection and drive-detection lines
  • New source files carry SPDX-License-Identifier: AGPL-3.0-only

🤖 Generated with Claude Code

https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Projection fix on the phone — 24 h chart, real data.

24 h chart with the discharge projection

The x-axis now spans 05 Oct 12:29 → 07 Oct 00:29 (24 h back + the 12 h horizon) and the dashed line drops 72 → 68 % over 12 h instead of the old near-vertical stub at the right edge. Caption: Discharging · projected 68% by 06 Oct 22:17.

The 6 h chart shows no projection right now: the newest sample is 2 h 11 m old, past the 6 h range's freshness bound (1.5 h).

@dzid26

dzid26 commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

Actually we should only project based on Parked slope. Waiting for #94 and getDriveState to land first.

@dzid26
dzid26 marked this pull request as draft October 6, 2026 14:45
@dzid26 dzid26 changed the title Project parked drain from every sample in the selected range Log VCSEC status and project the parked drain Oct 8, 2026
@dzid26
dzid26 marked this pull request as ready for review October 8, 2026 07:12
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Screenshots

App screenshots: light and dark

Captured from the demo build at 2a1b34a.

Debug APK: zip from Android CI (GitHub sign-in needed)

@dzid26 dzid26 changed the title Log VCSEC status and project the parked drain Project only the parked drain ahead Oct 8, 2026
dzid26 pushed a commit that referenced this pull request Oct 8, 2026
A build of the original #85 branch wrote an earlier record format to
<vehicleId>.status.pblog. The envelope reader stops at the first such
record, so that file would hide every later status reading. A new name
leaves the old file ignored, like the old raw charge logs, with no
manual delete before testing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Screenshots

App screenshots: light and dark

Captured from the demo build at 7aadccf.

Debug APK: zip from Android CI (GitHub sign-in needed)

@dzid26
dzid26 marked this pull request as draft October 8, 2026 08:20
@dzid26 dzid26 mentioned this pull request Oct 8, 2026
7 of 12 tasks
@dzid26 dzid26 changed the title Project only the parked drain ahead Project battery level ahead only for parked Oct 8, 2026
…head

The discharge projection now measures the drain over the selected range
from parked pairs of consecutive battery samples only: neither sample is
charging and the odometer did not move between them. Each sample takes
the odometer of the drive sample nearest to it within two minutes (a
drive read follows every charge read); a sample without one is not
known to be parked, so its pairs are left out. A long unobserved gap
still counts when the odometer is unchanged across it, which keeps the
overnight drop up to the wake-up read, while a drive no phone saw shows
up as an odometer change. Signed changes are summed, so a parked rounding
blip cancels out.

The horizon is half the selected range and the chart's x-axis expands
by it. With the sparser reads of the Infotainment poll policy (none
while asleep), the old 30 min to 6 h freshness bound hid the projection
for most of a night parked, so the projection now stays until its whole
line lies in the past: the last sample is older than the horizon.

The car screen gets the selected car's drive samples; MainScreen takes
both lists as one HistorySamples value to stay within detekt's parameter
limit. The demo history seeds a DriveState next to each charge record,
with the odometer moving only across its drives and ending at the
simulated car's live odometer. The discharge tests move to a kotlin.test
DischargeProjectionTest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
@dzid26
dzid26 force-pushed the work/projection-horizon branch from 7aadccf to e7cc97a Compare October 9, 2026 01:49
@dzid26 dzid26 changed the title Project battery level ahead only for parked Project the parked drain from odometer-steady samples Oct 9, 2026
@dzid26
dzid26 marked this pull request as ready for review October 9, 2026 01:49
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Screenshots

App screenshots: light and dark

Captured from the demo build at e7cc97a.

Debug APK: zip from Android CI (GitHub sign-in needed)

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Screenshots

Light

Scanning for nearby cars (light)Car detail with the battery reading (light)Settings (light)

Dark

Scanning for nearby cars (dark)Car detail with the battery reading (dark)Settings (dark)

Captured from the demo build at a47c909.

Debug APK: zip from Android CI (GitHub sign-in needed)

@dzid26
dzid26 merged commit 1ef75c2 into main Oct 9, 2026
5 checks passed
@dzid26
dzid26 deleted the work/projection-horizon branch October 9, 2026 15:01
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Screenshots

Light

Scanning for nearby cars (light)Car detail with the battery reading (light)Settings (light)

Dark

Scanning for nearby cars (dark)Car detail with the battery reading (dark)Settings (dark)

Captured from the demo build at 91efc11.

Debug APK: zip from Android CI (GitHub sign-in needed)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants