/** * Time to sample index, and back. * * Layer 7, and pure. Every viewer needs this and the obvious spelling is wrong: * `Math.round(seconds * signal.sampleRateHz)`. * * Three things break it. `sampleRateHz` is `samplesPerRecord / recordDurationSeconds`, so for a * record duration that is not a power of ten it is a float with no exact representation — * 128 samples over 0.3 s is 426.666..., and multiplying by a large `seconds` accumulates the * error until the index is off by one. `sampleRateHz` is also `undefined` when the record * duration is zero, which is legal EDF and which a real sleep-staging file relies on, so the * expression silently yields `NaN`. And a recording does not start at zero: onsets are relative * to record 0's own start, which is what every other time in edfcore is measured from. * * These do the arithmetic in integers on `(record, sampleWithinRecord)` instead, which is the * same rule `trimToWindow` follows. * * WHAT THEY MEASURE, PRECISELY: position on the SIGNAL'S OWN SAMPLE GRID. Sample `n` is the `n`th * sample the file stores for that signal, and its time is `n * recordDuration / samplesPerRecord`. * On a CONTIGUOUS recording that is also elapsed recording time, and the two ideas are the same * number — which is why the distinction is easy to miss. * * ON A DISCONTINUOUS FILE THEY ARE NOT THE SAME. Samples are adjacent in the array across a gap * while their times are not, so `gridSampleStartSeconds(signal, 12, d)` answers 3 s for a sample whose * record truly begins at 10 s, and `gridSampleIndexAt(signal, 10, d)` answers with a record index past * the end of a six-record file. These functions receive a signal, a number and a record duration — * no index, no timeline — so they CANNOT detect a gap, cannot bound the answer by the record * count, and are not being modest about it: the information is not in their arguments. * * So the contract is stated rather than guessed at. For a file that may be discontinuous, use * `sampleAt` / `sampleStartTicksOf` / `sampleStartSecondsOf` from `sample-locate.ts`, which take * the recording and can therefore see a gap. `contiguityOf(index)` answers which regime you are * in. On a contiguous file — the common case, and every plain EDF or EDF+C — these are exact and * are what you want. * * THE `grid` PREFIX IS LOAD-BEARING. These were `sampleIndexAt`, `sampleStartTicks` and * `sampleStartSeconds` until 0.3.0, and the arithmetic has not changed since — only the name. The * old names did not say which of two different quantities they returned, and seven releases of * this project were spent on exactly that confusion elsewhere. You cannot call * `gridSampleStartSeconds` while believing you asked for elapsed recording time, which is the * entire point. */ import type { EdfSampleLocation, EdfSignal } from './types.js'; /** * The sample covering `seconds` elapsed from the start of the recording. * * Floor, not round: a sample covers the half-open interval from its own start to the next one's, * so the sample "at" a time is the one whose interval contains it. Rounding would return the * NEXT sample for anything past the halfway point, which puts a window boundary one sample late. */ export declare function gridSampleIndexAt(signal: EdfSignal, seconds: number, recordDurationTicks: bigint): EdfSampleLocation; /** * The exact start time of a sample, in 100 ns ticks, elapsed from the start of the recording. * * Ticks rather than seconds because this is the value worth comparing: a float64 second loses * precision past about 28.5 years, and more importantly two times that should be equal can fail * to be once both have been divided by ten million. * * Rounded UP to a whole tick, which matters more than it looks. A sample boundary need not fall * on one: 128 samples over 0.3 s puts sample 1 at 23,437.5 ticks, and 100 ns is the finest unit * edfcore has. Truncating would return 23,437 — a tick that lies inside sample 0 — so * `gridSampleIndexAt` would send it straight back to the previous sample. Taking the first whole * tick at or after the exact start keeps the two functions inverse for every index. */ export declare function gridSampleStartTicks(signal: EdfSignal, sampleIndex: number, recordDurationTicks: bigint): bigint; /** `gridSampleStartTicks` in seconds, for display. Compare ticks, not this. */ export declare function gridSampleStartSeconds(signal: EdfSignal, sampleIndex: number, recordDurationTicks: bigint): number; //# sourceMappingURL=sample-grid.d.ts.map