edfcore 0.3.56 → 0.3.58
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 +45 -0
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/recording.d.ts.map +1 -1
- package/dist/recording.js +27 -7
- package/dist/recording.js.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +1 -1
- package/src/recording.ts +37 -7
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,51 @@ 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.3.58
|
|
10
|
+
|
|
11
|
+
- **Fixed** the `EdfAnnotation` table in `api-types.md`, which put `onsetTicks` on the wrong axis
|
|
12
|
+
and then told readers to compare event times with it and nothing else.
|
|
13
|
+
- The table said `onsetTicks` is "exact, in 100 ns units, on the same axis as the rebased value".
|
|
14
|
+
It is not: it is the number the file wrote, on the **header's** timebase. `src/types.ts` says so
|
|
15
|
+
in the docblock that generates the published `.d.ts`, and calls it "the wrong one for comparing
|
|
16
|
+
an annotation against a window".
|
|
17
|
+
- The rebased field, `onsetTicksFromFirstRecord`, was missing from the table altogether —
|
|
18
|
+
thirteen rows for a fourteen-field interface. So the page named the wrong field as the exact
|
|
19
|
+
one and omitted the right one, in the same three lines.
|
|
20
|
+
- A reader who followed it compared `onsetTicks` against `chunk.startTicks`, `segment.startTicks`
|
|
21
|
+
or a `readWindow` bound — all of which the same page puts on the `t = 0 = start of record 0`
|
|
22
|
+
axis — and every event landed up to a second late, with nothing to indicate it. On a file whose
|
|
23
|
+
record 0 starts 0.25 s in, an event written `+1.25` has `onsetTicks` 12500000 and
|
|
24
|
+
`onsetTicksFromFirstRecord` 10000000, which is exactly `chunk.startTicks` for record 1.
|
|
25
|
+
- `annotations.md` had it right in two places, so the package's two reference pages disagreed.
|
|
26
|
+
- `tests/integration/annotation-fields-doc.test.ts` reads the field list off a decoded annotation
|
|
27
|
+
rather than from a list written down beside it, so a field added to the interface fails the suite
|
|
28
|
+
until the table lists it. It builds a file whose two onset axes genuinely differ, which is what
|
|
29
|
+
makes the wording load-bearing rather than decorative.
|
|
30
|
+
|
|
31
|
+
## 0.3.57
|
|
32
|
+
|
|
33
|
+
- **Fixed** the zero-record chunk's start time, which was read off a segment on the wrong axis and
|
|
34
|
+
only for a record a segment BEGINS at. Both halves were introduced by 0.3.38's fix for the same
|
|
35
|
+
field.
|
|
36
|
+
- `segment.startTicks` is **rebased** — `buildSegmentation` stores `absoluteOnset - originTicks`
|
|
37
|
+
with `originTicks = timeline.startOffsetTicks` — while `readChunk` consumed it as a header-axis
|
|
38
|
+
value and subtracted the offset a second time. On an EDF+ file whose record 0 begins part-way
|
|
39
|
+
into a second, which is exactly what record 0's timekeeping TAL is for,
|
|
40
|
+
`readRecords({ start: 0, count: 0 })` reported **-0.25 s**: before the instant that defines
|
|
41
|
+
`t = 0`. At a segment boundary it reported 99.75 s while carrying a gap ending at 100 s — the
|
|
42
|
+
self-contradiction 0.3.38 existed to remove, reintroduced one line away by its own fix.
|
|
43
|
+
- The lookup matched only a segment's first record, so a mid-segment record fell through to the
|
|
44
|
+
nominal grid, which knows nothing about gaps: `{ start: 5, count: 0 }` answered **5 s** where
|
|
45
|
+
`{ start: 5, count: 1 }` answered **101 s**, for the same record of the same file.
|
|
46
|
+
- The helper now reads the segment that CONTAINS the record and adds the origin back, so both
|
|
47
|
+
forms answer identically at every record — boundary or not, offset or not.
|
|
48
|
+
- The 0.3.38 test asserted `gap.endTicks === chunk.startTicks + startOffsetTicks`. Both values are
|
|
49
|
+
on the rebased axis, so the real invariant is equality; the extra term was only ever right
|
|
50
|
+
because that fixture's offset is zero. A guard that states a relation which holds only at zero
|
|
51
|
+
would have accepted this bug back, so it now states the relation itself, and the new test gives
|
|
52
|
+
the file an offset.
|
|
53
|
+
|
|
9
54
|
## 0.3.56
|
|
10
55
|
|
|
11
56
|
- **Fixed** `trimToWindow` dropping the sample whose own start time the window was aligned to.
|
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.3.
|
|
112
|
+
export declare const VERSION = "0.3.58";
|
|
113
113
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.js
CHANGED
package/dist/recording.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recording.d.ts","sourceRoot":"","sources":["../src/recording.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAUH,OAAO,KAAK,EACV,UAAU,EACV,wBAAwB,EACxB,oBAAoB,EACpB,QAAQ,EAER,MAAM,EACN,SAAS,EACT,cAAc,EACd,YAAY,EACZ,SAAS,
|
|
1
|
+
{"version":3,"file":"recording.d.ts","sourceRoot":"","sources":["../src/recording.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAUH,OAAO,KAAK,EACV,UAAU,EACV,wBAAwB,EACxB,oBAAoB,EACpB,QAAQ,EAER,MAAM,EACN,SAAS,EACT,cAAc,EACd,YAAY,EACZ,SAAS,EAET,WAAW,EACX,WAAW,EACX,WAAW,EACX,eAAe,EACf,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAI9F;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,EACjB,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,SAAS,SAAS,EAAE,CA2BtB;AA0CD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQxF;AAoFD,kGAAkG;AAClG,wBAAsB,WAAW,CAC/B,SAAS,EAAE,YAAY,EACvB,SAAS,EAAE,eAAe,EAC1B,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,QAAQ,CAAC,CAEnB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,UAAU,CAC9B,SAAS,EAAE,YAAY,EACvB,SAAS,EAAE,eAAe,EAC1B,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,SAAS,QAAQ,EAAE,CAAC,CA2B9B;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,wBAAwB,GAAG,WAAW,GAC/C,OAAO,CAAC,oBAAoB,CAAC,CAU/B"}
|
package/dist/recording.js
CHANGED
|
@@ -73,14 +73,34 @@ export function resolveSignals(header, signalIndices) {
|
|
|
73
73
|
return signals;
|
|
74
74
|
}
|
|
75
75
|
/**
|
|
76
|
-
* The header-axis onset of
|
|
76
|
+
* The header-axis onset of `recordStart`, when a scanned index knows where that record is.
|
|
77
77
|
*
|
|
78
|
-
* `undefined` for a probed index,
|
|
79
|
-
*
|
|
78
|
+
* `undefined` for a probed index, where nothing better than the nominal grid is available and the
|
|
79
|
+
* caller falls back to it.
|
|
80
|
+
*
|
|
81
|
+
* Two things here are easy to get wrong, and 0.3.38 got both wrong at once.
|
|
82
|
+
*
|
|
83
|
+
* `segment.startTicks` is REBASED — `buildSegmentation` stores `absoluteOnset - originTicks` with
|
|
84
|
+
* `originTicks = timeline.startOffsetTicks` — while the caller consumes this as a header-axis
|
|
85
|
+
* value and subtracts the offset again. On a file whose record 0 begins part-way into a second,
|
|
86
|
+
* which is exactly what record 0's timekeeping TAL is for, a zero-record chunk at record 0 came
|
|
87
|
+
* back at **-0.25 s**: before the instant that defines t = 0. At a segment boundary it reported
|
|
88
|
+
* 99.75 s while carrying a gap that ends at 100 s — the self-contradiction 0.3.38 existed to
|
|
89
|
+
* remove, reintroduced one line away by the fix for it.
|
|
90
|
+
*
|
|
91
|
+
* And it matched only a segment's FIRST record, so a mid-segment record fell through to the
|
|
92
|
+
* nominal grid, which knows nothing about gaps: `readRecords({ start: 5, count: 0 })` said 5 s
|
|
93
|
+
* where `{ start: 5, count: 1 }` said 101 s, for the same record of the same file. Reading the
|
|
94
|
+
* segment that CONTAINS the record answers both the same way (fixed in 0.3.57).
|
|
80
95
|
*/
|
|
81
|
-
function
|
|
82
|
-
const segment = index.segments?.find((candidate) => candidate.records.start
|
|
83
|
-
|
|
96
|
+
function recordOnsetTicks(index, timeline, recordStart, durationTicks) {
|
|
97
|
+
const segment = index.segments?.find((candidate) => candidate.records.start <= recordStart &&
|
|
98
|
+
recordStart < candidate.records.start + candidate.records.count);
|
|
99
|
+
if (segment === undefined)
|
|
100
|
+
return undefined;
|
|
101
|
+
return (timeline.startOffsetTicks +
|
|
102
|
+
segment.startTicks +
|
|
103
|
+
BigInt(recordStart - segment.records.start) * durationTicks);
|
|
84
104
|
}
|
|
85
105
|
/**
|
|
86
106
|
* The gap immediately before `recordStart`, when the index knows where the gaps are.
|
|
@@ -137,7 +157,7 @@ async function readChunk(recording, records, signalIndices, options) {
|
|
|
137
157
|
//
|
|
138
158
|
// A scanned index knows where that record is, and `gapBefore` below already reads it from the
|
|
139
159
|
// same place. Consulting it costs nothing and makes the two fields agree (fixed in 0.3.38).
|
|
140
|
-
const nominalFirstTicks =
|
|
160
|
+
const nominalFirstTicks = recordOnsetTicks(index, timeline, records.start, durationTicks) ??
|
|
141
161
|
timeline.startOffsetTicks + BigInt(records.start) * durationTicks;
|
|
142
162
|
const firstOnsetTicks = onsets[0] ?? nominalFirstTicks;
|
|
143
163
|
const lastOnsetTicks = records.count > 0 ? (onsets[records.count - 1] ?? firstOnsetTicks) : firstOnsetTicks;
|
package/dist/recording.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recording.js","sourceRoot":"","sources":["../src/recording.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"recording.js","sourceRoot":"","sources":["../src/recording.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
package/src/recording.ts
CHANGED
|
@@ -40,6 +40,7 @@ import type {
|
|
|
40
40
|
EdfRecordIndex,
|
|
41
41
|
EdfRecording,
|
|
42
42
|
EdfSignal,
|
|
43
|
+
EdfTimeline,
|
|
43
44
|
OpenOptions,
|
|
44
45
|
ReadOptions,
|
|
45
46
|
RecordRange,
|
|
@@ -102,14 +103,43 @@ export function resolveSignals(
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
/**
|
|
105
|
-
* The header-axis onset of
|
|
106
|
+
* The header-axis onset of `recordStart`, when a scanned index knows where that record is.
|
|
106
107
|
*
|
|
107
|
-
* `undefined` for a probed index,
|
|
108
|
-
*
|
|
108
|
+
* `undefined` for a probed index, where nothing better than the nominal grid is available and the
|
|
109
|
+
* caller falls back to it.
|
|
110
|
+
*
|
|
111
|
+
* Two things here are easy to get wrong, and 0.3.38 got both wrong at once.
|
|
112
|
+
*
|
|
113
|
+
* `segment.startTicks` is REBASED — `buildSegmentation` stores `absoluteOnset - originTicks` with
|
|
114
|
+
* `originTicks = timeline.startOffsetTicks` — while the caller consumes this as a header-axis
|
|
115
|
+
* value and subtracts the offset again. On a file whose record 0 begins part-way into a second,
|
|
116
|
+
* which is exactly what record 0's timekeeping TAL is for, a zero-record chunk at record 0 came
|
|
117
|
+
* back at **-0.25 s**: before the instant that defines t = 0. At a segment boundary it reported
|
|
118
|
+
* 99.75 s while carrying a gap that ends at 100 s — the self-contradiction 0.3.38 existed to
|
|
119
|
+
* remove, reintroduced one line away by the fix for it.
|
|
120
|
+
*
|
|
121
|
+
* And it matched only a segment's FIRST record, so a mid-segment record fell through to the
|
|
122
|
+
* nominal grid, which knows nothing about gaps: `readRecords({ start: 5, count: 0 })` said 5 s
|
|
123
|
+
* where `{ start: 5, count: 1 }` said 101 s, for the same record of the same file. Reading the
|
|
124
|
+
* segment that CONTAINS the record answers both the same way (fixed in 0.3.57).
|
|
109
125
|
*/
|
|
110
|
-
function
|
|
111
|
-
|
|
112
|
-
|
|
126
|
+
function recordOnsetTicks(
|
|
127
|
+
index: EdfRecordIndex,
|
|
128
|
+
timeline: EdfTimeline,
|
|
129
|
+
recordStart: number,
|
|
130
|
+
durationTicks: bigint,
|
|
131
|
+
): bigint | undefined {
|
|
132
|
+
const segment = index.segments?.find(
|
|
133
|
+
(candidate) =>
|
|
134
|
+
candidate.records.start <= recordStart &&
|
|
135
|
+
recordStart < candidate.records.start + candidate.records.count,
|
|
136
|
+
);
|
|
137
|
+
if (segment === undefined) return undefined;
|
|
138
|
+
return (
|
|
139
|
+
timeline.startOffsetTicks +
|
|
140
|
+
segment.startTicks +
|
|
141
|
+
BigInt(recordStart - segment.records.start) * durationTicks
|
|
142
|
+
);
|
|
113
143
|
}
|
|
114
144
|
|
|
115
145
|
/**
|
|
@@ -175,7 +205,7 @@ async function readChunk(
|
|
|
175
205
|
// A scanned index knows where that record is, and `gapBefore` below already reads it from the
|
|
176
206
|
// same place. Consulting it costs nothing and makes the two fields agree (fixed in 0.3.38).
|
|
177
207
|
const nominalFirstTicks =
|
|
178
|
-
|
|
208
|
+
recordOnsetTicks(index, timeline, records.start, durationTicks) ??
|
|
179
209
|
timeline.startOffsetTicks + BigInt(records.start) * durationTicks;
|
|
180
210
|
const firstOnsetTicks = onsets[0] ?? nominalFirstTicks;
|
|
181
211
|
const lastOnsetTicks =
|