@intomind/sdk 0.0.0-stage → 1.4.1

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.
@@ -0,0 +1,1433 @@
1
+ /**
2
+ * The IntoMind BLE protocol, version 1.0.
3
+ *
4
+ * This module is the wire and nothing else. It has no device in it, no
5
+ * transport, no state, and no opinion about what a caller does with a
6
+ * message. Every function takes bytes and returns what the contract says
7
+ * those bytes mean, or throws a `ProtocolError` saying why they are not a
8
+ * message.
9
+ *
10
+ * It is held to `contract/conformance.json`, which the firmware emits from
11
+ * the codec it runs. Every message here decodes those vectors to the stated
12
+ * fields and refuses the malformed ones for the stated reason, so this
13
+ * implementation and the device cannot drift apart without a test failing.
14
+ *
15
+ * Three things are worth knowing before reading further.
16
+ *
17
+ * **Capabilities are the device's answer.** What a device can do is in the
18
+ * bits it reports, never in a table of hardware here. A device that does not
19
+ * claim a capability is not asked for it.
20
+ *
21
+ * **The timeline is never silently repaired.** A forward step in the sample
22
+ * index is a loss with an exact count. A step that is not forward is a break
23
+ * whose extent is not a number, and this module says so rather than
24
+ * returning a count that would be a fiction.
25
+ *
26
+ * **Nothing is assumed about width.** The number of channels, the converter
27
+ * resolution, the reference voltage, and the tick rate all come from the
28
+ * device, and the scaling helper takes them as arguments.
29
+ */
30
+ export { Crc32, crc32, sha256 } from "./digest.ts";
31
+ /** The protocol version this module implements, major and minor. */
32
+ export declare const VERSION: readonly [number, number];
33
+ /** The full identifier for one characteristic. */
34
+ export declare function uuid(fill: number): string;
35
+ /**
36
+ * The sixteen bit fill each characteristic sits at. A notification is
37
+ * routed by this rather than by its full identifier, so nothing downstream
38
+ * has to format or compare strings.
39
+ */
40
+ export declare const FILL: {
41
+ readonly SERVICE: 1;
42
+ readonly DEVICE_INFO: 2;
43
+ readonly CONTROL: 3;
44
+ readonly CONTROL_RESPONSE: 4;
45
+ readonly EEG_DATA: 5;
46
+ readonly STATUS: 6;
47
+ /** Held by an earlier development firmware. Never reused. */
48
+ readonly RESERVED_0007: 7;
49
+ readonly UPDATE_CONTROL: 8;
50
+ readonly UPDATE_DATA: 9;
51
+ readonly PREDICTIONS: 10;
52
+ /** 1.2: the encoder's output for each window. */
53
+ readonly EMBEDDINGS: 11;
54
+ };
55
+ /** The service's full identifier, advertised by every device. */
56
+ export declare const SERVICE: string;
57
+ /** Device Info's full identifier. */
58
+ export declare const DEVICE_INFO: string;
59
+ /** Control Point's full identifier. */
60
+ export declare const CONTROL: string;
61
+ /** Control Response's full identifier. */
62
+ export declare const CONTROL_RESPONSE: string;
63
+ /** EEG Data's full identifier. */
64
+ export declare const EEG_DATA: string;
65
+ /** Status's full identifier. */
66
+ export declare const STATUS: string;
67
+ /** Update Control's full identifier. */
68
+ export declare const UPDATE_CONTROL: string;
69
+ /** Update Data's full identifier. */
70
+ export declare const UPDATE_DATA: string;
71
+ /** Predictions' full identifier. */
72
+ export declare const PREDICTIONS: string;
73
+ /** Embeddings' full identifier. */
74
+ export declare const EMBEDDINGS: string;
75
+ /** The name a device advertises under, before its own identifier. */
76
+ export declare const NAME_PREFIX = "IntoMind-";
77
+ /**
78
+ * 1.3: the product name a composed device name ends with. A 1.3 device
79
+ * advertises `[name's] [adjective] IntoMind One`; a 1.2 device advertised
80
+ * `IntoMind-` and four hex digits.
81
+ */
82
+ export declare const PRODUCT_NAME = "IntoMind One";
83
+ /** Why bytes are not a message. The contract distinguishes these three. */
84
+ export type Refusal = "truncated" | "invalid" | "reserved";
85
+ /** Bytes that are not a message, with the reason they are not. */
86
+ export declare class ProtocolError extends Error {
87
+ /** Which of the three reasons this is. */
88
+ readonly reason: Refusal;
89
+ /** Build one with its reason and message. A decoder throws one of the three subclasses. */
90
+ constructor(reason: Refusal, message: string);
91
+ }
92
+ /** The message ends before the fields it promises. */
93
+ export declare class Truncated extends ProtocolError {
94
+ /** Build one with the message saying what is missing. */
95
+ constructor(message: string);
96
+ }
97
+ /** A field holds a value the contract does not define. */
98
+ export declare class Invalid extends ProtocolError {
99
+ /** Build one with the message saying what is wrong. */
100
+ constructor(message: string);
101
+ }
102
+ /**
103
+ * A number the contract reserves. A device answers these unsupported rather
104
+ * than treating them as malformed, and so does a host.
105
+ */
106
+ export declare class Reserved extends ProtocolError {
107
+ /** Build one with the message naming the reserved number. */
108
+ constructor(message: string);
109
+ }
110
+ declare function hex(b: Uint8Array): string;
111
+ /** Bytes from a hex string, for tests and for anything that logs one. */
112
+ export declare function fromHex(s: string): Uint8Array;
113
+ export { hex as toHex };
114
+ /** Every control point opcode this contract defines, by name. */
115
+ export declare const OPCODES: {
116
+ readonly start_stream: 1;
117
+ readonly stop_stream: 2;
118
+ readonly set_rate: 16;
119
+ readonly set_gain: 17;
120
+ readonly set_mode: 32;
121
+ readonly set_leadoff: 48;
122
+ readonly time_sync: 64;
123
+ readonly set_samples_per_packet: 65;
124
+ readonly get_battery: 66;
125
+ readonly get_boot_info: 67;
126
+ readonly reset_epoch: 80;
127
+ readonly clear_bonds: 83;
128
+ readonly set_predictions: 128;
129
+ readonly select_head: 129;
130
+ readonly list_heads: 130;
131
+ readonly remove_head: 131;
132
+ readonly get_model_info: 132;
133
+ readonly set_prediction_input: 133;
134
+ readonly get_prediction_input: 134;
135
+ readonly set_bias: 50;
136
+ readonly get_bias_diagnostic: 51;
137
+ readonly get_pipeline_catalog: 144;
138
+ readonly get_pipeline: 145;
139
+ readonly set_pipeline: 146;
140
+ readonly clear_pipeline: 147;
141
+ readonly restore_pipeline_default: 148;
142
+ readonly get_converter_registers: 52;
143
+ readonly get_indicator: 68;
144
+ readonly set_indicator: 69;
145
+ readonly identify: 70;
146
+ readonly set_embeddings: 135;
147
+ readonly set_model_interval: 136;
148
+ readonly get_model_interval: 137;
149
+ readonly get_name: 71;
150
+ readonly set_name: 72;
151
+ readonly list_head_encoders: 138;
152
+ readonly soft_reset: 240;
153
+ };
154
+ /** The name of any opcode `OPCODES` defines. */
155
+ export type OpcodeName = keyof typeof OPCODES;
156
+ /** Opcode names by their number, the reverse of `OPCODES`. */
157
+ export declare const NAME_BY_OPCODE: ReadonlyMap<number, OpcodeName>;
158
+ /** Opcodes that take one argument byte, and require it. */
159
+ export declare const TAKES_ARG: ReadonlySet<OpcodeName>;
160
+ /**
161
+ * Opcodes new in 1.1 that carry a payload after the opcode byte, laid out
162
+ * as the processing section below says. The only requests longer than two
163
+ * bytes.
164
+ */
165
+ export declare const TAKES_PAYLOAD: ReadonlySet<OpcodeName>;
166
+ /**
167
+ * Opcodes a 1.0 device does not know. A host sends one only to a device
168
+ * whose Device Info claims the capability behind it.
169
+ */
170
+ export declare const NEW_IN_1_1: ReadonlySet<OpcodeName>;
171
+ /** Opcodes a 1.1 device does not know, likewise gated on a capability. */
172
+ export declare const NEW_IN_1_2: ReadonlySet<OpcodeName>;
173
+ /** Opcodes a 1.2 device does not know: the model's cadence and the device's name. */
174
+ export declare const NEW_IN_1_3: ReadonlySet<OpcodeName>;
175
+ /**
176
+ * Numbers an earlier development firmware used, and numbers the contract
177
+ * keeps for factory and bench builds. Never reused for anything else.
178
+ */
179
+ export declare const RESERVED_OPCODES: ReadonlySet<number>;
180
+ /** A control response's status byte, in words. */
181
+ export declare const STATUS_CODES: Readonly<Record<number, string>>;
182
+ /** The gain a `gain_code` of 0 to 6 selects. */
183
+ export declare const GAIN_BY_CODE: readonly number[];
184
+ /** The code for a gain, the reverse of `GAIN_BY_CODE`. */
185
+ export declare const CODE_BY_GAIN: ReadonlyMap<number, number>;
186
+ /** The rate in samples per second a `rate_code` selects. */
187
+ export declare const RATE_BY_CODE: Readonly<Record<number, number>>;
188
+ /** The code for a rate, the reverse of `RATE_BY_CODE`. */
189
+ export declare const CODE_BY_RATE: ReadonlyMap<number, number>;
190
+ /** The bit in Device Info's rate mask that stands for each rate. */
191
+ export declare const RATE_BY_INFO_BIT: Readonly<Record<number, number>>;
192
+ /** 1.3 adds synthetic: the converter is not driven and the device generates the signal, on a device that claims it. */
193
+ export declare const MODES: {
194
+ readonly normal: 0;
195
+ readonly test: 1;
196
+ readonly short: 2;
197
+ readonly synthetic: 3;
198
+ };
199
+ /** The name of any mode `MODES` defines. */
200
+ export type ModeName = keyof typeof MODES;
201
+ /** Mode names by their code, the reverse of `MODES`. */
202
+ export declare const MODE_BY_CODE: Readonly<Record<number, ModeName>>;
203
+ /** What a charger state code means. */
204
+ export declare const CHARGER_STATES: Readonly<Record<number, string>>;
205
+ /** Why the device last booted, by `boot_reason` code. */
206
+ export declare const BOOT_REASONS: Readonly<Record<number, string>>;
207
+ /**
208
+ * One control point write.
209
+ *
210
+ * `opcode` is a name or a number. An opcode that takes an argument requires
211
+ * one, and one that does not takes none, because the device refuses either
212
+ * mistake and a host should not make it.
213
+ */
214
+ export declare function encodeRequest(opcode: OpcodeName | number, arg?: number | Uint8Array | null): Uint8Array;
215
+ /** One control point write, as `decodeRequest` reads it. */
216
+ export interface Request {
217
+ /** The raw opcode byte, even one this contract does not define. */
218
+ readonly opcode: number;
219
+ /** The opcode's name. */
220
+ readonly name: OpcodeName;
221
+ /** The one argument byte, for an opcode that takes one. */
222
+ readonly arg: number | null;
223
+ /** The bytes after the opcode, for an opcode that takes a payload. */
224
+ readonly payload: Uint8Array | null;
225
+ }
226
+ /**
227
+ * A control point write, as the device reads it. For tests and for anything
228
+ * that has to speak both sides of the contract.
229
+ */
230
+ export declare function decodeRequest(data: Uint8Array): Request;
231
+ /** One control response, as `decodeResponse` reads it. */
232
+ export interface Response {
233
+ /** The opcode this answers, echoed back even when it is not one this contract defines. */
234
+ readonly opcode: number;
235
+ /** Null for a number this contract does not define, which is echoed back. */
236
+ readonly name: OpcodeName | null;
237
+ /** The status byte, 0 for success. */
238
+ readonly status: number;
239
+ /** `status` in words, from `STATUS_CODES`. */
240
+ readonly statusName: string;
241
+ /** Whether `status` is 0. */
242
+ readonly ok: boolean;
243
+ /** The bytes after the opcode and status, specific to what was asked. */
244
+ readonly payload: Uint8Array;
245
+ }
246
+ /** One control response, as the device sends it. */
247
+ export declare function decodeResponse(data: Uint8Array): Response;
248
+ /**
249
+ * One control response. The opcode is a number rather than a name because a
250
+ * device echoes back whatever it was sent, including a reserved number.
251
+ */
252
+ export declare function encodeResponse(opcode: number, status: number, payload?: Uint8Array): Uint8Array;
253
+ /** The device's own time, from the answer to a time request. */
254
+ export declare function decodeTimeSync(payload: Uint8Array): bigint;
255
+ /** What a device can do, as the bits it reports. */
256
+ export declare const CAPABILITIES: {
257
+ readonly battery_voltage: number;
258
+ readonly battery_low_flag: number;
259
+ readonly leadoff: number;
260
+ readonly test_signal: number;
261
+ readonly dcdc_mode: number;
262
+ readonly input_short: number;
263
+ readonly update: number;
264
+ readonly model: number;
265
+ readonly model_ready: number;
266
+ readonly heads: number;
267
+ readonly pipeline: number;
268
+ readonly bias_drive: number;
269
+ readonly embeddings: number;
270
+ readonly indicator: number;
271
+ readonly converter_registers: number;
272
+ readonly model_cadence: number;
273
+ };
274
+ /** The name of any capability bit `CAPABILITIES` defines. */
275
+ export type CapabilityName = keyof typeof CAPABILITIES;
276
+ /** 1.3: capability bits 16 and up, the second word of Device Info at offset 76. Bit 16 of the contract is bit 0 here. */
277
+ export declare const CAPABILITIES_HIGH: {
278
+ readonly synthetic: number;
279
+ readonly device_name: number;
280
+ };
281
+ /** The name of any capability bit `CAPABILITIES_HIGH` defines. */
282
+ export type CapabilityHighName = keyof typeof CAPABILITIES_HIGH;
283
+ /** Length of the v0.1 prefix, which 1.0 left where it was. */
284
+ export declare const INFO_V01_LEN = 27;
285
+ /** Length of the 1.0 layout. */
286
+ export declare const INFO_LEN_1_0 = 76;
287
+ /** 1.3: the 1.0 layout with a second capability word appended. */
288
+ export declare const INFO_LEN = 80;
289
+ /** What a device with no battery telemetry reports instead of a percent. */
290
+ export declare const BATTERY_UNKNOWN = 255;
291
+ /** What `DeviceInfo`'s constructor takes. The optional fields default to zero or null. */
292
+ export interface DeviceInfoFields {
293
+ /** Protocol major and minor. */
294
+ protocol: readonly [number, number];
295
+ /** Firmware major, minor, and patch. */
296
+ firmware: readonly [number, number, number];
297
+ /** Channel count. */
298
+ channels: number;
299
+ /** Converter resolution, in bits. */
300
+ adcBits: number;
301
+ /** Device time ticks per second. */
302
+ tickHz: number;
303
+ /** Converter reference voltage, in microvolts. */
304
+ vrefUv: number;
305
+ /** The sixteen bit capability mask. */
306
+ capabilities: number;
307
+ /** Which rates the device supports, as a bit mask. */
308
+ supportedRates: number;
309
+ /** The device's stable per-unit identifier, as hex. */
310
+ deviceId: string;
311
+ /** Hardware major, minor, and patch, when the device reports the 1.0 extension. */
312
+ hardware?: readonly [number, number, number] | null;
313
+ /** The exact firmware build, when reported. */
314
+ buildId?: string | null;
315
+ /** The model's embedding width, zero for no model runtime. */
316
+ modelEmbedDim?: number;
317
+ /** The model's native sample rate. */
318
+ modelNativeSps?: number;
319
+ /** Samples per prediction window at the model's native rate. */
320
+ modelWindowSamples?: number;
321
+ /** User head slots. */
322
+ headSlots?: number;
323
+ /** The largest `out_dim` a head may have. */
324
+ headMaxOutputs?: number;
325
+ /** Capacity of one head slot, in bytes. */
326
+ headSlotBytes?: number;
327
+ /** The largest Update Data write the device accepts. */
328
+ updateChunkMax?: number;
329
+ /** Capacity of one application slot, including its header. */
330
+ appSlotBytes?: number;
331
+ /** Capacity of the weights image, including its header. */
332
+ weightsImageBytes?: number;
333
+ /** 1.3: capability bits 16 and up. Zero on a device whose info ends at 76 bytes. */
334
+ capabilitiesHigh?: number;
335
+ }
336
+ /** Everything a device says about itself, and nothing a host assumed. */
337
+ export declare class DeviceInfo {
338
+ /** Protocol major and minor. */
339
+ readonly protocol: readonly [number, number];
340
+ /** Firmware major, minor, and patch. */
341
+ readonly firmware: readonly [number, number, number];
342
+ /** Channel count. */
343
+ readonly channels: number;
344
+ /** Converter resolution, in bits. */
345
+ readonly adcBits: number;
346
+ /** Device time ticks per second. */
347
+ readonly tickHz: number;
348
+ /** Converter reference voltage, in microvolts. */
349
+ readonly vrefUv: number;
350
+ /** The sixteen bit capability mask, bits 0 to 15. `can` is how to read it. */
351
+ readonly capabilities: number;
352
+ /** Which rates the device supports, as a bit mask. `rates` is how to read it. */
353
+ readonly supportedRates: number;
354
+ /** The device's stable per-unit identifier, as hex. */
355
+ readonly deviceId: string;
356
+ /** Present on a device that reports the 1.0 layout. */
357
+ readonly hardware: readonly [number, number, number] | null;
358
+ /** The exact firmware build, or null when the device reports no extension. */
359
+ readonly buildId: string | null;
360
+ /** The model's embedding width, zero for no model runtime. */
361
+ readonly modelEmbedDim: number;
362
+ /** The model's native sample rate. */
363
+ readonly modelNativeSps: number;
364
+ /** Samples per prediction window at the model's native rate. */
365
+ readonly modelWindowSamples: number;
366
+ /** User head slots. */
367
+ readonly headSlots: number;
368
+ /** The largest `out_dim` a head may have. */
369
+ readonly headMaxOutputs: number;
370
+ /** Capacity of one head slot, in bytes. */
371
+ readonly headSlotBytes: number;
372
+ /** The largest Update Data write the device accepts. */
373
+ readonly updateChunkMax: number;
374
+ /** Capacity of one application slot, including its header. */
375
+ readonly appSlotBytes: number;
376
+ /** Capacity of the weights image, including its header. */
377
+ readonly weightsImageBytes: number;
378
+ /** The second capability word, bits 16 and up (1.3). Zero on a device whose info ends at 76 bytes. */
379
+ readonly capabilitiesHigh: number;
380
+ /** Build from the fields `decodeDeviceInfo` read, or assembled by hand. */
381
+ constructor(f: DeviceInfoFields);
382
+ /**
383
+ * Whether the device claims a capability. Unknown names are false rather
384
+ * than an error, so a host written against a later version of this
385
+ * contract still runs.
386
+ */
387
+ can(capability: CapabilityName | CapabilityHighName | string): boolean;
388
+ /** Whether the device reported the 1.0 extension. */
389
+ get extensionPresent(): boolean;
390
+ /** The rates the device declares, from its own mask. */
391
+ get rates(): number[];
392
+ /** `protocol` as `major.minor`. */
393
+ get protocolString(): string;
394
+ /** `firmware` as `major.minor.patch`. */
395
+ get firmwareString(): string;
396
+ /** `hardware` as `major.minor.patch`, or null when the device reports no extension. */
397
+ get hardwareString(): string | null;
398
+ /** One count, in microvolts, at a gain. From the device's own reference and width. */
399
+ microvoltsPerCount(gain: number): number;
400
+ }
401
+ /** Device Info, as the device sends it. */
402
+ export declare function decodeDeviceInfo(data: Uint8Array): DeviceInfo;
403
+ /**
404
+ * Device info as a device reports it. A device that has no extension to
405
+ * report encodes the v0.1 prefix, which is what an older one sends.
406
+ */
407
+ export declare function encodeDeviceInfo(info: DeviceInfo): Uint8Array;
408
+ /** The length of a Status message, in bytes. */
409
+ export declare const STATUS_LEN = 12;
410
+ /** The bits of Status's flags byte. */
411
+ export declare const STATUS_FLAGS: {
412
+ readonly usb_present: number;
413
+ readonly buffer_high_watermark: number;
414
+ };
415
+ /** Status, decoded, read or notified. */
416
+ export interface Status {
417
+ /** 0 idle, 1 streaming. */
418
+ readonly state: number;
419
+ /** Whether `state` is streaming. */
420
+ readonly streaming: boolean;
421
+ /** The mode code in force. See `MODES`. */
422
+ readonly mode: number;
423
+ /** `mode` in words. */
424
+ readonly modeName: string;
425
+ /** The gain code in force. */
426
+ readonly gainCode: number;
427
+ /** `gainCode` as a gain, or null for a code this contract does not define. */
428
+ readonly gain: number | null;
429
+ /** The rate code in force. */
430
+ readonly rateCode: number;
431
+ /** `rateCode` as samples per second, or null for a code this contract does not define. */
432
+ readonly sps: number | null;
433
+ /** The charger state code. */
434
+ readonly chargerState: number;
435
+ /** `chargerState` in words. */
436
+ readonly chargerName: string;
437
+ /** Null when the device has no battery telemetry, never 255 percent. */
438
+ readonly batteryPercent: number | null;
439
+ /** The raw byte: 0xFF for unknown, rather than null. */
440
+ readonly batteryPercentRaw: number;
441
+ /** Lead-off status latched with the most recently acquired sample. Bit n is channel n+1. */
442
+ readonly loffStatp: number;
443
+ /** Whether USB power is present (1.4). */
444
+ readonly usbPresent: boolean;
445
+ /** Whether the device's buffer is at or above three quarters full. */
446
+ readonly bufferHighWatermark: boolean;
447
+ /** Telemetry. The account of what a host missed is the sample index. */
448
+ readonly droppedTotal: number;
449
+ /** Samples currently buffered on the device. */
450
+ readonly bufferFill: number;
451
+ }
452
+ /** Status, as the device sends it. */
453
+ export declare function decodeStatus(data: Uint8Array): Status;
454
+ /** What `encodeStatus` takes. */
455
+ export interface StatusFields {
456
+ /** 0 idle, 1 streaming. */
457
+ state: number;
458
+ /** The mode code in force. */
459
+ mode: number;
460
+ /** The gain code in force. */
461
+ gainCode: number;
462
+ /** The rate code in force. */
463
+ rateCode: number;
464
+ /** The charger state code. */
465
+ chargerState: number;
466
+ /** Null for a device with no battery telemetry. */
467
+ batteryPercent: number | null;
468
+ /** Lead-off status latched with the most recently acquired sample. */
469
+ loffStatp: number;
470
+ /** Whether USB power is present. */
471
+ usbPresent?: boolean;
472
+ /** Whether the device's buffer is at or above three quarters full. */
473
+ bufferHighWatermark?: boolean;
474
+ /** Samples acquired but never delivered since power on. */
475
+ droppedTotal: number;
476
+ /** Samples currently buffered on the device. */
477
+ bufferFill: number;
478
+ }
479
+ /** Status, as the device sends it. */
480
+ export declare function encodeStatus(s: StatusFields): Uint8Array;
481
+ /** The EEG data packet type. */
482
+ export declare const PACKET_EEG = 1;
483
+ /** The prediction packet type. */
484
+ export declare const PACKET_PREDICTION = 2;
485
+ /** 1.3: samples the device generated with its converter off, in exactly the layout of `PACKET_EEG`. */
486
+ export declare const PACKET_SYNTHETIC = 4;
487
+ /** The length of an EEG data packet's header, before the samples. */
488
+ export declare const DATA_HEADER_LEN = 20;
489
+ /** The single bit flags of an EEG data packet's flags byte. Mode occupies the other two, via `MODE_SHIFT` and `MODE_MASK`. */
490
+ export declare const DATA_FLAGS: {
491
+ readonly discontinuity: number;
492
+ readonly leadoff_active: number;
493
+ readonly usb_present: number;
494
+ };
495
+ /** Where the mode bits sit in an EEG data packet's flags byte. */
496
+ export declare const MODE_SHIFT = 2;
497
+ /** The mode bits of an EEG data packet's flags byte, before shifting. */
498
+ export declare const MODE_MASK: number;
499
+ /** `samples_lost_before` saturates here. The sample index is authoritative. */
500
+ export declare const LOST_SATURATED = 65535;
501
+ /**
502
+ * One notification of samples, decoded but not scaled.
503
+ *
504
+ * `counts` is one row per sample across the channels, in converter counts.
505
+ * Scaling is the caller's, with the device's own reference and width,
506
+ * because this module assumes nothing about either.
507
+ */
508
+ export interface Packet {
509
+ /** 0x01 for a measured sample, 0x04 for one the device generated (1.3). */
510
+ readonly packetType: number;
511
+ /** Whether a break precedes this packet. */
512
+ readonly discontinuity: boolean;
513
+ /** Whether lead-off was active when this packet was built. */
514
+ readonly leadoffActive: boolean;
515
+ /** The mode code the samples were taken in. */
516
+ readonly mode: number;
517
+ /** `mode` in words. */
518
+ readonly modeName: string;
519
+ /** Whether USB power was present (1.4). */
520
+ readonly usbPresent: boolean;
521
+ /** A convenience count of samples lost before this packet, saturating at `LOST_SATURATED`. `index` continuity is authoritative. */
522
+ readonly samplesLostBefore: number;
523
+ /** The first sample's index, monotonic and wrapping at 2^32. */
524
+ readonly index: number;
525
+ /** The first sample's device time, in the device's ticks. */
526
+ readonly deviceTime: bigint;
527
+ /** Samples carried in this packet. */
528
+ readonly nSamples: number;
529
+ /** Lead-off status latched with the packet's last sample. */
530
+ readonly loffStatp: number;
531
+ /** The gain code the samples were taken at. */
532
+ readonly gainCode: number;
533
+ /** `gainCode` as a gain. */
534
+ readonly gain: number;
535
+ /** The rate code the samples were taken at. */
536
+ readonly rateCode: number;
537
+ /** `rateCode` as samples per second, or null for a code this contract does not define. */
538
+ readonly sps: number | null;
539
+ /** One row per sample, each row one converter count per channel. */
540
+ readonly counts: number[][];
541
+ /** 1.3: the samples were generated by the device, not measured (packet type 0x04). Never store one as a measurement. */
542
+ readonly synthetic: boolean;
543
+ }
544
+ /** One EEG notification. `channels` comes from the device. */
545
+ export declare function decodePacket(data: Uint8Array, channels: number): Packet;
546
+ /** What `encodePacket` takes. */
547
+ export interface PacketFields {
548
+ /** The first sample's index. */
549
+ index: number;
550
+ /** The first sample's device time, in the device's ticks. */
551
+ deviceTime: bigint;
552
+ /** The gain code the samples were taken at. */
553
+ gainCode: number;
554
+ /** The rate code the samples were taken at. */
555
+ rateCode: number;
556
+ /** One row per sample, each row one value per channel, in counts. */
557
+ counts: ReadonlyArray<ArrayLike<number>>;
558
+ /** Whether a break precedes this packet. */
559
+ discontinuity?: boolean;
560
+ /** Whether lead-off was active. */
561
+ leadoffActive?: boolean;
562
+ /** The mode code the samples were taken in. */
563
+ mode?: number;
564
+ /** Whether USB power was present. */
565
+ usbPresent?: boolean;
566
+ /** A convenience count of samples lost before this packet. */
567
+ samplesLostBefore?: number;
568
+ /** Lead-off status latched with the packet's last sample. */
569
+ loffStatp?: number;
570
+ /** 1.3: write the packet as synthetic samples, type 0x04. */
571
+ synthetic?: boolean;
572
+ }
573
+ /**
574
+ * One EEG notification, as the device sends it. The payload is converter
575
+ * order, big endian, which is why the device copies its bytes out without
576
+ * swapping any of them.
577
+ */
578
+ export declare function encodePacket(p: PacketFields): Uint8Array;
579
+ /** One big endian, two's complement int24, sign extended. */
580
+ export declare function int24(b: Uint8Array, at?: number): number;
581
+ /**
582
+ * One count in microvolts. Every term comes from the device: its reference,
583
+ * the gain the packet was taken at, and the converter's width.
584
+ */
585
+ export declare function microvolts(raw: number, vrefUv: number, gain: number, adcBits: number): number;
586
+ /** What one packet's indexing says happened since the one before it. */
587
+ export interface Continuity {
588
+ /** The contract's own words for the three cases. */
589
+ readonly verdict: "continuous" | "gap" | "break";
590
+ /** Samples missing. Null for a break, whose extent is not a number. */
591
+ readonly lost: number | null;
592
+ /** Whether this is a gap or a break rather than continuous. */
593
+ readonly broken: boolean;
594
+ }
595
+ /**
596
+ * Judge two packets by the contract's own rule.
597
+ *
598
+ * The subtraction is signed, so the index's own wrap at two to the thirty
599
+ * second is exact. A step that is not forward is a break in the timeline
600
+ * rather than a loss, and reporting the unsigned difference there claims a
601
+ * loss of four billion samples that never happened.
602
+ */
603
+ export declare function continuity(previousIndex: number, previousN: number, index: number, discontinuity: boolean): Continuity;
604
+ /** The length of a prediction packet's header, before the outputs. */
605
+ export declare const PREDICTION_HEADER_LEN = 28;
606
+ /** The bits of a prediction packet's flags byte. */
607
+ export declare const PREDICTION_FLAGS: {
608
+ readonly gap_in_window: number;
609
+ readonly duty_reduced: number;
610
+ readonly leadoff_in_window: number;
611
+ };
612
+ /** One prediction notification, decoded. */
613
+ export interface Prediction {
614
+ /** 0x02 for a prediction. */
615
+ readonly packetType: number;
616
+ /** The slot of the head that produced this. */
617
+ readonly headSlot: number;
618
+ /** The identity of the head that produced this. */
619
+ readonly headId: string;
620
+ /** The window's first sample's index. */
621
+ readonly index: number;
622
+ /** The window's first sample's device time. */
623
+ readonly deviceTime: bigint;
624
+ /** The window's length, in raw samples at the current rate. */
625
+ readonly windowSamples: number;
626
+ /** Whether a gap fell inside the window. */
627
+ readonly gapInWindow: boolean;
628
+ /** Whether the device skipped windows to stay within its compute budget. */
629
+ readonly dutyReduced: boolean;
630
+ /** Whether an electrode was off at some point in the window. */
631
+ readonly leadoffInWindow: boolean;
632
+ /** The head's outputs for this window. */
633
+ readonly outputs: number[];
634
+ /**
635
+ * 1.1: what the window was taken from, an `INPUT_SOURCES` value: the
636
+ * stream, the natural signal, or the model's own chain. A 1.0 device
637
+ * says the stream.
638
+ */
639
+ readonly inputSource: number;
640
+ }
641
+ /** One prediction notification, as the device sends it. */
642
+ export declare function decodePrediction(data: Uint8Array): Prediction;
643
+ /** What `encodePrediction` takes. */
644
+ export interface PredictionFields {
645
+ /** The slot of the head that produced this. */
646
+ headSlot: number;
647
+ /** The identity of the head that produced this. */
648
+ headId: string;
649
+ /** The window's first sample's index. */
650
+ index: number;
651
+ /** The window's first sample's device time. */
652
+ deviceTime: bigint;
653
+ /** The window's length, in raw samples. */
654
+ windowSamples: number;
655
+ /** The head's outputs for this window. */
656
+ outputs: ArrayLike<number>;
657
+ /** Whether a gap fell inside the window. */
658
+ gapInWindow?: boolean;
659
+ /** Whether the device skipped windows to stay within its compute budget. */
660
+ dutyReduced?: boolean;
661
+ /** Whether an electrode was off at some point in the window. */
662
+ leadoffInWindow?: boolean;
663
+ /** What the window was taken from, an `INPUT_SOURCES` value. */
664
+ inputSource?: number;
665
+ }
666
+ /** One prediction notification, as the device sends it. */
667
+ export declare function encodePrediction(p: PredictionFields): Uint8Array;
668
+ /** GET_BATTERY's answer. */
669
+ export interface Battery {
670
+ /** The measured pack voltage, in millivolts. */
671
+ readonly millivolts: number;
672
+ /** A state of charge estimate, or null when the device has no battery telemetry. */
673
+ readonly percent: number | null;
674
+ /** The charger state code. */
675
+ readonly chargerState: number;
676
+ /** `chargerState` in words. */
677
+ readonly chargerName: string;
678
+ }
679
+ /** GET_BATTERY's answer, as the device sends it. */
680
+ export declare function decodeBattery(payload: Uint8Array): Battery;
681
+ /** GET_BATTERY's answer, as the device sends it. */
682
+ export declare function encodeBattery(b: {
683
+ millivolts: number;
684
+ percent: number | null;
685
+ chargerState: number;
686
+ }): Uint8Array;
687
+ /** GET_BOOT_INFO's answer. */
688
+ export interface BootInfo {
689
+ /** Which application slot is running: 0 A, 1 B. */
690
+ readonly activeSlot: number;
691
+ /** The boot reason code. */
692
+ readonly bootReason: number;
693
+ /** `bootReason` in words. */
694
+ readonly bootReasonName: string;
695
+ /** 0 trial, the running image has not yet confirmed itself. 1 confirmed. */
696
+ readonly slotState: number;
697
+ /** A slot on trial has not yet been confirmed by the firmware in it. */
698
+ readonly confirmed: boolean;
699
+ /** Boots of this unit since manufacture. */
700
+ readonly bootCount: number;
701
+ }
702
+ /** GET_BOOT_INFO's answer, as the device sends it. */
703
+ export declare function decodeBootInfo(payload: Uint8Array): BootInfo;
704
+ /** GET_BOOT_INFO's answer, as the device sends it. */
705
+ export declare function encodeBootInfo(b: {
706
+ activeSlot: number;
707
+ bootReason: number;
708
+ slotState: number;
709
+ bootCount: number;
710
+ }): Uint8Array;
711
+ /** A head slot's state, in words, by its `state` code. */
712
+ export declare const HEAD_STATES: Readonly<Record<number, string>>;
713
+ /** The model runtime's state, in words, by its `model_state` code. */
714
+ export declare const MODEL_STATES: Readonly<Record<number, string>>;
715
+ /** An encoder id a head that does not name its encoder carries. */
716
+ export declare const NO_ENCODER_ID = "0000000000000000";
717
+ /** The slot byte meaning no head is selected. */
718
+ export declare const NO_HEAD = 255;
719
+ /** The length of one LIST_HEADS record, in bytes. */
720
+ export declare const HEAD_ENTRY_LEN = 30;
721
+ /** One LIST_HEADS record: one head slot. */
722
+ export interface HeadEntry {
723
+ /** 0 for the built-in head, 1 and up for user slots. */
724
+ readonly slot: number;
725
+ /** The slot's state code. See `HEAD_STATES`. */
726
+ readonly state: number;
727
+ /** `state` in words. */
728
+ readonly stateName: string;
729
+ /** The head's output count. */
730
+ readonly outDim: number;
731
+ /** The head's identity, the first eight bytes of its SHA-256. Zero when empty. */
732
+ readonly headId: string;
733
+ /** The head's name. */
734
+ readonly name: string;
735
+ /** Whether `state` is valid. */
736
+ readonly usable: boolean;
737
+ /**
738
+ * 1.3: the encoder the head was trained beside, as its file recorded it; `NO_ENCODER_ID` when it did not say.
739
+ * From 1.4 the list carries none: `decodeHeadEncoders` reads them, and `withEncoders` puts them here.
740
+ */
741
+ readonly encoderId: string;
742
+ }
743
+ /** LIST_HEADS's answer: the head slots, and which one is selected. */
744
+ export interface HeadList {
745
+ /** Null means no head is selected. */
746
+ readonly activeSlot: number | null;
747
+ /** The slots, in the order the device reports them. */
748
+ readonly heads: HeadEntry[];
749
+ }
750
+ /** The head slots, and which one is selected. */
751
+ export declare function decodeHeads(payload: Uint8Array): HeadList;
752
+ /** A LIST_HEADS payload as a device sends it: the records and nothing after them. */
753
+ export declare function encodeHeads(activeSlot: number | null, heads: ReadonlyArray<{
754
+ slot: number;
755
+ state: number;
756
+ outDim: number;
757
+ headId: string;
758
+ name: string;
759
+ }>): Uint8Array;
760
+ /** One LIST_HEAD_ENCODERS record (1.4): a slot and the encoder id its head names. */
761
+ export declare const HEAD_ENCODER_LEN = 9;
762
+ /**
763
+ * 1.4: the encoder each head names, by slot, from LIST_HEAD_ENCODERS: `NO_ENCODER_ID` for an empty slot or a head
764
+ * that does not say.
765
+ */
766
+ export declare function decodeHeadEncoders(payload: Uint8Array): ReadonlyMap<number, string>;
767
+ /** A LIST_HEAD_ENCODERS payload: one record for each head, in the order given. */
768
+ export declare function encodeHeadEncoders(heads: ReadonlyArray<{
769
+ slot: number;
770
+ encoderId: string;
771
+ }>): Uint8Array;
772
+ /** The heads, each with the encoder `encoders` says it names. A slot it does not mention keeps what it had. */
773
+ export declare function withEncoders(list: HeadList, encoders: ReadonlyMap<number, string>): HeadList;
774
+ /** GET_MODEL_INFO's answer. */
775
+ export interface ModelInfo {
776
+ /** The model runtime's state code. See `MODEL_STATES`. */
777
+ readonly modelState: number;
778
+ /** `modelState` in words. */
779
+ readonly modelStateName: string;
780
+ /** Whether `modelState` is ready, so predictions can be enabled. */
781
+ readonly ready: boolean;
782
+ /** Null means no head is selected. */
783
+ readonly activeHead: number | null;
784
+ /** Whether predictions are enabled. */
785
+ readonly predictionsOn: boolean;
786
+ /** The loaded weights' identity. Zero unless ready. */
787
+ readonly encoderId: string;
788
+ /** The loaded weights' major, minor, and patch. */
789
+ readonly weightsVersion: readonly [number, number, number];
790
+ /**
791
+ * 1.1: the signal classes the loaded model declares it takes, as
792
+ * `INPUT_CLASSES` bits. A 1.0 device sends zero, which reads as the time
793
+ * domain.
794
+ */
795
+ readonly inputClasses: number;
796
+ /**
797
+ * 1.2: tokens the loaded model produces per channel per window, the shape
798
+ * of the token form of embeddings. A 1.1 device's message ends before this
799
+ * byte and reads as zero: it sends no tokens.
800
+ */
801
+ readonly tokensPerChannel: number;
802
+ /** 1.3: the time one pass of the encoder takes on the device, milliseconds; zero before the first pass. */
803
+ readonly passMs: number;
804
+ /** 1.3: the interval between described windows in force, seconds; zero is every window the device can. */
805
+ readonly intervalS: number;
806
+ /** 1.3: whether the device holds a generator, so it can offer the synthetic signal. */
807
+ readonly generator: boolean;
808
+ }
809
+ /** GET_MODEL_INFO's answer, as the device sends it. */
810
+ export declare function decodeModelInfo(payload: Uint8Array): ModelInfo;
811
+ /** GET_MODEL_INFO's answer, as the device sends it. */
812
+ export declare function encodeModelInfo(m: {
813
+ modelState: number;
814
+ activeHead: number | null;
815
+ predictionsOn: boolean;
816
+ encoderId: string;
817
+ weightsVersion: readonly [number, number, number];
818
+ inputClasses?: number;
819
+ /** 1.2: when given, the message is seventeen bytes. */
820
+ tokensPerChannel?: number | undefined;
821
+ /** 1.3: when given, the message is twenty two bytes. */
822
+ passMs?: number | undefined;
823
+ intervalS?: number | undefined;
824
+ generator?: boolean | undefined;
825
+ }): Uint8Array;
826
+ /** GET_MODEL_INTERVAL's answer: the interval in force and the least the device keeps, seconds. */
827
+ export interface ModelInterval {
828
+ /** The interval in force, seconds. 0 is every window the device can. */
829
+ readonly intervalS: number;
830
+ /** The least interval the device keeps, seconds. */
831
+ readonly minimumS: number;
832
+ }
833
+ /** GET_MODEL_INTERVAL's answer, as the device sends it. */
834
+ export declare function decodeModelInterval(payload: Uint8Array): ModelInterval;
835
+ /** SET_MODEL_INTERVAL's payload: the seconds, little endian. Zero is every window the device can. */
836
+ export declare function encodeModelInterval(seconds: number): Uint8Array;
837
+ /** The most a composed name may be, in bytes: what a scan response carries. The same limit bounds each part. */
838
+ export declare const NAME_MAX_COMPOSED = 29;
839
+ /** A name or an adjective: UTF-8 of at most 29 bytes, no control characters, no leading or trailing space. Empty is allowed. */
840
+ export declare function namePartOk(part: string): boolean;
841
+ /** The device's name as it composes it: `name's adjective product`, the possessive only with a name, the adjective only when given. */
842
+ export declare function composeName(name: string, adjective: string, product?: string): string;
843
+ /** Whether the composition fits the air, so the device will accept it. */
844
+ export declare function nameFits(name: string, adjective: string, product?: string): boolean;
845
+ /** SET_NAME's payload and GET_NAME's answer: each part as a length and its UTF-8 bytes. */
846
+ export declare function encodeNameParts(name: string, adjective: string): Uint8Array;
847
+ /** GET_NAME's answer or SET_NAME's request, as the device reads or sends it: the name and the adjective. */
848
+ export declare function decodeNameParts(payload: Uint8Array): {
849
+ name: string;
850
+ adjective: string;
851
+ };
852
+ /**
853
+ * Labels for a list a person picks devices from, in the list's order: each
854
+ * device's own name, and when two in the list share one, a number after the
855
+ * later ones (`Ada's Blue IntoMind One 2`). The number is the host's and
856
+ * lasts as long as the list. The device knows nothing of it, since two
857
+ * devices cannot agree on which is second without a host that sees them
858
+ * both. A device heard before its name arrived is labeled with the product
859
+ * name.
860
+ */
861
+ export declare function displayNames(names: ReadonlyArray<string | null | undefined>): string[];
862
+ /** The processing stage kinds this version defines, by name. */
863
+ export declare const STAGE_KINDS: {
864
+ readonly highpass: 1;
865
+ readonly lowpass: 2;
866
+ readonly notch: 3;
867
+ };
868
+ /** The name of any stage kind `STAGE_KINDS` defines. */
869
+ export type StageKindName = keyof typeof STAGE_KINDS;
870
+ /** Stage kind names by their number, the reverse of `STAGE_KINDS`. */
871
+ export declare const KIND_NAMES: Readonly<Record<number, StageKindName>>;
872
+ /** The classes a stage kind belongs to: map, representation, or detector. */
873
+ export declare const STAGE_CLASSES: {
874
+ readonly map: 0;
875
+ readonly representation: 1;
876
+ readonly detector: 2;
877
+ };
878
+ /** Stage class names by their number, the reverse of `STAGE_CLASSES`. */
879
+ export declare const CLASS_NAMES: Readonly<Record<number, string>>;
880
+ /** 1.3 adds synthetic: reported by a device generating its signal, never requested. */
881
+ export declare const INPUT_SOURCES: {
882
+ readonly stream: 0;
883
+ readonly natural: 1;
884
+ readonly own_chain: 2;
885
+ readonly synthetic: 3;
886
+ };
887
+ /** The name of any source `INPUT_SOURCES` defines. */
888
+ export type InputSourceName = keyof typeof INPUT_SOURCES;
889
+ /** Input source names by their number, the reverse of `INPUT_SOURCES`. */
890
+ export declare const INPUT_SOURCE_NAMES: Readonly<Record<number, InputSourceName>>;
891
+ /** The signal classes a model declares it takes, as bits of `inputClasses`. */
892
+ export declare const INPUT_CLASSES: {
893
+ readonly time_domain: number;
894
+ readonly representation: number;
895
+ };
896
+ /** Whether the chain in force is the device's default or one a host set. */
897
+ export declare const PIPELINE_ORIGINS: {
898
+ readonly default: 0;
899
+ readonly host: 1;
900
+ };
901
+ /** The name of any origin `PIPELINE_ORIGINS` defines. */
902
+ export type OriginName = keyof typeof PIPELINE_ORIGINS;
903
+ /** Origin names by their number, the reverse of `PIPELINE_ORIGINS`. */
904
+ export declare const ORIGIN_NAMES: Readonly<Record<number, OriginName>>;
905
+ /** The bias drive's modes: off, on, or loop open. */
906
+ export declare const BIAS_MODES: {
907
+ readonly off: 0;
908
+ readonly on: 1;
909
+ readonly loop_open: 2;
910
+ };
911
+ /** The name of any mode `BIAS_MODES` defines. */
912
+ export type BiasModeName = keyof typeof BIAS_MODES;
913
+ /** The most stages one chain may hold. */
914
+ export declare const MAX_STAGES = 12;
915
+ /** The most parameters one stage may hold. */
916
+ export declare const MAX_PARAMS = 4;
917
+ /** The most notch stages one chain may hold. */
918
+ export declare const MAX_NOTCHES = 8;
919
+ /** The device's default high-pass corner, in tenths of a hertz. */
920
+ export declare const DEFAULT_HIGH_PASS_DHZ = 5;
921
+ /** Both mains fundamentals, second and third harmonics, four hertz wide: the device's default until a region is chosen. */
922
+ export declare const DEFAULT_MAINS_BANDS_DHZ: ReadonlyArray<readonly [number, number]>;
923
+ /** One stage of a chain: a kind and its parameters, in tenths of a hertz for every kind this version defines. */
924
+ export interface Stage {
925
+ /** A `STAGE_KINDS` value. */
926
+ readonly kind: number;
927
+ /** The stage's parameters, in tenths of a hertz for every kind this version defines. */
928
+ readonly params: readonly number[];
929
+ }
930
+ /** `kind` in words, from `KIND_NAMES`. */
931
+ export declare function stageName(s: Stage): string;
932
+ /** A high-pass at a corner, at least 0.1 Hz. */
933
+ export declare function highpass(cornerHz: number): Stage;
934
+ /** A low-pass at a corner, or the automatic corner at four tenths of the sample rate when none is given. */
935
+ export declare function lowpass(cornerHz?: number): Stage;
936
+ /** A notch over the band between two edges. Any band. */
937
+ export declare function notch(lowHz: number, highHz: number): Stage;
938
+ /** `u8 n_stages`, then per stage `u8 kind, u8 n_params, u16[n_params]`. */
939
+ export declare function encodeChain(stages: readonly Stage[]): Uint8Array;
940
+ /** A chain descriptor that is the whole of `data`. */
941
+ export declare function decodeChain(data: Uint8Array): Stage[];
942
+ /** One kind a device runs: its number, class, parameter count, how many instances a chain may hold, and its name. */
943
+ export interface CatalogEntry {
944
+ /** The stage kind's number. */
945
+ readonly kind: number;
946
+ /** The stage class's number. See `STAGE_CLASSES`. */
947
+ readonly cls: number;
948
+ /** `cls` in words. */
949
+ readonly className: string;
950
+ /** Parameters this kind takes. */
951
+ readonly nParams: number;
952
+ /** The most instances of this kind a chain may hold. */
953
+ readonly maxInstances: number;
954
+ /** The kind's name, as the device's catalog states it. */
955
+ readonly name: string;
956
+ }
957
+ /**
958
+ * The kinds this version of the contract defines, with their limits. A
959
+ * device's own catalog is the authority; this is what a host checks against
960
+ * when it has not read one.
961
+ */
962
+ export declare const CONTRACT_CATALOG: readonly CatalogEntry[];
963
+ /** The length of one catalog record, in bytes. */
964
+ export declare const CATALOG_ENTRY_LEN = 12;
965
+ /** GET_PIPELINE_CATALOG: `u8 n_kinds`, then records of twelve bytes. */
966
+ export declare function decodeCatalog(payload: Uint8Array): CatalogEntry[];
967
+ /** GET_PIPELINE_CATALOG's answer, as the device sends it. */
968
+ export declare function encodeCatalog(entries: readonly CatalogEntry[]): Uint8Array;
969
+ /** The chain in force, and whether it is the device's own default for its current rate or one a host set. */
970
+ export interface PipelineState {
971
+ /** A `PIPELINE_ORIGINS` value. */
972
+ readonly origin: number;
973
+ /** `origin` in words. */
974
+ readonly originName: OriginName;
975
+ /** The chain in force. */
976
+ readonly stages: Stage[];
977
+ }
978
+ /** GET_PIPELINE's answer, as the device sends it. */
979
+ export declare function decodePipelineState(payload: Uint8Array): PipelineState;
980
+ /** GET_PIPELINE's answer, as the device sends it. */
981
+ export declare function encodePipelineState(origin: number, stages: readonly Stage[]): Uint8Array;
982
+ /**
983
+ * Where the model's input comes from: the stream as it is, the natural
984
+ * signal, or a chain of the model's own. In an answer the stages are the
985
+ * chain in effect for the model.
986
+ */
987
+ export interface PredictionInput {
988
+ /** An `INPUT_SOURCES` value. */
989
+ readonly source: number;
990
+ /** `source` in words. */
991
+ readonly sourceName: InputSourceName;
992
+ /** The chain in effect for the model, when `source` is the model's own. */
993
+ readonly stages: Stage[];
994
+ }
995
+ /** SET_PREDICTION_INPUT's request, as the device reads it. */
996
+ export declare function encodePredictionInput(source: number | InputSourceName, stages?: readonly Stage[]): Uint8Array;
997
+ /** GET_PREDICTION_INPUT's answer, as the device sends it. */
998
+ export declare function decodePredictionInput(payload: Uint8Array): PredictionInput;
999
+ /** The bias output over the device's own window, in millivolts. */
1000
+ export interface BiasDiagnostic {
1001
+ /** Mean bias output, in millivolts. */
1002
+ readonly meanMv: number;
1003
+ /** Standard deviation of the bias output, in millivolts. */
1004
+ readonly sdMv: number;
1005
+ /** Minimum bias output, in millivolts. */
1006
+ readonly minMv: number;
1007
+ /** Maximum bias output, in millivolts. */
1008
+ readonly maxMv: number;
1009
+ }
1010
+ /** GET_BIAS_DIAGNOSTIC's answer, as the device sends it. */
1011
+ export declare function decodeBiasDiagnostic(payload: Uint8Array): BiasDiagnostic;
1012
+ /** GET_BIAS_DIAGNOSTIC's answer, as the device sends it. */
1013
+ export declare function encodeBiasDiagnostic(d: BiasDiagnostic): Uint8Array;
1014
+ /** The status lamp's verbosity levels: silent, reserved, or verbose. */
1015
+ export declare const INDICATOR_LEVELS: {
1016
+ readonly silent: 0;
1017
+ readonly reserved: 1;
1018
+ readonly verbose: 2;
1019
+ };
1020
+ /** The name of any level `INDICATOR_LEVELS` defines. */
1021
+ export type IndicatorLevelName = keyof typeof INDICATOR_LEVELS;
1022
+ /** Indicator level names by their number, the reverse of `INDICATOR_LEVELS`. */
1023
+ export declare const INDICATOR_LEVEL_NAMES: Readonly<Record<number, IndicatorLevelName>>;
1024
+ /** What each level shows, most important first. */
1025
+ export declare const INDICATOR_SHOWS: Readonly<Record<IndicatorLevelName, readonly string[]>>;
1026
+ /** Low battery begins below this percent of the device's own estimate and ends above the clear percent, or on external power. */
1027
+ export declare const LOW_BATTERY_PERCENT = 20;
1028
+ /** Low battery clears once the device's own estimate rises back above this percent. */
1029
+ export declare const LOW_BATTERY_CLEAR_PERCENT = 25;
1030
+ /** The most seconds one identify request may run. */
1031
+ export declare const IDENTIFY_MAX_SECONDS = 30;
1032
+ /** Which chip family a converter register file belongs to, by its `family` code. */
1033
+ export declare const CONVERTER_FAMILIES: Readonly<Record<number, string>>;
1034
+ /** GET_CONVERTER_REGISTERS's answer. */
1035
+ export interface ConverterRegisters {
1036
+ /** Which chip family the registers belong to. See `CONVERTER_FAMILIES`. */
1037
+ readonly family: number;
1038
+ /** `family` in words. */
1039
+ readonly familyName: string;
1040
+ /** Address of the first register carried. */
1041
+ readonly first: number;
1042
+ /** The registers themselves, consecutive addresses starting at `first`. */
1043
+ readonly values: Uint8Array;
1044
+ }
1045
+ /** GET_CONVERTER_REGISTERS's answer, as the device sends it. */
1046
+ export declare function decodeConverterRegisters(payload: Uint8Array): ConverterRegisters;
1047
+ /** GET_CONVERTER_REGISTERS's answer, as the device sends it. */
1048
+ export declare function encodeConverterRegisters(r: {
1049
+ family: number;
1050
+ first: number;
1051
+ values: Uint8Array;
1052
+ }): Uint8Array;
1053
+ /**
1054
+ * An ADS1299 family register file in words, from address 0: the identity,
1055
+ * the data rate, the test signal, the reference and bias settings, lead-off
1056
+ * detection, each channel's gain and input, and the bias and lead-off sense
1057
+ * masks. Every field is the datasheet's (Texas Instruments SBAS499C). A
1058
+ * file shorter than 24 bytes is described as far as it goes.
1059
+ */
1060
+ export declare function describeAds1299Registers(values: Uint8Array): Record<string, unknown>;
1061
+ /** The embedding packet type. */
1062
+ export declare const PACKET_EMBEDDING = 3;
1063
+ /** The length of an embedding notification's header, before its values. */
1064
+ export declare const EMBEDDING_HEADER_LEN = 28;
1065
+ /** The `token` byte meaning this is the window embedding, not one token. */
1066
+ export declare const WINDOW_TOKEN = 255;
1067
+ /** Which embeddings a device streams: off, window, tokens, or both. */
1068
+ export declare const EMBEDDING_FORMS: {
1069
+ readonly off: 0;
1070
+ readonly window: 1;
1071
+ readonly tokens: 2;
1072
+ readonly both: 3;
1073
+ };
1074
+ /** The name of any form `EMBEDDING_FORMS` defines. */
1075
+ export type EmbeddingFormName = keyof typeof EMBEDDING_FORMS;
1076
+ /** The bits of an embedding notification's flags byte. */
1077
+ export declare const EMBEDDING_FLAGS: {
1078
+ readonly gap_in_window: number;
1079
+ readonly duty_reduced: number;
1080
+ readonly leadoff_in_window: number;
1081
+ readonly more_parts: number;
1082
+ };
1083
+ /** One embedding notification, decoded: the whole vector, or the part carried here. */
1084
+ export interface Embedding {
1085
+ /** 0x03 for an embedding. */
1086
+ readonly packetType: number;
1087
+ /** The window's first sample's index. */
1088
+ readonly index: number;
1089
+ /** The window's first sample's device time. */
1090
+ readonly deviceTime: bigint;
1091
+ /** The window's length, in raw samples at the current rate. */
1092
+ readonly windowSamples: number;
1093
+ /** The encoder that produced this, as `GET_MODEL_INFO` reports it. */
1094
+ readonly encoderId: string;
1095
+ /** An `INPUT_SOURCES` value. */
1096
+ readonly inputSource: number;
1097
+ /** Values in the whole vector. */
1098
+ readonly embedDim: number;
1099
+ /** Index of the first value carried here. */
1100
+ readonly first: number;
1101
+ /** Null for the window embedding; otherwise the token's index, channel-major over all channels. */
1102
+ readonly token: number | null;
1103
+ /** Whether another notification carries the rest of this vector. */
1104
+ readonly moreParts: boolean;
1105
+ /** Whether a gap fell inside the window. */
1106
+ readonly gapInWindow: boolean;
1107
+ /** Whether the device skipped windows to stay within its compute budget. */
1108
+ readonly dutyReduced: boolean;
1109
+ /** Whether an electrode was off at some point in the window. */
1110
+ readonly leadoffInWindow: boolean;
1111
+ /** Quantized values: the encoder's output times 4096. */
1112
+ readonly values: number[];
1113
+ }
1114
+ /** One embedding notification, as the device sends it. */
1115
+ export declare function decodeEmbedding(data: Uint8Array): Embedding;
1116
+ /** What `encodeEmbedding` takes. */
1117
+ export interface EmbeddingFields {
1118
+ /** The window's first sample's index. */
1119
+ index: number;
1120
+ /** The window's first sample's device time. */
1121
+ deviceTime: bigint;
1122
+ /** The window's length, in raw samples. */
1123
+ windowSamples: number;
1124
+ /** The encoder that produced this. */
1125
+ encoderId: string;
1126
+ /** An `INPUT_SOURCES` value. */
1127
+ inputSource?: number;
1128
+ /** Values in the whole embedding. */
1129
+ embedDim: number;
1130
+ /** Index of the first value carried here. */
1131
+ first?: number;
1132
+ /** Null for the window embedding, otherwise the token's index. */
1133
+ token?: number | null;
1134
+ /** Whether another notification carries the rest of this vector. */
1135
+ moreParts?: boolean;
1136
+ /** Whether a gap fell inside the window. */
1137
+ gapInWindow?: boolean;
1138
+ /** Whether the device skipped windows to stay within its compute budget. */
1139
+ dutyReduced?: boolean;
1140
+ /** Whether an electrode was off at some point in the window. */
1141
+ leadoffInWindow?: boolean;
1142
+ /** The values carried in this notification. */
1143
+ values: ArrayLike<number>;
1144
+ }
1145
+ /** One embedding notification, as the device sends it. */
1146
+ export declare function encodeEmbedding(e: EmbeddingFields): Uint8Array;
1147
+ /** One window's embeddings, whole. */
1148
+ export interface EmbeddingWindow {
1149
+ /** The window's first sample's index. */
1150
+ readonly index: number;
1151
+ /** The window's first sample's device time. */
1152
+ readonly deviceTime: bigint;
1153
+ /** The window's length, in raw samples. */
1154
+ readonly windowSamples: number;
1155
+ /** The encoder that produced this. */
1156
+ readonly encoderId: string;
1157
+ /** An `INPUT_SOURCES` value. */
1158
+ readonly inputSource: number;
1159
+ /** Values in the whole embedding. */
1160
+ readonly embedDim: number;
1161
+ /** Whether a gap fell inside the window. */
1162
+ readonly gapInWindow: boolean;
1163
+ /** Whether the device skipped windows to stay within its compute budget. */
1164
+ readonly dutyReduced: boolean;
1165
+ /** Whether an electrode was off at some point in the window. */
1166
+ readonly leadoffInWindow: boolean;
1167
+ /** The window embedding, quantized, when asked for. */
1168
+ readonly embedding: number[] | null;
1169
+ /** The tokens, quantized, `channels × tokensPerChannel` vectors of `embedDim`, channel-major, when asked for. */
1170
+ readonly tokens: number[][] | null;
1171
+ }
1172
+ /**
1173
+ * Puts notifications back together into whole windows. Built for a device's
1174
+ * channel count, the model's tokens per channel, and the form asked for;
1175
+ * `feed` takes each decoded notification and returns the window when its
1176
+ * last piece arrives. Windows older than the two most recent that never
1177
+ * completed are dropped and counted in `incomplete`.
1178
+ */
1179
+ export declare class EmbeddingAssembler {
1180
+ #private;
1181
+ /** The device's channel count, as built. */
1182
+ readonly channels: number;
1183
+ /** Tokens per channel per window, as built. */
1184
+ readonly tokensPerChannel: number;
1185
+ /** Which form this was built for. */
1186
+ readonly form: EmbeddingFormName;
1187
+ /** Windows dropped before their last piece arrived. */
1188
+ incomplete: number;
1189
+ /** Build one for a device's channel count, the model's tokens per channel, and the form asked for. */
1190
+ constructor(channels: number, tokensPerChannel: number, form?: EmbeddingFormName);
1191
+ /** Tokens expected per window: `channels` times `tokensPerChannel`. */
1192
+ get nTokens(): number;
1193
+ /** Feed one decoded notification in. Returns the window once its last piece has arrived, else null. */
1194
+ feed(e: Embedding): EmbeddingWindow | null;
1195
+ }
1196
+ /** The automatic low-pass corner for a rate, in tenths of a hertz. */
1197
+ export declare function lowPassAutoDhz(rateSps: number): number;
1198
+ /** The first corner a rate refuses, in tenths of a hertz: nine tenths of Nyquist. */
1199
+ export declare function cornerLimitDhz(rateSps: number): number;
1200
+ /** Whether a chain can run at a rate. Throws `Invalid` saying why not. */
1201
+ export declare function checkChain(stages: readonly Stage[], rateSps: number, catalog?: readonly CatalogEntry[]): void;
1202
+ /** The device's own default for a rate: a 0.5 Hz high-pass, the mains bands the rate can represent, the automatic low-pass. */
1203
+ export declare function defaultChain(rateSps: number): Stage[];
1204
+ /**
1205
+ * The notch stages for a region's mains: the fundamental, its second and its third harmonic, four hertz wide.
1206
+ * Given a rate, only the bands that rate can represent: at 250 samples a second the device refuses the higher harmonics.
1207
+ */
1208
+ export declare function mainsBands(mainsHz: number, rateSps?: number): Stage[];
1209
+ /** The class of signal a chain produces: the time domain unless a stage is a representation change. */
1210
+ export declare function outputClass(stages: readonly Stage[], catalog?: readonly CatalogEntry[]): "time_domain" | "representation";
1211
+ /** A chain in words, for a manifest, a header, or a person. */
1212
+ export declare function describeChain(stages: readonly Stage[], rateSps?: number): string;
1213
+ /** The update service's operations, by name. */
1214
+ export declare const UPDATE_OPS: {
1215
+ readonly start: 1;
1216
+ readonly query: 2;
1217
+ readonly finish: 3;
1218
+ readonly activate: 4;
1219
+ readonly abort: 5;
1220
+ };
1221
+ /** The name of any operation `UPDATE_OPS` defines. */
1222
+ export type UpdateOpName = keyof typeof UPDATE_OPS;
1223
+ /** Operation names by their number, the reverse of `UPDATE_OPS`. */
1224
+ export declare const UPDATE_OP_BY_CODE: Readonly<Record<number, UpdateOpName>>;
1225
+ /** What a transfer carries: an application, weights, or a head. */
1226
+ export declare const UPDATE_TARGETS: {
1227
+ readonly app: 1;
1228
+ readonly weights: 2;
1229
+ readonly head: 3;
1230
+ };
1231
+ /** The name of any target `UPDATE_TARGETS` defines. */
1232
+ export type UpdateTargetName = keyof typeof UPDATE_TARGETS;
1233
+ /** Target names by their number, the reverse of `UPDATE_TARGETS`. */
1234
+ export declare const UPDATE_TARGET_BY_CODE: Readonly<Record<number, UpdateTargetName>>;
1235
+ /** What an update control response's status byte means. */
1236
+ export declare const UPDATE_STATUS: Readonly<Record<number, string>>;
1237
+ /** A transfer's state, in words, by its QUERY `state` code. */
1238
+ export declare const UPDATE_STATES: Readonly<Record<number, string>>;
1239
+ /** Why FINISH accepted or refused an image, in words, by its `verify_result` code. */
1240
+ export declare const VERIFY_RESULTS: Readonly<Record<number, string>>;
1241
+ /** The length of a START request, in bytes. */
1242
+ export declare const UPDATE_START_LEN = 15;
1243
+ /** The length of an image envelope's plain header, in bytes. */
1244
+ export declare const ENVELOPE_LEN = 32;
1245
+ /** An image envelope's magic bytes, as hex. */
1246
+ export declare const ENVELOPE_MAGIC = "494d5550";
1247
+ /** The only envelope version this contract defines. */
1248
+ export declare const ENVELOPE_VERSION = 1;
1249
+ /** The `slot_link` byte for an image that is not linked to an application slot. */
1250
+ export declare const SLOT_LINK_NONE = 255;
1251
+ /** A START request, as the device reads it. */
1252
+ export declare function encodeUpdateStart(target: UpdateTargetName | number, slot: number, totalLen: number, transferId: Uint8Array | string): Uint8Array;
1253
+ /** A request with no body: QUERY, FINISH, ACTIVATE, or ABORT, as the device reads it. */
1254
+ export declare function encodeUpdateOp(op: UpdateOpName): Uint8Array;
1255
+ /** An update control request, as `decodeUpdateRequest` reads it. */
1256
+ export interface UpdateRequest {
1257
+ /** The raw operation byte. */
1258
+ readonly op: number;
1259
+ /** `op` in words. */
1260
+ readonly opName: UpdateOpName;
1261
+ /** What is being transferred, for a START request. Null otherwise. */
1262
+ readonly target: number | null;
1263
+ /** `target` in words. */
1264
+ readonly targetName: UpdateTargetName | null;
1265
+ /** The slot, for a START request. Null otherwise. */
1266
+ readonly slot: number | null;
1267
+ /** The exact byte count the host will send, for a START request. Null otherwise. */
1268
+ readonly totalLen: number | null;
1269
+ /** The transfer's identifier, for a START request. Null otherwise. */
1270
+ readonly transferId: string | null;
1271
+ }
1272
+ /**
1273
+ * An update control write, as the device reads it. An operation with no
1274
+ * body arrives as exactly one byte.
1275
+ */
1276
+ export declare function decodeUpdateRequest(data: Uint8Array): UpdateRequest;
1277
+ /** An update control response, as `decodeUpdateResponse` reads it. */
1278
+ export interface UpdateResponse {
1279
+ /** The operation byte this answers. */
1280
+ readonly op: number;
1281
+ /** `op` in words, or null for a number this contract does not define. */
1282
+ readonly opName: UpdateOpName | null;
1283
+ /** The status byte, 0 for success. */
1284
+ readonly status: number;
1285
+ /** `status` in words. */
1286
+ readonly statusName: string;
1287
+ /** Whether `status` is 0. */
1288
+ readonly ok: boolean;
1289
+ /** FINISH says why it refused an image. No other operation carries one. */
1290
+ readonly verifyResult: number | null;
1291
+ /** `verifyResult` in words, or null when there is none. */
1292
+ readonly verifyResultName: string | null;
1293
+ /** The bytes after the operation and status. */
1294
+ readonly payload: Uint8Array;
1295
+ }
1296
+ /** An update control response, as the device sends it. */
1297
+ export declare function decodeUpdateResponse(data: Uint8Array): UpdateResponse;
1298
+ /** An update control response, as the device sends it. */
1299
+ export declare function encodeUpdateResponse(op: number, status: number, payload?: Uint8Array): Uint8Array;
1300
+ /** START's answer: the slot, the chunk limit, and how much the device already has. */
1301
+ export interface UpdateStart {
1302
+ /** Which slot the device wants the image in. */
1303
+ readonly targetSlot: number;
1304
+ /** The largest write it takes. */
1305
+ readonly chunkMax: number;
1306
+ /** How much of this transfer it already has. */
1307
+ readonly resumeOffset: number;
1308
+ }
1309
+ /** START's answer, as the device sends it. */
1310
+ export declare function decodeUpdateStart(payload: Uint8Array): UpdateStart;
1311
+ /** QUERY's answer: the transfer's state, how much the device holds, and its checksum over that. */
1312
+ export interface UpdateQuery {
1313
+ /** The transfer's state code. See `UPDATE_STATES`. */
1314
+ readonly state: number;
1315
+ /** `state` in words. */
1316
+ readonly stateName: string;
1317
+ /** Bytes the device has accepted so far. */
1318
+ readonly offset: number;
1319
+ /** The device's own checksum over every byte it has accepted. */
1320
+ readonly crc32: number;
1321
+ }
1322
+ /** QUERY's answer, as the device sends it. */
1323
+ export declare function decodeUpdateQuery(payload: Uint8Array): UpdateQuery;
1324
+ /**
1325
+ * The plain head of a wrapped image. Everything after it is encrypted and
1326
+ * means nothing to a host, which is the point: a host carries an update
1327
+ * without being able to read or forge one.
1328
+ */
1329
+ export interface Envelope {
1330
+ /** What is being transferred: application or weights. */
1331
+ readonly target: number;
1332
+ /** `target` in words. */
1333
+ readonly targetName: UpdateTargetName;
1334
+ /** Which application slot this image is built for. `SLOT_LINK_NONE` for weights. */
1335
+ readonly slotLink: number;
1336
+ /** The key identifier the envelope names. */
1337
+ readonly keyId: number;
1338
+ /** The encryption nonce. */
1339
+ readonly nonce: Uint8Array;
1340
+ /** Length of the encrypted payload, in bytes. */
1341
+ readonly plainLen: number;
1342
+ /** The envelope's length plus the payload's. */
1343
+ readonly totalLen: number;
1344
+ }
1345
+ /** An image envelope's plain header, as the device reads it. */
1346
+ export declare function decodeEnvelope(data: Uint8Array): Envelope;
1347
+ /** An image envelope's plain header, as the device reads it. */
1348
+ export declare function encodeEnvelope(e: {
1349
+ target: UpdateTargetName | number;
1350
+ slotLink: number;
1351
+ keyId: number;
1352
+ nonce: Uint8Array;
1353
+ plainLen: number;
1354
+ }): Uint8Array;
1355
+ /** A head blob's magic bytes, as hex. */
1356
+ export declare const HEAD_MAGIC = "494d4844";
1357
+ /** The length of a format 1 head header, in bytes. */
1358
+ export declare const HEAD_HEADER_LEN = 32;
1359
+ /** 1.3: format 2 appends the eight byte id of the encoder the head was trained beside. */
1360
+ export declare const HEAD_HEADER_LEN_2 = 40;
1361
+ /** The length of a head blob's trailing hash, in bytes. */
1362
+ export declare const HEAD_HASH_LEN = 32;
1363
+ /** The only head kind this contract defines. */
1364
+ export declare const HEAD_KIND_LINEAR = 1;
1365
+ /** The length of a head's name field, in bytes. */
1366
+ export declare const HEAD_NAME_LEN = 16;
1367
+ /**
1368
+ * Counts per unit of embedding. Part of the contract: changing it would
1369
+ * change every head ever trained.
1370
+ */
1371
+ export declare const EMBED_SCALE = 4096;
1372
+ /** The exact length of a head blob for a shape and format version. */
1373
+ export declare function headBlobLen(inDim: number, outDim: number, version?: number): number;
1374
+ /**
1375
+ * The integers a head receives for a float embedding. Saturates rather than
1376
+ * wrapping, so an extreme value is clipped and never reversed.
1377
+ */
1378
+ export declare function quantizeEmbedding(embedding: ArrayLike<number>): number[];
1379
+ /** A head, decoded: the weights, and everything needed to run them. */
1380
+ export interface HeadBlob {
1381
+ /** `HEAD_KIND_LINEAR`, the only kind this contract defines. */
1382
+ readonly kind: number;
1383
+ /** The embedding width this head takes. Must equal the loaded encoder's. */
1384
+ readonly inDim: number;
1385
+ /** Outputs this head produces. */
1386
+ readonly outDim: number;
1387
+ /** The head's name. */
1388
+ readonly name: string;
1389
+ /** The head's identity, the first eight bytes of its SHA-256. */
1390
+ readonly headId: string;
1391
+ /** One row per output, each row `inDim` signed byte values. */
1392
+ readonly weights: number[][];
1393
+ /** One integer per output. */
1394
+ readonly bias: number[];
1395
+ /** One float per output. */
1396
+ readonly scale: number[];
1397
+ /** The format the file was written in: 1, or 2 with an encoder id. */
1398
+ readonly version: number;
1399
+ /** 1.3: the encoder the head was trained beside; `NO_ENCODER_ID` when the file does not say. The device never judges it. */
1400
+ readonly encoderId: string;
1401
+ }
1402
+ /**
1403
+ * A head blob, ready to upload.
1404
+ *
1405
+ * `weights` is one row per output, each row `inDim` values in the range a
1406
+ * signed byte holds. `bias` is one integer per output and `scale` one float
1407
+ * per output, which together carry whatever range the outputs need. The
1408
+ * hash is computed here, because a head the device cannot check is a head
1409
+ * the device refuses.
1410
+ *
1411
+ * `encoderId` names the encoder the head was trained beside (1.3, format 2),
1412
+ * so a host can warn when it is uploaded to a device running another;
1413
+ * `version` 1 writes the 1.0 header for a device that predates format 2.
1414
+ */
1415
+ export declare function buildHead(weights: ReadonlyArray<ArrayLike<number>>, bias: ArrayLike<number>, scale: ArrayLike<number>, options?: {
1416
+ name?: string;
1417
+ inDim?: number;
1418
+ encoderId?: string;
1419
+ version?: number;
1420
+ }): Uint8Array;
1421
+ /** Read a head back, and check it the way the device does. */
1422
+ export declare function decodeHead(blob: Uint8Array): HeadBlob;
1423
+ /**
1424
+ * What a head produces, computed the way the device computes it: the
1425
+ * accumulation is exact in integers and only the scale is a float.
1426
+ *
1427
+ * `embedding` is the integers a head receives, which is what
1428
+ * `quantizeEmbedding` returns. Floats are not taken here, because a head
1429
+ * that was handed floats would silently produce something the device never
1430
+ * would.
1431
+ */
1432
+ export declare function evaluateHead(head: HeadBlob, embedding: ArrayLike<number>): number[];
1433
+ //# sourceMappingURL=protocol.d.ts.map