Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions __tests__/units.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {
convertDistanceFromYards,
convertSpeedFromMph,
formatCarryRange,
formatDistance,
formatSpeed,
getDistanceUnit,
getSpeedUnit,
getUnitsLabel,
} from '../utils/units';

describe('units helpers', () => {
it('preserves imperial speed values', () => {
expect(convertSpeedFromMph(150, 'imperial')).toBe(150);
expect(formatSpeed(150, 'imperial', 1)).toBe('150.0');
expect(getSpeedUnit('imperial')).toBe('mph');
});

it('converts mph to km/h with stable rounding', () => {
expect(convertSpeedFromMph(100, 'metric')).toBeCloseTo(160.934, 3);
expect(formatSpeed(100, 'metric', 1)).toBe('160.9');
expect(getSpeedUnit('metric')).toBe('km/h');
});

it('preserves imperial distance values', () => {
expect(convertDistanceFromYards(250, 'imperial')).toBe(250);
expect(formatDistance(250, 'imperial', 0)).toBe('250');
expect(getDistanceUnit('imperial')).toBe('yds');
});

it('converts yards to meters with stable rounding', () => {
expect(convertDistanceFromYards(100, 'metric')).toBeCloseTo(91.44, 2);
expect(formatDistance(100, 'metric', 0)).toBe('91');
expect(getDistanceUnit('metric')).toBe('m');
});

it('joins speed and distance units for chrome labels', () => {
expect(getUnitsLabel('imperial')).toBe('mph / yds');
expect(getUnitsLabel('metric')).toBe('km/h / m');
});

it('formats carry ranges in the selected unit system', () => {
expect(formatCarryRange([200, 220], 'imperial')).toBe('200-220 yds');
expect(formatCarryRange([200, 220], 'metric')).toBe('183-201 m');
});
});
56 changes: 56 additions & 0 deletions utils/units.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Imperial/metric conversion mirrored from the OpenFlight kiosk web UI
// (`ui/src/utils/units.ts` in open-flight/openflight). The two copies are
// hand-mirrored, not shared: this repository has no build-time dependency on
// the server repository (see AGENTS.md). Keep the constants and rounding here
// identical to the kiosk so both clients report the same numbers.

export type UnitSystem = 'imperial' | 'metric';

const MPH_TO_KMH = 1.60934;
const YARDS_TO_METERS = 0.9144;
const IMPERIAL_SPEED_UNIT = 'mph';
const METRIC_SPEED_UNIT = 'km/h';
const IMPERIAL_DISTANCE_UNIT = 'yds';
const METRIC_DISTANCE_UNIT = 'm';

export function convertSpeedFromMph(speedMph: number, unitSystem: UnitSystem): number {
if (unitSystem === 'metric') {
return speedMph * MPH_TO_KMH;
}

return speedMph;
}

export function convertDistanceFromYards(distanceYards: number, unitSystem: UnitSystem): number {
if (unitSystem === 'metric') {
return distanceYards * YARDS_TO_METERS;
}

return distanceYards;
}

export function formatSpeed(speedMph: number, unitSystem: UnitSystem, digits = 1): string {
return convertSpeedFromMph(speedMph, unitSystem).toFixed(digits);
}

export function formatDistance(distanceYards: number, unitSystem: UnitSystem, digits = 0): string {
return convertDistanceFromYards(distanceYards, unitSystem).toFixed(digits);
}

export function getSpeedUnit(unitSystem: UnitSystem): string {
return unitSystem === 'metric' ? METRIC_SPEED_UNIT : IMPERIAL_SPEED_UNIT;
}

export function getDistanceUnit(unitSystem: UnitSystem): string {
return unitSystem === 'metric' ? METRIC_DISTANCE_UNIT : IMPERIAL_DISTANCE_UNIT;
}

export function getUnitsLabel(unitSystem: UnitSystem): string {
return `${getSpeedUnit(unitSystem)} / ${getDistanceUnit(unitSystem)}`;
}

export function formatCarryRange(carryRange: [number, number], unitSystem: UnitSystem): string {
const min = formatDistance(carryRange[0], unitSystem, 0);
const max = formatDistance(carryRange[1], unitSystem, 0);
return `${min}-${max} ${getDistanceUnit(unitSystem)}`;
}
Loading