forza-trigger: drop pedal-off early-returns (root cause of "no reaction")

Live diagnostic on yarn revealed the daemon was receiving 324-byte FH5
packets correctly (5.7MB on the systemd socket; strace showed steady
recvfrom + write to /dev/hidraw7) but writing trigger mode 0x05
(no-resistance) on nearly every tick. Cause: `accel` and `brake` are
0 most of the time during normal play (off-throttle on straight
sections, off-brake when not braking). Both handlers had:

  if accel/255 <= THROTTLE_INPUT_THRESHOLD: effect.off(); return
  if brake/255 <= BRAKE_INPUT_THRESHOLD:    effect.off(); return

Every off-pedal packet set the trigger to OFF. Brief pedal-on moments
set vibration. The result: rapidly oscillating off↔vibration state,
imperceptible at 60 Hz packet rate.

These early-returns were holdovers from the previous Race-Element 1:1
port (variant A), which IS designed to be silent unless slipping.
Variant D's whole point is "always feels something" — Cosmii has no
pedal-off gate, and its baseline branch produces feedback even at
brake=0/accel=0 with strength clamped to MIN.

Fix: remove both early-returns. Foot-off-pedal flows through the
baseline branch and produces feedback(strength=MIN_*_RESISTANCE). The
user feels light constant resistance instead of silence. Trigger only
returns to physical-rest when out-of-race (run-loop's reset_triggers).

Also drop the now-dead BRAKE_INPUT_THRESHOLD / THROTTLE_INPUT_THRESHOLD
constants. Two tests renamed and updated to assert MIN-strength
baseline feedback instead of effect.off() on zero pedal.

54/54 tests pass. Build clean.
This commit is contained in:
2026-05-07 14:18:47 -04:00
parent e159f4d90f
commit 11d283585c
2 changed files with 16 additions and 18 deletions

View File

@@ -92,11 +92,6 @@ LOG = logging.getLogger("forza-trigger")
# Source citations point to legacy_Program.cs (Cosmii) and fds_Settings.cs # Source citations point to legacy_Program.cs (Cosmii) and fds_Settings.cs
# (Patmagauran defaults, inherited by Cosmii via INI file). # (Patmagauran defaults, inherited by Cosmii via INI file).
# Pedal input gates: avoid noise at zero pedal pressure. Not in upstream;
# carried over from the previous Race-Element port (BRAKE_INPUT_THRESHOLD = 3%).
BRAKE_INPUT_THRESHOLD = 0.03 # 0..1 fraction of pedal travel
THROTTLE_INPUT_THRESHOLD = 0.03
# --- L2 brake --------------------------------------------------------------- # --- L2 brake ---------------------------------------------------------------
# fds_Settings.cs (Cosmii inherits via appsettings.ini). # fds_Settings.cs (Cosmii inherits via appsettings.ini).
GRIP_LOSS_VAL = 0.5 # combined-slip threshold for vibration overlay GRIP_LOSS_VAL = 0.5 # combined-slip threshold for vibration overlay
@@ -402,12 +397,12 @@ def handle_throttle(controller: DualSenseController, pkt: ForzaDataPacket, state
fall through to feedback-only (avoids audible fall through to feedback-only (avoids audible
clicking at very low frequencies). clicking at very low frequencies).
- In-race + no slip: continuous feedback resistance scaled by avgAccel. - In-race + no slip: continuous feedback resistance scaled by avgAccel.
- Pedal not pressed: trigger off. - Pedal at rest: baseline still active at MIN strength so the
trigger always has *some* feel under the finger.
Trigger is only `effect.off()` when out of race
(handled by the run-loop, not here).
""" """
accel = _safe_field(pkt, "accel", 0.0) accel = _safe_field(pkt, "accel", 0.0)
if accel / 255.0 <= THROTTLE_INPUT_THRESHOLD:
controller.right_trigger.effect.off()
return
avg_a = _avg_accel(pkt) avg_a = _avg_accel(pkt)
_, combined_front, combined_rear = _combined_slip(pkt) _, combined_front, combined_rear = _combined_slip(pkt)
@@ -484,12 +479,10 @@ def handle_brake(controller: DualSenseController, pkt: ForzaDataPacket, state: D
- In-race + slip: vibration overlay (freq from slip, amp from brake input). - In-race + slip: vibration overlay (freq from slip, amp from brake input).
Below MIN_BRAKE_VIBRATION freq → revert to feedback-only. Below MIN_BRAKE_VIBRATION freq → revert to feedback-only.
- In-race + no slip: continuous feedback resistance scaled by brake input. - In-race + no slip: continuous feedback resistance scaled by brake input.
- Pedal not pressed: trigger off. - Pedal at rest: baseline still active at MIN strength so the
trigger always has some feel.
""" """
brake = _safe_field(pkt, "brake", 0.0) brake = _safe_field(pkt, "brake", 0.0)
if brake / 255.0 <= BRAKE_INPUT_THRESHOLD:
controller.left_trigger.effect.off()
return
# Use the worst axle's slip for both detection and the freq curve. # Use the worst axle's slip for both detection and the freq curve.
# Cosmii's all-4 average misses single-axle ABS events (front locks # Cosmii's all-4 average misses single-axle ABS events (front locks

View File

@@ -205,12 +205,16 @@ class TestIsRaceOn(unittest.TestCase):
class TestHandleThrottle(unittest.TestCase): class TestHandleThrottle(unittest.TestCase):
def test_zero_pedal_turns_off(self): def test_zero_pedal_baseline_min_feedback(self):
# Foot off throttle should NOT turn the trigger off — variant D's
# design is "always feels something". Baseline mode still fires
# with strength = MIN_THROTTLE_RESISTANCE so the user feels light
# constant resistance under the finger. The run-loop is the only
# place that calls effect.off() (out-of-race idle reset).
c = FakeController() c = FakeController()
s = ft.DaemonState() s = ft.DaemonState()
ft.handle_throttle(c, FakePacket(accel=0.0), s) ft.handle_throttle(c, FakePacket(accel=0.0), s)
self.assertEqual(c.calls, [("R2", "off")]) self.assertEqual(c.calls, [("R2", "feedback", 0, ft.MIN_THROTTLE_RESISTANCE)])
def test_baseline_under_normal_acceleration(self): def test_baseline_under_normal_acceleration(self):
c = FakeController() c = FakeController()
s = ft.DaemonState() s = ft.DaemonState()
@@ -276,11 +280,12 @@ class TestHandleThrottle(unittest.TestCase):
class TestHandleBrake(unittest.TestCase): class TestHandleBrake(unittest.TestCase):
def test_zero_pedal_turns_off(self): def test_zero_pedal_baseline_min_feedback(self):
# Same as throttle — foot off brake = light constant resistance, not off.
c = FakeController() c = FakeController()
s = ft.DaemonState() s = ft.DaemonState()
ft.handle_brake(c, FakePacket(brake=0.0), s) ft.handle_brake(c, FakePacket(brake=0.0), s)
self.assertEqual(c.calls, [("L2", "off")]) self.assertEqual(c.calls, [("L2", "feedback", 0, ft.MIN_BRAKE_RESISTANCE)])
def test_baseline_under_normal_braking(self): def test_baseline_under_normal_braking(self):
c = FakeController() c = FakeController()