@depz/sensor-sdk 0.1.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.
Files changed (130) hide show
  1. package/README.md +69 -0
  2. package/dist/dataset.d.ts +93 -0
  3. package/dist/dataset.d.ts.map +1 -0
  4. package/dist/dataset.js +253 -0
  5. package/dist/dataset.js.map +1 -0
  6. package/dist/device/device.d.ts +185 -0
  7. package/dist/device/device.d.ts.map +1 -0
  8. package/dist/device/device.js +421 -0
  9. package/dist/device/device.js.map +1 -0
  10. package/dist/device/link.d.ts +62 -0
  11. package/dist/device/link.d.ts.map +1 -0
  12. package/dist/device/link.js +130 -0
  13. package/dist/device/link.js.map +1 -0
  14. package/dist/discovery.d.ts +109 -0
  15. package/dist/discovery.d.ts.map +1 -0
  16. package/dist/discovery.js +281 -0
  17. package/dist/discovery.js.map +1 -0
  18. package/dist/errors.d.ts +39 -0
  19. package/dist/errors.d.ts.map +1 -0
  20. package/dist/errors.js +69 -0
  21. package/dist/errors.js.map +1 -0
  22. package/dist/index.d.ts +42 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +31 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/protocol/bno086.d.ts +36 -0
  27. package/dist/protocol/bno086.d.ts.map +1 -0
  28. package/dist/protocol/bno086.js +38 -0
  29. package/dist/protocol/bno086.js.map +1 -0
  30. package/dist/protocol/common.d.ts +103 -0
  31. package/dist/protocol/common.d.ts.map +1 -0
  32. package/dist/protocol/common.js +121 -0
  33. package/dist/protocol/common.js.map +1 -0
  34. package/dist/protocol/crc.d.ts +17 -0
  35. package/dist/protocol/crc.d.ts.map +1 -0
  36. package/dist/protocol/crc.js +55 -0
  37. package/dist/protocol/crc.js.map +1 -0
  38. package/dist/protocol/framing.d.ts +65 -0
  39. package/dist/protocol/framing.d.ts.map +1 -0
  40. package/dist/protocol/framing.js +152 -0
  41. package/dist/protocol/framing.js.map +1 -0
  42. package/dist/protocol/fwdepz.d.ts +21 -0
  43. package/dist/protocol/fwdepz.d.ts.map +1 -0
  44. package/dist/protocol/fwdepz.js +46 -0
  45. package/dist/protocol/fwdepz.js.map +1 -0
  46. package/dist/protocol/identity.d.ts +14 -0
  47. package/dist/protocol/identity.d.ts.map +1 -0
  48. package/dist/protocol/identity.js +28 -0
  49. package/dist/protocol/identity.js.map +1 -0
  50. package/dist/protocol/sr04.d.ts +38 -0
  51. package/dist/protocol/sr04.d.ts.map +1 -0
  52. package/dist/protocol/sr04.js +58 -0
  53. package/dist/protocol/sr04.js.map +1 -0
  54. package/dist/protocol/usb-ids.d.ts +60 -0
  55. package/dist/protocol/usb-ids.d.ts.map +1 -0
  56. package/dist/protocol/usb-ids.js +79 -0
  57. package/dist/protocol/usb-ids.js.map +1 -0
  58. package/dist/protocol/vl53l8.d.ts +59 -0
  59. package/dist/protocol/vl53l8.d.ts.map +1 -0
  60. package/dist/protocol/vl53l8.js +116 -0
  61. package/dist/protocol/vl53l8.js.map +1 -0
  62. package/dist/sensors/bno086/bno086.d.ts +197 -0
  63. package/dist/sensors/bno086/bno086.d.ts.map +1 -0
  64. package/dist/sensors/bno086/bno086.js +543 -0
  65. package/dist/sensors/bno086/bno086.js.map +1 -0
  66. package/dist/sensors/bno086/reports.d.ts +289 -0
  67. package/dist/sensors/bno086/reports.d.ts.map +1 -0
  68. package/dist/sensors/bno086/reports.js +397 -0
  69. package/dist/sensors/bno086/reports.js.map +1 -0
  70. package/dist/sensors/bno086/sh2.d.ts +307 -0
  71. package/dist/sensors/bno086/sh2.d.ts.map +1 -0
  72. package/dist/sensors/bno086/sh2.js +480 -0
  73. package/dist/sensors/bno086/sh2.js.map +1 -0
  74. package/dist/sensors/bno086/shtp.d.ts +98 -0
  75. package/dist/sensors/bno086/shtp.d.ts.map +1 -0
  76. package/dist/sensors/bno086/shtp.js +205 -0
  77. package/dist/sensors/bno086/shtp.js.map +1 -0
  78. package/dist/sensors/sr04.d.ts +65 -0
  79. package/dist/sensors/sr04.d.ts.map +1 -0
  80. package/dist/sensors/sr04.js +121 -0
  81. package/dist/sensors/sr04.js.map +1 -0
  82. package/dist/sensors/vl53l8/assets/ch.d.ts +16 -0
  83. package/dist/sensors/vl53l8/assets/ch.d.ts.map +1 -0
  84. package/dist/sensors/vl53l8/assets/ch.js +22 -0
  85. package/dist/sensors/vl53l8/assets/ch.js.map +1 -0
  86. package/dist/sensors/vl53l8/assets/cx.d.ts +16 -0
  87. package/dist/sensors/vl53l8/assets/cx.d.ts.map +1 -0
  88. package/dist/sensors/vl53l8/assets/cx.js +22 -0
  89. package/dist/sensors/vl53l8/assets/cx.js.map +1 -0
  90. package/dist/sensors/vl53l8/assets/decode.d.ts +7 -0
  91. package/dist/sensors/vl53l8/assets/decode.d.ts.map +1 -0
  92. package/dist/sensors/vl53l8/assets/decode.js +35 -0
  93. package/dist/sensors/vl53l8/assets/decode.js.map +1 -0
  94. package/dist/sensors/vl53l8/assets/index.d.ts +19 -0
  95. package/dist/sensors/vl53l8/assets/index.d.ts.map +1 -0
  96. package/dist/sensors/vl53l8/assets/index.js +11 -0
  97. package/dist/sensors/vl53l8/assets/index.js.map +1 -0
  98. package/dist/sensors/vl53l8/cnh.d.ts +113 -0
  99. package/dist/sensors/vl53l8/cnh.d.ts.map +1 -0
  100. package/dist/sensors/vl53l8/cnh.js +299 -0
  101. package/dist/sensors/vl53l8/cnh.js.map +1 -0
  102. package/dist/sensors/vl53l8/uld.d.ts +346 -0
  103. package/dist/sensors/vl53l8/uld.d.ts.map +1 -0
  104. package/dist/sensors/vl53l8/uld.js +1388 -0
  105. package/dist/sensors/vl53l8/uld.js.map +1 -0
  106. package/dist/sensors/vl53l8/vl53l8.d.ts +182 -0
  107. package/dist/sensors/vl53l8/vl53l8.d.ts.map +1 -0
  108. package/dist/sensors/vl53l8/vl53l8.js +411 -0
  109. package/dist/sensors/vl53l8/vl53l8.js.map +1 -0
  110. package/dist/transport/loopback.d.ts +18 -0
  111. package/dist/transport/loopback.d.ts.map +1 -0
  112. package/dist/transport/loopback.js +53 -0
  113. package/dist/transport/loopback.js.map +1 -0
  114. package/dist/transport/node.d.ts +51 -0
  115. package/dist/transport/node.d.ts.map +1 -0
  116. package/dist/transport/node.js +123 -0
  117. package/dist/transport/node.js.map +1 -0
  118. package/dist/transport/replay.d.ts +68 -0
  119. package/dist/transport/replay.d.ts.map +1 -0
  120. package/dist/transport/replay.js +196 -0
  121. package/dist/transport/replay.js.map +1 -0
  122. package/dist/transport/types.d.ts +23 -0
  123. package/dist/transport/types.d.ts.map +1 -0
  124. package/dist/transport/types.js +7 -0
  125. package/dist/transport/types.js.map +1 -0
  126. package/dist/transport/webserial.d.ts +69 -0
  127. package/dist/transport/webserial.d.ts.map +1 -0
  128. package/dist/transport/webserial.js +196 -0
  129. package/dist/transport/webserial.js.map +1 -0
  130. package/package.json +77 -0
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # @depz/sensor-sdk
2
+
3
+ TypeScript SDK for the **DEPZ USB sensor line** — three USB CDC-ACM sensors that
4
+ speak one shared framed protocol, usable from **the browser (WebSerial)** and
5
+ **Node (serialport)**:
6
+
7
+ - **HC-SR04** — ultrasonic distance
8
+ - **VL53L8CX / VL53L8CH** — 8×8 multizone Time-of-Flight (host runs the ST ULD)
9
+ - **BNO086** — 9-axis IMU (host runs the SH-2 stack)
10
+
11
+ Pure ESM, zero runtime dependencies on the browser path (`serialport` is an
12
+ optional peer used only by the `./node` entry). Mirrors the Python
13
+ [`depz-sensor-sdk`](https://pypi.org/project/depz-sensor-sdk/) byte-for-byte via
14
+ shared golden test vectors.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install @depz/sensor-sdk
20
+ # for Node serial access also:
21
+ npm install serialport
22
+ ```
23
+
24
+ ## Quick start (browser, WebSerial)
25
+
26
+ ```ts
27
+ import { openDevice } from "@depz/sensor-sdk/web";
28
+
29
+ const port = await navigator.serial.requestPort(); // user gesture
30
+ const dev = await openDevice(port);
31
+ // dev is the correctly-typed sensor class (Sr04 | Vl53l8Cx/Ch | Bno086)
32
+ ```
33
+
34
+ ## Quick start (Node)
35
+
36
+ ```ts
37
+ import { openDevice, listDepzDevices } from "@depz/sensor-sdk/node";
38
+
39
+ const dev = await openDevice(); // default: the DEPZ device with the smallest serial
40
+ ```
41
+
42
+ ## Selecting a device (contract 02 §4.3)
43
+
44
+ Selection is by **USB identity**: only ports with a known DEPZ (vid,pid) are
45
+ considered, ordered by serial — the same rules across Node and browser (in the
46
+ browser the serial comes from a `GET_SERIAL` probe of granted ports).
47
+
48
+ ```ts
49
+ await openDevice(); // default — smallest serial
50
+ await openDevice(0); // by INDEX — Nth candidate, sorted by serial
51
+ await openDevice(1); // second SR04 when two are plugged in
52
+ await openDevice(undefined, { serial: "UMPVOB2461" }); // by exact USB SERIAL
53
+ await openDevice("/dev/ttyACM0"); // explicit port path (Node)
54
+
55
+ for (const info of await listDepzDevices()) // enumerate candidates first
56
+ console.log(info.path, info.sensorType, info.serialNumber);
57
+ ```
58
+
59
+ No DEPZ candidate → throws (never falls back to the first system port), matching
60
+ the Python `open_device()` semantics.
61
+
62
+ Entry points: `@depz/sensor-sdk` (core, transport-agnostic), `@depz/sensor-sdk/web`
63
+ (WebSerial), `@depz/sensor-sdk/node` (serialport). See `docs/` for the per-sensor
64
+ guides and the generated API reference.
65
+
66
+ ## License
67
+
68
+ MIT. Bundled VL53L8 sensor-firmware blobs are © STMicroelectronics
69
+ (BSD-3-Clause).
@@ -0,0 +1,93 @@
1
+ /**
2
+ * `.depzdata` datasets: decoded, multi-device, time-synced recording &
3
+ * playback (contracts/09_DATASET_FORMAT.md). Mirrors the Python
4
+ * `depz_sensor_sdk.dataset` module; browser-safe (no Node APIs).
5
+ */
6
+ import type { DepzDevice } from "./device/device.js";
7
+ export declare const DATASET_SCHEMA = "depz.dataset/1";
8
+ export interface DatasetDeviceMeta {
9
+ serial?: string;
10
+ sensor_type?: string;
11
+ software_name?: string;
12
+ time_sync: {
13
+ offset_us: number;
14
+ rtt_us: number;
15
+ };
16
+ [k: string]: unknown;
17
+ }
18
+ export interface DatasetHeader {
19
+ schema: string;
20
+ created_utc: string;
21
+ devices: Record<string, DatasetDeviceMeta>;
22
+ note?: string;
23
+ [k: string]: unknown;
24
+ }
25
+ export interface DatasetRecord {
26
+ deviceId: string;
27
+ tHostUs: number;
28
+ kind: string;
29
+ value: Record<string, unknown>;
30
+ }
31
+ /** Accumulates a dataset in memory; `dump()` gives the JSONL file content. */
32
+ export declare class DatasetWriter {
33
+ private lines;
34
+ recordsWritten: number;
35
+ constructor(devices: Record<string, DatasetDeviceMeta>, note?: string);
36
+ write(deviceId: string, tHostUs: number, kind: string, value: Record<string, unknown>): void;
37
+ dump(): string;
38
+ }
39
+ /**
40
+ * Hooks live devices into a DatasetWriter on one shared host timeline.
41
+ * Call `await recorder.add(device)` for each device (runs syncTime), then
42
+ * `start()`; `stop()` unhooks; `dump()` returns the file content.
43
+ */
44
+ export declare class DatasetRecorder {
45
+ private opts;
46
+ private entries;
47
+ private writer;
48
+ constructor(opts?: {
49
+ note?: string;
50
+ vl53l8Layers?: boolean;
51
+ });
52
+ add(device: DepzDevice, deviceId?: string, syncSamples?: number): Promise<string>;
53
+ start(): void;
54
+ private hook;
55
+ stop(): void;
56
+ get recordsWritten(): number;
57
+ dump(): string;
58
+ }
59
+ /** Parse a `.depzdata` file; records come back merged by host time. */
60
+ export declare class DatasetReader {
61
+ readonly header: DatasetHeader;
62
+ readonly records: DatasetRecord[];
63
+ constructor(content: string);
64
+ get devices(): Record<string, DatasetDeviceMeta>;
65
+ get durationUs(): number;
66
+ }
67
+ export type PlayerState = "idle" | "playing" | "paused" | "done";
68
+ /**
69
+ * Timeline player: paced delivery with play/pause/seek/speed — the engine
70
+ * behind the viewer's playback mode.
71
+ */
72
+ export declare class DatasetPlayer {
73
+ readonly reader: DatasetReader;
74
+ private idx;
75
+ private timer;
76
+ private stateCbs;
77
+ private recordCbs;
78
+ private _state;
79
+ speed: number;
80
+ constructor(reader: DatasetReader);
81
+ get state(): PlayerState;
82
+ /** Current position in µs from the first record. */
83
+ get positionUs(): number;
84
+ onRecord(cb: (r: DatasetRecord) => void): () => void;
85
+ onState(cb: (s: PlayerState) => void): () => void;
86
+ private setState;
87
+ play(): void;
88
+ pause(): void;
89
+ /** Seek to µs offset from the start; delivery resumes from there. */
90
+ seekUs(offsetUs: number): void;
91
+ private scheduleNext;
92
+ }
93
+ //# sourceMappingURL=dataset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset.d.ts","sourceRoot":"","sources":["../src/dataset.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,253 @@
1
+ /**
2
+ * `.depzdata` datasets: decoded, multi-device, time-synced recording &
3
+ * playback (contracts/09_DATASET_FORMAT.md). Mirrors the Python
4
+ * `depz_sensor_sdk.dataset` module; browser-safe (no Node APIs).
5
+ */
6
+ export const DATASET_SCHEMA = "depz.dataset/1";
7
+ /** Accumulates a dataset in memory; `dump()` gives the JSONL file content. */
8
+ export class DatasetWriter {
9
+ lines;
10
+ recordsWritten = 0;
11
+ constructor(devices, note) {
12
+ const header = {
13
+ schema: DATASET_SCHEMA,
14
+ created_utc: new Date().toISOString().replace(/\.\d+Z$/, "Z"),
15
+ devices,
16
+ };
17
+ if (note)
18
+ header.note = note;
19
+ this.lines = [JSON.stringify(header)];
20
+ }
21
+ write(deviceId, tHostUs, kind, value) {
22
+ this.lines.push(JSON.stringify({ d: deviceId, t: tHostUs, k: kind, v: value }));
23
+ this.recordsWritten += 1;
24
+ }
25
+ dump() {
26
+ return this.lines.join("\n") + "\n";
27
+ }
28
+ }
29
+ /**
30
+ * Hooks live devices into a DatasetWriter on one shared host timeline.
31
+ * Call `await recorder.add(device)` for each device (runs syncTime), then
32
+ * `start()`; `stop()` unhooks; `dump()` returns the file content.
33
+ */
34
+ export class DatasetRecorder {
35
+ opts;
36
+ entries = [];
37
+ writer = null;
38
+ constructor(opts = {}) {
39
+ this.opts = opts;
40
+ }
41
+ async add(device, deviceId, syncSamples = 5) {
42
+ if (this.writer)
43
+ throw new Error("add() all devices before start()");
44
+ const id = deviceId ?? `d${this.entries.length}`;
45
+ const sync = await device.syncTime(syncSamples);
46
+ const meta = {
47
+ time_sync: { offset_us: Number(sync.offsetUs), rtt_us: Number(sync.rttUs) },
48
+ };
49
+ try {
50
+ meta.serial = await device.getSerialNumber();
51
+ const ident = await device.identify();
52
+ meta.software_name = ident.softwareName;
53
+ meta.sensor_type = ident.sensorType ?? "unknown";
54
+ }
55
+ catch {
56
+ /* identity is best-effort */
57
+ }
58
+ this.entries.push({ id, device, offsetUs: sync.offsetUs, meta });
59
+ return id;
60
+ }
61
+ start() {
62
+ if (this.writer)
63
+ return;
64
+ this.writer = new DatasetWriter(Object.fromEntries(this.entries.map((e) => [e.id, e.meta])), this.opts.note);
65
+ for (const entry of this.entries) {
66
+ entry.unsub = this.hook(entry);
67
+ }
68
+ }
69
+ hook(entry) {
70
+ const writer = this.writer;
71
+ const dev = entry.device;
72
+ const toHost = (tsUs) => Number(tsUs - entry.offsetUs);
73
+ if (typeof dev.onMeasurement === "function") {
74
+ return dev.onMeasurement((m) => {
75
+ writer.write(entry.id, toHost(m.timestampUs), "sr04", {
76
+ echo_us: m.echoTimeUs,
77
+ source: m.source,
78
+ });
79
+ });
80
+ }
81
+ if (typeof dev.onFrame === "function") {
82
+ const layers = this.opts.vl53l8Layers ?? false;
83
+ return dev.onFrame((f) => {
84
+ const n = f.resolution;
85
+ const v = {
86
+ resolution: n,
87
+ silicon_temp_degc: f.siliconTempDegc,
88
+ distance_mm: Array.from(f.distanceMm.subarray(0, n)),
89
+ target_status: Array.from(f.targetStatus.subarray(0, n)),
90
+ nb_target_detected: Array.from(f.nbTargetDetected.subarray(0, n)),
91
+ };
92
+ if (layers) {
93
+ v.signal_per_spad = Array.from(f.signalPerSpad.subarray(0, n));
94
+ v.ambient_per_spad = Array.from(f.ambientPerSpad.subarray(0, n));
95
+ v.range_sigma_mm = Array.from(f.rangeSigmaMm.subarray(0, n));
96
+ v.reflectance = Array.from(f.reflectance.subarray(0, n));
97
+ }
98
+ writer.write(entry.id, toHost(f.timestampUs), "vl53l8", v);
99
+ });
100
+ }
101
+ return () => { };
102
+ }
103
+ stop() {
104
+ for (const e of this.entries) {
105
+ e.unsub?.();
106
+ e.unsub = undefined;
107
+ }
108
+ }
109
+ get recordsWritten() {
110
+ return this.writer?.recordsWritten ?? 0;
111
+ }
112
+ dump() {
113
+ if (!this.writer)
114
+ throw new Error("recorder never started");
115
+ return this.writer.dump();
116
+ }
117
+ }
118
+ /** Parse a `.depzdata` file; records come back merged by host time. */
119
+ export class DatasetReader {
120
+ header;
121
+ records;
122
+ constructor(content) {
123
+ const lines = content.split("\n").filter((l) => l.trim().length > 0);
124
+ if (lines.length === 0)
125
+ throw new Error("empty dataset");
126
+ this.header = JSON.parse(lines[0]);
127
+ if (!String(this.header.schema ?? "").startsWith("depz.dataset/")) {
128
+ throw new Error("not a depz.dataset file");
129
+ }
130
+ const raw = lines.slice(1).map((l) => {
131
+ const ev = JSON.parse(l);
132
+ return { deviceId: ev.d, tHostUs: ev.t, kind: ev.k, value: ev.v };
133
+ });
134
+ // Per-device order is monotonic; a stable sort merges exactly.
135
+ this.records = raw
136
+ .map((r, i) => [r, i])
137
+ .sort((a, b) => a[0].tHostUs - b[0].tHostUs || a[1] - b[1])
138
+ .map(([r]) => r);
139
+ }
140
+ get devices() {
141
+ return this.header.devices ?? {};
142
+ }
143
+ get durationUs() {
144
+ if (this.records.length === 0)
145
+ return 0;
146
+ return this.records[this.records.length - 1].tHostUs - this.records[0].tHostUs;
147
+ }
148
+ }
149
+ /**
150
+ * Timeline player: paced delivery with play/pause/seek/speed — the engine
151
+ * behind the viewer's playback mode.
152
+ */
153
+ export class DatasetPlayer {
154
+ reader;
155
+ idx = 0;
156
+ timer = null;
157
+ stateCbs = [];
158
+ recordCbs = [];
159
+ _state = "idle";
160
+ speed = 1.0;
161
+ constructor(reader) {
162
+ this.reader = reader;
163
+ }
164
+ get state() {
165
+ return this._state;
166
+ }
167
+ /** Current position in µs from the first record. */
168
+ get positionUs() {
169
+ const recs = this.reader.records;
170
+ if (recs.length === 0 || this.idx === 0)
171
+ return 0;
172
+ return recs[Math.min(this.idx, recs.length) - 1].tHostUs - recs[0].tHostUs;
173
+ }
174
+ onRecord(cb) {
175
+ this.recordCbs.push(cb);
176
+ return () => {
177
+ this.recordCbs = this.recordCbs.filter((c) => c !== cb);
178
+ };
179
+ }
180
+ onState(cb) {
181
+ this.stateCbs.push(cb);
182
+ return () => {
183
+ this.stateCbs = this.stateCbs.filter((c) => c !== cb);
184
+ };
185
+ }
186
+ setState(s) {
187
+ this._state = s;
188
+ this.stateCbs.forEach((cb) => cb(s));
189
+ }
190
+ play() {
191
+ if (this._state === "playing")
192
+ return;
193
+ if (this.idx >= this.reader.records.length)
194
+ this.idx = 0;
195
+ this.setState("playing");
196
+ this.scheduleNext();
197
+ }
198
+ pause() {
199
+ if (this.timer)
200
+ clearTimeout(this.timer);
201
+ this.timer = null;
202
+ if (this._state === "playing")
203
+ this.setState("paused");
204
+ }
205
+ /** Seek to µs offset from the start; delivery resumes from there. */
206
+ seekUs(offsetUs) {
207
+ const recs = this.reader.records;
208
+ if (recs.length === 0)
209
+ return;
210
+ const target = recs[0].tHostUs + offsetUs;
211
+ let lo = 0;
212
+ let hi = recs.length;
213
+ while (lo < hi) {
214
+ const mid = (lo + hi) >> 1;
215
+ if (recs[mid].tHostUs < target)
216
+ lo = mid + 1;
217
+ else
218
+ hi = mid;
219
+ }
220
+ this.idx = lo;
221
+ if (this._state === "playing") {
222
+ this.pause();
223
+ this.play();
224
+ }
225
+ }
226
+ scheduleNext() {
227
+ const recs = this.reader.records;
228
+ if (this.idx >= recs.length) {
229
+ this.setState("done");
230
+ return;
231
+ }
232
+ const rec = recs[this.idx];
233
+ const emit = () => {
234
+ this.idx += 1;
235
+ this.recordCbs.forEach((cb) => cb(rec));
236
+ if (this._state === "playing")
237
+ this.scheduleNext();
238
+ };
239
+ if (this.speed <= 0 || this.idx === 0) {
240
+ emit();
241
+ return;
242
+ }
243
+ const prev = recs[this.idx - 1];
244
+ const gapMs = (rec.tHostUs - prev.tHostUs) / 1000 / this.speed;
245
+ if (gapMs <= 1) {
246
+ emit();
247
+ }
248
+ else {
249
+ this.timer = setTimeout(emit, gapMs);
250
+ }
251
+ }
252
+ }
253
+ //# sourceMappingURL=dataset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset.js","sourceRoot":"","sources":["../src/dataset.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,185 @@
1
+ /**
2
+ * DepzDevice: request correlation, events, common commands
3
+ * (contracts/02_COMMON_COMMANDS.md, 07_SDK_FACADE.md).
4
+ *
5
+ * Behavioral rules per contract 07 §3: correlation by echoed request opcode
6
+ * (never seq), one in-flight request per opcode, 200 ms default timeout,
7
+ * bounded drop-oldest stream queues, callbacks run on the read-pump context.
8
+ *
9
+ * Mirrors the Python reference `depz_sensor_sdk.device.DeviceBase`. The read
10
+ * pump only starts in `open()`, after the (sub)class constructor finished —
11
+ * subclass stream state always exists before the first dispatch.
12
+ */
13
+ import { type SyncPinConfig } from "../protocol/common.js";
14
+ import { type Identity } from "../protocol/identity.js";
15
+ import { CrcType, type PacketEvent } from "../protocol/framing.js";
16
+ import type { SerialTransport } from "../transport/types.js";
17
+ import { DepzLink, type LinkStats } from "./link.js";
18
+ export declare const DEFAULT_TIMEOUT_MS = 200;
19
+ /** Unsolicited/diagnostic events, mirrored from the Python event classes. */
20
+ export type DeviceEvent = {
21
+ type: "sequenceError";
22
+ expectedSeq: number;
23
+ receivedSeq: number;
24
+ /** true: device saw a gap in host TX (RPT 0x84). */
25
+ reportedByDevice: boolean;
26
+ } | {
27
+ type: "linkCrcError";
28
+ cmd: number;
29
+ seq: number;
30
+ } | {
31
+ type: "trash";
32
+ data: Uint8Array;
33
+ } | {
34
+ type: "unsolicitedStatus";
35
+ status: number;
36
+ } | {
37
+ type: "text";
38
+ cmd: number;
39
+ text: string;
40
+ } | {
41
+ type: "temperature";
42
+ timestampUs: bigint;
43
+ celsius: number;
44
+ } | {
45
+ type: "disconnected";
46
+ reason: string;
47
+ };
48
+ /** Result of `syncTime()` (contract 02 §5). offset = device − host clock. */
49
+ export interface TimeSync {
50
+ offsetUs: bigint;
51
+ rttUs: bigint;
52
+ syncedAtHostUs: bigint;
53
+ }
54
+ /** Monotonic host clock in µs — the host side of all time-sync math. */
55
+ export declare function hostNowUs(): bigint;
56
+ /** Returned by a matcher to decline a packet. */
57
+ export declare const NO_MATCH: unique symbol;
58
+ /**
59
+ * Claims a packet for an in-flight request: return the parsed result, or
60
+ * `NO_MATCH` to let other consumers see the packet.
61
+ */
62
+ export type Matcher<T> = (pkt: PacketEvent) => T | typeof NO_MATCH;
63
+ export interface RequestOptions<T> {
64
+ /** First shot at every non-status packet while the request is pending. */
65
+ matcher?: Matcher<T>;
66
+ /** Resolve on RPT_STATUS(cmd, OK) — for commands whose success reply is the ack. */
67
+ okCompletes?: boolean;
68
+ timeoutMs?: number;
69
+ }
70
+ /**
71
+ * Bounded drop-oldest async stream (contract 07 §3). Doubles as the async
72
+ * iterator returned by e.g. `Sr04.measurements()`; `droppedCount` is the
73
+ * monotonic count of items evicted while the consumer lagged.
74
+ */
75
+ export declare class StreamQueue<T> implements AsyncIterableIterator<T> {
76
+ droppedCount: number;
77
+ private readonly maxsize;
78
+ private readonly onDone;
79
+ private buf;
80
+ private waiters;
81
+ private closed;
82
+ constructor(maxsize: number, onDone?: () => void);
83
+ /** Producer side: enqueue, evicting the oldest item when full. */
84
+ push(item: T): void;
85
+ /** End the stream; buffered items are still drained by the consumer. */
86
+ close(): void;
87
+ next(): Promise<IteratorResult<T>>;
88
+ return(): Promise<IteratorResult<T>>;
89
+ [Symbol.asyncIterator](): AsyncIterableIterator<T>;
90
+ }
91
+ export interface DeviceOptions {
92
+ timeoutMs?: number;
93
+ txCrcType?: CrcType;
94
+ }
95
+ /**
96
+ * Connection to one DEPZ device in application mode. Construct over any
97
+ * `SerialTransport`, then `await open()` before use.
98
+ */
99
+ export declare class DepzDevice {
100
+ timeoutMs: number;
101
+ protected readonly link: DepzLink;
102
+ private readonly transport;
103
+ private readonly pending;
104
+ private eventCbs;
105
+ private streams;
106
+ private timeSyncState;
107
+ private closing;
108
+ private torndown;
109
+ constructor(transport: SerialTransport, opts?: DeviceOptions);
110
+ /** Open the transport and start the read pump. */
111
+ open(): Promise<void>;
112
+ close(): Promise<void>;
113
+ get stats(): LinkStats;
114
+ private handleLinkClosed;
115
+ private failAllPending;
116
+ private handleParserEvent;
117
+ private dispatch;
118
+ private settle;
119
+ /**
120
+ * Hook for sensor subclasses: route streaming reports here. Return true
121
+ * when the packet was consumed. Runs after status/matcher/common-report
122
+ * routing, on the read-pump context.
123
+ */
124
+ protected handleReport(pkt: PacketEvent): boolean;
125
+ /** Register a stream to be closed on device teardown. */
126
+ protected registerStream(stream: {
127
+ close(): void;
128
+ }): () => void;
129
+ /**
130
+ * Subscribe to unsolicited/diagnostic events (read-pump context; do not
131
+ * block). Returns an unsubscribe function.
132
+ */
133
+ onEvent(cb: (ev: DeviceEvent) => void): () => void;
134
+ protected emitEvent(event: DeviceEvent): void;
135
+ /**
136
+ * Send `cmd` and wait for its correlated completion (contract 02 §1).
137
+ *
138
+ * Exactly one of the completion paths must be configured:
139
+ * - `okCompletes` — RPT_STATUS(cmd, OK) resolves with undefined;
140
+ * - `matcher` — first packet for which the matcher returns non-`NO_MATCH`
141
+ * resolves with the matcher's return value.
142
+ * Non-OK RPT_STATUS echoing `cmd` always rejects (BusyError for ERR_BUSY,
143
+ * StatusError otherwise). One in-flight request per opcode.
144
+ */
145
+ request<T = void>(cmd: number, payload?: Uint8Array, opts?: RequestOptions<T>): Promise<T>;
146
+ /** Matcher for a typed report identified by its report ID alone. */
147
+ static expectReport<T>(reportId: number, unpack: (payload: Uint8Array) => T): Matcher<T>;
148
+ /** Matcher for RPT_TEXT echoing `requestCmd`. */
149
+ static expectText(requestCmd: number): Matcher<string>;
150
+ getDeviceName(): Promise<string>;
151
+ getSoftwareName(): Promise<string>;
152
+ getSerialNumber(): Promise<string>;
153
+ /** Classify the running firmware (contract 02 §4). */
154
+ identify(): Promise<Identity>;
155
+ /** Last cached MCU temperature in °C (device refreshes ~2 Hz). */
156
+ readMcuTemperature(): Promise<number>;
157
+ /** NTP-style sync; keeps the lowest-RTT sample (contract 02 §5). */
158
+ syncTime(samples?: number): Promise<TimeSync>;
159
+ get timeSync(): TimeSync | null;
160
+ /** Device µs → host monotonic µs (requires a prior `syncTime`). */
161
+ toHostTimeUs(deviceTsUs: bigint): bigint;
162
+ getReportPayloadCrc(): Promise<CrcType>;
163
+ /** Set the device→host payload CRC mode (host→device is per-packet). */
164
+ setReportPayloadCrc(crcType: CrcType): Promise<void>;
165
+ getSyncPin(pin: number): Promise<SyncPinConfig>;
166
+ setSyncPin(config: SyncPinConfig): Promise<void>;
167
+ /** DEVICE_RESET: device ACKs then reboots; the link will drop. */
168
+ reset(): Promise<void>;
169
+ /**
170
+ * Ask the device to reboot into the resident bootloader and close this
171
+ * connection. Re-discovery/flash flow lives in the bootloader module
172
+ * (contract 06).
173
+ */
174
+ enterBootloaderMode(): Promise<void>;
175
+ }
176
+ /**
177
+ * Software-sync several devices to the common host monotonic clock.
178
+ *
179
+ * Runs `syncTime()` on each device so that every device's `toHostTimeUs()`
180
+ * maps its own timestamps onto ONE shared host timeline — the basis for
181
+ * correlating reports from multiple sensors live. Returns a Map
182
+ * {device → TimeSync}. Mirrors the Python reference `sync_time_all`.
183
+ */
184
+ export declare function syncTimeAll(devices: Iterable<DepzDevice>, samples?: number): Promise<Map<DepzDevice, TimeSync>>;
185
+ //# sourceMappingURL=device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/device/device.ts"],"names":[],"mappings":"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"}