@depz/sensor-sdk 0.2.1 → 0.3.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 (166) hide show
  1. package/README.md +13 -5
  2. package/dist/dataset.d.ts.map +1 -1
  3. package/dist/dataset.js +66 -2
  4. package/dist/dataset.js.map +1 -1
  5. package/dist/device/device.d.ts +6 -0
  6. package/dist/device/device.d.ts.map +1 -1
  7. package/dist/device/device.js +29 -6
  8. package/dist/device/device.js.map +1 -1
  9. package/dist/discovery.d.ts.map +1 -1
  10. package/dist/discovery.js +25 -2
  11. package/dist/discovery.js.map +1 -1
  12. package/dist/index.d.ts +21 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +16 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/protocol/bno055.d.ts +101 -0
  17. package/dist/protocol/bno055.d.ts.map +1 -0
  18. package/dist/protocol/bno055.js +105 -0
  19. package/dist/protocol/bno055.js.map +1 -0
  20. package/dist/protocol/identity.d.ts +1 -1
  21. package/dist/protocol/identity.d.ts.map +1 -1
  22. package/dist/protocol/identity.js +6 -0
  23. package/dist/protocol/identity.js.map +1 -1
  24. package/dist/protocol/vl53l7.d.ts +77 -0
  25. package/dist/protocol/vl53l7.d.ts.map +1 -0
  26. package/dist/protocol/vl53l7.js +99 -0
  27. package/dist/protocol/vl53l7.js.map +1 -0
  28. package/dist/protocol/vl53lx.d.ts +72 -0
  29. package/dist/protocol/vl53lx.d.ts.map +1 -0
  30. package/dist/protocol/vl53lx.js +87 -0
  31. package/dist/protocol/vl53lx.js.map +1 -0
  32. package/dist/sensors/bno055/bno055.d.ts +207 -0
  33. package/dist/sensors/bno055/bno055.d.ts.map +1 -0
  34. package/dist/sensors/bno055/bno055.js +547 -0
  35. package/dist/sensors/bno055/bno055.js.map +1 -0
  36. package/dist/sensors/bno055/regs.d.ts +271 -0
  37. package/dist/sensors/bno055/regs.d.ts.map +1 -0
  38. package/dist/sensors/bno055/regs.js +366 -0
  39. package/dist/sensors/bno055/regs.js.map +1 -0
  40. package/dist/sensors/bno086/bno086.d.ts +15 -0
  41. package/dist/sensors/bno086/bno086.d.ts.map +1 -1
  42. package/dist/sensors/bno086/bno086.js +43 -1
  43. package/dist/sensors/bno086/bno086.js.map +1 -1
  44. package/dist/sensors/bno086/sh2.d.ts.map +1 -1
  45. package/dist/sensors/bno086/sh2.js +5 -2
  46. package/dist/sensors/bno086/sh2.js.map +1 -1
  47. package/dist/sensors/reg-bridge.d.ts +35 -0
  48. package/dist/sensors/reg-bridge.d.ts.map +1 -0
  49. package/dist/sensors/reg-bridge.js +44 -0
  50. package/dist/sensors/reg-bridge.js.map +1 -0
  51. package/dist/sensors/vl53l4/vl53l4.d.ts.map +1 -1
  52. package/dist/sensors/vl53l4/vl53l4.js +18 -30
  53. package/dist/sensors/vl53l4/vl53l4.js.map +1 -1
  54. package/dist/sensors/vl53l7/vl53l7.d.ts +97 -0
  55. package/dist/sensors/vl53l7/vl53l7.d.ts.map +1 -0
  56. package/dist/sensors/vl53l7/vl53l7.js +138 -0
  57. package/dist/sensors/vl53l7/vl53l7.js.map +1 -0
  58. package/dist/sensors/vl53l8/assets/index.d.ts +5 -1
  59. package/dist/sensors/vl53l8/assets/index.d.ts.map +1 -1
  60. package/dist/sensors/vl53l8/assets/index.js +7 -1
  61. package/dist/sensors/vl53l8/assets/index.js.map +1 -1
  62. package/dist/sensors/vl53l8/assets/l7ch.d.ts +12 -0
  63. package/dist/sensors/vl53l8/assets/l7ch.d.ts.map +1 -0
  64. package/dist/sensors/vl53l8/assets/l7ch.js +18 -0
  65. package/dist/sensors/vl53l8/assets/l7ch.js.map +1 -0
  66. package/dist/sensors/vl53l8/assets/l7cx.d.ts +14 -0
  67. package/dist/sensors/vl53l8/assets/l7cx.d.ts.map +1 -0
  68. package/dist/sensors/vl53l8/assets/l7cx.js +20 -0
  69. package/dist/sensors/vl53l8/assets/l7cx.js.map +1 -0
  70. package/dist/sensors/vl53l8/cnh.d.ts +4 -3
  71. package/dist/sensors/vl53l8/cnh.d.ts.map +1 -1
  72. package/dist/sensors/vl53l8/cnh.js +4 -3
  73. package/dist/sensors/vl53l8/cnh.js.map +1 -1
  74. package/dist/sensors/vl53l8/uld.d.ts +50 -9
  75. package/dist/sensors/vl53l8/uld.d.ts.map +1 -1
  76. package/dist/sensors/vl53l8/uld.js +241 -35
  77. package/dist/sensors/vl53l8/uld.js.map +1 -1
  78. package/dist/sensors/vl53l8/vl53l8.d.ts +8 -3
  79. package/dist/sensors/vl53l8/vl53l8.d.ts.map +1 -1
  80. package/dist/sensors/vl53l8/vl53l8.js +41 -39
  81. package/dist/sensors/vl53l8/vl53l8.js.map +1 -1
  82. package/dist/sensors/vl53lx/decode.d.ts +70 -0
  83. package/dist/sensors/vl53lx/decode.d.ts.map +1 -0
  84. package/dist/sensors/vl53lx/decode.js +108 -0
  85. package/dist/sensors/vl53lx/decode.js.map +1 -0
  86. package/dist/sensors/vl53lx/uld/arith.d.ts +23 -0
  87. package/dist/sensors/vl53lx/uld/arith.d.ts.map +1 -0
  88. package/dist/sensors/vl53lx/uld/arith.js +42 -0
  89. package/dist/sensors/vl53lx/uld/arith.js.map +1 -0
  90. package/dist/sensors/vl53lx/uld/bare/core.d.ts +345 -0
  91. package/dist/sensors/vl53lx/uld/bare/core.d.ts.map +1 -0
  92. package/dist/sensors/vl53lx/uld/bare/core.js +1085 -0
  93. package/dist/sensors/vl53lx/uld/bare/core.js.map +1 -0
  94. package/dist/sensors/vl53lx/uld/bare/driver.d.ts +91 -0
  95. package/dist/sensors/vl53lx/uld/bare/driver.d.ts.map +1 -0
  96. package/dist/sensors/vl53lx/uld/bare/driver.js +206 -0
  97. package/dist/sensors/vl53lx/uld/bare/driver.js.map +1 -0
  98. package/dist/sensors/vl53lx/uld/bare/hist.d.ts +180 -0
  99. package/dist/sensors/vl53lx/uld/bare/hist.d.ts.map +1 -0
  100. package/dist/sensors/vl53lx/uld/bare/hist.js +949 -0
  101. package/dist/sensors/vl53lx/uld/bare/hist.js.map +1 -0
  102. package/dist/sensors/vl53lx/uld/bare/image.d.ts +53 -0
  103. package/dist/sensors/vl53lx/uld/bare/image.d.ts.map +1 -0
  104. package/dist/sensors/vl53lx/uld/bare/image.js +108 -0
  105. package/dist/sensors/vl53lx/uld/bare/image.js.map +1 -0
  106. package/dist/sensors/vl53lx/uld/bare/imath.d.ts +39 -0
  107. package/dist/sensors/vl53lx/uld/bare/imath.d.ts.map +1 -0
  108. package/dist/sensors/vl53lx/uld/bare/imath.js +99 -0
  109. package/dist/sensors/vl53lx/uld/bare/imath.js.map +1 -0
  110. package/dist/sensors/vl53lx/uld/bare/index.d.ts +22 -0
  111. package/dist/sensors/vl53lx/uld/bare/index.d.ts.map +1 -0
  112. package/dist/sensors/vl53lx/uld/bare/index.js +22 -0
  113. package/dist/sensors/vl53lx/uld/bare/index.js.map +1 -0
  114. package/dist/sensors/vl53lx/uld/bare/nvm.d.ts +85 -0
  115. package/dist/sensors/vl53lx/uld/bare/nvm.d.ts.map +1 -0
  116. package/dist/sensors/vl53lx/uld/bare/nvm.js +148 -0
  117. package/dist/sensors/vl53lx/uld/bare/nvm.js.map +1 -0
  118. package/dist/sensors/vl53lx/uld/bare/regs.d.ts +54 -0
  119. package/dist/sensors/vl53lx/uld/bare/regs.d.ts.map +1 -0
  120. package/dist/sensors/vl53lx/uld/bare/regs.js +496 -0
  121. package/dist/sensors/vl53lx/uld/bare/regs.js.map +1 -0
  122. package/dist/sensors/vl53lx/uld/bare/tuning.d.ts +15 -0
  123. package/dist/sensors/vl53lx/uld/bare/tuning.d.ts.map +1 -0
  124. package/dist/sensors/vl53lx/uld/bare/tuning.js +303 -0
  125. package/dist/sensors/vl53lx/uld/bare/tuning.js.map +1 -0
  126. package/dist/sensors/vl53lx/uld/base.d.ts +129 -0
  127. package/dist/sensors/vl53lx/uld/base.d.ts.map +1 -0
  128. package/dist/sensors/vl53lx/uld/base.js +166 -0
  129. package/dist/sensors/vl53lx/uld/base.js.map +1 -0
  130. package/dist/sensors/vl53lx/uld/l0x.d.ts +306 -0
  131. package/dist/sensors/vl53lx/uld/l0x.d.ts.map +1 -0
  132. package/dist/sensors/vl53lx/uld/l0x.js +1492 -0
  133. package/dist/sensors/vl53lx/uld/l0x.js.map +1 -0
  134. package/dist/sensors/vl53lx/uld/l1.d.ts +69 -0
  135. package/dist/sensors/vl53lx/uld/l1.d.ts.map +1 -0
  136. package/dist/sensors/vl53lx/uld/l1.js +262 -0
  137. package/dist/sensors/vl53lx/uld/l1.js.map +1 -0
  138. package/dist/sensors/vl53lx/uld/l3.d.ts +25 -0
  139. package/dist/sensors/vl53lx/uld/l3.d.ts.map +1 -0
  140. package/dist/sensors/vl53lx/uld/l3.js +51 -0
  141. package/dist/sensors/vl53lx/uld/l3.js.map +1 -0
  142. package/dist/sensors/vl53lx/uld/l4.d.ts +22 -0
  143. package/dist/sensors/vl53lx/uld/l4.d.ts.map +1 -0
  144. package/dist/sensors/vl53lx/uld/l4.js +99 -0
  145. package/dist/sensors/vl53lx/uld/l4.js.map +1 -0
  146. package/dist/sensors/vl53lx/uld/link.d.ts +42 -0
  147. package/dist/sensors/vl53lx/uld/link.d.ts.map +1 -0
  148. package/dist/sensors/vl53lx/uld/link.js +28 -0
  149. package/dist/sensors/vl53lx/uld/link.js.map +1 -0
  150. package/dist/sensors/vl53lx/uld/registry.d.ts +68 -0
  151. package/dist/sensors/vl53lx/uld/registry.d.ts.map +1 -0
  152. package/dist/sensors/vl53lx/uld/registry.js +139 -0
  153. package/dist/sensors/vl53lx/uld/registry.js.map +1 -0
  154. package/dist/sensors/vl53lx/uld/vl53l1-die.d.ts +172 -0
  155. package/dist/sensors/vl53lx/uld/vl53l1-die.d.ts.map +1 -0
  156. package/dist/sensors/vl53lx/uld/vl53l1-die.js +431 -0
  157. package/dist/sensors/vl53lx/uld/vl53l1-die.js.map +1 -0
  158. package/dist/sensors/vl53lx/vl53lx.d.ts +323 -0
  159. package/dist/sensors/vl53lx/vl53lx.d.ts.map +1 -0
  160. package/dist/sensors/vl53lx/vl53lx.js +688 -0
  161. package/dist/sensors/vl53lx/vl53lx.js.map +1 -0
  162. package/dist/transport/webserial.d.ts +4 -0
  163. package/dist/transport/webserial.d.ts.map +1 -1
  164. package/dist/transport/webserial.js +58 -12
  165. package/dist/transport/webserial.js.map +1 -1
  166. package/package.json +9 -2
