edfcore 0.6.25 → 0.6.27
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/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/header/lookup.d.ts.map +1 -1
- package/dist/header/lookup.js +46 -5
- package/dist/header/lookup.js.map +1 -1
- package/dist/header/variant.d.ts.map +1 -1
- package/dist/header/variant.js +36 -2
- package/dist/header/variant.js.map +1 -1
- package/docs/CHANGELOG.md +39 -0
- package/package.json +1 -1
- package/src/constants.ts +1 -1
- package/src/header/lookup.ts +50 -5
- package/src/header/variant.ts +37 -2
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.6.
|
|
112
|
+
export declare const VERSION = "0.6.27";
|
|
113
113
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../../src/header/lookup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE,CAGlF;
|
|
1
|
+
{"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../../src/header/lookup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE,CAGlF;AA6CD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAsCjF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAQpE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,GAC3C,SAAS,SAAS,EAAE,CAKtB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAEjE"}
|
package/dist/header/lookup.js
CHANGED
|
@@ -30,8 +30,43 @@ export function findSignals(header, label) {
|
|
|
30
30
|
const wanted = trimEdfField(label);
|
|
31
31
|
return Object.freeze(header.signals.filter((signal) => signal.label === wanted));
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Enough labels to recognise the file, few enough to read in a terminal.
|
|
35
|
+
*
|
|
36
|
+
* Every other listing this package prints is capped — 24 bytes of hex, 16 bytes of field evidence,
|
|
37
|
+
* twenty diagnostics — and this one was not, in the one message whose length grows with the file.
|
|
38
|
+
* A mistyped label on a 512-signal recording put five thousand characters on one line behind
|
|
39
|
+
* `edfcore: `, and `inspect.ts` names a 512-signal file as the realistic one. The full list is
|
|
40
|
+
* still on the error, as `availableLabels`, for a program that wants it.
|
|
41
|
+
*/
|
|
42
|
+
const LABELS_SHOWN = 12;
|
|
43
|
+
/** The whole label list, capped, saying how many it withheld — never silently truncated. */
|
|
44
|
+
function quoteLabels(header, first) {
|
|
45
|
+
const labels = header.signals.map((signal) => signal.label);
|
|
46
|
+
const ordered = first === undefined ? labels : [first, ...labels.filter((label) => label !== first)];
|
|
47
|
+
const shown = ordered.slice(0, LABELS_SHOWN).map((label) => JSON.stringify(label));
|
|
48
|
+
const hidden = ordered.length - shown.length;
|
|
49
|
+
return hidden > 0 ? `${shown.join(', ')}, and ${hidden} more` : shown.join(', ');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* `String.prototype.toLowerCase`, not `toLocaleLowerCase`: the second one folds `'I'` to a dotless
|
|
53
|
+
* `'ı'` under a Turkish locale, and this package's output is deterministic and locale-free.
|
|
54
|
+
*/
|
|
55
|
+
const foldCase = (text) => trimEdfField(text).toLowerCase().replace(/\s+/g, ' ');
|
|
56
|
+
/**
|
|
57
|
+
* The label that differs from the selector only in case or in internal spacing, if there is one.
|
|
58
|
+
*
|
|
59
|
+
* The module note above says why matching is exact and case-sensitive: `'Fp1'` and `'FP1'` are
|
|
60
|
+
* written by different systems and edfcore has no montage vocabulary to decide they are the same
|
|
61
|
+
* electrode. That is a decision about MATCHING, not a reason to make the reader work out what
|
|
62
|
+
* happened — the message said "is case-sensitive" while holding the label that proves it is what
|
|
63
|
+
* bit them.
|
|
64
|
+
*
|
|
65
|
+
* Naming it changes nothing about which signal is returned. It is still refused.
|
|
66
|
+
*/
|
|
67
|
+
function differsOnlyInCase(header, selector) {
|
|
68
|
+
const wanted = foldCase(selector);
|
|
69
|
+
return header.signals.find((signal) => foldCase(signal.label) === wanted)?.label;
|
|
35
70
|
}
|
|
36
71
|
/**
|
|
37
72
|
* One signal, by index or by label.
|
|
@@ -53,9 +88,15 @@ export function getSignal(header, selector) {
|
|
|
53
88
|
const matches = findSignals(header, selector);
|
|
54
89
|
const first = matches[0];
|
|
55
90
|
if (first === undefined) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
91
|
+
const near = differsOnlyInCase(header, selector);
|
|
92
|
+
throw new EdfChannelNotFoundError(`no signal is labelled ${JSON.stringify(trimEdfField(selector))} in this file. ` +
|
|
93
|
+
(near === undefined
|
|
94
|
+
? ''
|
|
95
|
+
: `This file has ${JSON.stringify(near)}, which differs only in case or spacing. `) +
|
|
96
|
+
`Labels, in signal order: ${quoteLabels(header, near)}. Matching is exact on the ` +
|
|
97
|
+
'trimmed label and is case-sensitive. Next: pass ' +
|
|
98
|
+
(near === undefined ? 'one of those labels' : `${JSON.stringify(near)}`) +
|
|
99
|
+
', or select by index.', { selector, availableLabels: header.signals.map((signal) => signal.label) });
|
|
59
100
|
}
|
|
60
101
|
if (matches.length === 1)
|
|
61
102
|
return first;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lookup.js","sourceRoot":"","sources":["../../src/header/lookup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjD;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACpC,OAAO,OAAO,KAAK,qBAAqB,IAAI,OAAO,KAAK,qBAAqB,CAAC;AAChF,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,WAAW,CAAC,MAAiB,EAAE,KAAa;IAC1D,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC;AACnF,CAAC;AAED,SAAS,WAAW,CAAC,MAAiB;
|
|
1
|
+
{"version":3,"file":"lookup.js","sourceRoot":"","sources":["../../src/header/lookup.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"variant.d.ts","sourceRoot":"","sources":["../../src/header/variant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAcH,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,6BAA6B,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAW9C;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,CAAC;AAEtC,8FAA8F;AAC9F,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAUhF;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,qFAAqF;IACrF,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,CAAC,GAAG,CAAC,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,YAAY,GAAG,eAAe,CAAC;IACpD;uFACmF;IACnF,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB;wBACoB;IACpB,QAAQ,CAAC,cAAc,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACvD,gEAAgE;IAChE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,kFAAkF;IAClF,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,UAAU,GAAG,OAAO,CAOnE;AAED,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,UAAU,GAAG,OAAO,CAMnE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAKhF;
|
|
1
|
+
{"version":3,"file":"variant.d.ts","sourceRoot":"","sources":["../../src/header/variant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAcH,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,6BAA6B,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAW9C;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,CAAC;AAEtC,8FAA8F;AAC9F,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAUhF;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,qFAAqF;IACrF,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,CAAC,GAAG,CAAC,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,YAAY,GAAG,eAAe,CAAC;IACpD;uFACmF;IACnF,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB;wBACoB;IACpB,QAAQ,CAAC,cAAc,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACvD,gEAAgE;IAChE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,kFAAkF;IAClF,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,UAAU,GAAG,OAAO,CAOnE;AAED,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,UAAU,GAAG,OAAO,CAMnE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAKhF;AA8CD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,cAAc,GAAG,cAAc,CAqG3F"}
|
package/dist/header/variant.js
CHANGED
|
@@ -70,6 +70,35 @@ export function reservedMarkerOf(reserved) {
|
|
|
70
70
|
}
|
|
71
71
|
return undefined;
|
|
72
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* The containers whose first bytes are already in hand when `NOT_AN_EDF_FILE` is thrown.
|
|
75
|
+
*
|
|
76
|
+
* That message's `Next:` clause has always said "a gzip, zip or vendor container has to be
|
|
77
|
+
* unpacked before edfcore sees it" — a menu, offered while holding the two bytes that say which
|
|
78
|
+
* one it is. `1f 8b` is a gzip and nothing else, and a reader who has just been handed the hex is
|
|
79
|
+
* being asked to look it up.
|
|
80
|
+
*
|
|
81
|
+
* Only the archive formats, and only by their magic number. Naming a container is a fact about the
|
|
82
|
+
* first bytes; guessing at a vendor's binary EEG format from the same two bytes would be a claim
|
|
83
|
+
* about the whole file, and the generic clause still covers those.
|
|
84
|
+
*/
|
|
85
|
+
const CONTAINERS = [
|
|
86
|
+
['a gzip', [0x1f, 0x8b]],
|
|
87
|
+
['a zip', [0x50, 0x4b, 0x03, 0x04]],
|
|
88
|
+
['an empty zip', [0x50, 0x4b, 0x05, 0x06]],
|
|
89
|
+
['a bzip2', [0x42, 0x5a, 0x68]],
|
|
90
|
+
['an xz', [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00]],
|
|
91
|
+
['a zstd', [0x28, 0xb5, 0x2f, 0xfd]],
|
|
92
|
+
['a 7-Zip archive', [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c]],
|
|
93
|
+
];
|
|
94
|
+
/** The container these bytes begin with, or `undefined` — which is most files. */
|
|
95
|
+
function containerAt(bytes) {
|
|
96
|
+
for (const [name, magic] of CONTAINERS) {
|
|
97
|
+
if (magic.every((byte, at) => bytes[at] === byte))
|
|
98
|
+
return name;
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
73
102
|
function markerFamily(marker) {
|
|
74
103
|
return marker === 'EDF+C' || marker === 'EDF+D' ? 'EDF' : 'BDF';
|
|
75
104
|
}
|
|
@@ -98,14 +127,19 @@ export function detectVariant(headerBytes, sink) {
|
|
|
98
127
|
const isEdf = !isBdf && isEdfVersionBlock(versionBytes);
|
|
99
128
|
if (!isBdf && !isEdf) {
|
|
100
129
|
const raw = readAsciiField(headerBytes, HEADER_FIELDS.version.offset, HEADER_FIELDS.version.length);
|
|
130
|
+
const container = containerAt(headerBytes);
|
|
101
131
|
throw fatalError({
|
|
102
132
|
code: 'NOT_AN_EDF_FILE',
|
|
103
133
|
message: `version field (8 bytes at offset ${HEADER_FIELDS.version.offset}) is ` +
|
|
104
134
|
`${JSON.stringify(raw)}, bytes ${hexBytes(versionBytes)}: this is neither EDF's ` +
|
|
105
135
|
`"0" followed by seven spaces nor BDF's 0xff followed by "BIOSEMI". ` +
|
|
136
|
+
(container === undefined ? '' : `Those first bytes are ${container}. `) +
|
|
106
137
|
'EDF specification, header record bytes 0-7 (version of this data format). ' +
|
|
107
|
-
|
|
108
|
-
|
|
138
|
+
(container === undefined
|
|
139
|
+
? 'Next: confirm the bytes are an uncompressed EDF or BDF recording — a gzip, zip or ' +
|
|
140
|
+
'vendor container has to be unpacked before edfcore sees it.'
|
|
141
|
+
: `Next: unpack ${container} and open the EDF or BDF file inside it — edfcore reads ` +
|
|
142
|
+
'recordings, not archives.'),
|
|
109
143
|
field: 'version',
|
|
110
144
|
byteOffset: HEADER_FIELDS.version.offset,
|
|
111
145
|
byteLength: HEADER_FIELDS.version.length,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"variant.js","sourceRoot":"","sources":["../../src/header/variant.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"variant.js","sourceRoot":"","sources":["../../src/header/variant.ts"],"names":[],"mappings":"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"}
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,45 @@ 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.6.27
|
|
10
|
+
|
|
11
|
+
- **Changed** the "no signal is labelled" refusal to name the label that differs only in case or
|
|
12
|
+
spacing. `lookup.ts` explains why matching is exact and case-sensitive — `'Fp1'` and `'FP1'` are
|
|
13
|
+
written by different systems and edfcore has no montage vocabulary to decide they are the same
|
|
14
|
+
electrode — and the message said "matching is exact on the trimmed label and is case-sensitive"
|
|
15
|
+
while holding the label that proves that is what went wrong, and printing it somewhere in a list
|
|
16
|
+
of every other label for the reader to spot.
|
|
17
|
+
- That decision is about which signal comes back, and it is unchanged: `getSignal(header, 'FP1')`
|
|
18
|
+
on a file with `'Fp1'` still throws. It was never a reason to make the reader do the comparison.
|
|
19
|
+
The near miss is named, put first in the list, and repeated in the `Next:` clause.
|
|
20
|
+
- **Changed** the list of labels to be capped at twelve, saying how many it withheld. Every other
|
|
21
|
+
listing this package prints is capped — 24 bytes of hex, 16 of field evidence, twenty
|
|
22
|
+
diagnostics, `--limit` events — and this was the one that was not, in the one message whose
|
|
23
|
+
length grows with the file. A mistyped label on a 512-signal recording put five thousand
|
|
24
|
+
characters on one line behind `edfcore: `, and `inspect.ts` names a 512-signal file as the
|
|
25
|
+
realistic one.
|
|
26
|
+
- `availableLabels` on the error still carries every label, so nothing a program reads was capped.
|
|
27
|
+
Case is folded with `toLowerCase` rather than `toLocaleLowerCase`, which would fold `'I'` to a
|
|
28
|
+
dotless `'ı'` under a Turkish locale and make this message depend on where it ran.
|
|
29
|
+
|
|
30
|
+
## 0.6.26
|
|
31
|
+
|
|
32
|
+
- **Changed** `NOT_AN_EDF_FILE` to say which archive it was handed, when the first bytes say so.
|
|
33
|
+
That message's `Next:` clause has always read "a gzip, zip or vendor container has to be unpacked
|
|
34
|
+
before edfcore sees it" — a menu, offered on the same line as the hex that answers it. `1f 8b` is
|
|
35
|
+
a gzip and nothing else, and the message printed those bytes and asked the reader to look them up.
|
|
36
|
+
- It is also the likeliest first meeting with this package. Recordings travel compressed —
|
|
37
|
+
PhysioNet ships `.zip`, teuniz.net ships `.zip`, a shared study arrives as `.gz` — so pointing
|
|
38
|
+
edfcore at the download rather than at what is inside it is the ordinary mistake, not an exotic
|
|
39
|
+
one. `edfcore header study.edf.gz` now says it is a gzip and to unpack it.
|
|
40
|
+
- Seven magic numbers: gzip, zip, an empty zip, bzip2, xz, zstd and 7-Zip. Archive formats only,
|
|
41
|
+
and only by magic number — naming a container is a fact about the first bytes, while guessing a
|
|
42
|
+
vendor's binary EEG format from two of them would be a claim about the whole file. Everything
|
|
43
|
+
unrecognised keeps the general advice, which `it-says-which-container.test.ts` checks with bytes
|
|
44
|
+
chosen to sit one value away from a magic number, because a table that matched everything would
|
|
45
|
+
be worse than the menu.
|
|
46
|
+
- Same code, same field, same bytes, same `Next:` shape. A program branching on `code` sees nothing.
|
|
47
|
+
|
|
9
48
|
## 0.6.25
|
|
10
49
|
|
|
11
50
|
- **Fixed** an event listing losing its columns to a time wider than twelve characters.
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
package/src/header/lookup.ts
CHANGED
|
@@ -35,8 +35,47 @@ export function findSignals(header: EdfHeader, label: string): readonly EdfSigna
|
|
|
35
35
|
return Object.freeze(header.signals.filter((signal) => signal.label === wanted));
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Enough labels to recognise the file, few enough to read in a terminal.
|
|
40
|
+
*
|
|
41
|
+
* Every other listing this package prints is capped — 24 bytes of hex, 16 bytes of field evidence,
|
|
42
|
+
* twenty diagnostics — and this one was not, in the one message whose length grows with the file.
|
|
43
|
+
* A mistyped label on a 512-signal recording put five thousand characters on one line behind
|
|
44
|
+
* `edfcore: `, and `inspect.ts` names a 512-signal file as the realistic one. The full list is
|
|
45
|
+
* still on the error, as `availableLabels`, for a program that wants it.
|
|
46
|
+
*/
|
|
47
|
+
const LABELS_SHOWN = 12;
|
|
48
|
+
|
|
49
|
+
/** The whole label list, capped, saying how many it withheld — never silently truncated. */
|
|
50
|
+
function quoteLabels(header: EdfHeader, first?: string): string {
|
|
51
|
+
const labels = header.signals.map((signal) => signal.label);
|
|
52
|
+
const ordered =
|
|
53
|
+
first === undefined ? labels : [first, ...labels.filter((label) => label !== first)];
|
|
54
|
+
const shown = ordered.slice(0, LABELS_SHOWN).map((label) => JSON.stringify(label));
|
|
55
|
+
const hidden = ordered.length - shown.length;
|
|
56
|
+
return hidden > 0 ? `${shown.join(', ')}, and ${hidden} more` : shown.join(', ');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* `String.prototype.toLowerCase`, not `toLocaleLowerCase`: the second one folds `'I'` to a dotless
|
|
61
|
+
* `'ı'` under a Turkish locale, and this package's output is deterministic and locale-free.
|
|
62
|
+
*/
|
|
63
|
+
const foldCase = (text: string): string => trimEdfField(text).toLowerCase().replace(/\s+/g, ' ');
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The label that differs from the selector only in case or in internal spacing, if there is one.
|
|
67
|
+
*
|
|
68
|
+
* The module note above says why matching is exact and case-sensitive: `'Fp1'` and `'FP1'` are
|
|
69
|
+
* written by different systems and edfcore has no montage vocabulary to decide they are the same
|
|
70
|
+
* electrode. That is a decision about MATCHING, not a reason to make the reader work out what
|
|
71
|
+
* happened — the message said "is case-sensitive" while holding the label that proves it is what
|
|
72
|
+
* bit them.
|
|
73
|
+
*
|
|
74
|
+
* Naming it changes nothing about which signal is returned. It is still refused.
|
|
75
|
+
*/
|
|
76
|
+
function differsOnlyInCase(header: EdfHeader, selector: string): string | undefined {
|
|
77
|
+
const wanted = foldCase(selector);
|
|
78
|
+
return header.signals.find((signal) => foldCase(signal.label) === wanted)?.label;
|
|
40
79
|
}
|
|
41
80
|
|
|
42
81
|
/**
|
|
@@ -62,10 +101,16 @@ export function getSignal(header: EdfHeader, selector: number | string): EdfSign
|
|
|
62
101
|
const matches = findSignals(header, selector);
|
|
63
102
|
const first = matches[0];
|
|
64
103
|
if (first === undefined) {
|
|
104
|
+
const near = differsOnlyInCase(header, selector);
|
|
65
105
|
throw new EdfChannelNotFoundError(
|
|
66
|
-
`no signal is labelled ${JSON.stringify(trimEdfField(selector))} in this file.
|
|
67
|
-
|
|
68
|
-
|
|
106
|
+
`no signal is labelled ${JSON.stringify(trimEdfField(selector))} in this file. ` +
|
|
107
|
+
(near === undefined
|
|
108
|
+
? ''
|
|
109
|
+
: `This file has ${JSON.stringify(near)}, which differs only in case or spacing. `) +
|
|
110
|
+
`Labels, in signal order: ${quoteLabels(header, near)}. Matching is exact on the ` +
|
|
111
|
+
'trimmed label and is case-sensitive. Next: pass ' +
|
|
112
|
+
(near === undefined ? 'one of those labels' : `${JSON.stringify(near)}`) +
|
|
113
|
+
', or select by index.',
|
|
69
114
|
{ selector, availableLabels: header.signals.map((signal) => signal.label) },
|
|
70
115
|
);
|
|
71
116
|
}
|
package/src/header/variant.ts
CHANGED
|
@@ -116,6 +116,36 @@ export function reservedMarkerOf(reserved: string): EdfReservedMarker | undefine
|
|
|
116
116
|
return undefined;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* The containers whose first bytes are already in hand when `NOT_AN_EDF_FILE` is thrown.
|
|
121
|
+
*
|
|
122
|
+
* That message's `Next:` clause has always said "a gzip, zip or vendor container has to be
|
|
123
|
+
* unpacked before edfcore sees it" — a menu, offered while holding the two bytes that say which
|
|
124
|
+
* one it is. `1f 8b` is a gzip and nothing else, and a reader who has just been handed the hex is
|
|
125
|
+
* being asked to look it up.
|
|
126
|
+
*
|
|
127
|
+
* Only the archive formats, and only by their magic number. Naming a container is a fact about the
|
|
128
|
+
* first bytes; guessing at a vendor's binary EEG format from the same two bytes would be a claim
|
|
129
|
+
* about the whole file, and the generic clause still covers those.
|
|
130
|
+
*/
|
|
131
|
+
const CONTAINERS: ReadonlyArray<readonly [string, readonly number[]]> = [
|
|
132
|
+
['a gzip', [0x1f, 0x8b]],
|
|
133
|
+
['a zip', [0x50, 0x4b, 0x03, 0x04]],
|
|
134
|
+
['an empty zip', [0x50, 0x4b, 0x05, 0x06]],
|
|
135
|
+
['a bzip2', [0x42, 0x5a, 0x68]],
|
|
136
|
+
['an xz', [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00]],
|
|
137
|
+
['a zstd', [0x28, 0xb5, 0x2f, 0xfd]],
|
|
138
|
+
['a 7-Zip archive', [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c]],
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
/** The container these bytes begin with, or `undefined` — which is most files. */
|
|
142
|
+
function containerAt(bytes: Uint8Array): string | undefined {
|
|
143
|
+
for (const [name, magic] of CONTAINERS) {
|
|
144
|
+
if (magic.every((byte, at) => bytes[at] === byte)) return name;
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
|
|
119
149
|
function markerFamily(marker: EdfReservedMarker): EdfFamily {
|
|
120
150
|
return marker === 'EDF+C' || marker === 'EDF+D' ? 'EDF' : 'BDF';
|
|
121
151
|
}
|
|
@@ -157,15 +187,20 @@ export function detectVariant(headerBytes: Uint8Array, sink: DiagnosticSink): Ed
|
|
|
157
187
|
HEADER_FIELDS.version.offset,
|
|
158
188
|
HEADER_FIELDS.version.length,
|
|
159
189
|
);
|
|
190
|
+
const container = containerAt(headerBytes);
|
|
160
191
|
throw fatalError({
|
|
161
192
|
code: 'NOT_AN_EDF_FILE',
|
|
162
193
|
message:
|
|
163
194
|
`version field (8 bytes at offset ${HEADER_FIELDS.version.offset}) is ` +
|
|
164
195
|
`${JSON.stringify(raw)}, bytes ${hexBytes(versionBytes)}: this is neither EDF's ` +
|
|
165
196
|
`"0" followed by seven spaces nor BDF's 0xff followed by "BIOSEMI". ` +
|
|
197
|
+
(container === undefined ? '' : `Those first bytes are ${container}. `) +
|
|
166
198
|
'EDF specification, header record bytes 0-7 (version of this data format). ' +
|
|
167
|
-
|
|
168
|
-
|
|
199
|
+
(container === undefined
|
|
200
|
+
? 'Next: confirm the bytes are an uncompressed EDF or BDF recording — a gzip, zip or ' +
|
|
201
|
+
'vendor container has to be unpacked before edfcore sees it.'
|
|
202
|
+
: `Next: unpack ${container} and open the EDF or BDF file inside it — edfcore reads ` +
|
|
203
|
+
'recordings, not archives.'),
|
|
169
204
|
field: 'version',
|
|
170
205
|
byteOffset: HEADER_FIELDS.version.offset,
|
|
171
206
|
byteLength: HEADER_FIELDS.version.length,
|