edf2csv 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +123 -0
  3. package/dist/cli/report.d.ts +17 -0
  4. package/dist/cli/report.d.ts.map +1 -0
  5. package/dist/cli/report.js +107 -0
  6. package/dist/cli/report.js.map +1 -0
  7. package/dist/cli.d.ts +13 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +315 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/convert/channels.d.ts +40 -0
  12. package/dist/convert/channels.d.ts.map +1 -0
  13. package/dist/convert/channels.js +134 -0
  14. package/dist/convert/channels.js.map +1 -0
  15. package/dist/convert/plan.d.ts +70 -0
  16. package/dist/convert/plan.d.ts.map +1 -0
  17. package/dist/convert/plan.js +127 -0
  18. package/dist/convert/plan.js.map +1 -0
  19. package/dist/convert/run.d.ts +48 -0
  20. package/dist/convert/run.d.ts.map +1 -0
  21. package/dist/convert/run.js +358 -0
  22. package/dist/convert/run.js.map +1 -0
  23. package/dist/convert/time-range.d.ts +63 -0
  24. package/dist/convert/time-range.d.ts.map +1 -0
  25. package/dist/convert/time-range.js +188 -0
  26. package/dist/convert/time-range.js.map +1 -0
  27. package/dist/convert/timing.d.ts +18 -0
  28. package/dist/convert/timing.d.ts.map +1 -0
  29. package/dist/convert/timing.js +69 -0
  30. package/dist/convert/timing.js.map +1 -0
  31. package/dist/edf/annotations.d.ts +43 -0
  32. package/dist/edf/annotations.d.ts.map +1 -0
  33. package/dist/edf/annotations.js +88 -0
  34. package/dist/edf/annotations.js.map +1 -0
  35. package/dist/edf/errors.d.ts +27 -0
  36. package/dist/edf/errors.d.ts.map +1 -0
  37. package/dist/edf/errors.js +23 -0
  38. package/dist/edf/errors.js.map +1 -0
  39. package/dist/edf/header.d.ts +115 -0
  40. package/dist/edf/header.d.ts.map +1 -0
  41. package/dist/edf/header.js +383 -0
  42. package/dist/edf/header.js.map +1 -0
  43. package/dist/edf/reader.d.ts +75 -0
  44. package/dist/edf/reader.d.ts.map +1 -0
  45. package/dist/edf/reader.js +224 -0
  46. package/dist/edf/reader.js.map +1 -0
  47. package/dist/edf/scale.d.ts +46 -0
  48. package/dist/edf/scale.d.ts.map +1 -0
  49. package/dist/edf/scale.js +76 -0
  50. package/dist/edf/scale.js.map +1 -0
  51. package/dist/format/csv.d.ts +34 -0
  52. package/dist/format/csv.d.ts.map +1 -0
  53. package/dist/format/csv.js +136 -0
  54. package/dist/format/csv.js.map +1 -0
  55. package/dist/format/number.d.ts +42 -0
  56. package/dist/format/number.d.ts.map +1 -0
  57. package/dist/format/number.js +108 -0
  58. package/dist/format/number.js.map +1 -0
  59. package/dist/index.d.ts +25 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +17 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/version.d.ts +3 -0
  64. package/dist/version.d.ts.map +1 -0
  65. package/dist/version.js +8 -0
  66. package/dist/version.js.map +1 -0
  67. package/package.json +73 -0
