edfcore 0.2.61 → 0.2.63
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.
- package/CHANGELOG.md +28 -0
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/format-annotations.d.ts +34 -0
- package/dist/format-annotations.d.ts.map +1 -0
- package/dist/format-annotations.js +83 -0
- package/dist/format-annotations.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/sample-grid.d.ts +26 -5
- package/dist/sample-grid.d.ts.map +1 -1
- package/dist/sample-grid.js +26 -5
- package/dist/sample-grid.js.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +1 -1
- package/src/format-annotations.ts +103 -0
- package/src/index.ts +2 -0
- package/src/sample-grid.ts +26 -5
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,34 @@ alone does not tell you whether you were affected.
|
|
|
6
6
|
edfcore is pre-1.0. Patch releases have carried behaviour changes where the old behaviour was a
|
|
7
7
|
defect; those are called out below.
|
|
8
8
|
|
|
9
|
+
## 0.2.62
|
|
10
|
+
|
|
11
|
+
- **Added** `formatAnnotations`, the third formatter beside `formatHeader` and
|
|
12
|
+
`formatDiagnostics`, and the one a hypnogram or an event list actually needs.
|
|
13
|
+
- The clock is built from `onsetTicksFromFirstRecord` by integer division, never from the float
|
|
14
|
+
seconds. An event list is exactly where someone reads a number off the screen and types it into
|
|
15
|
+
something else, and a millisecond field derived from a float64 that came out of a division by
|
|
16
|
+
10,000,000 can be off by one. A test makes the two fields disagree deliberately, so only a
|
|
17
|
+
formatter reading the exact one passes.
|
|
18
|
+
- Hours are not wrapped at 24 — a 30-hour recording is real, and `30:12` is more useful than
|
|
19
|
+
`06:12` on day two. Times truncate to the millisecond rather than rounding, so the printed
|
|
20
|
+
instant is never later than the event. A NEGATIVE onset prints as one: EDF+ measures onsets from
|
|
21
|
+
the header start time, a recording may begin after its first annotation, and clamping to zero
|
|
22
|
+
would silently move it.
|
|
23
|
+
|
|
24
|
+
## 0.2.61
|
|
25
|
+
|
|
26
|
+
- **Deprecated** `sampleIndexAt`, `sampleStartTicks` and `sampleStartSeconds`, which are renamed
|
|
27
|
+
to `gridSampleIndexAt`, `gridSampleStartTicks` and `gridSampleStartSeconds` in **0.3.0**. The
|
|
28
|
+
behaviour does not change and neither do the arguments — only the name, which never said which of
|
|
29
|
+
two different quantities it returns. Six releases of this project were spent on exactly that
|
|
30
|
+
confusion elsewhere, and the `grid` prefix is what stops the seventh.
|
|
31
|
+
- The tag is folded into each function's existing documentation rather than added as a second
|
|
32
|
+
comment above it, so an editor shows the original prose AND the replacement instead of replacing
|
|
33
|
+
one with the other.
|
|
34
|
+
- Nothing is removed here. An editor will point at the replacement a release before the rename
|
|
35
|
+
lands, and for a contiguous file the rename is the only thing that affects a caller.
|
|
36
|
+
|
|
9
37
|
## 0.2.60
|
|
10
38
|
|
|
11
39
|
- **Added** `sampleAt`, `sampleStartTicksOf` and `sampleStartSecondsOf` — the recording-aware
|
package/dist/constants.d.ts
CHANGED
|
@@ -109,5 +109,5 @@ export declare const SIGNAL_FIELD_BLOCK_OFFSETS: {
|
|
|
109
109
|
readonly reserved: 224;
|
|
110
110
|
};
|
|
111
111
|
/** Published package version. Kept in sync with package.json by a test. */
|
|
112
|
-
export declare const VERSION = "0.2.
|
|
112
|
+
export declare const VERSION = "0.2.63";
|
|
113
113
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.js
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Annotations as text.
|
|
3
|
+
*
|
|
4
|
+
* Layer 7, and pure. `formatHeader` does this for the header and `formatDiagnostics` for problems;
|
|
5
|
+
* this is the third, and the one a hypnogram or an event list actually needs.
|
|
6
|
+
*
|
|
7
|
+
* The clock is built from `onsetTicksFromFirstRecord` by integer division, never from the float
|
|
8
|
+
* seconds. That is not fussiness: `onsetSecondsFromFirstRecord` is a float64 produced by dividing
|
|
9
|
+
* an exact tick count by 10,000,000, so an onset written `+1.0000001` can print a millisecond
|
|
10
|
+
* field that is off by one — and an event list is exactly where someone reads a number off the
|
|
11
|
+
* screen and types it into something else.
|
|
12
|
+
*
|
|
13
|
+
* A NEGATIVE onset is legal and prints as one. EDF+ measures onsets from the header start time
|
|
14
|
+
* and a recording may begin after its first annotation, so `-00:00:01.500` is a real thing a real
|
|
15
|
+
* file says. Clamping it to zero would silently move an event.
|
|
16
|
+
*/
|
|
17
|
+
import type { EdfAnnotation } from './types.js';
|
|
18
|
+
export interface FormatAnnotationsOptions {
|
|
19
|
+
/** Rows to print. Defaults to every annotation; the count of the rest is always stated. */
|
|
20
|
+
readonly maxItems?: number;
|
|
21
|
+
/** Include the `description@@channel` label EDF+ allows. Off by default: most files have none. */
|
|
22
|
+
readonly includeChannel?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A multi-line listing, one annotation per line, in the order given.
|
|
26
|
+
*
|
|
27
|
+
* Returns `''` for an empty list rather than a blank line, so it concatenates into a larger
|
|
28
|
+
* report cleanly — the same rule `formatDiagnostics` follows.
|
|
29
|
+
*
|
|
30
|
+
* The order is the caller's. `readAnnotations` already returns them sorted by onset, and
|
|
31
|
+
* re-sorting here would quietly discard a deliberate `filterAnnotationsByText` ordering.
|
|
32
|
+
*/
|
|
33
|
+
export declare function formatAnnotations(annotations: readonly EdfAnnotation[], options?: FormatAnnotationsOptions): string;
|
|
34
|
+
//# sourceMappingURL=format-annotations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-annotations.d.ts","sourceRoot":"","sources":["../src/format-annotations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,wBAAwB;IACvC,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,kGAAkG;IAClG,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AAqCD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,SAAS,aAAa,EAAE,EACrC,OAAO,CAAC,EAAE,wBAAwB,GACjC,MAAM,CA4BR"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Annotations as text.
|
|
3
|
+
*
|
|
4
|
+
* Layer 7, and pure. `formatHeader` does this for the header and `formatDiagnostics` for problems;
|
|
5
|
+
* this is the third, and the one a hypnogram or an event list actually needs.
|
|
6
|
+
*
|
|
7
|
+
* The clock is built from `onsetTicksFromFirstRecord` by integer division, never from the float
|
|
8
|
+
* seconds. That is not fussiness: `onsetSecondsFromFirstRecord` is a float64 produced by dividing
|
|
9
|
+
* an exact tick count by 10,000,000, so an onset written `+1.0000001` can print a millisecond
|
|
10
|
+
* field that is off by one — and an event list is exactly where someone reads a number off the
|
|
11
|
+
* screen and types it into something else.
|
|
12
|
+
*
|
|
13
|
+
* A NEGATIVE onset is legal and prints as one. EDF+ measures onsets from the header start time
|
|
14
|
+
* and a recording may begin after its first annotation, so `-00:00:01.500` is a real thing a real
|
|
15
|
+
* file says. Clamping it to zero would silently move an event.
|
|
16
|
+
*/
|
|
17
|
+
import { TICKS_PER_SECOND } from './constants.js';
|
|
18
|
+
const TICKS_PER_MILLISECOND = TICKS_PER_SECOND / 1000n;
|
|
19
|
+
/**
|
|
20
|
+
* `hh:mm:ss.mmm`, from exact ticks.
|
|
21
|
+
*
|
|
22
|
+
* Truncates to the millisecond rather than rounding, so the printed time never names an instant
|
|
23
|
+
* later than the event. Hours are not wrapped at 24 — a 30-hour recording is a real thing and
|
|
24
|
+
* `30:12:00.000` is more useful than `06:12:00.000` on day two.
|
|
25
|
+
*/
|
|
26
|
+
function clock(ticks) {
|
|
27
|
+
const negative = ticks < 0n;
|
|
28
|
+
const absolute = negative ? -ticks : ticks;
|
|
29
|
+
const totalMilliseconds = absolute / TICKS_PER_MILLISECOND;
|
|
30
|
+
const milliseconds = totalMilliseconds % 1000n;
|
|
31
|
+
const totalSeconds = totalMilliseconds / 1000n;
|
|
32
|
+
const seconds = totalSeconds % 60n;
|
|
33
|
+
const totalMinutes = totalSeconds / 60n;
|
|
34
|
+
const minutes = totalMinutes % 60n;
|
|
35
|
+
const hours = totalMinutes / 60n;
|
|
36
|
+
const pad = (value, width) => String(value).padStart(width, '0');
|
|
37
|
+
return (`${negative ? '-' : ''}${pad(hours, 2)}:${pad(minutes, 2)}:${pad(seconds, 2)}` +
|
|
38
|
+
`.${pad(milliseconds, 3)}`);
|
|
39
|
+
}
|
|
40
|
+
/** A duration in seconds, or blank. `undefined` and an explicit `0` are the same instant. */
|
|
41
|
+
function duration(annotation) {
|
|
42
|
+
const ticks = annotation.durationTicks;
|
|
43
|
+
if (ticks === undefined || ticks === 0n)
|
|
44
|
+
return '';
|
|
45
|
+
return `${clock(ticks)}`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A multi-line listing, one annotation per line, in the order given.
|
|
49
|
+
*
|
|
50
|
+
* Returns `''` for an empty list rather than a blank line, so it concatenates into a larger
|
|
51
|
+
* report cleanly — the same rule `formatDiagnostics` follows.
|
|
52
|
+
*
|
|
53
|
+
* The order is the caller's. `readAnnotations` already returns them sorted by onset, and
|
|
54
|
+
* re-sorting here would quietly discard a deliberate `filterAnnotationsByText` ordering.
|
|
55
|
+
*/
|
|
56
|
+
export function formatAnnotations(annotations, options) {
|
|
57
|
+
if (annotations.length === 0)
|
|
58
|
+
return '';
|
|
59
|
+
const limit = options?.maxItems === undefined || !Number.isFinite(options.maxItems)
|
|
60
|
+
? annotations.length
|
|
61
|
+
: Math.max(0, Math.min(annotations.length, Math.floor(options.maxItems)));
|
|
62
|
+
const rows = [];
|
|
63
|
+
for (let i = 0; i < limit; i += 1) {
|
|
64
|
+
const annotation = annotations[i];
|
|
65
|
+
if (annotation === undefined)
|
|
66
|
+
continue;
|
|
67
|
+
const parts = [
|
|
68
|
+
clock(annotation.onsetTicksFromFirstRecord),
|
|
69
|
+
duration(annotation).padEnd(12),
|
|
70
|
+
annotation.text,
|
|
71
|
+
];
|
|
72
|
+
if (options?.includeChannel === true && annotation.channelLabel !== undefined) {
|
|
73
|
+
parts.push(`@@${annotation.channelLabel}`);
|
|
74
|
+
}
|
|
75
|
+
rows.push(parts.join(' ').trimEnd());
|
|
76
|
+
}
|
|
77
|
+
const hidden = annotations.length - limit;
|
|
78
|
+
// Always stated. A truncated listing that does not say so reads as a complete one.
|
|
79
|
+
if (hidden > 0)
|
|
80
|
+
rows.push(`... and ${hidden} more`);
|
|
81
|
+
return rows.join('\n');
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=format-annotations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-annotations.js","sourceRoot":"","sources":["../src/format-annotations.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -47,6 +47,8 @@ export { annotationsAt, countAnnotationsByText, filterAnnotationsByText, filterA
|
|
|
47
47
|
export { decodeStatusWord, getStatusSignal, readTriggers } from './biosemi.js';
|
|
48
48
|
export { mergeChunks } from './chunks.js';
|
|
49
49
|
export { envelopeOfSamples, readEnvelope, readEnvelopeAtResolution, toPhysicalEnvelope, } from './envelope.js';
|
|
50
|
+
export type { FormatAnnotationsOptions } from './format-annotations.js';
|
|
51
|
+
export { formatAnnotations } from './format-annotations.js';
|
|
50
52
|
export { formatHeader } from './format-header.js';
|
|
51
53
|
export { inspectEdf } from './inspect.js';
|
|
52
54
|
export { openEdf, readAnnotations, readRecords, readWindow } from './recording.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -59,6 +59,7 @@ export { annotationsAt, countAnnotationsByText, filterAnnotationsByText, filterA
|
|
|
59
59
|
export { decodeStatusWord, getStatusSignal, readTriggers } from './biosemi.js';
|
|
60
60
|
export { mergeChunks } from './chunks.js';
|
|
61
61
|
export { envelopeOfSamples, readEnvelope, readEnvelopeAtResolution, toPhysicalEnvelope, } from './envelope.js';
|
|
62
|
+
export { formatAnnotations } from './format-annotations.js';
|
|
62
63
|
export { formatHeader } from './format-header.js';
|
|
63
64
|
export { inspectEdf } from './inspect.js';
|
|
64
65
|
export { openEdf, readAnnotations, readRecords, readWindow } from './recording.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA2EH,OAAO,EACL,wBAAwB,EACxB,cAAc,EACd,uBAAuB,EACvB,QAAQ,EACR,cAAc,EACd,aAAa,EACb,eAAe,EACf,cAAc,EACd,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,gCAAgC,EAChC,gBAAgB,EAChB,OAAO,GACR,MAAM,gBAAgB,CAAC;AAExB,8EAA8E;AAC9E,2EAA2E;AAC3E,2CAA2C;AAC3C,8EAA8E;AAE9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EACL,uBAAuB,EACvB,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEnE,8EAA8E;AAC9E,yEAAyE;AACzE,2EAA2E;AAC3E,6CAA6C;AAC7C,8EAA8E;AAE9E,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,8EAA8E;AAC9E,uCAAuC;AACvC,8EAA8E;AAE9E,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,KAAK,EACL,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E,OAAO,EACL,aAAa,EACb,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/sample-grid.d.ts
CHANGED
|
@@ -28,10 +28,16 @@
|
|
|
28
28
|
* count, and are not being modest about it: the information is not in their arguments.
|
|
29
29
|
*
|
|
30
30
|
* So the contract is stated rather than guessed at. For a file that may be discontinuous, use
|
|
31
|
-
* `
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
31
|
+
* `sampleAt` / `sampleStartTicksOf` / `sampleStartSecondsOf` from `sample-locate.ts`, which take
|
|
32
|
+
* the recording and can therefore see a gap. `contiguityOf(index)` answers which regime you are
|
|
33
|
+
* in. On a contiguous file — the common case, and every plain EDF or EDF+C — these are exact and
|
|
34
|
+
* are what you want.
|
|
35
|
+
*
|
|
36
|
+
* RENAMED IN 0.3.0. These three keep their behaviour and lose their misleading names: they become
|
|
37
|
+
* `gridSampleIndexAt`, `gridSampleStartTicks` and `gridSampleStartSeconds`. The `grid` prefix is
|
|
38
|
+
* the whole fix — the functions were never wrong, the names simply did not say which of two
|
|
39
|
+
* different quantities they returned, and six releases of this project were spent on exactly that
|
|
40
|
+
* confusion in other places. Nothing about the arithmetic changes.
|
|
35
41
|
*/
|
|
36
42
|
import type { EdfSampleLocation, EdfSignal } from './types.js';
|
|
37
43
|
/**
|
|
@@ -40,6 +46,11 @@ import type { EdfSampleLocation, EdfSignal } from './types.js';
|
|
|
40
46
|
* Floor, not round: a sample covers the half-open interval from its own start to the next one's,
|
|
41
47
|
* so the sample "at" a time is the one whose interval contains it. Rounding would return the
|
|
42
48
|
* NEXT sample for anything past the halfway point, which puts a window boundary one sample late.
|
|
49
|
+
*
|
|
50
|
+
* @deprecated Renamed to `gridSampleIndexAt` in 0.3.0. The behaviour is unchanged — only the
|
|
51
|
+
* name, which never said which of two different quantities it returns. For a file that may
|
|
52
|
+
* have gaps, use `sampleAt` / `sampleStartTicksOf` / `sampleStartSecondsOf`, which take the
|
|
53
|
+
* recording and can therefore see one.
|
|
43
54
|
*/
|
|
44
55
|
export declare function sampleIndexAt(signal: EdfSignal, seconds: number, recordDurationTicks: bigint): EdfSampleLocation;
|
|
45
56
|
/**
|
|
@@ -54,8 +65,18 @@ export declare function sampleIndexAt(signal: EdfSignal, seconds: number, record
|
|
|
54
65
|
* edfcore has. Truncating would return 23,437 — a tick that lies inside sample 0 — so
|
|
55
66
|
* `sampleIndexAt` would send it straight back to the previous sample. Taking the first whole
|
|
56
67
|
* tick at or after the exact start keeps the two functions inverse for every index.
|
|
68
|
+
*
|
|
69
|
+
* @deprecated Renamed to `gridSampleStartTicks` in 0.3.0. The behaviour is unchanged — only the
|
|
70
|
+
* name, which never said which of two different quantities it returns. For a file that may
|
|
71
|
+
* have gaps, use `sampleAt` / `sampleStartTicksOf` / `sampleStartSecondsOf`, which take the
|
|
72
|
+
* recording and can therefore see one.
|
|
57
73
|
*/
|
|
58
74
|
export declare function sampleStartTicks(signal: EdfSignal, sampleIndex: number, recordDurationTicks: bigint): bigint;
|
|
59
|
-
/** `sampleStartTicks` in seconds, for display. Compare ticks, not this.
|
|
75
|
+
/** `sampleStartTicks` in seconds, for display. Compare ticks, not this. *
|
|
76
|
+
* @deprecated Renamed to `gridSampleStartSeconds` in 0.3.0. The behaviour is unchanged — only the
|
|
77
|
+
* name, which never said which of two different quantities it returns. For a file that may
|
|
78
|
+
* have gaps, use `sampleAt` / `sampleStartTicksOf` / `sampleStartSecondsOf`, which take the
|
|
79
|
+
* recording and can therefore see one.
|
|
80
|
+
*/
|
|
60
81
|
export declare function sampleStartSeconds(signal: EdfSignal, sampleIndex: number, recordDurationTicks: bigint): number;
|
|
61
82
|
//# sourceMappingURL=sample-grid.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sample-grid.d.ts","sourceRoot":"","sources":["../src/sample-grid.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"sample-grid.d.ts","sourceRoot":"","sources":["../src/sample-grid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA0B/D;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,MAAM,EACf,mBAAmB,EAAE,MAAM,GAC1B,iBAAiB,CAoBnB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,MAAM,EACnB,mBAAmB,EAAE,MAAM,GAC1B,MAAM,CAcR;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,MAAM,EACnB,mBAAmB,EAAE,MAAM,GAC1B,MAAM,CAKR"}
|
package/dist/sample-grid.js
CHANGED
|
@@ -28,10 +28,16 @@
|
|
|
28
28
|
* count, and are not being modest about it: the information is not in their arguments.
|
|
29
29
|
*
|
|
30
30
|
* So the contract is stated rather than guessed at. For a file that may be discontinuous, use
|
|
31
|
-
* `
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
31
|
+
* `sampleAt` / `sampleStartTicksOf` / `sampleStartSecondsOf` from `sample-locate.ts`, which take
|
|
32
|
+
* the recording and can therefore see a gap. `contiguityOf(index)` answers which regime you are
|
|
33
|
+
* in. On a contiguous file — the common case, and every plain EDF or EDF+C — these are exact and
|
|
34
|
+
* are what you want.
|
|
35
|
+
*
|
|
36
|
+
* RENAMED IN 0.3.0. These three keep their behaviour and lose their misleading names: they become
|
|
37
|
+
* `gridSampleIndexAt`, `gridSampleStartTicks` and `gridSampleStartSeconds`. The `grid` prefix is
|
|
38
|
+
* the whole fix — the functions were never wrong, the names simply did not say which of two
|
|
39
|
+
* different quantities they returned, and six releases of this project were spent on exactly that
|
|
40
|
+
* confusion in other places. Nothing about the arithmetic changes.
|
|
35
41
|
*/
|
|
36
42
|
import { TICKS_PER_SECOND } from './constants.js';
|
|
37
43
|
import { secondsToTicks } from './tal/ticks.js';
|
|
@@ -58,6 +64,11 @@ function assertGrid(signal, recordDurationTicks) {
|
|
|
58
64
|
* Floor, not round: a sample covers the half-open interval from its own start to the next one's,
|
|
59
65
|
* so the sample "at" a time is the one whose interval contains it. Rounding would return the
|
|
60
66
|
* NEXT sample for anything past the halfway point, which puts a window boundary one sample late.
|
|
67
|
+
*
|
|
68
|
+
* @deprecated Renamed to `gridSampleIndexAt` in 0.3.0. The behaviour is unchanged — only the
|
|
69
|
+
* name, which never said which of two different quantities it returns. For a file that may
|
|
70
|
+
* have gaps, use `sampleAt` / `sampleStartTicksOf` / `sampleStartSecondsOf`, which take the
|
|
71
|
+
* recording and can therefore see one.
|
|
61
72
|
*/
|
|
62
73
|
export function sampleIndexAt(signal, seconds, recordDurationTicks) {
|
|
63
74
|
assertGrid(signal, recordDurationTicks);
|
|
@@ -90,6 +101,11 @@ export function sampleIndexAt(signal, seconds, recordDurationTicks) {
|
|
|
90
101
|
* edfcore has. Truncating would return 23,437 — a tick that lies inside sample 0 — so
|
|
91
102
|
* `sampleIndexAt` would send it straight back to the previous sample. Taking the first whole
|
|
92
103
|
* tick at or after the exact start keeps the two functions inverse for every index.
|
|
104
|
+
*
|
|
105
|
+
* @deprecated Renamed to `gridSampleStartTicks` in 0.3.0. The behaviour is unchanged — only the
|
|
106
|
+
* name, which never said which of two different quantities it returns. For a file that may
|
|
107
|
+
* have gaps, use `sampleAt` / `sampleStartTicksOf` / `sampleStartSecondsOf`, which take the
|
|
108
|
+
* recording and can therefore see one.
|
|
93
109
|
*/
|
|
94
110
|
export function sampleStartTicks(signal, sampleIndex, recordDurationTicks) {
|
|
95
111
|
assertGrid(signal, recordDurationTicks);
|
|
@@ -105,7 +121,12 @@ export function sampleStartTicks(signal, sampleIndex, recordDurationTicks) {
|
|
|
105
121
|
return quotient;
|
|
106
122
|
return numerator > 0n ? quotient + 1n : quotient;
|
|
107
123
|
}
|
|
108
|
-
/** `sampleStartTicks` in seconds, for display. Compare ticks, not this.
|
|
124
|
+
/** `sampleStartTicks` in seconds, for display. Compare ticks, not this. *
|
|
125
|
+
* @deprecated Renamed to `gridSampleStartSeconds` in 0.3.0. The behaviour is unchanged — only the
|
|
126
|
+
* name, which never said which of two different quantities it returns. For a file that may
|
|
127
|
+
* have gaps, use `sampleAt` / `sampleStartTicksOf` / `sampleStartSecondsOf`, which take the
|
|
128
|
+
* recording and can therefore see one.
|
|
129
|
+
*/
|
|
109
130
|
export function sampleStartSeconds(signal, sampleIndex, recordDurationTicks) {
|
|
110
131
|
const ticks = sampleStartTicks(signal, sampleIndex, recordDurationTicks);
|
|
111
132
|
const whole = ticks / TICKS_PER_SECOND;
|
package/dist/sample-grid.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sample-grid.js","sourceRoot":"","sources":["../src/sample-grid.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"sample-grid.js","sourceRoot":"","sources":["../src/sample-grid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhD,SAAS,UAAU,CAAC,MAAiB,EAAE,mBAA2B;IAChE,IAAI,MAAM,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAClC,MAAM,IAAI,UAAU,CAClB,UAAU,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,+BAA+B;YACpF,uFAAuF;YACvF,2CAA2C,CAC9C,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,gBAAgB,IAAI,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,UAAU,CAClB,UAAU,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa;YAClE,GAAG,MAAM,CAAC,gBAAgB,0DAA0D;YACpF,6DAA6D,CAChE,CAAC;IACJ,CAAC;IACD,IAAI,mBAAmB,IAAI,EAAE,EAAE,CAAC;QAC9B,MAAM,IAAI,UAAU,CAClB,yFAAyF;YACvF,oFAAoF;YACpF,2CAA2C,CAC9C,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAiB,EACjB,OAAe,EACf,mBAA2B;IAE3B,UAAU,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAExC,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAClD,+FAA+F;IAC/F,0FAA0F;IAC1F,sDAAsD;IACtD,MAAM,SAAS,GAAG,KAAK,GAAG,SAAS,CAAC;IACpC,IAAI,KAAK,GAAG,SAAS,GAAG,mBAAmB,CAAC;IAC5C,IAAI,SAAS,GAAG,mBAAmB,KAAK,EAAE,IAAI,SAAS,GAAG,EAAE;QAAE,KAAK,IAAI,EAAE,CAAC;IAE1E,MAAM,WAAW,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,SAAS,GAAG,EAAE,CAAC,GAAG,SAAS,CAAC;IAC3F,MAAM,kBAAkB,GAAG,KAAK,GAAG,WAAW,GAAG,SAAS,CAAC;IAE3D,OAAO;QACL,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC;QAC1B,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC;QAChC,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAiB,EACjB,WAAmB,EACnB,mBAA2B;IAE3B,UAAU,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IACxC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,UAAU,CAClB,oEAAoE,WAAW,GAAG,CACnF,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,mBAAmB,CAAC;IAC5D,MAAM,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IACvC,6FAA6F;IAC7F,6DAA6D;IAC7D,IAAI,SAAS,GAAG,SAAS,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC;IAClD,OAAO,SAAS,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;AACnD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAiB,EACjB,WAAmB,EACnB,mBAA2B;IAE3B,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,mBAAmB,CAAC,CAAC;IACzE,MAAM,KAAK,GAAG,KAAK,GAAG,gBAAgB,CAAC;IACvC,MAAM,SAAS,GAAG,KAAK,GAAG,gBAAgB,CAAC;IAC3C,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;AACtE,CAAC"}
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Annotations as text.
|
|
3
|
+
*
|
|
4
|
+
* Layer 7, and pure. `formatHeader` does this for the header and `formatDiagnostics` for problems;
|
|
5
|
+
* this is the third, and the one a hypnogram or an event list actually needs.
|
|
6
|
+
*
|
|
7
|
+
* The clock is built from `onsetTicksFromFirstRecord` by integer division, never from the float
|
|
8
|
+
* seconds. That is not fussiness: `onsetSecondsFromFirstRecord` is a float64 produced by dividing
|
|
9
|
+
* an exact tick count by 10,000,000, so an onset written `+1.0000001` can print a millisecond
|
|
10
|
+
* field that is off by one — and an event list is exactly where someone reads a number off the
|
|
11
|
+
* screen and types it into something else.
|
|
12
|
+
*
|
|
13
|
+
* A NEGATIVE onset is legal and prints as one. EDF+ measures onsets from the header start time
|
|
14
|
+
* and a recording may begin after its first annotation, so `-00:00:01.500` is a real thing a real
|
|
15
|
+
* file says. Clamping it to zero would silently move an event.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { TICKS_PER_SECOND } from './constants.js';
|
|
19
|
+
import type { EdfAnnotation } from './types.js';
|
|
20
|
+
|
|
21
|
+
export interface FormatAnnotationsOptions {
|
|
22
|
+
/** Rows to print. Defaults to every annotation; the count of the rest is always stated. */
|
|
23
|
+
readonly maxItems?: number;
|
|
24
|
+
/** Include the `description@@channel` label EDF+ allows. Off by default: most files have none. */
|
|
25
|
+
readonly includeChannel?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const TICKS_PER_MILLISECOND = TICKS_PER_SECOND / 1000n;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* `hh:mm:ss.mmm`, from exact ticks.
|
|
32
|
+
*
|
|
33
|
+
* Truncates to the millisecond rather than rounding, so the printed time never names an instant
|
|
34
|
+
* later than the event. Hours are not wrapped at 24 — a 30-hour recording is a real thing and
|
|
35
|
+
* `30:12:00.000` is more useful than `06:12:00.000` on day two.
|
|
36
|
+
*/
|
|
37
|
+
function clock(ticks: bigint): string {
|
|
38
|
+
const negative = ticks < 0n;
|
|
39
|
+
const absolute = negative ? -ticks : ticks;
|
|
40
|
+
|
|
41
|
+
const totalMilliseconds = absolute / TICKS_PER_MILLISECOND;
|
|
42
|
+
const milliseconds = totalMilliseconds % 1000n;
|
|
43
|
+
const totalSeconds = totalMilliseconds / 1000n;
|
|
44
|
+
const seconds = totalSeconds % 60n;
|
|
45
|
+
const totalMinutes = totalSeconds / 60n;
|
|
46
|
+
const minutes = totalMinutes % 60n;
|
|
47
|
+
const hours = totalMinutes / 60n;
|
|
48
|
+
|
|
49
|
+
const pad = (value: bigint, width: number): string => String(value).padStart(width, '0');
|
|
50
|
+
return (
|
|
51
|
+
`${negative ? '-' : ''}${pad(hours, 2)}:${pad(minutes, 2)}:${pad(seconds, 2)}` +
|
|
52
|
+
`.${pad(milliseconds, 3)}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** A duration in seconds, or blank. `undefined` and an explicit `0` are the same instant. */
|
|
57
|
+
function duration(annotation: EdfAnnotation): string {
|
|
58
|
+
const ticks = annotation.durationTicks;
|
|
59
|
+
if (ticks === undefined || ticks === 0n) return '';
|
|
60
|
+
return `${clock(ticks)}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A multi-line listing, one annotation per line, in the order given.
|
|
65
|
+
*
|
|
66
|
+
* Returns `''` for an empty list rather than a blank line, so it concatenates into a larger
|
|
67
|
+
* report cleanly — the same rule `formatDiagnostics` follows.
|
|
68
|
+
*
|
|
69
|
+
* The order is the caller's. `readAnnotations` already returns them sorted by onset, and
|
|
70
|
+
* re-sorting here would quietly discard a deliberate `filterAnnotationsByText` ordering.
|
|
71
|
+
*/
|
|
72
|
+
export function formatAnnotations(
|
|
73
|
+
annotations: readonly EdfAnnotation[],
|
|
74
|
+
options?: FormatAnnotationsOptions,
|
|
75
|
+
): string {
|
|
76
|
+
if (annotations.length === 0) return '';
|
|
77
|
+
|
|
78
|
+
const limit =
|
|
79
|
+
options?.maxItems === undefined || !Number.isFinite(options.maxItems)
|
|
80
|
+
? annotations.length
|
|
81
|
+
: Math.max(0, Math.min(annotations.length, Math.floor(options.maxItems)));
|
|
82
|
+
|
|
83
|
+
const rows: string[] = [];
|
|
84
|
+
for (let i = 0; i < limit; i += 1) {
|
|
85
|
+
const annotation = annotations[i];
|
|
86
|
+
if (annotation === undefined) continue;
|
|
87
|
+
const parts = [
|
|
88
|
+
clock(annotation.onsetTicksFromFirstRecord),
|
|
89
|
+
duration(annotation).padEnd(12),
|
|
90
|
+
annotation.text,
|
|
91
|
+
];
|
|
92
|
+
if (options?.includeChannel === true && annotation.channelLabel !== undefined) {
|
|
93
|
+
parts.push(`@@${annotation.channelLabel}`);
|
|
94
|
+
}
|
|
95
|
+
rows.push(parts.join(' ').trimEnd());
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const hidden = annotations.length - limit;
|
|
99
|
+
// Always stated. A truncated listing that does not say so reads as a complete one.
|
|
100
|
+
if (hidden > 0) rows.push(`... and ${hidden} more`);
|
|
101
|
+
|
|
102
|
+
return rows.join('\n');
|
|
103
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -186,6 +186,8 @@ export {
|
|
|
186
186
|
readEnvelopeAtResolution,
|
|
187
187
|
toPhysicalEnvelope,
|
|
188
188
|
} from './envelope.js';
|
|
189
|
+
export type { FormatAnnotationsOptions } from './format-annotations.js';
|
|
190
|
+
export { formatAnnotations } from './format-annotations.js';
|
|
189
191
|
export { formatHeader } from './format-header.js';
|
|
190
192
|
export { inspectEdf } from './inspect.js';
|
|
191
193
|
export { openEdf, readAnnotations, readRecords, readWindow } from './recording.js';
|
package/src/sample-grid.ts
CHANGED
|
@@ -28,10 +28,16 @@
|
|
|
28
28
|
* count, and are not being modest about it: the information is not in their arguments.
|
|
29
29
|
*
|
|
30
30
|
* So the contract is stated rather than guessed at. For a file that may be discontinuous, use
|
|
31
|
-
* `
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
31
|
+
* `sampleAt` / `sampleStartTicksOf` / `sampleStartSecondsOf` from `sample-locate.ts`, which take
|
|
32
|
+
* the recording and can therefore see a gap. `contiguityOf(index)` answers which regime you are
|
|
33
|
+
* in. On a contiguous file — the common case, and every plain EDF or EDF+C — these are exact and
|
|
34
|
+
* are what you want.
|
|
35
|
+
*
|
|
36
|
+
* RENAMED IN 0.3.0. These three keep their behaviour and lose their misleading names: they become
|
|
37
|
+
* `gridSampleIndexAt`, `gridSampleStartTicks` and `gridSampleStartSeconds`. The `grid` prefix is
|
|
38
|
+
* the whole fix — the functions were never wrong, the names simply did not say which of two
|
|
39
|
+
* different quantities they returned, and six releases of this project were spent on exactly that
|
|
40
|
+
* confusion in other places. Nothing about the arithmetic changes.
|
|
35
41
|
*/
|
|
36
42
|
|
|
37
43
|
import { TICKS_PER_SECOND } from './constants.js';
|
|
@@ -68,6 +74,11 @@ function assertGrid(signal: EdfSignal, recordDurationTicks: bigint): void {
|
|
|
68
74
|
* Floor, not round: a sample covers the half-open interval from its own start to the next one's,
|
|
69
75
|
* so the sample "at" a time is the one whose interval contains it. Rounding would return the
|
|
70
76
|
* NEXT sample for anything past the halfway point, which puts a window boundary one sample late.
|
|
77
|
+
*
|
|
78
|
+
* @deprecated Renamed to `gridSampleIndexAt` in 0.3.0. The behaviour is unchanged — only the
|
|
79
|
+
* name, which never said which of two different quantities it returns. For a file that may
|
|
80
|
+
* have gaps, use `sampleAt` / `sampleStartTicksOf` / `sampleStartSecondsOf`, which take the
|
|
81
|
+
* recording and can therefore see one.
|
|
71
82
|
*/
|
|
72
83
|
export function sampleIndexAt(
|
|
73
84
|
signal: EdfSignal,
|
|
@@ -107,6 +118,11 @@ export function sampleIndexAt(
|
|
|
107
118
|
* edfcore has. Truncating would return 23,437 — a tick that lies inside sample 0 — so
|
|
108
119
|
* `sampleIndexAt` would send it straight back to the previous sample. Taking the first whole
|
|
109
120
|
* tick at or after the exact start keeps the two functions inverse for every index.
|
|
121
|
+
*
|
|
122
|
+
* @deprecated Renamed to `gridSampleStartTicks` in 0.3.0. The behaviour is unchanged — only the
|
|
123
|
+
* name, which never said which of two different quantities it returns. For a file that may
|
|
124
|
+
* have gaps, use `sampleAt` / `sampleStartTicksOf` / `sampleStartSecondsOf`, which take the
|
|
125
|
+
* recording and can therefore see one.
|
|
110
126
|
*/
|
|
111
127
|
export function sampleStartTicks(
|
|
112
128
|
signal: EdfSignal,
|
|
@@ -128,7 +144,12 @@ export function sampleStartTicks(
|
|
|
128
144
|
return numerator > 0n ? quotient + 1n : quotient;
|
|
129
145
|
}
|
|
130
146
|
|
|
131
|
-
/** `sampleStartTicks` in seconds, for display. Compare ticks, not this.
|
|
147
|
+
/** `sampleStartTicks` in seconds, for display. Compare ticks, not this. *
|
|
148
|
+
* @deprecated Renamed to `gridSampleStartSeconds` in 0.3.0. The behaviour is unchanged — only the
|
|
149
|
+
* name, which never said which of two different quantities it returns. For a file that may
|
|
150
|
+
* have gaps, use `sampleAt` / `sampleStartTicksOf` / `sampleStartSecondsOf`, which take the
|
|
151
|
+
* recording and can therefore see one.
|
|
152
|
+
*/
|
|
132
153
|
export function sampleStartSeconds(
|
|
133
154
|
signal: EdfSignal,
|
|
134
155
|
sampleIndex: number,
|