/** * Digital-to-physical scale, and the decision to refuse one. * * Layer 2. Sole owner of `EdfScale` construction and of the five conditions under which a * signal gets `scale: undefined` instead of a fabricated gain: the three degenerate or inverted * ranges, a log-transformed channel, and a derived gain that is not a usable float64. The last * arrived in 0.4.509 and `design-decisions.md`, `physical-values.md` and `api-errors.md` have * called it the fifth ever since; this said "the four conditions" until 0.6.72. * * The expression is EDFlib's, verbatim: * * bitValue = (physicalMaximum - physicalMinimum) / (digitalMaximum - digitalMinimum) * offset = physicalMaximum / bitValue - digitalMaximum * physical = bitValue * (offset + digital) * * The textbook `physicalMinimum + (digital - digitalMinimum) * gain` form is numerically * *better* and is deliberately not used: it shifts up to ~45% of samples by one ULP on * asymmetric ranges, which would forfeit float64 bit-parity with pyEDFlib for a divergence ten * orders of magnitude below the quantisation floor. Do not "simplify" this. * * `physicalMinimum > physicalMaximum` is legal — it is how a negative amplifier gain is * written — and produces a negative `bitValue`. The two are never swapped: a silent polarity * flip is a clinically wrong result that looks perfectly normal. */ import type { DiagnosticSink } from '../diagnostics/collector.js'; import type { EdfRawSignalFields, EdfScale } from '../types.js'; /** Byte offsets of the per-signal fields a scaling diagnostic points at. */ export interface ScaleFieldOffsets { readonly physicalDimension: number; readonly physicalMinimum: number; readonly physicalMaximum: number; readonly digitalMinimum: number; readonly digitalMaximum: number; } /** * Everything the gain derivation is allowed to see. Deliberately not an `EdfSignal`: the scale is * derived while the signal is still being built, so taking only the four numbers and the two * strings keeps the dependency one-way and the function testable from literals. */ export interface ScaleInput { readonly signalIndex: number; /** Trimmed label, for the message. */ readonly label: string; /** Physical dimension exactly as written; `'Filtered'` is refused after trimming. */ readonly physicalDimension: string; readonly physicalMinimum: number; readonly physicalMaximum: number; readonly digitalMinimum: number; readonly digitalMaximum: number; /** The raw per-signal field text, so a message can quote the bytes as written. */ readonly raw: EdfRawSignalFields | undefined; /** Where those fields live in the header. */ readonly byteOffsets: ScaleFieldOffsets | undefined; } /** * Collapse the encodings of micro to `'u'` so units can be compared. * * For comparison ONLY: `signal.physicalDimension` stays exactly as the file wrote it. Nothing * else is touched — case is meaningful (`mV` is not `MV`), and edfcore does not normalise units * to SI volts. */ export declare function normaliseUnit(physicalDimension: string): string; /** * The scale for one signal, or `undefined` when edfcore refuses to invent one. * * The refusals are checked in this fixed order: degenerate digital range, degenerate physical * range, inverted digital range, log-transformed channel, and finally a derived gain that is not * a usable float64 number. Each is deferred-fatal — `decodeDigital` keeps working on the signal * and `toPhysical` throws `EdfScalingError`. * * A non-finite input returns `undefined` without a diagnostic: the only way to get here with * one is a numeric field that failed its grammar, which the caller has already reported * against the field itself. */ export declare function buildScale(input: ScaleInput, sink: DiagnosticSink): EdfScale | undefined; //# sourceMappingURL=scale.d.ts.map