@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.
Files changed (99) hide show
  1. package/.eslintrc.json +4 -2
  2. package/debug/absolute-attitude.html +16 -0
  3. package/debug/arcore.html +16 -0
  4. package/debug/gnss-wifi-pdr.html +16 -0
  5. package/debug/gnss-wifi.html +16 -0
  6. package/debug/imu.html +16 -0
  7. package/debug/inclination.html +16 -0
  8. package/debug/pdr.html +16 -0
  9. package/debug/pose.html +16 -0
  10. package/debug/positioning.html +16 -0
  11. package/debug/relative-attitude.html +16 -0
  12. package/package.json +7 -4
  13. package/src/PositioningHandler.js +96 -34
  14. package/src/components/AbsoluteAttitudeComponent.jsx +104 -0
  15. package/src/components/ArCoreComponent.jsx +74 -0
  16. package/src/components/GnssWifiComponent.jsx +58 -0
  17. package/src/components/GnssWifiPdrComponent.jsx +94 -0
  18. package/src/components/ImuComponent.jsx +100 -0
  19. package/src/components/InclinationComponent.jsx +53 -0
  20. package/src/components/MapComponent.jsx +226 -0
  21. package/src/components/PdrComponent.jsx +97 -0
  22. package/src/components/PoseComponent.jsx +81 -0
  23. package/src/components/PositioningComponent.jsx +26 -0
  24. package/src/components/PositioningInclinationComponent.jsx +76 -0
  25. package/src/components/PositioningPoseComponent.jsx +120 -0
  26. package/src/components/RelativeAttitudeComponent.jsx +82 -0
  27. package/src/components/StartStopComponent.jsx +50 -0
  28. package/src/components/Utils.js +92 -0
  29. package/src/components/index.js +32 -0
  30. package/src/errors/AskImuOnDesktopError.js +9 -0
  31. package/src/errors/GeolocationApiMissingError.js +9 -0
  32. package/src/errors/GeolocationPermissionDeniedError.js +9 -0
  33. package/src/errors/GeolocationPositionUnavailableError.js +9 -0
  34. package/src/errors/IpResolveServerError.js +9 -0
  35. package/src/errors/MissingAccelerometerError.js +11 -0
  36. package/src/errors/MissingArCoreError.js +9 -0
  37. package/src/errors/MissingGyroscopeError.js +11 -0
  38. package/src/errors/MissingMagnetometerError.js +9 -0
  39. package/src/errors/MissingNativeInterfaceError.js +9 -0
  40. package/src/errors/MissingSensorError.js +14 -0
  41. package/src/events/EventType.js +20 -0
  42. package/src/events/ProviderError.js +52 -0
  43. package/src/events/ProviderEvent.js +35 -0
  44. package/src/index.js +2 -2
  45. package/src/providers/Constants.js +5 -0
  46. package/src/providers/FakeAbsolutePositionProvider.js +56 -0
  47. package/src/providers/Provider.js +230 -0
  48. package/src/providers/ProviderOptions.js +28 -0
  49. package/{src.old → src}/providers/ProvidersLogger.js +2 -2
  50. package/src/providers/attitude/AbsoluteAttitudeProvider.js +207 -0
  51. package/src/providers/attitude/RelativeAttitudeProvider.js +129 -0
  52. package/src/providers/others/ImuProvider.js +186 -0
  53. package/src/providers/others/InclinationProvider.js +107 -0
  54. package/{src.old/providers/LocationSource.js → src/providers/others/MapMatchingProvider.js} +5 -148
  55. package/src/providers/pose/ArCoreProvider.js +127 -0
  56. package/src/providers/pose/GnssWifiPdrProvider.js +233 -0
  57. package/src/providers/pose/PoseProvider.js +90 -0
  58. package/{src.old/providers/PdrLocationSource.js → src/providers/pose/pdr/PdrProvider.js} +145 -113
  59. package/{src.old/providers/pdr → src/providers/pose/pdr/helpers}/Smoother.js +1 -1
  60. package/src/providers/position/GnssWifiProvider.js +129 -0
  61. package/src/providers/position/IpProvider.js +75 -0
  62. package/webpack/webpack.common.js +1 -1
  63. package/webpack/webpack.dev.js +1 -1
  64. package/debug/index.html +0 -15
  65. package/debug/index.old.html +0 -37
  66. package/scripts/release-github.js +0 -216
  67. package/src/providers/FakeLocationSource.js +0 -36
  68. package/src.old/Constants.js +0 -11
  69. package/src.old/NavigationHandler.js +0 -244
  70. package/src.old/Pose.js +0 -8
  71. package/src.old/attitude/AttitudeHandler.js +0 -342
  72. package/src.old/components/AbsoluteAttitude.jsx +0 -136
  73. package/src.old/components/Imu.jsx +0 -89
  74. package/src.old/components/LocationSource.jsx +0 -434
  75. package/src.old/components/Logger.jsx +0 -113
  76. package/src.old/components/NavigationDebugApp.jsx +0 -106
  77. package/src.old/components/Others.jsx +0 -121
  78. package/src.old/components/RelativeAttitude.jsx +0 -104
  79. package/src.old/components/Utils.js +0 -35
  80. package/src.old/components/index.js +0 -13
  81. package/src.old/index.js +0 -7
  82. package/src.old/providers/FixedLocationImuLocationSource.js +0 -66
  83. package/src.old/providers/GnssLocationSource.js +0 -118
  84. package/src.old/providers/GnssPdrLocationSource.js +0 -182
  85. package/src.old/providers/IPLocationSource.js +0 -96
  86. package/src.old/sensors/SensorsCompatibility.js +0 -486
  87. package/src.old/sensors/SensorsCompatibility.spec.js +0 -270
  88. package/src.old/sensors/SensorsLogger.js +0 -94
  89. package/src.old/sensors/SensorsLoggerUtils.js +0 -35
  90. /package/{src.old → src/providers}/attitude/EkfAttitude.js +0 -0
  91. /package/{src.old → src/providers}/attitude/EkfAttitude.spec.js +0 -0
  92. /package/{src.old/providers/pdr → src/providers/pose/pdr/helpers}/HeadingUnlocker.js +0 -0
  93. /package/{src.old/providers/pdr → src/providers/pose/pdr/helpers}/HeadingUnlocker.spec.js +0 -0
  94. /package/{src.old/providers/pdr → src/providers/pose/pdr/helpers}/Smoother.spec.js +0 -0
  95. /package/{src.old/providers/pdr → src/providers/pose/pdr/helpers}/ThugDetector.js +0 -0
  96. /package/{src.old/providers → src/providers/pose/pdr}/steps/StepDetection.js +0 -0
  97. /package/{src.old/providers → src/providers/pose/pdr}/steps/StepDetectionLadetto.js +0 -0
  98. /package/{src.old/providers → src/providers/pose/pdr}/steps/StepDetectionMinMaxPeaks.js +0 -0
  99. /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
- AttitudeHandler, PositioningHandler
5
+ InclinationProvider, PositioningHandler
6
6
  };
@@ -0,0 +1,5 @@
1
+ const Constants = {
2
+ DEFAULT_ALTITUDE: 1.6
3
+ };
4
+
5
+ export default Constants;
@@ -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 + ' values from ' + pushEventsRef[key].constructor.name + ' last second');
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 addPushEvent(object) {
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;