@@ -0,0 +1,358 @@
1
+ /**
2
+ * Executing a conversion.
3
+ *
4
+ * Everything is written in a single pass over the data records. All rate groups are
5
+ * open at once and fed from the same batch of bytes, so a file is read once no
6
+ * matter how many output tables it produces, and memory stays flat.
7
+ */
8
+ import { createWriteStream } from 'node:fs';
9
+ import { mkdir, readdir, stat, writeFile } from 'node:fs/promises';
10
+ import { createHash } from 'node:crypto';
11
+ import { createReadStream } from 'node:fs';
12
+ import { pipeline } from 'node:stream/promises';
13
+ import path from 'node:path';
14
+ import { EdfFile } from '../edf/reader.js';
15
+ import { describeFormat, formatWallClock } from '../edf/header.js';
16
+ import { BufferedLineWriter, csvRow } from '../format/csv.js';
17
+ import { fixed, makeSampleFormatter } from '../format/number.js';
18
+ import { buildPlan } from './plan.js';
19
+ import { deriveRecordStarts } from './timing.js';
20
+ import { sampleTimeIsInRange } from './time-range.js';
21
+ import { VERSION as TOOL_VERSION } from '../version.js';
22
+ export { TOOL_VERSION };
23
+ export class ConversionError extends Error {
24
+ code;
25
+ hint;
26
+ constructor(code, message, hint) {
27
+ super(message);
28
+ this.name = 'ConversionError';
29
+ this.code = code;
30
+ this.hint = hint;
31
+ }
32
+ }
33
+ export async function convert(inputPath, options = {}) {
34
+ const startedAt = Date.now();
35
+ const file = await EdfFile.open(inputPath);
36
+ try {
37
+ // One pass over the annotation channel supplies everything annotation-related:
38
+ // where each record sits in time, and the full event list. It reads the whole
39
+ // file even when a window was requested, because an annotation inside the window
40
+ // may be stored in a record outside it.
41
+ const annotationData = file.annotationSignals.length > 0
42
+ ? await file.readAnnotations()
43
+ : { annotations: [], recordStarts: [], malformed: 0 };
44
+ const timing = deriveRecordStarts(file, annotationData);
45
+ const plan = buildPlan({
46
+ signals: file.header.signals,
47
+ recordDuration: file.header.recordDuration,
48
+ recordCount: file.recordCount,
49
+ hasAnnotationChannel: file.annotationSignals.length > 0,
50
+ recordStarts: timing.starts,
51
+ }, options);
52
+ plan.diagnostics.push(...timing.diagnostics);
53
+ const outputDir = options.outputDir ?? defaultOutputDir(inputPath);
54
+ await assertInputDoesNotOverlapOutputs(inputPath, outputDir, file, plan);
55
+ await prepareOutputDir(outputDir, options.force === true);
56
+ const written = [];
57
+ if (plan.writeSignals && plan.groups.length > 0) {
58
+ written.push(...(await writeSignalFiles(file, plan, outputDir, timing.starts, options)));
59
+ }
60
+ if (options.annotationsOnly === true && file.annotationSignals.length === 0) {
61
+ plan.diagnostics.push({
62
+ code: 'NO_ANNOTATIONS',
63
+ severity: 'warning',
64
+ message: '--annotations-only was requested but this recording has no annotation channel, ' +
65
+ 'so there are no events to export.',
66
+ hint: 'Plain EDF files carry no annotations. Convert without --annotations-only to get the signals.',
67
+ });
68
+ }
69
+ let annotationsWritten = 0;
70
+ if (file.annotationSignals.length > 0) {
71
+ const result = await writeAnnotationsCsv(outputDir, annotationData.annotations, plan);
72
+ written.push(result);
73
+ annotationsWritten = result.rows;
74
+ }
75
+ written.push(await writeChannelsCsv(outputDir, file, plan));
76
+ await writeMetadata(outputDir, inputPath, file, plan, written, annotationsWritten, options);
77
+ const stale = await findStaleOutput(outputDir, written);
78
+ return {
79
+ outputDir,
80
+ files: written,
81
+ annotationCount: annotationsWritten,
82
+ diagnostics: [...file.diagnostics, ...plan.diagnostics, ...stale],
83
+ plan,
84
+ file,
85
+ elapsedMs: Date.now() - startedAt,
86
+ };
87
+ }
88
+ finally {
89
+ await file.close();
90
+ }
91
+ }
92
+ /** Files this tool produces, used to spot leftovers from an earlier conversion. */
93
+ const OUTPUT_PATTERN = /^(signals(_[\w.]+hz)?\.csv|annotations\.csv|channels\.csv|metadata\.json)$/u;
94
+ /**
95
+ * Detect output from a previous run that this one did not replace.
96
+ *
97
+ * `--force` overwrites files but does not empty the directory, so converting a
98
+ * mixed-rate recording and then a single-rate one into the same place leaves
99
+ * `signals_256hz.csv` sitting next to a fresh `signals.csv`. Both look current.
100
+ * Nothing is deleted here — the user is told, and decides.
101
+ */
102
+ async function findStaleOutput(outputDir, written) {
103
+ const entries = await readdir(outputDir).catch(() => null);
104
+ if (!entries)
105
+ return [];
106
+ // metadata.json is rewritten on every run but is not part of the reported file list.
107
+ const fresh = new Set([...written.map((f) => f.name), 'metadata.json']);
108
+ const stale = entries.filter((name) => OUTPUT_PATTERN.test(name) && !fresh.has(name)).sort();
109
+ if (stale.length === 0)
110
+ return [];
111
+ return [
112
+ {
113
+ code: 'STALE_OUTPUT',
114
+ severity: 'warning',
115
+ message: `${stale.join(', ')} ${stale.length === 1 ? 'is' : 'are'} left over from an earlier ` +
116
+ `conversion into this directory and ${stale.length === 1 ? 'was' : 'were'} not rewritten.`,
117
+ hint: 'Delete them, or convert into a fresh directory, so the two runs do not get mixed up.',
118
+ },
119
+ ];
120
+ }
121
+ export function defaultOutputDir(inputPath) {
122
+ const base = path.basename(inputPath, path.extname(inputPath));
123
+ return path.join(path.dirname(inputPath), `${base}_csv`);
124
+ }
125
+ /** Never let an output target resolve to the recording being read, even with --force. */
126
+ async function assertInputDoesNotOverlapOutputs(inputPath, outputDir, file, plan) {
127
+ const names = new Set(plan.groups.map((group) => group.fileName));
128
+ if (file.annotationSignals.length > 0)
129
+ names.add('annotations.csv');
130
+ names.add('channels.csv');
131
+ names.add('metadata.json');
132
+ const inputResolved = path.resolve(inputPath);
133
+ const inputInfo = await stat(inputPath);
134
+ for (const name of names) {
135
+ const target = path.join(outputDir, name);
136
+ const targetResolved = path.resolve(target);
137
+ const targetInfo = await stat(target).catch(() => null);
138
+ const samePath = targetResolved === inputResolved;
139
+ const sameFile = targetInfo !== null && targetInfo.dev === inputInfo.dev && targetInfo.ino === inputInfo.ino;
140
+ if (!samePath && !sameFile)
141
+ continue;
142
+ throw new ConversionError('INPUT_OUTPUT_COLLISION', `Output file "${target}" is the same file as the input recording.`, 'Choose a separate directory with --out. The input was not modified.');
143
+ }
144
+ }
145
+ async function prepareOutputDir(dir, force) {
146
+ const existing = await stat(dir).catch(() => null);
147
+ // --force means "replace my previous output", not "write output files into
148
+ // whatever this happens to be". Pointing it at a regular file needs saying so.
149
+ if (existing && !existing.isDirectory()) {
150
+ throw new ConversionError('OUTPUT_UNWRITABLE', `"${dir}" is a file, but the converted data needs a directory.`, 'Choose a directory with --out.');
151
+ }
152
+ if (existing && !force) {
153
+ throw new ConversionError('OUTPUT_EXISTS', `"${dir}" already exists.`, 'Pass --force to overwrite it, or --out to choose a different directory.');
154
+ }
155
+ await mkdir(dir, { recursive: true }).catch((cause) => {
156
+ throw new ConversionError('OUTPUT_UNWRITABLE', `Cannot create "${dir}": ${describeFsError(cause)}.`, 'Check the path exists and that you have permission to write there.');
157
+ });
158
+ }
159
+ /** Turn a Node filesystem error into something a person can act on. */
160
+ function describeFsError(cause) {
161
+ const code = cause?.code;
162
+ if (code === 'EACCES' || code === 'EPERM')
163
+ return 'permission denied';
164
+ if (code === 'ENOSPC')
165
+ return 'the disk is full';
166
+ if (code === 'ENOTDIR')
167
+ return 'part of the path is a file, not a directory';
168
+ if (code === 'EROFS')
169
+ return 'the filesystem is read-only';
170
+ if (code === 'ENAMETOOLONG')
171
+ return 'the path is too long';
172
+ return cause instanceof Error ? cause.message : String(cause);
173
+ }
174
+ async function writeSignalFiles(file, plan, outputDir, recordStarts, options) {
175
+ const open = plan.groups.map((group) => {
176
+ const stream = createWriteStream(path.join(outputDir, group.fileName));
177
+ const writer = new BufferedLineWriter(stream);
178
+ writer.pushLine(csvRow(['time_s', ...group.channels.map((c) => c.column)]));
179
+ return {
180
+ group,
181
+ writer,
182
+ formatters: group.channels.map((c) => makeSampleFormatter(c.signal, c.decimals)),
183
+ rows: 0,
184
+ };
185
+ });
186
+ try {
187
+ return await streamSignalRows(file, plan, open, recordStarts, options);
188
+ }
189
+ catch (cause) {
190
+ for (const entry of open)
191
+ entry.writer.destroy();
192
+ const detail = cause instanceof Error ? cause.message : String(cause);
193
+ throw new ConversionError('WRITE_FAILED', `Writing to "${outputDir}" failed: ${detail}`, 'The files written so far are incomplete and should not be used. Free up space or ' +
194
+ 'choose another destination with --out, then run the conversion again.');
195
+ }
196
+ }
197
+ async function streamSignalRows(file, plan, open, recordStarts, options) {
198
+ const { startSeconds, endSeconds, startRecord, endRecord } = plan.range;
199
+ const { recordDuration } = file.header;
200
+ let recordsDone = 0;
201
+ for await (const batch of file.readRecords({ startRecord, endRecord })) {
202
+ for (let r = 0; r < batch.recordCount; r++) {
203
+ const index = batch.firstRecordIndex + r;
204
+ const recordStart = recordStarts ? (recordStarts[index] ?? index * recordDuration) : index * recordDuration;
205
+ for (const entry of open) {
206
+ const { group, writer, formatters } = entry;
207
+ const { channels, rate, timeDecimals: tDecimals } = group;
208
+ for (let sample = 0; sample < group.samplesPerRecord; sample++) {
209
+ const time = recordStart + sample / rate;
210
+ if (!sampleTimeIsInRange(time, startSeconds, endSeconds))
211
+ continue;
212
+ let row = fixed(time, tDecimals);
213
+ for (let c = 0; c < channels.length; c++) {
214
+ const channel = channels[c];
215
+ const format = formatters[c];
216
+ if (!channel || !format)
217
+ continue;
218
+ row += ',' + format(file.sampleAt(batch, r, channel.signal, sample));
219
+ }
220
+ writer.pushLine(row);
221
+ entry.rows++;
222
+ }
223
+ await writer.maybeFlush();
224
+ }
225
+ recordsDone++;
226
+ }
227
+ options.onProgress?.({
228
+ recordsDone,
229
+ recordsTotal: endRecord - startRecord,
230
+ bytesWritten: open.reduce((sum, g) => sum + g.writer.charsWritten, 0),
231
+ });
232
+ }
233
+ for (const entry of open)
234
+ await entry.writer.end();
235
+ return open.map((entry) => ({ name: entry.group.fileName, rows: entry.rows }));
236
+ }
237
+ async function writeChannelsCsv(outputDir, file, plan) {
238
+ const includedColumns = new Set(plan.groups.flatMap((g) => g.channels.map((c) => c.signal.index)));
239
+ const fileFor = new Map();
240
+ for (const group of plan.groups) {
241
+ for (const channel of group.channels)
242
+ fileFor.set(channel.signal.index, group.fileName);
243
+ }
244
+ const lines = [
245
+ csvRow([
246
+ 'column',
247
+ 'signal_index',
248
+ 'label',
249
+ 'unit',
250
+ 'sampling_rate_hz',
251
+ 'samples_per_record',
252
+ 'physical_min',
253
+ 'physical_max',
254
+ 'digital_min',
255
+ 'digital_max',
256
+ 'transducer',
257
+ 'prefiltering',
258
+ 'output_file',
259
+ 'converted',
260
+ ]),
261
+ ];
262
+ for (const signal of file.header.signals) {
263
+ if (signal.isAnnotations)
264
+ continue;
265
+ const column = plan.columnNames.get(signal.index) ?? `signal_${signal.index}`;
266
+ lines.push(csvRow([
267
+ column,
268
+ String(signal.index),
269
+ signal.label,
270
+ signal.physicalDimension,
271
+ String(signal.samplingRate),
272
+ String(signal.samplesPerRecord),
273
+ String(signal.physicalMin),
274
+ String(signal.physicalMax),
275
+ String(signal.digitalMin),
276
+ String(signal.digitalMax),
277
+ signal.transducer,
278
+ signal.prefiltering,
279
+ fileFor.get(signal.index) ?? '',
280
+ includedColumns.has(signal.index) ? 'yes' : 'no',
281
+ ]));
282
+ }
283
+ await writeFile(path.join(outputDir, 'channels.csv'), lines.join('\n') + '\n', 'utf8');
284
+ return { name: 'channels.csv', rows: lines.length - 1 };
285
+ }
286
+ async function writeAnnotationsCsv(outputDir, annotations, plan) {
287
+ const { startSeconds, endSeconds } = plan.range;
288
+ const inWindow = annotations
289
+ .filter((a) => a.onset >= startSeconds && a.onset < endSeconds)
290
+ .sort((a, b) => a.onset - b.onset || a.recordIndex - b.recordIndex);
291
+ const lines = [csvRow(['onset_s', 'duration_s', 'description', 'record_index'])];
292
+ for (const annotation of inWindow) {
293
+ lines.push(csvRow([
294
+ String(annotation.onset),
295
+ annotation.duration === null ? '' : String(annotation.duration),
296
+ annotation.text,
297
+ String(annotation.recordIndex),
298
+ ]));
299
+ }
300
+ await writeFile(path.join(outputDir, 'annotations.csv'), lines.join('\n') + '\n', 'utf8');
301
+ return { name: 'annotations.csv', rows: inWindow.length };
302
+ }
303
+ async function writeMetadata(outputDir, inputPath, file, plan, written, annotationCount, options) {
304
+ const { header } = file;
305
+ const info = await stat(inputPath).catch(() => null);
306
+ const metadata = {
307
+ tool: { name: 'edf2csv', version: TOOL_VERSION },
308
+ source: {
309
+ path: path.resolve(inputPath),
310
+ bytes: info?.size ?? null,
311
+ modified: info ? new Date(info.mtimeMs).toISOString() : null,
312
+ sha256: options.checksum === true ? await sha256(inputPath) : null,
313
+ },
314
+ recording: {
315
+ format: describeFormat(header),
316
+ version: header.version,
317
+ patient_id: header.patientId,
318
+ recording_id: header.recordingId,
319
+ // Zone-less on purpose: EDF records local wall-clock digits and no timezone.
320
+ start_datetime_local: formatWallClock(header.startDateTime),
321
+ start_date_raw: header.startDateRaw,
322
+ start_time_raw: header.startTimeRaw,
323
+ data_records: file.recordCount,
324
+ data_records_declared: header.declaredRecordCount,
325
+ record_duration_seconds: header.recordDuration,
326
+ duration_seconds: file.durationSeconds,
327
+ signal_count: header.signalCount,
328
+ annotation_channels: file.annotationSignals.length,
329
+ },
330
+ conversion: {
331
+ converted_at: new Date().toISOString(),
332
+ start_seconds: plan.range.startSeconds,
333
+ end_seconds: plan.range.endSeconds,
334
+ whole_recording: plan.range.isWholeRecording,
335
+ records_converted: [plan.range.startRecord, plan.range.endRecord],
336
+ annotations_written: annotationCount,
337
+ files: written.map((f) => ({ name: f.name, rows: f.rows })),
338
+ rate_groups: plan.groups.map((g) => ({
339
+ file: g.fileName,
340
+ sampling_rate_hz: g.rate,
341
+ channels: g.channels.map((c) => c.column),
342
+ decimals: g.channels.map((c) => c.decimals),
343
+ })),
344
+ },
345
+ notes: [...file.diagnostics, ...plan.diagnostics].map((d) => ({
346
+ code: d.code,
347
+ severity: d.severity,
348
+ message: d.message,
349
+ })),
350
+ };
351
+ await writeFile(path.join(outputDir, 'metadata.json'), JSON.stringify(metadata, null, 2) + '\n', 'utf8');
352
+ }
353
+ async function sha256(filePath) {
354
+ const hash = createHash('sha256');
355
+ await pipeline(createReadStream(filePath), hash);
356
+ return hash.digest('hex');
357
+ }
358
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/convert/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Parsing for the time-range options.
3
+ *
4
+ * Researchers write offsets in whatever form is natural for the recording in front
5
+ * of them: seconds for a short ECG strip, `30m` into a sleep study, `01:23:45` when
6
+ * reading off a clock. All three are accepted; anything ambiguous is rejected with
7
+ * a message that shows the forms that work.
8
+ */
9
+ export declare class TimeRangeError extends Error {
10
+ constructor(message: string);
11
+ }
12
+ /**
13
+ * Parse a duration or offset into seconds.
14
+ *
15
+ * Accepted: `90`, `90s`, `5m`, `1h30m`, `1h 30m 15s`, `00:30:00`, `30:00`, `250ms`.
16
+ */
17
+ export declare function parseTimeSpec(input: string, optionName: string): number;
18
+ export interface ResolvedRange {
19
+ /** Inclusive start, in seconds from the beginning of the recording. */
20
+ startSeconds: number;
21
+ /** Exclusive end, in seconds. */
22
+ endSeconds: number;
23
+ /** First data record touching the window. */
24
+ startRecord: number;
25
+ /** One past the last data record touching the window. */
26
+ endRecord: number;
27
+ /** True when the window covers the whole recording. */
28
+ isWholeRecording: boolean;
29
+ /** Earliest record start, including EDF+D timing gaps. */
30
+ recordingStartSeconds: number;
31
+ /** End of the latest record, including EDF+D timing gaps. */
32
+ recordingEndSeconds: number;
33
+ }
34
+ /** Slack for comparisons at sample/window boundaries. */
35
+ export declare const BOUNDARY_TOLERANCE = 1e-9;
36
+ /** Match the exact half-open boundary rules used while writing signal rows. */
37
+ export declare function sampleTimeIsInRange(time: number, startSeconds: number, endSeconds: number): boolean;
38
+ /** Count samples from one record that fall inside a half-open requested window. */
39
+ export declare function countSamplesInRange(options: {
40
+ recordStart: number;
41
+ rate: number;
42
+ samplesPerRecord: number;
43
+ startSeconds: number;
44
+ endSeconds: number;
45
+ }): number;
46
+ /**
47
+ * Turn a requested window into both an exact time span and the record range that
48
+ * contains it. Records are the unit the file can be read in; the exact span is what
49
+ * decides which samples inside those records are actually written.
50
+ */
51
+ export declare function resolveRange(options: {
52
+ start?: number | undefined;
53
+ duration?: number | undefined;
54
+ end?: number | undefined;
55
+ recordDuration: number;
56
+ recordCount: number;
57
+ /**
58
+ * True start time of each data record, for discontinuous files. When absent,
59
+ * records are assumed to sit end to end.
60
+ */
61
+ recordStarts?: Float64Array | null | undefined;
62
+ }): ResolvedRange;
63
+ //# sourceMappingURL=time-range.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-range.d.ts","sourceRoot":"","sources":["../../src/convert/time-range.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAwBD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAoDvE;AASD,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,YAAY,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,0DAA0D;IAC1D,qBAAqB,EAAE,MAAM,CAAC;IAC9B,6DAA6D;IAC7D,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,yDAAyD;AACzD,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAEnG;AAED,mFAAmF;AACnF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,CAUT;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE;IACpC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,CAAC;CAChD,GAAG,aAAa,CA4ChB"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Parsing for the time-range options.
3
+ *
4
+ * Researchers write offsets in whatever form is natural for the recording in front
5
+ * of them: seconds for a short ECG strip, `30m` into a sleep study, `01:23:45` when
6
+ * reading off a clock. All three are accepted; anything ambiguous is rejected with
7
+ * a message that shows the forms that work.
8
+ */
9
+ export class TimeRangeError extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = 'TimeRangeError';
13
+ }
14
+ }
15
+ const UNIT_SECONDS = {
16
+ h: 3600,
17
+ hr: 3600,
18
+ hrs: 3600,
19
+ hour: 3600,
20
+ hours: 3600,
21
+ m: 60,
22
+ min: 60,
23
+ mins: 60,
24
+ minute: 60,
25
+ minutes: 60,
26
+ s: 1,
27
+ sec: 1,
28
+ secs: 1,
29
+ second: 1,
30
+ seconds: 1,
31
+ ms: 0.001,
32
+ };
33
+ const CLOCK = /^(?:(\d+):)?(\d{1,2}):(\d{1,2}(?:\.\d+)?)$/u;
34
+ const UNIT_TOKEN = /(\d+(?:\.\d+)?)\s*([a-z]+)/giu;
35
+ /**
36
+ * Parse a duration or offset into seconds.
37
+ *
38
+ * Accepted: `90`, `90s`, `5m`, `1h30m`, `1h 30m 15s`, `00:30:00`, `30:00`, `250ms`.
39
+ */
40
+ export function parseTimeSpec(input, optionName) {
41
+ const text = input.trim().toLowerCase();
42
+ if (text === '') {
43
+ throw new TimeRangeError(`${optionName} is empty. Try a value like 30s, 5m, or 00:30:00.`);
44
+ }
45
+ // Bare number means seconds.
46
+ if (/^\d+(?:\.\d+)?$/u.test(text))
47
+ return assertFinite(Number(text), optionName, input);
48
+ // Clock form: hh:mm:ss or mm:ss.
49
+ const clock = CLOCK.exec(text);
50
+ if (clock) {
51
+ const hours = clock[1] === undefined ? 0 : Number(clock[1]);
52
+ const minutes = Number(clock[2]);
53
+ const seconds = Number(clock[3]);
54
+ if (minutes >= 60 || seconds >= 60) {
55
+ throw new TimeRangeError(`${optionName} "${input}" has a minutes or seconds field of 60 or more.`);
56
+ }
57
+ return assertFinite(hours * 3600 + minutes * 60 + seconds, optionName, input);
58
+ }
59
+ // Unit form: 1h30m, 5 min, 250ms.
60
+ UNIT_TOKEN.lastIndex = 0;
61
+ let total = 0;
62
+ let matched = 0;
63
+ let consumed = 0;
64
+ let match;
65
+ while ((match = UNIT_TOKEN.exec(text)) !== null) {
66
+ const amount = Number(match[1]);
67
+ const unit = match[2] ?? '';
68
+ const scale = UNIT_SECONDS[unit];
69
+ if (scale === undefined) {
70
+ throw new TimeRangeError(`${optionName} "${input}" uses an unknown unit "${unit}". Use h, m, s, or ms.`);
71
+ }
72
+ total += amount * scale;
73
+ matched++;
74
+ consumed += match[0].length;
75
+ }
76
+ // Reject partially-understood input like "5x" or "1h banana".
77
+ if (matched === 0 || consumed !== text.replace(/\s+/gu, '').length) {
78
+ throw new TimeRangeError(`${optionName} "${input}" is not a time I understand. ` +
79
+ `Try 30s, 5m, 1h30m, 00:30:00, or a plain number of seconds.`);
80
+ }
81
+ return assertFinite(total, optionName, input);
82
+ }
83
+ function assertFinite(value, optionName, input) {
84
+ if (!Number.isFinite(value) || value < 0) {
85
+ throw new TimeRangeError(`${optionName} "${input}" is not a valid non-negative time.`);
86
+ }
87
+ return value;
88
+ }
89
+ /** Slack for comparisons at sample/window boundaries. */
90
+ export const BOUNDARY_TOLERANCE = 1e-9;
91
+ /** Match the exact half-open boundary rules used while writing signal rows. */
92
+ export function sampleTimeIsInRange(time, startSeconds, endSeconds) {
93
+ return time >= startSeconds - BOUNDARY_TOLERANCE && time < endSeconds - BOUNDARY_TOLERANCE;
94
+ }
95
+ /** Count samples from one record that fall inside a half-open requested window. */
96
+ export function countSamplesInRange(options) {
97
+ const lower = Math.ceil((options.startSeconds - BOUNDARY_TOLERANCE - options.recordStart) * options.rate);
98
+ const upper = Math.ceil((options.endSeconds - BOUNDARY_TOLERANCE - options.recordStart) * options.rate);
99
+ const first = Math.max(0, Math.min(options.samplesPerRecord, lower));
100
+ const last = Math.max(0, Math.min(options.samplesPerRecord, upper));
101
+ return Math.max(0, last - first);
102
+ }
103
+ /**
104
+ * Turn a requested window into both an exact time span and the record range that
105
+ * contains it. Records are the unit the file can be read in; the exact span is what
106
+ * decides which samples inside those records are actually written.
107
+ */
108
+ export function resolveRange(options) {
109
+ // For a continuous file the recording spans recordCount * recordDuration. A
110
+ // discontinuous one does not: a 10-second recording with a 95-second gap in the
111
+ // middle still ends at 105 seconds. Deriving the span from the records' real
112
+ // positions is what stops a requested window from being clipped back to the
113
+ // amount of *data* in the file and silently discarding everything past it.
114
+ const { earliest, latest } = span(options.recordStarts, options.recordCount, options.recordDuration);
115
+ if (options.duration !== undefined && options.end !== undefined) {
116
+ throw new TimeRangeError('Use either --duration or --end, not both.');
117
+ }
118
+ const startSeconds = options.start ?? earliest;
119
+ let endSeconds;
120
+ if (options.duration !== undefined)
121
+ endSeconds = startSeconds + options.duration;
122
+ else if (options.end !== undefined)
123
+ endSeconds = options.end;
124
+ else
125
+ endSeconds = latest;
126
+ if (startSeconds >= latest) {
127
+ throw new TimeRangeError(`--start ${formatSeconds(startSeconds)} is at or past the end of this ` +
128
+ `${formatSeconds(latest)} recording.`);
129
+ }
130
+ if (endSeconds <= startSeconds) {
131
+ throw new TimeRangeError(`The requested window ends at ${formatSeconds(endSeconds)}, which is not after its ` +
132
+ `start at ${formatSeconds(startSeconds)}.`);
133
+ }
134
+ const clampedEnd = Math.min(endSeconds, latest);
135
+ const { startRecord, endRecord } = selectRecords(options, startSeconds, clampedEnd);
136
+ return {
137
+ startSeconds,
138
+ endSeconds: clampedEnd,
139
+ startRecord,
140
+ endRecord,
141
+ isWholeRecording: startSeconds <= earliest && clampedEnd >= latest,
142
+ recordingStartSeconds: earliest,
143
+ recordingEndSeconds: latest,
144
+ };
145
+ }
146
+ function span(recordStarts, recordCount, recordDuration) {
147
+ if (!recordStarts || recordStarts.length === 0) {
148
+ return { earliest: 0, latest: recordCount * recordDuration };
149
+ }
150
+ let earliest = Infinity;
151
+ let latest = -Infinity;
152
+ for (const start of recordStarts) {
153
+ if (start < earliest)
154
+ earliest = start;
155
+ if (start + recordDuration > latest)
156
+ latest = start + recordDuration;
157
+ }
158
+ if (!Number.isFinite(earliest) || !Number.isFinite(latest)) {
159
+ return { earliest: 0, latest: recordCount * recordDuration };
160
+ }
161
+ return { earliest, latest };
162
+ }
163
+ /** Every record whose own time span overlaps the requested window. */
164
+ function selectRecords(options, startSeconds, endSeconds) {
165
+ const starts = options.recordStarts;
166
+ if (!starts || starts.length === 0) {
167
+ return {
168
+ startRecord: Math.max(0, Math.floor(startSeconds / options.recordDuration)),
169
+ endRecord: Math.min(options.recordCount, Math.ceil(endSeconds / options.recordDuration)),
170
+ };
171
+ }
172
+ let first = options.recordCount;
173
+ let last = 0;
174
+ for (let i = 0; i < starts.length; i++) {
175
+ const begin = starts[i] ?? 0;
176
+ if (begin + options.recordDuration > startSeconds && begin < endSeconds) {
177
+ if (i < first)
178
+ first = i;
179
+ if (i + 1 > last)
180
+ last = i + 1;
181
+ }
182
+ }
183
+ return first < last ? { startRecord: first, endRecord: last } : { startRecord: 0, endRecord: 0 };
184
+ }
185
+ function formatSeconds(seconds) {
186
+ return `${Number(seconds.toFixed(3))}s`;
187
+ }
188
+ //# sourceMappingURL=time-range.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-range.js","sourceRoot":"","sources":["../../src/convert/time-range.ts"],"names":[],"mappings":"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"}