gps-plus-slam-app-framework 1.4.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/absolute-orientation-CAEh840t.d.ts +54 -0
- package/dist/{app-selectors-CrOucL00.d.ts → app-selectors-ikTUQZpe.d.ts} +1 -1
- package/dist/ar/bresenham3d.d.ts +1 -1
- package/dist/ar/camera-blit-capture.d.ts +1 -1
- package/dist/ar/capability-checker.d.ts +1 -1
- package/dist/ar/capture-failure-tracker.d.ts +1 -1
- package/dist/ar/chromium-camera-access-workaround.d.ts +1 -1
- package/dist/ar/depth-grid-lookup.d.ts +1 -1
- package/dist/ar/depth-sampler.d.ts +2 -2
- package/dist/ar/depth-unprojection.d.ts +1 -1
- package/dist/ar/enable-gps-ar.d.ts +1 -1
- package/dist/ar/enable-gps-ar.js +1 -1
- package/dist/ar/frame-loop.d.ts +1 -1
- package/dist/ar/image-capture.d.ts +2 -2
- package/dist/ar/image-capture.js +1 -154
- package/dist/ar/image-quality.d.ts +2 -0
- package/dist/ar/image-quality.js +168 -0
- package/dist/ar/index.d.ts +31 -30
- package/dist/ar/index.js +6 -5
- package/dist/ar/occupancy-grid.d.ts +1 -1
- package/dist/ar/planar-pnp.d.ts +1 -1
- package/dist/ar/qr-debug-view.d.ts +1 -1
- package/dist/ar/qr-derived-pose.d.ts +1 -1
- package/dist/ar/qr-detection-controller.d.ts +1 -1
- package/dist/ar/qr-frontend.d.ts +1 -1
- package/dist/ar/qr-pose.d.ts +1 -1
- package/dist/ar/qr-size-depth-context.d.ts +2 -2
- package/dist/ar/qr-size-measurer.d.ts +1 -1
- package/dist/ar/replay-scene.d.ts +1 -1
- package/dist/ar/replay-scene.js +1 -1
- package/dist/ar/scene-node-names.d.ts +1 -1
- package/dist/ar/webxr-nue-basis.d.ts +1 -1
- package/dist/ar/webxr-session.d.ts +4 -4
- package/dist/ar/webxr-session.js +2 -2
- package/dist/ar/xr-camera-texture.d.ts +1 -1
- package/dist/ar/xr-error-handler.d.ts +1 -1
- package/dist/ar/xr-frame-loop.d.ts +1 -1
- package/dist/{ar-Dc72Ksiu.js → ar-MFpFlV7j.js} +3 -3
- package/dist/{ar-world-group-alignment-DDLkgNf_.d.ts → ar-world-group-alignment-69fgnB39.d.ts} +1 -1
- package/dist/capture-motion-gate-0Yl2NpQG.d.ts +24 -0
- package/dist/capture-motion-gate-4EscNZCw.js +94 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +2 -2
- package/dist/create-slam-app-store-DkCHOpvb.js +149 -0
- package/dist/{create-slam-app-store-Ce2FMKCp.d.ts → create-slam-app-store-n_VF63D5.d.ts} +50 -40
- package/dist/{depth-grid-lookup-vMDrGTk_.d.ts → depth-grid-lookup-DQ-IYhO3.d.ts} +1 -1
- package/dist/{depth-sampler-DwfPYzS7.d.ts → depth-sampler-asc8exHt.d.ts} +1 -1
- package/dist/{depth-unprojection-CK3b9htH.d.ts → depth-unprojection-DjsbJW9L.d.ts} +1 -1
- package/dist/{enable-gps-ar-CjZ1nEdu.d.ts → enable-gps-ar-DkNhFv4Y.d.ts} +4 -4
- package/dist/{frame-conversions-D_Af8Dt3.d.ts → frame-conversions-DObhj8Wf.d.ts} +1 -1
- package/dist/{fused-path-C0FHe67t.d.ts → fused-path-v6eOmLl3.d.ts} +1 -1
- package/dist/geo/h3-proximity.d.ts +1 -1
- package/dist/geo/index.d.ts +1 -1
- package/dist/geodesic-angle-BTldfFgs.js +39 -0
- package/dist/{gps-anchor-C6I9NDHA.d.ts → gps-anchor-BeFO2RYa.d.ts} +1 -1
- package/dist/{gps-ar-pose-sampler-DaHg2aPZ.d.ts → gps-ar-pose-sampler-DHMZmYAc.d.ts} +1 -1
- package/dist/{gps-event-coordinator-C7hwHyLA.d.ts → gps-event-coordinator-CjWW636x.d.ts} +21 -11
- package/dist/image-capture-B2-b6tKK.js +374 -0
- package/dist/{image-capture-C2Feywet.d.ts → image-capture-CNZDrQXG.d.ts} +103 -3
- package/dist/image-quality-smCBoFj1.d.ts +158 -0
- package/dist/index-BAPokxMb.d.ts +22 -0
- package/dist/{index-m_LneUSi.d.ts → index-BODDUTvD.d.ts} +1 -1
- package/dist/{index-BvLo2poy.d.ts → index-BneSj4_5.d.ts} +3 -3
- package/dist/index-_Rhgoqkh.d.ts +2 -0
- package/dist/index.d.ts +80 -77
- package/dist/index.js +11 -8
- package/dist/{leaflet-map-overlay-bnb2U1NQ.d.ts → leaflet-map-overlay-CmIyiE8n.d.ts} +1 -1
- package/dist/{map-data-DdjPiSav.d.ts → map-data-B4oEehUh.d.ts} +1 -1
- package/dist/{map-overlay-draw-CPVdAsFq.d.ts → map-overlay-draw-ymz1s7fG.d.ts} +1 -1
- package/dist/{null-storage-backend-CuSdYFsM.d.ts → null-storage-backend-B9rtyDfG.d.ts} +2 -2
- package/dist/{occupancy-grid-Bvi2Gw1U.d.ts → occupancy-grid-DnOc-6PU.d.ts} +2 -2
- package/dist/{opfs-storage-backend-CeQAb0SD.d.ts → opfs-storage-backend-JJiUJo-3.d.ts} +2 -2
- package/dist/{persistence-middleware-5eearhSV.d.ts → persistence-middleware-DAPTTwB2.d.ts} +1 -1
- package/dist/{planar-pnp-cjthe7Be.d.ts → planar-pnp-my9bqULS.d.ts} +1 -1
- package/dist/{qr-debug-view-B8gTzjMm.d.ts → qr-debug-view-Brl73HGO.d.ts} +1 -1
- package/dist/{qr-derived-pose-DinKE600.d.ts → qr-derived-pose-CmxTP2m4.d.ts} +2 -2
- package/dist/{qr-detected-slice-Cpv-dg82.d.ts → qr-detected-slice-VQssZmQJ.d.ts} +4 -4
- package/dist/{qr-detection-controller-Cd2Ih4cC.d.ts → qr-detection-controller-CDurHv8E.d.ts} +3 -3
- package/dist/{qr-frontend-DufZp2xS.d.ts → qr-frontend--fFX25mV.d.ts} +1 -1
- package/dist/{qr-pose-aggregation-CFlmLm6w.d.ts → qr-pose-aggregation-B7tWJq7j.d.ts} +1 -1
- package/dist/{qr-pose-aggregation-DXR9STHs.js → qr-pose-aggregation-DBiaLW0O.js} +1 -10
- package/dist/{qr-size-depth-context-onu36E9s.d.ts → qr-size-depth-context-CeAG53fy.d.ts} +2 -2
- package/dist/{qr-size-measurer-D7739ZpB.d.ts → qr-size-measurer-DQdAsid1.d.ts} +3 -3
- package/dist/{recording-options-D93kksJU.d.ts → recording-options-BPCpMHLK.d.ts} +154 -9
- package/dist/{recording-replayer-CUEK82tM.d.ts → recording-replayer-DQKpAGyQ.d.ts} +2 -2
- package/dist/{recording-slice-B-uCgGoh.d.ts → recording-slice-DaxHZvNu.d.ts} +2 -2
- package/dist/{replay-engine-BSVd7Zzb.d.ts → replay-engine-CmKh_4wf.d.ts} +1 -1
- package/dist/{replay-scene-CPt56pps.d.ts → replay-scene-DBlDrV_x.d.ts} +3 -3
- package/dist/sensors/absolute-orientation.d.ts +2 -0
- package/dist/sensors/absolute-orientation.js +145 -0
- package/dist/sensors/gps-error-handler.d.ts +1 -1
- package/dist/sensors/gps.d.ts +1 -1
- package/dist/sensors/index.d.ts +5 -4
- package/dist/sensors/index.js +2 -1
- package/dist/sensors/permission-checker.d.ts +1 -1
- package/dist/state/app-selectors.d.ts +1 -1
- package/dist/state/combined-root-state.d.ts +1 -1
- package/dist/state/create-slam-app-store.d.ts +1 -1
- package/dist/state/create-slam-app-store.js +1 -74
- package/dist/state/gps-ar-pose-sampler.d.ts +3 -3
- package/dist/state/gps-event-coordinator.d.ts +2 -2
- package/dist/state/gps-event-coordinator.js +27 -17
- package/dist/state/index.d.ts +20 -20
- package/dist/state/index.js +4 -4
- package/dist/state/persistence-middleware.d.ts +1 -1
- package/dist/state/persistence-middleware.js +30 -19
- package/dist/state/qr-detected-slice.d.ts +3 -3
- package/dist/state/qr-detected-slice.js +1 -1
- package/dist/state/recording-options.d.ts +2 -2
- package/dist/state/recording-options.js +155 -13
- package/dist/state/recording-replayer.d.ts +2 -2
- package/dist/state/recording-replayer.js +1 -1
- package/dist/state/recording-slice.d.ts +1 -1
- package/dist/state/replay-engine.d.ts +1 -1
- package/dist/state/store-subscribers.d.ts +2 -2
- package/dist/state/subscribe-to-selector.d.ts +1 -1
- package/dist/state/tracking-quality.d.ts +2 -2
- package/dist/state/tracking-quality.js +40 -128
- package/dist/state/tracking-slice.d.ts +1 -1
- package/dist/{state-IEw0ZsBu.js → state-E51QT_-j.js} +1 -1
- package/dist/storage/file-system-utils.d.ts +1 -1
- package/dist/storage/index.d.ts +8 -8
- package/dist/storage/null-storage-backend.d.ts +1 -1
- package/dist/storage/opfs-storage-backend.d.ts +1 -1
- package/dist/storage/opfs-storage.d.ts +1 -1
- package/dist/storage/storage-backend.d.ts +1 -1
- package/dist/storage/zip-coverage-embed.d.ts +1 -1
- package/dist/storage/zip-export.d.ts +1 -1
- package/dist/storage/zip-reader.d.ts +1 -1
- package/dist/{storage-backend-CTJHNdMp.d.ts → storage-backend-BdVLD0dU.d.ts} +1 -1
- package/dist/{store-subscribers-BZP81hjh.d.ts → store-subscribers-CA6AJH_8.d.ts} +2 -2
- package/dist/{subscribe-to-selector-Bp3sLx8L.d.ts → subscribe-to-selector-B6EUmQ3I.d.ts} +1 -1
- package/dist/test-utils/browser-mocks.d.ts +1 -1
- package/dist/test-utils/zip-round-trip-helpers.d.ts +1 -1
- package/dist/{tracking-slice-DJZ9qeGg.d.ts → tracking-slice-BuAZxXZ5.d.ts} +1 -1
- package/dist/types/ar-types.d.ts +1 -1
- package/dist/types/geo-types.d.ts +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/utils/concurrency.d.ts +1 -1
- package/dist/utils/failure-tracker.d.ts +1 -1
- package/dist/utils/format-file-size.d.ts +1 -1
- package/dist/utils/fused-path.d.ts +1 -1
- package/dist/utils/index.d.ts +8 -7
- package/dist/utils/index.js +2 -1
- package/dist/utils/list-formatter.d.ts +1 -1
- package/dist/utils/logger.d.ts +1 -1
- package/dist/visualization/accuracy-circles.d.ts +1 -1
- package/dist/visualization/alignment-lerper.d.ts +1 -1
- package/dist/visualization/ar-world-group-alignment.d.ts +1 -1
- package/dist/visualization/ar-world-group-alignment.js +1 -1
- package/dist/visualization/camera-follower.d.ts +1 -1
- package/dist/visualization/css3d-renderer-manager.d.ts +1 -1
- package/dist/visualization/frame-conversions.d.ts +1 -1
- package/dist/visualization/frustum-visibility.d.ts +1 -1
- package/dist/visualization/gps-anchor.d.ts +1 -1
- package/dist/visualization/gps-compass-cubes.d.ts +1 -1
- package/dist/visualization/gps-event-markers.d.ts +1 -1
- package/dist/visualization/gps-event-markers.js +1 -1
- package/dist/visualization/hit-test-reticle.d.ts +1 -1
- package/dist/visualization/index.d.ts +18 -18
- package/dist/visualization/leaflet-map-overlay.d.ts +1 -1
- package/dist/visualization/lerp-utils.d.ts +1 -1
- package/dist/visualization/map-data.d.ts +1 -1
- package/dist/visualization/map-overlay-draw.d.ts +1 -1
- package/dist/visualization/map-overlay.d.ts +1 -1
- package/dist/visualization/three-dispose.d.ts +1 -1
- package/dist/visualization/vis-colors.d.ts +1 -1
- package/dist/{webxr-session-CJF9Nijp.js → webxr-session-DG-eNfny.js} +25 -4
- package/dist/{webxr-session-COsOjJwn.d.ts → webxr-session-Do5L6G3B.d.ts} +19 -9
- package/package.json +2 -2
- package/dist/index-eaI1Z1KR.d.ts +0 -2
- /package/dist/{accuracy-circles-Pi2YeCrM.d.ts → accuracy-circles-BtqOP_v1.d.ts} +0 -0
- /package/dist/{alignment-lerper-BUTsQ2yu.d.ts → alignment-lerper-Bil6vbVw.d.ts} +0 -0
- /package/dist/{ar-types-BckI1E-O.d.ts → ar-types-BFX2r1wk.d.ts} +0 -0
- /package/dist/{bresenham3d-ByjcmEgM.d.ts → bresenham3d-CU1Gv2W4.d.ts} +0 -0
- /package/dist/{camera-blit-capture-CqWe1G7s.d.ts → camera-blit-capture-NKT9H5-e.d.ts} +0 -0
- /package/dist/{camera-follower-BTb2Yp12.d.ts → camera-follower-CqJJ7zRj.d.ts} +0 -0
- /package/dist/{capability-checker-CXqoJYtb.d.ts → capability-checker-DUQLg6gq.d.ts} +0 -0
- /package/dist/{capture-failure-tracker-BL5SoG2a.d.ts → capture-failure-tracker-gsm8q7yi.d.ts} +0 -0
- /package/dist/{chromium-camera-access-workaround-BNK189P0.d.ts → chromium-camera-access-workaround-D4K2uwwQ.d.ts} +0 -0
- /package/dist/{concurrency-DqAlKX4j.d.ts → concurrency-By0oOTZt.d.ts} +0 -0
- /package/dist/{css3d-renderer-manager-m-ab19vp.d.ts → css3d-renderer-manager-B3kGNtcn.d.ts} +0 -0
- /package/dist/{failure-tracker-Cnc_xWUw.d.ts → failure-tracker-BO7M7jF_.d.ts} +0 -0
- /package/dist/{file-system-utils-Bs7lpeVP.d.ts → file-system-utils-DOajnMm-.d.ts} +0 -0
- /package/dist/{format-file-size-BFUWvir1.d.ts → format-file-size-Dc98ulzB.d.ts} +0 -0
- /package/dist/{frame-loop-BEO9_q3k.d.ts → frame-loop-CpVjQ1dH.d.ts} +0 -0
- /package/dist/{frustum-visibility-D_RGmH98.d.ts → frustum-visibility-CeoCZchO.d.ts} +0 -0
- /package/dist/{geo-types-DqiBzPO9.d.ts → geo-types-3Ip1NzVR.d.ts} +0 -0
- /package/dist/{gps-C5inWeNV.d.ts → gps-BBO3hKfT.d.ts} +0 -0
- /package/dist/{gps-compass-cubes-BExxrWJ-.d.ts → gps-compass-cubes-DSN87L8p.d.ts} +0 -0
- /package/dist/{gps-error-handler-C2bS69oJ.d.ts → gps-error-handler-BbPsfw-_.d.ts} +0 -0
- /package/dist/{gps-event-markers-Dd0G2bD0.d.ts → gps-event-markers-Cz9RMA2k.d.ts} +0 -0
- /package/dist/{h3-proximity-B57C6rYL.d.ts → h3-proximity-C-j0pw1j.d.ts} +0 -0
- /package/dist/{hit-test-reticle-Dqak9hiR.d.ts → hit-test-reticle-DV-2TpB9.d.ts} +0 -0
- /package/dist/{lerp-utils-kLxMJ2Xj.d.ts → lerp-utils-BLmHhkVe.d.ts} +0 -0
- /package/dist/{list-formatter-ct8txwvR.d.ts → list-formatter-KNMS0ay2.d.ts} +0 -0
- /package/dist/{logger-CzQrCe6C.d.ts → logger-CLnPrHiD.d.ts} +0 -0
- /package/dist/{map-overlay-Dci4yvm3.d.ts → map-overlay-Dh2B97MP.d.ts} +0 -0
- /package/dist/{opfs-storage-DMtlt57S.d.ts → opfs-storage-3VZh-qH6.d.ts} +0 -0
- /package/dist/{permission-checker-CtPtPQRq.d.ts → permission-checker-CDuI8BwC.d.ts} +0 -0
- /package/dist/{qr-pose-Bt2reSw7.d.ts → qr-pose-D2rfu5DA.d.ts} +0 -0
- /package/dist/{scene-node-names-CdNeQjOE.d.ts → scene-node-names-BiShOQDB.d.ts} +0 -0
- /package/dist/{session-disposers-D8wnO-uW.js → session-disposers-zv9OGXPB.js} +0 -0
- /package/dist/{three-dispose-BCZkSJp2.d.ts → three-dispose-Bt1k7WKh.d.ts} +0 -0
- /package/dist/{vis-colors-BZsSLI4i.d.ts → vis-colors-Obw2pLqT.d.ts} +0 -0
- /package/dist/{webxr-nue-basis-CW1wsbm6.d.ts → webxr-nue-basis-3nfjDJJp.d.ts} +0 -0
- /package/dist/{xr-camera-texture-BmrP7iNZ.d.ts → xr-camera-texture-Cva1q3Qj.d.ts} +0 -0
- /package/dist/{xr-error-handler-BEjRgZjt.d.ts → xr-error-handler-CtvHpjfm.d.ts} +0 -0
- /package/dist/{xr-frame-loop-K8ebdjLw.d.ts → xr-frame-loop-DIYtRcUa.d.ts} +0 -0
- /package/dist/{zip-coverage-embed-tFohI2J4.d.ts → zip-coverage-embed-DCAREVmw.d.ts} +0 -0
- /package/dist/{zip-export-DDWXInTR.d.ts → zip-export-ol0S_Ofd.d.ts} +0 -0
- /package/dist/{zip-reader-Q2ue6zlC.d.ts → zip-reader-D9iPQwdQ.d.ts} +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { createLogger } from "../utils/logger.js";
|
|
2
|
+
import { t as DEFAULT_MOTION_FILTER } from "../capture-motion-gate-4EscNZCw.js";
|
|
3
|
+
import { DEFAULT_QUALITY_FILTER } from "../ar/image-quality.js";
|
|
2
4
|
//#region ../src/state/recording-options.ts
|
|
3
5
|
/**
|
|
4
6
|
* Recording Options - User-configurable settings for data capture.
|
|
@@ -31,7 +33,9 @@ const DEFAULT_RECORDING_OPTIONS = {
|
|
|
31
33
|
enabled: true,
|
|
32
34
|
intervalMs: 2e3,
|
|
33
35
|
quality: .7,
|
|
34
|
-
resolutionDivisor: 1
|
|
36
|
+
resolutionDivisor: 1,
|
|
37
|
+
motionFilter: { ...DEFAULT_MOTION_FILTER },
|
|
38
|
+
qualityFilter: { ...DEFAULT_QUALITY_FILTER }
|
|
35
39
|
},
|
|
36
40
|
arCrashIsolation: {
|
|
37
41
|
enableDomOverlay: true,
|
|
@@ -41,7 +45,10 @@ const DEFAULT_RECORDING_OPTIONS = {
|
|
|
41
45
|
enableCameraTextureAcquisition: true,
|
|
42
46
|
applyChromiumProjectionLayerWorkaround: true
|
|
43
47
|
},
|
|
44
|
-
occupancy: {
|
|
48
|
+
occupancy: {
|
|
49
|
+
cellSizeM: .15,
|
|
50
|
+
minConfidence: 3
|
|
51
|
+
},
|
|
45
52
|
frameTileDisplay: { divisor: 2 },
|
|
46
53
|
visualization: {
|
|
47
54
|
frameTiles: true,
|
|
@@ -53,6 +60,11 @@ const DEFAULT_RECORDING_OPTIONS = {
|
|
|
53
60
|
enabled: false,
|
|
54
61
|
intervalMs: 125,
|
|
55
62
|
captureSize: 1024
|
|
63
|
+
},
|
|
64
|
+
compassDebug: {
|
|
65
|
+
coldStartOverride: true,
|
|
66
|
+
rotationPrior: false,
|
|
67
|
+
webXRConsistency: false
|
|
56
68
|
}
|
|
57
69
|
};
|
|
58
70
|
/** Validation constraints for depth options */
|
|
@@ -87,6 +99,65 @@ const IMAGE_CONSTRAINTS = {
|
|
|
87
99
|
}
|
|
88
100
|
};
|
|
89
101
|
/**
|
|
102
|
+
* Validation constraints for the motion-filter (blurry-frame gate) thresholds.
|
|
103
|
+
*
|
|
104
|
+
* The velocity ranges (0.05–5) bracket the plausible scanning regime: below
|
|
105
|
+
* ~0.05 the gate would reject almost everything; above ~5 rad/s ≈ 286°/s (or
|
|
106
|
+
* 5 m/s) it would never reject, so the gate would be inert. `maxWaitMs` is
|
|
107
|
+
* clamped to 0.5–20 s — the never-calm fallback must always be able to fire.
|
|
108
|
+
* All three back a (currently advanced/hidden) settings slider, so a corrupt
|
|
109
|
+
* stored value can never disable capture. The default thresholds themselves are
|
|
110
|
+
* placeholders pending on-device field tuning (plan §7).
|
|
111
|
+
*/
|
|
112
|
+
const MOTION_FILTER_CONSTRAINTS = {
|
|
113
|
+
maxAngularVelocity: {
|
|
114
|
+
min: .05,
|
|
115
|
+
max: 5,
|
|
116
|
+
step: .05
|
|
117
|
+
},
|
|
118
|
+
maxLinearVelocity: {
|
|
119
|
+
min: .05,
|
|
120
|
+
max: 5,
|
|
121
|
+
step: .05
|
|
122
|
+
},
|
|
123
|
+
maxWaitMs: {
|
|
124
|
+
min: 500,
|
|
125
|
+
max: 2e4,
|
|
126
|
+
step: 500
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Validation constraints for the image-quality (blur/blackness) gate thresholds.
|
|
131
|
+
*
|
|
132
|
+
* `blurRelativeThreshold` (`k` in `sharpness < k·median`) is clamped to
|
|
133
|
+
* 0.05–0.95: it is a fraction of the recent sharpness median, so values must sit
|
|
134
|
+
* strictly inside (0, 1) — at ~0 the blur check never rejects, near 1 it rejects
|
|
135
|
+
* almost everything. `minMeanLuminance` (the absolute black cutoff on a 0–255
|
|
136
|
+
* luma scale) is clamped to 0–128: 0 disables the black check, and a cutoff
|
|
137
|
+
* above mid-grey would reject normally-lit frames. `maxWaitMs` mirrors the motion
|
|
138
|
+
* gate's range (0.5–20 s) so the never-good fallback can always fire. All back a
|
|
139
|
+
* (currently advanced/hidden) settings slider, so a corrupt stored value can
|
|
140
|
+
* never disable capture. The default thresholds are placeholders pending
|
|
141
|
+
* on-device field tuning (plan §5, §10).
|
|
142
|
+
*/
|
|
143
|
+
const QUALITY_FILTER_CONSTRAINTS = {
|
|
144
|
+
blurRelativeThreshold: {
|
|
145
|
+
min: .05,
|
|
146
|
+
max: .95,
|
|
147
|
+
step: .05
|
|
148
|
+
},
|
|
149
|
+
minMeanLuminance: {
|
|
150
|
+
min: 0,
|
|
151
|
+
max: 128,
|
|
152
|
+
step: 1
|
|
153
|
+
},
|
|
154
|
+
maxWaitMs: {
|
|
155
|
+
min: 500,
|
|
156
|
+
max: 2e4,
|
|
157
|
+
step: 500
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
90
161
|
* Validation constraints for occupancy options.
|
|
91
162
|
*
|
|
92
163
|
* `cellSizeM` is clamped to 1–20 cm. The floor exists because cell count (and
|
|
@@ -94,12 +165,24 @@ const IMAGE_CONSTRAINTS = {
|
|
|
94
165
|
* `points3D` row count) scales as 1/cellSize³ — sub-centimetre voxels are both
|
|
95
166
|
* a memory/perf cliff on a phone and below the depth sensor's noise floor.
|
|
96
167
|
* Step is 1 cm (the settings slider operates in cm).
|
|
168
|
+
*
|
|
169
|
+
* `minConfidence` is clamped to 1–10 (integer). 1 disables the filter (legacy
|
|
170
|
+
* behaviour: a single observation counts as occupied); the ceiling exists
|
|
171
|
+
* because real surfaces accumulate only a handful of observations per second
|
|
172
|
+
* of dwell, so a floor above ~10 would start hiding genuine geometry.
|
|
97
173
|
*/
|
|
98
|
-
const OCCUPANCY_CONSTRAINTS = {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
174
|
+
const OCCUPANCY_CONSTRAINTS = {
|
|
175
|
+
cellSizeM: {
|
|
176
|
+
min: .01,
|
|
177
|
+
max: .2,
|
|
178
|
+
step: .01
|
|
179
|
+
},
|
|
180
|
+
minConfidence: {
|
|
181
|
+
min: 1,
|
|
182
|
+
max: 10,
|
|
183
|
+
step: 1
|
|
184
|
+
}
|
|
185
|
+
};
|
|
103
186
|
/**
|
|
104
187
|
* Validation constraints for the frame-tile display-resolution divisor.
|
|
105
188
|
*
|
|
@@ -150,6 +233,19 @@ function validateArCrashIsolationOptions(options) {
|
|
|
150
233
|
};
|
|
151
234
|
}
|
|
152
235
|
/**
|
|
236
|
+
* Validate and normalize the compass alignment debug toggles. Boolean-or-default
|
|
237
|
+
* per field; a missing/corrupted/pre-feature value falls back to the OFF default
|
|
238
|
+
* so a bad persisted value can never silently turn an alignment override ON.
|
|
239
|
+
*/
|
|
240
|
+
function validateCompassDebugOptions(options) {
|
|
241
|
+
const defaults = DEFAULT_RECORDING_OPTIONS.compassDebug;
|
|
242
|
+
return {
|
|
243
|
+
coldStartOverride: typeof options.coldStartOverride === "boolean" ? options.coldStartOverride : defaults.coldStartOverride,
|
|
244
|
+
rotationPrior: typeof options.rotationPrior === "boolean" ? options.rotationPrior : defaults.rotationPrior,
|
|
245
|
+
webXRConsistency: typeof options.webXRConsistency === "boolean" ? options.webXRConsistency : defaults.webXRConsistency
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
153
249
|
* Validate and normalize the live debug-overlay visibility toggles.
|
|
154
250
|
* Each field is boolean-or-default (same policy as the AR-crash-isolation
|
|
155
251
|
* flags): a missing, corrupted, or pre-feature persisted value falls back to
|
|
@@ -200,6 +296,41 @@ function validateDepthOptions(options) {
|
|
|
200
296
|
};
|
|
201
297
|
}
|
|
202
298
|
/**
|
|
299
|
+
* Validate and normalize the motion-filter (blurry-frame gate) options.
|
|
300
|
+
* `enabled` is boolean-or-default; the three numeric thresholds are clamped to
|
|
301
|
+
* {@link MOTION_FILTER_CONSTRAINTS} with a `Number.isFinite` guard (a stored
|
|
302
|
+
* `NaN` is `typeof 'number'` and would survive `clamp`). A missing group
|
|
303
|
+
* default-fills entirely — a pre-feature persisted options object that lacks
|
|
304
|
+
* `motionFilter` therefore loads with the gate enabled rather than crashing.
|
|
305
|
+
*/
|
|
306
|
+
function validateMotionFilterOptions(options) {
|
|
307
|
+
const defaults = DEFAULT_RECORDING_OPTIONS.images.motionFilter;
|
|
308
|
+
return {
|
|
309
|
+
enabled: typeof options.enabled === "boolean" ? options.enabled : defaults.enabled,
|
|
310
|
+
maxAngularVelocity: clamp(typeof options.maxAngularVelocity === "number" && Number.isFinite(options.maxAngularVelocity) ? options.maxAngularVelocity : defaults.maxAngularVelocity, MOTION_FILTER_CONSTRAINTS.maxAngularVelocity.min, MOTION_FILTER_CONSTRAINTS.maxAngularVelocity.max),
|
|
311
|
+
maxLinearVelocity: clamp(typeof options.maxLinearVelocity === "number" && Number.isFinite(options.maxLinearVelocity) ? options.maxLinearVelocity : defaults.maxLinearVelocity, MOTION_FILTER_CONSTRAINTS.maxLinearVelocity.min, MOTION_FILTER_CONSTRAINTS.maxLinearVelocity.max),
|
|
312
|
+
maxWaitMs: clamp(typeof options.maxWaitMs === "number" && Number.isFinite(options.maxWaitMs) ? options.maxWaitMs : defaults.maxWaitMs, MOTION_FILTER_CONSTRAINTS.maxWaitMs.min, MOTION_FILTER_CONSTRAINTS.maxWaitMs.max)
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Validate and normalize the image-quality (blur/blackness gate) options. Same
|
|
317
|
+
* policy as {@link validateMotionFilterOptions}: `enabled` is boolean-or-default;
|
|
318
|
+
* the three numeric thresholds are clamped to {@link QUALITY_FILTER_CONSTRAINTS}
|
|
319
|
+
* with a `Number.isFinite` guard (a stored `NaN` is `typeof 'number'` and would
|
|
320
|
+
* survive `clamp`). A missing group default-fills entirely — a pre-feature
|
|
321
|
+
* persisted options object that lacks `qualityFilter` loads with the gate
|
|
322
|
+
* disabled (the safe default) rather than crashing.
|
|
323
|
+
*/
|
|
324
|
+
function validateQualityFilterOptions(options) {
|
|
325
|
+
const defaults = DEFAULT_RECORDING_OPTIONS.images.qualityFilter;
|
|
326
|
+
return {
|
|
327
|
+
enabled: typeof options.enabled === "boolean" ? options.enabled : defaults.enabled,
|
|
328
|
+
blurRelativeThreshold: clamp(typeof options.blurRelativeThreshold === "number" && Number.isFinite(options.blurRelativeThreshold) ? options.blurRelativeThreshold : defaults.blurRelativeThreshold, QUALITY_FILTER_CONSTRAINTS.blurRelativeThreshold.min, QUALITY_FILTER_CONSTRAINTS.blurRelativeThreshold.max),
|
|
329
|
+
minMeanLuminance: clamp(typeof options.minMeanLuminance === "number" && Number.isFinite(options.minMeanLuminance) ? options.minMeanLuminance : defaults.minMeanLuminance, QUALITY_FILTER_CONSTRAINTS.minMeanLuminance.min, QUALITY_FILTER_CONSTRAINTS.minMeanLuminance.max),
|
|
330
|
+
maxWaitMs: clamp(typeof options.maxWaitMs === "number" && Number.isFinite(options.maxWaitMs) ? options.maxWaitMs : defaults.maxWaitMs, QUALITY_FILTER_CONSTRAINTS.maxWaitMs.min, QUALITY_FILTER_CONSTRAINTS.maxWaitMs.max)
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
203
334
|
* Validate and normalize image options.
|
|
204
335
|
* Invalid values are clamped to valid ranges.
|
|
205
336
|
*/
|
|
@@ -209,7 +340,9 @@ function validateImageOptions(options) {
|
|
|
209
340
|
enabled: typeof options.enabled === "boolean" ? options.enabled : defaults.enabled,
|
|
210
341
|
intervalMs: clamp(typeof options.intervalMs === "number" ? options.intervalMs : defaults.intervalMs, IMAGE_CONSTRAINTS.intervalMs.min, IMAGE_CONSTRAINTS.intervalMs.max),
|
|
211
342
|
quality: clamp(typeof options.quality === "number" ? options.quality : defaults.quality, IMAGE_CONSTRAINTS.quality.min, IMAGE_CONSTRAINTS.quality.max),
|
|
212
|
-
resolutionDivisor: clamp(typeof options.resolutionDivisor === "number" ? options.resolutionDivisor : defaults.resolutionDivisor, IMAGE_CONSTRAINTS.resolutionDivisor.min, IMAGE_CONSTRAINTS.resolutionDivisor.max)
|
|
343
|
+
resolutionDivisor: clamp(typeof options.resolutionDivisor === "number" ? options.resolutionDivisor : defaults.resolutionDivisor, IMAGE_CONSTRAINTS.resolutionDivisor.min, IMAGE_CONSTRAINTS.resolutionDivisor.max),
|
|
344
|
+
motionFilter: validateMotionFilterOptions(options.motionFilter ?? {}),
|
|
345
|
+
qualityFilter: validateQualityFilterOptions(options.qualityFilter ?? {})
|
|
213
346
|
};
|
|
214
347
|
}
|
|
215
348
|
/**
|
|
@@ -223,7 +356,10 @@ function validateImageOptions(options) {
|
|
|
223
356
|
*/
|
|
224
357
|
function validateOccupancyOptions(options) {
|
|
225
358
|
const defaults = DEFAULT_RECORDING_OPTIONS.occupancy;
|
|
226
|
-
return {
|
|
359
|
+
return {
|
|
360
|
+
cellSizeM: clamp(typeof options.cellSizeM === "number" && Number.isFinite(options.cellSizeM) ? options.cellSizeM : defaults.cellSizeM, OCCUPANCY_CONSTRAINTS.cellSizeM.min, OCCUPANCY_CONSTRAINTS.cellSizeM.max),
|
|
361
|
+
minConfidence: clamp(typeof options.minConfidence === "number" && Number.isFinite(options.minConfidence) ? Math.round(options.minConfidence) : defaults.minConfidence, OCCUPANCY_CONSTRAINTS.minConfidence.min, OCCUPANCY_CONSTRAINTS.minConfidence.max)
|
|
362
|
+
};
|
|
227
363
|
}
|
|
228
364
|
/**
|
|
229
365
|
* Validate and normalize frame-tile display options. `divisor` is clamped to
|
|
@@ -248,7 +384,8 @@ function validateRecordingOptions(options) {
|
|
|
248
384
|
occupancy: validateOccupancyOptions(options.occupancy ?? {}),
|
|
249
385
|
frameTileDisplay: validateFrameTileDisplayOptions(options.frameTileDisplay ?? {}),
|
|
250
386
|
visualization: validateVisualizationOptions(options.visualization ?? {}),
|
|
251
|
-
qr: validateQrOptions(options.qr ?? {})
|
|
387
|
+
qr: validateQrOptions(options.qr ?? {}),
|
|
388
|
+
compassDebug: validateCompassDebugOptions(options.compassDebug ?? {})
|
|
252
389
|
};
|
|
253
390
|
}
|
|
254
391
|
/**
|
|
@@ -306,13 +443,18 @@ function resetRecordingOptions(storageKey = STORAGE_KEY) {
|
|
|
306
443
|
function cloneRecordingOptions(options) {
|
|
307
444
|
return {
|
|
308
445
|
depth: { ...options.depth },
|
|
309
|
-
images: {
|
|
446
|
+
images: {
|
|
447
|
+
...options.images,
|
|
448
|
+
motionFilter: { ...options.images.motionFilter },
|
|
449
|
+
qualityFilter: { ...options.images.qualityFilter }
|
|
450
|
+
},
|
|
310
451
|
arCrashIsolation: { ...options.arCrashIsolation },
|
|
311
452
|
occupancy: { ...options.occupancy },
|
|
312
453
|
frameTileDisplay: { ...options.frameTileDisplay },
|
|
313
454
|
visualization: { ...options.visualization },
|
|
314
|
-
qr: { ...options.qr }
|
|
455
|
+
qr: { ...options.qr },
|
|
456
|
+
compassDebug: { ...options.compassDebug }
|
|
315
457
|
};
|
|
316
458
|
}
|
|
317
459
|
//#endregion
|
|
318
|
-
export { DEFAULT_RECORDING_OPTIONS, DEPTH_CONSTRAINTS, FRAME_TILE_DISPLAY_CONSTRAINTS, IMAGE_CONSTRAINTS, OCCUPANCY_CONSTRAINTS, QR_CONSTRAINTS, STORAGE_KEY, cloneRecordingOptions, loadRecordingOptions, resetRecordingOptions, saveRecordingOptions, validateArCrashIsolationOptions, validateDepthOptions, validateFrameTileDisplayOptions, validateImageOptions, validateOccupancyOptions, validateQrOptions, validateRecordingOptions, validateVisualizationOptions };
|
|
460
|
+
export { DEFAULT_RECORDING_OPTIONS, DEPTH_CONSTRAINTS, FRAME_TILE_DISPLAY_CONSTRAINTS, IMAGE_CONSTRAINTS, MOTION_FILTER_CONSTRAINTS, OCCUPANCY_CONSTRAINTS, QR_CONSTRAINTS, QUALITY_FILTER_CONSTRAINTS, STORAGE_KEY, cloneRecordingOptions, loadRecordingOptions, resetRecordingOptions, saveRecordingOptions, validateArCrashIsolationOptions, validateCompassDebugOptions, validateDepthOptions, validateFrameTileDisplayOptions, validateImageOptions, validateMotionFilterOptions, validateOccupancyOptions, validateQrOptions, validateQualityFilterOptions, validateRecordingOptions, validateVisualizationOptions };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as replayRecording, t as ReplayRecordingOptions } from "../recording-replayer-
|
|
1
|
+
import { N as CombinedRootState } from "../create-slam-app-store-n_VF63D5.js";
|
|
2
|
+
import { n as replayRecording, t as ReplayRecordingOptions } from "../recording-replayer-DQKpAGyQ.js";
|
|
3
3
|
export { CombinedRootState, ReplayRecordingOptions, replayRecording };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadActionsFromZip } from "../storage/zip-reader.js";
|
|
2
2
|
import { NullStorageBackend } from "../storage/null-storage-backend.js";
|
|
3
|
-
import { createSlamAppStore } from "
|
|
3
|
+
import { t as createSlamAppStore } from "../create-slam-app-store-DkCHOpvb.js";
|
|
4
4
|
//#region ../src/state/recording-replayer.ts
|
|
5
5
|
/**
|
|
6
6
|
* Recording Replayer
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as recordWriteFailure, i as recordDepthSample, n as SessionMetadata, o as recordingReducer, r as endSession, s as startSession, t as RecordingState } from "../recording-slice-
|
|
1
|
+
import { a as recordWriteFailure, i as recordDepthSample, n as SessionMetadata, o as recordingReducer, r as endSession, s as startSession, t as RecordingState } from "../recording-slice-DaxHZvNu.js";
|
|
2
2
|
export { RecordingState, SessionMetadata, endSession, recordDepthSample, recordWriteFailure, recordingReducer, startSession };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as ReplayAction, c as computeInterActionDelay, i as ProgressCallback, l as extractActionTimestamp, n as DEFAULT_MAX_DELAY_MS, o as ReplayEngine, r as ErrorCallback, s as ReplayState, t as CompleteCallback } from "../replay-engine-
|
|
1
|
+
import { a as ReplayAction, c as computeInterActionDelay, i as ProgressCallback, l as extractActionTimestamp, n as DEFAULT_MAX_DELAY_MS, o as ReplayEngine, r as ErrorCallback, s as ReplayState, t as CompleteCallback } from "../replay-engine-CmKh_4wf.js";
|
|
2
2
|
export { CompleteCallback, DEFAULT_MAX_DELAY_MS, ErrorCallback, ProgressCallback, ReplayAction, ReplayEngine, ReplayState, computeInterActionDelay, extractActionTimestamp };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as SubscribableStore } from "../subscribe-to-selector-
|
|
2
|
-
import { n as wireStoreSubscribers, t as StoreSubscriberDeps } from "../store-subscribers-
|
|
1
|
+
import { t as SubscribableStore } from "../subscribe-to-selector-B6EUmQ3I.js";
|
|
2
|
+
import { n as wireStoreSubscribers, t as StoreSubscriberDeps } from "../store-subscribers-CA6AJH_8.js";
|
|
3
3
|
export { StoreSubscriberDeps, SubscribableStore, wireStoreSubscribers };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as subscribeToSelector, t as SubscribableStore } from "../subscribe-to-selector-
|
|
1
|
+
import { n as subscribeToSelector, t as SubscribableStore } from "../subscribe-to-selector-B6EUmQ3I.js";
|
|
2
2
|
export { SubscribableStore, subscribeToSelector };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { AlignmentSnapshot,
|
|
1
|
+
import { A as snapshotPushed, C as createTrackingQualityListenerMiddleware, D as resetTrackingQuality, E as reportUpdated, M as trackingQualityReducer, O as selectRecentAlignments, S as computeTrackingQualityReport, T as matrixDelta, _ as computeConvergence, b as computeGpsVsFusedDivergence, c as ConvergenceResult, d as GpsAccuracyResult, f as ResidualConsensusResult, g as TrackingQualityState, h as TrackingQualitySliceState, j as snapshotsTrimmed, k as selectTrackingQuality, l as CoverageResult, m as TrackingQualityReport, p as TrackingQualityOptions, s as AlignmentSnapshot, u as DEFAULT_TRACKING_QUALITY_OPTIONS, v as computeCoverage, w as degradedCountUpdated, x as computeResidualConsensus, y as computeGpsAccuracy } from "../create-slam-app-store-n_VF63D5.js";
|
|
2
|
+
export { AlignmentSnapshot, ConvergenceResult, CoverageResult, DEFAULT_TRACKING_QUALITY_OPTIONS, GpsAccuracyResult, ResidualConsensusResult, TrackingQualityOptions, TrackingQualityReport, TrackingQualitySliceState, TrackingQualityState, computeConvergence, computeCoverage, computeGpsAccuracy, computeGpsVsFusedDivergence, computeResidualConsensus, computeTrackingQualityReport, createTrackingQualityListenerMiddleware, degradedCountUpdated, matrixDelta, reportUpdated, resetTrackingQuality, selectRecentAlignments, selectTrackingQuality, snapshotPushed, snapshotsTrimmed, trackingQualityReducer };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as geodesicAngleRad } from "../geodesic-angle-BTldfFgs.js";
|
|
2
|
+
import { selectTrackingPhase } from "./tracking-slice.js";
|
|
2
3
|
import { selectAlignmentMatrix, selectGpsPositions, selectOdometryPositions, selectZeroReference } from "./app-selectors.js";
|
|
3
4
|
import { calcGpsCoords, distanceInMeters } from "gps-plus-slam-js";
|
|
4
5
|
import { mat4, quat, vec3 } from "gl-matrix";
|
|
@@ -13,19 +14,15 @@ const DEFAULT_TRACKING_QUALITY_OPTIONS = {
|
|
|
13
14
|
coverageDirectionSpreadDeg: 90,
|
|
14
15
|
convergenceRotationWarnDeg: 6,
|
|
15
16
|
convergenceTranslationWarnM: 2,
|
|
16
|
-
compassWarnDeg: 15,
|
|
17
|
-
compassFailDeg: 35,
|
|
18
17
|
warmupMinObservations: 10,
|
|
19
18
|
warmupMinCoverage: .5,
|
|
20
19
|
residualConfidenceTargetM: 3,
|
|
21
20
|
gpsAccuracyFloorM: 1,
|
|
22
|
-
firstAgreementMinStreak: 3,
|
|
23
21
|
degradedHoldoff: 3,
|
|
24
22
|
convergenceEmaAlpha: .3
|
|
25
23
|
};
|
|
26
24
|
const initialState = {
|
|
27
25
|
recentAlignments: [],
|
|
28
|
-
firstAgreementObservationIndex: null,
|
|
29
26
|
report: null,
|
|
30
27
|
degradedConsecutiveCount: 0,
|
|
31
28
|
smoothedConvergence: null
|
|
@@ -47,11 +44,6 @@ function emaBlend(prev, raw, alpha) {
|
|
|
47
44
|
if (prev === null) return raw;
|
|
48
45
|
return prev + (Number.isFinite(alpha) ? alpha : 1) * (raw - prev);
|
|
49
46
|
}
|
|
50
|
-
function wrapToHalfCircle(deg) {
|
|
51
|
-
let d = (deg + 180) % 360 - 180;
|
|
52
|
-
if (d < -180) d += 360;
|
|
53
|
-
return d;
|
|
54
|
-
}
|
|
55
47
|
function bearingDeg(north, east) {
|
|
56
48
|
let deg = Math.atan2(east, north) * 180 / Math.PI;
|
|
57
49
|
if (deg < 0) deg += 360;
|
|
@@ -87,6 +79,11 @@ function rampUp(value, low, high) {
|
|
|
87
79
|
if (value >= high) return 1;
|
|
88
80
|
return (value - low) / (high - low);
|
|
89
81
|
}
|
|
82
|
+
/** True iff every element of the matrix is a finite number (no NaN/Infinity). */
|
|
83
|
+
function isFiniteMatrix(m) {
|
|
84
|
+
for (let i = 0; i < m.length; i++) if (!Number.isFinite(m[i])) return false;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
90
87
|
/**
|
|
91
88
|
* §4.1 matrix-history convergence over the buffered snapshots.
|
|
92
89
|
*
|
|
@@ -118,7 +115,15 @@ function computeConvergence(snapshots, options = {}) {
|
|
|
118
115
|
let sumTransM = 0;
|
|
119
116
|
let pairCount = 0;
|
|
120
117
|
for (let i = 1; i < snapshots.length; i++) {
|
|
121
|
-
const
|
|
118
|
+
const prevM = snapshots[i - 1].matrix;
|
|
119
|
+
const currM = snapshots[i].matrix;
|
|
120
|
+
if (!isFiniteMatrix(prevM) || !isFiniteMatrix(currM)) {
|
|
121
|
+
sumRotDeg += rotFail;
|
|
122
|
+
sumTransM += transFail;
|
|
123
|
+
pairCount += 1;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const { rotationDeltaDeg, translationDeltaM } = matrixDelta(prevM, currM);
|
|
122
127
|
sumRotDeg += Math.abs(rotationDeltaDeg);
|
|
123
128
|
sumTransM += Math.abs(translationDeltaM);
|
|
124
129
|
pairCount += 1;
|
|
@@ -137,7 +142,8 @@ function computeConvergence(snapshots, options = {}) {
|
|
|
137
142
|
* the column-major layout that gl-matrix and the framework use.
|
|
138
143
|
*
|
|
139
144
|
* Rotation: extract the orientation quaternion with `mat4.getRotation`
|
|
140
|
-
* and compute the relative angle via `
|
|
145
|
+
* and compute the relative angle via the shared `geodesicAngleRad` kernel
|
|
146
|
+
* (radians, converted to degrees here).
|
|
141
147
|
* Translation: extract the origin column with `mat4.getTranslation` and
|
|
142
148
|
* compute the Euclidean distance.
|
|
143
149
|
*
|
|
@@ -161,15 +167,16 @@ function matrixDelta(prev, curr) {
|
|
|
161
167
|
mat4.getRotation(currQuat, currMat);
|
|
162
168
|
quat.normalize(prevQuat, prevQuat);
|
|
163
169
|
quat.normalize(currQuat, currQuat);
|
|
164
|
-
const angleRad =
|
|
165
|
-
const rotationDeltaDeg = Number.
|
|
170
|
+
const angleRad = geodesicAngleRad(prevQuat, currQuat);
|
|
171
|
+
const rotationDeltaDeg = Number.isFinite(angleRad) ? angleRad * 180 / Math.PI : 0;
|
|
166
172
|
const prevT = vec3.create();
|
|
167
173
|
const currT = vec3.create();
|
|
168
174
|
mat4.getTranslation(prevT, prevMat);
|
|
169
175
|
mat4.getTranslation(currT, currMat);
|
|
176
|
+
const rawTranslation = vec3.distance(prevT, currT);
|
|
170
177
|
return {
|
|
171
178
|
rotationDeltaDeg,
|
|
172
|
-
translationDeltaM:
|
|
179
|
+
translationDeltaM: Number.isFinite(rawTranslation) ? rawTranslation : 0
|
|
173
180
|
};
|
|
174
181
|
}
|
|
175
182
|
/**
|
|
@@ -319,56 +326,6 @@ function computeCoverage(odometryPositions, options = {}) {
|
|
|
319
326
|
};
|
|
320
327
|
}
|
|
321
328
|
/**
|
|
322
|
-
* §4.3 — Compare the alignment matrix's mapping of AR-forward to ENU
|
|
323
|
-
* against the device compass heading. Returns `score = null` whenever
|
|
324
|
-
* the sensor's `absolute !== true` so the aggregate excludes the
|
|
325
|
-
* sub-score (per Q2 in the plan).
|
|
326
|
-
*
|
|
327
|
-
* AR-forward in our `ARPose` convention is `(0, 0, -1)` (right-handed,
|
|
328
|
-
* Y-up). We rotate that by the alignment's 3×3 rotation, take its
|
|
329
|
-
* North/East components, and compare against the compass alpha.
|
|
330
|
-
*/
|
|
331
|
-
function computeCompassAgreement(alignmentMatrix, sensorOrientation, arPose, options = {}) {
|
|
332
|
-
const warn = options.warnDeg ?? DEFAULT_TRACKING_QUALITY_OPTIONS.compassWarnDeg;
|
|
333
|
-
const fail = options.failDeg ?? DEFAULT_TRACKING_QUALITY_OPTIONS.compassFailDeg;
|
|
334
|
-
if (!alignmentMatrix || !sensorOrientation || !arPose) return {
|
|
335
|
-
score: null,
|
|
336
|
-
headingDeltaDeg: null
|
|
337
|
-
};
|
|
338
|
-
if (sensorOrientation.absolute !== true) return {
|
|
339
|
-
score: null,
|
|
340
|
-
headingDeltaDeg: null
|
|
341
|
-
};
|
|
342
|
-
const q = arPose.orientation;
|
|
343
|
-
const fx = 0;
|
|
344
|
-
const fy = 0;
|
|
345
|
-
const fz = -1;
|
|
346
|
-
const x = q.x, y = q.y, z = q.z, w = q.w;
|
|
347
|
-
const tx = 2 * (y * fz - z * fy);
|
|
348
|
-
const ty = 2 * (z * fx - x * fz);
|
|
349
|
-
const tz = 2 * (x * fy - y * fx);
|
|
350
|
-
const arForwardLocal = [
|
|
351
|
-
fx + w * tx + (y * tz - z * ty),
|
|
352
|
-
fy + w * ty + (z * tx - x * tz),
|
|
353
|
-
fz + w * tz + (x * ty - y * tx)
|
|
354
|
-
];
|
|
355
|
-
const m = alignmentMatrix;
|
|
356
|
-
const enuN = m[0] * arForwardLocal[0] + m[4] * arForwardLocal[1] + m[8] * arForwardLocal[2];
|
|
357
|
-
m[1] * arForwardLocal[0] + m[5] * arForwardLocal[1] + m[9] * arForwardLocal[2];
|
|
358
|
-
const enuE = m[2] * arForwardLocal[0] + m[6] * arForwardLocal[1] + m[10] * arForwardLocal[2];
|
|
359
|
-
if (Math.hypot(enuN, enuE) < 1e-6) return {
|
|
360
|
-
score: null,
|
|
361
|
-
headingDeltaDeg: null
|
|
362
|
-
};
|
|
363
|
-
const alignmentHeadingDeg = bearingDeg(enuN, enuE);
|
|
364
|
-
const compassHeadingDeg = sensorOrientation.alpha;
|
|
365
|
-
const delta = Math.abs(wrapToHalfCircle(alignmentHeadingDeg - compassHeadingDeg));
|
|
366
|
-
return {
|
|
367
|
-
score: rampDown(delta, warn, fail),
|
|
368
|
-
headingDeltaDeg: delta
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
329
|
* §4.6 — Maximum lateral distance between raw GPS positions and the
|
|
373
330
|
* positions you would predict by transforming the matching odometry
|
|
374
331
|
* positions through the current alignment. Diagnostic only — never
|
|
@@ -418,9 +375,6 @@ const trackingQualitySlice = createSlice({
|
|
|
418
375
|
reportUpdated(state, action) {
|
|
419
376
|
state.report = action.payload;
|
|
420
377
|
},
|
|
421
|
-
firstAgreementReached(state, action) {
|
|
422
|
-
state.firstAgreementObservationIndex = action.payload;
|
|
423
|
-
},
|
|
424
378
|
/** §4.8 — update the hysteresis counter for ok → degraded holdoff. */
|
|
425
379
|
degradedCountUpdated(state, action) {
|
|
426
380
|
state.degradedConsecutiveCount = action.payload;
|
|
@@ -439,7 +393,7 @@ const trackingQualitySlice = createSlice({
|
|
|
439
393
|
}
|
|
440
394
|
}
|
|
441
395
|
});
|
|
442
|
-
const { snapshotPushed, snapshotsTrimmed, reportUpdated,
|
|
396
|
+
const { snapshotPushed, snapshotsTrimmed, reportUpdated, degradedCountUpdated, resetTrackingQuality } = trackingQualitySlice.actions;
|
|
443
397
|
const { smoothedConvergenceUpdated } = trackingQualitySlice.actions;
|
|
444
398
|
const trackingQualityReducer = trackingQualitySlice.reducer;
|
|
445
399
|
function selectTrackingQuality(state) {
|
|
@@ -448,9 +402,6 @@ function selectTrackingQuality(state) {
|
|
|
448
402
|
function selectRecentAlignments(state) {
|
|
449
403
|
return state.trackingQuality?.recentAlignments ?? EMPTY_SNAPSHOTS;
|
|
450
404
|
}
|
|
451
|
-
function selectFirstAgreementObservationIndex(state) {
|
|
452
|
-
return state.trackingQuality?.firstAgreementObservationIndex ?? null;
|
|
453
|
-
}
|
|
454
405
|
const EMPTY_SNAPSHOTS = Object.freeze([]);
|
|
455
406
|
/**
|
|
456
407
|
* Pure function — produces a {@link TrackingQualityReport} from a root
|
|
@@ -474,10 +425,7 @@ function computeTrackingQualityReport(rootState, options = {}) {
|
|
|
474
425
|
const odometryPositions = selectOdometryPositions(rootState);
|
|
475
426
|
const zeroRef = selectZeroReference(rootState);
|
|
476
427
|
const trackingPhase = selectTrackingPhase(rootState);
|
|
477
|
-
const sensorOrientation = selectLastSensorOrientation(rootState);
|
|
478
|
-
const lastPose = selectLastValidPose(rootState);
|
|
479
428
|
const snapshots = selectRecentAlignments(rootState);
|
|
480
|
-
const firstAgreementIdx = selectFirstAgreementObservationIndex(rootState);
|
|
481
429
|
const coverage = computeCoverage(odometryPositions, {
|
|
482
430
|
walkedDistanceM: opts.coverageWalkedDistanceM,
|
|
483
431
|
directionSpreadDeg: opts.coverageDirectionSpreadDeg
|
|
@@ -492,15 +440,10 @@ function computeTrackingQualityReport(rootState, options = {}) {
|
|
|
492
440
|
rotationWarnDeg: opts.convergenceRotationWarnDeg,
|
|
493
441
|
translationWarnM: opts.convergenceTranslationWarnM
|
|
494
442
|
});
|
|
495
|
-
const compass = computeCompassAgreement(alignmentMatrix, sensorOrientation, lastPose, {
|
|
496
|
-
warnDeg: opts.compassWarnDeg,
|
|
497
|
-
failDeg: opts.compassFailDeg
|
|
498
|
-
});
|
|
499
443
|
const gpsVsFusedMaxDivergenceM = computeGpsVsFusedDivergence(alignmentMatrix, gpsPositions, odometryPositions, zeroRef);
|
|
500
444
|
const subScores = {
|
|
501
445
|
convergence: clamp01(emaBlend(rootState.trackingQuality?.smoothedConvergence ?? null, convergence.score, opts.convergenceEmaAlpha)),
|
|
502
446
|
residualConsensus: clamp01(residual.score),
|
|
503
|
-
compassAgreement: compass.score === null ? null : clamp01(compass.score),
|
|
504
447
|
gpsAccuracy: clamp01(gpsAccuracy.score),
|
|
505
448
|
coverage: clamp01(coverage.score)
|
|
506
449
|
};
|
|
@@ -510,7 +453,6 @@ function computeTrackingQualityReport(rootState, options = {}) {
|
|
|
510
453
|
subScores.gpsAccuracy,
|
|
511
454
|
subScores.coverage
|
|
512
455
|
];
|
|
513
|
-
if (subScores.compassAgreement !== null) aggregateInputs.push(subScores.compassAgreement);
|
|
514
456
|
const confidence = clamp01(Math.min(...aggregateInputs));
|
|
515
457
|
const observationsSeen = gpsPositions.length;
|
|
516
458
|
let state;
|
|
@@ -529,8 +471,6 @@ function computeTrackingQualityReport(rootState, options = {}) {
|
|
|
529
471
|
medianRecentGpsAccuracyM: gpsAccuracy.medianM,
|
|
530
472
|
walkedDistanceM: coverage.walkedDistanceM,
|
|
531
473
|
directionSpreadDeg: coverage.directionSpreadDeg,
|
|
532
|
-
headingDeltaDeg: compass.headingDeltaDeg,
|
|
533
|
-
compassDriftDetected: firstAgreementIdx !== null && compass.headingDeltaDeg !== null && compass.headingDeltaDeg > opts.compassWarnDeg,
|
|
534
474
|
observationsSeen,
|
|
535
475
|
gpsVsFusedMaxDivergenceM
|
|
536
476
|
}
|
|
@@ -540,15 +480,14 @@ function computeTrackingQualityReport(rootState, options = {}) {
|
|
|
540
480
|
* Per-field tolerances for the {@link reportsEqual} dispatch gate.
|
|
541
481
|
*
|
|
542
482
|
* Why this exists: `tracking/poseReceived` fires on *every* XR frame
|
|
543
|
-
* (30–60 fps). The
|
|
544
|
-
*
|
|
545
|
-
*
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
* quality actually moved.
|
|
483
|
+
* (30–60 fps). The per-frame recompute reuses GPS/odometry windows that
|
|
484
|
+
* are unchanged between pose frames, so the float sub-scores and
|
|
485
|
+
* diagnostics (and `confidence`) jitter by imperceptible floating-point
|
|
486
|
+
* amounts on a held-still device. With a strict `!==` comparison every
|
|
487
|
+
* such frame produced a fresh `reportUpdated` dispatch → high Redux
|
|
488
|
+
* churn and a HUD re-render at frame rate. The tolerances below quantise
|
|
489
|
+
* those sub-perceptual changes so the gate only fires when the
|
|
490
|
+
* user-visible quality actually moved.
|
|
552
491
|
*
|
|
553
492
|
* The gate compares the freshly computed report against the *last
|
|
554
493
|
* dispatched* report (the stored `prev`), not against the previous
|
|
@@ -567,10 +506,9 @@ const REPORT_METRE_EPSILON_M = .001;
|
|
|
567
506
|
* (both `null` ⇒ equal; exactly one `null` ⇒ different) and otherwise
|
|
568
507
|
* compares with an absolute epsilon. Non-finite values are compared with
|
|
569
508
|
* {@link Object.is}, so a genuine finite↔NaN/Infinity transition still
|
|
570
|
-
* fires a dispatch, but a *persistently* NaN diagnostic
|
|
571
|
-
*
|
|
572
|
-
* `
|
|
573
|
-
* `Object.is(NaN, NaN)` is `true` whereas `NaN === NaN` is `false`.
|
|
509
|
+
* fires a dispatch, but a *persistently* NaN diagnostic does NOT churn
|
|
510
|
+
* the gate every frame — `Object.is(NaN, NaN)` is `true` whereas
|
|
511
|
+
* `NaN === NaN` is `false`.
|
|
574
512
|
*/
|
|
575
513
|
function nearlyEqual(a, b, eps) {
|
|
576
514
|
if (a === null || b === null) return a === b;
|
|
@@ -585,8 +523,8 @@ function nearlyEqual(a, b, eps) {
|
|
|
585
523
|
*
|
|
586
524
|
* Float fields use the per-field tolerances above
|
|
587
525
|
* ({@link REPORT_SCORE_EPSILON} et al.) so imperceptible per-frame
|
|
588
|
-
*
|
|
589
|
-
*
|
|
526
|
+
* jitter does not churn the store — see the constants' doc comment for
|
|
527
|
+
* the rationale.
|
|
590
528
|
*/
|
|
591
529
|
function reportsEqual(a, b) {
|
|
592
530
|
if (a === b) return true;
|
|
@@ -595,10 +533,10 @@ function reportsEqual(a, b) {
|
|
|
595
533
|
if (!nearlyEqual(a.confidence, b.confidence, REPORT_SCORE_EPSILON)) return false;
|
|
596
534
|
const sa = a.subScores;
|
|
597
535
|
const sb = b.subScores;
|
|
598
|
-
if (!nearlyEqual(sa.convergence, sb.convergence, REPORT_SCORE_EPSILON) || !nearlyEqual(sa.residualConsensus, sb.residualConsensus, REPORT_SCORE_EPSILON) || !nearlyEqual(sa.
|
|
536
|
+
if (!nearlyEqual(sa.convergence, sb.convergence, REPORT_SCORE_EPSILON) || !nearlyEqual(sa.residualConsensus, sb.residualConsensus, REPORT_SCORE_EPSILON) || !nearlyEqual(sa.gpsAccuracy, sb.gpsAccuracy, REPORT_SCORE_EPSILON) || !nearlyEqual(sa.coverage, sb.coverage, REPORT_SCORE_EPSILON)) return false;
|
|
599
537
|
const da = a.diagnostics;
|
|
600
538
|
const db = b.diagnostics;
|
|
601
|
-
return da.
|
|
539
|
+
return da.observationsSeen === db.observationsSeen && nearlyEqual(da.recentSumRotationDeltaDeg, db.recentSumRotationDeltaDeg, REPORT_ANGLE_EPSILON_DEG) && nearlyEqual(da.directionSpreadDeg, db.directionSpreadDeg, REPORT_ANGLE_EPSILON_DEG) && nearlyEqual(da.recentSumTranslationDeltaM, db.recentSumTranslationDeltaM, REPORT_METRE_EPSILON_M) && nearlyEqual(da.medianResidualM, db.medianResidualM, REPORT_METRE_EPSILON_M) && nearlyEqual(da.medianRecentGpsAccuracyM, db.medianRecentGpsAccuracyM, REPORT_METRE_EPSILON_M) && nearlyEqual(da.walkedDistanceM, db.walkedDistanceM, REPORT_METRE_EPSILON_M) && nearlyEqual(da.gpsVsFusedMaxDivergenceM, db.gpsVsFusedMaxDivergenceM, REPORT_METRE_EPSILON_M);
|
|
602
540
|
}
|
|
603
541
|
function matricesNearlyEqual(a, b) {
|
|
604
542
|
if (a.length !== b.length) return false;
|
|
@@ -650,16 +588,14 @@ function createTrackingQualityListenerMiddleware(options = {}) {
|
|
|
650
588
|
...DEFAULT_TRACKING_QUALITY_OPTIONS,
|
|
651
589
|
...options
|
|
652
590
|
};
|
|
653
|
-
let compassStreak = 0;
|
|
654
591
|
const listenerMiddleware = createListenerMiddleware();
|
|
655
592
|
listenerMiddleware.startListening({
|
|
656
593
|
predicate: (action) => inputActionPredicate(action),
|
|
657
594
|
effect: (action, api) => {
|
|
658
595
|
const state = api.getState();
|
|
659
596
|
if (action.type === TRACKING_QUALITY_INPUT_ACTIONS.startSession || action.type === TRACKING_QUALITY_INPUT_ACTIONS.resetTracking) {
|
|
660
|
-
compassStreak = 0;
|
|
661
597
|
const tq = state.trackingQuality;
|
|
662
|
-
if (tq && (tq.recentAlignments.length > 0 || tq.
|
|
598
|
+
if (tq && (tq.recentAlignments.length > 0 || tq.report !== null || tq.degradedConsecutiveCount > 0)) api.dispatch(resetTrackingQuality());
|
|
663
599
|
return;
|
|
664
600
|
}
|
|
665
601
|
if (action.type === TRACKING_QUALITY_INPUT_ACTIONS.gpsRecorded || action.type === TRACKING_QUALITY_INPUT_ACTIONS.setZeroPos) {
|
|
@@ -678,30 +614,6 @@ function createTrackingQualityListenerMiddleware(options = {}) {
|
|
|
678
614
|
}
|
|
679
615
|
}
|
|
680
616
|
}
|
|
681
|
-
const midState = api.getState();
|
|
682
|
-
const midTq = midState.trackingQuality;
|
|
683
|
-
if (midTq && midTq.firstAgreementObservationIndex === null) {
|
|
684
|
-
const snapshots = selectRecentAlignments(midState);
|
|
685
|
-
if (snapshots.length >= 2) {
|
|
686
|
-
const conv = computeConvergence(snapshots, {
|
|
687
|
-
rotationWarnDeg: opts.convergenceRotationWarnDeg,
|
|
688
|
-
translationWarnM: opts.convergenceTranslationWarnM
|
|
689
|
-
});
|
|
690
|
-
const sensorOr = selectLastSensorOrientation(midState);
|
|
691
|
-
const pose = selectLastValidPose(midState);
|
|
692
|
-
const compass = computeCompassAgreement(selectAlignmentMatrix(midState), sensorOr, pose, {
|
|
693
|
-
warnDeg: opts.compassWarnDeg,
|
|
694
|
-
failDeg: opts.compassFailDeg
|
|
695
|
-
});
|
|
696
|
-
if (conv.score >= .7 && compass.headingDeltaDeg !== null && compass.headingDeltaDeg <= opts.compassWarnDeg) {
|
|
697
|
-
compassStreak += 1;
|
|
698
|
-
if (compassStreak >= opts.firstAgreementMinStreak) {
|
|
699
|
-
const obsCount = selectGpsPositions(midState).length;
|
|
700
|
-
api.dispatch(firstAgreementReached(obsCount));
|
|
701
|
-
}
|
|
702
|
-
} else compassStreak = 0;
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
617
|
const next = api.getState();
|
|
706
618
|
const report = computeTrackingQualityReport(next, opts);
|
|
707
619
|
const prev = next.trackingQuality?.report ?? null;
|
|
@@ -720,4 +632,4 @@ function createTrackingQualityListenerMiddleware(options = {}) {
|
|
|
720
632
|
return listenerMiddleware.middleware;
|
|
721
633
|
}
|
|
722
634
|
//#endregion
|
|
723
|
-
export { DEFAULT_TRACKING_QUALITY_OPTIONS,
|
|
635
|
+
export { DEFAULT_TRACKING_QUALITY_OPTIONS, computeConvergence, computeCoverage, computeGpsAccuracy, computeGpsVsFusedDivergence, computeResidualConsensus, computeTrackingQualityReport, createTrackingQualityListenerMiddleware, degradedCountUpdated, matrixDelta, reportUpdated, resetTrackingQuality, selectRecentAlignments, selectTrackingQuality, snapshotPushed, snapshotsTrimmed, trackingQualityReducer };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as TrackingSliceState, c as poseLost, d as selectLastRestartedPayload, f as selectLastSensorOrientation, g as trackingReducer, h as selectTrackingPhase, i as TrackingPhase, l as poseReceived, m as selectLostFrameCount, n as PoseReceivedPayload, o as clearLastRestartedPayload, p as selectLastValidPose, r as ResetTransformData, s as originReset, t as DeviceOrientation, u as resetTracking } from "../tracking-slice-
|
|
1
|
+
import { a as TrackingSliceState, c as poseLost, d as selectLastRestartedPayload, f as selectLastSensorOrientation, g as trackingReducer, h as selectTrackingPhase, i as TrackingPhase, l as poseReceived, m as selectLostFrameCount, n as PoseReceivedPayload, o as clearLastRestartedPayload, p as selectLastValidPose, r as ResetTransformData, s as originReset, t as DeviceOrientation, u as resetTracking } from "../tracking-slice-BuAZxXZ5.js";
|
|
2
2
|
export { DeviceOrientation, PoseReceivedPayload, ResetTransformData, TrackingPhase, TrackingSliceState, clearLastRestartedPayload, originReset, poseLost, poseReceived, resetTracking, selectLastRestartedPayload, selectLastSensorOrientation, selectLastValidPose, selectLostFrameCount, selectTrackingPhase, trackingReducer };
|