@ziztechnology/dial-library 0.0.10 → 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 +664 -661
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -45,40 +45,55 @@ const dialSdkDebugWarn = (moduleName, scope, phase, data) => {
|
|
|
45
45
|
//#endregion
|
|
46
46
|
//#region src/sensor/check_status.ts
|
|
47
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
|
+
};
|
|
48
58
|
const DEFAULT_DRIVING_STATUS_TUNING = Object.freeze({
|
|
49
|
-
version: "driving-status-
|
|
59
|
+
version: "driving-status-calibrated-2026-07",
|
|
50
60
|
maxSampleAgeMs: 1e3,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
turnLateralThreshold: 2.5,
|
|
61
|
+
gravityFilterTimeConstantMs: 1e3,
|
|
62
|
+
biasFilterTimeConstantMs: 1e4,
|
|
63
|
+
vehicleRightMinimumNorm: .2,
|
|
55
64
|
motionWindowMs: 1e3,
|
|
56
65
|
motionMinimumSamples: 5,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
stoppedGyroThreshold: .
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
|
82
97
|
});
|
|
83
98
|
const classifyDrivingSnapshot = (info, tuningOverrides) => {
|
|
84
99
|
dialSdkDebugWarn(DEBUG_MODULE$5, "classifyDrivingSnapshot", "called", {
|
|
@@ -87,52 +102,91 @@ const classifyDrivingSnapshot = (info, tuningOverrides) => {
|
|
|
87
102
|
});
|
|
88
103
|
const tuning = resolveDrivingStatusTuning(tuningOverrides);
|
|
89
104
|
const metrics = extractDrivingSnapshotMetrics(info, tuning);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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,
|
|
93
112
|
metrics,
|
|
94
|
-
|
|
95
|
-
|
|
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 });
|
|
96
121
|
return result;
|
|
97
122
|
};
|
|
98
|
-
const checkRunningStatus = classifyDrivingSnapshot;
|
|
99
123
|
const resolveDrivingStatusTuning = (overrides = {}) => {
|
|
100
124
|
dialSdkDebugWarn(DEBUG_MODULE$5, "resolveDrivingStatusTuning", "called", { overrides });
|
|
101
125
|
const tuning = {
|
|
102
126
|
...DEFAULT_DRIVING_STATUS_TUNING,
|
|
103
|
-
...overrides
|
|
104
|
-
confirmationSamples: {
|
|
105
|
-
...DEFAULT_DRIVING_STATUS_TUNING.confirmationSamples,
|
|
106
|
-
...overrides.confirmationSamples
|
|
107
|
-
},
|
|
108
|
-
minimumDisplayMs: {
|
|
109
|
-
...DEFAULT_DRIVING_STATUS_TUNING.minimumDisplayMs,
|
|
110
|
-
...overrides.minimumDisplayMs
|
|
111
|
-
}
|
|
127
|
+
...overrides
|
|
112
128
|
};
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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");
|
|
129
182
|
dialSdkDebugWarn(DEBUG_MODULE$5, "resolveDrivingStatusTuning", "returned", { tuning });
|
|
130
183
|
return tuning;
|
|
131
184
|
};
|
|
132
|
-
const extractDrivingSnapshotMetrics = (info, tuning = DEFAULT_DRIVING_STATUS_TUNING) => {
|
|
185
|
+
const extractDrivingSnapshotMetrics = (info, tuning = DEFAULT_DRIVING_STATUS_TUNING, correction = {}) => {
|
|
133
186
|
dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "called", {
|
|
134
187
|
info,
|
|
135
|
-
tuning
|
|
188
|
+
tuning,
|
|
189
|
+
correction
|
|
136
190
|
});
|
|
137
191
|
if (!info || !Number.isFinite(info.capturedAtMs)) {
|
|
138
192
|
dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "returned invalid capture time", { info });
|
|
@@ -140,149 +194,148 @@ const extractDrivingSnapshotMetrics = (info, tuning = DEFAULT_DRIVING_STATUS_TUN
|
|
|
140
194
|
}
|
|
141
195
|
const linearAcceleration = availableFreshVector(info.linearAcceleration, info.capturedAtMs, tuning.maxSampleAgeMs);
|
|
142
196
|
const gyroscope = availableFreshVector(info.gyroscope, info.capturedAtMs, tuning.maxSampleAgeMs);
|
|
143
|
-
|
|
197
|
+
const gravityMetric = availableFreshVector(info.gravity, info.capturedAtMs, tuning.maxSampleAgeMs);
|
|
198
|
+
if (linearAcceleration === null || gyroscope === null || gravityMetric === null) {
|
|
144
199
|
dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "returned unavailable vectors", {
|
|
145
200
|
linearAcceleration,
|
|
146
|
-
gyroscope
|
|
201
|
+
gyroscope,
|
|
202
|
+
gravity: gravityMetric
|
|
147
203
|
});
|
|
148
204
|
return null;
|
|
149
205
|
}
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
+
});
|
|
156
213
|
return null;
|
|
157
214
|
}
|
|
215
|
+
const correctedLinear = subtract(linearAcceleration.value, correction.linearAccelerationBias ?? ZERO_VECTOR$1);
|
|
216
|
+
const correctedGyroscope = subtract(gyroscope.value, correction.gyroscopeBias ?? ZERO_VECTOR$1);
|
|
158
217
|
const result = {
|
|
159
218
|
capturedAtMs: info.capturedAtMs,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
|
165
231
|
};
|
|
166
232
|
dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "returned", { result });
|
|
167
233
|
return result;
|
|
168
234
|
};
|
|
169
|
-
const
|
|
170
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "
|
|
171
|
-
|
|
172
|
-
|
|
235
|
+
const buildVehicleFrame = (gravity, rightMinimumNorm = DEFAULT_DRIVING_STATUS_TUNING.vehicleRightMinimumNorm) => {
|
|
236
|
+
dialSdkDebugWarn(DEBUG_MODULE$5, "buildVehicleFrame", "called", {
|
|
237
|
+
gravity,
|
|
238
|
+
rightMinimumNorm
|
|
173
239
|
});
|
|
174
|
-
const
|
|
175
|
-
if (
|
|
176
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "
|
|
177
|
-
return
|
|
178
|
-
}
|
|
179
|
-
if (longitudinal >= tuning.rapidLongitudinalThreshold) {
|
|
180
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "classifyDrivingMetrics", "returned", { result: "RAPID_ACCELERATION" });
|
|
181
|
-
return "RAPID_ACCELERATION";
|
|
182
|
-
}
|
|
183
|
-
if (Math.abs(yaw) >= tuning.turnYawThreshold) {
|
|
184
|
-
const result = yaw > 0 ? "LEFT_TURN" : "RIGHT_TURN";
|
|
185
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "classifyDrivingMetrics", "returned", {
|
|
186
|
-
result,
|
|
187
|
-
basis: "yaw"
|
|
188
|
-
});
|
|
189
|
-
return result;
|
|
190
|
-
}
|
|
191
|
-
if (Math.abs(lateral) >= tuning.turnLateralThreshold) {
|
|
192
|
-
const result = lateral < 0 ? "LEFT_TURN" : "RIGHT_TURN";
|
|
193
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "classifyDrivingMetrics", "returned", {
|
|
194
|
-
result,
|
|
195
|
-
basis: "lateral"
|
|
196
|
-
});
|
|
197
|
-
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;
|
|
198
244
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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;
|
|
202
250
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
251
|
+
const right = normalize(projectedRight);
|
|
252
|
+
if (right === null) {
|
|
253
|
+
dialSdkDebugWarn(DEBUG_MODULE$5, "buildVehicleFrame", "returned invalid right axis", { result: null });
|
|
254
|
+
return null;
|
|
206
255
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
+
};
|
|
210
276
|
}
|
|
211
|
-
|
|
212
|
-
|
|
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
|
+
};
|
|
213
289
|
};
|
|
214
290
|
const availableFreshVector = (metric, capturedAtMs, maxSampleAgeMs) => {
|
|
215
|
-
|
|
216
|
-
metric,
|
|
217
|
-
capturedAtMs,
|
|
218
|
-
maxSampleAgeMs
|
|
219
|
-
});
|
|
220
|
-
if (!metric?.available || !isFresh(metric.sampledAtMs, capturedAtMs, maxSampleAgeMs)) {
|
|
221
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "availableFreshVector", "returned", { result: null });
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
291
|
+
if (!metric?.available || !isFresh(metric.sampledAtMs, capturedAtMs, maxSampleAgeMs)) return null;
|
|
224
292
|
const { x, y, z } = metric.value;
|
|
225
|
-
|
|
293
|
+
return [
|
|
226
294
|
x,
|
|
227
295
|
y,
|
|
228
296
|
z
|
|
229
|
-
].every(Number.isFinite) ?
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
};
|
|
233
|
-
const availableFreshNumber = (metric, capturedAtMs, maxSampleAgeMs) => {
|
|
234
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "availableFreshNumber", "called", {
|
|
235
|
-
metric,
|
|
236
|
-
capturedAtMs,
|
|
237
|
-
maxSampleAgeMs
|
|
238
|
-
});
|
|
239
|
-
if (!metric?.available || !isFresh(metric.sampledAtMs, capturedAtMs, maxSampleAgeMs)) {
|
|
240
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "availableFreshNumber", "returned", { result: null });
|
|
241
|
-
return null;
|
|
242
|
-
}
|
|
243
|
-
const result = Number.isFinite(metric.value) && metric.value >= 0 ? metric.value : null;
|
|
244
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "availableFreshNumber", "returned", { result });
|
|
245
|
-
return result;
|
|
297
|
+
].every(Number.isFinite) ? {
|
|
298
|
+
value: metric.value,
|
|
299
|
+
sampledAtMs: metric.sampledAtMs
|
|
300
|
+
} : null;
|
|
246
301
|
};
|
|
247
302
|
const isFresh = (sampledAtMs, capturedAtMs, maxSampleAgeMs) => {
|
|
248
|
-
|
|
249
|
-
sampledAtMs,
|
|
250
|
-
capturedAtMs,
|
|
251
|
-
maxSampleAgeMs
|
|
252
|
-
});
|
|
253
|
-
if (sampledAtMs === null || !Number.isFinite(sampledAtMs)) {
|
|
254
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "isFresh", "returned", { result: false });
|
|
255
|
-
return false;
|
|
256
|
-
}
|
|
303
|
+
if (sampledAtMs === null || !Number.isFinite(sampledAtMs)) return false;
|
|
257
304
|
const age = capturedAtMs - sampledAtMs;
|
|
258
|
-
|
|
259
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "isFresh", "returned", {
|
|
260
|
-
age,
|
|
261
|
-
result
|
|
262
|
-
});
|
|
263
|
-
return result;
|
|
305
|
+
return age >= 0 && age <= maxSampleAgeMs;
|
|
264
306
|
};
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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;
|
|
274
327
|
};
|
|
275
328
|
const assertPositiveFinite = (name, value) => {
|
|
276
|
-
dialSdkDebugWarn(DEBUG_MODULE$5, "assertPositiveFinite", "called", {
|
|
277
|
-
name,
|
|
278
|
-
value
|
|
279
|
-
});
|
|
280
329
|
if (!Number.isFinite(value) || value <= 0) {
|
|
281
330
|
const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
|
|
282
331
|
dialSdkDebugWarn(DEBUG_MODULE$5, "assertPositiveFinite", "threw", error);
|
|
283
332
|
throw error;
|
|
284
333
|
}
|
|
285
|
-
|
|
334
|
+
};
|
|
335
|
+
const throwLoggedRangeError = (scope, message) => {
|
|
336
|
+
const error = new RangeError(message);
|
|
337
|
+
dialSdkDebugWarn(DEBUG_MODULE$5, scope, "threw", error);
|
|
338
|
+
throw error;
|
|
286
339
|
};
|
|
287
340
|
//#endregion
|
|
288
341
|
//#region src/sensor/unified_info.ts
|
|
@@ -1272,6 +1325,11 @@ const safeInvoke = (callback) => {
|
|
|
1272
1325
|
//#endregion
|
|
1273
1326
|
//#region src/sensor/driving_status_controller.ts
|
|
1274
1327
|
const DEBUG_MODULE$1 = "driving_status_controller.ts";
|
|
1328
|
+
const ZERO_VECTOR = {
|
|
1329
|
+
x: 0,
|
|
1330
|
+
y: 0,
|
|
1331
|
+
z: 0
|
|
1332
|
+
};
|
|
1275
1333
|
const createDrivingStatusController = (options = {}) => {
|
|
1276
1334
|
dialSdkDebugWarn(DEBUG_MODULE$1, "createDrivingStatusController", "called", { options });
|
|
1277
1335
|
const result = new DefaultDrivingStatusController(options);
|
|
@@ -1281,14 +1339,30 @@ const createDrivingStatusController = (options = {}) => {
|
|
|
1281
1339
|
var DefaultDrivingStatusController = class {
|
|
1282
1340
|
constructor(options) {
|
|
1283
1341
|
this.listeners = /* @__PURE__ */ new Set();
|
|
1284
|
-
this.
|
|
1342
|
+
this.longitudinalWindow = [];
|
|
1343
|
+
this.yawWindow = [];
|
|
1285
1344
|
this.motionWindow = [];
|
|
1286
1345
|
this.candidate = null;
|
|
1287
|
-
this.
|
|
1346
|
+
this.candidateSinceMs = null;
|
|
1288
1347
|
this.lastAvailableAtMs = null;
|
|
1289
1348
|
this.unavailableSinceMs = null;
|
|
1290
1349
|
this.fallbackApplied = false;
|
|
1291
|
-
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;
|
|
1292
1366
|
this.requestedRunning = false;
|
|
1293
1367
|
this.polling = false;
|
|
1294
1368
|
this.lifecycleSuspended = false;
|
|
@@ -1297,12 +1371,12 @@ var DefaultDrivingStatusController = class {
|
|
|
1297
1371
|
this.pendingRead = null;
|
|
1298
1372
|
this.unsubscribePageLifecycle = null;
|
|
1299
1373
|
this.handleLifecyclePause = () => {
|
|
1300
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecyclePause", "called"
|
|
1301
|
-
requestedRunning: this.requestedRunning,
|
|
1302
|
-
lifecycleSuspended: this.lifecycleSuspended
|
|
1303
|
-
});
|
|
1374
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecyclePause", "called");
|
|
1304
1375
|
if (!this.requestedRunning || this.lifecycleSuspended) {
|
|
1305
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecyclePause", "ignored"
|
|
1376
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecyclePause", "ignored", {
|
|
1377
|
+
requestedRunning: this.requestedRunning,
|
|
1378
|
+
lifecycleSuspended: this.lifecycleSuspended
|
|
1379
|
+
});
|
|
1306
1380
|
return;
|
|
1307
1381
|
}
|
|
1308
1382
|
this.lifecycleSuspended = true;
|
|
@@ -1311,13 +1385,15 @@ var DefaultDrivingStatusController = class {
|
|
|
1311
1385
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecyclePause", "returned");
|
|
1312
1386
|
};
|
|
1313
1387
|
this.handleLifecycleResume = () => {
|
|
1314
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecycleResume", "called"
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
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
|
+
});
|
|
1321
1397
|
return;
|
|
1322
1398
|
}
|
|
1323
1399
|
this.lifecycleSuspended = false;
|
|
@@ -1333,18 +1409,15 @@ var DefaultDrivingStatusController = class {
|
|
|
1333
1409
|
this.onDiagnostic = options.onDiagnostic;
|
|
1334
1410
|
this.managePageLifecycle = options.managePageLifecycle ?? true;
|
|
1335
1411
|
const initialStatus = options.initialStatus ?? "STOPPED";
|
|
1336
|
-
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
|
+
}
|
|
1337
1417
|
this.status = initialStatus;
|
|
1338
1418
|
this.baseStatus = isBaseStatus(initialStatus) ? initialStatus : "STOPPED";
|
|
1339
1419
|
this.statusCommittedAtMs = this.clock.now();
|
|
1340
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.constructor", "created",
|
|
1341
|
-
initialStatus,
|
|
1342
|
-
baseStatus: this.baseStatus,
|
|
1343
|
-
pollIntervalMs: this.pollIntervalMs,
|
|
1344
|
-
unavailableFallbackMs: this.unavailableFallbackMs,
|
|
1345
|
-
tuning: this.tuning,
|
|
1346
|
-
managePageLifecycle: this.managePageLifecycle
|
|
1347
|
-
});
|
|
1420
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.constructor", "created", this.getSnapshot());
|
|
1348
1421
|
}
|
|
1349
1422
|
start() {
|
|
1350
1423
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.start", "called", this.getSnapshot());
|
|
@@ -1389,16 +1462,17 @@ var DefaultDrivingStatusController = class {
|
|
|
1389
1462
|
}
|
|
1390
1463
|
this.destroyed = true;
|
|
1391
1464
|
this.listeners.clear();
|
|
1392
|
-
this.
|
|
1393
|
-
this.motionWindow.length = 0;
|
|
1465
|
+
this.resetTransientRecognition();
|
|
1394
1466
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.destroy", "returned", this.getSnapshot());
|
|
1395
1467
|
}
|
|
1396
1468
|
getSnapshot() {
|
|
1397
1469
|
const result = {
|
|
1398
1470
|
lifecycle: this.destroyed ? "destroyed" : this.polling ? "running" : "stopped",
|
|
1399
1471
|
status: this.status,
|
|
1472
|
+
baseStatus: this.baseStatus,
|
|
1473
|
+
activeTurn: this.turnStatus,
|
|
1400
1474
|
candidate: this.candidate,
|
|
1401
|
-
|
|
1475
|
+
candidateSinceMs: this.candidateSinceMs,
|
|
1402
1476
|
statusCommittedAtMs: this.statusCommittedAtMs,
|
|
1403
1477
|
lastAvailableAtMs: this.lastAvailableAtMs,
|
|
1404
1478
|
unavailableSinceMs: this.unavailableSinceMs,
|
|
@@ -1409,16 +1483,16 @@ var DefaultDrivingStatusController = class {
|
|
|
1409
1483
|
}
|
|
1410
1484
|
subscribe(listener) {
|
|
1411
1485
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.subscribe", "called", {
|
|
1412
|
-
|
|
1413
|
-
|
|
1486
|
+
listener,
|
|
1487
|
+
destroyed: this.destroyed
|
|
1414
1488
|
});
|
|
1415
1489
|
if (this.destroyed) {
|
|
1416
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.subscribe", "
|
|
1490
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.subscribe", "ignored destroyed controller");
|
|
1417
1491
|
return noop;
|
|
1418
1492
|
}
|
|
1419
1493
|
this.listeners.add(listener);
|
|
1494
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.subscribe", "returned", { listenerCount: this.listeners.size });
|
|
1420
1495
|
let subscribed = true;
|
|
1421
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.subscribe", "subscribed", { listenerCount: this.listeners.size });
|
|
1422
1496
|
return () => {
|
|
1423
1497
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.unsubscribe", "called", { subscribed });
|
|
1424
1498
|
if (!subscribed) {
|
|
@@ -1431,15 +1505,14 @@ var DefaultDrivingStatusController = class {
|
|
|
1431
1505
|
};
|
|
1432
1506
|
}
|
|
1433
1507
|
activatePolling() {
|
|
1434
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.activatePolling", "called"
|
|
1435
|
-
destroyed: this.destroyed,
|
|
1436
|
-
polling: this.polling,
|
|
1437
|
-
requestedRunning: this.requestedRunning,
|
|
1438
|
-
lifecycleSuspended: this.lifecycleSuspended,
|
|
1439
|
-
generation: this.generation
|
|
1440
|
-
});
|
|
1508
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.activatePolling", "called");
|
|
1441
1509
|
if (this.destroyed || this.polling || !this.requestedRunning || this.lifecycleSuspended) {
|
|
1442
|
-
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
|
+
});
|
|
1443
1516
|
return;
|
|
1444
1517
|
}
|
|
1445
1518
|
this.polling = true;
|
|
@@ -1449,14 +1522,10 @@ var DefaultDrivingStatusController = class {
|
|
|
1449
1522
|
}
|
|
1450
1523
|
deactivatePolling() {
|
|
1451
1524
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.deactivatePolling", "called", {
|
|
1452
|
-
|
|
1453
|
-
|
|
1525
|
+
generation: this.generation,
|
|
1526
|
+
hasTimer: this.timer !== void 0,
|
|
1454
1527
|
hasPendingRead: this.pendingRead !== null
|
|
1455
1528
|
});
|
|
1456
|
-
if (!this.polling && this.timer === void 0 && this.pendingRead === null) {
|
|
1457
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.deactivatePolling", "ignored");
|
|
1458
|
-
return;
|
|
1459
|
-
}
|
|
1460
1529
|
this.polling = false;
|
|
1461
1530
|
this.generation += 1;
|
|
1462
1531
|
if (this.timer !== void 0) {
|
|
@@ -1468,72 +1537,86 @@ var DefaultDrivingStatusController = class {
|
|
|
1468
1537
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.deactivatePolling", "returned", { generation: this.generation });
|
|
1469
1538
|
}
|
|
1470
1539
|
schedulePoll(delayMs, generation) {
|
|
1471
|
-
|
|
1472
|
-
delayMs,
|
|
1473
|
-
generation,
|
|
1474
|
-
currentGeneration: this.generation,
|
|
1475
|
-
polling: this.polling,
|
|
1476
|
-
timer: this.timer
|
|
1477
|
-
});
|
|
1478
|
-
if (!this.polling || generation !== this.generation || this.timer !== void 0) {
|
|
1479
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.schedulePoll", "ignored");
|
|
1480
|
-
return;
|
|
1481
|
-
}
|
|
1540
|
+
if (!this.polling || generation !== this.generation || this.timer !== void 0) return;
|
|
1482
1541
|
this.timer = this.clock.setTimeout(() => {
|
|
1483
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.schedulePoll.callback", "called", { generation });
|
|
1484
1542
|
this.timer = void 0;
|
|
1485
1543
|
this.poll(generation);
|
|
1486
1544
|
}, delayMs);
|
|
1487
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.schedulePoll", "scheduled", {
|
|
1488
|
-
delayMs,
|
|
1489
|
-
generation,
|
|
1490
|
-
timer: this.timer
|
|
1491
|
-
});
|
|
1492
1545
|
}
|
|
1493
1546
|
async poll(generation) {
|
|
1494
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "called", {
|
|
1495
|
-
generation,
|
|
1496
|
-
currentGeneration: this.generation,
|
|
1497
|
-
polling: this.polling,
|
|
1498
|
-
hasPendingRead: this.pendingRead !== null
|
|
1499
|
-
});
|
|
1547
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "called", { generation });
|
|
1500
1548
|
if (!this.polling || generation !== this.generation || this.pendingRead !== null) {
|
|
1501
|
-
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
|
+
});
|
|
1502
1555
|
return;
|
|
1503
1556
|
}
|
|
1504
1557
|
const pendingRead = new AbortController();
|
|
1505
1558
|
this.pendingRead = pendingRead;
|
|
1506
1559
|
try {
|
|
1507
1560
|
const payload = await this.sensorProvider(pendingRead.signal);
|
|
1508
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "provider resolved", { payload });
|
|
1509
1561
|
if (!this.polling || generation !== this.generation || pendingRead.signal.aborted) {
|
|
1510
|
-
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
|
+
});
|
|
1511
1568
|
return;
|
|
1512
1569
|
}
|
|
1513
|
-
const
|
|
1514
|
-
const
|
|
1515
|
-
|
|
1516
|
-
|
|
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
|
+
}
|
|
1517
1587
|
} catch (error) {
|
|
1518
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "provider rejected", error);
|
|
1519
1588
|
if (!this.polling || generation !== this.generation || pendingRead.signal.aborted || isAbortError$1(error)) {
|
|
1520
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "ignored
|
|
1589
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "ignored provider error", { error });
|
|
1521
1590
|
return;
|
|
1522
1591
|
}
|
|
1592
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "caught provider error", { error });
|
|
1523
1593
|
this.processUnavailable("provider_error");
|
|
1524
1594
|
} finally {
|
|
1525
1595
|
if (this.pendingRead === pendingRead) this.pendingRead = null;
|
|
1526
1596
|
if (this.polling && generation === this.generation) this.schedulePoll(this.pollIntervalMs, generation);
|
|
1527
1597
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "completed", {
|
|
1528
1598
|
generation,
|
|
1529
|
-
polling: this.polling
|
|
1530
|
-
currentGeneration: this.generation
|
|
1599
|
+
polling: this.polling
|
|
1531
1600
|
});
|
|
1532
1601
|
}
|
|
1533
1602
|
}
|
|
1534
|
-
|
|
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) {
|
|
1535
1618
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processAvailable", "called", {
|
|
1536
|
-
|
|
1619
|
+
metrics,
|
|
1537
1620
|
accelerometerSample
|
|
1538
1621
|
});
|
|
1539
1622
|
const now = this.clock.now();
|
|
@@ -1543,56 +1626,156 @@ var DefaultDrivingStatusController = class {
|
|
|
1543
1626
|
this.emitDiagnostic({
|
|
1544
1627
|
type: "sample_available",
|
|
1545
1628
|
atMs: now,
|
|
1546
|
-
capturedAtMs:
|
|
1629
|
+
capturedAtMs: metrics.capturedAtMs,
|
|
1547
1630
|
tuningVersion: this.tuning.version
|
|
1548
1631
|
});
|
|
1549
|
-
this.
|
|
1550
|
-
|
|
1551
|
-
const
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
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
|
|
1561
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
|
+
}
|
|
1562
1723
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
if (!this.motionWindow.some((existing) => existing.sampledAtMs === sample.sampledAtMs)) {
|
|
1570
|
-
this.motionWindow.push(sample);
|
|
1571
|
-
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;
|
|
1572
1730
|
}
|
|
1573
|
-
const
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
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;
|
|
1581
1741
|
}
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
}
|
|
1587
|
-
|
|
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");
|
|
1588
1772
|
}
|
|
1589
1773
|
processUnavailable(category) {
|
|
1590
1774
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processUnavailable", "called", { category });
|
|
1591
1775
|
const now = this.clock.now();
|
|
1592
1776
|
if (this.unavailableSinceMs === null) this.unavailableSinceMs = now;
|
|
1593
1777
|
const unavailableForMs = Math.max(0, now - this.unavailableSinceMs);
|
|
1594
|
-
this.
|
|
1595
|
-
this.motionWindow.length = 0;
|
|
1778
|
+
this.clearRecognitionWindows();
|
|
1596
1779
|
this.setCandidate(null, now);
|
|
1597
1780
|
this.emitDiagnostic({
|
|
1598
1781
|
type: "sample_unavailable",
|
|
@@ -1602,97 +1785,39 @@ var DefaultDrivingStatusController = class {
|
|
|
1602
1785
|
tuningVersion: this.tuning.version
|
|
1603
1786
|
});
|
|
1604
1787
|
if (unavailableForMs < this.unavailableFallbackMs || this.fallbackApplied) {
|
|
1605
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processUnavailable", "
|
|
1788
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processUnavailable", "holding previous status", {
|
|
1789
|
+
category,
|
|
1606
1790
|
unavailableForMs,
|
|
1607
1791
|
fallbackApplied: this.fallbackApplied
|
|
1608
1792
|
});
|
|
1609
1793
|
return;
|
|
1610
1794
|
}
|
|
1611
1795
|
this.fallbackApplied = true;
|
|
1796
|
+
this.baseStatus = "STOPPED";
|
|
1612
1797
|
this.emitDiagnostic({
|
|
1613
1798
|
type: "fallback_to_stopped",
|
|
1614
1799
|
atMs: now,
|
|
1615
1800
|
unavailableForMs,
|
|
1616
1801
|
tuningVersion: this.tuning.version
|
|
1617
1802
|
});
|
|
1618
|
-
this.commit("STOPPED", now, "unavailable_fallback"
|
|
1619
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processUnavailable", "returned
|
|
1803
|
+
this.commit("STOPPED", now, "unavailable_fallback");
|
|
1804
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processUnavailable", "returned fallback", {
|
|
1620
1805
|
unavailableForMs,
|
|
1621
|
-
|
|
1806
|
+
status: this.status
|
|
1622
1807
|
});
|
|
1623
1808
|
}
|
|
1624
|
-
|
|
1625
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processCandidate", "called", {
|
|
1626
|
-
nextCandidate,
|
|
1627
|
-
now,
|
|
1628
|
-
currentStatus: this.status,
|
|
1629
|
-
candidate: this.candidate,
|
|
1630
|
-
candidateConfirmationCount: this.candidateConfirmationCount
|
|
1631
|
-
});
|
|
1632
|
-
if (nextCandidate === this.status) {
|
|
1633
|
-
this.setCandidate(null, now);
|
|
1634
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processCandidate", "returned current status");
|
|
1635
|
-
return;
|
|
1636
|
-
}
|
|
1637
|
-
if (nextCandidate !== this.candidate) {
|
|
1638
|
-
this.setCandidate(nextCandidate, now);
|
|
1639
|
-
this.candidateConfirmationCount = 1;
|
|
1640
|
-
this.heldCandidate = null;
|
|
1641
|
-
} else this.candidateConfirmationCount += 1;
|
|
1642
|
-
if (this.candidateConfirmationCount < this.tuning.confirmationSamples[nextCandidate]) {
|
|
1643
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processCandidate", "awaiting confirmation", {
|
|
1644
|
-
nextCandidate,
|
|
1645
|
-
candidateConfirmationCount: this.candidateConfirmationCount,
|
|
1646
|
-
required: this.tuning.confirmationSamples[nextCandidate]
|
|
1647
|
-
});
|
|
1648
|
-
return;
|
|
1649
|
-
}
|
|
1650
|
-
const elapsedMs = Math.max(0, now - this.statusCommittedAtMs);
|
|
1651
|
-
const remainingMs = Math.max(0, this.tuning.minimumDisplayMs[this.status] - elapsedMs);
|
|
1652
|
-
const canPreempt = CAR_RUNNING_STATUS_PRIORITY[nextCandidate] > CAR_RUNNING_STATUS_PRIORITY[this.status];
|
|
1653
|
-
if (remainingMs > 0 && !canPreempt) {
|
|
1654
|
-
if (this.heldCandidate !== nextCandidate) {
|
|
1655
|
-
this.heldCandidate = nextCandidate;
|
|
1656
|
-
this.emitDiagnostic({
|
|
1657
|
-
type: "status_held_by_min_duration",
|
|
1658
|
-
atMs: now,
|
|
1659
|
-
status: this.status,
|
|
1660
|
-
candidate: nextCandidate,
|
|
1661
|
-
remainingMs,
|
|
1662
|
-
tuningVersion: this.tuning.version
|
|
1663
|
-
});
|
|
1664
|
-
}
|
|
1665
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processCandidate", "held by minimum duration", {
|
|
1666
|
-
nextCandidate,
|
|
1667
|
-
remainingMs
|
|
1668
|
-
});
|
|
1669
|
-
return;
|
|
1670
|
-
}
|
|
1671
|
-
this.commit(nextCandidate, now, "candidate_confirmed");
|
|
1672
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processCandidate", "returned after commit", { snapshot: this.getSnapshot() });
|
|
1673
|
-
}
|
|
1674
|
-
commit(nextStatus, now, reason, force = false) {
|
|
1675
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.commit", "called", {
|
|
1676
|
-
nextStatus,
|
|
1677
|
-
now,
|
|
1678
|
-
reason,
|
|
1679
|
-
force,
|
|
1680
|
-
currentStatus: this.status
|
|
1681
|
-
});
|
|
1682
|
-
if (!force && nextStatus === this.status) {
|
|
1683
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.commit", "ignored unchanged status");
|
|
1684
|
-
return;
|
|
1685
|
-
}
|
|
1809
|
+
commit(nextStatus, now, reason) {
|
|
1686
1810
|
if (nextStatus === this.status) {
|
|
1687
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.commit", "ignored
|
|
1811
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.commit", "ignored unchanged status", {
|
|
1812
|
+
status: this.status,
|
|
1813
|
+
reason
|
|
1814
|
+
});
|
|
1688
1815
|
return;
|
|
1689
1816
|
}
|
|
1690
1817
|
const previousStatus = this.status;
|
|
1691
1818
|
this.status = nextStatus;
|
|
1692
|
-
if (isBaseStatus(nextStatus)) this.baseStatus = nextStatus;
|
|
1693
1819
|
this.statusCommittedAtMs = now;
|
|
1694
1820
|
this.setCandidate(null, now);
|
|
1695
|
-
this.heldCandidate = null;
|
|
1696
1821
|
const event = {
|
|
1697
1822
|
type: "status_changed",
|
|
1698
1823
|
status: nextStatus,
|
|
@@ -1709,53 +1834,49 @@ var DefaultDrivingStatusController = class {
|
|
|
1709
1834
|
reason,
|
|
1710
1835
|
tuningVersion: this.tuning.version
|
|
1711
1836
|
});
|
|
1712
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.commit", "
|
|
1713
|
-
event,
|
|
1714
|
-
listenerCount: this.listeners.size,
|
|
1715
|
-
snapshot: this.getSnapshot()
|
|
1716
|
-
});
|
|
1837
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.commit", "status changed", { event });
|
|
1717
1838
|
}
|
|
1718
|
-
setCandidate(candidate,
|
|
1719
|
-
|
|
1720
|
-
candidate,
|
|
1721
|
-
now,
|
|
1722
|
-
currentCandidate: this.candidate
|
|
1723
|
-
});
|
|
1724
|
-
if (candidate === this.candidate) {
|
|
1725
|
-
if (candidate === null) this.candidateConfirmationCount = 0;
|
|
1726
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.setCandidate", "returned unchanged", { candidateConfirmationCount: this.candidateConfirmationCount });
|
|
1727
|
-
return;
|
|
1728
|
-
}
|
|
1839
|
+
setCandidate(candidate, atMs) {
|
|
1840
|
+
if (candidate === this.candidate) return;
|
|
1729
1841
|
const previousCandidate = this.candidate;
|
|
1730
1842
|
this.candidate = candidate;
|
|
1731
|
-
this.
|
|
1732
|
-
this.heldCandidate = null;
|
|
1843
|
+
this.candidateSinceMs = candidate === null ? null : atMs;
|
|
1733
1844
|
this.emitDiagnostic({
|
|
1734
1845
|
type: "candidate_changed",
|
|
1735
|
-
atMs: now,
|
|
1846
|
+
atMs: this.clock.now(),
|
|
1736
1847
|
candidate,
|
|
1737
1848
|
previousCandidate,
|
|
1738
1849
|
tuningVersion: this.tuning.version
|
|
1739
1850
|
});
|
|
1740
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.setCandidate", "
|
|
1851
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.setCandidate", "candidate changed", {
|
|
1741
1852
|
candidate,
|
|
1742
|
-
previousCandidate
|
|
1853
|
+
previousCandidate,
|
|
1854
|
+
candidateSinceMs: this.candidateSinceMs
|
|
1743
1855
|
});
|
|
1744
1856
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
motionWindow: this.motionWindow,
|
|
1749
|
-
candidate: this.candidate
|
|
1750
|
-
});
|
|
1751
|
-
this.metricWindow.length = 0;
|
|
1857
|
+
clearRecognitionWindows() {
|
|
1858
|
+
this.longitudinalWindow.length = 0;
|
|
1859
|
+
this.yawWindow.length = 0;
|
|
1752
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();
|
|
1753
1874
|
this.candidate = null;
|
|
1754
|
-
this.
|
|
1875
|
+
this.candidateSinceMs = null;
|
|
1755
1876
|
this.unavailableSinceMs = null;
|
|
1756
1877
|
this.fallbackApplied = false;
|
|
1757
|
-
this.
|
|
1758
|
-
|
|
1878
|
+
this.filteredGravity = null;
|
|
1879
|
+
this.lastGravitySampledAtMs = null;
|
|
1759
1880
|
}
|
|
1760
1881
|
emitDiagnostic(event) {
|
|
1761
1882
|
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.emitDiagnostic", "called", {
|
|
@@ -1763,315 +1884,197 @@ var DefaultDrivingStatusController = class {
|
|
|
1763
1884
|
hasListener: this.onDiagnostic !== void 0
|
|
1764
1885
|
});
|
|
1765
1886
|
if (this.onDiagnostic) safeCall(this.onDiagnostic, event);
|
|
1766
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.emitDiagnostic", "returned", { event });
|
|
1767
1887
|
}
|
|
1768
1888
|
attachLifecycleListeners() {
|
|
1769
|
-
|
|
1770
|
-
managePageLifecycle: this.managePageLifecycle,
|
|
1771
|
-
attached: this.unsubscribePageLifecycle !== null
|
|
1772
|
-
});
|
|
1773
|
-
if (!this.managePageLifecycle || this.unsubscribePageLifecycle !== null) {
|
|
1774
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.attachLifecycleListeners", "ignored");
|
|
1775
|
-
return;
|
|
1776
|
-
}
|
|
1889
|
+
if (!this.managePageLifecycle || this.unsubscribePageLifecycle !== null) return;
|
|
1777
1890
|
this.unsubscribePageLifecycle = subscribeDrivingPageLifecycle(this.handleLifecyclePause, this.handleLifecycleResume);
|
|
1778
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.attachLifecycleListeners", "returned");
|
|
1779
1891
|
}
|
|
1780
1892
|
detachLifecycleListeners() {
|
|
1781
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.detachLifecycleListeners", "called", { attached: this.unsubscribePageLifecycle !== null });
|
|
1782
1893
|
this.unsubscribePageLifecycle?.();
|
|
1783
1894
|
this.unsubscribePageLifecycle = null;
|
|
1784
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.detachLifecycleListeners", "returned");
|
|
1785
1895
|
}
|
|
1786
1896
|
};
|
|
1787
|
-
const
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
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
|
+
};
|
|
1805
1933
|
};
|
|
1806
|
-
const
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
}
|
|
1827
|
-
if (Math.abs(smoothed.lateral) >= tuning.turnLateralThreshold) {
|
|
1828
|
-
const result = smoothed.lateral < 0 ? "LEFT_TURN" : "RIGHT_TURN";
|
|
1829
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "classifyEntryCandidate", "returned", { result });
|
|
1830
|
-
return result;
|
|
1831
|
-
}
|
|
1832
|
-
if (smoothed.longitudinal <= -tuning.longitudinalThreshold) {
|
|
1833
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "classifyEntryCandidate", "returned", { result: "BRAKING" });
|
|
1834
|
-
return "BRAKING";
|
|
1835
|
-
}
|
|
1836
|
-
if (smoothed.longitudinal >= tuning.longitudinalThreshold) {
|
|
1837
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "classifyEntryCandidate", "returned", { result: "ACCELERATION" });
|
|
1838
|
-
return "ACCELERATION";
|
|
1839
|
-
}
|
|
1840
|
-
if (motionIntensity === null) {
|
|
1841
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "classifyEntryCandidate", "returned base status", { result: baseStatus });
|
|
1842
|
-
return baseStatus;
|
|
1843
|
-
}
|
|
1844
|
-
if (motionIntensity > tuning.drivingMotionThreshold || smoothed.gyroMagnitude > tuning.stoppedGyroThreshold) {
|
|
1845
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "classifyEntryCandidate", "returned", { result: "STEADY_DRIVING" });
|
|
1846
|
-
return "STEADY_DRIVING";
|
|
1847
|
-
}
|
|
1848
|
-
if (motionIntensity < tuning.stoppedMotionThreshold) {
|
|
1849
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "classifyEntryCandidate", "returned", { result: "STOPPED" });
|
|
1850
|
-
return "STOPPED";
|
|
1851
|
-
}
|
|
1852
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "classifyEntryCandidate", "returned base status", { result: baseStatus });
|
|
1853
|
-
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
|
+
};
|
|
1854
1954
|
};
|
|
1855
|
-
const
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
const
|
|
1864
|
-
|
|
1865
|
-
|
|
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
|
-
|
|
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;
|
|
1893
2017
|
};
|
|
1894
|
-
const
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
gyroMagnitude: 0
|
|
1903
|
-
};
|
|
1904
|
-
for (const value of metrics) {
|
|
1905
|
-
result.longitudinal += value.longitudinal;
|
|
1906
|
-
result.lateral += value.lateral;
|
|
1907
|
-
result.yaw += value.yaw;
|
|
1908
|
-
result.motionIntensity += value.motionIntensity;
|
|
1909
|
-
result.gyroMagnitude += value.gyroMagnitude;
|
|
1910
|
-
}
|
|
1911
|
-
const count = Math.max(1, metrics.length);
|
|
1912
|
-
result.longitudinal /= count;
|
|
1913
|
-
result.lateral /= count;
|
|
1914
|
-
result.yaw /= count;
|
|
1915
|
-
result.motionIntensity /= count;
|
|
1916
|
-
result.gyroMagnitude /= count;
|
|
1917
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "averageMetrics", "returned", { result });
|
|
1918
|
-
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;
|
|
1919
2026
|
};
|
|
1920
|
-
const
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
});
|
|
1925
|
-
const metric = info?.accelerometer;
|
|
1926
|
-
if (!metric?.available || !Number.isFinite(info.capturedAtMs) || !Number.isFinite(metric.sampledAtMs)) {
|
|
1927
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "extractFreshAccelerometerSample", "returned", { result: null });
|
|
1928
|
-
return null;
|
|
1929
|
-
}
|
|
1930
|
-
const ageMs = info.capturedAtMs - metric.sampledAtMs;
|
|
1931
|
-
if (ageMs < 0 || ageMs > maxSampleAgeMs) {
|
|
1932
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "extractFreshAccelerometerSample", "returned stale sample", {
|
|
1933
|
-
result: null,
|
|
1934
|
-
ageMs
|
|
1935
|
-
});
|
|
1936
|
-
return null;
|
|
1937
|
-
}
|
|
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;
|
|
1938
2031
|
const { x, y, z } = metric.value;
|
|
1939
|
-
|
|
2032
|
+
return [
|
|
1940
2033
|
x,
|
|
1941
2034
|
y,
|
|
1942
2035
|
z
|
|
1943
|
-
].every(Number.isFinite)
|
|
1944
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "extractFreshAccelerometerSample", "returned invalid vector", { result: null });
|
|
1945
|
-
return null;
|
|
1946
|
-
}
|
|
1947
|
-
const result = {
|
|
2036
|
+
].every(Number.isFinite) ? {
|
|
1948
2037
|
sampledAtMs: metric.sampledAtMs,
|
|
1949
2038
|
value: metric.value
|
|
1950
|
-
};
|
|
1951
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "extractFreshAccelerometerSample", "returned", { result });
|
|
1952
|
-
return result;
|
|
1953
|
-
};
|
|
1954
|
-
const combinedAxisStandardDeviation = (samples) => {
|
|
1955
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "combinedAxisStandardDeviation", "called", { samples });
|
|
1956
|
-
const count = samples.length;
|
|
1957
|
-
let meanX = 0;
|
|
1958
|
-
let meanY = 0;
|
|
1959
|
-
let meanZ = 0;
|
|
1960
|
-
for (const sample of samples) {
|
|
1961
|
-
meanX += sample.value.x;
|
|
1962
|
-
meanY += sample.value.y;
|
|
1963
|
-
meanZ += sample.value.z;
|
|
1964
|
-
}
|
|
1965
|
-
meanX /= count;
|
|
1966
|
-
meanY /= count;
|
|
1967
|
-
meanZ /= count;
|
|
1968
|
-
let varianceX = 0;
|
|
1969
|
-
let varianceY = 0;
|
|
1970
|
-
let varianceZ = 0;
|
|
1971
|
-
for (const sample of samples) {
|
|
1972
|
-
varianceX += (sample.value.x - meanX) ** 2;
|
|
1973
|
-
varianceY += (sample.value.y - meanY) ** 2;
|
|
1974
|
-
varianceZ += (sample.value.z - meanZ) ** 2;
|
|
1975
|
-
}
|
|
1976
|
-
const result = Math.sqrt((varianceX + varianceY + varianceZ) / count);
|
|
1977
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "combinedAxisStandardDeviation", "returned", {
|
|
1978
|
-
meanX,
|
|
1979
|
-
meanY,
|
|
1980
|
-
meanZ,
|
|
1981
|
-
varianceX,
|
|
1982
|
-
varianceY,
|
|
1983
|
-
varianceZ,
|
|
1984
|
-
result
|
|
1985
|
-
});
|
|
1986
|
-
return result;
|
|
2039
|
+
} : null;
|
|
1987
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
|
+
});
|
|
1988
2047
|
const defaultSensorProvider = () => {
|
|
1989
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "defaultSensorProvider", "called");
|
|
1990
2048
|
const runtimeGlobal = globalThis;
|
|
1991
|
-
if (typeof runtimeGlobal.unifiedSensorInfo !== "function")
|
|
1992
|
-
|
|
1993
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "defaultSensorProvider", "rejected", error);
|
|
1994
|
-
return Promise.reject(error);
|
|
1995
|
-
}
|
|
1996
|
-
try {
|
|
1997
|
-
const result = runtimeGlobal.unifiedSensorInfo();
|
|
1998
|
-
result.then((payload) => dialSdkDebugWarn(DEBUG_MODULE$1, "defaultSensorProvider", "resolved", { payload }), (error) => dialSdkDebugWarn(DEBUG_MODULE$1, "defaultSensorProvider", "rejected", error));
|
|
1999
|
-
return result;
|
|
2000
|
-
} catch (error) {
|
|
2001
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "defaultSensorProvider", "threw", error);
|
|
2002
|
-
throw error;
|
|
2003
|
-
}
|
|
2049
|
+
if (typeof runtimeGlobal.unifiedSensorInfo !== "function") return Promise.reject(/* @__PURE__ */ new Error("window.unifiedSensorInfo is unavailable"));
|
|
2050
|
+
return runtimeGlobal.unifiedSensorInfo();
|
|
2004
2051
|
};
|
|
2005
2052
|
const systemClock = {
|
|
2006
|
-
now: () =>
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
return result;
|
|
2010
|
-
},
|
|
2011
|
-
setTimeout: (callback, delayMs) => {
|
|
2012
|
-
const result = globalThis.setTimeout(callback, delayMs);
|
|
2013
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "systemClock.setTimeout", "returned", {
|
|
2014
|
-
delayMs,
|
|
2015
|
-
result
|
|
2016
|
-
});
|
|
2017
|
-
return result;
|
|
2018
|
-
},
|
|
2019
|
-
clearTimeout: (handle) => {
|
|
2020
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "systemClock.clearTimeout", "called", { handle });
|
|
2021
|
-
globalThis.clearTimeout(handle);
|
|
2022
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "systemClock.clearTimeout", "returned");
|
|
2023
|
-
}
|
|
2024
|
-
};
|
|
2025
|
-
const isOppositeTurn = (candidate, current) => {
|
|
2026
|
-
const result = candidate === "LEFT_TURN" && current === "RIGHT_TURN" || candidate === "RIGHT_TURN" && current === "LEFT_TURN";
|
|
2027
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "isOppositeTurn", "returned", {
|
|
2028
|
-
candidate,
|
|
2029
|
-
current,
|
|
2030
|
-
result
|
|
2031
|
-
});
|
|
2032
|
-
return result;
|
|
2033
|
-
};
|
|
2034
|
-
const isBaseStatus = (status) => {
|
|
2035
|
-
const result = status === "STOPPED" || status === "STEADY_DRIVING";
|
|
2036
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "isBaseStatus", "returned", {
|
|
2037
|
-
status,
|
|
2038
|
-
result
|
|
2039
|
-
});
|
|
2040
|
-
return result;
|
|
2041
|
-
};
|
|
2042
|
-
const isAbortError$1 = (error) => {
|
|
2043
|
-
const result = error instanceof Error && error.name === "AbortError";
|
|
2044
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "isAbortError", "returned", {
|
|
2045
|
-
error,
|
|
2046
|
-
result
|
|
2047
|
-
});
|
|
2048
|
-
return result;
|
|
2053
|
+
now: () => Date.now(),
|
|
2054
|
+
setTimeout: (callback, delayMs) => globalThis.setTimeout(callback, delayMs),
|
|
2055
|
+
clearTimeout: (handle) => globalThis.clearTimeout(handle)
|
|
2049
2056
|
};
|
|
2057
|
+
const isBaseStatus = (status) => status === "STOPPED" || status === "STEADY_DRIVING";
|
|
2058
|
+
const isAbortError$1 = (error) => error instanceof Error && error.name === "AbortError";
|
|
2050
2059
|
const requirePositiveFinite = (name, value) => {
|
|
2051
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "requirePositiveFinite", "called", {
|
|
2052
|
-
name,
|
|
2053
|
-
value
|
|
2054
|
-
});
|
|
2055
2060
|
if (!Number.isFinite(value) || value <= 0) {
|
|
2056
2061
|
const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
|
|
2057
2062
|
dialSdkDebugWarn(DEBUG_MODULE$1, "requirePositiveFinite", "threw", error);
|
|
2058
2063
|
throw error;
|
|
2059
2064
|
}
|
|
2060
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "requirePositiveFinite", "returned", { result: value });
|
|
2061
2065
|
return value;
|
|
2062
2066
|
};
|
|
2063
2067
|
const safeCall = (callback, value) => {
|
|
2064
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "called", { value });
|
|
2065
2068
|
try {
|
|
2066
2069
|
callback(value);
|
|
2067
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "returned");
|
|
2068
2070
|
} catch (error) {
|
|
2069
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "callback threw",
|
|
2071
|
+
dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "callback threw", {
|
|
2072
|
+
error,
|
|
2073
|
+
value
|
|
2074
|
+
});
|
|
2070
2075
|
}
|
|
2071
2076
|
};
|
|
2072
|
-
const noop = () =>
|
|
2073
|
-
dialSdkDebugWarn(DEBUG_MODULE$1, "noop", "called and returned");
|
|
2074
|
-
};
|
|
2077
|
+
const noop = () => void 0;
|
|
2075
2078
|
//#endregion
|
|
2076
2079
|
//#region src/sensor/driving_expression_player.ts
|
|
2077
2080
|
const DEBUG_MODULE = "driving_expression_player.ts";
|
|
@@ -3351,4 +3354,4 @@ const isRecord = (value) => {
|
|
|
3351
3354
|
return result;
|
|
3352
3355
|
};
|
|
3353
3356
|
//#endregion
|
|
3354
|
-
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 };
|