@ziztechnology/dial-library 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -23
- package/dist/index.d.mts +81 -28
- package/dist/index.mjs +667 -666
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { Gunzip } from "fflate";
|
|
2
|
-
|
|
3
|
-
const DEFAULT_LOG_INTERVAL_MS = 1e3;
|
|
4
|
-
const resolveLogIntervalMs = (intervalMs = void 0) => typeof intervalMs === "number" && Number.isFinite(intervalMs) && intervalMs > 0 ? intervalMs : DEFAULT_LOG_INTERVAL_MS;
|
|
2
|
+
const resolveLogIntervalMs = (intervalMs = void 0) => typeof intervalMs === "number" && Number.isFinite(intervalMs) && intervalMs > 0 ? intervalMs : 500;
|
|
5
3
|
//#endregion
|
|
6
4
|
//#region src/helper/debugger.ts
|
|
7
5
|
const logIntervalMs = resolveLogIntervalMs();
|
|
@@ -40,47 +38,62 @@ const dialSdkDebugWarn = (moduleName, scope, phase, data) => {
|
|
|
40
38
|
if (!shouldLog(moduleName, scope, phase)) return;
|
|
41
39
|
const message = `[DIAL_SDK_DEBUG - ${moduleName}] ${scope}: ${phase}`;
|
|
42
40
|
try {
|
|
43
|
-
if (data === void 0) console.
|
|
44
|
-
else console.
|
|
41
|
+
if (data === void 0) console.log(message);
|
|
42
|
+
else console.log(message, stringifyDebugData(data));
|
|
45
43
|
} catch {}
|
|
46
44
|
};
|
|
47
45
|
//#endregion
|
|
48
46
|
//#region src/sensor/check_status.ts
|
|
49
47
|
const DEBUG_MODULE$5 = "check_status.ts";
|
|
48
|
+
const DEVICE_RIGHT = {
|
|
49
|
+
x: 1,
|
|
50
|
+
y: 0,
|
|
51
|
+
z: 0
|
|
52
|
+
};
|
|
53
|
+
const ZERO_VECTOR$1 = {
|
|
54
|
+
x: 0,
|
|
55
|
+
y: 0,
|
|
56
|
+
z: 0
|
|
57
|
+
};
|
|
50
58
|
const DEFAULT_DRIVING_STATUS_TUNING = Object.freeze({
|
|
51
|
-
version: "driving-status-
|
|
59
|
+
version: "driving-status-calibrated-2026-07",
|
|
52
60
|
maxSampleAgeMs: 1e3,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
turnLateralThreshold: 2.5,
|
|
61
|
+
gravityFilterTimeConstantMs: 1e3,
|
|
62
|
+
biasFilterTimeConstantMs: 1e4,
|
|
63
|
+
vehicleRightMinimumNorm: .2,
|
|
57
64
|
motionWindowMs: 1e3,
|
|
58
65
|
motionMinimumSamples: 5,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
stoppedGyroThreshold: .
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
66
|
+
stoppedMotionThreshold: .12,
|
|
67
|
+
drivingMotionThreshold: .18,
|
|
68
|
+
stoppedGyroThreshold: .025,
|
|
69
|
+
stoppedRawGyroThreshold: .065,
|
|
70
|
+
drivingGyroThreshold: .04,
|
|
71
|
+
drivingRawGyroThreshold: .08,
|
|
72
|
+
stoppedConfirmationMs: 5e3,
|
|
73
|
+
drivingConfirmationMs: 600,
|
|
74
|
+
longitudinalWindowMs: 800,
|
|
75
|
+
longitudinalMinimumSamples: 5,
|
|
76
|
+
longitudinalThreshold: .7,
|
|
77
|
+
longitudinalExitThreshold: .35,
|
|
78
|
+
longitudinalSignCoherence: .8,
|
|
79
|
+
longitudinalReleaseMs: 500,
|
|
80
|
+
rapidLongitudinalWindowMs: 600,
|
|
81
|
+
rapidLongitudinalMinimumSamples: 4,
|
|
82
|
+
rapidLongitudinalPeakThreshold: 2.2,
|
|
83
|
+
rapidLongitudinalMeanThreshold: 1.4,
|
|
84
|
+
rapidLongitudinalSignCoherence: .75,
|
|
85
|
+
rapidMinimumDisplayMs: 500,
|
|
86
|
+
verticalBumpRatio: 1.25,
|
|
87
|
+
longitudinalReversalWindowMs: 400,
|
|
88
|
+
turnWindowMs: 1500,
|
|
89
|
+
turnMinimumSamples: 4,
|
|
90
|
+
turnYawThreshold: .12,
|
|
91
|
+
turnConfirmationMs: 600,
|
|
92
|
+
turnIntegratedAngleThreshold: .12,
|
|
93
|
+
turnSignCoherence: .7,
|
|
94
|
+
turnExitYawThreshold: .04,
|
|
95
|
+
turnExitConfirmationMs: 700,
|
|
96
|
+
turnClosureWindowMs: 1500
|
|
84
97
|
});
|
|
85
98
|
const classifyDrivingSnapshot = (info, tuningOverrides) => {
|
|
86
99
|
dialSdkDebugWarn(DEBUG_MODULE$5, "classifyDrivingSnapshot", "called", {
|
|
@@ -89,52 +102,91 @@ const classifyDrivingSnapshot = (info, tuningOverrides) => {
|
|
|
89
102
|
});
|
|
90
103
|
const tuning = resolveDrivingStatusTuning(tuningOverrides);
|
|
91
104
|
const metrics = extractDrivingSnapshotMetrics(info, tuning);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
105
|
+
if (metrics === null) {
|
|
106
|
+
dialSdkDebugWarn(DEBUG_MODULE$5, "classifyDrivingSnapshot", "returned", { result: null });
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const { candidateStatus, rejectionReason } = classifyInstantaneousEvidence(metrics, tuning);
|
|
110
|
+
const result = {
|
|
111
|
+
capturedAtMs: info.capturedAtMs,
|
|
95
112
|
metrics,
|
|
96
|
-
|
|
97
|
-
|
|
113
|
+
candidateStatus,
|
|
114
|
+
rejectionReason,
|
|
115
|
+
quality: {
|
|
116
|
+
frameValid: true,
|
|
117
|
+
maximumSampleAgeMs: Math.max(info.capturedAtMs - metrics.linearAccelerationSampledAtMs, info.capturedAtMs - metrics.gyroscopeSampledAtMs, info.capturedAtMs - metrics.gravitySampledAtMs)
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
dialSdkDebugWarn(DEBUG_MODULE$5, "classifyDrivingSnapshot", "returned", { result });
|
|
98
121
|
return result;
|
|
99
122
|
};
|
|
100
|
-
const checkRunningStatus = classifyDrivingSnapshot;
|
|
101
123
|
const resolveDrivingStatusTuning = (overrides = {}) => {
|
|
102
124
|
dialSdkDebugWarn(DEBUG_MODULE$5, "resolveDrivingStatusTuning", "called", { overrides });
|
|
103
125
|
const tuning = {
|
|
104
126
|
...DEFAULT_DRIVING_STATUS_TUNING,
|
|
105
|
-
...overrides
|
|
106
|
-
confirmationSamples: {
|
|
107
|
-
...DEFAULT_DRIVING_STATUS_TUNING.confirmationSamples,
|
|
108
|
-
...overrides.confirmationSamples
|
|
109
|
-
},
|
|
110
|
-
minimumDisplayMs: {
|
|
111
|
-
...DEFAULT_DRIVING_STATUS_TUNING.minimumDisplayMs,
|
|
112
|
-
...overrides.minimumDisplayMs
|
|
113
|
-
}
|
|
127
|
+
...overrides
|
|
114
128
|
};
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
for (const field of [
|
|
130
|
+
"maxSampleAgeMs",
|
|
131
|
+
"gravityFilterTimeConstantMs",
|
|
132
|
+
"biasFilterTimeConstantMs",
|
|
133
|
+
"vehicleRightMinimumNorm",
|
|
134
|
+
"motionWindowMs",
|
|
135
|
+
"stoppedMotionThreshold",
|
|
136
|
+
"drivingMotionThreshold",
|
|
137
|
+
"stoppedGyroThreshold",
|
|
138
|
+
"stoppedRawGyroThreshold",
|
|
139
|
+
"drivingGyroThreshold",
|
|
140
|
+
"drivingRawGyroThreshold",
|
|
141
|
+
"stoppedConfirmationMs",
|
|
142
|
+
"drivingConfirmationMs",
|
|
143
|
+
"longitudinalWindowMs",
|
|
144
|
+
"longitudinalThreshold",
|
|
145
|
+
"longitudinalExitThreshold",
|
|
146
|
+
"longitudinalReleaseMs",
|
|
147
|
+
"rapidLongitudinalWindowMs",
|
|
148
|
+
"rapidLongitudinalPeakThreshold",
|
|
149
|
+
"rapidLongitudinalMeanThreshold",
|
|
150
|
+
"rapidMinimumDisplayMs",
|
|
151
|
+
"verticalBumpRatio",
|
|
152
|
+
"longitudinalReversalWindowMs",
|
|
153
|
+
"turnWindowMs",
|
|
154
|
+
"turnYawThreshold",
|
|
155
|
+
"turnConfirmationMs",
|
|
156
|
+
"turnIntegratedAngleThreshold",
|
|
157
|
+
"turnExitYawThreshold",
|
|
158
|
+
"turnExitConfirmationMs",
|
|
159
|
+
"turnClosureWindowMs"
|
|
160
|
+
]) assertPositiveFinite(field, tuning[field]);
|
|
161
|
+
for (const field of [
|
|
162
|
+
"motionMinimumSamples",
|
|
163
|
+
"longitudinalMinimumSamples",
|
|
164
|
+
"rapidLongitudinalMinimumSamples",
|
|
165
|
+
"turnMinimumSamples"
|
|
166
|
+
]) if (!Number.isInteger(tuning[field]) || tuning[field] < 1) throwLoggedRangeError("resolveDrivingStatusTuning", `${field} must be a positive integer`);
|
|
167
|
+
for (const field of [
|
|
168
|
+
"longitudinalSignCoherence",
|
|
169
|
+
"rapidLongitudinalSignCoherence",
|
|
170
|
+
"turnSignCoherence"
|
|
171
|
+
]) {
|
|
172
|
+
const value = tuning[field];
|
|
173
|
+
if (!Number.isFinite(value) || value <= .5 || value > 1) throwLoggedRangeError("resolveDrivingStatusTuning", `${field} must be greater than 0.5 and less than or equal to 1`);
|
|
174
|
+
}
|
|
175
|
+
if (tuning.drivingMotionThreshold <= tuning.stoppedMotionThreshold) throwLoggedRangeError("resolveDrivingStatusTuning", "drivingMotionThreshold must be greater than stoppedMotionThreshold");
|
|
176
|
+
if (tuning.drivingGyroThreshold <= tuning.stoppedGyroThreshold) throwLoggedRangeError("resolveDrivingStatusTuning", "drivingGyroThreshold must be greater than stoppedGyroThreshold");
|
|
177
|
+
if (tuning.drivingRawGyroThreshold <= tuning.stoppedRawGyroThreshold) throwLoggedRangeError("resolveDrivingStatusTuning", "drivingRawGyroThreshold must be greater than stoppedRawGyroThreshold");
|
|
178
|
+
if (tuning.longitudinalThreshold <= tuning.longitudinalExitThreshold) throwLoggedRangeError("resolveDrivingStatusTuning", "longitudinalThreshold must be greater than longitudinalExitThreshold");
|
|
179
|
+
if (tuning.rapidLongitudinalMeanThreshold <= tuning.longitudinalThreshold) throwLoggedRangeError("resolveDrivingStatusTuning", "rapidLongitudinalMeanThreshold must be greater than longitudinalThreshold");
|
|
180
|
+
if (tuning.rapidLongitudinalPeakThreshold < tuning.rapidLongitudinalMeanThreshold) throwLoggedRangeError("resolveDrivingStatusTuning", "rapidLongitudinalPeakThreshold must be greater than or equal to rapidLongitudinalMeanThreshold");
|
|
181
|
+
if (tuning.turnYawThreshold <= tuning.turnExitYawThreshold) throwLoggedRangeError("resolveDrivingStatusTuning", "turnYawThreshold must be greater than turnExitYawThreshold");
|
|
131
182
|
dialSdkDebugWarn(DEBUG_MODULE$5, "resolveDrivingStatusTuning", "returned", { tuning });
|
|
132
183
|
return tuning;
|
|
133
184
|
};
|
|
134
|
-
const extractDrivingSnapshotMetrics = (info, tuning = DEFAULT_DRIVING_STATUS_TUNING) => {
|
|
185
|
+
const extractDrivingSnapshotMetrics = (info, tuning = DEFAULT_DRIVING_STATUS_TUNING, correction = {}) => {
|
|
135
186
|
dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "called", {
|
|
136
187
|
info,
|
|
137
|
-
tuning
|
|
188
|
+
tuning,
|
|
189
|
+
correction
|
|
138
190
|
});
|
|
139
191
|
if (!info || !Number.isFinite(info.capturedAtMs)) {
|
|
140
192
|
dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "returned invalid capture time", { info });
|
|
@@ -142,149 +194,148 @@ const extractDrivingSnapshotMetrics = (info, tuning = DEFAULT_DRIVING_STATUS_TUN
|
|
|
142
194
|
}
|
|
143
195
|
const linearAcceleration = availableFreshVector(info.linearAcceleration, info.capturedAtMs, tuning.maxSampleAgeMs);
|
|
144
196
|
const gyroscope = availableFreshVector(info.gyroscope, info.capturedAtMs, tuning.maxSampleAgeMs);
|
|
145
|
-
|
|
197
|
+
const gravityMetric = availableFreshVector(info.gravity, info.capturedAtMs, tuning.maxSampleAgeMs);
|
|
198
|
+
if (linearAcceleration === null || gyroscope === null || gravityMetric === null) {
|
|
146
199
|
dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "returned unavailable vectors", {
|
|
147
200
|
linearAcceleration,
|
|
148
|
-
gyroscope
|
|
201
|
+
gyroscope,
|
|
202
|
+
gravity: gravityMetric
|
|
149
203
|
});
|
|
150
204
|
return null;
|
|
151
205
|
}
|
|
152
|
-
const
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
206
|
+
const gravity = correction.filteredGravity ?? gravityMetric.value;
|
|
207
|
+
const frame = buildVehicleFrame(gravity, tuning.vehicleRightMinimumNorm);
|
|
208
|
+
if (frame === null) {
|
|
209
|
+
dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "returned invalid vehicle frame", {
|
|
210
|
+
gravity,
|
|
211
|
+
vehicleRightMinimumNorm: tuning.vehicleRightMinimumNorm
|
|
212
|
+
});
|
|
158
213
|
return null;
|
|
159
214
|
}
|
|
215
|
+
const correctedLinear = subtract(linearAcceleration.value, correction.linearAccelerationBias ?? ZERO_VECTOR$1);
|
|
216
|
+
const correctedGyroscope = subtract(gyroscope.value, correction.gyroscopeBias ?? ZERO_VECTOR$1);
|
|
160
217
|
const result = {
|
|
161
218
|
capturedAtMs: info.capturedAtMs,
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
219
|
+
linearAccelerationSampledAtMs: linearAcceleration.sampledAtMs,
|
|
220
|
+
gyroscopeSampledAtMs: gyroscope.sampledAtMs,
|
|
221
|
+
gravitySampledAtMs: gravityMetric.sampledAtMs,
|
|
222
|
+
longitudinal: dot(correctedLinear, frame.forward),
|
|
223
|
+
lateral: dot(correctedLinear, frame.right),
|
|
224
|
+
vertical: dot(correctedLinear, frame.up),
|
|
225
|
+
yaw: dot(correctedGyroscope, frame.up),
|
|
226
|
+
gyroMagnitude: magnitude(correctedGyroscope),
|
|
227
|
+
rawGyroMagnitude: magnitude(gyroscope.value),
|
|
228
|
+
rawLinearAcceleration: linearAcceleration.value,
|
|
229
|
+
rawGyroscope: gyroscope.value,
|
|
230
|
+
frame
|
|
167
231
|
};
|
|
168
232
|
dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "returned", { result });
|
|
169
233
|
return result;
|
|
170
234
|
};
|
|
171
|
-
const
|
|
172
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "
|
|
173
|
-
|
|
174
|
-
|
|
235
|
+
const buildVehicleFrame = (gravity, rightMinimumNorm = DEFAULT_DRIVING_STATUS_TUNING.vehicleRightMinimumNorm) => {
|
|
236
|
+
dialSdkDebugWarn(DEBUG_MODULE$5, "buildVehicleFrame", "called", {
|
|
237
|
+
gravity,
|
|
238
|
+
rightMinimumNorm
|
|
175
239
|
});
|
|
176
|
-
const
|
|
177
|
-
if (
|
|
178
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "
|
|
179
|
-
return
|
|
180
|
-
}
|
|
181
|
-
if (longitudinal >= tuning.rapidLongitudinalThreshold) {
|
|
182
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "classifyDrivingMetrics", "returned", { result: "RAPID_ACCELERATION" });
|
|
183
|
-
return "RAPID_ACCELERATION";
|
|
184
|
-
}
|
|
185
|
-
if (Math.abs(yaw) >= tuning.turnYawThreshold) {
|
|
186
|
-
const result = yaw > 0 ? "LEFT_TURN" : "RIGHT_TURN";
|
|
187
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "classifyDrivingMetrics", "returned", {
|
|
188
|
-
result,
|
|
189
|
-
basis: "yaw"
|
|
190
|
-
});
|
|
191
|
-
return result;
|
|
192
|
-
}
|
|
193
|
-
if (Math.abs(lateral) >= tuning.turnLateralThreshold) {
|
|
194
|
-
const result = lateral < 0 ? "LEFT_TURN" : "RIGHT_TURN";
|
|
195
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "classifyDrivingMetrics", "returned", {
|
|
196
|
-
result,
|
|
197
|
-
basis: "lateral"
|
|
198
|
-
});
|
|
199
|
-
return result;
|
|
240
|
+
const gravityDirection = normalize(gravity);
|
|
241
|
+
if (gravityDirection === null) {
|
|
242
|
+
dialSdkDebugWarn(DEBUG_MODULE$5, "buildVehicleFrame", "returned invalid gravity", { result: null });
|
|
243
|
+
return null;
|
|
200
244
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
245
|
+
const up = gravityDirection;
|
|
246
|
+
const projectedRight = subtract(DEVICE_RIGHT, scale(up, dot(DEVICE_RIGHT, up)));
|
|
247
|
+
if (magnitude(projectedRight) < rightMinimumNorm) {
|
|
248
|
+
dialSdkDebugWarn(DEBUG_MODULE$5, "buildVehicleFrame", "returned degenerate right axis", { result: null });
|
|
249
|
+
return null;
|
|
204
250
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
251
|
+
const right = normalize(projectedRight);
|
|
252
|
+
if (right === null) {
|
|
253
|
+
dialSdkDebugWarn(DEBUG_MODULE$5, "buildVehicleFrame", "returned invalid right axis", { result: null });
|
|
254
|
+
return null;
|
|
208
255
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
256
|
+
const forward = normalize(cross(right, up));
|
|
257
|
+
const result = forward === null ? null : {
|
|
258
|
+
up,
|
|
259
|
+
right,
|
|
260
|
+
forward
|
|
261
|
+
};
|
|
262
|
+
dialSdkDebugWarn(DEBUG_MODULE$5, "buildVehicleFrame", "returned", { result });
|
|
263
|
+
return result;
|
|
264
|
+
};
|
|
265
|
+
const classifyInstantaneousEvidence = (metrics, tuning) => {
|
|
266
|
+
const longitudinalMagnitude = Math.abs(metrics.longitudinal);
|
|
267
|
+
if (longitudinalMagnitude >= tuning.rapidLongitudinalPeakThreshold) {
|
|
268
|
+
if (Math.abs(metrics.vertical) > longitudinalMagnitude * tuning.verticalBumpRatio) return {
|
|
269
|
+
candidateStatus: null,
|
|
270
|
+
rejectionReason: "vertical_bump"
|
|
271
|
+
};
|
|
272
|
+
return {
|
|
273
|
+
candidateStatus: metrics.longitudinal > 0 ? "RAPID_ACCELERATION" : "SUDDEN_BRAKING",
|
|
274
|
+
rejectionReason: null
|
|
275
|
+
};
|
|
212
276
|
}
|
|
213
|
-
|
|
214
|
-
|
|
277
|
+
if (Math.abs(metrics.yaw) >= tuning.turnYawThreshold) return {
|
|
278
|
+
candidateStatus: metrics.yaw > 0 ? "LEFT_TURN" : "RIGHT_TURN",
|
|
279
|
+
rejectionReason: null
|
|
280
|
+
};
|
|
281
|
+
if (longitudinalMagnitude >= tuning.longitudinalThreshold) return {
|
|
282
|
+
candidateStatus: metrics.longitudinal > 0 ? "ACCELERATION" : "BRAKING",
|
|
283
|
+
rejectionReason: null
|
|
284
|
+
};
|
|
285
|
+
return {
|
|
286
|
+
candidateStatus: null,
|
|
287
|
+
rejectionReason: null
|
|
288
|
+
};
|
|
215
289
|
};
|
|
216
290
|
const availableFreshVector = (metric, capturedAtMs, maxSampleAgeMs) => {
|
|
217
|
-
|
|
218
|
-
metric,
|
|
219
|
-
capturedAtMs,
|
|
220
|
-
maxSampleAgeMs
|
|
221
|
-
});
|
|
222
|
-
if (!metric?.available || !isFresh(metric.sampledAtMs, capturedAtMs, maxSampleAgeMs)) {
|
|
223
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "availableFreshVector", "returned", { result: null });
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
291
|
+
if (!metric?.available || !isFresh(metric.sampledAtMs, capturedAtMs, maxSampleAgeMs)) return null;
|
|
226
292
|
const { x, y, z } = metric.value;
|
|
227
|
-
|
|
293
|
+
return [
|
|
228
294
|
x,
|
|
229
295
|
y,
|
|
230
296
|
z
|
|
231
|
-
].every(Number.isFinite) ?
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
};
|
|
235
|
-
const availableFreshNumber = (metric, capturedAtMs, maxSampleAgeMs) => {
|
|
236
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "availableFreshNumber", "called", {
|
|
237
|
-
metric,
|
|
238
|
-
capturedAtMs,
|
|
239
|
-
maxSampleAgeMs
|
|
240
|
-
});
|
|
241
|
-
if (!metric?.available || !isFresh(metric.sampledAtMs, capturedAtMs, maxSampleAgeMs)) {
|
|
242
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "availableFreshNumber", "returned", { result: null });
|
|
243
|
-
return null;
|
|
244
|
-
}
|
|
245
|
-
const result = Number.isFinite(metric.value) && metric.value >= 0 ? metric.value : null;
|
|
246
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "availableFreshNumber", "returned", { result });
|
|
247
|
-
return result;
|
|
297
|
+
].every(Number.isFinite) ? {
|
|
298
|
+
value: metric.value,
|
|
299
|
+
sampledAtMs: metric.sampledAtMs
|
|
300
|
+
} : null;
|
|
248
301
|
};
|
|
249
302
|
const isFresh = (sampledAtMs, capturedAtMs, maxSampleAgeMs) => {
|
|
250
|
-
|
|
251
|
-
sampledAtMs,
|
|
252
|
-
capturedAtMs,
|
|
253
|
-
maxSampleAgeMs
|
|
254
|
-
});
|
|
255
|
-
if (sampledAtMs === null || !Number.isFinite(sampledAtMs)) {
|
|
256
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "isFresh", "returned", { result: false });
|
|
257
|
-
return false;
|
|
258
|
-
}
|
|
303
|
+
if (sampledAtMs === null || !Number.isFinite(sampledAtMs)) return false;
|
|
259
304
|
const age = capturedAtMs - sampledAtMs;
|
|
260
|
-
|
|
261
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "isFresh", "returned", {
|
|
262
|
-
age,
|
|
263
|
-
result
|
|
264
|
-
});
|
|
265
|
-
return result;
|
|
305
|
+
return age >= 0 && age <= maxSampleAgeMs;
|
|
266
306
|
};
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
307
|
+
const dot = (left, right) => left.x * right.x + left.y * right.y + left.z * right.z;
|
|
308
|
+
const cross = (left, right) => ({
|
|
309
|
+
x: left.y * right.z - left.z * right.y,
|
|
310
|
+
y: left.z * right.x - left.x * right.z,
|
|
311
|
+
z: left.x * right.y - left.y * right.x
|
|
312
|
+
});
|
|
313
|
+
const subtract = (left, right) => ({
|
|
314
|
+
x: left.x - right.x,
|
|
315
|
+
y: left.y - right.y,
|
|
316
|
+
z: left.z - right.z
|
|
317
|
+
});
|
|
318
|
+
const scale = (value, factor) => ({
|
|
319
|
+
x: value.x * factor,
|
|
320
|
+
y: value.y * factor,
|
|
321
|
+
z: value.z * factor
|
|
322
|
+
});
|
|
323
|
+
const magnitude = (value) => Math.hypot(value.x, value.y, value.z);
|
|
324
|
+
const normalize = (value) => {
|
|
325
|
+
const length = magnitude(value);
|
|
326
|
+
return Number.isFinite(length) && length > Number.EPSILON ? scale(value, 1 / length) : null;
|
|
276
327
|
};
|
|
277
328
|
const assertPositiveFinite = (name, value) => {
|
|
278
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "assertPositiveFinite", "called", {
|
|
279
|
-
name,
|
|
280
|
-
value
|
|
281
|
-
});
|
|
282
329
|
if (!Number.isFinite(value) || value <= 0) {
|
|
283
330
|
const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
|
|
284
331
|
dialSdkDebugWarn(DEBUG_MODULE$5, "assertPositiveFinite", "threw", error);
|
|
285
332
|
throw error;
|
|
286
333
|
}
|
|
287
|
-
|
|
334
|
+
};
|
|
335
|
+
const throwLoggedRangeError = (scope, message) => {
|
|
336
|
+
const error = new RangeError(message);
|
|
337
|
+
dialSdkDebugWarn(DEBUG_MODULE$5, scope, "threw", error);
|
|
338
|
+
throw error;
|
|
288
339
|
};
|
|
289
340
|
//#endregion
|
|
290
341
|
//#region src/sensor/unified_info.ts
|
|
@@ -1274,6 +1325,11 @@ const safeInvoke = (callback) => {
|
|
|
1274
1325
|
//#endregion
|
|
1275
1326
|
//#region src/sensor/driving_status_controller.ts
|
|
1276
1327
|
const DEBUG_MODULE$1 = "driving_status_controller.ts";
|
|
1328
|
+
const ZERO_VECTOR = {
|
|
1329
|
+
x: 0,
|
|
1330
|
+
y: 0,
|
|
1331
|
+
z: 0
|
|
1332
|
+
};
|
|
1277
1333
|
const createDrivingStatusController = (options = {}) => {
|
|
1278
1334
|
dialSdkDebugWarn(DEBUG_MODULE$1, "createDrivingStatusController", "called", { options });
|
|
1279
1335
|
const result = new DefaultDrivingStatusController(options);
|
|
@@ -1283,14 +1339,30 @@ const createDrivingStatusController = (options = {}) => {
|
|
|
1283
1339
|
var DefaultDrivingStatusController = class {
|
|
1284
1340
|
constructor(options) {
|
|
1285
1341
|
this.listeners = /* @__PURE__ */ new Set();
|
|
1286
|
-
this.
|
|
1342
|
+
this.longitudinalWindow = [];
|
|
1343
|
+
this.yawWindow = [];
|
|
1287
1344
|
this.motionWindow = [];
|
|
1288
1345
|
this.candidate = null;
|
|
1289
|
-
this.
|
|
1346
|
+
this.candidateSinceMs = null;
|
|
1290
1347
|
this.lastAvailableAtMs = null;
|
|
1291
1348
|
this.unavailableSinceMs = null;
|
|
1292
1349
|
this.fallbackApplied = false;
|
|
1293
|
-
this.
|
|
1350
|
+
this.filteredGravity = null;
|
|
1351
|
+
this.lastGravitySampledAtMs = null;
|
|
1352
|
+
this.linearAccelerationBias = { ...ZERO_VECTOR };
|
|
1353
|
+
this.gyroscopeBias = { ...ZERO_VECTOR };
|
|
1354
|
+
this.biasCalibrated = false;
|
|
1355
|
+
this.lastBiasSampledAtMs = null;
|
|
1356
|
+
this.stoppedConditionSinceMs = null;
|
|
1357
|
+
this.drivingConditionSinceMs = null;
|
|
1358
|
+
this.stationaryConfirmed = false;
|
|
1359
|
+
this.urgentStatus = null;
|
|
1360
|
+
this.urgentStartedAtMs = null;
|
|
1361
|
+
this.normalLongitudinalStatus = null;
|
|
1362
|
+
this.longitudinalClearSinceMs = null;
|
|
1363
|
+
this.turnStatus = null;
|
|
1364
|
+
this.turnQuietSinceMs = null;
|
|
1365
|
+
this.oppositeTurnSinceMs = null;
|
|
1294
1366
|
this.requestedRunning = false;
|
|
1295
1367
|
this.polling = false;
|
|
1296
1368
|
this.lifecycleSuspended = false;
|
|
@@ -1299,12 +1371,12 @@ var DefaultDrivingStatusController = class {
|
|
|
1299
1371
|
this.pendingRead = null;
|
|
1300
1372
|
this.unsubscribePageLifecycle = null;
|
|
1301
1373
|
this.handleLifecyclePause = () => {
|
|
1302
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecyclePause", "called"
|
|
1303
|
-
requestedRunning: this.requestedRunning,
|
|
1304
|
-
lifecycleSuspended: this.lifecycleSuspended
|
|
1305
|
-
});
|
|
1374
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecyclePause", "called");
|
|
1306
1375
|
if (!this.requestedRunning || this.lifecycleSuspended) {
|
|
1307
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecyclePause", "ignored"
|
|
1376
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecyclePause", "ignored", {
|
|
1377
|
+
requestedRunning: this.requestedRunning,
|
|
1378
|
+
lifecycleSuspended: this.lifecycleSuspended
|
|
1379
|
+
});
|
|
1308
1380
|
return;
|
|
1309
1381
|
}
|
|
1310
1382
|
this.lifecycleSuspended = true;
|
|
@@ -1313,13 +1385,15 @@ var DefaultDrivingStatusController = class {
|
|
|
1313
1385
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecyclePause", "returned");
|
|
1314
1386
|
};
|
|
1315
1387
|
this.handleLifecycleResume = () => {
|
|
1316
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecycleResume", "called"
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1388
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecycleResume", "called");
|
|
1389
|
+
const pageHidden = this.requestedRunning && this.lifecycleSuspended && !this.destroyed ? isDrivingPageHidden() : false;
|
|
1390
|
+
if (!this.requestedRunning || !this.lifecycleSuspended || this.destroyed || pageHidden) {
|
|
1391
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecycleResume", "ignored", {
|
|
1392
|
+
requestedRunning: this.requestedRunning,
|
|
1393
|
+
lifecycleSuspended: this.lifecycleSuspended,
|
|
1394
|
+
destroyed: this.destroyed,
|
|
1395
|
+
pageHidden
|
|
1396
|
+
});
|
|
1323
1397
|
return;
|
|
1324
1398
|
}
|
|
1325
1399
|
this.lifecycleSuspended = false;
|
|
@@ -1335,18 +1409,15 @@ var DefaultDrivingStatusController = class {
|
|
|
1335
1409
|
this.onDiagnostic = options.onDiagnostic;
|
|
1336
1410
|
this.managePageLifecycle = options.managePageLifecycle ?? true;
|
|
1337
1411
|
const initialStatus = options.initialStatus ?? "STOPPED";
|
|
1338
|
-
if (!CAR_RUNNING_STATUSES.includes(initialStatus))
|
|
1412
|
+
if (!CAR_RUNNING_STATUSES.includes(initialStatus)) {
|
|
1413
|
+
const error = /* @__PURE__ */ new TypeError(`Unsupported initialStatus: ${initialStatus}`);
|
|
1414
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.constructor", "threw", error);
|
|
1415
|
+
throw error;
|
|
1416
|
+
}
|
|
1339
1417
|
this.status = initialStatus;
|
|
1340
1418
|
this.baseStatus = isBaseStatus(initialStatus) ? initialStatus : "STOPPED";
|
|
1341
1419
|
this.statusCommittedAtMs = this.clock.now();
|
|
1342
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.constructor", "created",
|
|
1343
|
-
initialStatus,
|
|
1344
|
-
baseStatus: this.baseStatus,
|
|
1345
|
-
pollIntervalMs: this.pollIntervalMs,
|
|
1346
|
-
unavailableFallbackMs: this.unavailableFallbackMs,
|
|
1347
|
-
tuning: this.tuning,
|
|
1348
|
-
managePageLifecycle: this.managePageLifecycle
|
|
1349
|
-
});
|
|
1420
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.constructor", "created", this.getSnapshot());
|
|
1350
1421
|
}
|
|
1351
1422
|
start() {
|
|
1352
1423
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.start", "called", this.getSnapshot());
|
|
@@ -1391,16 +1462,17 @@ var DefaultDrivingStatusController = class {
|
|
|
1391
1462
|
}
|
|
1392
1463
|
this.destroyed = true;
|
|
1393
1464
|
this.listeners.clear();
|
|
1394
|
-
this.
|
|
1395
|
-
this.motionWindow.length = 0;
|
|
1465
|
+
this.resetTransientRecognition();
|
|
1396
1466
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.destroy", "returned", this.getSnapshot());
|
|
1397
1467
|
}
|
|
1398
1468
|
getSnapshot() {
|
|
1399
1469
|
const result = {
|
|
1400
1470
|
lifecycle: this.destroyed ? "destroyed" : this.polling ? "running" : "stopped",
|
|
1401
1471
|
status: this.status,
|
|
1472
|
+
baseStatus: this.baseStatus,
|
|
1473
|
+
activeTurn: this.turnStatus,
|
|
1402
1474
|
candidate: this.candidate,
|
|
1403
|
-
|
|
1475
|
+
candidateSinceMs: this.candidateSinceMs,
|
|
1404
1476
|
statusCommittedAtMs: this.statusCommittedAtMs,
|
|
1405
1477
|
lastAvailableAtMs: this.lastAvailableAtMs,
|
|
1406
1478
|
unavailableSinceMs: this.unavailableSinceMs,
|
|
@@ -1411,16 +1483,16 @@ var DefaultDrivingStatusController = class {
|
|
|
1411
1483
|
}
|
|
1412
1484
|
subscribe(listener) {
|
|
1413
1485
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.subscribe", "called", {
|
|
1414
|
-
|
|
1415
|
-
|
|
1486
|
+
listener,
|
|
1487
|
+
destroyed: this.destroyed
|
|
1416
1488
|
});
|
|
1417
1489
|
if (this.destroyed) {
|
|
1418
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.subscribe", "
|
|
1490
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.subscribe", "ignored destroyed controller");
|
|
1419
1491
|
return noop;
|
|
1420
1492
|
}
|
|
1421
1493
|
this.listeners.add(listener);
|
|
1494
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.subscribe", "returned", { listenerCount: this.listeners.size });
|
|
1422
1495
|
let subscribed = true;
|
|
1423
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.subscribe", "subscribed", { listenerCount: this.listeners.size });
|
|
1424
1496
|
return () => {
|
|
1425
1497
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.unsubscribe", "called", { subscribed });
|
|
1426
1498
|
if (!subscribed) {
|
|
@@ -1433,15 +1505,14 @@ var DefaultDrivingStatusController = class {
|
|
|
1433
1505
|
};
|
|
1434
1506
|
}
|
|
1435
1507
|
activatePolling() {
|
|
1436
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.activatePolling", "called"
|
|
1437
|
-
destroyed: this.destroyed,
|
|
1438
|
-
polling: this.polling,
|
|
1439
|
-
requestedRunning: this.requestedRunning,
|
|
1440
|
-
lifecycleSuspended: this.lifecycleSuspended,
|
|
1441
|
-
generation: this.generation
|
|
1442
|
-
});
|
|
1508
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.activatePolling", "called");
|
|
1443
1509
|
if (this.destroyed || this.polling || !this.requestedRunning || this.lifecycleSuspended) {
|
|
1444
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.activatePolling", "ignored"
|
|
1510
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.activatePolling", "ignored", {
|
|
1511
|
+
destroyed: this.destroyed,
|
|
1512
|
+
polling: this.polling,
|
|
1513
|
+
requestedRunning: this.requestedRunning,
|
|
1514
|
+
lifecycleSuspended: this.lifecycleSuspended
|
|
1515
|
+
});
|
|
1445
1516
|
return;
|
|
1446
1517
|
}
|
|
1447
1518
|
this.polling = true;
|
|
@@ -1451,14 +1522,10 @@ var DefaultDrivingStatusController = class {
|
|
|
1451
1522
|
}
|
|
1452
1523
|
deactivatePolling() {
|
|
1453
1524
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.deactivatePolling", "called", {
|
|
1454
|
-
|
|
1455
|
-
|
|
1525
|
+
generation: this.generation,
|
|
1526
|
+
hasTimer: this.timer !== void 0,
|
|
1456
1527
|
hasPendingRead: this.pendingRead !== null
|
|
1457
1528
|
});
|
|
1458
|
-
if (!this.polling && this.timer === void 0 && this.pendingRead === null) {
|
|
1459
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.deactivatePolling", "ignored");
|
|
1460
|
-
return;
|
|
1461
|
-
}
|
|
1462
1529
|
this.polling = false;
|
|
1463
1530
|
this.generation += 1;
|
|
1464
1531
|
if (this.timer !== void 0) {
|
|
@@ -1470,72 +1537,86 @@ var DefaultDrivingStatusController = class {
|
|
|
1470
1537
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.deactivatePolling", "returned", { generation: this.generation });
|
|
1471
1538
|
}
|
|
1472
1539
|
schedulePoll(delayMs, generation) {
|
|
1473
|
-
|
|
1474
|
-
delayMs,
|
|
1475
|
-
generation,
|
|
1476
|
-
currentGeneration: this.generation,
|
|
1477
|
-
polling: this.polling,
|
|
1478
|
-
timer: this.timer
|
|
1479
|
-
});
|
|
1480
|
-
if (!this.polling || generation !== this.generation || this.timer !== void 0) {
|
|
1481
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.schedulePoll", "ignored");
|
|
1482
|
-
return;
|
|
1483
|
-
}
|
|
1540
|
+
if (!this.polling || generation !== this.generation || this.timer !== void 0) return;
|
|
1484
1541
|
this.timer = this.clock.setTimeout(() => {
|
|
1485
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.schedulePoll.callback", "called", { generation });
|
|
1486
1542
|
this.timer = void 0;
|
|
1487
1543
|
this.poll(generation);
|
|
1488
1544
|
}, delayMs);
|
|
1489
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.schedulePoll", "scheduled", {
|
|
1490
|
-
delayMs,
|
|
1491
|
-
generation,
|
|
1492
|
-
timer: this.timer
|
|
1493
|
-
});
|
|
1494
1545
|
}
|
|
1495
1546
|
async poll(generation) {
|
|
1496
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "called", {
|
|
1497
|
-
generation,
|
|
1498
|
-
currentGeneration: this.generation,
|
|
1499
|
-
polling: this.polling,
|
|
1500
|
-
hasPendingRead: this.pendingRead !== null
|
|
1501
|
-
});
|
|
1547
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "called", { generation });
|
|
1502
1548
|
if (!this.polling || generation !== this.generation || this.pendingRead !== null) {
|
|
1503
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "ignored"
|
|
1549
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "ignored", {
|
|
1550
|
+
polling: this.polling,
|
|
1551
|
+
generation,
|
|
1552
|
+
currentGeneration: this.generation,
|
|
1553
|
+
hasPendingRead: this.pendingRead !== null
|
|
1554
|
+
});
|
|
1504
1555
|
return;
|
|
1505
1556
|
}
|
|
1506
1557
|
const pendingRead = new AbortController();
|
|
1507
1558
|
this.pendingRead = pendingRead;
|
|
1508
1559
|
try {
|
|
1509
1560
|
const payload = await this.sensorProvider(pendingRead.signal);
|
|
1510
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "provider resolved", { payload });
|
|
1511
1561
|
if (!this.polling || generation !== this.generation || pendingRead.signal.aborted) {
|
|
1512
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "discarded provider result"
|
|
1562
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "discarded provider result", {
|
|
1563
|
+
generation,
|
|
1564
|
+
currentGeneration: this.generation,
|
|
1565
|
+
polling: this.polling,
|
|
1566
|
+
aborted: pendingRead.signal.aborted
|
|
1567
|
+
});
|
|
1513
1568
|
return;
|
|
1514
1569
|
}
|
|
1515
|
-
const
|
|
1516
|
-
const
|
|
1517
|
-
|
|
1518
|
-
|
|
1570
|
+
const gravity = this.updateFilteredGravity(payload);
|
|
1571
|
+
const metrics = gravity === null ? null : extractDrivingSnapshotMetrics(payload, this.tuning, {
|
|
1572
|
+
filteredGravity: gravity,
|
|
1573
|
+
linearAccelerationBias: this.linearAccelerationBias,
|
|
1574
|
+
gyroscopeBias: this.gyroscopeBias
|
|
1575
|
+
});
|
|
1576
|
+
const accelerometerSample = extractFreshVectorMetric(payload.accelerometer, payload.capturedAtMs, this.tuning.maxSampleAgeMs);
|
|
1577
|
+
if (metrics === null || accelerometerSample === null) {
|
|
1578
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "invalid or stale snapshot", {
|
|
1579
|
+
metrics,
|
|
1580
|
+
accelerometerSample
|
|
1581
|
+
});
|
|
1582
|
+
this.processUnavailable("invalid_or_stale_snapshot");
|
|
1583
|
+
} else {
|
|
1584
|
+
this.processAvailable(metrics, accelerometerSample);
|
|
1585
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "processed sample", { capturedAtMs: metrics.capturedAtMs });
|
|
1586
|
+
}
|
|
1519
1587
|
} catch (error) {
|
|
1520
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "provider rejected", error);
|
|
1521
1588
|
if (!this.polling || generation !== this.generation || pendingRead.signal.aborted || isAbortError$1(error)) {
|
|
1522
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "ignored
|
|
1589
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "ignored provider error", { error });
|
|
1523
1590
|
return;
|
|
1524
1591
|
}
|
|
1592
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "caught provider error", { error });
|
|
1525
1593
|
this.processUnavailable("provider_error");
|
|
1526
1594
|
} finally {
|
|
1527
1595
|
if (this.pendingRead === pendingRead) this.pendingRead = null;
|
|
1528
1596
|
if (this.polling && generation === this.generation) this.schedulePoll(this.pollIntervalMs, generation);
|
|
1529
1597
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "completed", {
|
|
1530
1598
|
generation,
|
|
1531
|
-
polling: this.polling
|
|
1532
|
-
currentGeneration: this.generation
|
|
1599
|
+
polling: this.polling
|
|
1533
1600
|
});
|
|
1534
1601
|
}
|
|
1535
1602
|
}
|
|
1536
|
-
|
|
1603
|
+
updateFilteredGravity(info) {
|
|
1604
|
+
const sample = extractFreshVectorMetric(info.gravity, info.capturedAtMs, this.tuning.maxSampleAgeMs);
|
|
1605
|
+
if (sample === null) return null;
|
|
1606
|
+
if (sample.sampledAtMs === this.lastGravitySampledAtMs) return this.filteredGravity;
|
|
1607
|
+
if (this.filteredGravity === null || this.lastGravitySampledAtMs === null) this.filteredGravity = { ...sample.value };
|
|
1608
|
+
else {
|
|
1609
|
+
if (sample.sampledAtMs < this.lastGravitySampledAtMs) return this.filteredGravity;
|
|
1610
|
+
const elapsedMs = Math.max(0, sample.sampledAtMs - this.lastGravitySampledAtMs);
|
|
1611
|
+
const alpha = 1 - Math.exp(-elapsedMs / this.tuning.gravityFilterTimeConstantMs);
|
|
1612
|
+
this.filteredGravity = lerpVector(this.filteredGravity, sample.value, alpha);
|
|
1613
|
+
}
|
|
1614
|
+
this.lastGravitySampledAtMs = sample.sampledAtMs;
|
|
1615
|
+
return this.filteredGravity;
|
|
1616
|
+
}
|
|
1617
|
+
processAvailable(metrics, accelerometerSample) {
|
|
1537
1618
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processAvailable", "called", {
|
|
1538
|
-
|
|
1619
|
+
metrics,
|
|
1539
1620
|
accelerometerSample
|
|
1540
1621
|
});
|
|
1541
1622
|
const now = this.clock.now();
|
|
@@ -1545,56 +1626,156 @@ var DefaultDrivingStatusController = class {
|
|
|
1545
1626
|
this.emitDiagnostic({
|
|
1546
1627
|
type: "sample_available",
|
|
1547
1628
|
atMs: now,
|
|
1548
|
-
capturedAtMs:
|
|
1629
|
+
capturedAtMs: metrics.capturedAtMs,
|
|
1549
1630
|
tuningVersion: this.tuning.version
|
|
1550
1631
|
});
|
|
1551
|
-
this.
|
|
1552
|
-
|
|
1553
|
-
const
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1632
|
+
const addedLongitudinal = pushUniqueMetric(this.longitudinalWindow, metrics, "linearAccelerationSampledAtMs", Math.max(this.tuning.longitudinalWindowMs, this.tuning.rapidLongitudinalWindowMs));
|
|
1633
|
+
const addedYaw = pushUniqueMetric(this.yawWindow, metrics, "gyroscopeSampledAtMs", Math.max(this.tuning.turnWindowMs, this.tuning.turnClosureWindowMs));
|
|
1634
|
+
const addedMotion = pushUniqueVector(this.motionWindow, accelerometerSample, this.tuning.motionWindowMs);
|
|
1635
|
+
if (!addedLongitudinal && !addedYaw && !addedMotion) {
|
|
1636
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processAvailable", "ignored duplicate sample");
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
const motionMad = calculateCombinedAxisMad(this.motionWindow, this.tuning.motionMinimumSamples);
|
|
1640
|
+
const baseCondition = this.updateBaseDetector(motionMad, metrics, accelerometerSample.sampledAtMs, addedMotion);
|
|
1641
|
+
const longitudinalEvidence = detectLongitudinalEvidence(this.longitudinalWindow, this.tuning);
|
|
1642
|
+
if (addedLongitudinal) this.updateLongitudinalDetector(longitudinalEvidence, metrics.linearAccelerationSampledAtMs);
|
|
1643
|
+
const turnEvidence = detectTurnEvidence(this.yawWindow, this.tuning);
|
|
1644
|
+
if (addedYaw) this.updateTurnDetector(turnEvidence);
|
|
1645
|
+
if (this.urgentStatus !== null || this.turnStatus !== null || this.normalLongitudinalStatus !== null) {
|
|
1646
|
+
this.baseStatus = "STEADY_DRIVING";
|
|
1647
|
+
this.stoppedConditionSinceMs = null;
|
|
1648
|
+
this.stationaryConfirmed = false;
|
|
1649
|
+
}
|
|
1650
|
+
if (this.stationaryConfirmed) this.updateBias(metrics);
|
|
1651
|
+
const pendingCandidate = instantaneousCandidate(metrics, this.tuning);
|
|
1652
|
+
this.setCandidate(pendingCandidate === this.status ? null : pendingCandidate, observationTime(metrics));
|
|
1653
|
+
this.emitDiagnostic({
|
|
1654
|
+
type: "detector_evidence",
|
|
1655
|
+
atMs: now,
|
|
1656
|
+
motionMad,
|
|
1657
|
+
baseCondition,
|
|
1658
|
+
rapidCandidate: longitudinalEvidence.rapidCandidate,
|
|
1659
|
+
longitudinalCandidate: longitudinalEvidence.normalCandidate,
|
|
1660
|
+
turnCandidate: turnEvidence.candidate,
|
|
1661
|
+
rejectionReason: longitudinalEvidence.rejectionReason,
|
|
1662
|
+
tuningVersion: this.tuning.version
|
|
1563
1663
|
});
|
|
1664
|
+
const hasAction = this.urgentStatus !== null || this.turnStatus !== null || this.normalLongitudinalStatus !== null;
|
|
1665
|
+
if (motionMad !== null || hasAction) this.commitSelectedStatus(now);
|
|
1666
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processAvailable", "returned", {
|
|
1667
|
+
motionMad,
|
|
1668
|
+
baseCondition,
|
|
1669
|
+
status: this.status,
|
|
1670
|
+
baseStatus: this.baseStatus,
|
|
1671
|
+
urgentStatus: this.urgentStatus,
|
|
1672
|
+
turnStatus: this.turnStatus,
|
|
1673
|
+
normalLongitudinalStatus: this.normalLongitudinalStatus
|
|
1674
|
+
});
|
|
1675
|
+
}
|
|
1676
|
+
updateBaseDetector(motionMad, metrics, sampledAtMs, isNewMotionSample) {
|
|
1677
|
+
if (motionMad === null || !isNewMotionSample) return "warming_up";
|
|
1678
|
+
const gyroForStop = this.biasCalibrated ? metrics.gyroMagnitude : metrics.rawGyroMagnitude;
|
|
1679
|
+
const stoppedGyroThreshold = this.biasCalibrated ? this.tuning.stoppedGyroThreshold : this.tuning.stoppedRawGyroThreshold;
|
|
1680
|
+
const drivingGyroThreshold = this.biasCalibrated ? this.tuning.drivingGyroThreshold : this.tuning.drivingRawGyroThreshold;
|
|
1681
|
+
const stationary = motionMad <= this.tuning.stoppedMotionThreshold && gyroForStop <= stoppedGyroThreshold;
|
|
1682
|
+
const moving = motionMad >= this.tuning.drivingMotionThreshold || gyroForStop >= drivingGyroThreshold;
|
|
1683
|
+
if (stationary) {
|
|
1684
|
+
this.drivingConditionSinceMs = null;
|
|
1685
|
+
if (this.stoppedConditionSinceMs === null) this.stoppedConditionSinceMs = this.motionWindow[0]?.sampledAtMs ?? sampledAtMs;
|
|
1686
|
+
if (sampledAtMs - this.stoppedConditionSinceMs >= this.tuning.stoppedConfirmationMs) {
|
|
1687
|
+
this.baseStatus = "STOPPED";
|
|
1688
|
+
this.stationaryConfirmed = true;
|
|
1689
|
+
}
|
|
1690
|
+
return "stationary";
|
|
1691
|
+
}
|
|
1692
|
+
this.stoppedConditionSinceMs = null;
|
|
1693
|
+
this.stationaryConfirmed = false;
|
|
1694
|
+
if (moving) {
|
|
1695
|
+
if (this.drivingConditionSinceMs === null) this.drivingConditionSinceMs = this.motionWindow[0]?.sampledAtMs ?? sampledAtMs;
|
|
1696
|
+
if (sampledAtMs - this.drivingConditionSinceMs >= this.tuning.drivingConfirmationMs) this.baseStatus = "STEADY_DRIVING";
|
|
1697
|
+
return "moving";
|
|
1698
|
+
}
|
|
1699
|
+
this.drivingConditionSinceMs = null;
|
|
1700
|
+
return "hysteresis";
|
|
1701
|
+
}
|
|
1702
|
+
updateLongitudinalDetector(evidence, sampledAtMs) {
|
|
1703
|
+
if (evidence.rapidCandidate !== null) {
|
|
1704
|
+
if (this.urgentStatus !== evidence.rapidCandidate) {
|
|
1705
|
+
this.urgentStatus = evidence.rapidCandidate;
|
|
1706
|
+
this.urgentStartedAtMs = sampledAtMs;
|
|
1707
|
+
}
|
|
1708
|
+
} else if (this.urgentStatus !== null && this.urgentStartedAtMs !== null && sampledAtMs - this.urgentStartedAtMs >= this.tuning.rapidMinimumDisplayMs) {
|
|
1709
|
+
this.urgentStatus = null;
|
|
1710
|
+
this.urgentStartedAtMs = null;
|
|
1711
|
+
}
|
|
1712
|
+
if (evidence.normalCandidate !== null) {
|
|
1713
|
+
this.normalLongitudinalStatus = evidence.normalCandidate;
|
|
1714
|
+
this.longitudinalClearSinceMs = null;
|
|
1715
|
+
} else if (this.normalLongitudinalStatus !== null) if (evidence.retainedNormal) this.longitudinalClearSinceMs = null;
|
|
1716
|
+
else {
|
|
1717
|
+
if (this.longitudinalClearSinceMs === null) this.longitudinalClearSinceMs = sampledAtMs;
|
|
1718
|
+
if (sampledAtMs - this.longitudinalClearSinceMs >= this.tuning.longitudinalReleaseMs) {
|
|
1719
|
+
this.normalLongitudinalStatus = null;
|
|
1720
|
+
this.longitudinalClearSinceMs = null;
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1564
1723
|
}
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
if (!this.motionWindow.some((existing) => existing.sampledAtMs === sample.sampledAtMs)) {
|
|
1572
|
-
this.motionWindow.push(sample);
|
|
1573
|
-
this.motionWindow.sort((left, right) => left.sampledAtMs - right.sampledAtMs);
|
|
1724
|
+
updateTurnDetector(evidence) {
|
|
1725
|
+
if (this.turnStatus === null) {
|
|
1726
|
+
if (evidence.candidate !== null) this.turnStatus = evidence.candidate;
|
|
1727
|
+
this.turnQuietSinceMs = null;
|
|
1728
|
+
this.oppositeTurnSinceMs = null;
|
|
1729
|
+
return;
|
|
1574
1730
|
}
|
|
1575
|
-
const
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1731
|
+
const direction = evidence.latestYaw > 0 ? "LEFT_TURN" : "RIGHT_TURN";
|
|
1732
|
+
if (!(Math.abs(evidence.latestYaw) >= this.tuning.turnExitYawThreshold)) {
|
|
1733
|
+
this.oppositeTurnSinceMs = null;
|
|
1734
|
+
if (this.turnQuietSinceMs === null) this.turnQuietSinceMs = evidence.sampledAtMs;
|
|
1735
|
+
if (evidence.sampledAtMs - this.turnQuietSinceMs >= this.tuning.turnExitConfirmationMs) {
|
|
1736
|
+
this.turnStatus = null;
|
|
1737
|
+
this.turnQuietSinceMs = null;
|
|
1738
|
+
this.yawWindow.length = 0;
|
|
1739
|
+
}
|
|
1740
|
+
return;
|
|
1583
1741
|
}
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1742
|
+
this.turnQuietSinceMs = null;
|
|
1743
|
+
if (direction === this.turnStatus) {
|
|
1744
|
+
this.oppositeTurnSinceMs = null;
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1747
|
+
if (this.oppositeTurnSinceMs === null) this.oppositeTurnSinceMs = evidence.sampledAtMs;
|
|
1748
|
+
if (evidence.sampledAtMs - this.oppositeTurnSinceMs >= this.tuning.turnClosureWindowMs) {
|
|
1749
|
+
this.turnStatus = direction;
|
|
1750
|
+
this.oppositeTurnSinceMs = null;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
updateBias(metrics) {
|
|
1754
|
+
const sampledAtMs = Math.min(metrics.linearAccelerationSampledAtMs, metrics.gyroscopeSampledAtMs);
|
|
1755
|
+
if (sampledAtMs === this.lastBiasSampledAtMs) return;
|
|
1756
|
+
if (this.lastBiasSampledAtMs !== null && sampledAtMs < this.lastBiasSampledAtMs) return;
|
|
1757
|
+
if (!this.biasCalibrated || this.lastBiasSampledAtMs === null) {
|
|
1758
|
+
this.linearAccelerationBias = { ...metrics.rawLinearAcceleration };
|
|
1759
|
+
this.gyroscopeBias = { ...metrics.rawGyroscope };
|
|
1760
|
+
this.biasCalibrated = true;
|
|
1761
|
+
} else {
|
|
1762
|
+
const elapsedMs = Math.max(0, sampledAtMs - this.lastBiasSampledAtMs);
|
|
1763
|
+
const alpha = 1 - Math.exp(-elapsedMs / this.tuning.biasFilterTimeConstantMs);
|
|
1764
|
+
this.linearAccelerationBias = lerpVector(this.linearAccelerationBias, metrics.rawLinearAcceleration, alpha);
|
|
1765
|
+
this.gyroscopeBias = lerpVector(this.gyroscopeBias, metrics.rawGyroscope, alpha);
|
|
1766
|
+
}
|
|
1767
|
+
this.lastBiasSampledAtMs = sampledAtMs;
|
|
1768
|
+
}
|
|
1769
|
+
commitSelectedStatus(now) {
|
|
1770
|
+
const selected = this.urgentStatus ?? this.turnStatus ?? this.normalLongitudinalStatus ?? this.baseStatus;
|
|
1771
|
+
this.commit(selected, now, "candidate_confirmed");
|
|
1590
1772
|
}
|
|
1591
1773
|
processUnavailable(category) {
|
|
1592
1774
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processUnavailable", "called", { category });
|
|
1593
1775
|
const now = this.clock.now();
|
|
1594
1776
|
if (this.unavailableSinceMs === null) this.unavailableSinceMs = now;
|
|
1595
1777
|
const unavailableForMs = Math.max(0, now - this.unavailableSinceMs);
|
|
1596
|
-
this.
|
|
1597
|
-
this.motionWindow.length = 0;
|
|
1778
|
+
this.clearRecognitionWindows();
|
|
1598
1779
|
this.setCandidate(null, now);
|
|
1599
1780
|
this.emitDiagnostic({
|
|
1600
1781
|
type: "sample_unavailable",
|
|
@@ -1604,97 +1785,39 @@ var DefaultDrivingStatusController = class {
|
|
|
1604
1785
|
tuningVersion: this.tuning.version
|
|
1605
1786
|
});
|
|
1606
1787
|
if (unavailableForMs < this.unavailableFallbackMs || this.fallbackApplied) {
|
|
1607
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processUnavailable", "
|
|
1788
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processUnavailable", "holding previous status", {
|
|
1789
|
+
category,
|
|
1608
1790
|
unavailableForMs,
|
|
1609
1791
|
fallbackApplied: this.fallbackApplied
|
|
1610
1792
|
});
|
|
1611
1793
|
return;
|
|
1612
1794
|
}
|
|
1613
1795
|
this.fallbackApplied = true;
|
|
1796
|
+
this.baseStatus = "STOPPED";
|
|
1614
1797
|
this.emitDiagnostic({
|
|
1615
1798
|
type: "fallback_to_stopped",
|
|
1616
1799
|
atMs: now,
|
|
1617
1800
|
unavailableForMs,
|
|
1618
1801
|
tuningVersion: this.tuning.version
|
|
1619
1802
|
});
|
|
1620
|
-
this.commit("STOPPED", now, "unavailable_fallback"
|
|
1621
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processUnavailable", "returned
|
|
1803
|
+
this.commit("STOPPED", now, "unavailable_fallback");
|
|
1804
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processUnavailable", "returned fallback", {
|
|
1622
1805
|
unavailableForMs,
|
|
1623
|
-
|
|
1806
|
+
status: this.status
|
|
1624
1807
|
});
|
|
1625
1808
|
}
|
|
1626
|
-
|
|
1627
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processCandidate", "called", {
|
|
1628
|
-
nextCandidate,
|
|
1629
|
-
now,
|
|
1630
|
-
currentStatus: this.status,
|
|
1631
|
-
candidate: this.candidate,
|
|
1632
|
-
candidateConfirmationCount: this.candidateConfirmationCount
|
|
1633
|
-
});
|
|
1634
|
-
if (nextCandidate === this.status) {
|
|
1635
|
-
this.setCandidate(null, now);
|
|
1636
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processCandidate", "returned current status");
|
|
1637
|
-
return;
|
|
1638
|
-
}
|
|
1639
|
-
if (nextCandidate !== this.candidate) {
|
|
1640
|
-
this.setCandidate(nextCandidate, now);
|
|
1641
|
-
this.candidateConfirmationCount = 1;
|
|
1642
|
-
this.heldCandidate = null;
|
|
1643
|
-
} else this.candidateConfirmationCount += 1;
|
|
1644
|
-
if (this.candidateConfirmationCount < this.tuning.confirmationSamples[nextCandidate]) {
|
|
1645
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processCandidate", "awaiting confirmation", {
|
|
1646
|
-
nextCandidate,
|
|
1647
|
-
candidateConfirmationCount: this.candidateConfirmationCount,
|
|
1648
|
-
required: this.tuning.confirmationSamples[nextCandidate]
|
|
1649
|
-
});
|
|
1650
|
-
return;
|
|
1651
|
-
}
|
|
1652
|
-
const elapsedMs = Math.max(0, now - this.statusCommittedAtMs);
|
|
1653
|
-
const remainingMs = Math.max(0, this.tuning.minimumDisplayMs[this.status] - elapsedMs);
|
|
1654
|
-
const canPreempt = CAR_RUNNING_STATUS_PRIORITY[nextCandidate] > CAR_RUNNING_STATUS_PRIORITY[this.status];
|
|
1655
|
-
if (remainingMs > 0 && !canPreempt) {
|
|
1656
|
-
if (this.heldCandidate !== nextCandidate) {
|
|
1657
|
-
this.heldCandidate = nextCandidate;
|
|
1658
|
-
this.emitDiagnostic({
|
|
1659
|
-
type: "status_held_by_min_duration",
|
|
1660
|
-
atMs: now,
|
|
1661
|
-
status: this.status,
|
|
1662
|
-
candidate: nextCandidate,
|
|
1663
|
-
remainingMs,
|
|
1664
|
-
tuningVersion: this.tuning.version
|
|
1665
|
-
});
|
|
1666
|
-
}
|
|
1667
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processCandidate", "held by minimum duration", {
|
|
1668
|
-
nextCandidate,
|
|
1669
|
-
remainingMs
|
|
1670
|
-
});
|
|
1671
|
-
return;
|
|
1672
|
-
}
|
|
1673
|
-
this.commit(nextCandidate, now, "candidate_confirmed");
|
|
1674
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processCandidate", "returned after commit", { snapshot: this.getSnapshot() });
|
|
1675
|
-
}
|
|
1676
|
-
commit(nextStatus, now, reason, force = false) {
|
|
1677
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.commit", "called", {
|
|
1678
|
-
nextStatus,
|
|
1679
|
-
now,
|
|
1680
|
-
reason,
|
|
1681
|
-
force,
|
|
1682
|
-
currentStatus: this.status
|
|
1683
|
-
});
|
|
1684
|
-
if (!force && nextStatus === this.status) {
|
|
1685
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.commit", "ignored unchanged status");
|
|
1686
|
-
return;
|
|
1687
|
-
}
|
|
1809
|
+
commit(nextStatus, now, reason) {
|
|
1688
1810
|
if (nextStatus === this.status) {
|
|
1689
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.commit", "ignored
|
|
1811
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.commit", "ignored unchanged status", {
|
|
1812
|
+
status: this.status,
|
|
1813
|
+
reason
|
|
1814
|
+
});
|
|
1690
1815
|
return;
|
|
1691
1816
|
}
|
|
1692
1817
|
const previousStatus = this.status;
|
|
1693
1818
|
this.status = nextStatus;
|
|
1694
|
-
if (isBaseStatus(nextStatus)) this.baseStatus = nextStatus;
|
|
1695
1819
|
this.statusCommittedAtMs = now;
|
|
1696
1820
|
this.setCandidate(null, now);
|
|
1697
|
-
this.heldCandidate = null;
|
|
1698
1821
|
const event = {
|
|
1699
1822
|
type: "status_changed",
|
|
1700
1823
|
status: nextStatus,
|
|
@@ -1711,53 +1834,49 @@ var DefaultDrivingStatusController = class {
|
|
|
1711
1834
|
reason,
|
|
1712
1835
|
tuningVersion: this.tuning.version
|
|
1713
1836
|
});
|
|
1714
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.commit", "
|
|
1715
|
-
event,
|
|
1716
|
-
listenerCount: this.listeners.size,
|
|
1717
|
-
snapshot: this.getSnapshot()
|
|
1718
|
-
});
|
|
1837
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.commit", "status changed", { event });
|
|
1719
1838
|
}
|
|
1720
|
-
setCandidate(candidate,
|
|
1721
|
-
|
|
1722
|
-
candidate,
|
|
1723
|
-
now,
|
|
1724
|
-
currentCandidate: this.candidate
|
|
1725
|
-
});
|
|
1726
|
-
if (candidate === this.candidate) {
|
|
1727
|
-
if (candidate === null) this.candidateConfirmationCount = 0;
|
|
1728
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.setCandidate", "returned unchanged", { candidateConfirmationCount: this.candidateConfirmationCount });
|
|
1729
|
-
return;
|
|
1730
|
-
}
|
|
1839
|
+
setCandidate(candidate, atMs) {
|
|
1840
|
+
if (candidate === this.candidate) return;
|
|
1731
1841
|
const previousCandidate = this.candidate;
|
|
1732
1842
|
this.candidate = candidate;
|
|
1733
|
-
this.
|
|
1734
|
-
this.heldCandidate = null;
|
|
1843
|
+
this.candidateSinceMs = candidate === null ? null : atMs;
|
|
1735
1844
|
this.emitDiagnostic({
|
|
1736
1845
|
type: "candidate_changed",
|
|
1737
|
-
atMs: now,
|
|
1846
|
+
atMs: this.clock.now(),
|
|
1738
1847
|
candidate,
|
|
1739
1848
|
previousCandidate,
|
|
1740
1849
|
tuningVersion: this.tuning.version
|
|
1741
1850
|
});
|
|
1742
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.setCandidate", "
|
|
1851
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.setCandidate", "candidate changed", {
|
|
1743
1852
|
candidate,
|
|
1744
|
-
previousCandidate
|
|
1853
|
+
previousCandidate,
|
|
1854
|
+
candidateSinceMs: this.candidateSinceMs
|
|
1745
1855
|
});
|
|
1746
1856
|
}
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
motionWindow: this.motionWindow,
|
|
1751
|
-
candidate: this.candidate
|
|
1752
|
-
});
|
|
1753
|
-
this.metricWindow.length = 0;
|
|
1857
|
+
clearRecognitionWindows() {
|
|
1858
|
+
this.longitudinalWindow.length = 0;
|
|
1859
|
+
this.yawWindow.length = 0;
|
|
1754
1860
|
this.motionWindow.length = 0;
|
|
1861
|
+
this.stoppedConditionSinceMs = null;
|
|
1862
|
+
this.drivingConditionSinceMs = null;
|
|
1863
|
+
this.stationaryConfirmed = false;
|
|
1864
|
+
this.urgentStatus = null;
|
|
1865
|
+
this.urgentStartedAtMs = null;
|
|
1866
|
+
this.normalLongitudinalStatus = null;
|
|
1867
|
+
this.longitudinalClearSinceMs = null;
|
|
1868
|
+
this.turnStatus = null;
|
|
1869
|
+
this.turnQuietSinceMs = null;
|
|
1870
|
+
this.oppositeTurnSinceMs = null;
|
|
1871
|
+
}
|
|
1872
|
+
resetTransientRecognition() {
|
|
1873
|
+
this.clearRecognitionWindows();
|
|
1755
1874
|
this.candidate = null;
|
|
1756
|
-
this.
|
|
1875
|
+
this.candidateSinceMs = null;
|
|
1757
1876
|
this.unavailableSinceMs = null;
|
|
1758
1877
|
this.fallbackApplied = false;
|
|
1759
|
-
this.
|
|
1760
|
-
|
|
1878
|
+
this.filteredGravity = null;
|
|
1879
|
+
this.lastGravitySampledAtMs = null;
|
|
1761
1880
|
}
|
|
1762
1881
|
emitDiagnostic(event) {
|
|
1763
1882
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.emitDiagnostic", "called", {
|
|
@@ -1765,315 +1884,197 @@ var DefaultDrivingStatusController = class {
|
|
|
1765
1884
|
hasListener: this.onDiagnostic !== void 0
|
|
1766
1885
|
});
|
|
1767
1886
|
if (this.onDiagnostic) safeCall(this.onDiagnostic, event);
|
|
1768
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.emitDiagnostic", "returned", { event });
|
|
1769
1887
|
}
|
|
1770
1888
|
attachLifecycleListeners() {
|
|
1771
|
-
|
|
1772
|
-
managePageLifecycle: this.managePageLifecycle,
|
|
1773
|
-
attached: this.unsubscribePageLifecycle !== null
|
|
1774
|
-
});
|
|
1775
|
-
if (!this.managePageLifecycle || this.unsubscribePageLifecycle !== null) {
|
|
1776
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.attachLifecycleListeners", "ignored");
|
|
1777
|
-
return;
|
|
1778
|
-
}
|
|
1889
|
+
if (!this.managePageLifecycle || this.unsubscribePageLifecycle !== null) return;
|
|
1779
1890
|
this.unsubscribePageLifecycle = subscribeDrivingPageLifecycle(this.handleLifecyclePause, this.handleLifecycleResume);
|
|
1780
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.attachLifecycleListeners", "returned");
|
|
1781
1891
|
}
|
|
1782
1892
|
detachLifecycleListeners() {
|
|
1783
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.detachLifecycleListeners", "called", { attached: this.unsubscribePageLifecycle !== null });
|
|
1784
1893
|
this.unsubscribePageLifecycle?.();
|
|
1785
1894
|
this.unsubscribePageLifecycle = null;
|
|
1786
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.detachLifecycleListeners", "returned");
|
|
1787
1895
|
}
|
|
1788
1896
|
};
|
|
1789
|
-
const
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1897
|
+
const detectLongitudinalEvidence = (window, tuning) => {
|
|
1898
|
+
const rapidWindow = recentMetrics(window, tuning.rapidLongitudinalWindowMs, "linearAccelerationSampledAtMs");
|
|
1899
|
+
const normalWindow = recentMetrics(window, tuning.longitudinalWindowMs, "linearAccelerationSampledAtMs");
|
|
1900
|
+
let rapidCandidate = null;
|
|
1901
|
+
let rejectionReason = null;
|
|
1902
|
+
if (windowCovers(rapidWindow, tuning.rapidLongitudinalMinimumSamples, tuning.rapidLongitudinalWindowMs, "linearAccelerationSampledAtMs")) {
|
|
1903
|
+
const values = rapidWindow.map((sample) => sample.longitudinal);
|
|
1904
|
+
const direction = Math.sign(trimmedMean(values));
|
|
1905
|
+
const coherence = signCoherence(values, direction, tuning.longitudinalExitThreshold);
|
|
1906
|
+
const peak = Math.max(...values.map(Math.abs));
|
|
1907
|
+
const mean = Math.abs(trimmedMean(values));
|
|
1908
|
+
const longitudinalRms = rootMeanSquare(values);
|
|
1909
|
+
const verticalRms = rootMeanSquare(rapidWindow.map((sample) => sample.vertical));
|
|
1910
|
+
const reversalWindow = recentMetrics(window, tuning.longitudinalReversalWindowMs, "linearAccelerationSampledAtMs");
|
|
1911
|
+
const hasReversal = containsBothSigns(reversalWindow.map((sample) => sample.longitudinal), tuning.longitudinalThreshold);
|
|
1912
|
+
if (peak >= tuning.rapidLongitudinalPeakThreshold && mean >= tuning.rapidLongitudinalMeanThreshold) {
|
|
1913
|
+
if (verticalRms > longitudinalRms * tuning.verticalBumpRatio) rejectionReason = "vertical_bump";
|
|
1914
|
+
else if (hasReversal) rejectionReason = "longitudinal_reversal";
|
|
1915
|
+
else if (direction !== 0 && coherence >= tuning.rapidLongitudinalSignCoherence) rapidCandidate = direction > 0 ? "RAPID_ACCELERATION" : "SUDDEN_BRAKING";
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
let normalCandidate = null;
|
|
1919
|
+
let retainedNormal = false;
|
|
1920
|
+
if (normalWindow.length > 0) {
|
|
1921
|
+
const values = normalWindow.map((sample) => sample.longitudinal);
|
|
1922
|
+
const mean = trimmedMean(values);
|
|
1923
|
+
const direction = Math.sign(mean);
|
|
1924
|
+
retainedNormal = Math.abs(mean) >= tuning.longitudinalExitThreshold;
|
|
1925
|
+
if (windowCovers(normalWindow, tuning.longitudinalMinimumSamples, tuning.longitudinalWindowMs, "linearAccelerationSampledAtMs") && Math.abs(mean) >= tuning.longitudinalThreshold && signCoherence(values, direction, tuning.longitudinalExitThreshold) >= tuning.longitudinalSignCoherence) normalCandidate = direction > 0 ? "ACCELERATION" : "BRAKING";
|
|
1926
|
+
}
|
|
1927
|
+
return {
|
|
1928
|
+
rapidCandidate,
|
|
1929
|
+
normalCandidate,
|
|
1930
|
+
retainedNormal,
|
|
1931
|
+
rejectionReason
|
|
1932
|
+
};
|
|
1807
1933
|
};
|
|
1808
|
-
const
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
}
|
|
1829
|
-
if (Math.abs(smoothed.lateral) >= tuning.turnLateralThreshold) {
|
|
1830
|
-
const result = smoothed.lateral < 0 ? "LEFT_TURN" : "RIGHT_TURN";
|
|
1831
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "classifyEntryCandidate", "returned", { result });
|
|
1832
|
-
return result;
|
|
1833
|
-
}
|
|
1834
|
-
if (smoothed.longitudinal <= -tuning.longitudinalThreshold) {
|
|
1835
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "classifyEntryCandidate", "returned", { result: "BRAKING" });
|
|
1836
|
-
return "BRAKING";
|
|
1837
|
-
}
|
|
1838
|
-
if (smoothed.longitudinal >= tuning.longitudinalThreshold) {
|
|
1839
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "classifyEntryCandidate", "returned", { result: "ACCELERATION" });
|
|
1840
|
-
return "ACCELERATION";
|
|
1841
|
-
}
|
|
1842
|
-
if (motionIntensity === null) {
|
|
1843
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "classifyEntryCandidate", "returned base status", { result: baseStatus });
|
|
1844
|
-
return baseStatus;
|
|
1845
|
-
}
|
|
1846
|
-
if (motionIntensity > tuning.drivingMotionThreshold || smoothed.gyroMagnitude > tuning.stoppedGyroThreshold) {
|
|
1847
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "classifyEntryCandidate", "returned", { result: "STEADY_DRIVING" });
|
|
1848
|
-
return "STEADY_DRIVING";
|
|
1849
|
-
}
|
|
1850
|
-
if (motionIntensity < tuning.stoppedMotionThreshold) {
|
|
1851
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "classifyEntryCandidate", "returned", { result: "STOPPED" });
|
|
1852
|
-
return "STOPPED";
|
|
1853
|
-
}
|
|
1854
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "classifyEntryCandidate", "returned base status", { result: baseStatus });
|
|
1855
|
-
return baseStatus;
|
|
1934
|
+
const detectTurnEvidence = (window, tuning) => {
|
|
1935
|
+
const latest = window[window.length - 1];
|
|
1936
|
+
if (!latest) return {
|
|
1937
|
+
candidate: null,
|
|
1938
|
+
latestYaw: 0,
|
|
1939
|
+
sampledAtMs: 0
|
|
1940
|
+
};
|
|
1941
|
+
const strongWindow = recentMetrics(window, tuning.turnConfirmationMs, "gyroscopeSampledAtMs");
|
|
1942
|
+
const strongMean = trimmedMean(strongWindow.map((sample) => sample.yaw));
|
|
1943
|
+
const strongDirection = Math.sign(strongMean);
|
|
1944
|
+
const strong = windowCovers(strongWindow, tuning.turnMinimumSamples, tuning.turnConfirmationMs, "gyroscopeSampledAtMs") && Math.abs(strongMean) >= tuning.turnYawThreshold && signCoherence(strongWindow.map((sample) => sample.yaw), strongDirection, tuning.turnExitYawThreshold) >= tuning.turnSignCoherence;
|
|
1945
|
+
const signedAngle = integrateYaw(window);
|
|
1946
|
+
const gentleDirection = Math.sign(signedAngle);
|
|
1947
|
+
const gentle = window.length >= tuning.turnMinimumSamples && Math.abs(signedAngle) >= tuning.turnIntegratedAngleThreshold && signCoherence(window.map((sample) => sample.yaw), gentleDirection, tuning.turnExitYawThreshold) >= tuning.turnSignCoherence;
|
|
1948
|
+
const direction = strong ? strongDirection : gentle ? gentleDirection : 0;
|
|
1949
|
+
return {
|
|
1950
|
+
candidate: direction === 0 ? null : direction > 0 ? "LEFT_TURN" : "RIGHT_TURN",
|
|
1951
|
+
latestYaw: latest.yaw,
|
|
1952
|
+
sampledAtMs: latest.gyroscopeSampledAtMs
|
|
1953
|
+
};
|
|
1856
1954
|
};
|
|
1857
|
-
const
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
const
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1955
|
+
const instantaneousCandidate = (metrics, tuning) => {
|
|
1956
|
+
const longitudinalMagnitude = Math.abs(metrics.longitudinal);
|
|
1957
|
+
if (longitudinalMagnitude >= tuning.rapidLongitudinalPeakThreshold && Math.abs(metrics.vertical) <= longitudinalMagnitude * tuning.verticalBumpRatio) return metrics.longitudinal > 0 ? "RAPID_ACCELERATION" : "SUDDEN_BRAKING";
|
|
1958
|
+
if (Math.abs(metrics.yaw) >= tuning.turnYawThreshold) return metrics.yaw > 0 ? "LEFT_TURN" : "RIGHT_TURN";
|
|
1959
|
+
if (longitudinalMagnitude >= tuning.longitudinalThreshold) return metrics.longitudinal > 0 ? "ACCELERATION" : "BRAKING";
|
|
1960
|
+
return null;
|
|
1961
|
+
};
|
|
1962
|
+
const recentMetrics = (values, windowMs, timestampKey) => {
|
|
1963
|
+
const latestAtMs = values[values.length - 1]?.[timestampKey] ?? 0;
|
|
1964
|
+
return values.filter((value) => value[timestampKey] >= latestAtMs - windowMs);
|
|
1965
|
+
};
|
|
1966
|
+
const windowCovers = (values, minimumSamples, durationMs, timestampKey) => {
|
|
1967
|
+
if (values.length < minimumSamples) return false;
|
|
1968
|
+
const first = values[0];
|
|
1969
|
+
return values[values.length - 1][timestampKey] - first[timestampKey] >= durationMs;
|
|
1970
|
+
};
|
|
1971
|
+
const integrateYaw = (values) => {
|
|
1972
|
+
let angle = 0;
|
|
1973
|
+
for (let index = 1; index < values.length; index += 1) {
|
|
1974
|
+
const previous = values[index - 1];
|
|
1975
|
+
const current = values[index];
|
|
1976
|
+
const elapsedSeconds = Math.max(0, current.gyroscopeSampledAtMs - previous.gyroscopeSampledAtMs) / 1e3;
|
|
1977
|
+
angle += (previous.yaw + current.yaw) / 2 * elapsedSeconds;
|
|
1978
|
+
}
|
|
1979
|
+
return angle;
|
|
1980
|
+
};
|
|
1981
|
+
const trimmedMean = (values) => {
|
|
1982
|
+
if (values.length === 0) return 0;
|
|
1983
|
+
if (values.length < 4) return values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
1984
|
+
const sorted = [...values].sort((left, right) => left - right).slice(1, -1);
|
|
1985
|
+
return sorted.reduce((sum, value) => sum + value, 0) / sorted.length;
|
|
1986
|
+
};
|
|
1987
|
+
const signCoherence = (values, direction, deadband) => {
|
|
1988
|
+
if (values.length === 0 || direction === 0) return 0;
|
|
1989
|
+
return values.filter((value) => Math.abs(value) >= deadband && Math.sign(value) === direction).length / values.length;
|
|
1990
|
+
};
|
|
1991
|
+
const containsBothSigns = (values, threshold) => values.some((value) => value >= threshold) && values.some((value) => value <= -threshold);
|
|
1992
|
+
const rootMeanSquare = (values) => values.length === 0 ? 0 : Math.sqrt(values.reduce((sum, value) => sum + value ** 2, 0) / values.length);
|
|
1993
|
+
const calculateCombinedAxisMad = (samples, minimumSamples) => {
|
|
1994
|
+
if (samples.length < minimumSamples) return null;
|
|
1995
|
+
const medianX = median(samples.map((sample) => sample.value.x));
|
|
1996
|
+
const medianY = median(samples.map((sample) => sample.value.y));
|
|
1997
|
+
const medianZ = median(samples.map((sample) => sample.value.z));
|
|
1998
|
+
const madX = median(samples.map((sample) => Math.abs(sample.value.x - medianX)));
|
|
1999
|
+
const madY = median(samples.map((sample) => Math.abs(sample.value.y - medianY)));
|
|
2000
|
+
const madZ = median(samples.map((sample) => Math.abs(sample.value.z - medianZ)));
|
|
2001
|
+
return Math.hypot(madX, madY, madZ);
|
|
2002
|
+
};
|
|
2003
|
+
const median = (values) => {
|
|
2004
|
+
if (values.length === 0) return 0;
|
|
2005
|
+
const sorted = [...values].sort((left, right) => left - right);
|
|
2006
|
+
const middle = Math.floor(sorted.length / 2);
|
|
2007
|
+
return sorted.length % 2 === 0 ? (sorted[middle - 1] + sorted[middle]) / 2 : sorted[middle];
|
|
2008
|
+
};
|
|
2009
|
+
const pushUniqueMetric = (window, metrics, timestampKey, windowMs) => {
|
|
2010
|
+
const latest = window[window.length - 1];
|
|
2011
|
+
if (latest && metrics[timestampKey] <= latest[timestampKey]) return false;
|
|
2012
|
+
if (window.some((value) => value[timestampKey] === metrics[timestampKey])) return false;
|
|
2013
|
+
window.push(metrics);
|
|
2014
|
+
const cutoffMs = metrics[timestampKey] - windowMs;
|
|
2015
|
+
while (window[0] && window[0][timestampKey] < cutoffMs) window.shift();
|
|
2016
|
+
return true;
|
|
1895
2017
|
};
|
|
1896
|
-
const
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
gyroMagnitude: 0
|
|
1905
|
-
};
|
|
1906
|
-
for (const value of metrics) {
|
|
1907
|
-
result.longitudinal += value.longitudinal;
|
|
1908
|
-
result.lateral += value.lateral;
|
|
1909
|
-
result.yaw += value.yaw;
|
|
1910
|
-
result.motionIntensity += value.motionIntensity;
|
|
1911
|
-
result.gyroMagnitude += value.gyroMagnitude;
|
|
1912
|
-
}
|
|
1913
|
-
const count = Math.max(1, metrics.length);
|
|
1914
|
-
result.longitudinal /= count;
|
|
1915
|
-
result.lateral /= count;
|
|
1916
|
-
result.yaw /= count;
|
|
1917
|
-
result.motionIntensity /= count;
|
|
1918
|
-
result.gyroMagnitude /= count;
|
|
1919
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "averageMetrics", "returned", { result });
|
|
1920
|
-
return result;
|
|
2018
|
+
const pushUniqueVector = (window, sample, windowMs) => {
|
|
2019
|
+
const latest = window[window.length - 1];
|
|
2020
|
+
if (latest && sample.sampledAtMs <= latest.sampledAtMs) return false;
|
|
2021
|
+
if (window.some((value) => value.sampledAtMs === sample.sampledAtMs)) return false;
|
|
2022
|
+
window.push(sample);
|
|
2023
|
+
const cutoffMs = sample.sampledAtMs - windowMs;
|
|
2024
|
+
while (window[0] && window[0].sampledAtMs < cutoffMs) window.shift();
|
|
2025
|
+
return true;
|
|
1921
2026
|
};
|
|
1922
|
-
const
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
});
|
|
1927
|
-
const metric = info?.accelerometer;
|
|
1928
|
-
if (!metric?.available || !Number.isFinite(info.capturedAtMs) || !Number.isFinite(metric.sampledAtMs)) {
|
|
1929
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "extractFreshAccelerometerSample", "returned", { result: null });
|
|
1930
|
-
return null;
|
|
1931
|
-
}
|
|
1932
|
-
const ageMs = info.capturedAtMs - metric.sampledAtMs;
|
|
1933
|
-
if (ageMs < 0 || ageMs > maxSampleAgeMs) {
|
|
1934
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "extractFreshAccelerometerSample", "returned stale sample", {
|
|
1935
|
-
result: null,
|
|
1936
|
-
ageMs
|
|
1937
|
-
});
|
|
1938
|
-
return null;
|
|
1939
|
-
}
|
|
2027
|
+
const extractFreshVectorMetric = (metric, capturedAtMs, maxSampleAgeMs) => {
|
|
2028
|
+
if (!metric?.available || !Number.isFinite(capturedAtMs) || !Number.isFinite(metric.sampledAtMs)) return null;
|
|
2029
|
+
const ageMs = capturedAtMs - metric.sampledAtMs;
|
|
2030
|
+
if (ageMs < 0 || ageMs > maxSampleAgeMs) return null;
|
|
1940
2031
|
const { x, y, z } = metric.value;
|
|
1941
|
-
|
|
2032
|
+
return [
|
|
1942
2033
|
x,
|
|
1943
2034
|
y,
|
|
1944
2035
|
z
|
|
1945
|
-
].every(Number.isFinite)
|
|
1946
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "extractFreshAccelerometerSample", "returned invalid vector", { result: null });
|
|
1947
|
-
return null;
|
|
1948
|
-
}
|
|
1949
|
-
const result = {
|
|
2036
|
+
].every(Number.isFinite) ? {
|
|
1950
2037
|
sampledAtMs: metric.sampledAtMs,
|
|
1951
2038
|
value: metric.value
|
|
1952
|
-
};
|
|
1953
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "extractFreshAccelerometerSample", "returned", { result });
|
|
1954
|
-
return result;
|
|
1955
|
-
};
|
|
1956
|
-
const combinedAxisStandardDeviation = (samples) => {
|
|
1957
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "combinedAxisStandardDeviation", "called", { samples });
|
|
1958
|
-
const count = samples.length;
|
|
1959
|
-
let meanX = 0;
|
|
1960
|
-
let meanY = 0;
|
|
1961
|
-
let meanZ = 0;
|
|
1962
|
-
for (const sample of samples) {
|
|
1963
|
-
meanX += sample.value.x;
|
|
1964
|
-
meanY += sample.value.y;
|
|
1965
|
-
meanZ += sample.value.z;
|
|
1966
|
-
}
|
|
1967
|
-
meanX /= count;
|
|
1968
|
-
meanY /= count;
|
|
1969
|
-
meanZ /= count;
|
|
1970
|
-
let varianceX = 0;
|
|
1971
|
-
let varianceY = 0;
|
|
1972
|
-
let varianceZ = 0;
|
|
1973
|
-
for (const sample of samples) {
|
|
1974
|
-
varianceX += (sample.value.x - meanX) ** 2;
|
|
1975
|
-
varianceY += (sample.value.y - meanY) ** 2;
|
|
1976
|
-
varianceZ += (sample.value.z - meanZ) ** 2;
|
|
1977
|
-
}
|
|
1978
|
-
const result = Math.sqrt((varianceX + varianceY + varianceZ) / count);
|
|
1979
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "combinedAxisStandardDeviation", "returned", {
|
|
1980
|
-
meanX,
|
|
1981
|
-
meanY,
|
|
1982
|
-
meanZ,
|
|
1983
|
-
varianceX,
|
|
1984
|
-
varianceY,
|
|
1985
|
-
varianceZ,
|
|
1986
|
-
result
|
|
1987
|
-
});
|
|
1988
|
-
return result;
|
|
2039
|
+
} : null;
|
|
1989
2040
|
};
|
|
2041
|
+
const observationTime = (metrics) => Math.max(metrics.linearAccelerationSampledAtMs, metrics.gyroscopeSampledAtMs);
|
|
2042
|
+
const lerpVector = (from, to, alpha) => ({
|
|
2043
|
+
x: from.x + (to.x - from.x) * alpha,
|
|
2044
|
+
y: from.y + (to.y - from.y) * alpha,
|
|
2045
|
+
z: from.z + (to.z - from.z) * alpha
|
|
2046
|
+
});
|
|
1990
2047
|
const defaultSensorProvider = () => {
|
|
1991
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "defaultSensorProvider", "called");
|
|
1992
2048
|
const runtimeGlobal = globalThis;
|
|
1993
|
-
if (typeof runtimeGlobal.unifiedSensorInfo !== "function")
|
|
1994
|
-
|
|
1995
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "defaultSensorProvider", "rejected", error);
|
|
1996
|
-
return Promise.reject(error);
|
|
1997
|
-
}
|
|
1998
|
-
try {
|
|
1999
|
-
const result = runtimeGlobal.unifiedSensorInfo();
|
|
2000
|
-
result.then((payload) => dialSdkDebugWarn(DEBUG_MODULE$1, "defaultSensorProvider", "resolved", { payload }), (error) => dialSdkDebugWarn(DEBUG_MODULE$1, "defaultSensorProvider", "rejected", error));
|
|
2001
|
-
return result;
|
|
2002
|
-
} catch (error) {
|
|
2003
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "defaultSensorProvider", "threw", error);
|
|
2004
|
-
throw error;
|
|
2005
|
-
}
|
|
2049
|
+
if (typeof runtimeGlobal.unifiedSensorInfo !== "function") return Promise.reject(/* @__PURE__ */ new Error("window.unifiedSensorInfo is unavailable"));
|
|
2050
|
+
return runtimeGlobal.unifiedSensorInfo();
|
|
2006
2051
|
};
|
|
2007
2052
|
const systemClock = {
|
|
2008
|
-
now: () =>
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
return result;
|
|
2012
|
-
},
|
|
2013
|
-
setTimeout: (callback, delayMs) => {
|
|
2014
|
-
const result = globalThis.setTimeout(callback, delayMs);
|
|
2015
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "systemClock.setTimeout", "returned", {
|
|
2016
|
-
delayMs,
|
|
2017
|
-
result
|
|
2018
|
-
});
|
|
2019
|
-
return result;
|
|
2020
|
-
},
|
|
2021
|
-
clearTimeout: (handle) => {
|
|
2022
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "systemClock.clearTimeout", "called", { handle });
|
|
2023
|
-
globalThis.clearTimeout(handle);
|
|
2024
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "systemClock.clearTimeout", "returned");
|
|
2025
|
-
}
|
|
2026
|
-
};
|
|
2027
|
-
const isOppositeTurn = (candidate, current) => {
|
|
2028
|
-
const result = candidate === "LEFT_TURN" && current === "RIGHT_TURN" || candidate === "RIGHT_TURN" && current === "LEFT_TURN";
|
|
2029
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "isOppositeTurn", "returned", {
|
|
2030
|
-
candidate,
|
|
2031
|
-
current,
|
|
2032
|
-
result
|
|
2033
|
-
});
|
|
2034
|
-
return result;
|
|
2035
|
-
};
|
|
2036
|
-
const isBaseStatus = (status) => {
|
|
2037
|
-
const result = status === "STOPPED" || status === "STEADY_DRIVING";
|
|
2038
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "isBaseStatus", "returned", {
|
|
2039
|
-
status,
|
|
2040
|
-
result
|
|
2041
|
-
});
|
|
2042
|
-
return result;
|
|
2043
|
-
};
|
|
2044
|
-
const isAbortError$1 = (error) => {
|
|
2045
|
-
const result = error instanceof Error && error.name === "AbortError";
|
|
2046
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "isAbortError", "returned", {
|
|
2047
|
-
error,
|
|
2048
|
-
result
|
|
2049
|
-
});
|
|
2050
|
-
return result;
|
|
2053
|
+
now: () => Date.now(),
|
|
2054
|
+
setTimeout: (callback, delayMs) => globalThis.setTimeout(callback, delayMs),
|
|
2055
|
+
clearTimeout: (handle) => globalThis.clearTimeout(handle)
|
|
2051
2056
|
};
|
|
2057
|
+
const isBaseStatus = (status) => status === "STOPPED" || status === "STEADY_DRIVING";
|
|
2058
|
+
const isAbortError$1 = (error) => error instanceof Error && error.name === "AbortError";
|
|
2052
2059
|
const requirePositiveFinite = (name, value) => {
|
|
2053
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "requirePositiveFinite", "called", {
|
|
2054
|
-
name,
|
|
2055
|
-
value
|
|
2056
|
-
});
|
|
2057
2060
|
if (!Number.isFinite(value) || value <= 0) {
|
|
2058
2061
|
const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
|
|
2059
2062
|
dialSdkDebugWarn(DEBUG_MODULE$1, "requirePositiveFinite", "threw", error);
|
|
2060
2063
|
throw error;
|
|
2061
2064
|
}
|
|
2062
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "requirePositiveFinite", "returned", { result: value });
|
|
2063
2065
|
return value;
|
|
2064
2066
|
};
|
|
2065
2067
|
const safeCall = (callback, value) => {
|
|
2066
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "called", { value });
|
|
2067
2068
|
try {
|
|
2068
2069
|
callback(value);
|
|
2069
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "returned");
|
|
2070
2070
|
} catch (error) {
|
|
2071
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "callback threw",
|
|
2071
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "callback threw", {
|
|
2072
|
+
error,
|
|
2073
|
+
value
|
|
2074
|
+
});
|
|
2072
2075
|
}
|
|
2073
2076
|
};
|
|
2074
|
-
const noop = () =>
|
|
2075
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "noop", "called and returned");
|
|
2076
|
-
};
|
|
2077
|
+
const noop = () => void 0;
|
|
2077
2078
|
//#endregion
|
|
2078
2079
|
//#region src/sensor/driving_expression_player.ts
|
|
2079
2080
|
const DEBUG_MODULE = "driving_expression_player.ts";
|
|
@@ -3353,4 +3354,4 @@ const isRecord = (value) => {
|
|
|
3353
3354
|
return result;
|
|
3354
3355
|
};
|
|
3355
3356
|
//#endregion
|
|
3356
|
-
export { BATTERY_HEALTH_LABELS, CAR_RUNNING_LABELS, CAR_RUNNING_STATUSES, CAR_RUNNING_STATUS_PRIORITY, DEFAULT_DRIVING_STATUS_TUNING, DrivingExpressionResolutionError, DrivingExpressionsConfigError, OFFICIAL_DRIVING_EXPRESSIONS_CONFIG, OFFICIAL_DRIVING_EXPRESSION_FALLBACKS,
|
|
3357
|
+
export { BATTERY_HEALTH_LABELS, CAR_RUNNING_LABELS, CAR_RUNNING_STATUSES, CAR_RUNNING_STATUS_PRIORITY, DEFAULT_DRIVING_STATUS_TUNING, DrivingExpressionResolutionError, DrivingExpressionsConfigError, OFFICIAL_DRIVING_EXPRESSIONS_CONFIG, OFFICIAL_DRIVING_EXPRESSION_FALLBACKS, buildVehicleFrame, classifyDrivingSnapshot, createDrivingExpressionPlayer, createDrivingStatusController, extractDrivingSnapshotMetrics, isPublicDrivingMediaUrl, parseDrivingExpressionsConfig, readDrivingExpressionsConfig, resolveDrivingExpression, resolveDrivingExpressionStrict, resolveDrivingStatusTuning, tryParseDrivingExpressionsConfig, unifiedSensorInfo, waitForDrivingExpressionsConfig };
|