From 0a48a2928ed56f714e5a33d3157d4108c4db5f44 Mon Sep 17 00:00:00 2001 From: Matt Hills Date: Mon, 19 Jan 2026 17:28:24 -0500 Subject: [PATCH] v1.3.2: Fix HR variation jumping instead of smooth transitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove hard clamp that instantly snapped HR to ±3 of target, causing jumps when target changed. Now uses rate-limited transitions (1 BPM per tick at 0.5s intervals) for smooth ramping. Co-Authored-By: Claude Opus 4.5 --- src/api/hr_variation.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/api/hr_variation.py b/src/api/hr_variation.py index 2555b5f..90adb17 100644 --- a/src/api/hr_variation.py +++ b/src/api/hr_variation.py @@ -113,14 +113,16 @@ class HRVariationManager: # Calculate ideal HR ideal_hr = state.target + sine_variation + random_walk - # Smooth transition (30% toward ideal each tick) - state.float_hr += (ideal_hr - state.float_hr) * 0.3 - - # Clamp to ±3 of target - state.float_hr = max( - state.target - 3, - min(state.target + 3, state.float_hr) - ) + # Smooth transition toward ideal + # Use rate-limited step when far from target for smooth ramp + distance = abs(ideal_hr - state.float_hr) + if distance > 3: + # Far from target: move max 1 BPM per tick (2 BPM/sec at 0.5s ticks) + step = 1.0 if ideal_hr > state.float_hr else -1.0 + state.float_hr += step + else: + # Close to target: use proportional smoothing + state.float_hr += (ideal_hr - state.float_hr) * 0.2 # Round for transmission current_hr = round(max(30, min(220, state.float_hr))) @@ -130,7 +132,7 @@ class HRVariationManager: last_sent = current_hr await self._send_hr(device_id, current_hr) - await asyncio.sleep(1) + await asyncio.sleep(0.5) except asyncio.CancelledError: pass