Skip to content

Wizard: MaxSteer as invariant raw counts, MaxPWM = 2 × MinPWM default#390

Draft
Xyntexx wants to merge 2 commits into
AgOpenGPS-Official:developfrom
Xyntexx:feat/max-steer-raw-counts-and-maxpwm-default
Draft

Wizard: MaxSteer as invariant raw counts, MaxPWM = 2 × MinPWM default#390
Xyntexx wants to merge 2 commits into
AgOpenGPS-Official:developfrom
Xyntexx:feat/max-steer-raw-counts-and-maxpwm-default

Conversation

@Xyntexx

@Xyntexx Xyntexx commented May 14, 2026

Copy link
Copy Markdown
Collaborator

Two related calibration refinements from bench-test of the wizard.

Issue M — MaxSteer stored as raw counts, degrees computed from current CPD (`ca6fea45`)

The wizard's Max Steering Angle step read `ActualSteerAngle` (post-CPD-conversion degrees) and saved it as `MaxSteerAngle` (also degrees). If CPD circle test ran after max-steer (or didn't run at all), the stored max-angle was wrong by the ratio `actualCPD / defaultCPD` — operator's wizard order shouldn't matter that way.

  • `AutoSteerConfig.MaxSteerRawCounts` is the new storage primitive (int counts).
  • `AutoSteerConfig.MaxSteerAngle` becomes a computed view: `MaxSteerRawCounts / CountsPerDegree` on get; back-converts on set. CPD's PropertyChanged raises `MaxSteerAngle` PropertyChanged so config dialog bindings refresh when the operator finishes the CPD circle test.
  • `MaxSteeringAngleStepViewModel` captures plateau, computes `chosenDeg = min(left, right) × 0.9`, multiplies by current CPD → raw counts. Persists only the raw count.

Net: operator can run CPD circle BEFORE or AFTER max-steer — final `MaxSteerAngle` is correct either way, because the raw-counts capture is CPD-invariant.

Issue N — MaxPwm default = 2 × MinPwm after motor cal (`0404bacc`)

After the Kp-ramp captures MinPwm, also write `MaxPwm = Math.Min(2 × MinPwm, 255)`. Sensible default; operator can tune in the AutoSteer config dialog if needed.

While in that area, found and fixed two real bugs:

  • `RunKpRampAsync` never set `CalibrationCompleted=true` on success. Result: `OnLeaving`'s save block (gated on the flag) silently dropped operator's MinPwm/InvertMotor. Now sets the flag inline at the success branch.
  • Vestigial `autoSteer.MaxSteerAngle = MaxSteerAngle` write in `OnLeaving` referenced a dead `_maxSteerAngle` field (default 0). Any future success would clobber the real max-steer calibration with 0. Removed — max-steer persistence lives entirely in the new step now.

The result panel now correctly shows both MinPwm and the seeded MaxPwm.

Tests

5 new in `MaxSteerRawCountsAndMaxPwmTests`:

  • `MaxSteer_RawCountsInvariantUnderCpdChange`
  • `MaxSteer_SetByDegrees_BackConvertsToRawCounts`
  • `MaxSteer_CapturesPlateauAsRawCounts`
  • `MotorCal_AfterCapture_SetsMaxPwmToDoubleMinPwm`
  • `MotorCal_AfterCapture_CapsMaxPwmAt255`

Full Services suite: 903 pass, 1 fail (pre-existing Windows file-lock flake, unrelated), 2 skip.

Non-spec note

The develop motor-cal VM still carries dead Phase B fields (`MaxSteerAngle` property, `MeasuringMaxAngle`/`Complete` enum values, `IsPhaseB0/B1`, `IsComplete`) that aren't wired to AXAML anymore. Harmless but visible. Worth a small follow-up to delete the carcass; left alone here to keep the diff focused on M+N.

Test plan

  • All 5 new tests + existing wizard suite green.
  • Build clean.
  • Bench: run wizard motor cal first (captures MinPwm, seeds MaxPwm). Then CPD circle test → CPD updates. Then max-steer → captures raw counts. Verify the persisted MaxSteerAngle in the config dialog reflects `rawCounts / cpd` correctly.
  • Bench: reverse the order — max-steer before CPD circle. Final degree value should still be correct after CPD calibration.

Xyntexx added 2 commits May 14, 2026 23:49
The legacy MaxSteerAngle was an int field on AutoSteerConfig storing
degrees. The wizard measured a plateau angle from PGN 253 (post-CPD-
conversion) and saved that degree value, so running CPD calibration
later left the stored max-steer wrong by ratio actualCPD/defaultCPD.
Bench-test on AgOpenGPS-Official#388 confirmed this — operators running the wizard in
the default order (CPD after max-steer) got a max-steer setting that
clipped guidance corrections far inside the real mechanical limit.

Switch the storage primitive to raw WAS counts and make
MaxSteerAngle (degrees) a computed view of
MaxSteerRawCounts / CountsPerDegree. Setting MaxSteerAngle by degrees
back-converts to raw counts at the current CPD, so legacy callers
(JSON profile load, config dialog spinner) keep working. CPD
changes raise PropertyChanged on MaxSteerAngle so the config dialog
display refreshes the moment the operator finishes the CPD circle
test.

MaxSteeringAngleStepViewModel:
- Adds MaxSteerRawCounts property alongside MaxSteerAngle.
- After plateau capture, computes chosenDeg = min(left, right) x 0.9
  then writes MaxSteerRawCounts = chosenDeg x currentCPD. Both the
  degree view and the raw-counts capture are exposed for the result
  panel.
- OnLeaving persists MaxSteerRawCounts (the source of truth); the
  config's computed MaxSteerAngle is derived on read.
Two related fixes in the Phase A Kp ramp:

N. After the ramp's success branch captures MinPwm from PGN 253,
   the step also seeds ConfigStore.AutoSteer.MaxPwm to
   min(2 x MinPwm, 255). Operators previously had to discover the
   default MaxPwm themselves in the config dialog; the 2x rule of
   thumb gives them a reasonable starting point that's high enough
   for the firmware to actually drive the wheel under load, low
   enough to avoid runaway tuning.

Persist-at-capture: moved the MinPwm / InvertMotor save out of
OnLeaving and inline at the success branch. The old OnLeaving block
was gated on CalibrationCompleted, but RunKpRampAsync never set that
flag (Phase B used to; that's split into MaxSteeringAngleStepViewModel
now). Result: operators who ran the ramp and then pressed Next saw
their Kp-ramp results silently dropped on the floor. Saving inline
at the success branch fixes the loss and reads more naturally as a
single 'capture + persist' step.

Also cleaned up a stale OnLeaving write to autoSteer.MaxSteerAngle.
The vestigial _maxSteerAngle field in this VM (left over from the
split) defaulted to 0; if CalibrationCompleted ever did get set, the
save block would have clobbered the operator's real max-steer
calibration with that zero. Removed the line; MaxSteer persistence
lives entirely in MaxSteeringAngleStepViewModel now.

Tests in MaxSteerRawCountsAndMaxPwmTests:
- MotorCal_AfterCapture_SetsMaxPwmToDoubleMinPwm — MinPwm=35 leads
  to MaxPwm=70.
- MotorCal_AfterCapture_CapsMaxPwmAt255 — MinPwm=200 caps MaxPwm
  at the byte ceiling rather than overflowing.
- Companion M tests (raw-counts invariance, plateau capture) live
  in the same fixture for cohesion.
@Xyntexx Xyntexx marked this pull request as draft May 15, 2026 06:23
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.

1 participant