@wemap/positioning 1.2.2 → 2.1.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/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 +6 -4
- package/src/PositioningHandler.js +124 -0
- package/src/components/AbsoluteAttitudeComponent.jsx +104 -0
- package/src/components/GnssWifiComponent.jsx +46 -0
- package/src/components/GnssWifiPdrComponent.jsx +85 -0
- package/src/components/ImuComponent.jsx +100 -0
- package/src/components/InclinationComponent.jsx +53 -0
- package/src/components/PdrComponent.jsx +88 -0
- package/src/components/PoseComponent.jsx +74 -0
- package/src/components/PositioningComponent.jsx +26 -0
- package/src/components/PositioningInclinationComponent.jsx +76 -0
- package/src/components/PositioningPoseComponent.jsx +111 -0
- package/src/components/RelativeAttitudeComponent.jsx +82 -0
- package/src/components/StartStopComponent.jsx +50 -0
- package/src/components/Utils.js +41 -2
- package/src/components/index.js +19 -2
- 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/MissingGyroscopeError.js +11 -0
- package/src/errors/MissingMagnetometerError.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 +3 -4
- package/src/providers/Constants.js +5 -0
- package/src/providers/FakeAbsolutePositionProvider.js +56 -0
- package/src/providers/Provider.js +218 -0
- package/src/providers/ProviderOptions.js +28 -0
- package/src/providers/ProvidersLogger.js +3 -3
- package/src/providers/attitude/AbsoluteAttitudeProvider.js +207 -0
- package/src/providers/attitude/EkfAttitude.js +238 -0
- package/src/providers/attitude/EkfAttitude.spec.js +116 -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/providers/others/MapMatchingProvider.js +147 -0
- package/src/providers/pose/GnssWifiPdrProvider.js +233 -0
- package/src/providers/pose/PoseProvider.js +90 -0
- package/src/providers/pose/pdr/PdrProvider.js +352 -0
- package/src/providers/pose/pdr/helpers/HeadingUnlocker.js +41 -0
- package/src/providers/pose/pdr/helpers/HeadingUnlocker.spec.js +26 -0
- package/src/providers/pose/pdr/helpers/Smoother.js +90 -0
- package/src/providers/pose/pdr/helpers/Smoother.spec.js +424 -0
- package/src/providers/pose/pdr/helpers/ThugDetector.js +37 -0
- package/src/providers/pose/pdr/steps/StepDetection.js +7 -0
- package/src/providers/pose/pdr/steps/StepDetectionLadetto.js +67 -0
- package/src/providers/pose/pdr/steps/StepDetectionMinMaxPeaks.js +80 -0
- package/src/providers/pose/pdr/steps/StepDetectionMinMaxPeaks2.js +108 -0
- package/src/providers/position/GnssWifiProvider.js +129 -0
- package/src/providers/position/IpProvider.js +75 -0
- package/src.old/components/Utils.js +35 -0
- package/src.old/components/index.js +13 -0
- package/src.old/index.js +7 -0
- package/{src → src.old}/providers/GnssPdrLocationSource.js +1 -1
- package/src.old/providers/ProvidersLogger.js +77 -0
- 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.new/NavigationHandler.js +0 -62
- package/src.new/index.js +0 -3
- package/src.new/providers/FakeLocationSource.js +0 -39
- /package/{src → src.old}/Constants.js +0 -0
- /package/{src → src.old}/NavigationHandler.js +0 -0
- /package/{src → src.old}/Pose.js +0 -0
- /package/{src → src.old}/attitude/AttitudeHandler.js +0 -0
- /package/{src → src.old}/attitude/EkfAttitude.js +0 -0
- /package/{src → src.old}/attitude/EkfAttitude.spec.js +0 -0
- /package/{src → src.old}/components/AbsoluteAttitude.jsx +0 -0
- /package/{src → src.old}/components/Imu.jsx +0 -0
- /package/{src → src.old}/components/LocationSource.jsx +0 -0
- /package/{src → src.old}/components/Logger.jsx +0 -0
- /package/{src → src.old}/components/NavigationDebugApp.jsx +0 -0
- /package/{src → src.old}/components/Others.jsx +0 -0
- /package/{src → src.old}/components/RelativeAttitude.jsx +0 -0
- /package/{src → src.old}/providers/FixedLocationImuLocationSource.js +0 -0
- /package/{src → src.old}/providers/GnssLocationSource.js +0 -0
- /package/{src → src.old}/providers/IPLocationSource.js +0 -0
- /package/{src → src.old}/providers/LocationSource.js +0 -0
- /package/{src → src.old}/providers/PdrLocationSource.js +0 -0
- /package/{src → src.old}/providers/pdr/HeadingUnlocker.js +0 -0
- /package/{src → src.old}/providers/pdr/HeadingUnlocker.spec.js +0 -0
- /package/{src → src.old}/providers/pdr/Smoother.js +0 -0
- /package/{src → src.old}/providers/pdr/Smoother.spec.js +0 -0
- /package/{src → src.old}/providers/pdr/ThugDetector.js +0 -0
- /package/{src → src.old}/providers/steps/StepDetection.js +0 -0
- /package/{src → src.old}/providers/steps/StepDetectionLadetto.js +0 -0
- /package/{src → src.old}/providers/steps/StepDetectionMinMaxPeaks.js +0 -0
- /package/{src → src.old}/providers/steps/StepDetectionMinMaxPeaks2.js +0 -0
- /package/{src → src.old}/sensors/SensorsCompatibility.js +0 -0
- /package/{src → src.old}/sensors/SensorsCompatibility.spec.js +0 -0
- /package/{src → src.old}/sensors/SensorsLogger.js +0 -0
- /package/{src → src.old}/sensors/SensorsLoggerUtils.js +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import Provider from '../Provider';
|
|
2
|
+
import EventType from '../../events/EventType';
|
|
3
|
+
|
|
4
|
+
import GnssWifiProvider from '../position/GnssWifiProvider';
|
|
5
|
+
import AbsoluteAttitudeProvider from '../attitude/AbsoluteAttitudeProvider';
|
|
6
|
+
import ProviderError from '../../events/ProviderError';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Pose provider is the provider used by the PositioningHandler. It uses the best fusion
|
|
10
|
+
* of what he can and provides an AbsoluteAttitude and an AbsolutePosition as an output.
|
|
11
|
+
*/
|
|
12
|
+
class PoseProvider extends Provider {
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @override
|
|
16
|
+
*/
|
|
17
|
+
constructor(onEvent, onError, options) {
|
|
18
|
+
super(onEvent, onError, options);
|
|
19
|
+
|
|
20
|
+
this.absoluteAttitudeProvider = new AbsoluteAttitudeProvider(onEvent, e => this.onAbsoluteAttitudeError(e));
|
|
21
|
+
this.gnssWifiProvider = new GnssWifiProvider((events) => {
|
|
22
|
+
this.absoluteAttitudeProvider.setLocation(events[0].data);
|
|
23
|
+
onEvent(events);
|
|
24
|
+
}, e => this.onGnssWifiError(e));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @override
|
|
29
|
+
*/
|
|
30
|
+
static get displayName() {
|
|
31
|
+
return 'Pose provider';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @override
|
|
36
|
+
*/
|
|
37
|
+
static get eventsType() {
|
|
38
|
+
return [EventType.AbsoluteAttitude, EventType.AbsolutePosition];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Return the list of required providers
|
|
43
|
+
*/
|
|
44
|
+
static get requiredProviders() {
|
|
45
|
+
return [AbsoluteAttitudeProvider, GnssWifiProvider];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @override
|
|
50
|
+
*/
|
|
51
|
+
startInternal() {
|
|
52
|
+
this.absoluteAttitudeProvider.start();
|
|
53
|
+
this.gnssWifiProvider.start();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @override
|
|
58
|
+
*/
|
|
59
|
+
stopInternal() {
|
|
60
|
+
this.absoluteAttitudeProvider.stop();
|
|
61
|
+
this.gnssWifiProvider.stop();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
onAbsoluteAttitudeError(errors) {
|
|
65
|
+
this.notifyError(...ProviderError.modifyArrayDataType(errors, EventType.AbsoluteAttitude));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
onGnssWifiError(errors) {
|
|
69
|
+
this.notifyError(...ProviderError.modifyArrayDataType(errors, EventType.AbsolutePosition));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @override
|
|
74
|
+
*/
|
|
75
|
+
static checkAvailabilityErrors() {
|
|
76
|
+
const errorsAttitude = AbsoluteAttitudeProvider.checkAvailabilityErrors();
|
|
77
|
+
const errorsGnssWifi = GnssWifiProvider.checkAvailabilityErrors();
|
|
78
|
+
return ProviderError.modifyArrayDataType(
|
|
79
|
+
errorsGnssWifi,
|
|
80
|
+
EventType.AbsolutePosition
|
|
81
|
+
).concat(
|
|
82
|
+
ProviderError.modifyArrayDataType(
|
|
83
|
+
errorsAttitude,
|
|
84
|
+
EventType.AbsoluteAttitude
|
|
85
|
+
)
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default PoseProvider;
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import noop from 'lodash.noop';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Constants as GeoConstants, Itinerary, WGS84UserPosition
|
|
5
|
+
} from '@wemap/geo';
|
|
6
|
+
import {
|
|
7
|
+
rad2deg, Quaternion
|
|
8
|
+
} from '@wemap/maths';
|
|
9
|
+
|
|
10
|
+
import StepDetection from './steps/StepDetection';
|
|
11
|
+
import HeadingUnlocker from './helpers/HeadingUnlocker';
|
|
12
|
+
import ThugDetector from './helpers/ThugDetector';
|
|
13
|
+
import Smoother from './helpers/Smoother';
|
|
14
|
+
import RelativeAttitudeProvider from '../../attitude/RelativeAttitudeProvider';
|
|
15
|
+
import ImuProvider from '../../others/ImuProvider';
|
|
16
|
+
import EventType from '../../../events/EventType';
|
|
17
|
+
import MapMatchingProvider from '../../others/MapMatchingProvider';
|
|
18
|
+
import ProviderError from '../../../events/ProviderError';
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
const MM_PDR_ANGLE = 20;
|
|
22
|
+
const MM_PDR_DIST = 15;
|
|
23
|
+
const MM_CONV_SPEED = 0.7;
|
|
24
|
+
|
|
25
|
+
const LO_SEGMENT_SIZE = 1.5;
|
|
26
|
+
|
|
27
|
+
const DEFAULT_OPTIONS = {
|
|
28
|
+
stepdetectionlocker: true,
|
|
29
|
+
smoother: true,
|
|
30
|
+
onThugEvent: noop
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
class PdrProvider extends MapMatchingProvider {
|
|
34
|
+
|
|
35
|
+
// pdrLocation can be different from this.location
|
|
36
|
+
// pdrLocation is raw location calculated by PdrProvider where
|
|
37
|
+
// this.location is smoothed location.
|
|
38
|
+
pdrLocation = null;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @override
|
|
42
|
+
*/
|
|
43
|
+
constructor(onEvent, onError, options) {
|
|
44
|
+
super(onEvent, onError, Object.assign(DEFAULT_OPTIONS, options || {}));
|
|
45
|
+
|
|
46
|
+
// Input data providers
|
|
47
|
+
this.relativeAttitudeProvider = new RelativeAttitudeProvider(
|
|
48
|
+
e => this.onRelativeAttitudeEvent(e),
|
|
49
|
+
e => this.onProviderError(e),
|
|
50
|
+
options
|
|
51
|
+
);
|
|
52
|
+
this.imuProvider = new ImuProvider(
|
|
53
|
+
e => this.onImuEvent(e),
|
|
54
|
+
e => this.onProviderError(e),
|
|
55
|
+
Object.assign(options || {}, { require: [EventType.Acceleration, EventType.AngularRate] })
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Helpers for PDR
|
|
59
|
+
this.stepDetection = new StepDetection();
|
|
60
|
+
this.stepDetectionLocker = new HeadingUnlocker();
|
|
61
|
+
this.smoother = new Smoother();
|
|
62
|
+
this.thugDetector = new ThugDetector();
|
|
63
|
+
|
|
64
|
+
if (options.useMapMatching) {
|
|
65
|
+
this.enableMapMatching();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @override
|
|
72
|
+
*/
|
|
73
|
+
static get displayName() {
|
|
74
|
+
return 'PDR';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @override
|
|
79
|
+
*/
|
|
80
|
+
static get eventsType() {
|
|
81
|
+
return [EventType.AbsoluteAttitude, EventType.AbsolutePosition];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @override
|
|
86
|
+
*/
|
|
87
|
+
static get requiredProviders() {
|
|
88
|
+
return [ImuProvider, RelativeAttitudeProvider];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @override
|
|
94
|
+
*/
|
|
95
|
+
startInternal() {
|
|
96
|
+
this.imuProvider.start();
|
|
97
|
+
this.relativeAttitudeProvider.start();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @override
|
|
102
|
+
*/
|
|
103
|
+
stopInternal() {
|
|
104
|
+
this.imuProvider.stop();
|
|
105
|
+
this.relativeAttitudeProvider.stop();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get provider name
|
|
110
|
+
* @override
|
|
111
|
+
*/
|
|
112
|
+
get nameWithOptions() {
|
|
113
|
+
let name = this.name;
|
|
114
|
+
name += ' (locker: ';
|
|
115
|
+
name += this.options.stepdetectionlocker ? 'Y' : 'N';
|
|
116
|
+
name += ', ';
|
|
117
|
+
name += 'smoo: ';
|
|
118
|
+
name += this.options.smoother ? 'Y' : 'N';
|
|
119
|
+
name += ', ';
|
|
120
|
+
name += 'mm: ';
|
|
121
|
+
name += this.options.useMapMatching ? 'Y' : 'N';
|
|
122
|
+
name += ')';
|
|
123
|
+
return name;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
setHeading(heading) {
|
|
127
|
+
this.relativeAttitudeProvider.setOffset(heading);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
setLocation(location) {
|
|
131
|
+
this.pdrLocation = location;
|
|
132
|
+
|
|
133
|
+
if (this.options.smoother) {
|
|
134
|
+
this.smoother.generateNextLocations(location, true);
|
|
135
|
+
// this.smoother.pullLocation(location.time) should never return null
|
|
136
|
+
this.location = this.smoother.pullLocation(location.time);
|
|
137
|
+
} else {
|
|
138
|
+
this.location = location.clone();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
this.notify(this.createEvent(EventType.AbsolutePosition, this.location));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @override
|
|
146
|
+
*/
|
|
147
|
+
onRelativeAttitudeEvent = events => {
|
|
148
|
+
const attitudeEvent = events[0];
|
|
149
|
+
this.attitude = attitudeEvent.data;
|
|
150
|
+
|
|
151
|
+
const newEvent = attitudeEvent.clone();
|
|
152
|
+
newEvent.dataType = EventType.AbsoluteAttitude;
|
|
153
|
+
this.notify(newEvent);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @private
|
|
158
|
+
*/
|
|
159
|
+
onImuEvent = imuEvent => {
|
|
160
|
+
|
|
161
|
+
if (!this.attitude) {
|
|
162
|
+
// We should wait attitude for stepDetectionLocker and linear acceleration
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!this.pdrLocation) {
|
|
167
|
+
// We should wait at least an input location for PDR
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (this.options.stepdetectionlocker
|
|
172
|
+
&& this.stepDetectionLocker.locked
|
|
173
|
+
&& this.stepDetectionLocker.feedHeading(this.attitude.heading)) {
|
|
174
|
+
// Step detection is locked by stepDetectionLocker
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
let timestamp, acceleration, angularRate;
|
|
180
|
+
imuEvent.forEach(event => {
|
|
181
|
+
if (event.dataType === EventType.Acceleration) {
|
|
182
|
+
acceleration = event.data;
|
|
183
|
+
timestamp = event.timestamp;
|
|
184
|
+
} else if (event.dataType === EventType.AngularRate) {
|
|
185
|
+
angularRate = event.data;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const heading = this.attitude.heading;
|
|
190
|
+
|
|
191
|
+
if (this.onThugEvent && this.thugDetector.compute(timestamp, acceleration)) {
|
|
192
|
+
this.onThugEvent();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Step Detection and Step Size Detection
|
|
197
|
+
*/
|
|
198
|
+
const linearAcc = PdrProvider.computeLinearAcceleration(
|
|
199
|
+
this.attitude.quaternion, acceleration);
|
|
200
|
+
const stepDetected = this.stepDetection.compute(timestamp, linearAcc, angularRate);
|
|
201
|
+
|
|
202
|
+
if (stepDetected) {
|
|
203
|
+
|
|
204
|
+
this.pdrLocation = this.calculateNewLocation(this.pdrLocation, timestamp,
|
|
205
|
+
heading, this.stepDetection.lastStepSize);
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Update current_position
|
|
209
|
+
*/
|
|
210
|
+
if (this.options.smoother) {
|
|
211
|
+
this.smoother.generateNextLocations(this.pdrLocation);
|
|
212
|
+
const newLocation = this.smoother.pullLocation(timestamp);
|
|
213
|
+
// At this time, newLocation can be null if a new step has been detected
|
|
214
|
+
// and smoother has not been completely consumed.
|
|
215
|
+
if (newLocation) {
|
|
216
|
+
this.location = newLocation;
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
this.location = this.pdrLocation;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
} else if (this.options.smoother) {
|
|
223
|
+
// If no step is detected, we pull last known location from smoother until now (timestamp).
|
|
224
|
+
const smoothedLocation = this.smoother.pullLocation(timestamp);
|
|
225
|
+
if (!smoothedLocation) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
this.location = smoothedLocation;
|
|
229
|
+
} else {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
this.notify(this.createEvent(EventType.AbsolutePosition, this.location));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
onProviderError(errors) {
|
|
237
|
+
this.notifyError(...ProviderError.modifyArrayDataType(errors, EventType.AbsoluteAttitude));
|
|
238
|
+
this.notifyError(...ProviderError.modifyArrayDataType(errors, EventType.AbsolutePosition));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* @override
|
|
243
|
+
*/
|
|
244
|
+
static checkAvailabilityErrors() {
|
|
245
|
+
const errors = super.checkAvailabilityErrors();
|
|
246
|
+
return ProviderError.modifyArrayDataType(
|
|
247
|
+
errors,
|
|
248
|
+
EventType.AbsolutePosition
|
|
249
|
+
).concat(
|
|
250
|
+
ProviderError.modifyArrayDataType(
|
|
251
|
+
errors,
|
|
252
|
+
EventType.AbsoluteAttitude
|
|
253
|
+
)
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
calculateNewLocation(previousLocation, timestamp, heading, stepSize) {
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Compute new_position
|
|
261
|
+
*/
|
|
262
|
+
const newLocationWithoutMM = previousLocation.clone();
|
|
263
|
+
newLocationWithoutMM.move(stepSize, heading);
|
|
264
|
+
newLocationWithoutMM.bearing = rad2deg(heading);
|
|
265
|
+
newLocationWithoutMM.time = timestamp;
|
|
266
|
+
|
|
267
|
+
if (!this.mapMatching) {
|
|
268
|
+
return newLocationWithoutMM;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const projection = this.mapMatching.getProjection(newLocationWithoutMM);
|
|
272
|
+
|
|
273
|
+
if (!projection || !projection.projection) {
|
|
274
|
+
return newLocationWithoutMM;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (projection.distanceFromNearestElement < stepSize) {
|
|
278
|
+
return WGS84UserPosition.fromWGS84(projection.projection, newLocationWithoutMM);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Update new_position
|
|
283
|
+
*/
|
|
284
|
+
const smoothedDistance = projection.distanceFromNearestElement * MM_CONV_SPEED;
|
|
285
|
+
const smoothedBearing = previousLocation.bearingTo(projection.projection);
|
|
286
|
+
const smoothedLocation = previousLocation.clone();
|
|
287
|
+
smoothedLocation.move(smoothedDistance, smoothedBearing);
|
|
288
|
+
|
|
289
|
+
return WGS84UserPosition.fromWGS84(smoothedLocation, newLocationWithoutMM);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* MapMatching
|
|
295
|
+
*/
|
|
296
|
+
|
|
297
|
+
enableMapMatching(network, maxDistance, maxAngleBearing) {
|
|
298
|
+
super.enableMapMatching(network, maxDistance || MM_PDR_DIST, maxAngleBearing || MM_PDR_ANGLE);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Itinerary and PDR Locker
|
|
304
|
+
*/
|
|
305
|
+
|
|
306
|
+
setItinerary(itinerary) {
|
|
307
|
+
super.setItinerary(itinerary);
|
|
308
|
+
|
|
309
|
+
if (this.options.stepdetectionlocker) {
|
|
310
|
+
this.stepDetectionLocker.setWaitingOrientation(
|
|
311
|
+
PdrProvider.retrieveWaitingOrientationFromItinerary(itinerary)
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
static retrieveWaitingOrientationFromItinerary(itinerary) {
|
|
317
|
+
|
|
318
|
+
if (!itinerary) {
|
|
319
|
+
throw new Error('Empty itinerary');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (!(itinerary instanceof Itinerary)) {
|
|
323
|
+
throw new TypeError(itinerary + ' is not an instance of itinerary');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (itinerary.length < 1) {
|
|
327
|
+
throw new Error('Empty itinerary');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (itinerary.firstEdge.getLength() > LO_SEGMENT_SIZE) {
|
|
331
|
+
return itinerary.firstEdge.getBearing();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (itinerary.length < 2) {
|
|
335
|
+
throw new Error('Itinerary is too short');
|
|
336
|
+
}
|
|
337
|
+
return itinerary.secondEdge.getBearing();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
setStepDetectionLockerOrientation(orientation) {
|
|
341
|
+
this.stepDetectionLocker.setWaitingOrientation(orientation);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Linear acceleration in ENU
|
|
345
|
+
static computeLinearAcceleration(quaternion, acc) {
|
|
346
|
+
const linearAcc = Quaternion.rotate(Quaternion.inverse(quaternion), acc);
|
|
347
|
+
linearAcc[2] -= GeoConstants.EARTH_GRAVITY;
|
|
348
|
+
return linearAcc;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export default PdrProvider;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import isnumber from 'lodash.isnumber';
|
|
2
|
+
|
|
3
|
+
import { diffAngle } from '@wemap/maths';
|
|
4
|
+
|
|
5
|
+
const LO_ANGLE = 15 * Math.PI / 180;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* HeadingUnlocker is designed to block Pdr Step Detection until user is aiming in the right direction
|
|
9
|
+
*/
|
|
10
|
+
class HeadingUnlocker {
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
// HeadingUnlocker status, by default it is unlocked
|
|
14
|
+
this.locked = false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
setWaitingOrientation(waitingOrientation) {
|
|
18
|
+
this.waitingOrientation = waitingOrientation;
|
|
19
|
+
this.locked = true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
feedHeading(heading) {
|
|
23
|
+
|
|
24
|
+
if (!isnumber(this.waitingOrientation)) {
|
|
25
|
+
throw new Error('HeadingUnlocker has not been initialized by setWaitingOrientation');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!this.locked) {
|
|
29
|
+
throw new Error('HeadingUnlocker is already unlocked');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (Math.abs(diffAngle(heading, this.waitingOrientation)) < LO_ANGLE) {
|
|
33
|
+
this.locked = false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return this.locked;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default HeadingUnlocker;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import chai from 'chai';
|
|
2
|
+
import chaiAlmost from 'chai-almost';
|
|
3
|
+
|
|
4
|
+
import HeadingUnlocker from './HeadingUnlocker';
|
|
5
|
+
|
|
6
|
+
const expect = chai.expect;
|
|
7
|
+
chai.use(chaiAlmost());
|
|
8
|
+
|
|
9
|
+
describe('HeadingUnlocker test short first edge', () => {
|
|
10
|
+
|
|
11
|
+
it('Should return the good value', () => {
|
|
12
|
+
|
|
13
|
+
const headingUnlocker = new HeadingUnlocker();
|
|
14
|
+
headingUnlocker.setWaitingOrientation(102 * Math.PI / 180);
|
|
15
|
+
|
|
16
|
+
headingUnlocker.feedHeading(0 * Math.PI / 180);
|
|
17
|
+
expect(headingUnlocker.locked).to.be.true;
|
|
18
|
+
|
|
19
|
+
headingUnlocker.feedHeading(130 * Math.PI / 180);
|
|
20
|
+
expect(headingUnlocker.locked).to.be.true;
|
|
21
|
+
|
|
22
|
+
headingUnlocker.feedHeading(100 * Math.PI / 180);
|
|
23
|
+
expect(headingUnlocker.locked).to.be.false;
|
|
24
|
+
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* eslint max-statements: ["error", 27]*/
|
|
2
|
+
import { WGS84UserPosition } from '@wemap/geo';
|
|
3
|
+
|
|
4
|
+
// Generated locations by second
|
|
5
|
+
const GEN_FREQUENCY = 60;
|
|
6
|
+
|
|
7
|
+
// Min and max time for flyby (in second)
|
|
8
|
+
const MIN_FLYBY_TIME = 0.25;
|
|
9
|
+
const MAX_FLYBY_TIME = 1;
|
|
10
|
+
|
|
11
|
+
class Smoother {
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
this.locationsQueue = [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Calculate smoothed positions for the next milliseconds given a new location
|
|
19
|
+
*/
|
|
20
|
+
generateNextLocations(_newLocation, flyby = false) {
|
|
21
|
+
|
|
22
|
+
if (!(_newLocation instanceof WGS84UserPosition)) {
|
|
23
|
+
throw new TypeError('newLocation is not instance of WGS84UserPosition');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const newLocation = _newLocation.clone();
|
|
27
|
+
if (!newLocation.hasOwnProperty('time')) {
|
|
28
|
+
throw new Error('newLocation does not have time property');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!this.previousLocation) {
|
|
32
|
+
this.previousLocation = newLocation;
|
|
33
|
+
this.locationsQueue.push(this.previousLocation);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const distance = this.previousLocation.distanceTo(newLocation);
|
|
38
|
+
const azimuth = this.previousLocation.bearingTo(newLocation);
|
|
39
|
+
|
|
40
|
+
let refTimestamp = newLocation.time;
|
|
41
|
+
|
|
42
|
+
const queueLength = this.locationsQueue.length;
|
|
43
|
+
if (queueLength) {
|
|
44
|
+
refTimestamp = Math.max(refTimestamp, this.locationsQueue[queueLength - 1].time);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let diffTime = newLocation.time - this.previousLocation.time;
|
|
48
|
+
|
|
49
|
+
if (flyby) {
|
|
50
|
+
diffTime = MAX_FLYBY_TIME;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const nSamples = GEN_FREQUENCY * Math.min(Math.max(MIN_FLYBY_TIME, diffTime), MAX_FLYBY_TIME);
|
|
54
|
+
|
|
55
|
+
let i = 1;
|
|
56
|
+
while (i < nSamples + 1) {
|
|
57
|
+
i = Math.min(i, nSamples);
|
|
58
|
+
const smoothedLocation = this.previousLocation.destinationPoint(distance * i / nSamples, azimuth);
|
|
59
|
+
smoothedLocation.time = refTimestamp + (i - 1) / GEN_FREQUENCY;
|
|
60
|
+
this.locationsQueue.push(smoothedLocation);
|
|
61
|
+
i++;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.previousLocation = newLocation;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Pull locations until timestamp and return the last one.
|
|
69
|
+
* @param {*} timestamp location returned is the last before this timestamp
|
|
70
|
+
* @returns last known location before timestamp, undefined otherwise
|
|
71
|
+
*/
|
|
72
|
+
pullLocation(timestamp) {
|
|
73
|
+
|
|
74
|
+
if ((typeof timestamp === 'undefined' || !timestamp)
|
|
75
|
+
&& this.locationsQueue.length > 0) {
|
|
76
|
+
const output = this.locationsQueue[this.locationsQueue.length - 1];
|
|
77
|
+
this.locationsQueue = [];
|
|
78
|
+
return output;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let location;
|
|
82
|
+
while (this.locationsQueue.length && this.locationsQueue[0].time <= timestamp) {
|
|
83
|
+
location = this.locationsQueue.shift();
|
|
84
|
+
}
|
|
85
|
+
return location;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default Smoother;
|