edfcore 0.3.31 → 0.3.32
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 +20 -0
- package/dist/biosemi.d.ts.map +1 -1
- package/dist/biosemi.js +16 -2
- package/dist/biosemi.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/package.json +1 -1
- package/src/biosemi.ts +16 -2
- package/src/constants.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,26 @@ 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.32
|
|
10
|
+
|
|
11
|
+
- **Fixed** `readTriggers` timestamping a Status sample by TRUNCATING to a whole tick, so an
|
|
12
|
+
event's own reported time mapped back to the previous sample. `sampleAt(event.seconds)` returned
|
|
13
|
+
sample **100** for an event `readTriggers` called sample 101.
|
|
14
|
+
- A sample boundary need not fall on a whole tick — 10^7 / 512 is 19531.25 — and edfcore's two
|
|
15
|
+
sample-start functions round UP for the reason `gridSampleStartTicks` gives in its own comment:
|
|
16
|
+
*"Truncating would return 23,437 — a tick that lies inside sample 0 — so `gridSampleIndexAt`
|
|
17
|
+
would send it straight back to the previous sample."* `readTriggers` was the one function that
|
|
18
|
+
truncated, and three boundaries in four are affected at any power-of-two rate BioSemi actually
|
|
19
|
+
uses.
|
|
20
|
+
- The ERP consequence is the sharp one. Align a window to the stimulus with
|
|
21
|
+
`sampleStartSecondsOf(rec, status, 101)` and the event sat one tick before the window's left
|
|
22
|
+
edge, so the onset came back as sample **102** — 2 ms at 512 Hz, on the one number an evoked-
|
|
23
|
+
potential pipeline reads. A one-sample-wide trigger at that edge disappeared entirely, which
|
|
24
|
+
contradicts this function's own rule that the first in-window sample always produces an event.
|
|
25
|
+
- Now `ceilDiv`, the same rule as `gridSampleStartTicks` and `sampleStartTicksOf`. An event's tick
|
|
26
|
+
is the first whole tick at or after the sample's true start, so `sampleAt`, `sampleStartTicksOf`,
|
|
27
|
+
a window bound and `readTriggers` all name the same sample.
|
|
28
|
+
|
|
9
29
|
## 0.3.31
|
|
10
30
|
|
|
11
31
|
**Two ways patient identification reached the output with `--patient` absent.** Both produced text
|
package/dist/biosemi.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"biosemi.d.ts","sourceRoot":"","sources":["../src/biosemi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EAEZ,SAAS,EACT,aAAa,EACb,eAAe,EACf,WAAW,EAEX,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAWpB;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAOxE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAS9D;AAqBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,YAAY,EACvB,SAAS,EAAE,gBAAgB,EAC3B,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"biosemi.d.ts","sourceRoot":"","sources":["../src/biosemi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EAEZ,SAAS,EACT,aAAa,EACb,eAAe,EACf,WAAW,EAEX,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAWpB;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAOxE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAS9D;AAqBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,YAAY,EACvB,SAAS,EAAE,gBAAgB,EAC3B,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CA+GrC"}
|
package/dist/biosemi.js
CHANGED
|
@@ -18,7 +18,7 @@ import { decodeDigitalCounted } from './decode/digital.js';
|
|
|
18
18
|
import { readRecordBytes } from './io/read.js';
|
|
19
19
|
import { scanChunkRecords } from './record-index.js';
|
|
20
20
|
import { gapBefore } from './recording.js';
|
|
21
|
-
import { secondsToTicks, ticksToSeconds } from './tal/ticks.js';
|
|
21
|
+
import { ceilDiv, secondsToTicks, ticksToSeconds } from './tal/ticks.js';
|
|
22
22
|
import { resolveTimeWindow } from './time/window.js';
|
|
23
23
|
/** BioSemi's own label for the channel. Matched case-insensitively after trimming. */
|
|
24
24
|
const STATUS_LABEL = 'status';
|
|
@@ -169,9 +169,23 @@ export async function readTriggers(recording, selection, options) {
|
|
|
169
169
|
const word = decodeStatusWord(decoded.digital[r * samplesPerRecord + s]);
|
|
170
170
|
const changed = previous === undefined || word.trigger !== previous;
|
|
171
171
|
previous = word.trigger;
|
|
172
|
+
// CEILING, the same rule `gridSampleStartTicks` and `sampleStartTicksOf` use, and for
|
|
173
|
+
// the reason the first of those states in its own comment: a sample boundary need not
|
|
174
|
+
// fall on a whole tick, and truncating returns a tick that lies inside the PREVIOUS
|
|
175
|
+
// sample. 10^7 / 512 is 19531.25, so three boundaries in four are affected on an
|
|
176
|
+
// ordinary 512 Hz BioSemi file.
|
|
177
|
+
//
|
|
178
|
+
// Truncating made an event's own reported time round-trip to the wrong sample —
|
|
179
|
+
// `sampleAt(event.seconds)` returned 100 for an event readTriggers called sample 101 —
|
|
180
|
+
// and a window aligned to the stimulus with `sampleStartSecondsOf` reported the onset a
|
|
181
|
+
// sample late, which at 512 Hz is 2 ms on the one number an ERP pipeline reads. A
|
|
182
|
+
// one-sample-wide trigger at the window's left edge disappeared entirely, contradicting
|
|
183
|
+
// this function's own rule that the first in-window sample always produces an event
|
|
184
|
+
// (fixed in 0.3.32).
|
|
185
|
+
//
|
|
172
186
|
// A zero record duration puts every sample of the record at its start instant.
|
|
173
187
|
const ticks = durationTicks > 0n && samplesPerRecord > 0
|
|
174
|
-
? recordStartTicks + (BigInt(s) * durationTicks
|
|
188
|
+
? recordStartTicks + ceilDiv(BigInt(s) * durationTicks, BigInt(samplesPerRecord))
|
|
175
189
|
: recordStartTicks;
|
|
176
190
|
if (ticks < windowStartTicks || ticks >= windowEndTicks)
|
|
177
191
|
continue;
|
package/dist/biosemi.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"biosemi.js","sourceRoot":"","sources":["../src/biosemi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"biosemi.js","sourceRoot":"","sources":["../src/biosemi.ts"],"names":[],"mappings":"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"}
|
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.32";
|
|
113
113
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.js
CHANGED
package/package.json
CHANGED
package/src/biosemi.ts
CHANGED
|
@@ -19,7 +19,7 @@ import { decodeDigitalCounted } from './decode/digital.js';
|
|
|
19
19
|
import { readRecordBytes } from './io/read.js';
|
|
20
20
|
import { scanChunkRecords } from './record-index.js';
|
|
21
21
|
import { gapBefore } from './recording.js';
|
|
22
|
-
import { secondsToTicks, ticksToSeconds } from './tal/ticks.js';
|
|
22
|
+
import { ceilDiv, secondsToTicks, ticksToSeconds } from './tal/ticks.js';
|
|
23
23
|
import { resolveTimeWindow } from './time/window.js';
|
|
24
24
|
import type {
|
|
25
25
|
EdfHeader,
|
|
@@ -207,10 +207,24 @@ export async function readTriggers(
|
|
|
207
207
|
const changed = previous === undefined || word.trigger !== previous;
|
|
208
208
|
previous = word.trigger;
|
|
209
209
|
|
|
210
|
+
// CEILING, the same rule `gridSampleStartTicks` and `sampleStartTicksOf` use, and for
|
|
211
|
+
// the reason the first of those states in its own comment: a sample boundary need not
|
|
212
|
+
// fall on a whole tick, and truncating returns a tick that lies inside the PREVIOUS
|
|
213
|
+
// sample. 10^7 / 512 is 19531.25, so three boundaries in four are affected on an
|
|
214
|
+
// ordinary 512 Hz BioSemi file.
|
|
215
|
+
//
|
|
216
|
+
// Truncating made an event's own reported time round-trip to the wrong sample —
|
|
217
|
+
// `sampleAt(event.seconds)` returned 100 for an event readTriggers called sample 101 —
|
|
218
|
+
// and a window aligned to the stimulus with `sampleStartSecondsOf` reported the onset a
|
|
219
|
+
// sample late, which at 512 Hz is 2 ms on the one number an ERP pipeline reads. A
|
|
220
|
+
// one-sample-wide trigger at the window's left edge disappeared entirely, contradicting
|
|
221
|
+
// this function's own rule that the first in-window sample always produces an event
|
|
222
|
+
// (fixed in 0.3.32).
|
|
223
|
+
//
|
|
210
224
|
// A zero record duration puts every sample of the record at its start instant.
|
|
211
225
|
const ticks =
|
|
212
226
|
durationTicks > 0n && samplesPerRecord > 0
|
|
213
|
-
? recordStartTicks + (BigInt(s) * durationTicks
|
|
227
|
+
? recordStartTicks + ceilDiv(BigInt(s) * durationTicks, BigInt(samplesPerRecord))
|
|
214
228
|
: recordStartTicks;
|
|
215
229
|
|
|
216
230
|
if (ticks < windowStartTicks || ticks >= windowEndTicks) continue;
|
package/src/constants.ts
CHANGED