Satisfactory fuel calculator done (still have plenty to do on it)

This commit is contained in:
2026-06-01 17:38:30 +01:00
parent b6075e2138
commit 0c90e5d4ac
4 changed files with 502 additions and 361 deletions
+325 -277
View File
@@ -1,29 +1,5 @@
Windows:
1:
uiwindow:
Dims:
X0: 359
Y0: 355
Width: 120
Height: 49
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: YELLOW
Title: RPMColour
uidata:
WID: 1
TelemetryField: RPM State Colour
2:
uiwindow:
Dims:
X0: 74
@@ -45,177 +21,9 @@ Windows:
PreviewValue: "12"
Title: TC
uidata:
WID: 2
WID: 1
TelemetryField: TC Control
3:
uiwindow:
Dims:
X0: 60
Y0: 91
Width: 85
Height: 57
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 4
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "123"
Title: Lap
uidata:
WID: 3
TelemetryField: Lap Number
4:
uiwindow:
Dims:
X0: 9
Y0: 413
Width: 57
Height: 57
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 4
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "12"
Title: ABS
uidata:
WID: 4
TelemetryField: ABS Control
5:
uiwindow:
Dims:
X0: 694
Y0: 367
Width: 100
Height: 50
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "101.4"
Title: Oil T
uidata:
WID: 5
TelemetryField: Oil Temperature
6:
uiwindow:
Dims:
X0: 60
Y0: 155
Width: 226
Height: 57
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 4
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "06:45.123"
Title: Last Lap
uidata:
WID: 6
TelemetryField: Last Lap Time
7:
uiwindow:
Dims:
X0: 590
Y0: 367
Width: 100
Height: 50
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "101.5"
Title: Water T
uidata:
WID: 7
TelemetryField: Water Temperature
8:
uiwindow:
Dims:
X0: 642
Y0: 188
Width: 150
Height: 50
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "2.7"
Title: Fuel Cur Lap
uidata:
WID: 8
TelemetryField: Fuel Current Lap
9:
uiwindow:
Dims:
X0: 470
Y0: 188
Width: 168
Height: 50
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: 12.3 L
Title: Fuel Last Lap
uidata:
WID: 9
TelemetryField: Fuel Last Lap
10:
2:
uiwindow:
Dims:
X0: 354
@@ -237,33 +45,9 @@ Windows:
PreviewValue: R
Title: Box
uidata:
WID: 10
WID: 2
TelemetryField: Gear
11:
uiwindow:
Dims:
X0: 142
Y0: 19
Width: 500
Height: 20
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 1
TextSize: 2
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 1
PreviewValue: "5678"
Title: TACHO
uidata:
WID: 11
TelemetryField: RPM
12:
3:
uiwindow:
Dims:
X0: 140
@@ -285,9 +69,105 @@ Windows:
PreviewValue: "6"
Title: Thr
uidata:
WID: 12
WID: 3
TelemetryField: Throttle Control
13:
4:
uiwindow:
Dims:
X0: 359
Y0: 355
Width: 120
Height: 49
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: YELLOW
Title: RPMColour
uidata:
WID: 4
TelemetryField: RPM State Colour
5:
uiwindow:
Dims:
X0: 60
Y0: 155
Width: 226
Height: 57
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 4
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "06:45.123"
Title: Last Lap
uidata:
WID: 5
TelemetryField: Last Lap Time
6:
uiwindow:
Dims:
X0: 470
Y0: 188
Width: 168
Height: 50
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: 12.3 L
Title: Fuel Last Lap
uidata:
WID: 6
TelemetryField: Fuel Last Lap
7:
uiwindow:
Dims:
X0: 142
Y0: 19
Width: 500
Height: 20
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 1
TextSize: 2
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 1
PreviewValue: "5678"
Title: TACHO
uidata:
WID: 7
TelemetryField: RPM
8:
uiwindow:
Dims:
X0: 694
@@ -309,57 +189,9 @@ Windows:
PreviewValue: "4.56"
Title: Oil P
uidata:
WID: 13
WID: 8
TelemetryField: Oil Pressure
14:
uiwindow:
Dims:
X0: 436
Y0: 71
Width: 206
Height: 57
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 4
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "9999"
Title: Dig Tacho
uidata:
WID: 14
TelemetryField: RPM
15:
uiwindow:
Dims:
X0: 710
Y0: 8
Width: 84
Height: 57
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 4
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: PIT
Title: Pit Sp
uidata:
WID: 15
TelemetryField: Pit Speed Limiter
16:
9:
uiwindow:
Dims:
X0: 331
@@ -381,9 +213,129 @@ Windows:
PreviewValue: "234"
Title: SPEEDO
uidata:
WID: 16
WID: 9
TelemetryField: Speed
17:
10:
uiwindow:
Dims:
X0: 694
Y0: 367
Width: 100
Height: 50
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "101.4"
Title: Oil T
uidata:
WID: 10
TelemetryField: Oil Temperature
11:
uiwindow:
Dims:
X0: 590
Y0: 367
Width: 100
Height: 50
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "101.5"
Title: Water T
uidata:
WID: 11
TelemetryField: Water Temperature
12:
uiwindow:
Dims:
X0: 642
Y0: 188
Width: 150
Height: 50
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "2.7"
Title: Fuel Cur Lap
uidata:
WID: 12
TelemetryField: Fuel Current Lap
13:
uiwindow:
Dims:
X0: 436
Y0: 71
Width: 206
Height: 57
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 4
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "9999"
Title: Dig Tacho
uidata:
WID: 13
TelemetryField: RPM
14:
uiwindow:
Dims:
X0: 710
Y0: 8
Width: 84
Height: 57
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 4
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: PIT
Title: Pit Sp
uidata:
WID: 14
TelemetryField: Pit Speed Limiter
15:
uiwindow:
Dims:
X0: 207
@@ -405,9 +357,9 @@ Windows:
PreviewValue: "61.4"
Title: BBias
uidata:
WID: 17
WID: 15
TelemetryField: BrakeBias
18:
16:
uiwindow:
Dims:
X0: 470
@@ -429,5 +381,101 @@ Windows:
PreviewValue: "123.4"
Title: Fuel Level
uidata:
WID: 18
WID: 16
TelemetryField: Fuel Level
17:
uiwindow:
Dims:
X0: 60
Y0: 91
Width: 85
Height: 57
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 4
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "123"
Title: Lap
uidata:
WID: 17
TelemetryField: Lap Number
18:
uiwindow:
Dims:
X0: 9
Y0: 413
Width: 57
Height: 57
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 4
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "12"
Title: ABS
uidata:
WID: 18
TelemetryField: ABS Control
19:
uiwindow:
Dims:
X0: 643
Y0: 132
Width: 149
Height: 50
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "10.54"
Title: Fuel Av.
uidata:
WID: 19
TelemetryField: Fuel Average Usage
20:
uiwindow:
Dims:
X0: 470
Y0: 242
Width: 322
Height: 50
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "41.2"
Title: Fuel Rem.
uidata:
WID: 20
TelemetryField: Fuel Expected Laps
+1 -1
View File
@@ -171,7 +171,7 @@ func (i *IRacing) Subscribe(requestFields map[int16]telem.FieldID) {
switch id {
case telem.RPMStateColour:
i.data.VirtualBinds = append(i.data.VirtualBinds, telem.NewRPMLights())
case telem.FuelCurrentLap:
case telem.FCCurrentLap:
i.data.VirtualBinds = append(i.data.VirtualBinds,
telem.NewFuelCalculator(i.logger.WithGroup("FUEL CALC")))
default:
+8 -4
View File
@@ -172,8 +172,10 @@ const (
// RPM Dash Lights
RPMStateColour
// Fuel Calculator
FuelCurrentLap
FuelLastLap
FCCurrentLap
FCLastLap
FCAverage
FCExpectedLaps
MaxFields
)
const FirstField = Speed
@@ -217,8 +219,10 @@ var FieldNames = [MaxFields]string{
// RPM Dash Lights
RPMStateColour: "RPM State Colour",
// Fuel Calculator
FuelCurrentLap: "Fuel Current Lap",
FuelLastLap: "Fuel Last Lap",
FCCurrentLap: "Fuel Current Lap",
FCLastLap: "Fuel Last Lap",
FCAverage: "Fuel Average Usage",
FCExpectedLaps: "Fuel Expected Laps",
Empty: "Emtpy",
}
+168 -79
View File
@@ -10,18 +10,47 @@ import (
// NOTE: for managing fuel consumption and predictions we need to filter out
// abnormal laps like going into the pits and refueling and whatnot
// NOTE: create a log file specifically for this, I reckon it would be better
type fuelCalcState int
const (
MaxLaps = 256
)
const (
// leaves pits or whenever this starts
outlap fuelCalcState = iota
OutlapState fuelCalcState = iota
// we cross the line for the first time, we can count real time usage here
// but don't have history yet
firstLap
FirstLapState
// We already have history and can therefore do averages
normal
NormalState
)
type lapFuelData struct {
Lap int // Number of the lap of this data
StartFuelLevel float64 // Fuel level at the start/finish line
FuelUsage float64 // Fuel used during the lap
Valid bool // If we should consider this data for calculations
}
func (lfd *lapFuelData) SetLap(lap int) {
lfd.Lap = lap
}
func (lfd *lapFuelData) SetStartFuelLevel(fuelLevel float64) {
lfd.StartFuelLevel = fuelLevel
}
func (lfd *lapFuelData) InvalidateLap() {
lfd.Valid = false
}
func (lfd *lapFuelData) CalculateFuelUsage(fuelLevel float64) {
lfd.FuelUsage = lfd.StartFuelLevel - fuelLevel
}
// FuelCalculator acts as a Virtual Field middleware
type FuelCalculator struct {
Logger *slog.Logger
@@ -47,121 +76,181 @@ type FuelCalculator struct {
maxUsage float64
state fuelCalcState
Laps [MaxLaps]lapFuelData
CurrentLap int
averageFuelUsage float64
expectedLaps float64
}
func NewFuelCalculator(logger *slog.Logger) *FuelCalculator {
return &FuelCalculator{
Logger: logger,
setup: &sync.Once{},
lapHistory: make([]float64, 0, 5), // Pre-allocate space for 5 laps
maxHistoryLaps: 3, // We want a 3-lap rolling average
lastLapNumber: 0,
state: outlap,
Logger: logger,
setup: &sync.Once{},
lapHistory: make([]float64, 0, 5), // Pre-allocate space for 5 laps
maxHistoryLaps: 3, // We want a 3-lap rolling average
lastLapNumber: 0,
state: OutlapState,
CurrentLap: 0,
averageFuelUsage: 0.0,
}
}
func (fc *FuelCalculator) transitionState(state fuelCalcState) {
fc.state = state
func (fc *FuelCalculator) initializeFCTelemetryFields(td *TelemetryData) {
td.Values[FCLastLap].Type = DataTypeSTRING
td.Values[FCLastLap].Str = "No Data"
td.Values[FCCurrentLap].Type = DataTypeSTRING
td.Values[FCCurrentLap].Str = "No Data"
td.Values[FCExpectedLaps].Type = DataTypeSTRING
td.Values[FCExpectedLaps].Str = "No Data"
td.Values[FCAverage].Type = DataTypeSTRING
td.Values[FCAverage].Str = "No Data"
}
func (fc *FuelCalculator) crossedStartFinishLine(lap int) bool {
return lap > fc.lastLapNumber
func (fc *FuelCalculator) newLapData(lap int, fuelLevel float64) {
fc.Laps[fc.CurrentLap].SetStartFuelLevel(fuelLevel)
fc.Laps[fc.CurrentLap].SetLap(lap)
fc.Laps[fc.CurrentLap].Valid = true
}
func (fc *FuelCalculator) refueled(currentFuelLevel float64) bool {
return currentFuelLevel > fc.fuelAtLapStart
func (fc *FuelCalculator) MaxLapsReached(td *TelemetryData) {
td.Values[FCLastLap].Str = "MAX!!!"
td.Values[FCCurrentLap].Str = "MAX!!!"
}
func (fc *FuelCalculator) storeStartFinishLineFuelLevels(curFuel float64, lap int) {
fc.fuelAtLapStart = curFuel
fc.lastLapNumber = lap
func (fc *FuelCalculator) updateLapFuelUsage(td *TelemetryData, fuel float64) {
fc.Logger.Debug(fmt.Sprintf("= %-2d =============================================", fc.CurrentLap))
fc.Logger.Debug(fmt.Sprintf("-> Updating Lap Fuel Usage"))
fc.Logger.Debug(fmt.Sprintf(" %+v", fc.Laps[fc.CurrentLap]))
fc.Logger.Debug(fmt.Sprintf(" %f - %f = %f",
fc.Laps[fc.CurrentLap].StartFuelLevel, fuel, fc.Laps[fc.CurrentLap].StartFuelLevel-fuel))
fc.Laps[fc.CurrentLap].CalculateFuelUsage(fuel)
td.Values[FCLastLap].Str = fmt.Sprintf("%.1f", fc.Laps[fc.CurrentLap].FuelUsage)
}
func (fc *FuelCalculator) calculateCurLapFuelUsage(td *TelemetryData, fuel float64) {
td.Values[FuelCurrentLap].Type = DataTypeSTRING
td.Values[FuelCurrentLap].Str = fmt.Sprintf("%.2f", fc.fuelAtLapStart-fuel)
func (fc *FuelCalculator) updateAverageFuelUsage(td *TelemetryData) {
// NOTE: this is a dumb approach on how to calculte fuel usage
// sum := 0.0
// lapCount := 0
// for k := 0; k < fc.CurrentLap; k++ {
// if !fc.Laps[k].Valid {
// continue
// }
//
// sum += fc.Laps[k].FuelUsage
// lapCount += 1
// }
//
// if lapCount == 0 {
// return
// }
//
// fc.averageFuelUsage = sum / float64(lapCount)
fc.averageFuelUsage = fc.Laps[fc.CurrentLap].FuelUsage
td.Values[FCAverage].Str = fmt.Sprintf("%.2f", fc.averageFuelUsage)
}
func (fc *FuelCalculator) processOutlap(td *TelemetryData, lap int) {
currentFuelLevel, err := strconv.ParseFloat(td.Values[FuelLevel].Str, 64)
if err != nil {
fc.Logger.Debug(fmt.Sprintf("error parsing fuel level: %+v", err))
func (fc *FuelCalculator) updateExpectedLaps(td *TelemetryData, fuel float64) {
fc.expectedLaps = fuel / fc.averageFuelUsage
td.Values[FCExpectedLaps].Str = fmt.Sprintf("%.1f", fc.expectedLaps)
}
func (fc *FuelCalculator) startFinishLineCrossed(td *TelemetryData, lap int, fuel float64) {
if fc.state == OutlapState {
fc.state = FirstLapState
}
if fc.state == FirstLapState {
fc.state = NormalState
}
// if fc.state == NormalState {
// // In the normal state we have normal calculations to compute
// fc.updateLapFuelUsage(td, fuel)
// }
if fc.CurrentLap == MaxLaps {
fc.MaxLapsReached(td)
return
}
if fc.crossedStartFinishLine(lap) {
fc.storeStartFinishLineFuelLevels(currentFuelLevel, lap)
fc.transitionState(firstLap)
}
// 1. Update last lap (we are still on the curret index) lap's fuel usage
fc.updateLapFuelUsage(td, fuel)
// 2. Update the average usage
fc.updateAverageFuelUsage(td)
// 3. Update the expected laps
fc.updateExpectedLaps(td, fuel)
if fc.refueled(currentFuelLevel) {
fc.transitionState(outlap)
}
// Go into the next lap
fc.CurrentLap += 1
fc.newLapData(lap, fuel)
}
func (fc *FuelCalculator) processFirstlap(td *TelemetryData, lap int) {
currentFuelLevel, err := strconv.ParseFloat(td.Values[FuelLevel].Str, 64)
if err != nil {
fc.Logger.Debug(fmt.Sprintf("error parsing fuel level: %+v", err))
return
}
func (fc *FuelCalculator) updateCurrentLapFuelUsage(td *TelemetryData, fuel float64) {
// NOTE: find a more performant way of float64 -> string
fuelDelta := fc.Laps[fc.CurrentLap].StartFuelLevel - fuel
if fc.crossedStartFinishLine(lap) {
fc.storeStartFinishLineFuelLevels(currentFuelLevel, lap)
fc.transitionState(normal)
}
if fc.refueled(currentFuelLevel) {
fc.transitionState(outlap)
}
fc.calculateCurLapFuelUsage(td, currentFuelLevel)
td.Values[FCCurrentLap].Str = fmt.Sprintf("%.1f", fuelDelta)
}
func (fc *FuelCalculator) processNormal(td *TelemetryData, lap int) {
currentFuelLevel, err := strconv.ParseFloat(td.Values[FuelLevel].Str, 64)
if err != nil {
fc.Logger.Debug(fmt.Sprintf("error parsing fuel level: %+v", err))
return
}
func (fc *FuelCalculator) updateRealTimeVariables(td *TelemetryData, lap int, fuel float64) {
// if fc.state == OutlapState {
// Nothing to calculate here currently
// return
// }
if fc.crossedStartFinishLine(lap) {
fc.storeStartFinishLineFuelLevels(currentFuelLevel, lap)
fc.transitionState(normal)
}
// Real time updates
fc.updateCurrentLapFuelUsage(td, fuel)
}
if fc.refueled(currentFuelLevel) {
fc.transitionState(outlap)
}
fc.calculateCurLapFuelUsage(td, currentFuelLevel)
// And here we add how to calculate last lap usage or whatever yo
// NOTE: I need to add better logic for this. We need to detect pit stops
// instead of just checking if the fuel is greater than before
func (fc *FuelCalculator) refueled(fuel float64) bool {
return fuel > fc.Laps[fc.CurrentLap].StartFuelLevel
}
func (fc *FuelCalculator) Process(td *TelemetryData) {
// This setup function will set the default values for the fields we work
// with on this fuel calculator
fc.setup.Do(func() {
td.Values[FuelLastLap].Type = DataTypeSTRING
td.Values[FuelLastLap].Str = "No Data"
td.Values[FuelCurrentLap].Type = DataTypeSTRING
td.Values[FuelCurrentLap].Str = "No Data"
fc.initializeFCTelemetryFields(td)
})
currentLap := int(td.Values[LapNumber].Raw)
scannedLap := int(td.Values[LapNumber].Raw)
// NOTE: this is stupid as, fix it. I mean don't store fuel level as a string
// currentFuelLevel, err := strconv.ParseFloat(td.Values[FuelLevel].Str, 64)
scannedFuelLevel, err := strconv.ParseFloat(td.Values[FuelLevel].Str, 64)
if err != nil {
fc.Logger.Debug(fmt.Sprintf("error parsing fuel level: %+v", err))
switch fc.state {
case outlap:
fc.processOutlap(td, currentLap)
case firstLap:
fc.processFirstlap(td, currentLap)
case normal:
fc.processNormal(td, currentLap)
td.Values[FCLastLap].Str = "ERROR"
td.Values[FCCurrentLap].Str = "ERROR"
return
}
// Start the 0th lap
if fc.CurrentLap == 0 && fc.Laps[0].StartFuelLevel == 0 {
fc.newLapData(scannedLap, scannedFuelLevel)
}
if fc.refueled(scannedFuelLevel) {
fc.Laps[fc.CurrentLap].StartFuelLevel = scannedFuelLevel
fc.Laps[fc.CurrentLap].InvalidateLap()
}
if scannedLap > fc.CurrentLap {
// Crossed the start finish line to the next lap
fc.startFinishLineCrossed(td, scannedLap, scannedFuelLevel)
}
fc.updateRealTimeVariables(td, scannedLap, scannedFuelLevel)
}
// Simple helper to wipe state on session changes