implementing a bare bones fuel calculator

This commit is contained in:
2026-05-31 10:44:16 +01:00
parent 2cb30baf6a
commit 32dc35d008
6 changed files with 553 additions and 359 deletions
+409 -337
View File
@@ -1,341 +1,5 @@
Windows:
1:
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: 1
TelemetryField: ABS Control
2:
uiwindow:
Dims:
X0: 140
Y0: 412
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: "6"
Title: Thr
uidata:
WID: 2
TelemetryField: Throttle Control
3:
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: 3
TelemetryField: Last Lap Time
4:
uiwindow:
Dims:
X0: 74
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: TC
uidata:
WID: 4
TelemetryField: TC Control
5:
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: 5
TelemetryField: Water Temperature
6:
uiwindow:
Dims:
X0: 354
Y0: 71
Width: 74
Height: 111
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 12
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: R
Title: Box
uidata:
WID: 6
TelemetryField: Gear
7:
uiwindow:
Dims:
X0: 694
Y0: 423
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: "4.56"
Title: Oil P
uidata:
WID: 7
TelemetryField: Oil Pressure
8:
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: 8
TelemetryField: Oil Temperature
9:
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: 9
TelemetryField: RPM
10:
uiwindow:
Dims:
X0: 207
Y0: 412
Width: 111
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: "61.4"
Title: BBias
uidata:
WID: 10
TelemetryField: BrakeBias
11:
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: 11
TelemetryField: Pit Speed Limiter
12:
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: 12
TelemetryField: Lap Number
13:
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: 13
TelemetryField: RPM
14:
uiwindow:
Dims:
X0: 331
Y0: 190
Width: 116
Height: 70
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 6
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "234"
Title: SPEEDO
uidata:
WID: 14
TelemetryField: Speed
15:
uiwindow:
Dims:
X0: 359
@@ -357,5 +21,413 @@ Windows:
PreviewValue: YELLOW
Title: RPMColour
uidata:
WID: 15
WID: 1
TelemetryField: RPM State Colour
2:
uiwindow:
Dims:
X0: 74
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: TC
uidata:
WID: 2
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:
uiwindow:
Dims:
X0: 354
Y0: 71
Width: 74
Height: 111
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 12
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: R
Title: Box
uidata:
WID: 10
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:
uiwindow:
Dims:
X0: 140
Y0: 412
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: "6"
Title: Thr
uidata:
WID: 12
TelemetryField: Throttle Control
13:
uiwindow:
Dims:
X0: 694
Y0: 423
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: "4.56"
Title: Oil P
uidata:
WID: 13
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:
uiwindow:
Dims:
X0: 331
Y0: 190
Width: 116
Height: 70
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 6
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "234"
Title: SPEEDO
uidata:
WID: 16
TelemetryField: Speed
17:
uiwindow:
Dims:
X0: 207
Y0: 412
Width: 111
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: "61.4"
Title: BBias
uidata:
WID: 17
TelemetryField: BrakeBias
18:
uiwindow:
Dims:
X0: 470
Y0: 132
Width: 169
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: "123.4"
Title: Fuel Level
uidata:
WID: 18
TelemetryField: Fuel Level
+4 -3
View File
@@ -7,9 +7,10 @@ import (
)
var internalToSDKFieldNames = map[telemetry.FieldID]string{
telemetry.Speed: "Speed",
telemetry.Gear: "Gear",
telemetry.RPM: "RPM",
telemetry.Speed: "Speed",
telemetry.Gear: "Gear",
telemetry.RPM: "RPM",
telemetry.FuelLevel: "FuelLevel",
// Engine Data
telemetry.OilPress: "OilPress",
telemetry.OilTemp: "OilTemp",
+21 -16
View File
@@ -161,11 +161,29 @@ func (i *IRacing) Subscribe(requestFields map[int16]telem.FieldID) {
i.data.ActiveBinds = make([]telem.BoundField, 0, len(requestFields))
boundCheck := make(map[telem.FieldID]bool)
// First we must add the virtual fields
// we will add their dependencies and the primitives to a slice
pendingBinds := make([]telem.FieldID, telem.MaxFields)
for winID, id := range requestFields {
i.data.Values[id].IDs = append(i.data.Values[id].IDs, winID)
switch id {
case telem.RPMStateColour:
i.data.VirtualBinds = append(i.data.VirtualBinds, telem.NewRPMLights())
case telem.FuelCurrentLap:
i.data.VirtualBinds = append(i.data.VirtualBinds,
telem.NewFuelCalculator(i.logger.WithGroup("FUEL CALC")))
default:
// primitive telemetry field
pendingBinds = append(pendingBinds, id)
}
}
boundCheck := make(map[telem.FieldID]bool)
// Now that we know all the fields we need to bind we follow the binding procedure
for _, id := range pendingBinds {
// Check if we already bound this FieldID
if boundCheck[id] {
continue
@@ -220,6 +238,8 @@ func (i *IRacing) Subscribe(requestFields map[int16]telem.FieldID) {
out.Type = telem.DataTypeUINT16
out.Raw = uint64(uint16(v.(float32)))
}
case telem.FuelLevel:
binding.Transform = FloatToStringTransform
// Engine Data
case telem.OilPress:
binding.Transform = FloatToStringTransform
@@ -266,20 +286,5 @@ func (i *IRacing) Subscribe(requestFields map[int16]telem.FieldID) {
boundCheck[id] = true
}
// Subscribe to whatever we need of virtual binds I guess - we need to ensure
// the virtual binds fields are being read
needsRPMColour := false
for _, id := range requestFields {
if id == telem.RPMStateColour {
needsRPMColour = true
}
}
i.logger.Debug(fmt.Sprintf("Subscribing to virtual bind? %+v", needsRPMColour))
if needsRPMColour {
i.logger.Debug("Subscribing to virtual bind")
i.data.VirtualBinds = append(i.data.VirtualBinds, telem.NewRPMLights())
}
i.logger.Debug(fmt.Sprintf("Subscribed: %+v\n", i.data.ActiveBinds))
}
+14 -3
View File
@@ -21,6 +21,7 @@ type FieldMapper struct {
// So fuel per lap predictions, compound gauge lights and so on
type VirtualField interface {
Process(td *TelemetryData)
EnsureSubscribed() []FieldID
}
// NOTE: Update the iracing SDK to write data to the same map ALWAYS, then
@@ -135,6 +136,7 @@ const (
Speed FieldID = iota
RPM
Gear
FuelLevel
// Engine Data
OilPress
OilTemp
@@ -167,15 +169,20 @@ const (
ReplaySessionTime
Empty
// Virtual Fields -- fields derived from primitive fields
// RPM Dash Lights
RPMStateColour
// Fuel Calculator
FuelCurrentLap
FuelLastLap
MaxFields
)
const FirstField = Speed
var FieldNames = [MaxFields]string{
Speed: "Speed",
RPM: "RPM",
Gear: "Gear",
Speed: "Speed",
RPM: "RPM",
Gear: "Gear",
FuelLevel: "Fuel Level",
// Engine Data
OilPress: "Oil Pressure",
OilTemp: "Oil Temperature",
@@ -207,7 +214,11 @@ var FieldNames = [MaxFields]string{
SessionTime: "SessionTime",
ReplaySessionTime: "ReplaySessionTime",
// Virtual Fields
// RPM Dash Lights
RPMStateColour: "RPM State Colour",
// Fuel Calculator
FuelCurrentLap: "Fuel Current Lap",
FuelLastLap: "Fuel Last Lap",
Empty: "Emtpy",
}
+101
View File
@@ -0,0 +1,101 @@
package telemetry
import (
"fmt"
"log/slog"
"strconv"
)
type fuelCalcState int
const (
outlap fuelCalcState = iota
firstLap
normal
)
// FuelCalculator acts as a Virtual Field middleware
type FuelCalculator struct {
Logger *slog.Logger
// --- Internal State (The Memory) ---
lastLapNumber int // Tracks when we cross the start/finish line
fuelAtLapStart float64 // Snapshot of fuel when the lap began
lapHistory []float64 // Buffer holding the last N valid laps
maxHistoryLaps int // How many laps to keep in the buffer (e.g., 3)
// --- Validation Flags ---
isOutLap bool // True if we just left the pits
isUnderCaution bool // True if safety car is out (optional, based on flags)
// --- The Outputs (What gets pushed back to the TUI) ---
lastLapUsage float64
avgUsage float64
maxUsage float64
state fuelCalcState
}
func NewFuelCalculator(logger *slog.Logger) *FuelCalculator {
return &FuelCalculator{
Logger: logger,
lapHistory: make([]float64, 0, 5), // Pre-allocate space for 5 laps
maxHistoryLaps: 3, // We want a 3-lap rolling average
lastLapNumber: 0,
state: outlap,
}
}
// func (fc *FuelCalculator) isOutlap(lap int) {
// if fc.isOutLap
// }
func (fc *FuelCalculator) Process(td *TelemetryData) {
currentLap := int(td.Values[LapNumber].Raw)
// NOTE: this is stupid as, fix it
currentFuelLevel, err := strconv.ParseFloat(td.Values[FuelLevel].Str, 64)
if err != nil {
fc.Logger.Debug(fmt.Sprintf("error parsing fuel level: %+v", err))
return
}
if currentLap == 0 {
td.Values[FuelLastLap].Type = DataTypeSTRING
td.Values[FuelLastLap].Str = "No Data"
td.Values[FuelCurrentLap].Type = DataTypeSTRING
td.Values[FuelCurrentLap].Str = "No Data"
return
}
if currentLap > fc.lastLapNumber {
fc.Logger.Debug(fmt.Sprintf("crossed the line: %+v -> %+v", currentLap, fc.lastLapNumber))
fc.state = firstLap
fc.lastLapNumber = currentLap
fc.fuelAtLapStart = currentFuelLevel
}
td.Values[FuelLastLap].Type = DataTypeSTRING
td.Values[FuelLastLap].Str = "No Data"
td.Values[FuelCurrentLap].Type = DataTypeSTRING
fc.Logger.Debug(fmt.Sprintf("calculate: %+v - %+v = %+v", fc.fuelAtLapStart,
currentFuelLevel, fc.fuelAtLapStart-currentFuelLevel))
td.Values[FuelCurrentLap].Str = fmt.Sprintf("%.2f", fc.fuelAtLapStart-currentFuelLevel)
}
// Simple helper to wipe state on session changes
func (fc *FuelCalculator) resetHistory() {
fc.lapHistory = make([]float64, 0, fc.maxHistoryLaps)
fc.lastLapNumber = 0
fc.avgUsage = 0
fc.maxUsage = 0
}
func (fc *FuelCalculator) EnsureSubscribed() []FieldID {
return []FieldID{FuelLevel, LapNumber}
}
+4
View File
@@ -24,3 +24,7 @@ func (rl *RPMLights) Process(td *TelemetryData) {
td.Values[RPMStateColour].Str = "WHITE"
}
}
func (rl *RPMLights) EnsureSubscribed() []FieldID {
return []FieldID{RPM}
}