Skip to content

Commit cf97aa8

Browse files
committed
New SBT graphs and general fixes to the SBT system.
1 parent e5c5938 commit cf97aa8

5 files changed

Lines changed: 413 additions & 128 deletions

File tree

Components/SeatBeltTensioner.cs

Lines changed: 124 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,36 @@
11
using MarvinsAIRARefactored.Classes;
2+
using MarvinsAIRARefactored.Controls;
23
using MarvinsAIRARefactored.Windows;
34

45
namespace MarvinsAIRARefactored.Components;
56

67
public class SeatBeltTensioner
78
{
8-
// Telemetry → SBT update rate: 60fps source / 6 = 10 updates per second
9-
private const int UpdateInterval = 6;
9+
// Telemetry → SBT update rate: 60fps source / 3 = 20 updates per second
10+
private const int UpdateInterval = 3;
1011

1112
public bool IsConnected { get; private set; } = false;
1213

1314
private readonly UsbSerialPortHelper _usbSerialPortHelper = new( "MAIRA SBT" );
1415

16+
private readonly SeatBeltTensionerGraph _surgeGraph = new();
17+
private readonly SeatBeltTensionerGraph _swayGraph = new();
18+
private readonly SeatBeltTensionerGraph _heaveGraph = new();
19+
20+
private readonly SeatBeltTensionerGraph _leftShoulderGraph = new();
21+
private readonly SeatBeltTensionerGraph _rightShoulderGraph = new();
22+
1523
private int _updateCounter = UpdateInterval + 2;
1624
private int _lastSentLeftTenths = -1;
1725
private int _lastSentRightTenths = -1;
1826

27+
private float _longAccelSum = 0f;
28+
private float _latAccelSum = 0f;
29+
private float _vertAccelSum = 0f;
30+
private float _pitchSum = 0f;
31+
private float _rollSum = 0f;
32+
private int _accelSampleCount = 0;
33+
1934
public SeatBeltTensioner()
2035
{
2136
var app = App.Instance!;
@@ -35,6 +50,15 @@ public void Initialize()
3550

3651
_usbSerialPortHelper.Initialize();
3752

53+
var sbtPage = MainWindow._seatBeltTensionerPage;
54+
55+
_surgeGraph.Initialize( sbtPage.SurgeGraph_Image, 0.3f, 0f, 0.2f, 1f, 0.08f, 0.58f );
56+
_swayGraph.Initialize( sbtPage.SwayGraph_Image, 0.1f, 0.3f, 0f, 0.5f, 1f, 0f );
57+
_heaveGraph.Initialize( sbtPage.HeaveGraph_Image, 0.1f, 0.2f, 0.3f, 0.3f, 0.7f, 1f );
58+
59+
_leftShoulderGraph.Initialize( sbtPage.LeftShoulderGraph_Image, 0f, 0f, 0.2f, 0f, 0f, 1f );
60+
_rightShoulderGraph.Initialize( sbtPage.RightShoulderGraph_Image, 0.2f, 0f, 0f, 1f, 0f, 0f );
61+
3862
if ( !_usbSerialPortHelper.DeviceFound )
3963
{
4064
app.Logger.WriteLine( "[SeatBeltTensioner] Device not found - disabling SeatBeltTensionerEnabled" );
@@ -160,7 +184,28 @@ private void Update( App app )
160184
{
161185
var settings = DataContext.DataContext.Instance.Settings;
162186

163-
if ( !settings.SeatBeltTensionerEnabled || !IsConnected || !app.Simulator.IsOnTrack )
187+
if ( _accelSampleCount == 0 )
188+
{
189+
return;
190+
}
191+
192+
var longAccelAvg = _longAccelSum / _accelSampleCount;
193+
var latAccelAvg = _latAccelSum / _accelSampleCount;
194+
var vertAccelAvg = _vertAccelSum / _accelSampleCount;
195+
196+
var pitch = _pitchSum / _accelSampleCount;
197+
var roll = _rollSum / _accelSampleCount;
198+
199+
_longAccelSum = 0f;
200+
_latAccelSum = 0f;
201+
_vertAccelSum = 0f;
202+
203+
_pitchSum = 0f;
204+
_rollSum = 0f;
205+
206+
_accelSampleCount = 0;
207+
208+
if ( !settings.SeatBeltTensionerEnabled || !IsConnected )
164209
{
165210
return;
166211
}
@@ -176,14 +221,57 @@ private void Update( App app )
176221
// Calculate normalized neutral position
177222
var neutralPositionNormalized = ( neutralTenths - 900 ) / (float) rangeTenths;
178223

224+
// Compute gravity components in car body space using averaged pitch and roll.
225+
// iRacing includes gravity in the acceleration telemetry (specific force), so we subtract
226+
// the gravitational contribution to get the true inertial acceleration.
227+
// Pitch: positive = nose up. Roll: positive = left side up.
228+
var cosPitch = MathF.Cos( pitch );
229+
var sinPitch = MathF.Sin( pitch );
230+
var cosRoll = MathF.Cos( roll );
231+
var sinRoll = MathF.Sin( roll );
232+
233+
// Gravity component along each body axis (what iRacing adds to raw acceleration)
234+
var gravVert = MathZ.OneG * cosPitch * cosRoll;
235+
236+
float longAccel;
237+
float latAccel;
238+
float vertAccel;
239+
240+
if ( settings.SeatBeltTensionerSubtractFullGravity )
241+
{
242+
var gravLong = MathZ.OneG * -sinPitch;
243+
var gravLat = MathZ.OneG * cosPitch * sinRoll;
244+
245+
longAccel = longAccelAvg - gravLong;
246+
latAccel = latAccelAvg - gravLat;
247+
vertAccel = vertAccelAvg - gravVert;
248+
}
249+
else
250+
{
251+
longAccel = longAccelAvg;
252+
latAccel = latAccelAvg;
253+
vertAccel = vertAccelAvg - gravVert;
254+
}
255+
179256
// Surge normalized [-1..1]:
180-
var surgeNormalized = Math.Clamp( -app.Simulator.LongAccel / MathZ.OneG / settings.SeatBeltTensionerSurgeMaxG, -1f, 1f );
257+
var surgeNormalized = Math.Clamp( -longAccel / MathZ.OneG / settings.SeatBeltTensionerSurgeMaxG, -1f, 1f );
181258

182259
// Sway normalized [-1..1]: positive biases right belt tighter, left belt looser
183-
var swayNormalized = Math.Clamp( app.Simulator.LatAccel / MathZ.OneG / settings.SeatBeltTensionerSwayMaxG, -1f, 1f );
260+
var swayNormalized = Math.Clamp( latAccel / MathZ.OneG / settings.SeatBeltTensionerSwayMaxG, -1f, 1f );
184261

185262
// Heave normalized [-1..1]: bumps and crests both tighten both belts
186-
var heaveNormalized = Math.Clamp( app.Simulator.VertAccel / MathZ.OneG / settings.SeatBeltTensionerHeaveMaxG, -1f, 1f );
263+
var heaveNormalized = Math.Clamp( -vertAccel / MathZ.OneG / settings.SeatBeltTensionerHeaveMaxG, -1f, 1f );
264+
265+
if ( MairaAppMenuPopup.CurrentAppPage == MainWindow.AppPage.SeatBeltTensioner )
266+
{
267+
_surgeGraph.Advance( surgeNormalized );
268+
_swayGraph.Advance( swayNormalized );
269+
_heaveGraph.Advance( heaveNormalized );
270+
271+
_surgeGraph.WritePixels();
272+
_swayGraph.WritePixels();
273+
_heaveGraph.WritePixels();
274+
}
187275

188276
// Combine into per-arm normalized signal and offset by neutral position
189277
var leftCombinedNormalized = surgeNormalized + heaveNormalized - swayNormalized + neutralPositionNormalized;
@@ -197,6 +285,24 @@ private void Update( App app )
197285
var leftTargetPositionTenths = Math.Clamp( (int) MathF.Round( limitedLeftNormalized * rangeTenths + 900 ), minimumTenths, maximumTenths );
198286
var rightTargetPositionTenths = Math.Clamp( (int) MathF.Round( limitedRightNormalized * rangeTenths + 900 ), minimumTenths, maximumTenths );
199287

288+
if ( MairaAppMenuPopup.CurrentAppPage == MainWindow.AppPage.SeatBeltTensioner )
289+
{
290+
// Remap tenths to [-1..1]: -1=minimum, 0=neutral, +1=maximum (piecewise linear)
291+
var leftShoulderNormalized = leftTargetPositionTenths <= neutralTenths
292+
? (float) ( leftTargetPositionTenths - neutralTenths ) / ( neutralTenths - minimumTenths )
293+
: (float) ( leftTargetPositionTenths - neutralTenths ) / ( maximumTenths - neutralTenths );
294+
295+
var rightShoulderNormalized = rightTargetPositionTenths <= neutralTenths
296+
? (float) ( rightTargetPositionTenths - neutralTenths ) / ( neutralTenths - minimumTenths )
297+
: (float) ( rightTargetPositionTenths - neutralTenths ) / ( maximumTenths - neutralTenths );
298+
299+
_leftShoulderGraph.Advance( leftShoulderNormalized );
300+
_rightShoulderGraph.Advance( rightShoulderNormalized );
301+
302+
_leftShoulderGraph.WritePixels();
303+
_rightShoulderGraph.WritePixels();
304+
}
305+
200306
// Send the new positions to the SBT if they have changed since the last update
201307
SendSetPosition( leftTargetPositionTenths, rightTargetPositionTenths );
202308
}
@@ -208,6 +314,18 @@ private void OnPortClosed( object? sender, EventArgs e )
208314

209315
public void Tick( App app )
210316
{
317+
if ( app.Simulator.IsOnTrack )
318+
{
319+
_longAccelSum += app.Simulator.LongAccel;
320+
_latAccelSum += app.Simulator.LatAccel;
321+
_vertAccelSum += app.Simulator.VertAccel;
322+
323+
_pitchSum += app.Simulator.Pitch;
324+
_rollSum += app.Simulator.Roll;
325+
326+
_accelSampleCount++;
327+
}
328+
211329
_updateCounter--;
212330

213331
if ( _updateCounter <= 0 )
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System.Runtime.CompilerServices;
2+
3+
using MarvinsAIRARefactored.Classes;
4+
5+
using Image = System.Windows.Controls.Image;
6+
7+
namespace MarvinsAIRARefactored.Components;
8+
9+
public class SeatBeltTensionerGraph : GraphBase
10+
{
11+
private float _minR;
12+
private float _minG;
13+
private float _minB;
14+
15+
private float _maxR;
16+
private float _maxG;
17+
private float _maxB;
18+
19+
public void Initialize( Image image, float minR, float minG, float minB, float maxR, float maxG, float maxB )
20+
{
21+
Initialize( image );
22+
23+
_minR = minR;
24+
_minG = minG;
25+
_minB = minB;
26+
27+
_maxR = maxR;
28+
_maxG = maxG;
29+
_maxB = maxB;
30+
}
31+
32+
[MethodImpl( MethodImplOptions.AggressiveInlining )]
33+
public void Advance( float value )
34+
{
35+
Update( value, _minR, _minG, _minB, _maxR, _maxG, _maxB );
36+
FinishUpdates();
37+
}
38+
}

DataContext/Settings.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10053,6 +10053,27 @@ public bool SeatBeltTensionerEnabled
1005310053

1005410054
#endregion
1005510055

10056+
#region Seat Belt Tensioner - Subtract Full Gravity
10057+
10058+
private bool _seatBeltTensionerSubtractFullGravity = false;
10059+
10060+
public bool SeatBeltTensionerSubtractFullGravity
10061+
{
10062+
get => _seatBeltTensionerSubtractFullGravity;
10063+
10064+
set
10065+
{
10066+
if ( value != _seatBeltTensionerSubtractFullGravity )
10067+
{
10068+
_seatBeltTensionerSubtractFullGravity = value;
10069+
10070+
OnPropertyChanged();
10071+
}
10072+
}
10073+
}
10074+
10075+
#endregion
10076+
1005610077
#region Seat Belt Tensioner - Minimum
1005710078

1005810079
private float _seatBeltTensionerMinimum = 45f;

0 commit comments

Comments
 (0)