/ concept-collection / dulcimer
concept-collection / dulcimer
dulcimer / src / units.ts
43 lines · 1.8 KBBlameHistoryRaw
1/**
2 * Everything in SI: metres, seconds, hertz, metres per second.
3 *
4 * Physical units are what make the numbers here mean something. The string's
5 * fundamental is a real pitch, the box is a real size you could build, the
6 * timestep is a real duration, and the microphone's trace plays back at the
7 * pitch a microphone there would have heard. They also keep the method's
8 * limits visible: a grid solver resolves a wavelength with some number of
9 * cells, so a fixed grid is a low-frequency method, and how low is a number
10 * the app can show rather than hide.
11 */
13/** Speed of sound in air at about 20 °C, m/s. */
14export const C_AIR = 343;
16/** The domain, metres: a box around the instrument, twice as long along the
17 * string (x) as across (y) and up (z), centred on the origin. The top plate
18 * of the dulcimer body sits at z = 0 and the string runs along x just above
19 * it. */
20export const DOMAIN_X = 1.0;
21export const DOMAIN_YZ = 0.5;
23/** Fraction of the stability limit the timestep is taken at. Fixed rather
24 * than a control: everything downstream (the string grid, the recording's
25 * sample rate) follows from dt, and one good value is worth more here than a
26 * slider. */
27export const CFL = 0.5;
29/** Cells per wavelength below which what is on screen (and in the recording)
30 * is as much grid dispersion as it is sound. */
31export const POOR_RESOLUTION = 8;
33/** A length in metres, written the way a person would say it. */
34export const fmtLength = (m: number): string =>
35 Math.abs(m) < 1 ? `${(1000 * m).toPrecision(3)} mm` : `${m.toPrecision(3)} m`;
37/** A duration in seconds, likewise. */
38export const fmtTime = (s: number): string => {
39 const a = Math.abs(s);
40 if (a > 0 && a < 1e-3) return `${(1e6 * s).toPrecision(3)} µs`;
41 if (a < 1) return `${(1e3 * s).toPrecision(3)} ms`;
42 return `${s.toPrecision(3)} s`;
43};