edf2csv 0.3.3 → 0.3.5

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 (2) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/package.json +3 -2
package/CHANGELOG.md CHANGED
@@ -3,6 +3,85 @@
3
3
  Notable changes to edf2csv. Versions follow [semantic versioning](https://semver.org); while the
4
4
  major version is 0, a minor bump may contain breaking changes.
5
5
 
6
+ ## 0.3.5
7
+
8
+ ### The cross-check now covers BDF and annotations
9
+
10
+ 0.3.4 made the pyEDFlib comparison a command, but it only read `.edf` files carrying signal
11
+ data. That left out the two places a reader is most likely to be quietly wrong.
12
+
13
+ **24-bit BDF.** A BDF sample is three bytes, and the sign has to be extended by hand. A value
14
+ that comes out unsigned is not obviously wrong to look at: it is a large positive number where
15
+ a large negative one belongs, which is exactly the kind of mistake that survives review. A
16
+ quarter of the generated recordings are now BDF over BioSemi's own digital range, with both
17
+ extremes present in every one.
18
+
19
+ **Annotations.** Half the recordings now carry EDF+ or BDF+ events, including one with no
20
+ duration and one whose duration is zero, and `annotations.csv` is compared against pyEDFlib's
21
+ own reading of the TALs — onset, duration and text. The two disagree on one point by design:
22
+ pyEDFlib reports a missing duration as `-1.0` where edf2csv leaves the cell empty, a duration
23
+ nobody recorded not being a duration of minus one second. That difference is expected and
24
+ treated as agreement.
25
+
26
+ ```
27
+ Compared 16,943 sample values and 120 annotations across 75 recordings.
28
+ Every value agreed.
29
+ ```
30
+
31
+ Both halves were confirmed capable of failing before being trusted: a one-part-in-a-million
32
+ error in the gain, and a one-millisecond shift in every annotation onset. Each exits 1 and
33
+ names the sample.
34
+
35
+ Writing the recordings turned up a fault in the generator rather than in the tool. A record's
36
+ timekeeping TAL states where that record begins, which is its index times the record duration —
37
+ it was writing the index. Recordings whose records are not one second long were therefore
38
+ internally inconsistent, and pyEDFlib rejected precisely those rather than reading them wrongly,
39
+ which is a good argument for comparing against something strict.
40
+
41
+ No behaviour of the tool changed in this release.
42
+
43
+ ## 0.3.4
44
+
45
+ ### Added: `npm run crossvalidate`, the pyEDFlib comparison as something you can run
46
+
47
+ The README and the correctness page have both said the arithmetic is checked against an
48
+ independent implementation. That was true, and it was done by hand, which meant nothing
49
+ rechecked it when the code changed. It is now a command:
50
+
51
+ ```bash
52
+ pip install pyedflib
53
+ npm run crossvalidate
54
+ ```
55
+
56
+ ```
57
+ Compared 12,559 sample values across 77 recordings.
58
+ Every value agreed.
59
+ ```
60
+
61
+ Why another implementation rather than another test: the digital-to-physical mapping is four
62
+ numbers out of the header and one multiply. It is easy to get subtly wrong and nearly
63
+ impossible to catch by reading, and a test written next to the code tends to encode the same
64
+ misunderstanding the code has. pyEDFlib was written by other people from the same
65
+ specification.
66
+
67
+ The recordings it generates are deliberately not the test fixtures. Those target what real
68
+ files get wrong, and pyEDFlib declines several of them — a truncated file, a header whose
69
+ digital range is a single point. These are the opposite: ordinary well-formed recordings
70
+ across a wide spread of calibrations, digital spans from `-1..1` to `-32768..32767` against
71
+ physical spans from `0.0001` to `99999`, which puts the gain anywhere from about 1e-9 to 1e5.
72
+ Both ends of the digital range appear in every one, because those are the two points the
73
+ header actually calibrates and where two derivations of the same mapping differ most.
74
+
75
+ The comparison runs at `--decimals 20` so that what is compared is two computations of a value
76
+ rather than one of them against its printed form. The first attempt used 12, where a reading
77
+ near 1e-5 keeps only seven significant digits and the rounding was larger than the
78
+ disagreement being looked for.
79
+
80
+ It is opt-in and not part of `npm test`, which stays dependency-free. Without pyEDFlib
81
+ installed it says so and exits 0 rather than reporting a pass it did not earn. Putting a
82
+ one-part-in-a-million error into the gain makes it exit 1 and name the sample, which is how
83
+ it was confirmed to be capable of failing at all.
84
+
6
85
  ## 0.3.3
7
86
 
8
87
  ### Fixed: a mistyped channel position converted a different channel
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edf2csv",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Convert EDF, EDF+ and BDF biosignal recordings (European Data Format) to CSV from the command line. Local, streaming, and never resamples or alters units.",
5
5
  "keywords": [
6
6
  "edf",
@@ -60,7 +60,8 @@
60
60
  "fixtures": "node test/fixtures/generate.mjs",
61
61
  "test": "npm run build && npm run fixtures && node --test test/*.test.js",
62
62
  "typecheck": "tsc -p tsconfig.json --noEmit",
63
- "prepublishOnly": "npm run clean && npm run build && npm test"
63
+ "prepublishOnly": "npm run clean && npm run build && npm test",
64
+ "crossvalidate": "npm run build && node test/crossvalidate/generate.mjs && python3 test/crossvalidate/compare.py"
64
65
  },
65
66
  "devDependencies": {
66
67
  "@types/node": "^22.10.0",