@@ -0,0 +1,1085 @@
1
+ /**
2
+ * uld/bare/core — preset modes, timing maths and the ranging cycle.
3
+ * Mirror of the Python `depz_sensor_sdk.vl53lx.uld.bare.core` (a faithful
4
+ * port of ST's VL53LX BareDriver C).
5
+ *
6
+ * This turns a set of tuning numbers into a device image and back:
7
+ * `VL53LX_read_p2p_data()` (the factory data), the
8
+ * `VL53LX_preset_mode_histogram_*()` builders, `VL53LX_set_timeouts_us()`,
9
+ * `VL53LX_init_and_start_range()` and `VL53LX_get_histogram_bin_data()`.
10
+ *
11
+ * **The device is configured in one shot.** Every preset builder edits the
12
+ * host-side image, and the span 0x0001..0x0087 goes out as one transfer when
13
+ * ranging starts.
14
+ *
15
+ * **Naming.** Methods are camelCase; the C structs (`HistogramBinData`,
16
+ * `HistPostProcessConfig`, register fields, NVM shapes) keep their C field
17
+ * names so they stay diffable against the Python and the C driver.
18
+ *
19
+ * **Integers.** Python `//`, `%`, `<<`, `>>` are mirrored through `imath.ts`
20
+ * (floor vs trunc, no 32-bit wrap); see there.
21
+ */
22
+ import { DeviceImage, RANGE_START_BLOCKS } from "./image.js";
23
+ import { floorDiv, cdiv, pymod, shl, shr } from "./imath.js";
24
+ import { NvmReader, } from "./nvm.js";
25
+ import { DEFAULTS, TUNING } from "./tuning.js";
26
+ // ── register map (vl53lx_register_map.h, vl53lx_hist_map.h) ──────────────────
27
+ export const POWER_MANAGEMENT__GO1_POWER_FORCE = 0x0083;
28
+ export const FIRMWARE__ENABLE = 0x0085;
29
+ export const RESULT__OSC_CALIBRATE_VAL = 0x00de;
30
+ export const PATCH__CTRL = 0x0470;
31
+ export const PATCH__JMP_ENABLES = 0x0472;
32
+ export const PATCH__DATA_ENABLES = 0x0474;
33
+ export const PATCH__OFFSET_0 = 0x0476;
34
+ export const PATCH__ADDRESS_0 = 0x0496;
35
+ export const HISTOGRAM_BIN_DATA_I2C_INDEX = 0x0088; // result__interrupt_status
36
+ export const RESULT__HISTOGRAM_BIN_0_2 = 0x008e;
37
+ export const RESULT__HISTOGRAM_BIN_23_0 = 0x00d5;
38
+ export const PHASECAL_RESULT__REFERENCE_PHASE = 0x00d6;
39
+ export const PHASECAL_RESULT__VCSEL_START = 0x00d8;
40
+ export const RESULT__HISTOGRAM_BIN_23_0_MSB = 0x00d9;
41
+ export const RESULT__HISTOGRAM_BIN_23_0_LSB = 0x00da;
42
+ export const HISTOGRAM_BIN_DATA_I2C_SIZE_BYTES = RESULT__HISTOGRAM_BIN_23_0_LSB - HISTOGRAM_BIN_DATA_I2C_INDEX + 1;
43
+ // ── register settings (vl53lx_register_settings.h, vl53lx_ll_device.h) ───────
44
+ export const DEVICEMEASUREMENTMODE_BACKTOBACK = 0x20;
45
+ export const DEVICEMEASUREMENTMODE_ABORT = 0x80;
46
+ export const DEVICEMEASUREMENTMODE_STOP_MASK = 0x0f;
47
+ export const DEVICEMEASUREMENTMODE_MODE_MASK = 0xf0;
48
+ export const DEVICESCHEDULERMODE_STREAMING = 0x01;
49
+ export const DEVICESCHEDULERMODE_HISTOGRAM = 0x02;
50
+ export const DEVICEREADOUTMODE_SINGLE_SD = 0x00 << 2;
51
+ export const DEVICEREADOUTMODE_DUAL_SD = 0x01 << 2;
52
+ export const GROUPEDPARAMETERHOLD_ID_MASK = 0x02;
53
+ export const INTERRUPT_CONFIG_NEW_SAMPLE_READY = 0x20;
54
+ export const CLEAR_RANGE_INT = 0x01;
55
+ export const RANGE_STATUS__RANGE_STATUS_MASK = 0x1f;
56
+ export const DEVICEINTERRUPTPOLARITY_ACTIVE_LOW = 0x10;
57
+ export const DEVICEGPIOMODE_OUTPUT_RANGE_AND_ERROR_INTERRUPTS = 0x01;
58
+ export const DEVICEDSSMODE__TARGET_RATE = 1;
59
+ export const SEQUENCE_VHV_EN = 0x01;
60
+ export const SEQUENCE_PHASECAL_EN = 0x02;
61
+ export const SEQUENCE_DSS1_EN = 0x08;
62
+ export const SEQUENCE_DSS2_EN = 0x10;
63
+ export const SEQUENCE_MM1_EN = 0x20;
64
+ export const SEQUENCE_MM2_EN = 0x40;
65
+ export const SEQUENCE_RANGE_EN = 0x80;
66
+ export const SPAD_ARRAY_WIDTH = 16;
67
+ export const SPAD_ARRAY_HEIGHT = 16;
68
+ export const RTN_SPAD_APERTURE_TRANSMISSION = 0x0038;
69
+ export const RTN_SPAD_UNITY_TRANSMISSION = 0x0100;
70
+ export const AMBIENT_WINDOW_VCSEL_PERIODS = 256;
71
+ export const RANGING_WINDOW_VCSEL_PERIODS = 2048;
72
+ export const MACRO_PERIOD_VCSEL_PERIODS = AMBIENT_WINDOW_VCSEL_PERIODS + RANGING_WINDOW_VCSEL_PERIODS;
73
+ export const HISTOGRAM_BUFFER_SIZE = 24;
74
+ /** The three preset modes, by the distance mode that picks them. */
75
+ export const DISTANCE_MODES = ["short", "medium", "long"];
76
+ // VL53LX_SetMeasurementTimingBudgetMicroSeconds: a fixed guard comes off the
77
+ // top and the rest is split six ways.
78
+ export const TIMING_GUARD_US = 1700;
79
+ export const TIMING_DIVISOR = 6;
80
+ export const FDA_MAX_TIMING_BUDGET_US = 550000;
81
+ function tp(key) {
82
+ const v = TUNING[key];
83
+ if (v === undefined)
84
+ throw new Error(`no tuning parameter ${key}`);
85
+ return v;
86
+ }
87
+ function dflt(key) {
88
+ const v = DEFAULTS[key];
89
+ if (v === undefined)
90
+ throw new Error(`no tuning default ${key}`);
91
+ return v;
92
+ }
93
+ // ── SPAD geometry (vl53lx_core.c, vl53lx_core_support.c) ─────────────────────
94
+ export function encodeRowCol(row, col) {
95
+ if (row > 7)
96
+ return (128 + (col << 3) + (15 - row)) & 0xff;
97
+ return (((15 - col) << 3) + row) & 0xff;
98
+ }
99
+ /** → [row, col] */
100
+ export function decodeRowCol(spadNumber) {
101
+ if (spadNumber > 127)
102
+ return [8 + ((255 - spadNumber) & 0x07), (spadNumber - 128) >> 3];
103
+ return [spadNumber & 0x07, (127 - spadNumber) >> 3];
104
+ }
105
+ /** → [width, height] */
106
+ export function decodeZoneSize(encodedXySize) {
107
+ return [encodedXySize & 0x0f, encodedXySize >> 4];
108
+ }
109
+ export function encodeZoneSize(width, height) {
110
+ return ((height << 4) + width) & 0xff;
111
+ }
112
+ /** → [x_ll, y_ll, x_ur, y_ur], clipped to the SPAD array. */
113
+ export function decodeZoneLimits(encodedXyCentre, encodedXySize) {
114
+ const [yCentre, xCentre] = decodeRowCol(encodedXyCentre);
115
+ const [width, height] = decodeZoneSize(encodedXySize);
116
+ const xLl = Math.max(0, xCentre - floorDiv(width + 1, 2));
117
+ const xUr = Math.min(SPAD_ARRAY_WIDTH - 1, xLl + width);
118
+ const yLl = Math.max(0, yCentre - floorDiv(height + 1, 2));
119
+ const yUr = Math.min(SPAD_ARRAY_HEIGHT - 1, yLl + height);
120
+ return [xLl, yLl, xUr, yUr];
121
+ }
122
+ export function isApertureLocation(row, col) {
123
+ const r = row % 4;
124
+ const c = col % 4;
125
+ return (r === 0 && c === 2) || (r === 2 && c === 0);
126
+ }
127
+ /** → [mm inner, mm outer] effective SPADs, in 1/256 of a SPAD. */
128
+ export function calcMmEffectiveSpads(encodedMmRoiCentre, encodedMmRoiSize, encodedZoneCentre, encodedZoneSize, goodSpads, apertureAttenuation) {
129
+ const [mmXLl, mmYLl, mmXUr, mmYUr] = decodeZoneLimits(encodedMmRoiCentre, encodedMmRoiSize);
130
+ const [zXLl, zYLl, zXUr, zYUr] = decodeZoneLimits(encodedZoneCentre, encodedZoneSize);
131
+ let inner = 0;
132
+ let outer = 0;
133
+ for (let y = zYLl; y <= zYUr; y++) {
134
+ for (let x = zXLl; x <= zXUr; x++) {
135
+ const spad = encodeRowCol(y, x);
136
+ if (!(goodSpads[spad >> 3] & (1 << (spad & 0x07))))
137
+ continue;
138
+ const attenuation = isApertureLocation(y, x)
139
+ ? apertureAttenuation
140
+ : RTN_SPAD_UNITY_TRANSMISSION;
141
+ if (mmXLl <= x && x <= mmXUr && mmYLl <= y && y <= mmYUr)
142
+ inner += attenuation;
143
+ else
144
+ outer += attenuation;
145
+ }
146
+ }
147
+ return [inner, outer];
148
+ }
149
+ /**
150
+ * `VL53LX_hist_combine_mm1_mm2_offsets()` → `range_offset_mm`, in quarter
151
+ * millimetres. The two NVM offsets are weighted by the nominal MM1/MM2 peak
152
+ * rates scaled by the share of each region the ROI covers.
153
+ */
154
+ export function combineMm1Mm2Offsets(mm1OffsetMm, mm2OffsetMm, encodedMmRoiCentre, encodedMmRoiSize, encodedZoneCentre, encodedZoneSize, calData, goodSpads, apertureAttenuation) {
155
+ const [maxInner, maxOuter] = calcMmEffectiveSpads(encodedMmRoiCentre, encodedMmRoiSize, 0xc7, 0xff, goodSpads, apertureAttenuation);
156
+ if (maxInner === 0 || maxOuter === 0)
157
+ return 0;
158
+ const [inner, outer] = calcMmEffectiveSpads(encodedMmRoiCentre, encodedMmRoiSize, encodedZoneCentre, encodedZoneSize, goodSpads, apertureAttenuation);
159
+ const mm1Rate = floorDiv(calData.result__mm_inner_peak_signal_count_rtn_mcps * inner, maxInner);
160
+ const mm2Rate = floorDiv(calData.result__mm_outer_peak_signal_count_rtn_mcps * outer, maxOuter);
161
+ const total = mm1Rate + mm2Rate;
162
+ if (total === 0)
163
+ return 0;
164
+ // The offsets are signed: the division truncates towards zero, as in C.
165
+ const num = (mm1OffsetMm * mm1Rate + mm2OffsetMm * mm2Rate) * 4;
166
+ return cdiv(num, total);
167
+ }
168
+ /** The 32 `global_config__spad_enables_rtn_*` bytes as one bitmap. */
169
+ export function rtnGoodSpads(nvmCopyData) {
170
+ const out = new Uint8Array(32);
171
+ for (let i = 0; i < 32; i++)
172
+ out[i] = nvmCopyData.v[`global_config__spad_enables_rtn_${i}`];
173
+ return out;
174
+ }
175
+ // ── timing maths (vl53lx_core.c, vl53lx_core_support.c) ──────────────────────
176
+ export function calcPllPeriodUs(fastOscFrequency) {
177
+ return fastOscFrequency > 0 ? floorDiv(2 ** 30, fastOscFrequency) : 0;
178
+ }
179
+ export function decodeVcselPeriod(vcselPeriodReg) {
180
+ return shl(vcselPeriodReg + 1, 1);
181
+ }
182
+ export function calcMacroPeriodUs(fastOscFrequency, vcselPeriodReg) {
183
+ let macro = MACRO_PERIOD_VCSEL_PERIODS * calcPllPeriodUs(fastOscFrequency);
184
+ macro = shr(macro, 6);
185
+ macro *= decodeVcselPeriod(vcselPeriodReg);
186
+ return shr(macro, 6);
187
+ }
188
+ export function calcTimeoutMclks(timeoutUs, macroPeriodUs) {
189
+ if (macroPeriodUs === 0)
190
+ return 0;
191
+ return floorDiv(shl(timeoutUs, 12) + shr(macroPeriodUs, 1), macroPeriodUs);
192
+ }
193
+ export function encodeTimeout(timeoutMclks) {
194
+ if (timeoutMclks <= 0)
195
+ return 0;
196
+ let lsByte = timeoutMclks - 1;
197
+ let msByte = 0;
198
+ // Python `ls_byte & 0xFFFFFF00` on an unbounded int: bits 8..31 only.
199
+ while (pymod(shr(lsByte, 8), 2 ** 24) !== 0) {
200
+ lsByte = shr(lsByte, 1);
201
+ msByte += 1;
202
+ }
203
+ return pymod(shl(msByte, 8) + pymod(lsByte, 256), 0x10000);
204
+ }
205
+ export function decodeTimeout(encoded) {
206
+ return shl(encoded & 0x00ff, (encoded & 0xff00) >> 8) + 1;
207
+ }
208
+ export function calcEncodedTimeout(timeoutUs, macroPeriodUs) {
209
+ return encodeTimeout(calcTimeoutMclks(timeoutUs, macroPeriodUs));
210
+ }
211
+ export function durationMaths(pllPeriodUs, vcselParmPclks, windowVclks, elapsedMclks) {
212
+ let duration = shr(windowVclks * pllPeriodUs, 12);
213
+ duration *= shr(elapsedMclks * vcselParmPclks, 4);
214
+ return Math.min(shr(duration, 12), 0xffffffff);
215
+ }
216
+ /** Fill the six timeout registers, for VCSEL period A and then B. */
217
+ export function calcTimeoutRegisterValues(phasecalUs, mmUs, rangeUs, fastOscFrequency, genCfg, timCfg) {
218
+ if (fastOscFrequency === 0)
219
+ throw new Error("osc_measured__fast_osc__frequency is 0");
220
+ const g = genCfg.v;
221
+ const t = timCfg.v;
222
+ let macro = calcMacroPeriodUs(fastOscFrequency, t.range_config__vcsel_period_a);
223
+ g.phasecal_config__timeout_macrop = Math.min(0xff, calcTimeoutMclks(phasecalUs, macro));
224
+ let encoded = calcEncodedTimeout(mmUs, macro);
225
+ t.mm_config__timeout_macrop_a_hi = encoded >> 8;
226
+ t.mm_config__timeout_macrop_a_lo = encoded & 0xff;
227
+ encoded = calcEncodedTimeout(rangeUs, macro);
228
+ t.range_config__timeout_macrop_a_hi = encoded >> 8;
229
+ t.range_config__timeout_macrop_a_lo = encoded & 0xff;
230
+ macro = calcMacroPeriodUs(fastOscFrequency, t.range_config__vcsel_period_b);
231
+ encoded = calcEncodedTimeout(mmUs, macro);
232
+ t.mm_config__timeout_macrop_b_hi = encoded >> 8;
233
+ t.mm_config__timeout_macrop_b_lo = encoded & 0xff;
234
+ encoded = calcEncodedTimeout(rangeUs, macro);
235
+ t.range_config__timeout_macrop_b_hi = encoded >> 8;
236
+ t.range_config__timeout_macrop_b_lo = encoded & 0xff;
237
+ }
238
+ // ── the histogram config struct (not a register block) ───────────────────────
239
+ export const HIST_CFG_FIELDS = [
240
+ "low_amb_even_bin_0_1", "low_amb_even_bin_2_3", "low_amb_even_bin_4_5",
241
+ "low_amb_odd_bin_0_1", "low_amb_odd_bin_2_3", "low_amb_odd_bin_4_5",
242
+ "mid_amb_even_bin_0_1", "mid_amb_even_bin_2_3", "mid_amb_even_bin_4_5",
243
+ "mid_amb_odd_bin_0_1", "mid_amb_odd_bin_2", "mid_amb_odd_bin_3_4",
244
+ "mid_amb_odd_bin_5", "user_bin_offset",
245
+ "high_amb_even_bin_0_1", "high_amb_even_bin_2_3", "high_amb_even_bin_4_5",
246
+ "high_amb_odd_bin_0_1", "high_amb_odd_bin_2_3", "high_amb_odd_bin_4_5",
247
+ "amb_thresh_low", "amb_thresh_high", "spad_array_selection",
248
+ ];
249
+ /**
250
+ * `VL53LX_histogram_config_t`. It has no registers of its own: the bin
251
+ * sequence is smuggled into static_config and timing_config fields that mean
252
+ * something else in lite mode, which `copyToStaticCfg()` does.
253
+ */
254
+ export class HistConfig {
255
+ low_amb_even_bin_0_1 = 0;
256
+ low_amb_even_bin_2_3 = 0;
257
+ low_amb_even_bin_4_5 = 0;
258
+ low_amb_odd_bin_0_1 = 0;
259
+ low_amb_odd_bin_2_3 = 0;
260
+ low_amb_odd_bin_4_5 = 0;
261
+ mid_amb_even_bin_0_1 = 0;
262
+ mid_amb_even_bin_2_3 = 0;
263
+ mid_amb_even_bin_4_5 = 0;
264
+ mid_amb_odd_bin_0_1 = 0;
265
+ mid_amb_odd_bin_2 = 0;
266
+ mid_amb_odd_bin_3_4 = 0;
267
+ mid_amb_odd_bin_5 = 0;
268
+ user_bin_offset = 0;
269
+ high_amb_even_bin_0_1 = 0;
270
+ high_amb_even_bin_2_3 = 0;
271
+ high_amb_even_bin_4_5 = 0;
272
+ high_amb_odd_bin_0_1 = 0;
273
+ high_amb_odd_bin_2_3 = 0;
274
+ high_amb_odd_bin_4_5 = 0;
275
+ amb_thresh_low = 0;
276
+ amb_thresh_high = 0;
277
+ spad_array_selection = 0;
278
+ get(name) {
279
+ return this[name];
280
+ }
281
+ /**
282
+ * VL53LX_init_histogram_config_structure(): six even and six odd bin codes,
283
+ * packed two to a byte, then repeated across the three ambient levels.
284
+ */
285
+ setBinSequence(even, odd) {
286
+ this.low_amb_even_bin_0_1 = (even[1] << 4) + even[0];
287
+ this.low_amb_even_bin_2_3 = (even[3] << 4) + even[2];
288
+ this.low_amb_even_bin_4_5 = (even[5] << 4) + even[4];
289
+ this.low_amb_odd_bin_0_1 = (odd[1] << 4) + odd[0];
290
+ this.low_amb_odd_bin_2_3 = (odd[3] << 4) + odd[2];
291
+ this.low_amb_odd_bin_4_5 = (odd[5] << 4) + odd[4];
292
+ this.mid_amb_even_bin_0_1 = this.low_amb_even_bin_0_1;
293
+ this.mid_amb_even_bin_2_3 = this.low_amb_even_bin_2_3;
294
+ this.mid_amb_even_bin_4_5 = this.low_amb_even_bin_4_5;
295
+ this.mid_amb_odd_bin_0_1 = this.low_amb_odd_bin_0_1;
296
+ this.mid_amb_odd_bin_2 = odd[2];
297
+ this.mid_amb_odd_bin_3_4 = (odd[4] << 4) + odd[3];
298
+ this.mid_amb_odd_bin_5 = odd[5];
299
+ this.user_bin_offset = 0x00;
300
+ this.high_amb_even_bin_0_1 = this.low_amb_even_bin_0_1;
301
+ this.high_amb_even_bin_2_3 = this.low_amb_even_bin_2_3;
302
+ this.high_amb_even_bin_4_5 = this.low_amb_even_bin_4_5;
303
+ this.high_amb_odd_bin_0_1 = this.low_amb_odd_bin_0_1;
304
+ this.high_amb_odd_bin_2_3 = this.low_amb_odd_bin_2_3;
305
+ this.high_amb_odd_bin_4_5 = this.low_amb_odd_bin_4_5;
306
+ this.amb_thresh_low = 0xffff;
307
+ this.amb_thresh_high = 0xffff;
308
+ this.spad_array_selection = 0x00;
309
+ }
310
+ /** VL53LX_copy_hist_cfg_to_static_cfg(). */
311
+ copyToStaticCfg(staticCfg, timing, dynamic) {
312
+ const s = staticCfg.v;
313
+ const t = timing.v;
314
+ const d = dynamic.v;
315
+ s.sigma_estimator__effective_pulse_width_ns = this.high_amb_even_bin_0_1;
316
+ s.sigma_estimator__effective_ambient_width_ns = this.high_amb_even_bin_2_3;
317
+ s.sigma_estimator__sigma_ref_mm = this.high_amb_even_bin_4_5;
318
+ s.algo__crosstalk_compensation_valid_height_mm = this.high_amb_odd_bin_0_1;
319
+ s.spare_host_config__static_config_spare_0 = this.high_amb_odd_bin_2_3;
320
+ s.spare_host_config__static_config_spare_1 = this.high_amb_odd_bin_4_5;
321
+ s.algo__range_ignore_threshold_mcps =
322
+ shl(this.mid_amb_even_bin_0_1, 8) + this.mid_amb_even_bin_2_3;
323
+ s.algo__range_ignore_valid_height_mm = this.mid_amb_even_bin_4_5;
324
+ s.algo__range_min_clip = this.mid_amb_odd_bin_0_1;
325
+ s.algo__consistency_check__tolerance = this.mid_amb_odd_bin_2;
326
+ s.spare_host_config__static_config_spare_2 = this.mid_amb_odd_bin_3_4;
327
+ s.sd_config__reset_stages_msb = this.mid_amb_odd_bin_5;
328
+ s.sd_config__reset_stages_lsb = this.user_bin_offset;
329
+ t.range_config__sigma_thresh = shl(this.low_amb_even_bin_0_1, 8) + this.low_amb_even_bin_2_3;
330
+ t.range_config__min_count_rate_rtn_limit_mcps =
331
+ shl(this.low_amb_even_bin_4_5, 8) + this.low_amb_odd_bin_0_1;
332
+ t.range_config__valid_phase_low = this.low_amb_odd_bin_2_3;
333
+ t.range_config__valid_phase_high = this.low_amb_odd_bin_4_5;
334
+ d.system__thresh_high = this.amb_thresh_low;
335
+ d.system__thresh_low = this.amb_thresh_high;
336
+ d.system__enable_xtalk_per_quadrant = this.spad_array_selection;
337
+ }
338
+ /**
339
+ * VL53LX_hist_get_bin_sequence_config(): which of the three sequences the
340
+ * device used for this frame, even or odd according to the stream count.
341
+ */
342
+ binSequence(streamCount, ambientEventsSum) {
343
+ let level = "mid";
344
+ if (ambientEventsSum > 1024 * this.amb_thresh_high)
345
+ level = "high";
346
+ if (ambientEventsSum < 1024 * this.amb_thresh_low)
347
+ level = "low";
348
+ let packed;
349
+ if ((streamCount & 0x01) === 0) {
350
+ packed = [
351
+ this.get(`${level}_amb_even_bin_0_1`),
352
+ this.get(`${level}_amb_even_bin_2_3`),
353
+ this.get(`${level}_amb_even_bin_4_5`),
354
+ ];
355
+ }
356
+ else if (level === "mid") {
357
+ // The mid odd sequence is the one packed differently.
358
+ return [
359
+ this.mid_amb_odd_bin_0_1 & 0x0f,
360
+ shr(this.mid_amb_odd_bin_0_1, 4),
361
+ this.mid_amb_odd_bin_2,
362
+ shr(this.mid_amb_odd_bin_3_4, 4),
363
+ this.mid_amb_odd_bin_3_4 & 0x0f,
364
+ this.mid_amb_odd_bin_5 & 0x0f,
365
+ ];
366
+ }
367
+ else {
368
+ packed = [
369
+ this.get(`${level}_amb_odd_bin_0_1`),
370
+ this.get(`${level}_amb_odd_bin_2_3`),
371
+ this.get(`${level}_amb_odd_bin_4_5`),
372
+ ];
373
+ }
374
+ const seq = [];
375
+ for (const byte of packed)
376
+ seq.push(byte & 0x0f, shr(byte, 4));
377
+ return seq;
378
+ }
379
+ }
380
+ /**
381
+ * `VL53LX_hist_post_process_config_t`, as
382
+ * `VL53LX_init_hist_post_process_config_struct()` leaves it.
383
+ */
384
+ export class HistPostProcessConfig {
385
+ hist_algo_select = dflt("TUNINGPARM_HIST_ALGO_SELECT_DEFAULT");
386
+ hist_target_order = dflt("TUNINGPARM_HIST_TARGET_ORDER_DEFAULT");
387
+ filter_woi0 = dflt("TUNINGPARM_HIST_FILTER_WOI_0_DEFAULT");
388
+ filter_woi1 = dflt("TUNINGPARM_HIST_FILTER_WOI_1_DEFAULT");
389
+ hist_amb_est_method = dflt("TUNINGPARM_HIST_AMB_EST_METHOD_DEFAULT");
390
+ ambient_thresh_sigma0 = dflt("TUNINGPARM_HIST_AMB_THRESH_SIGMA_0_DEFAULT");
391
+ ambient_thresh_sigma1 = dflt("TUNINGPARM_HIST_AMB_THRESH_SIGMA_1_DEFAULT");
392
+ ambient_thresh_events_scaler = dflt("TUNINGPARM_HIST_AMB_EVENTS_SCALER_DEFAULT");
393
+ min_ambient_thresh_events = dflt("TUNINGPARM_HIST_MIN_AMB_THRESH_EVENTS_DEFAULT");
394
+ noise_threshold = dflt("TUNINGPARM_HIST_NOISE_THRESHOLD_DEFAULT");
395
+ signal_total_events_limit = dflt("TUNINGPARM_HIST_SIGNAL_TOTAL_EVENTS_LIMIT_DEFAULT");
396
+ sigma_estimator__sigma_ref_mm = dflt("TUNINGPARM_HIST_SIGMA_EST_REF_MM_DEFAULT");
397
+ sigma_thresh = dflt("TUNINGPARM_HIST_SIGMA_THRESH_MM_DEFAULT");
398
+ range_offset_mm = 0;
399
+ gain_factor = dflt("TUNINGPARM_HIST_GAIN_FACTOR_DEFAULT");
400
+ valid_phase_low = 0x08;
401
+ valid_phase_high = 0x88;
402
+ algo__consistency_check__phase_tolerance = dflt("TUNINGPARM_CONSISTENCY_HIST_PHASE_TOLERANCE_DEFAULT");
403
+ algo__consistency_check__event_sigma = dflt("TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_DEFAULT");
404
+ algo__consistency_check__event_min_spad_count = dflt("TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_MIN_SPAD_LIMIT_DEFAULT");
405
+ algo__consistency_check__min_max_tolerance = dflt("TUNINGPARM_CONSISTENCY_HIST_MIN_MAX_TOLERANCE_MM_DEFAULT");
406
+ algo__crosstalk_compensation_enable;
407
+ algo__crosstalk_detect_min_valid_range_mm = dflt("TUNINGPARM_XTALK_DETECT_MIN_VALID_RANGE_MM_DEFAULT");
408
+ algo__crosstalk_detect_max_valid_range_mm = dflt("TUNINGPARM_XTALK_DETECT_MAX_VALID_RANGE_MM_DEFAULT");
409
+ algo__crosstalk_detect_max_valid_rate_kcps = dflt("TUNINGPARM_XTALK_DETECT_MAX_VALID_RATE_KCPS_DEFAULT");
410
+ algo__crosstalk_detect_max_sigma_mm = dflt("TUNINGPARM_XTALK_DETECT_MAX_SIGMA_MM_DEFAULT");
411
+ algo__crosstalk_detect_event_sigma = dflt("TUNINGPARM_XTALK_DETECT_EVENT_SIGMA_DEFAULT");
412
+ algo__crosstalk_detect_min_max_tolerance = dflt("TUNINGPARM_XTALK_DETECT_MIN_MAX_TOLERANCE_DEFAULT");
413
+ // Filled in by readP2pData() from the customer NVM block.
414
+ algo__crosstalk_compensation_plane_offset_kcps = 0;
415
+ algo__crosstalk_compensation_x_plane_gradient_kcps = 0;
416
+ algo__crosstalk_compensation_y_plane_gradient_kcps = 0;
417
+ constructor(xtalkCompensationEnable = 0) {
418
+ this.algo__crosstalk_compensation_enable = xtalkCompensationEnable;
419
+ }
420
+ }
421
+ // ── the histogram frame ──────────────────────────────────────────────────────
422
+ /**
423
+ * `VL53LX_histogram_bin_data_t`: 24 bins plus everything the post-processing
424
+ * needs to read them — the VCSEL period they were taken at, the phase of zero
425
+ * distance, the bin sequence and the ambient estimate. C field names kept.
426
+ */
427
+ export class HistogramBinData {
428
+ result__interrupt_status = 0;
429
+ result__range_status = 0;
430
+ result__report_status = 0;
431
+ result__stream_count = 0;
432
+ result__dss_actual_effective_spads = 0;
433
+ phasecal_result__reference_phase = 0;
434
+ phasecal_result__vcsel_start = 0;
435
+ bin_data = new Array(HISTOGRAM_BUFFER_SIZE).fill(0);
436
+ zone_id = 0;
437
+ first_bin = 0; // VL53LX_p_019
438
+ number_of_bins = HISTOGRAM_BUFFER_SIZE; // VL53LX_p_020, buffer
439
+ bins_in_data = HISTOGRAM_BUFFER_SIZE; // VL53LX_p_021, in use
440
+ cal_config__vcsel_start = 0;
441
+ vcsel_width = 0;
442
+ fast_osc_frequency = 0; // VL53LX_p_015
443
+ vcsel_period = 0; // VL53LX_p_005, the register value
444
+ bin_seq = [0, 0, 0, 0, 0, 0];
445
+ bin_rep = [0, 0, 0, 0, 0, 0]; // how often each code was sampled
446
+ min_bin_value = 0;
447
+ max_bin_value = 0;
448
+ number_of_ambient_bins = 0;
449
+ number_of_ambient_samples = 0;
450
+ ambient_events_sum = 0;
451
+ ambient_per_bin = 0; // VL53LX_p_028
452
+ total_periods_elapsed = 0;
453
+ peak_duration_us = 0;
454
+ woi_duration_us = 0;
455
+ zero_distance_phase = 0;
456
+ roi_config__user_roi_centre_spad = 0;
457
+ roi_config__user_roi_requested_global_xy_size = 0;
458
+ get range_status() {
459
+ return this.result__range_status & RANGE_STATUS__RANGE_STATUS_MASK;
460
+ }
461
+ /** `copy.deepcopy()` — every field is a number or a number array. */
462
+ clone() {
463
+ const c = Object.assign(new HistogramBinData(), this);
464
+ c.bin_data = this.bin_data.slice();
465
+ c.bin_seq = this.bin_seq.slice();
466
+ c.bin_rep = this.bin_rep.slice();
467
+ return c;
468
+ }
469
+ }
470
+ // ── the ll driver state machine ──────────────────────────────────────────────
471
+ // `VL53LX_update_ll_driver_{rd,cfg}_state()`, reduced to the single-zone case.
472
+ // The GPH id stamped into the dynamic config tells the device a configuration
473
+ // is new; the timing status says whether this frame ran on VCSEL period A or B.
474
+ export const DEVICESTATE_SW_STANDBY = "sw_standby";
475
+ export const DEVICESTATE_RANGING_WAIT_GPH_SYNC = "wait_gph_sync";
476
+ export const DEVICESTATE_RANGING_OUTPUT_DATA = "output_data";
477
+ export const DEVICESTATE_RANGING_DSS_AUTO = "dss_auto";
478
+ export class LLState {
479
+ cfg_device_state = DEVICESTATE_SW_STANDBY;
480
+ cfg_stream_count = 0;
481
+ cfg_gph_id = GROUPEDPARAMETERHOLD_ID_MASK;
482
+ cfg_timing_status = 0;
483
+ rd_device_state = DEVICESTATE_SW_STANDBY;
484
+ rd_stream_count = 0;
485
+ rd_gph_id = GROUPEDPARAMETERHOLD_ID_MASK;
486
+ rd_timing_status = 0;
487
+ reset() {
488
+ this.cfg_device_state = DEVICESTATE_SW_STANDBY;
489
+ this.cfg_stream_count = 0;
490
+ this.cfg_gph_id = GROUPEDPARAMETERHOLD_ID_MASK;
491
+ this.cfg_timing_status = 0;
492
+ this.rd_device_state = DEVICESTATE_SW_STANDBY;
493
+ this.rd_stream_count = 0;
494
+ this.rd_gph_id = GROUPEDPARAMETERHOLD_ID_MASK;
495
+ this.rd_timing_status = 0;
496
+ }
497
+ updateRd(modeStart, groupedParameterHold) {
498
+ if ((modeStart & DEVICEMEASUREMENTMODE_MODE_MASK) === 0) {
499
+ this.rd_device_state = DEVICESTATE_SW_STANDBY;
500
+ this.rd_stream_count = 0;
501
+ this.rd_gph_id = GROUPEDPARAMETERHOLD_ID_MASK;
502
+ this.rd_timing_status = 0;
503
+ return;
504
+ }
505
+ this.rd_stream_count = this.rd_stream_count === 0xff ? 0x80 : this.rd_stream_count + 1;
506
+ this.rd_gph_id ^= GROUPEDPARAMETERHOLD_ID_MASK;
507
+ if (this.rd_device_state === DEVICESTATE_SW_STANDBY) {
508
+ this.rd_device_state =
509
+ groupedParameterHold & GROUPEDPARAMETERHOLD_ID_MASK
510
+ ? DEVICESTATE_RANGING_WAIT_GPH_SYNC
511
+ : DEVICESTATE_RANGING_OUTPUT_DATA;
512
+ this.rd_stream_count = 0;
513
+ this.rd_timing_status = 0;
514
+ }
515
+ else if (this.rd_device_state === DEVICESTATE_RANGING_WAIT_GPH_SYNC) {
516
+ this.rd_stream_count = 0;
517
+ this.rd_device_state = DEVICESTATE_RANGING_OUTPUT_DATA;
518
+ }
519
+ else if (this.rd_device_state === DEVICESTATE_RANGING_OUTPUT_DATA) {
520
+ this.rd_timing_status ^= 0x01;
521
+ }
522
+ else {
523
+ this.reset();
524
+ }
525
+ }
526
+ updateCfg(modeStart) {
527
+ if ((modeStart & DEVICEMEASUREMENTMODE_MODE_MASK) === 0) {
528
+ this.cfg_device_state = DEVICESTATE_SW_STANDBY;
529
+ this.cfg_stream_count = 0;
530
+ this.cfg_gph_id = GROUPEDPARAMETERHOLD_ID_MASK;
531
+ this.cfg_timing_status = 0;
532
+ return;
533
+ }
534
+ this.cfg_stream_count = this.cfg_stream_count === 0xff ? 0x80 : this.cfg_stream_count + 1;
535
+ this.cfg_gph_id ^= GROUPEDPARAMETERHOLD_ID_MASK;
536
+ if (this.cfg_device_state === DEVICESTATE_SW_STANDBY) {
537
+ this.cfg_timing_status ^= 0x01;
538
+ this.cfg_stream_count = 1;
539
+ this.cfg_device_state = DEVICESTATE_RANGING_DSS_AUTO;
540
+ }
541
+ else if (this.cfg_device_state === DEVICESTATE_RANGING_DSS_AUTO) {
542
+ this.cfg_timing_status ^= 0x01;
543
+ }
544
+ else {
545
+ this.reset();
546
+ }
547
+ }
548
+ }
549
+ // ── the three distance-mode tails (vl53lx_api_preset_modes.c) ────────────────
550
+ const PRESET_TAIL = {
551
+ long: {
552
+ even: [7, 0, 1, 2, 3, 4], odd: [0, 1, 2, 3, 4, 5],
553
+ vcselA: 0x09, vcselB: 0x0b, mmA: 0x0021, mmB: 0x001b,
554
+ rangeA: 0x0029, rangeB: 0x0022, calVcselStart: 0x09, validPhaseHigh: 0x88,
555
+ phaseRtn: "tp_init_phase_rtn_hist_long", phaseRef: "tp_init_phase_ref_hist_long",
556
+ },
557
+ medium: {
558
+ even: [7, 0, 1, 1, 2, 2], odd: [0, 1, 2, 1, 2, 3],
559
+ vcselA: 0x05, vcselB: 0x07, mmA: 0x0036, mmB: 0x0028,
560
+ rangeA: 0x0044, rangeB: 0x0033, calVcselStart: 0x05, validPhaseHigh: 0x48,
561
+ phaseRtn: "tp_init_phase_rtn_hist_med", phaseRef: "tp_init_phase_ref_hist_med",
562
+ },
563
+ short: {
564
+ even: [7, 7, 0, 1, 1, 1], odd: [0, 1, 1, 1, 2, 2],
565
+ vcselA: 0x03, vcselB: 0x05, mmA: 0x0052, mmB: 0x0037,
566
+ rangeA: 0x0066, rangeB: 0x0044, calVcselStart: 0x03, validPhaseHigh: 0x28,
567
+ phaseRtn: "tp_init_phase_rtn_hist_short", phaseRef: "tp_init_phase_ref_hist_short",
568
+ extraSequence: SEQUENCE_MM1_EN,
569
+ },
570
+ };
571
+ const PHASECAL_KEY = {
572
+ short: "tp_phasecal_timeout_hist_short_us",
573
+ medium: "tp_phasecal_timeout_hist_med_us",
574
+ long: "tp_phasecal_timeout_hist_long_us",
575
+ };
576
+ function isDistanceMode(mode) {
577
+ return DISTANCE_MODES.includes(mode);
578
+ }
579
+ // ── the driver ───────────────────────────────────────────────────────────────
580
+ /**
581
+ * The VL53LX BareDriver's ranging path, on top of `DeviceImage`. It owns what
582
+ * the C driver keeps in `VL53LX_LLDriverData_t` and nowhere else: the factory
583
+ * data that has no registers, the histogram config, the post-process config
584
+ * and the timeouts.
585
+ */
586
+ export class BareDriver {
587
+ p;
588
+ img;
589
+ nvm;
590
+ tuning = { ...TUNING };
591
+ histCfg = new HistConfig();
592
+ hpp = new HistPostProcessConfig();
593
+ // Factory data with no register of its own (readP2pData).
594
+ rtnGoodSpads = new Uint8Array(32);
595
+ opticalCentre = null;
596
+ calPeakRateMap = null;
597
+ addOffCalData = null;
598
+ fmtDmaxCal = null;
599
+ mmRoi = null;
600
+ result__osc_calibrate_val = 0;
601
+ presetMode = "medium";
602
+ phasecalConfigTimeoutUs = 1000;
603
+ mmConfigTimeoutUs = 2000;
604
+ rangeConfigTimeoutUs = 13000;
605
+ interMeasurementPeriodMs = 100;
606
+ measurementMode = 0;
607
+ state = new LLState();
608
+ // Carried from frame to frame: the C driver keeps one bin_data struct and
609
+ // reads last frame's ambient out of it when it picks the bin sequence,
610
+ // before this frame's ambient is estimated.
611
+ ambientEventsSum = 0;
612
+ constructor(p) {
613
+ this.p = p;
614
+ this.img = new DeviceImage(p);
615
+ this.nvm = new NvmReader(p, this.img);
616
+ }
617
+ // ── VL53LX_read_p2p_data ──
618
+ /** Pull the factory data: three register blocks and four NVM regions. */
619
+ async readP2pData() {
620
+ await this.img.pull("static_nvm_managed");
621
+ await this.img.pull("customer_nvm_managed");
622
+ await this.img.pull("nvm_copy_data");
623
+ this.rtnGoodSpads = rtnGoodSpads(this.img.nvm_copy_data);
624
+ const customer = this.img.customer_nvm_managed.v;
625
+ this.hpp.algo__crosstalk_compensation_plane_offset_kcps =
626
+ customer.algo__crosstalk_compensation_plane_offset_kcps;
627
+ this.hpp.algo__crosstalk_compensation_x_plane_gradient_kcps =
628
+ customer.algo__crosstalk_compensation_x_plane_gradient_kcps;
629
+ this.hpp.algo__crosstalk_compensation_y_plane_gradient_kcps =
630
+ customer.algo__crosstalk_compensation_y_plane_gradient_kcps;
631
+ this.opticalCentre = await this.nvm.opticalCentre();
632
+ this.calPeakRateMap = await this.nvm.calPeakRateMap();
633
+ this.addOffCalData = await this.nvm.additionalOffsetCalData();
634
+ // Our boards have no FMT offset calibration. ST's fallback: nominal
635
+ // MM1/MM2 peak rates, and the effective SPAD counts worked out here.
636
+ const cal = this.addOffCalData;
637
+ const nvmCopy = this.img.nvm_copy_data.v;
638
+ if (cal.result__mm_inner_peak_signal_count_rtn_mcps === 0 &&
639
+ cal.result__mm_outer_peak_signal_count_rtn_mcps === 0) {
640
+ const [inner, outer] = calcMmEffectiveSpads(nvmCopy.roi_config__mode_roi_centre_spad, nvmCopy.roi_config__mode_roi_xy_size, 0xc7, 0xff, this.rtnGoodSpads, RTN_SPAD_APERTURE_TRANSMISSION);
641
+ this.addOffCalData = {
642
+ ...cal,
643
+ result__mm_inner_peak_signal_count_rtn_mcps: 0x0080,
644
+ result__mm_outer_peak_signal_count_rtn_mcps: 0x0180,
645
+ result__mm_inner_actual_effective_spads: inner,
646
+ result__mm_outer_actual_effective_spads: outer,
647
+ };
648
+ }
649
+ const fmt = await this.nvm.fmtRangeResults();
650
+ this.fmtDmaxCal = {
651
+ ref__actual_effective_spads: fmt.result__actual_effective_rtn_spads,
652
+ ref__peak_signal_count_rate_mcps: fmt.result__peak_signal_count_rate_rtn_mcps,
653
+ ref__distance_mm: fmt.measured_distance_mm,
654
+ ref_reflectance_pc: this.calPeakRateMap.cal_reflectance_pc || 0x0014,
655
+ coverglass_transmission: 0x0100,
656
+ };
657
+ this.result__osc_calibrate_val = await this.p.rdWord(RESULT__OSC_CALIBRATE_VAL);
658
+ const statNvm = this.img.static_nvm_managed.v;
659
+ if (statNvm.osc_measured__fast_osc__frequency < 0x1000) {
660
+ statNvm.osc_measured__fast_osc__frequency = 0xbccc;
661
+ }
662
+ // VL53LX_get_mode_mitigation_roi
663
+ const [y, x] = decodeRowCol(nvmCopy.roi_config__mode_roi_centre_spad);
664
+ const xySize = nvmCopy.roi_config__mode_roi_xy_size;
665
+ this.mmRoi = { x_centre: x, y_centre: y, width: xySize & 0x0f, height: xySize >> 4 };
666
+ if (this.opticalCentre.x_centre === 0 && this.opticalCentre.y_centre === 0) {
667
+ this.opticalCentre = { x_centre: x << 4, y_centre: y << 4 };
668
+ }
669
+ }
670
+ // ── VL53LX_data_init + VL53LX_DataInit ──
671
+ /**
672
+ * Everything the L3CX API does between boot and the first start: read the
673
+ * factory data, take the default preset mode and the default timing budget.
674
+ */
675
+ async dataInit() {
676
+ this.state.reset();
677
+ this.hpp = new HistPostProcessConfig();
678
+ await this.readP2pData();
679
+ this.setPresetMode("medium", null, 1000);
680
+ this.setMeasurementTimingBudgetUs(33333);
681
+ }
682
+ // ── preset modes ──
683
+ /** VL53LX_set_preset_mode() for the three histogram ranging modes. */
684
+ setPresetMode(mode = "medium", dssTargetMcps = null, interMeasurementPeriodMs = null) {
685
+ if (!isDistanceMode(mode))
686
+ throw new RangeError(`unknown distance mode '${mode}'`);
687
+ this.presetMode = mode;
688
+ const dss = dssTargetMcps ?? this.tuning.tp_dss_target_histo_mcps;
689
+ const imp = interMeasurementPeriodMs ?? this.interMeasurementPeriodMs;
690
+ // Only the phasecal timeout differs between the three modes.
691
+ const phasecalUs = this.tp(PHASECAL_KEY[mode]);
692
+ const mmUs = this.tp("tp_mm_timeout_histo_us");
693
+ const rangeUs = this.tp("tp_range_timeout_histo_us");
694
+ this.state.reset();
695
+ this.presetModeHistogramRanging();
696
+ this.presetTail(PRESET_TAIL[mode]);
697
+ this.img.static_config.v.dss_config__target_total_rate_mcps = dss;
698
+ this.setTimeoutsUs(phasecalUs, mmUs, rangeUs);
699
+ this.setInterMeasurementPeriodMs(imp);
700
+ this.updateRangeOffset();
701
+ }
702
+ tp(key) {
703
+ const v = this.tuning[key];
704
+ if (v === undefined)
705
+ throw new Error(`no tuning parameter ${key}`);
706
+ return v;
707
+ }
708
+ /**
709
+ * `VL53LX_get_device_results()` recomputes `hpp.range_offset_mm` every frame
710
+ * under MM1_MM2_OFFSETS; its inputs are the factory data and the user ROI,
711
+ * so once per preset is the same answer for less work.
712
+ */
713
+ updateRangeOffset() {
714
+ if (this.addOffCalData === null)
715
+ return;
716
+ const customer = this.img.customer_nvm_managed.v;
717
+ const nvm = this.img.nvm_copy_data.v;
718
+ const dynamic = this.img.dynamic_config.v;
719
+ this.hpp.range_offset_mm = combineMm1Mm2Offsets(customer.mm_config__inner_offset_mm, customer.mm_config__outer_offset_mm, nvm.roi_config__mode_roi_centre_spad, nvm.roi_config__mode_roi_xy_size, dynamic.roi_config__user_roi_centre_spad, dynamic.roi_config__user_roi_requested_global_xy_size, this.addOffCalData, this.rtnGoodSpads, this.img.general_config.v.dss_config__aperture_attenuation);
720
+ }
721
+ /**
722
+ * VL53LX_SetDistanceMode(): the preset mode, with the timeouts put back
723
+ * afterwards (the preset alone would reset them to its own defaults).
724
+ */
725
+ setDistanceMode(mode) {
726
+ const phasecalUs = this.phasecalConfigTimeoutUs;
727
+ const mmUs = this.mmConfigTimeoutUs;
728
+ const rangeUs = this.rangeConfigTimeoutUs;
729
+ this.setPresetMode(mode);
730
+ this.setTimeoutsUs(phasecalUs, mmUs, rangeUs);
731
+ }
732
+ /** VL53LX_preset_mode_standard_ranging(): the base of every histogram mode. */
733
+ presetModeStandardRanging() {
734
+ const s = this.img.static_config.v;
735
+ const g = this.img.general_config.v;
736
+ const t = this.img.timing_config.v;
737
+ const d = this.img.dynamic_config.v;
738
+ const sys = this.img.system_control.v;
739
+ s.dss_config__target_total_rate_mcps = 0x0a00;
740
+ s.debug__ctrl = 0x00;
741
+ s.test_mode__ctrl = 0x00;
742
+ s.clk_gating__ctrl = 0x00;
743
+ s.nvm_bist__ctrl = 0x00;
744
+ s.nvm_bist__num_nvm_words = 0x00;
745
+ s.nvm_bist__start_address = 0x00;
746
+ s.host_if__status = 0x00;
747
+ s.pad_i2c_hv__config = 0x00;
748
+ s.pad_i2c_hv__extsup_config = 0x00;
749
+ s.gpio_hv_pad__ctrl = 0x00;
750
+ s.gpio_hv_mux__ctrl =
751
+ DEVICEINTERRUPTPOLARITY_ACTIVE_LOW | DEVICEGPIOMODE_OUTPUT_RANGE_AND_ERROR_INTERRUPTS;
752
+ s.gpio__tio_hv_status = 0x02;
753
+ s.gpio__fio_hv_status = 0x00;
754
+ s.ana_config__spad_sel_pswidth = 0x02;
755
+ s.ana_config__vcsel_pulse_width_offset = 0x08;
756
+ s.ana_config__fast_osc__config_ctrl = 0x00;
757
+ s.sigma_estimator__effective_pulse_width_ns = this.tp("tp_lite_sigma_est_pulse_width_ns");
758
+ s.sigma_estimator__effective_ambient_width_ns = this.tp("tp_lite_sigma_est_amb_width_ns");
759
+ s.sigma_estimator__sigma_ref_mm = this.tp("tp_lite_sigma_ref_mm");
760
+ s.algo__crosstalk_compensation_valid_height_mm = 0x01;
761
+ s.spare_host_config__static_config_spare_0 = 0x00;
762
+ s.spare_host_config__static_config_spare_1 = 0x00;
763
+ s.algo__range_ignore_threshold_mcps = 0x0000;
764
+ s.algo__range_ignore_valid_height_mm = 0xff;
765
+ s.algo__range_min_clip = this.tp("tp_lite_min_clip");
766
+ s.algo__consistency_check__tolerance = this.tp("tp_consistency_lite_phase_tolerance");
767
+ s.spare_host_config__static_config_spare_2 = 0x00;
768
+ s.sd_config__reset_stages_msb = 0x00;
769
+ s.sd_config__reset_stages_lsb = 0x00;
770
+ g.gph_config__stream_count_update_value = 0x00;
771
+ g.global_config__stream_divider = 0x00;
772
+ g.system__interrupt_config_gpio = INTERRUPT_CONFIG_NEW_SAMPLE_READY;
773
+ g.cal_config__vcsel_start = 0x0b;
774
+ g.cal_config__repeat_rate = this.tp("tp_cal_repeat_rate");
775
+ g.global_config__vcsel_width = 0x02;
776
+ g.phasecal_config__timeout_macrop = 0x0d;
777
+ g.phasecal_config__target = this.tp("tp_phasecal_target");
778
+ g.phasecal_config__override = 0x00;
779
+ g.dss_config__roi_mode_control = DEVICEDSSMODE__TARGET_RATE;
780
+ g.system__thresh_rate_high = 0x0000;
781
+ g.system__thresh_rate_low = 0x0000;
782
+ g.dss_config__manual_effective_spads_select = 0x8c00;
783
+ g.dss_config__manual_block_select = 0x00;
784
+ g.dss_config__aperture_attenuation = 0x38;
785
+ g.dss_config__max_spads_limit = 0xff;
786
+ g.dss_config__min_spads_limit = 0x01;
787
+ t.mm_config__timeout_macrop_a_hi = 0x00;
788
+ t.mm_config__timeout_macrop_a_lo = 0x1a;
789
+ t.mm_config__timeout_macrop_b_hi = 0x00;
790
+ t.mm_config__timeout_macrop_b_lo = 0x20;
791
+ t.range_config__timeout_macrop_a_hi = 0x01;
792
+ t.range_config__timeout_macrop_a_lo = 0xcc;
793
+ t.range_config__vcsel_period_a = 0x0b;
794
+ t.range_config__timeout_macrop_b_hi = 0x01;
795
+ t.range_config__timeout_macrop_b_lo = 0xf5;
796
+ t.range_config__vcsel_period_b = 0x09;
797
+ t.range_config__sigma_thresh = this.tp("tp_lite_med_sigma_thresh_mm");
798
+ t.range_config__min_count_rate_rtn_limit_mcps = this.tp("tp_lite_med_min_count_rate_rtn_mcps");
799
+ t.range_config__valid_phase_low = 0x08;
800
+ t.range_config__valid_phase_high = 0x78;
801
+ t.system__intermeasurement_period = 0x00000000;
802
+ t.system__fractional_enable = 0x00;
803
+ // Standard ranging writes the histogram config out byte by byte; every
804
+ // histogram mode overwrites it, but this is the only definition of the
805
+ // struct's initial state.
806
+ const h = this.histCfg;
807
+ h.low_amb_even_bin_0_1 = 0x07;
808
+ h.low_amb_even_bin_2_3 = 0x21;
809
+ h.low_amb_even_bin_4_5 = 0x43;
810
+ h.low_amb_odd_bin_0_1 = 0x10;
811
+ h.low_amb_odd_bin_2_3 = 0x32;
812
+ h.low_amb_odd_bin_4_5 = 0x54;
813
+ h.mid_amb_even_bin_0_1 = 0x07;
814
+ h.mid_amb_even_bin_2_3 = 0x21;
815
+ h.mid_amb_even_bin_4_5 = 0x43;
816
+ h.mid_amb_odd_bin_0_1 = 0x10;
817
+ h.mid_amb_odd_bin_2 = 0x02;
818
+ h.mid_amb_odd_bin_3_4 = 0x43;
819
+ h.mid_amb_odd_bin_5 = 0x05;
820
+ h.user_bin_offset = 0x00;
821
+ h.high_amb_even_bin_0_1 = 0x07;
822
+ h.high_amb_even_bin_2_3 = 0x21;
823
+ h.high_amb_even_bin_4_5 = 0x43;
824
+ h.high_amb_odd_bin_0_1 = 0x10;
825
+ h.high_amb_odd_bin_2_3 = 0x32;
826
+ h.high_amb_odd_bin_4_5 = 0x54;
827
+ h.amb_thresh_low = 0xffff;
828
+ h.amb_thresh_high = 0xffff;
829
+ h.spad_array_selection = 0x00;
830
+ d.system__grouped_parameter_hold_0 = 0x01;
831
+ d.system__thresh_high = 0x0000;
832
+ d.system__thresh_low = 0x0000;
833
+ d.system__enable_xtalk_per_quadrant = 0x00;
834
+ d.system__seed_config = this.tp("tp_lite_seed_cfg");
835
+ d.sd_config__woi_sd0 = 0x0b;
836
+ d.sd_config__woi_sd1 = 0x09;
837
+ d.sd_config__initial_phase_sd0 = this.tp("tp_init_phase_rtn_lite_med");
838
+ d.sd_config__initial_phase_sd1 = this.tp("tp_init_phase_ref_lite_med");
839
+ d.system__grouped_parameter_hold_1 = 0x01;
840
+ d.sd_config__first_order_select = this.tp("tp_lite_first_order_select");
841
+ d.sd_config__quantifier = this.tp("tp_lite_quantifier");
842
+ d.roi_config__user_roi_centre_spad = 0xc7;
843
+ d.roi_config__user_roi_requested_global_xy_size = 0xff;
844
+ d.system__sequence_config =
845
+ SEQUENCE_VHV_EN | SEQUENCE_PHASECAL_EN | SEQUENCE_DSS1_EN |
846
+ SEQUENCE_DSS2_EN | SEQUENCE_MM2_EN | SEQUENCE_RANGE_EN;
847
+ d.system__grouped_parameter_hold = 0x02;
848
+ sys.system__stream_count_ctrl = 0x00;
849
+ sys.firmware__enable = 0x01;
850
+ sys.system__interrupt_clear = CLEAR_RANGE_INT;
851
+ sys.system__mode_start =
852
+ DEVICESCHEDULERMODE_STREAMING | DEVICEREADOUTMODE_SINGLE_SD |
853
+ DEVICEMEASUREMENTMODE_BACKTOBACK;
854
+ }
855
+ /** VL53LX_preset_mode_histogram_ranging(): standard ranging, then histogram. */
856
+ presetModeHistogramRanging() {
857
+ this.presetModeStandardRanging();
858
+ this.img.static_config.v.dss_config__target_total_rate_mcps = 0x1400;
859
+ this.histCfg.setBinSequence([7, 0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5]);
860
+ const t = this.img.timing_config.v;
861
+ const d = this.img.dynamic_config.v;
862
+ t.range_config__vcsel_period_a = 0x09;
863
+ t.range_config__vcsel_period_b = 0x0b;
864
+ d.sd_config__woi_sd0 = 0x09;
865
+ d.sd_config__woi_sd1 = 0x0b;
866
+ t.mm_config__timeout_macrop_a_hi = 0x00;
867
+ t.mm_config__timeout_macrop_a_lo = 0x20;
868
+ t.mm_config__timeout_macrop_b_hi = 0x00;
869
+ t.mm_config__timeout_macrop_b_lo = 0x1a;
870
+ t.range_config__timeout_macrop_a_hi = 0x00;
871
+ t.range_config__timeout_macrop_a_lo = 0x28;
872
+ t.range_config__timeout_macrop_b_hi = 0x00;
873
+ t.range_config__timeout_macrop_b_lo = 0x21;
874
+ this.img.general_config.v.phasecal_config__timeout_macrop = 0xf5;
875
+ this.hpp.valid_phase_low = 0x08;
876
+ this.hpp.valid_phase_high = 0x88;
877
+ this.histCfg.copyToStaticCfg(this.img.static_config, this.img.timing_config, this.img.dynamic_config);
878
+ d.system__sequence_config =
879
+ SEQUENCE_VHV_EN | SEQUENCE_PHASECAL_EN | SEQUENCE_DSS1_EN |
880
+ SEQUENCE_DSS2_EN | SEQUENCE_RANGE_EN;
881
+ this.img.system_control.v.system__mode_start =
882
+ DEVICESCHEDULERMODE_HISTOGRAM | DEVICEREADOUTMODE_DUAL_SD |
883
+ DEVICEMEASUREMENTMODE_BACKTOBACK;
884
+ }
885
+ /** The shape all three histogram distance modes share. */
886
+ presetTail(c) {
887
+ const g = this.img.general_config.v;
888
+ const t = this.img.timing_config.v;
889
+ const d = this.img.dynamic_config.v;
890
+ this.histCfg.setBinSequence(c.even, c.odd);
891
+ this.histCfg.copyToStaticCfg(this.img.static_config, this.img.timing_config, this.img.dynamic_config);
892
+ t.range_config__vcsel_period_a = c.vcselA;
893
+ t.range_config__vcsel_period_b = c.vcselB;
894
+ t.mm_config__timeout_macrop_a_hi = c.mmA >> 8;
895
+ t.mm_config__timeout_macrop_a_lo = c.mmA & 0xff;
896
+ t.mm_config__timeout_macrop_b_hi = c.mmB >> 8;
897
+ t.mm_config__timeout_macrop_b_lo = c.mmB & 0xff;
898
+ t.range_config__timeout_macrop_a_hi = c.rangeA >> 8;
899
+ t.range_config__timeout_macrop_a_lo = c.rangeA & 0xff;
900
+ t.range_config__timeout_macrop_b_hi = c.rangeB >> 8;
901
+ t.range_config__timeout_macrop_b_lo = c.rangeB & 0xff;
902
+ g.cal_config__vcsel_start = c.calVcselStart;
903
+ g.phasecal_config__timeout_macrop = 0xf5;
904
+ d.sd_config__woi_sd0 = c.vcselA;
905
+ d.sd_config__woi_sd1 = c.vcselB;
906
+ d.sd_config__initial_phase_sd0 = this.tp(c.phaseRtn);
907
+ d.sd_config__initial_phase_sd1 = this.tp(c.phaseRef);
908
+ d.system__sequence_config =
909
+ SEQUENCE_VHV_EN | SEQUENCE_PHASECAL_EN | SEQUENCE_DSS1_EN |
910
+ SEQUENCE_DSS2_EN | (c.extraSequence ?? 0) | SEQUENCE_RANGE_EN;
911
+ this.hpp.valid_phase_low = 0x08;
912
+ this.hpp.valid_phase_high = c.validPhaseHigh;
913
+ this.img.system_control.v.system__mode_start =
914
+ DEVICESCHEDULERMODE_HISTOGRAM | DEVICEREADOUTMODE_DUAL_SD |
915
+ DEVICEMEASUREMENTMODE_BACKTOBACK;
916
+ }
917
+ // ── timeouts ──
918
+ setTimeoutsUs(phasecalUs, mmUs, rangeUs) {
919
+ this.phasecalConfigTimeoutUs = phasecalUs;
920
+ this.mmConfigTimeoutUs = mmUs;
921
+ this.rangeConfigTimeoutUs = rangeUs;
922
+ calcTimeoutRegisterValues(phasecalUs, mmUs, rangeUs, this.img.static_nvm_managed.v.osc_measured__fast_osc__frequency, this.img.general_config, this.img.timing_config);
923
+ }
924
+ setInterMeasurementPeriodMs(periodMs) {
925
+ if (this.result__osc_calibrate_val === 0) {
926
+ throw new Error("result__osc_calibrate_val is 0");
927
+ }
928
+ this.interMeasurementPeriodMs = periodMs;
929
+ this.img.timing_config.v.system__intermeasurement_period =
930
+ periodMs * this.result__osc_calibrate_val;
931
+ }
932
+ /**
933
+ * VL53LX_SetMeasurementTimingBudgetMicroSeconds(): the range timeout is one
934
+ * sixth of the budget after a fixed guard comes off.
935
+ */
936
+ setMeasurementTimingBudgetUs(budgetUs) {
937
+ if (!(TIMING_GUARD_US < budgetUs && budgetUs <= 10000000)) {
938
+ throw new RangeError(`timing budget ${budgetUs} us out of range`);
939
+ }
940
+ const rangeUs = floorDiv(budgetUs - TIMING_GUARD_US, TIMING_DIVISOR);
941
+ if (rangeUs * TIMING_DIVISOR > FDA_MAX_TIMING_BUDGET_US) {
942
+ throw new RangeError(`timing budget ${budgetUs} us out of range`);
943
+ }
944
+ this.setTimeoutsUs(this.phasecalConfigTimeoutUs, this.mmConfigTimeoutUs, rangeUs);
945
+ }
946
+ // ── the firmware patch StartMeasurement loads ──
947
+ /** VL53LX_load_patch(): a six-instruction phasecal patch. */
948
+ async loadPatch() {
949
+ const powerMap = { 0: 0x00, 1: 0x10, 2: 0x20, 3: 0x40 };
950
+ const power = powerMap[this.tp("tp_phasecal_patch_power")] ?? 0x00;
951
+ await this.p.wrByte(FIRMWARE__ENABLE, 0x00);
952
+ await this.enablePowerforce();
953
+ await this.p.wrMulti(PATCH__OFFSET_0, Uint8Array.of(0x29, 0xc9, 0x0e, 0x40, 0x28, power));
954
+ await this.p.wrMulti(PATCH__ADDRESS_0, Uint8Array.of(0x03, 0x6d, 0x03, 0x6f, 0x07, 0x29));
955
+ await this.p.wrMulti(PATCH__JMP_ENABLES, Uint8Array.of(0x00, 0x07));
956
+ await this.p.wrMulti(PATCH__DATA_ENABLES, Uint8Array.of(0x00, 0x07));
957
+ await this.p.wrByte(PATCH__CTRL, 0x01);
958
+ await this.p.wrByte(FIRMWARE__ENABLE, 0x01);
959
+ }
960
+ async unloadPatch() {
961
+ await this.p.wrByte(FIRMWARE__ENABLE, 0x00);
962
+ await this.disablePowerforce();
963
+ await this.p.wrByte(PATCH__CTRL, 0x00);
964
+ await this.p.wrByte(FIRMWARE__ENABLE, 0x01);
965
+ }
966
+ async enablePowerforce() {
967
+ await this.p.wrByte(POWER_MANAGEMENT__GO1_POWER_FORCE, 0x01);
968
+ await this.p.sleepMs(1); // 250 us settling, rounded up
969
+ }
970
+ async disablePowerforce() {
971
+ await this.p.wrByte(POWER_MANAGEMENT__GO1_POWER_FORCE, 0x00);
972
+ }
973
+ // ── VL53LX_init_and_start_range ──
974
+ /**
975
+ * Write the configuration and start. `blocks` is ST's config level: all of
976
+ * them to start, general_config onwards to clear the interrupt.
977
+ */
978
+ async initAndStartRange(blocks = RANGE_START_BLOCKS) {
979
+ const dynamic = this.img.dynamic_config.v;
980
+ const system = this.img.system_control.v;
981
+ this.measurementMode = DEVICEMEASUREMENTMODE_BACKTOBACK;
982
+ system.system__mode_start =
983
+ (system.system__mode_start & DEVICEMEASUREMENTMODE_STOP_MASK) | this.measurementMode;
984
+ system.system__interrupt_clear = CLEAR_RANGE_INT;
985
+ const gphId = this.state.cfg_gph_id;
986
+ dynamic.system__grouped_parameter_hold_0 = gphId | 0x01;
987
+ dynamic.system__grouped_parameter_hold_1 = gphId | 0x01;
988
+ dynamic.system__grouped_parameter_hold = gphId;
989
+ await this.img.pushRange(blocks);
990
+ this.state.updateRd(system.system__mode_start, dynamic.system__grouped_parameter_hold);
991
+ this.state.updateCfg(system.system__mode_start);
992
+ }
993
+ /**
994
+ * VL53LX_clear_interrupt_and_enable_next_range(): in histogram mode the
995
+ * interrupt is cleared by rewriting general_config onwards.
996
+ */
997
+ clearInterruptAndStartNextRange() {
998
+ return this.initAndStartRange([
999
+ "general_config", "timing_config", "dynamic_config", "system_control",
1000
+ ]);
1001
+ }
1002
+ async stopRange() {
1003
+ const system = this.img.system_control.v;
1004
+ system.system__mode_start =
1005
+ (system.system__mode_start & DEVICEMEASUREMENTMODE_STOP_MASK) | DEVICEMEASUREMENTMODE_ABORT;
1006
+ await this.img.push("system_control");
1007
+ system.system__mode_start = system.system__mode_start & DEVICEMEASUREMENTMODE_STOP_MASK;
1008
+ this.state.reset();
1009
+ }
1010
+ // ── VL53LX_get_histogram_bin_data ──
1011
+ /**
1012
+ * One frame: the 83-byte block decoded into bins plus the metadata the
1013
+ * post-processing needs. Pure (no bus access): the caller reads or receives
1014
+ * the block. Stateful: carries the ambient sum to the next frame.
1015
+ */
1016
+ getHistogramBinData(raw) {
1017
+ if (raw.length < HISTOGRAM_BIN_DATA_I2C_SIZE_BYTES) {
1018
+ throw new RangeError(`histogram frame is ${raw.length} bytes, need ${HISTOGRAM_BIN_DATA_I2C_SIZE_BYTES}`);
1019
+ }
1020
+ const buf = raw.slice();
1021
+ const off = HISTOGRAM_BIN_DATA_I2C_INDEX;
1022
+ const d = new HistogramBinData();
1023
+ d.result__interrupt_status = buf[0];
1024
+ d.result__range_status = buf[1];
1025
+ d.result__report_status = buf[2];
1026
+ d.result__stream_count = buf[3];
1027
+ d.result__dss_actual_effective_spads = (buf[4] << 8) | buf[5];
1028
+ const rp = PHASECAL_RESULT__REFERENCE_PHASE - off;
1029
+ d.phasecal_result__reference_phase = (buf[rp] << 8) | buf[rp + 1];
1030
+ d.phasecal_result__vcsel_start = buf[PHASECAL_RESULT__VCSEL_START - off];
1031
+ // Bin 23 does not fit in its three bytes: the low byte is carried in a
1032
+ // separate MSB/LSB pair and patched back in before the bins are read.
1033
+ buf[RESULT__HISTOGRAM_BIN_23_0 - off] =
1034
+ ((buf[RESULT__HISTOGRAM_BIN_23_0_MSB - off] << 2) +
1035
+ buf[RESULT__HISTOGRAM_BIN_23_0_LSB - off]) & 0xff;
1036
+ const base = RESULT__HISTOGRAM_BIN_0_2 - off;
1037
+ d.bin_data = [];
1038
+ for (let i = 0; i < HISTOGRAM_BUFFER_SIZE; i++) {
1039
+ const o = base + 3 * i;
1040
+ d.bin_data.push(buf[o] * 65536 + buf[o + 1] * 256 + buf[o + 2]);
1041
+ }
1042
+ const g = this.img.general_config.v;
1043
+ const s = this.img.static_config.v;
1044
+ const t = this.img.timing_config.v;
1045
+ const dyn = this.img.dynamic_config.v;
1046
+ d.cal_config__vcsel_start = g.cal_config__vcsel_start;
1047
+ d.vcsel_width = shl(g.global_config__vcsel_width, 4) + s.ana_config__vcsel_pulse_width_offset;
1048
+ d.fast_osc_frequency = this.img.static_nvm_managed.v.osc_measured__fast_osc__frequency;
1049
+ d.roi_config__user_roi_centre_spad = dyn.roi_config__user_roi_centre_spad;
1050
+ d.roi_config__user_roi_requested_global_xy_size =
1051
+ dyn.roi_config__user_roi_requested_global_xy_size;
1052
+ d.zone_id = 0;
1053
+ d.bin_seq = this.histCfg.binSequence(this.state.rd_stream_count, this.ambientEventsSum);
1054
+ let encoded;
1055
+ if (this.state.rd_timing_status === 0) {
1056
+ encoded = shl(t.range_config__timeout_macrop_a_hi, 8) + t.range_config__timeout_macrop_a_lo;
1057
+ d.vcsel_period = t.range_config__vcsel_period_a;
1058
+ }
1059
+ else {
1060
+ encoded = shl(t.range_config__timeout_macrop_b_hi, 8) + t.range_config__timeout_macrop_b_lo;
1061
+ d.vcsel_period = t.range_config__vcsel_period_b;
1062
+ }
1063
+ d.number_of_ambient_bins = 4 * d.bin_seq.filter((x) => (x & 0x07) === 0x07).length;
1064
+ d.total_periods_elapsed = decodeTimeout(encoded);
1065
+ d.peak_duration_us = durationMaths(calcPllPeriodUs(d.fast_osc_frequency), d.vcsel_width, RANGING_WINDOW_VCSEL_PERIODS, d.total_periods_elapsed + 1);
1066
+ d.woi_duration_us = 0;
1067
+ // VL53LX_hist_calc_zero_distance_phase
1068
+ const period = 2048 * decodeVcselPeriod(d.vcsel_period);
1069
+ const phase = period + d.phasecal_result__reference_phase +
1070
+ 2048 * d.phasecal_result__vcsel_start - 2048 * d.cal_config__vcsel_start;
1071
+ d.zero_distance_phase = period ? pymod(phase, period) : 0;
1072
+ // VL53LX_hist_estimate_ambient_from_ambient_bins
1073
+ if (d.number_of_ambient_bins > 0) {
1074
+ d.number_of_ambient_samples = d.number_of_ambient_bins;
1075
+ let sum = 0;
1076
+ for (const x of d.bin_data.slice(0, d.number_of_ambient_bins))
1077
+ sum += x;
1078
+ d.ambient_events_sum = sum;
1079
+ d.ambient_per_bin = floorDiv(d.ambient_events_sum + floorDiv(d.number_of_ambient_bins, 2), d.number_of_ambient_bins);
1080
+ }
1081
+ this.ambientEventsSum = d.ambient_events_sum;
1082
+ return d;
1083
+ }
1084
+ }
1085
+ //# sourceMappingURL=core.js.map