@neurodevs/node-lsl 18.1.0 → 20.0.0
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/build/__tests__/AbstractPackageTest.d.ts +19 -17
- package/build/__tests__/AbstractPackageTest.js +54 -39
- package/build/__tests__/AbstractPackageTest.js.map +1 -1
- package/build/__tests__/impl/BleDeviceController.test.d.ts +6 -1
- package/build/__tests__/impl/BleDeviceController.test.js +44 -15
- package/build/__tests__/impl/BleDeviceController.test.js.map +1 -1
- package/build/__tests__/impl/LslEventMarkerEmitter.test.js +13 -13
- package/build/__tests__/impl/LslEventMarkerEmitter.test.js.map +1 -1
- package/build/__tests__/impl/LslStreamInfo.test.js +2 -2
- package/build/__tests__/impl/LslStreamInfo.test.js.map +1 -1
- package/build/__tests__/impl/LslStreamInlet.test.d.ts +3 -5
- package/build/__tests__/impl/LslStreamInlet.test.js +13 -46
- package/build/__tests__/impl/LslStreamInlet.test.js.map +1 -1
- package/build/__tests__/impl/LslStreamOutlet.test.js +9 -16
- package/build/__tests__/impl/LslStreamOutlet.test.js.map +1 -1
- package/build/__tests__/impl/LslWebSocketBridge.test.js +9 -10
- package/build/__tests__/impl/LslWebSocketBridge.test.js.map +1 -1
- package/build/__tests__/impl/UsbDeviceController.test.d.ts +17 -0
- package/build/__tests__/impl/UsbDeviceController.test.js +81 -0
- package/build/__tests__/impl/UsbDeviceController.test.js.map +1 -0
- package/build/__tests__/impl/WindowedClockRegressor.test.d.ts +19 -0
- package/build/__tests__/impl/WindowedClockRegressor.test.js +172 -0
- package/build/__tests__/impl/WindowedClockRegressor.test.js.map +1 -0
- package/build/assertions.js +1 -1
- package/build/assertions.js.map +1 -1
- package/build/impl/LslEventMarkerEmitter.d.ts +12 -12
- package/build/impl/LslEventMarkerEmitter.js.map +1 -1
- package/build/impl/LslStreamInfo.d.ts +7 -7
- package/build/impl/LslStreamInfo.js.map +1 -1
- package/build/impl/LslStreamInlet.d.ts +63 -20
- package/build/impl/LslStreamInlet.js +221 -64
- package/build/impl/LslStreamInlet.js.map +1 -1
- package/build/impl/LslStreamOutlet.d.ts +16 -14
- package/build/impl/LslStreamOutlet.js +34 -37
- package/build/impl/LslStreamOutlet.js.map +1 -1
- package/build/impl/LslWebSocketBridge.d.ts +10 -10
- package/build/impl/LslWebSocketBridge.js.map +1 -1
- package/build/impl/clocks/WindowedClockRegressor.d.ts +24 -0
- package/build/impl/clocks/WindowedClockRegressor.js +73 -0
- package/build/impl/clocks/WindowedClockRegressor.js.map +1 -0
- package/build/impl/controllers/BleDeviceController.d.ts +3 -1
- package/build/impl/controllers/BleDeviceController.js +9 -1
- package/build/impl/controllers/BleDeviceController.js.map +1 -1
- package/build/impl/controllers/UsbDeviceController.d.ts +23 -0
- package/build/impl/controllers/UsbDeviceController.js +40 -0
- package/build/impl/controllers/UsbDeviceController.js.map +1 -0
- package/build/index.d.ts +32 -20
- package/build/index.js +39 -26
- package/build/index.js.map +1 -1
- package/build/scripts/{runLslEventMarkerEmitter.js → runLslEmitter.js} +5 -4
- package/build/scripts/runLslEmitter.js.map +1 -0
- package/build/scripts/{runLslStreamOutlet.js → runLslOutlet.js} +1 -1
- package/build/scripts/runLslOutlet.js.map +1 -0
- package/build/scripts/runMuse.js +1 -1
- package/build/scripts/runMuse.js.map +1 -1
- package/build/scripts/runUsb.js +14 -0
- package/build/scripts/runUsb.js.map +1 -0
- package/build/testDoubles/BleController/FakeBleController.d.ts +3 -1
- package/build/testDoubles/BleController/FakeBleController.js +5 -0
- package/build/testDoubles/BleController/FakeBleController.js.map +1 -1
- package/build/testDoubles/ClockRegressor/FakeClockRegressor.d.ts +17 -0
- package/build/testDoubles/ClockRegressor/FakeClockRegressor.js +23 -0
- package/build/testDoubles/ClockRegressor/FakeClockRegressor.js.map +1 -0
- package/build/testDoubles/{EventMarkerEmitter/FakeEventMarkerEmitter.d.ts → LslEmitter/FakeLslEmitter.d.ts} +4 -4
- package/build/testDoubles/{EventMarkerEmitter/FakeEventMarkerEmitter.js → LslEmitter/FakeLslEmitter.js} +7 -7
- package/build/testDoubles/LslEmitter/FakeLslEmitter.js.map +1 -0
- package/build/testDoubles/{EventMarkerEmitter/SpyEventMarkerEmitter.d.ts → LslEmitter/SpyLslEmitter.d.ts} +4 -4
- package/build/testDoubles/{EventMarkerEmitter/SpyEventMarkerEmitter.js → LslEmitter/SpyLslEmitter.js} +3 -3
- package/build/testDoubles/LslEmitter/SpyLslEmitter.js.map +1 -0
- package/build/testDoubles/LslEmitter/ThrowingLslEmitter.d.ts +9 -0
- package/build/testDoubles/{EventMarkerEmitter/ThrowingEventMarkerEmitter.js → LslEmitter/ThrowingLslEmitter.js} +2 -2
- package/build/testDoubles/LslEmitter/ThrowingLslEmitter.js.map +1 -0
- package/build/testDoubles/{StreamInfo/FakeStreamInfo.d.ts → LslInfo/FakeLslInfo.d.ts} +4 -4
- package/build/testDoubles/{StreamInfo/FakeStreamInfo.js → LslInfo/FakeLslInfo.js} +7 -7
- package/build/testDoubles/LslInfo/FakeLslInfo.js.map +1 -0
- package/build/testDoubles/LslInfo/SpyLslInfo.d.ts +10 -0
- package/build/testDoubles/{StreamInfo/SpyStreamInfo.js → LslInfo/SpyLslInfo.js} +4 -4
- package/build/testDoubles/LslInfo/SpyLslInfo.js.map +1 -0
- package/build/testDoubles/{StreamInlet/FakeStreamInlet.d.ts → LslInlet/FakeLslInlet.d.ts} +4 -4
- package/build/testDoubles/{StreamInlet/FakeStreamInlet.js → LslInlet/FakeLslInlet.js} +12 -11
- package/build/testDoubles/LslInlet/FakeLslInlet.js.map +1 -0
- package/build/testDoubles/LslInlet/SpyLslInlet.d.ts +4 -0
- package/build/testDoubles/{StreamInlet/SpyStreamInlet.js → LslInlet/SpyLslInlet.js} +2 -2
- package/build/testDoubles/LslInlet/SpyLslInlet.js.map +1 -0
- package/build/testDoubles/{StreamOutlet/FakeStreamOutlet.d.ts → LslOutlet/FakeLslOutlet.d.ts} +5 -5
- package/build/testDoubles/{StreamOutlet/FakeStreamOutlet.js → LslOutlet/FakeLslOutlet.js} +8 -8
- package/build/testDoubles/LslOutlet/FakeLslOutlet.js.map +1 -0
- package/build/testDoubles/LslWsBridge/FakeLslWsBridge.d.ts +14 -0
- package/build/testDoubles/{WebSocketBridge/FakeWebSocketBridge.js → LslWsBridge/FakeLslWsBridge.js} +14 -14
- package/build/testDoubles/LslWsBridge/FakeLslWsBridge.js.map +1 -0
- package/build/testDoubles/LslWsBridge/SpyLslWsBridge.d.ts +4 -0
- package/build/testDoubles/{WebSocketBridge/SpyLslWebSocketBridge.js → LslWsBridge/SpyLslWsBridge.js} +2 -2
- package/build/testDoubles/LslWsBridge/SpyLslWsBridge.js.map +1 -0
- package/build/testDoubles/UsbController/FakeUsbController.d.ts +12 -0
- package/build/testDoubles/UsbController/FakeUsbController.js +25 -0
- package/build/testDoubles/UsbController/FakeUsbController.js.map +1 -0
- package/build/testDoubles/consts.d.ts +2 -12
- package/build/testDoubles/consts.js +0 -20
- package/build/testDoubles/consts.js.map +1 -1
- package/build/testDoubles/generateRandomOutletOptions.d.ts +2 -2
- package/build/testDoubles/generateRandomOutletOptions.js.map +1 -1
- package/package.json +6 -5
- package/src/__tests__/AbstractPackageTest.ts +78 -49
- package/src/__tests__/impl/BleDeviceController.test.ts +59 -18
- package/src/__tests__/impl/LslEventMarkerEmitter.test.ts +19 -24
- package/src/__tests__/impl/LslStreamInfo.test.ts +8 -8
- package/src/__tests__/impl/LslStreamInlet.test.ts +21 -78
- package/src/__tests__/impl/LslStreamOutlet.test.ts +22 -28
- package/src/__tests__/impl/LslWebSocketBridge.test.ts +15 -18
- package/src/__tests__/impl/UsbDeviceController.test.ts +109 -0
- package/src/__tests__/impl/WindowedClockRegressor.test.ts +262 -0
- package/src/assertions.ts +1 -2
- package/src/impl/LslEventMarkerEmitter.ts +15 -17
- package/src/impl/LslStreamInfo.ts +7 -9
- package/src/impl/LslStreamInlet.ts +304 -77
- package/src/impl/LslStreamOutlet.ts +59 -57
- package/src/impl/LslWebSocketBridge.ts +15 -18
- package/src/impl/clocks/WindowedClockRegressor.ts +136 -0
- package/src/impl/controllers/BleDeviceController.ts +18 -1
- package/src/impl/controllers/UsbDeviceController.ts +65 -0
- package/src/index.ts +48 -28
- package/src/scripts/{runLslEventMarkerEmitter.ts → runLslEmitter.ts} +4 -3
- package/src/scripts/runMuse.ts +1 -1
- package/src/scripts/runUsb.ts +20 -0
- package/src/testDoubles/BleController/FakeBleController.ts +11 -0
- package/src/testDoubles/ClockRegressor/FakeClockRegressor.ts +46 -0
- package/src/testDoubles/{EventMarkerEmitter/FakeEventMarkerEmitter.ts → LslEmitter/FakeLslEmitter.ts} +10 -10
- package/src/testDoubles/{EventMarkerEmitter/SpyEventMarkerEmitter.ts → LslEmitter/SpyLslEmitter.ts} +4 -4
- package/src/testDoubles/{EventMarkerEmitter/ThrowingEventMarkerEmitter.ts → LslEmitter/ThrowingLslEmitter.ts} +4 -4
- package/src/testDoubles/{StreamInfo/FakeStreamInfo.ts → LslInfo/FakeLslInfo.ts} +9 -9
- package/src/testDoubles/{StreamInfo/SpyStreamInfo.ts → LslInfo/SpyLslInfo.ts} +5 -5
- package/src/testDoubles/{StreamInlet/FakeStreamInlet.ts → LslInlet/FakeLslInlet.ts} +15 -14
- package/src/testDoubles/LslInlet/SpyLslInlet.ts +10 -0
- package/src/testDoubles/{StreamOutlet/FakeStreamOutlet.ts → LslOutlet/FakeLslOutlet.ts} +11 -14
- package/src/testDoubles/{WebSocketBridge/FakeWebSocketBridge.ts → LslWsBridge/FakeLslWsBridge.ts} +19 -20
- package/src/testDoubles/LslWsBridge/SpyLslWsBridge.ts +9 -0
- package/src/testDoubles/UsbController/FakeUsbController.ts +34 -0
- package/src/testDoubles/consts.ts +3 -23
- package/src/testDoubles/generateRandomOutletOptions.ts +2 -2
- package/build/consts.d.ts +0 -11
- package/build/consts.js +0 -21
- package/build/consts.js.map +0 -1
- package/build/impl/workers/inlet/LslStreamInlet.worker.d.ts +0 -22
- package/build/impl/workers/inlet/LslStreamInlet.worker.js +0 -42
- package/build/impl/workers/inlet/LslStreamInlet.worker.js.map +0 -1
- package/build/impl/workers/inlet/StreamInletWorker.d.ts +0 -73
- package/build/impl/workers/inlet/StreamInletWorker.js +0 -253
- package/build/impl/workers/inlet/StreamInletWorker.js.map +0 -1
- package/build/impl/workers/outlet/LslStreamOutlet.worker.d.ts +0 -32
- package/build/impl/workers/outlet/LslStreamOutlet.worker.js +0 -82
- package/build/impl/workers/outlet/LslStreamOutlet.worker.js.map +0 -1
- package/build/scripts/discoverMuse.js +0 -40
- package/build/scripts/discoverMuse.js.map +0 -1
- package/build/scripts/runLslEventMarkerEmitter.js.map +0 -1
- package/build/scripts/runLslStreamOutlet.js.map +0 -1
- package/build/scripts/testDestroyOutlet.d.ts +0 -1
- package/build/scripts/testDestroyOutlet.js +0 -4
- package/build/scripts/testDestroyOutlet.js.map +0 -1
- package/build/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.js.map +0 -1
- package/build/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.js.map +0 -1
- package/build/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.d.ts +0 -9
- package/build/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.js.map +0 -1
- package/build/testDoubles/StreamInfo/FakeStreamInfo.js.map +0 -1
- package/build/testDoubles/StreamInfo/SpyStreamInfo.d.ts +0 -10
- package/build/testDoubles/StreamInfo/SpyStreamInfo.js.map +0 -1
- package/build/testDoubles/StreamInlet/FakeStreamInlet.js.map +0 -1
- package/build/testDoubles/StreamInlet/SpyStreamInlet.d.ts +0 -4
- package/build/testDoubles/StreamInlet/SpyStreamInlet.js.map +0 -1
- package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js.map +0 -1
- package/build/testDoubles/WebSocketBridge/FakeWebSocketBridge.d.ts +0 -14
- package/build/testDoubles/WebSocketBridge/FakeWebSocketBridge.js.map +0 -1
- package/build/testDoubles/WebSocketBridge/SpyLslWebSocketBridge.d.ts +0 -4
- package/build/testDoubles/WebSocketBridge/SpyLslWebSocketBridge.js.map +0 -1
- package/build/testDoubles/WorkerThreads/FakeWorker.d.ts +0 -27
- package/build/testDoubles/WorkerThreads/FakeWorker.js +0 -67
- package/build/testDoubles/WorkerThreads/FakeWorker.js.map +0 -1
- package/src/consts.ts +0 -21
- package/src/impl/workers/inlet/LslStreamInlet.worker.ts +0 -74
- package/src/impl/workers/inlet/StreamInletWorker.ts +0 -348
- package/src/impl/workers/outlet/LslStreamOutlet.worker.ts +0 -144
- package/src/scripts/discoverMuse.ts +0 -50
- package/src/scripts/testDestroyOutlet.ts +0 -5
- package/src/testDoubles/StreamInlet/SpyStreamInlet.ts +0 -10
- package/src/testDoubles/WebSocketBridge/SpyLslWebSocketBridge.ts +0 -9
- package/src/testDoubles/WorkerThreads/FakeWorker.ts +0 -99
- /package/build/scripts/{discoverMuse.d.ts → runLslEmitter.d.ts} +0 -0
- /package/build/scripts/{runLslEventMarkerEmitter.d.ts → runLslOutlet.d.ts} +0 -0
- /package/build/scripts/{runLslStreamOutlet.d.ts → runUsb.d.ts} +0 -0
- /package/src/scripts/{runLslStreamOutlet.ts → runLslOutlet.ts} +0 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { randomInt } from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
import { test, assert } from '@neurodevs/node-tdd'
|
|
4
|
+
|
|
5
|
+
import WindowedClockRegressor, {
|
|
6
|
+
ClockRegressor,
|
|
7
|
+
} from '../../impl/clocks/WindowedClockRegressor.js'
|
|
8
|
+
import AbstractPackageTest from '../AbstractPackageTest.js'
|
|
9
|
+
|
|
10
|
+
export default class WindowedClockRegressorTest extends AbstractPackageTest {
|
|
11
|
+
private static instance: ClockRegressor
|
|
12
|
+
|
|
13
|
+
protected static readonly chunkSize = randomInt(10, 20)
|
|
14
|
+
|
|
15
|
+
private static readonly nominalHz = 50 + Math.random() * 50
|
|
16
|
+
private static readonly deviceTime = Math.random()
|
|
17
|
+
private static readonly earliestLslTime = Math.random()
|
|
18
|
+
|
|
19
|
+
protected static async beforeEach() {
|
|
20
|
+
await super.beforeEach()
|
|
21
|
+
|
|
22
|
+
this.instance = this.WindowedClockRegressor()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@test()
|
|
26
|
+
protected static async createsInstance() {
|
|
27
|
+
assert.isTruthy(this.instance, 'Failed to create instance!')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@test()
|
|
31
|
+
protected static async deriveReturnsChunkSizeEntries() {
|
|
32
|
+
const timestamps = this.deriveTimestamps()
|
|
33
|
+
|
|
34
|
+
assert.isEqual(
|
|
35
|
+
timestamps.length,
|
|
36
|
+
this.chunkSize,
|
|
37
|
+
'Should return an array with chunkSize entries!'
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@test()
|
|
42
|
+
protected static async deriveAnchorsLastTimestampOnRegressionLine() {
|
|
43
|
+
const nominalChunkStep = this.chunkSize / this.nominalHz
|
|
44
|
+
|
|
45
|
+
let deviceTime = this.deviceTime
|
|
46
|
+
let lslTime = this.earliestLslTime
|
|
47
|
+
|
|
48
|
+
for (let i = 0; i < 100; i++) {
|
|
49
|
+
deviceTime += nominalChunkStep
|
|
50
|
+
lslTime += nominalChunkStep
|
|
51
|
+
this.deriveTimestamps(deviceTime, lslTime)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
deviceTime += nominalChunkStep
|
|
55
|
+
lslTime += nominalChunkStep
|
|
56
|
+
|
|
57
|
+
const jitter = nominalChunkStep * Math.random()
|
|
58
|
+
|
|
59
|
+
const timestamps = this.deriveTimestamps(deviceTime, lslTime + jitter)
|
|
60
|
+
|
|
61
|
+
for (let i = 0; i < timestamps.length; i++) {
|
|
62
|
+
const stepsFromLast = timestamps.length - 1 - i
|
|
63
|
+
const expected = lslTime - stepsFromLast / this.nominalHz
|
|
64
|
+
|
|
65
|
+
assert.isBetweenInclusive(
|
|
66
|
+
timestamps[i],
|
|
67
|
+
expected - jitter * 0.1,
|
|
68
|
+
expected + jitter * 0.1,
|
|
69
|
+
`Timestamp at index ${i} should be anchored on the fitted regression line, not the jittery arrival time!`
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@test()
|
|
75
|
+
protected static async deriveSpacesEntriesByNominalHz() {
|
|
76
|
+
const timestamps = this.deriveTimestamps()
|
|
77
|
+
|
|
78
|
+
const expected = Array(this.chunkSize)
|
|
79
|
+
.fill(0)
|
|
80
|
+
.map((_, i) => {
|
|
81
|
+
return (
|
|
82
|
+
this.earliestLslTime -
|
|
83
|
+
(this.chunkSize - 1 - i) / this.nominalHz
|
|
84
|
+
)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
assert.isEqualDeep(
|
|
88
|
+
timestamps,
|
|
89
|
+
expected,
|
|
90
|
+
'Entries should be spaced 1/nominalHz apart, working backward from earliestLslTime!'
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@test()
|
|
95
|
+
protected static async deriveUsesFittedSlopeOnceHistoryAccumulates() {
|
|
96
|
+
const trueSlope = 2
|
|
97
|
+
const nominalStep = this.chunkSize / this.nominalHz
|
|
98
|
+
const lslStep = nominalStep * trueSlope
|
|
99
|
+
|
|
100
|
+
let deviceTime = this.deviceTime
|
|
101
|
+
let earliestLslTime = this.earliestLslTime
|
|
102
|
+
|
|
103
|
+
for (let i = 0; i < 50; i++) {
|
|
104
|
+
deviceTime += nominalStep
|
|
105
|
+
earliestLslTime += lslStep
|
|
106
|
+
|
|
107
|
+
this.deriveTimestamps(deviceTime, earliestLslTime)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
deviceTime += nominalStep
|
|
111
|
+
earliestLslTime += lslStep
|
|
112
|
+
|
|
113
|
+
const timestamps = this.deriveTimestamps(deviceTime, earliestLslTime)
|
|
114
|
+
|
|
115
|
+
const expectedSpacing = trueSlope / this.nominalHz
|
|
116
|
+
|
|
117
|
+
for (let i = 1; i < timestamps.length; i++) {
|
|
118
|
+
const actualSpacing = timestamps[i] - timestamps[i - 1]
|
|
119
|
+
|
|
120
|
+
assert.isBetweenInclusive(
|
|
121
|
+
actualSpacing,
|
|
122
|
+
expectedSpacing * 0.99,
|
|
123
|
+
expectedSpacing * 1.01,
|
|
124
|
+
`Spacing at index ${i} should reflect the fitted slope once history has accumulated!`
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@test()
|
|
130
|
+
protected static async deriveThrowsWhenDeviceTimeDoesNotIncreaseMonotonically() {
|
|
131
|
+
this.deriveTimestamps()
|
|
132
|
+
|
|
133
|
+
assert.doesThrow(
|
|
134
|
+
() =>
|
|
135
|
+
this.deriveTimestamps(
|
|
136
|
+
this.deviceTime,
|
|
137
|
+
this.earliestLslTime + Math.random()
|
|
138
|
+
),
|
|
139
|
+
`\nDevice time (${this.deviceTime}) did not increase from the previous device time (${this.deviceTime})! Device time is expected to always increase between calls, so this likely indicates a bug in the calling code.\n`
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@test()
|
|
144
|
+
protected static async deriveForgetsHistoryOutsideWindowLenSec() {
|
|
145
|
+
const nominalChunkStep = this.chunkSize / this.nominalHz
|
|
146
|
+
|
|
147
|
+
// With chunkSize in [10, 20) and nominalHz in [50, 100), nominalChunkStep
|
|
148
|
+
// stays within [0.1, 0.4). A fixed 700 iterations always spans at least
|
|
149
|
+
// 70 device-seconds — comfortably more than 2x the 30s windowLenSec.
|
|
150
|
+
const oldPhaseCount = 700
|
|
151
|
+
const newPhaseCount = 700
|
|
152
|
+
|
|
153
|
+
let deviceTime = this.deviceTime
|
|
154
|
+
let earliestLslTime = this.earliestLslTime
|
|
155
|
+
|
|
156
|
+
const oldSlope = 2
|
|
157
|
+
|
|
158
|
+
for (let i = 0; i < oldPhaseCount; i++) {
|
|
159
|
+
deviceTime += nominalChunkStep
|
|
160
|
+
earliestLslTime += nominalChunkStep * oldSlope
|
|
161
|
+
|
|
162
|
+
this.deriveTimestamps(deviceTime, earliestLslTime)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const newSlope = 5
|
|
166
|
+
let timestamps: number[] = []
|
|
167
|
+
|
|
168
|
+
for (let i = 0; i < newPhaseCount; i++) {
|
|
169
|
+
deviceTime += nominalChunkStep
|
|
170
|
+
earliestLslTime += nominalChunkStep * newSlope
|
|
171
|
+
|
|
172
|
+
timestamps = this.deriveTimestamps(deviceTime, earliestLslTime)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const expectedSpacing = newSlope / this.nominalHz
|
|
176
|
+
|
|
177
|
+
for (let i = 1; i < timestamps.length; i++) {
|
|
178
|
+
const actualSpacing = timestamps[i] - timestamps[i - 1]
|
|
179
|
+
|
|
180
|
+
assert.isBetweenInclusive(
|
|
181
|
+
actualSpacing,
|
|
182
|
+
expectedSpacing * 0.99,
|
|
183
|
+
expectedSpacing * 1.01,
|
|
184
|
+
`Spacing at index ${i} should reflect the new slope once old history falls outside windowLenSec!`
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@test()
|
|
190
|
+
protected static async deriveAllowsOverridingWindowLenSec() {
|
|
191
|
+
const nominalChunkStep = this.chunkSize / this.nominalHz
|
|
192
|
+
const customWindowLenSec = 5
|
|
193
|
+
|
|
194
|
+
// With nominalChunkStep in [0.1, 0.4), 150 iterations always spans at
|
|
195
|
+
// least 15 device-seconds — more than 2x the 5s customWindowLenSec,
|
|
196
|
+
// but well short of 2x the 30s default, so this only passes if the
|
|
197
|
+
// override actually takes effect.
|
|
198
|
+
const phaseCount = 150
|
|
199
|
+
|
|
200
|
+
const instance = WindowedClockRegressor.Create(this.nominalHz, {
|
|
201
|
+
windowLenSec: customWindowLenSec,
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
let deviceTime = this.deviceTime
|
|
205
|
+
let earliestLslTime = this.earliestLslTime
|
|
206
|
+
|
|
207
|
+
const oldSlope = 2
|
|
208
|
+
|
|
209
|
+
for (let i = 0; i < phaseCount; i++) {
|
|
210
|
+
deviceTime += nominalChunkStep
|
|
211
|
+
earliestLslTime += nominalChunkStep * oldSlope
|
|
212
|
+
|
|
213
|
+
instance.deriveTimestamps(
|
|
214
|
+
deviceTime,
|
|
215
|
+
earliestLslTime,
|
|
216
|
+
this.chunkSize
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const newSlope = 5
|
|
221
|
+
let timestamps: number[] = []
|
|
222
|
+
|
|
223
|
+
for (let i = 0; i < phaseCount; i++) {
|
|
224
|
+
deviceTime += nominalChunkStep
|
|
225
|
+
earliestLslTime += nominalChunkStep * newSlope
|
|
226
|
+
|
|
227
|
+
timestamps = instance.deriveTimestamps(
|
|
228
|
+
deviceTime,
|
|
229
|
+
earliestLslTime,
|
|
230
|
+
this.chunkSize
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const expectedSpacing = newSlope / this.nominalHz
|
|
235
|
+
|
|
236
|
+
for (let i = 1; i < timestamps.length; i++) {
|
|
237
|
+
const actualSpacing = timestamps[i] - timestamps[i - 1]
|
|
238
|
+
|
|
239
|
+
assert.isBetweenInclusive(
|
|
240
|
+
actualSpacing,
|
|
241
|
+
expectedSpacing * 0.99,
|
|
242
|
+
expectedSpacing * 1.01,
|
|
243
|
+
`Spacing at index ${i} should reflect the new slope once old history falls outside the custom windowLenSec!`
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private static deriveTimestamps(
|
|
249
|
+
deviceTime = this.deviceTime,
|
|
250
|
+
earliestLslTime = this.earliestLslTime
|
|
251
|
+
) {
|
|
252
|
+
return this.instance.deriveTimestamps(
|
|
253
|
+
deviceTime,
|
|
254
|
+
earliestLslTime,
|
|
255
|
+
this.chunkSize
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
private static WindowedClockRegressor() {
|
|
260
|
+
return WindowedClockRegressor.Create(this.nominalHz)
|
|
261
|
+
}
|
|
262
|
+
}
|
package/src/assertions.ts
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
import { generateShortId } from '@neurodevs/generate-id'
|
|
2
2
|
|
|
3
3
|
import LslStreamOutlet, {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
LslOutletOptions,
|
|
5
|
+
LslOutlet,
|
|
6
6
|
} from './LslStreamOutlet.js'
|
|
7
7
|
|
|
8
|
-
export default class LslEventMarkerEmitter implements
|
|
9
|
-
public static Class?:
|
|
8
|
+
export default class LslEventMarkerEmitter implements LslEmitter {
|
|
9
|
+
public static Class?: LslEmitterConstructor
|
|
10
10
|
|
|
11
|
-
protected outlet:
|
|
11
|
+
protected outlet: LslOutlet
|
|
12
12
|
private isRunning = false
|
|
13
13
|
private waitResolve?: () => void
|
|
14
14
|
private timeout?: any
|
|
15
15
|
|
|
16
|
-
protected constructor(outlet:
|
|
16
|
+
protected constructor(outlet: LslOutlet) {
|
|
17
17
|
this.outlet = outlet
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
public static async Create(options?:
|
|
20
|
+
public static async Create(options?: LslEmitterOptions) {
|
|
21
21
|
const outlet = await this.LslStreamOutlet(options)
|
|
22
22
|
return new (this.Class ?? this)(outlet)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
public async emit(markerName: string, options?:
|
|
25
|
+
public async emit(markerName: string, options?: EmitOptions) {
|
|
26
26
|
this.throwIfAlreadyRunning('emit')
|
|
27
27
|
this.isRunning = true
|
|
28
28
|
|
|
@@ -99,10 +99,10 @@ export default class LslEventMarkerEmitter implements EventMarkerEmitter {
|
|
|
99
99
|
channelFormat: 'string',
|
|
100
100
|
sampleRateHz: 0,
|
|
101
101
|
chunkSize: 1,
|
|
102
|
-
} as
|
|
102
|
+
} as LslOutletOptions
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
private static LslStreamOutlet(options?: Partial<
|
|
105
|
+
private static LslStreamOutlet(options?: Partial<LslOutletOptions>) {
|
|
106
106
|
return LslStreamOutlet.Create({
|
|
107
107
|
...this.generateDefaultOutletOptions(),
|
|
108
108
|
...options,
|
|
@@ -110,20 +110,18 @@ export default class LslEventMarkerEmitter implements EventMarkerEmitter {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
export interface
|
|
114
|
-
emit(markerName: string, options?:
|
|
113
|
+
export interface LslEmitter {
|
|
114
|
+
emit(markerName: string, options?: EmitOptions): Promise<void>
|
|
115
115
|
emitMany(markers: TimedEventMarker[]): Promise<void>
|
|
116
116
|
interrupt(): void
|
|
117
117
|
destroy(): void
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
export type
|
|
120
|
+
export type LslEmitterOptions = Partial<LslOutletOptions>
|
|
121
121
|
|
|
122
|
-
export type
|
|
123
|
-
outlet: StreamOutlet
|
|
124
|
-
) => EventMarkerEmitter
|
|
122
|
+
export type LslEmitterConstructor = new (outlet: LslOutlet) => LslEmitter
|
|
125
123
|
|
|
126
|
-
export interface
|
|
124
|
+
export interface EmitOptions {
|
|
127
125
|
waitAfterMs?: number
|
|
128
126
|
}
|
|
129
127
|
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
CHANNEL_FORMATS_MAP,
|
|
7
7
|
} from '@neurodevs/ndx-native'
|
|
8
8
|
|
|
9
|
-
export default class LslStreamInfo implements
|
|
10
|
-
public static Class?:
|
|
9
|
+
export default class LslStreamInfo implements LslInfo {
|
|
10
|
+
public static Class?: LslInfoConstructor
|
|
11
11
|
|
|
12
12
|
public readonly name: string
|
|
13
13
|
public readonly type: string
|
|
@@ -22,7 +22,7 @@ export default class LslStreamInfo implements StreamInfo {
|
|
|
22
22
|
|
|
23
23
|
private lsl = LiblslAdapter.getInstance()
|
|
24
24
|
|
|
25
|
-
protected constructor(options:
|
|
25
|
+
protected constructor(options: LslInfoOptions) {
|
|
26
26
|
const {
|
|
27
27
|
name = `lsl-stream-info-${generateId()}`,
|
|
28
28
|
type = generateId(),
|
|
@@ -46,7 +46,7 @@ export default class LslStreamInfo implements StreamInfo {
|
|
|
46
46
|
this.appendChannelsToStreamInfo()
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
public static Create(options:
|
|
49
|
+
public static Create(options: LslInfoOptions) {
|
|
50
50
|
return new (this.Class ?? this)(options)
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -81,7 +81,7 @@ export default class LslStreamInfo implements StreamInfo {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
export interface
|
|
84
|
+
export interface LslInfo {
|
|
85
85
|
destroy(): void
|
|
86
86
|
readonly name: string
|
|
87
87
|
readonly type: string
|
|
@@ -94,11 +94,9 @@ export interface StreamInfo {
|
|
|
94
94
|
readonly infoHandle: InfoHandle
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
export type
|
|
98
|
-
options: StreamInfoOptions
|
|
99
|
-
) => StreamInfo
|
|
97
|
+
export type LslInfoConstructor = new (options: LslInfoOptions) => LslInfo
|
|
100
98
|
|
|
101
|
-
export interface
|
|
99
|
+
export interface LslInfoOptions {
|
|
102
100
|
channelNames: string[]
|
|
103
101
|
channelFormat: ChannelFormat
|
|
104
102
|
sampleRateHz: number
|