Skip to content

Commit 0097bf2

Browse files
CtrlAltDaletariknz
andauthored
refactor: give speed conversion a single home (#657)
Co-authored-by: Tarik Alani <tarik.nzl@gmail.com>
1 parent b8ab220 commit 0097bf2

12 files changed

Lines changed: 224 additions & 31 deletions

File tree

src/frontend/components/Battle/Battle.tsx

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { memo, useMemo } from 'react';
2+
import { resolveSpeedUnit, speedFromKph } from '@irdashies/utils/units';
3+
import type { SpeedUnit } from '@irdashies/utils/units';
24
import {
35
useDrivingState,
46
useSessionVisibility,
@@ -87,7 +89,7 @@ interface BattleRowProps {
8789
stintLaps?: number;
8890
lastTimeFormat?: import('@irdashies/types').TimeFormat;
8991
speedKph?: number;
90-
isMetricSpeed?: boolean;
92+
speedUnit?: SpeedUnit;
9193
rowIndex: number;
9294
}
9395

@@ -107,7 +109,7 @@ const BattleRow = memo(
107109
stintLaps,
108110
lastTimeFormat = 'mixed',
109111
speedKph,
110-
isMetricSpeed = true,
112+
speedUnit = 'km/h',
111113
rowIndex,
112114
}: BattleRowProps) => {
113115
const onTrack = entry?.onTrack ?? true;
@@ -216,7 +218,7 @@ const BattleRow = memo(
216218
} else if (key === 'speed' && settings?.speed?.enabled) {
217219
const displaySpeed =
218220
speedKph != null && speedKph > 0
219-
? Math.round(isMetricSpeed ? speedKph : speedKph / 1.60934)
221+
? Math.round(speedFromKph(speedKph, speedUnit))
220222
: null;
221223
cols.push(
222224
<td
@@ -385,10 +387,11 @@ export const Battle = () => {
385387

386388
// Speed: derived from CarIdxLapDistPct movement, in km/h.
387389
const carSpeeds = useCarIdxSpeed();
388-
const displayUnits = useTelemetryValue('DisplayUnits'); // 0 = imperial, 1 = metric
389-
const speedUnit = settings?.speed?.unit ?? 'auto';
390-
const isMetricSpeed =
391-
speedUnit === 'auto' ? displayUnits === 1 : speedUnit === 'km/h';
390+
const displayUnits = useTelemetryValue('DisplayUnits');
391+
const resolvedSpeedUnit = resolveSpeedUnit(
392+
settings?.speed?.unit,
393+
displayUnits
394+
);
392395

393396
const stintLaps = (carIdx: number) => {
394397
const currentLap = carLaps?.[carIdx] ?? 0;
@@ -449,7 +452,7 @@ export const Battle = () => {
449452
speedKph={
450453
aheadEntry != null ? carSpeeds[aheadEntry.carIdx] : undefined
451454
}
452-
isMetricSpeed={isMetricSpeed}
455+
speedUnit={resolvedSpeedUnit}
453456
rowIndex={0}
454457
/>
455458
<BattleRow
@@ -468,7 +471,7 @@ export const Battle = () => {
468471
speedKph={
469472
playerEntry != null ? carSpeeds[playerEntry.carIdx] : undefined
470473
}
471-
isMetricSpeed={isMetricSpeed}
474+
speedUnit={resolvedSpeedUnit}
472475
rowIndex={1}
473476
/>
474477
<BattleRow
@@ -496,7 +499,7 @@ export const Battle = () => {
496499
speedKph={
497500
behindEntry != null ? carSpeeds[behindEntry.carIdx] : undefined
498501
}
499-
isMetricSpeed={isMetricSpeed}
502+
speedUnit={resolvedSpeedUnit}
500503
rowIndex={2}
501504
/>
502505
</tbody>

src/frontend/components/Input/InputGear/InputGear.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { memo } from 'react';
2+
import { resolveSpeedUnit, speedFromMs } from '@irdashies/utils/units';
23

34
export interface InputGearProps {
45
gear?: number;
@@ -16,10 +17,14 @@ export interface InputGearProps {
1617

1718
export const InputGear = memo(
1819
({ gear, speedMs, unit, settings }: InputGearProps) => {
19-
const isMetric =
20-
(unit === 1 && settings.unit === 'auto') || settings.unit === 'km/h';
21-
const speed = (speedMs ?? 0) * (isMetric ? 3.6 : 2.23694);
22-
const displayUnit = isMetric ? 'km/h' : 'mph';
20+
// 'none' is vestigial — the settings UI only offers auto/mph/km/h — but the
21+
// type still permits it, and it previously fell through to mph. Kept that
22+
// way rather than quietly changing behaviour for a hand-edited config.
23+
const displayUnit = resolveSpeedUnit(
24+
settings.unit === 'none' ? 'mph' : settings.unit,
25+
unit
26+
);
27+
const speed = speedFromMs(speedMs ?? 0, displayUnit);
2328
let gearText;
2429
switch (gear) {
2530
case -1:

src/frontend/components/PitlaneHelper/hooks/usePitSpeed.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { useMemo } from 'react';
22
import { useTelemetryValue, useSessionStore } from '@irdashies/context';
3+
import {
4+
kphFromSpeed,
5+
speedFromKph,
6+
speedFromMs,
7+
} from '@irdashies/utils/units';
38

49
export interface PitSpeedResult {
510
deltaKph: number;
@@ -24,13 +29,15 @@ export const usePitSpeed = (): PitSpeedResult => {
2429
const limitValue = parseFloat(limitString.split(' ')[0]);
2530
const limitUnit = limitString.split(' ')[1]?.toLowerCase();
2631

27-
// Determine limit in both units
28-
const limitKph = limitUnit === 'mph' ? limitValue * 1.60934 : limitValue;
29-
const limitMph = limitUnit === 'kph' ? limitValue / 1.60934 : limitValue;
32+
// Determine limit in both units. iRacing writes the limit in whichever unit
33+
// the track uses, so normalise via km/h rather than trusting one of them.
34+
const limitKph =
35+
limitUnit === 'mph' ? kphFromSpeed(limitValue, 'mph') : limitValue;
36+
const limitMph = speedFromKph(limitKph, 'mph');
3037

3138
// Current speed (convert m/s to km/h and mph)
32-
const speedKph = speed * 3.6;
33-
const speedMph = speed * 2.23694;
39+
const speedKph = speedFromMs(speed, 'km/h');
40+
const speedMph = speedFromMs(speed, 'mph');
3441

3542
// Calculate deltas
3643
const deltaKph = speedKph - limitKph;

src/frontend/components/RejoinIndicator/RejoinIndicator.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { useDriverRelatives } from '../Standings/hooks/useDriverRelatives';
1616
import { useRejoinSettings } from './hooks/useRejoinSettings';
1717
import { getDemoRejoinData } from './demoData';
1818
import type { Standings } from '../Standings/createStandings';
19+
import { speedFromMs } from '@irdashies/utils/units';
1920

2021
export const RejoinIndicator = () => {
2122
const { isDemoMode } = useDashboard();
@@ -78,7 +79,7 @@ export const RejoinIndicator = () => {
7879
}
7980

8081
// Read telemetry and computed car speed for the focused car index
81-
const speedKmH = (carSpeedForPlayer ?? 0) * 3.6;
82+
const speedKmH = speedFromMs(carSpeedForPlayer ?? 0, 'km/h');
8283

8384
const gap = Math.abs(carBehind?.delta ?? Number.POSITIVE_INFINITY);
8485
const gapLabel = Number.isFinite(gap) ? gap.toFixed(1) : '--';

src/frontend/components/Standings/components/SessionBar/components/TopSpeedItem/TopSpeedItem.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
useSessionBestTopSpeed,
66
useTelemetryValue,
77
} from '@irdashies/context';
8+
import { resolveSpeedUnit, speedFromMs } from '@irdashies/utils/units';
89
import { sessionBarItemWrapperClass } from '../../sessionBarItemWrapperClass';
910
import type { SessionBarItemProps } from '../../sessionBarItemTypes';
1011

@@ -13,16 +14,14 @@ export const TopSpeedItem = memo(({ standalone }: SessionBarItemProps) => {
1314
const lastLapTopSpeedMs = useLastLapTopSpeed();
1415
const sessionBestTopSpeedMs = useSessionBestTopSpeed();
1516

16-
const isMetric = displayUnits === 1;
17-
const factor = isMetric ? 3.6 : 2.23694;
18-
const unit = isMetric ? 'km/h' : 'mph';
17+
const unit = resolveSpeedUnit('auto', displayUnits);
1918
const last =
2019
lastLapTopSpeedMs !== null
21-
? `${(lastLapTopSpeedMs * factor).toFixed(0)} ${unit}`
20+
? `${speedFromMs(lastLapTopSpeedMs, unit).toFixed(0)} ${unit}`
2221
: '—';
2322
const best =
2423
sessionBestTopSpeedMs !== null
25-
? (sessionBestTopSpeedMs * factor).toFixed(0)
24+
? speedFromMs(sessionBestTopSpeedMs, unit).toFixed(0)
2625
: null;
2726

2827
return (

src/frontend/components/Standings/components/SessionBar/components/WindItem/WindItem.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { memo } from 'react';
22
import { useTelemetryValue, useThrottledWeather } from '@irdashies/context';
3+
import { resolveSpeedUnit, speedFromMs } from '@irdashies/utils/units';
34
import { WindArrow } from '../../../../../shared/WindArrow';
45
import { sessionBarItemWrapperClass } from '../../sessionBarItemWrapperClass';
56
import type { SessionBarItemProps } from '../../sessionBarItemTypes';
@@ -10,11 +11,11 @@ export const WindItem = memo(
1011
const { windDirection, windVelocity, windYaw } = useThrottledWeather();
1112
const relativeWindDirection = (windDirection ?? 0) - (windYaw ?? 0);
1213

13-
const isMetric = displayUnits === 1;
14+
const speedUnit = resolveSpeedUnit('auto', displayUnits);
1415
const speedPosition = settings?.wind?.speedPosition ?? 'right';
1516
const speed =
1617
windVelocity !== undefined
17-
? Math.round(windVelocity * (isMetric ? 3.6 : 2.23694))
18+
? Math.round(speedFromMs(windVelocity, speedUnit))
1819
: '-';
1920
const speedEl = <span>{speed}</span>;
2021
const arrowEl = (

src/frontend/components/Weather/WindDirection/WindDirection.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { WindIcon } from '@phosphor-icons/react';
22
import { memo, useRef, useEffect, useState } from 'react';
33
import { getWindIntensityClass } from '../../../domain/weather/wind';
4+
import { speedFromMs } from '@irdashies/utils/units';
45

56
export interface WindDirectionProps {
67
speedMs?: number;
@@ -46,7 +47,7 @@ export const WindDirection = memo(
4647
// Convert m/s to user's preferred unit
4748
const speed =
4849
speedMs !== undefined
49-
? speedMs * (metric ? 3.6 : 2.23694) // km/h or mph
50+
? speedFromMs(speedMs, metric ? 'km/h' : 'mph')
5051
: undefined;
5152

5253
const [normalizedAngle, setNormalizedAngle] = useState<number>(0);

src/frontend/components/Wind/WindDirection/WindDirection.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { memo, useEffect, useLayoutEffect, useRef, useState } from 'react';
22
import { getWindIntensityClass } from '../../../domain/weather/wind';
3+
import { speedFromMs } from '@irdashies/utils/units';
34

45
export interface WindDirectionProps {
56
speedMs?: number;
@@ -10,7 +11,9 @@ export interface WindDirectionProps {
1011
export const WindDirection = memo(
1112
({ speedMs, direction, metric = true }: WindDirectionProps) => {
1213
const speed =
13-
speedMs !== undefined ? speedMs * (metric ? 3.6 : 2.23694) : undefined;
14+
speedMs !== undefined
15+
? speedFromMs(speedMs, metric ? 'km/h' : 'mph')
16+
: undefined;
1417
const [normalizedAngle, setNormalizedAngle] = useState(0);
1518
const prevAngleRef = useRef(0);
1619
const containerRef = useRef<HTMLDivElement | null>(null);

src/frontend/context/CarSpeedStore/CarSpeedsStore.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Telemetry } from '@irdashies/types';
22
import { create, useStore } from 'zustand';
3+
import { speedFromMs } from '@irdashies/utils/units';
34

45
export interface CarSpeedBuffer {
56
lastLapDistPct: number[];
@@ -65,7 +66,9 @@ export const useCarSpeedsStore = create<CarSpeedsState>((set, get) => ({
6566
if (wentBackwards && startedNewLap) distancePercent += 1.0;
6667

6768
const distance = trackLength * distancePercent; // meters
68-
const speed = deltaTime > 0 ? (distance / deltaTime) * 3.6 : 0; // m/s to km/h
69+
// Stored in km/h throughout; converted here once from m/s.
70+
const speed =
71+
deltaTime > 0 ? speedFromMs(distance / deltaTime, 'km/h') : 0;
6972
if (!newHistory[idx]) newHistory[idx] = [];
7073
newHistory[idx].push(speed);
7174
if (newHistory[idx].length > SPEED_AVG_WINDOW) newHistory[idx].shift();

src/frontend/domain/weather/wind.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { msFromSpeed } from '@irdashies/utils/units';
2+
13
export interface WindDemoData {
24
speedMs: number;
35
direction: number;
@@ -18,7 +20,7 @@ export const getDemoWindData = (
1820
metric: boolean
1921
): WindDemoData => {
2022
const demoValue = WIND_DEMO_VALUES[index % WIND_DEMO_VALUES.length];
21-
const speedMs = demoValue.speed / (metric ? 3.6 : 2.23694);
23+
const speedMs = msFromSpeed(demoValue.speed, metric ? 'km/h' : 'mph');
2224

2325
return {
2426
speedMs,

0 commit comments

Comments
 (0)