@wemap/positioning 2.0.0 → 2.2.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/.eslintrc.json +4 -2
- package/debug/absolute-attitude.html +16 -0
- package/debug/arcore.html +16 -0
- package/debug/gnss-wifi-pdr.html +16 -0
- package/debug/gnss-wifi.html +16 -0
- package/debug/imu.html +16 -0
- package/debug/inclination.html +16 -0
- package/debug/pdr.html +16 -0
- package/debug/pose.html +16 -0
- package/debug/positioning.html +16 -0
- package/debug/relative-attitude.html +16 -0
- package/package.json +7 -4
- package/src/PositioningHandler.js +96 -34
- package/src/components/AbsoluteAttitudeComponent.jsx +104 -0
- package/src/components/ArCoreComponent.jsx +74 -0
- package/src/components/GnssWifiComponent.jsx +58 -0
- package/src/components/GnssWifiPdrComponent.jsx +94 -0
- package/src/components/ImuComponent.jsx +100 -0
- package/src/components/InclinationComponent.jsx +53 -0
- package/src/components/MapComponent.jsx +226 -0
- package/src/components/PdrComponent.jsx +97 -0
- package/src/components/PoseComponent.jsx +81 -0
- package/src/components/PositioningComponent.jsx +26 -0
- package/src/components/PositioningInclinationComponent.jsx +76 -0
- package/src/components/PositioningPoseComponent.jsx +120 -0
- package/src/components/RelativeAttitudeComponent.jsx +82 -0
- package/src/components/StartStopComponent.jsx +50 -0
- package/src/components/Utils.js +92 -0
- package/src/components/index.js +32 -0
- package/src/errors/AskImuOnDesktopError.js +9 -0
- package/src/errors/GeolocationApiMissingError.js +9 -0
- package/src/errors/GeolocationPermissionDeniedError.js +9 -0
- package/src/errors/GeolocationPositionUnavailableError.js +9 -0
- package/src/errors/IpResolveServerError.js +9 -0
- package/src/errors/MissingAccelerometerError.js +11 -0
- package/src/errors/MissingArCoreError.js +9 -0
- package/src/errors/MissingGyroscopeError.js +11 -0
- package/src/errors/MissingMagnetometerError.js +9 -0
- package/src/errors/MissingNativeInterfaceError.js +9 -0
- package/src/errors/MissingSensorError.js +14 -0
- package/src/events/EventType.js +20 -0
- package/src/events/ProviderError.js +52 -0
- package/src/events/ProviderEvent.js +35 -0
- package/src/index.js +2 -2
- package/src/providers/Constants.js +5 -0
- package/src/providers/FakeAbsolutePositionProvider.js +56 -0
- package/src/providers/Provider.js +230 -0
- package/src/providers/ProviderOptions.js +28 -0
- package/{src.old → src}/providers/ProvidersLogger.js +2 -2
- package/src/providers/attitude/AbsoluteAttitudeProvider.js +207 -0
- package/src/providers/attitude/RelativeAttitudeProvider.js +129 -0
- package/src/providers/others/ImuProvider.js +186 -0
- package/src/providers/others/InclinationProvider.js +107 -0
- package/{src.old/providers/LocationSource.js → src/providers/others/MapMatchingProvider.js} +5 -148
- package/src/providers/pose/ArCoreProvider.js +127 -0
- package/src/providers/pose/GnssWifiPdrProvider.js +233 -0
- package/src/providers/pose/PoseProvider.js +90 -0
- package/{src.old/providers/PdrLocationSource.js → src/providers/pose/pdr/PdrProvider.js} +145 -113
- package/{src.old/providers/pdr → src/providers/pose/pdr/helpers}/Smoother.js +1 -1
- package/src/providers/position/GnssWifiProvider.js +129 -0
- package/src/providers/position/IpProvider.js +75 -0
- package/webpack/webpack.common.js +1 -1
- package/webpack/webpack.dev.js +1 -1
- package/debug/index.html +0 -15
- package/debug/index.old.html +0 -37
- package/scripts/release-github.js +0 -216
- package/src/providers/FakeLocationSource.js +0 -36
- package/src.old/Constants.js +0 -11
- package/src.old/NavigationHandler.js +0 -244
- package/src.old/Pose.js +0 -8
- package/src.old/attitude/AttitudeHandler.js +0 -342
- package/src.old/components/AbsoluteAttitude.jsx +0 -136
- package/src.old/components/Imu.jsx +0 -89
- package/src.old/components/LocationSource.jsx +0 -434
- package/src.old/components/Logger.jsx +0 -113
- package/src.old/components/NavigationDebugApp.jsx +0 -106
- package/src.old/components/Others.jsx +0 -121
- package/src.old/components/RelativeAttitude.jsx +0 -104
- package/src.old/components/Utils.js +0 -35
- package/src.old/components/index.js +0 -13
- package/src.old/index.js +0 -7
- package/src.old/providers/FixedLocationImuLocationSource.js +0 -66
- package/src.old/providers/GnssLocationSource.js +0 -118
- package/src.old/providers/GnssPdrLocationSource.js +0 -182
- package/src.old/providers/IPLocationSource.js +0 -96
- package/src.old/sensors/SensorsCompatibility.js +0 -486
- package/src.old/sensors/SensorsCompatibility.spec.js +0 -270
- package/src.old/sensors/SensorsLogger.js +0 -94
- package/src.old/sensors/SensorsLoggerUtils.js +0 -35
- /package/{src.old → src/providers}/attitude/EkfAttitude.js +0 -0
- /package/{src.old → src/providers}/attitude/EkfAttitude.spec.js +0 -0
- /package/{src.old/providers/pdr → src/providers/pose/pdr/helpers}/HeadingUnlocker.js +0 -0
- /package/{src.old/providers/pdr → src/providers/pose/pdr/helpers}/HeadingUnlocker.spec.js +0 -0
- /package/{src.old/providers/pdr → src/providers/pose/pdr/helpers}/Smoother.spec.js +0 -0
- /package/{src.old/providers/pdr → src/providers/pose/pdr/helpers}/ThugDetector.js +0 -0
- /package/{src.old/providers → src/providers/pose/pdr}/steps/StepDetection.js +0 -0
- /package/{src.old/providers → src/providers/pose/pdr}/steps/StepDetectionLadetto.js +0 -0
- /package/{src.old/providers → src/providers/pose/pdr}/steps/StepDetectionMinMaxPeaks.js +0 -0
- /package/{src.old/providers → src/providers/pose/pdr}/steps/StepDetectionMinMaxPeaks2.js +0 -0
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import InclinationProvider from './providers/computed/InclinationProvider';
|
|
1
2
|
import PositioningHandler from './PositioningHandler';
|
|
2
|
-
import AttitudeHandler from '../src.old/attitude/AttitudeHandler';
|
|
3
3
|
|
|
4
4
|
export {
|
|
5
|
-
|
|
5
|
+
InclinationProvider, PositioningHandler
|
|
6
6
|
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WGS84, Attitude
|
|
3
|
+
} from '@wemap/geo';
|
|
4
|
+
import Provider from './Provider';
|
|
5
|
+
import EventType from '../events/EventType';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @private
|
|
10
|
+
*/
|
|
11
|
+
class FakeAbsolutePositionProvider extends Provider {
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @override
|
|
15
|
+
*/
|
|
16
|
+
static get displayName() {
|
|
17
|
+
return 'FakeAbsolutePosition';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @override
|
|
22
|
+
*/
|
|
23
|
+
static get eventsType() {
|
|
24
|
+
return [EventType.AbsoluteAttitude, EventType.AbsolutePosition];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @override
|
|
29
|
+
*/
|
|
30
|
+
startInternal() {
|
|
31
|
+
|
|
32
|
+
this.interval = setInterval(() => {
|
|
33
|
+
this.notify(
|
|
34
|
+
this.createEvent(EventType.AbsoluteAttitude, new Attitude([1, 0, 0, 0])),
|
|
35
|
+
this.createEvent(EventType.AbsolutePosition, new WGS84(45, 5))
|
|
36
|
+
);
|
|
37
|
+
}, 1000);
|
|
38
|
+
|
|
39
|
+
return Promise.resolve();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @override
|
|
44
|
+
*/
|
|
45
|
+
stopInternal() {
|
|
46
|
+
|
|
47
|
+
if (this.interval) {
|
|
48
|
+
clearInterval(this.interval);
|
|
49
|
+
this.interval = null;
|
|
50
|
+
}
|
|
51
|
+
return Promise.resolve();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default FakeAbsolutePositionProvider;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import noop from 'lodash.noop';
|
|
2
|
+
|
|
3
|
+
import EventType from '../events/EventType';
|
|
4
|
+
import ProviderEvent from '../events/ProviderEvent';
|
|
5
|
+
import Logger from '@wemap/logger';
|
|
6
|
+
import ProviderError from '../events/ProviderError';
|
|
7
|
+
import ProvidersLogger from './ProvidersLogger';
|
|
8
|
+
import MissingNativeInterfaceError from '../errors/MissingNativeInterfaceError';
|
|
9
|
+
|
|
10
|
+
let uniqueId = 1;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A provider is a meta class to define an entity which can be
|
|
14
|
+
* started / stopped and provides a data of {@link ProviderEvent#DataType}
|
|
15
|
+
*/
|
|
16
|
+
class Provider {
|
|
17
|
+
|
|
18
|
+
static DEFAULT_NAME = 'Unknown';
|
|
19
|
+
static DEFAULT_OPTIONS = {
|
|
20
|
+
stopOnError: true,
|
|
21
|
+
checkAvailabilityOnStart: true
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
static State = {
|
|
25
|
+
STARTING: 0,
|
|
26
|
+
STARTED: 1,
|
|
27
|
+
STOPPPED: 2
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
static Warnings = {
|
|
31
|
+
ALREADY_STARTED: 'Provider already started',
|
|
32
|
+
ALREADY_STOPPED: 'Provider already stopped'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
state = Provider.State.STOPPPED;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Provider constructor
|
|
39
|
+
* @param {Function} onEvent a callback which will receive events
|
|
40
|
+
* @param {Function} onError a callback when an error happen
|
|
41
|
+
* @param {Object} options provider options
|
|
42
|
+
*/
|
|
43
|
+
constructor(onEvent, onError, options) {
|
|
44
|
+
this.onEvent = onEvent || noop;
|
|
45
|
+
this.onError = onError || noop;
|
|
46
|
+
this.options = Object.assign(this.constructor.DEFAULT_OPTIONS, options);
|
|
47
|
+
this.id = uniqueId++;
|
|
48
|
+
ProvidersLogger.addEvent(this, 'constructor');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get the provider name
|
|
53
|
+
* @public
|
|
54
|
+
* @abstract
|
|
55
|
+
*/
|
|
56
|
+
static get displayName() {
|
|
57
|
+
return this.constructor.DEFAULT_NAME;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get the list of events type which can be returned by the provider
|
|
62
|
+
* @returns {EventType[]} the list of events type
|
|
63
|
+
* @public
|
|
64
|
+
* @abstract
|
|
65
|
+
*/
|
|
66
|
+
static get eventsType() {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @override
|
|
72
|
+
*/
|
|
73
|
+
static checkAvailabilityErrors() {
|
|
74
|
+
return this.requiredProviders.reduce(
|
|
75
|
+
(acc, val) => acc.concat(val.checkAvailabilityErrors()), []
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Return the list of required providers
|
|
81
|
+
*/
|
|
82
|
+
static get requiredProviders() {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Start the Provider
|
|
88
|
+
* @public
|
|
89
|
+
*/
|
|
90
|
+
start() {
|
|
91
|
+
if (this.state === Provider.State.STARTING
|
|
92
|
+
|| this.state === Provider.State.STARTED) {
|
|
93
|
+
Logger.warn(Provider.Warnings.ALREADY_STARTED);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
this.state = Provider.State.STARTING;
|
|
97
|
+
|
|
98
|
+
if (this.options.checkAvailabilityOnStart) {
|
|
99
|
+
const availabilityErrors = this.constructor.checkAvailabilityErrors();
|
|
100
|
+
if (availabilityErrors.length !== 0) {
|
|
101
|
+
this.notifyError(...availabilityErrors);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
ProvidersLogger.addEvent(this, 'start');
|
|
107
|
+
this.state = Provider.State.STARTED;
|
|
108
|
+
this.startInternal();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @abstract
|
|
113
|
+
*/
|
|
114
|
+
startInternal() {
|
|
115
|
+
throw new Error('Provider "' + this.constructor.name
|
|
116
|
+
+ '" does not @override startInternal()');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Stop the Provider
|
|
121
|
+
* @public
|
|
122
|
+
*/
|
|
123
|
+
stop() {
|
|
124
|
+
|
|
125
|
+
if (this.state === Provider.State.STOPPPED) {
|
|
126
|
+
Logger.warn(Provider.Warnings.ALREADY_STOPPED);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
ProvidersLogger.addEvent(this, 'stop');
|
|
131
|
+
this.state = Provider.State.STOPPPED;
|
|
132
|
+
this.stopInternal();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @abstract
|
|
137
|
+
*/
|
|
138
|
+
stopInternal() {
|
|
139
|
+
throw new Error('Provider "' + this.constructor.name
|
|
140
|
+
+ '" does not @override stopInternal()');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Notify the subscriber defined in {@link constructor}
|
|
146
|
+
* @param {ProviderEvent[]} events events to send to subscriber
|
|
147
|
+
*/
|
|
148
|
+
notify(...events) {
|
|
149
|
+
ProvidersLogger.incrementNotifications(this);
|
|
150
|
+
this.onEvent(events);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Notify the subscriber defined in {@link constructor}
|
|
155
|
+
* @param {ProviderError[]} errors The error raised
|
|
156
|
+
*/
|
|
157
|
+
notifyError(...errors) {
|
|
158
|
+
this.onError(errors);
|
|
159
|
+
if (this.options.stopOnError && this.state === Provider.State.STARTED) {
|
|
160
|
+
this.stop();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Create an event from current provider
|
|
166
|
+
* @param {ProviderEvent#DataType} dataType type of ProviderEvent
|
|
167
|
+
* @param {Object} data data exported to ProviderEvent
|
|
168
|
+
* @param {Number} timestamp event timestamp
|
|
169
|
+
* @protected
|
|
170
|
+
*/
|
|
171
|
+
static createEvent(dataType, data, timestamp) {
|
|
172
|
+
const event = new ProviderEvent(this.constructor.name, dataType, data);
|
|
173
|
+
if (timestamp) {
|
|
174
|
+
event.timestamp = timestamp;
|
|
175
|
+
}
|
|
176
|
+
return event;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Create an event from current provider
|
|
181
|
+
* @param {EventType} dataType type of ProviderEvent
|
|
182
|
+
* @param {Object} data data exported to ProviderEvent
|
|
183
|
+
* @param {Number} timestamp event timestamp
|
|
184
|
+
* @protected
|
|
185
|
+
*/
|
|
186
|
+
createEvent(dataType, data, timestamp) {
|
|
187
|
+
return this.constructor.createEvent(dataType, data, timestamp);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Create an event error from current provider
|
|
192
|
+
* @param {EventType} dataType type of ProviderError
|
|
193
|
+
* @param {ProviderError} error error raised by the problem exported to ProviderError
|
|
194
|
+
* @param {Number} timestamp event timestamp
|
|
195
|
+
* @protected
|
|
196
|
+
*/
|
|
197
|
+
static createError(dataType, error, timestamp) {
|
|
198
|
+
const event = new ProviderError(this.name, dataType, error);
|
|
199
|
+
if (timestamp) {
|
|
200
|
+
event.timestamp = timestamp;
|
|
201
|
+
}
|
|
202
|
+
return event;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Create an event error from current provider
|
|
207
|
+
* @param {EventType} dataType type of ProviderError
|
|
208
|
+
* @param {ProviderError} error error raised by the problem exported to ProviderError
|
|
209
|
+
* @param {Number} timestamp event timestamp
|
|
210
|
+
* @protected
|
|
211
|
+
*/
|
|
212
|
+
createError(dataType, error, timestamp) {
|
|
213
|
+
return this.constructor.createError(dataType, error, timestamp);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
static hasNativeInterface() {
|
|
218
|
+
return Boolean(Provider.getNativeInterface());
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
static getNativeInterface() {
|
|
222
|
+
return global.WemapProvidersAndroid;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
static createMissingNativeInterfaceError(dataType) {
|
|
226
|
+
return this.createError(dataType, new MissingNativeInterfaceError());
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export default Provider;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const ProviderOptions = {
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Does provider have to wait an input position to start
|
|
5
|
+
* @see PositioningHandler#setLocation()
|
|
6
|
+
*/
|
|
7
|
+
waitInputPosition: false,
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Does provider have to wait an input heading to start
|
|
11
|
+
* @see PositioningHandler#setHeading()
|
|
12
|
+
*/
|
|
13
|
+
waitInputHeading: false,
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Does provider will use map to
|
|
17
|
+
* @see PositioningHandler#setItinerary()
|
|
18
|
+
*/
|
|
19
|
+
useMapMatching: false,
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Providers listed here will not be used by PositioningHandler
|
|
23
|
+
* List of {@link Provider}
|
|
24
|
+
*/
|
|
25
|
+
ignoreProviders: []
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default ProviderOptions;
|
|
@@ -20,7 +20,7 @@ class ProvidersLogger {
|
|
|
20
20
|
interval = setInterval(() => {
|
|
21
21
|
|
|
22
22
|
for (const [key, value] of Object.entries(pushEvents)) {
|
|
23
|
-
Logger.debug('Received ' + value + '
|
|
23
|
+
Logger.debug('Received ' + value + ' notifications from ' + pushEventsRef[key].constructor.name + ' last second');
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
pushEvents = {};
|
|
@@ -53,7 +53,7 @@ class ProvidersLogger {
|
|
|
53
53
|
Logger.debug(objectClassName + '[' + objectId + '].' + method);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
static
|
|
56
|
+
static incrementNotifications(object) {
|
|
57
57
|
|
|
58
58
|
if (!ProvidersLogger.enabled) {
|
|
59
59
|
return;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import geomagnetism from 'geomagnetism';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Attitude, WGS84
|
|
5
|
+
} from '@wemap/geo';
|
|
6
|
+
import {
|
|
7
|
+
deg2rad, rad2deg, Quaternion, Rotations
|
|
8
|
+
} from '@wemap/maths';
|
|
9
|
+
import {
|
|
10
|
+
Browser, BrowserUtils
|
|
11
|
+
} from '@wemap/utils';
|
|
12
|
+
|
|
13
|
+
import Provider from '../Provider';
|
|
14
|
+
import EventType from '../../events/EventType';
|
|
15
|
+
import AskImuOnDesktopError from '../../errors/AskImuOnDesktopError';
|
|
16
|
+
import MissingMagnetometerError from '../../errors/MissingMagnetometerError';
|
|
17
|
+
import MissingSensorError from '../../errors/MissingSensorError';
|
|
18
|
+
import Logger from '@wemap/logger';
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Absolute attitude provider gives the device attitude in East-North-Up (ENU) frame using
|
|
23
|
+
* browser deviceorientation or deviceorientationabsolute
|
|
24
|
+
* The provider does not work until an AbsolutePosition is given. This is necessary to
|
|
25
|
+
* calculate declination.
|
|
26
|
+
*
|
|
27
|
+
* -----------------------------------
|
|
28
|
+
* Overview of compatibilities:
|
|
29
|
+
* -----------------------------------
|
|
30
|
+
*
|
|
31
|
+
* Chrome Android (v72.0.3626): YES (via deviceorientationabsolute)
|
|
32
|
+
* Safari iOS (v12.0): YES (via deviceorientation and event.webkitCompassHeading)
|
|
33
|
+
* Opera Android (v50.2.2426): NO {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/ondeviceorientation}
|
|
34
|
+
* Firefox Android (v65.0.1): NO {@link https://www.fxsitecompat.com/en-CA/docs/2018/various-device-sensor-apis-are-now-deprecated/}
|
|
35
|
+
*
|
|
36
|
+
* -----------------------------------
|
|
37
|
+
*/
|
|
38
|
+
class AbsoluteAttitudeProvider extends Provider {
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @override
|
|
42
|
+
*/
|
|
43
|
+
static get displayName() {
|
|
44
|
+
return 'Absolute Attitude from Browser';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @override
|
|
49
|
+
*/
|
|
50
|
+
static get eventsType() {
|
|
51
|
+
return [EventType.AbsoluteAttitude];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @override
|
|
56
|
+
*/
|
|
57
|
+
static checkAvailabilityErrors() {
|
|
58
|
+
|
|
59
|
+
if (BrowserUtils.isMobile) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return [
|
|
64
|
+
AbsoluteAttitudeProvider.createError(
|
|
65
|
+
EventType.AbsoluteAttitude,
|
|
66
|
+
new AskImuOnDesktopError()
|
|
67
|
+
)
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @override
|
|
73
|
+
*/
|
|
74
|
+
startInternal() {
|
|
75
|
+
switch (BrowserUtils.name) {
|
|
76
|
+
case Browser.CHROME:
|
|
77
|
+
window.addEventListener('deviceorientationabsolute',
|
|
78
|
+
this.onDeviceOrientationChromeEvent, true);
|
|
79
|
+
break;
|
|
80
|
+
|
|
81
|
+
case Browser.SAFARI:
|
|
82
|
+
window.addEventListener('deviceorientation',
|
|
83
|
+
this.onDeviceOrientationSafariEvent, true);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @override
|
|
90
|
+
*/
|
|
91
|
+
stopInternal() {
|
|
92
|
+
switch (BrowserUtils.name) {
|
|
93
|
+
case Browser.CHROME:
|
|
94
|
+
window.removeEventListener('deviceorientationabsolute',
|
|
95
|
+
this.onDeviceOrientationChromeEvent, true);
|
|
96
|
+
break;
|
|
97
|
+
|
|
98
|
+
case Browser.SAFARI:
|
|
99
|
+
window.removeEventListener('deviceorientation',
|
|
100
|
+
this.onDeviceOrientationSafariEvent, true);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
onDeviceOrientationChromeEvent = e => {
|
|
107
|
+
|
|
108
|
+
const timestamp = e.timeStamp / 1e3;
|
|
109
|
+
|
|
110
|
+
if (!e.alpha || !e.beta || !e.gamma) {
|
|
111
|
+
super.notifyError(this.createError(
|
|
112
|
+
EventType.AbsoluteAttitude,
|
|
113
|
+
new MissingSensorError().from('deviceorientationabsolute'),
|
|
114
|
+
timestamp
|
|
115
|
+
));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
this.onDeviceOrientationCommonEvent(timestamp,
|
|
119
|
+
Rotations.eulerToQuaternionZXYDegrees([e.alpha, e.beta, e.gamma]));
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
onDeviceOrientationSafariEvent = e => {
|
|
124
|
+
|
|
125
|
+
const timestamp = e.timeStamp / 1e3;
|
|
126
|
+
|
|
127
|
+
if (!e.beta || !e.gamma) {
|
|
128
|
+
super.notifyError(this.createError(
|
|
129
|
+
EventType.AbsoluteAttitude,
|
|
130
|
+
new MissingSensorError().from('deviceorientation'),
|
|
131
|
+
timestamp
|
|
132
|
+
));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!e.webkitCompassHeading) {
|
|
137
|
+
super.notifyError(this.createError(
|
|
138
|
+
EventType.AbsoluteAttitude,
|
|
139
|
+
new MissingMagnetometerError().from('deviceorientation'),
|
|
140
|
+
timestamp
|
|
141
|
+
));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let webkitCompassHeading = -e.webkitCompassHeading;
|
|
146
|
+
// Be Careful: webkitCompassHeading is not continuous.
|
|
147
|
+
// Reference frame changes in function of beta with thresholds: beta > 30deg and beta < 62deg
|
|
148
|
+
// Below 30deg, East-North-Up (ENU) frame is used
|
|
149
|
+
// Above 62def, East-Up-South (EUS) frame is used
|
|
150
|
+
// Between 30deg and 62deg either ENU or EUS can be used
|
|
151
|
+
if (e.beta > 62) {
|
|
152
|
+
webkitCompassHeading = AbsoluteAttitudeProvider.headingEusToEnu(
|
|
153
|
+
180 - webkitCompassHeading, e.beta, e.gamma);
|
|
154
|
+
}
|
|
155
|
+
const quaternion = Rotations.eulerToQuaternionZXYDegrees(
|
|
156
|
+
[webkitCompassHeading, e.beta, e.gamma]);
|
|
157
|
+
|
|
158
|
+
this.onDeviceOrientationCommonEvent(timestamp, quaternion);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
onDeviceOrientationCommonEvent = (timestamp, quaternion) => {
|
|
163
|
+
|
|
164
|
+
if (!this.declinationQuaternion) {
|
|
165
|
+
Logger.warn('Location of AbsoluteAttitude provider is not set yet. '
|
|
166
|
+
+ 'Please call setLocation() before.');
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const trueQuaternion = Quaternion.multiply(this.declinationQuaternion, quaternion);
|
|
170
|
+
|
|
171
|
+
super.notify(
|
|
172
|
+
this.createEvent(
|
|
173
|
+
EventType.AbsoluteAttitude,
|
|
174
|
+
new Attitude(trueQuaternion),
|
|
175
|
+
timestamp
|
|
176
|
+
)
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Initialized declination quaternion using current location.
|
|
182
|
+
* This method should be theoretically called every time the user moves.
|
|
183
|
+
* But in reality declination does not change as much.
|
|
184
|
+
* @param {WGS84} location user location
|
|
185
|
+
*/
|
|
186
|
+
setLocation(location) {
|
|
187
|
+
const wmmResult = geomagnetism.model().point([location.lat, location.lng]);
|
|
188
|
+
// Declination is given in NED frame and our code use ENU, that is why we have: "-decl"
|
|
189
|
+
this.declinationQuaternion = Quaternion.fromAxisAngle([0, 0, 1], - deg2rad(wmmResult.decl));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* {@link https://math.stackexchange.com/questions/3181981/solve-a-system-of-rotation-matrices-z-x-z-z-x-y}
|
|
194
|
+
*/
|
|
195
|
+
static headingEusToEnu(_alpha, _beta, _gamma) {
|
|
196
|
+
const alpha = deg2rad(_alpha);
|
|
197
|
+
const beta = deg2rad(_beta);
|
|
198
|
+
const gamma = deg2rad(_gamma);
|
|
199
|
+
|
|
200
|
+
return rad2deg(Math.atan2(
|
|
201
|
+
Math.cos(alpha) * Math.sin(gamma) + Math.cos(gamma) * Math.sin(alpha) * Math.sin(beta),
|
|
202
|
+
Math.sin(alpha) * Math.sin(gamma) - Math.cos(alpha) * Math.cos(gamma) * Math.sin(beta)
|
|
203
|
+
));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export default AbsoluteAttitudeProvider;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import Provider from '../Provider';
|
|
2
|
+
import EventType from '../../events/EventType';
|
|
3
|
+
import EkfAttitude from './EkfAttitude';
|
|
4
|
+
import ImuProvider from '../others/ImuProvider';
|
|
5
|
+
import { Attitude } from '@wemap/geo';
|
|
6
|
+
import { deg2rad } from '@wemap/maths';
|
|
7
|
+
import ProviderError from '../../events/ProviderError';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Relative attitude provider gives the device attitude in East-North-Up (ENU) frame using
|
|
12
|
+
* browser deviceorientation
|
|
13
|
+
* The provider does not work until an offset is given.
|
|
14
|
+
*/
|
|
15
|
+
class RelativeAttitudeProvider extends Provider {
|
|
16
|
+
|
|
17
|
+
lastTimestamp = 0;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @override
|
|
21
|
+
*/
|
|
22
|
+
constructor(onEvent, onError, options) {
|
|
23
|
+
super(onEvent, onError, options);
|
|
24
|
+
|
|
25
|
+
this.ekfAttitude = new EkfAttitude();
|
|
26
|
+
this.relativeOffsetQuaternion = [1, 0, 0, 0];
|
|
27
|
+
|
|
28
|
+
this.imuProvider = new ImuProvider(this.onImuEvent,
|
|
29
|
+
this.onImuError, { require: [EventType.Acceleration, EventType.AngularRate] });
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @override
|
|
35
|
+
*/
|
|
36
|
+
static get displayName() {
|
|
37
|
+
return 'Relative Attitude from Ekf';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @override
|
|
42
|
+
*/
|
|
43
|
+
static get eventsType() {
|
|
44
|
+
return [EventType.RelativeAttitude];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @override
|
|
49
|
+
*/
|
|
50
|
+
static get requiredProviders() {
|
|
51
|
+
return [ImuProvider];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @override
|
|
56
|
+
*/
|
|
57
|
+
startInternal() {
|
|
58
|
+
this.imuProvider.start();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @override
|
|
63
|
+
*/
|
|
64
|
+
stopInternal() {
|
|
65
|
+
this.imuProvider.stop();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
onImuEvent = imuEvent => {
|
|
72
|
+
|
|
73
|
+
let timestamp, acceleration, angularRate;
|
|
74
|
+
imuEvent.forEach(event => {
|
|
75
|
+
if (event.dataType === EventType.Acceleration) {
|
|
76
|
+
acceleration = event.data;
|
|
77
|
+
timestamp = event.timestamp;
|
|
78
|
+
} else if (event.dataType === EventType.AngularRate) {
|
|
79
|
+
angularRate = event.data;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Handle timestamps and dt
|
|
84
|
+
if (this.lastTimestamp === 0) {
|
|
85
|
+
this.lastTimestamp = timestamp;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const diffTime = timestamp - this.lastTimestamp;
|
|
89
|
+
this.lastTimestamp = timestamp;
|
|
90
|
+
|
|
91
|
+
const quaternion = this.ekfAttitude.update(diffTime, acceleration, angularRate);
|
|
92
|
+
|
|
93
|
+
if (quaternion) {
|
|
94
|
+
this.notify(this.createEvent(EventType.RelativeAttitude, new Attitude(quaternion), timestamp));
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
*
|
|
100
|
+
* Set yaw offset, this value will be used as the filter does not use magnetometer
|
|
101
|
+
* @param {Number} heading heading offset in radians and clockwise
|
|
102
|
+
*/
|
|
103
|
+
setOffset(_heading) {
|
|
104
|
+
|
|
105
|
+
// Minus before "heading" is here because ENU attitude is counter-clockwise whereas WGS84 heading is clockwise.
|
|
106
|
+
let heading = -_heading;
|
|
107
|
+
|
|
108
|
+
// Offset from window orientation
|
|
109
|
+
heading += deg2rad(window.orientation || 0);
|
|
110
|
+
|
|
111
|
+
this.ekfAttitude.setOrientationYaw(heading);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
onImuError = imuErrors => {
|
|
115
|
+
this.notifyError(...ProviderError.modifyArrayDataType(imuErrors, EventType.RelativeAttitude));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @override
|
|
120
|
+
*/
|
|
121
|
+
static checkAvailabilityErrors() {
|
|
122
|
+
return ProviderError.modifyArrayDataType(
|
|
123
|
+
super.checkAvailabilityErrors(),
|
|
124
|
+
EventType.RelativeAttitude
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export default RelativeAttitudeProvider;
|