77 lines
2.5 KiB
TypeScript
77 lines
2.5 KiB
TypeScript
/**
|
|
* Parse a value string with SPICE-style SI suffixes.
|
|
*
|
|
* "4.7k" → 4700
|
|
* "220" → 220
|
|
* "1Meg" → 1_000_000
|
|
* "10u" → 1e-5
|
|
* "100n" → 1e-7
|
|
* "0.1u" → 1e-7
|
|
* "22p" → 2.2e-11
|
|
* "10mH" → 0.01
|
|
* "1.5H" → 1.5
|
|
*
|
|
* Recognized suffixes (case-insensitive for most; `Meg` is special):
|
|
* G = 1e9 T = 1e12 Meg = 1e6 K = 1e3
|
|
* m = 1e-3 u = 1e-6 n = 1e-9 p = 1e-12 f = 1e-15
|
|
*
|
|
* Trailing unit letters (Ω, F, H, V, A) are tolerated and stripped.
|
|
* Returns `fallback` if parsing fails.
|
|
*/
|
|
const SUFFIX_MAP: Array<[string, number]> = [
|
|
['meg', 1e6],
|
|
['g', 1e9],
|
|
['t', 1e12],
|
|
['k', 1e3],
|
|
['m', 1e-3],
|
|
['u', 1e-6],
|
|
['µ', 1e-6],
|
|
['n', 1e-9],
|
|
['p', 1e-12],
|
|
['f', 1e-15],
|
|
];
|
|
|
|
export function parseValueWithUnits(input: unknown, fallback = NaN): number {
|
|
if (typeof input === 'number' && Number.isFinite(input)) return input;
|
|
if (typeof input !== 'string') return fallback;
|
|
|
|
let s = input.trim();
|
|
if (!s) return fallback;
|
|
|
|
// Strip trailing physical unit words (non-SI-prefix ones)
|
|
s = s.replace(/(ohms?|Ω|farads?|henrys?|henries|amps?|volts?|ω|watts?)\s*$/i, '');
|
|
// Strip trailing single-letter unit (Ω, H, V, A, W). NOT F (which is femto in SPICE).
|
|
// Only strip when preceded by a digit or SI-prefix letter, not when the whole
|
|
// input is just the unit (so we don't turn "k" into "").
|
|
s = s.replace(/([0-9a-zµ])[ΩHVAW]$/i, '$1');
|
|
|
|
// Try suffix match (longest first — "meg" before "m")
|
|
const lower = s.toLowerCase();
|
|
for (const [suffix, mult] of SUFFIX_MAP) {
|
|
if (lower.endsWith(suffix)) {
|
|
const numPart = s.slice(0, s.length - suffix.length);
|
|
const n = parseFloat(numPart);
|
|
if (Number.isFinite(n)) return n * mult;
|
|
}
|
|
}
|
|
|
|
// Plain number
|
|
const n = parseFloat(s);
|
|
return Number.isFinite(n) ? n : fallback;
|
|
}
|
|
|
|
/** Format a number back into a compact SPICE-style string. */
|
|
export function formatValueWithUnits(value: number): string {
|
|
if (!Number.isFinite(value)) return '0';
|
|
const abs = Math.abs(value);
|
|
if (abs >= 1e9) return `${(value / 1e9).toPrecision(3)}G`;
|
|
if (abs >= 1e6) return `${(value / 1e6).toPrecision(3)}Meg`;
|
|
if (abs >= 1e3) return `${(value / 1e3).toPrecision(3)}k`;
|
|
if (abs >= 1) return `${value.toPrecision(3)}`;
|
|
if (abs >= 1e-3) return `${(value * 1e3).toPrecision(3)}m`;
|
|
if (abs >= 1e-6) return `${(value * 1e6).toPrecision(3)}u`;
|
|
if (abs >= 1e-9) return `${(value * 1e9).toPrecision(3)}n`;
|
|
if (abs >= 1e-12) return `${(value * 1e12).toPrecision(3)}p`;
|
|
return `${(value * 1e15).toPrecision(3)}f`;
|
|
}
|