@wemap/positioning 1.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 +479 -0
- package/.nvmrc +1 -0
- package/babel.config.js +11 -0
- package/config.json +7 -0
- package/debug/index.html +15 -0
- package/debug/index.old.html +37 -0
- package/package.json +82 -0
- package/scripts/release-github.js +216 -0
- package/src/Constants.js +11 -0
- package/src/NavigationHandler.js +244 -0
- package/src/Pose.js +8 -0
- package/src/attitude/Attitude.js +65 -0
- package/src/attitude/AttitudeHandler.js +343 -0
- package/src/attitude/EkfAttitude.js +238 -0
- package/src/attitude/EkfAttitude.spec.js +116 -0
- package/src/components/AbsoluteAttitude.jsx +136 -0
- package/src/components/Imu.jsx +89 -0
- package/src/components/LocationSource.jsx +434 -0
- package/src/components/Logger.jsx +113 -0
- package/src/components/NavigationDebugApp.jsx +106 -0
- package/src/components/Others.jsx +121 -0
- package/src/components/RelativeAttitude.jsx +104 -0
- package/src/components/Utils.js +35 -0
- package/src/components/index.js +13 -0
- package/src/index.js +9 -0
- package/src/providers/FixedLocationImuLocationSource.js +66 -0
- package/src/providers/GnssLocationSource.js +118 -0
- package/src/providers/GnssPdrLocationSource.js +182 -0
- package/src/providers/IPLocationSource.js +96 -0
- package/src/providers/LocationSource.js +290 -0
- package/src/providers/PdrLocationSource.js +312 -0
- package/src/providers/ProvidersLogger.js +77 -0
- package/src/providers/pdr/HeadingUnlocker.js +41 -0
- package/src/providers/pdr/HeadingUnlocker.spec.js +26 -0
- package/src/providers/pdr/Smoother.js +90 -0
- package/src/providers/pdr/Smoother.spec.js +424 -0
- package/src/providers/pdr/ThugDetector.js +37 -0
- package/src/providers/steps/StepDetection.js +7 -0
- package/src/providers/steps/StepDetectionLadetto.js +67 -0
- package/src/providers/steps/StepDetectionMinMaxPeaks.js +80 -0
- package/src/providers/steps/StepDetectionMinMaxPeaks2.js +108 -0
- package/src/sensors/SensorsCompatibility.js +484 -0
- package/src/sensors/SensorsCompatibility.spec.js +270 -0
- package/src/sensors/SensorsLogger.js +94 -0
- package/src/sensors/SensorsLoggerUtils.js +35 -0
- package/src.new/NavigationHandler.js +62 -0
- package/src.new/index.js +3 -0
- package/src.new/providers/FakeLocationSource.js +39 -0
- package/webpack/webpack.common.js +20 -0
- package/webpack/webpack.dev.js +24 -0
- package/webpack/webpack.prod.js +15 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
const dotenv = require('dotenv');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const git = require('simple-git/promise')('.').silent(true);
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const npm = require('npm');
|
|
6
|
+
const requestp = require('request-promise');
|
|
7
|
+
|
|
8
|
+
const config = require('../config.json');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Check GITHUB_TOKEN in .env file
|
|
12
|
+
*/
|
|
13
|
+
const result = dotenv.config();
|
|
14
|
+
if (result.error) {
|
|
15
|
+
console.error('.env does not exist. '
|
|
16
|
+
+ 'You have to create a .env file at the root directory');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const ENV = result.parsed;
|
|
20
|
+
if (!ENV.GITHUB_TOKEN) {
|
|
21
|
+
console.error('GITHUB_TOKEN does not exist in .env file. '
|
|
22
|
+
+ 'More info here: https://github.com/settings/tokens');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Github Url and Headers
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const GITHUB_API_URL = 'https://api.github.com';
|
|
31
|
+
const GITHUB_UPLOAD_URL = 'https://uploads.github.com';
|
|
32
|
+
|
|
33
|
+
const HEADERS = {
|
|
34
|
+
'Authorization': 'token ' + ENV.GITHUB_TOKEN,
|
|
35
|
+
'User-Agent': 'NodeJS/Release-' + config.githubRepoName
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function checkGithubToken() {
|
|
39
|
+
|
|
40
|
+
const uri = GITHUB_API_URL;
|
|
41
|
+
const headers = HEADERS;
|
|
42
|
+
|
|
43
|
+
return requestp({
|
|
44
|
+
method: 'GET',
|
|
45
|
+
uri,
|
|
46
|
+
headers,
|
|
47
|
+
json: true
|
|
48
|
+
}).catch(e => {
|
|
49
|
+
return Promise.reject(e.message);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function createRelease(version) {
|
|
54
|
+
|
|
55
|
+
const uri = GITHUB_API_URL + '/repos/' + config.githubRepoOwner + '/'
|
|
56
|
+
+ config.githubRepoName + '/releases';
|
|
57
|
+
const headers = Object.assign({ 'Content-Type': 'application/json' }, HEADERS);
|
|
58
|
+
const inputData = {
|
|
59
|
+
'tag_name': version,
|
|
60
|
+
'target_commitish': 'master',
|
|
61
|
+
'name': version
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return requestp({
|
|
65
|
+
method: 'POST',
|
|
66
|
+
uri,
|
|
67
|
+
headers,
|
|
68
|
+
body: inputData,
|
|
69
|
+
json: true
|
|
70
|
+
}).catch(e => {
|
|
71
|
+
return Promise.reject(e.message);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
function uploadFile(releaseId, fileName, filePath) {
|
|
77
|
+
|
|
78
|
+
const stats = fs.statSync(filePath);
|
|
79
|
+
const fileSizeInBytes = stats.size;
|
|
80
|
+
|
|
81
|
+
const uri = GITHUB_UPLOAD_URL + '/repos/' + config.githubRepoOwner + '/'
|
|
82
|
+
+ config.githubRepoName + '/releases/' + releaseId
|
|
83
|
+
+ '/assets?name=' + fileName;
|
|
84
|
+
|
|
85
|
+
const headers = Object.assign({
|
|
86
|
+
'Content-Type': 'application/javascript',
|
|
87
|
+
'Content-length': fileSizeInBytes
|
|
88
|
+
}, HEADERS);
|
|
89
|
+
|
|
90
|
+
return requestp({
|
|
91
|
+
method: 'POST',
|
|
92
|
+
uri,
|
|
93
|
+
headers,
|
|
94
|
+
body: fs.createReadStream(filePath)
|
|
95
|
+
}).catch(e => {
|
|
96
|
+
return e.message;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function revertNpmVersion() {
|
|
101
|
+
return git.reset(['--hard', 'HEAD^1'])
|
|
102
|
+
.then(() => git.tag(['-d', 'v1.1.0']))
|
|
103
|
+
.catch(err => console.error(err));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const npmLoaderPromise = new Promise((resolve, reject) => {
|
|
107
|
+
npm.load(err => {
|
|
108
|
+
if (err) {
|
|
109
|
+
reject(err);
|
|
110
|
+
} else {
|
|
111
|
+
resolve();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
function main() {
|
|
118
|
+
|
|
119
|
+
const versionsList = ['minor', 'major', 'patch'];
|
|
120
|
+
|
|
121
|
+
let isVersionCreated = false;
|
|
122
|
+
const isPushed = false;
|
|
123
|
+
|
|
124
|
+
Promise.resolve()
|
|
125
|
+
// Check github token as soon as possible to avoid to revert a push
|
|
126
|
+
.then(checkGithubToken)
|
|
127
|
+
|
|
128
|
+
// Check if current branch name corresponds to "releaseBranch"
|
|
129
|
+
.then(async () => {
|
|
130
|
+
const res = await git.raw(['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
131
|
+
if (res.trim() !== config.releaseBranch) {
|
|
132
|
+
return Promise.reject('You are not on the ' + config.releaseBranch + ' branch');
|
|
133
|
+
}
|
|
134
|
+
return Promise.resolve();
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// Check if git repo is clean before running npm version
|
|
138
|
+
.then(async () => {
|
|
139
|
+
const res = await git.raw(['status', '--porcelain']);
|
|
140
|
+
if (res) {
|
|
141
|
+
return Promise.reject('Your git working directory not clean');
|
|
142
|
+
}
|
|
143
|
+
return Promise.resolve();
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
// Update npm version with npm version argv[2]
|
|
147
|
+
.then(async () => {
|
|
148
|
+
const args = process.argv;
|
|
149
|
+
if (args.length < 3) {
|
|
150
|
+
throw new Error('version argument is missing');
|
|
151
|
+
}
|
|
152
|
+
if (!versionsList.includes(args[2])) {
|
|
153
|
+
throw new Error('version argument is unknown, it should be: ' + versionsList.join(','));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await npmLoaderPromise;
|
|
157
|
+
return new Promise((resolve, reject) => {
|
|
158
|
+
npm.commands.version([args[2]], err => {
|
|
159
|
+
if (err) {
|
|
160
|
+
reject(err);
|
|
161
|
+
} else {
|
|
162
|
+
isVersionCreated = true;
|
|
163
|
+
resolve();
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
// Build the project in production mode
|
|
170
|
+
.then(async () => {
|
|
171
|
+
await npmLoaderPromise;
|
|
172
|
+
return new Promise((resolve, reject) => {
|
|
173
|
+
npm.commands.run(['build'], err => {
|
|
174
|
+
if (err) {
|
|
175
|
+
reject(err);
|
|
176
|
+
} else {
|
|
177
|
+
resolve();
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
// Push release to origin
|
|
184
|
+
.then(() => git.push('origin', config.releaseBranch, ['--tags']))
|
|
185
|
+
|
|
186
|
+
// Get tag name
|
|
187
|
+
.then(async () => {
|
|
188
|
+
const res = await git.raw(['describe']);
|
|
189
|
+
// TODO this condition can be improved
|
|
190
|
+
if (!res || res[0] !== 'v') {
|
|
191
|
+
return Promise.reject('Could not find tag name');
|
|
192
|
+
}
|
|
193
|
+
return Promise.resolve(res.trim());
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
// Create the release
|
|
197
|
+
.then((tagName) => createRelease(tagName))
|
|
198
|
+
|
|
199
|
+
// Upload file
|
|
200
|
+
.then(response => {
|
|
201
|
+
const filePath = path.join(config.distFolder, config.distFileName);
|
|
202
|
+
uploadFile(response.id, config.distFileName, filePath);
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
.then(() => {
|
|
206
|
+
console.log('Release finished with success');
|
|
207
|
+
})
|
|
208
|
+
.catch(e => {
|
|
209
|
+
if (isVersionCreated && !isPushed) {
|
|
210
|
+
revertNpmVersion();
|
|
211
|
+
}
|
|
212
|
+
console.error('error', e);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
main();
|
package/src/Constants.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import noop from 'lodash.noop';
|
|
2
|
+
|
|
3
|
+
import { WGS84UserPosition, Itinerary } from '@wemap/geo';
|
|
4
|
+
|
|
5
|
+
import LocationSource from './providers/LocationSource';
|
|
6
|
+
import IPLocationSource from './providers/IPLocationSource';
|
|
7
|
+
import GnssLocationSource from './providers/GnssLocationSource';
|
|
8
|
+
import PdrLocationSource from './providers/PdrLocationSource';
|
|
9
|
+
import GnssPdrLocationSource from './providers/GnssPdrLocationSource';
|
|
10
|
+
import FixedLocationImuLocationSource from './providers/FixedLocationImuLocationSource';
|
|
11
|
+
import SensorsLogger from './sensors/SensorsLogger';
|
|
12
|
+
|
|
13
|
+
const DEFAULT_ALTITUDE = 1.6;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
class NavigationHandler {
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Constructor of NavigationHandler
|
|
22
|
+
* @param {Function} callbackOnStart callback when navigation starts
|
|
23
|
+
* @param {Function} callbackOnStop callback when navigation stops
|
|
24
|
+
* @param {Function} callbackOnNewPose callback on new pose
|
|
25
|
+
* @public
|
|
26
|
+
*/
|
|
27
|
+
constructor(callbackOnStart, callbackOnStop, callbackOnNewPose) {
|
|
28
|
+
this.callback = this.callback.bind(this);
|
|
29
|
+
|
|
30
|
+
this.callbackOnStart = callbackOnStart || noop;
|
|
31
|
+
this.callbackOnStop = callbackOnStop || noop;
|
|
32
|
+
this.callbackOnNewPose = callbackOnNewPose || noop;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Starts navigation from an initial position an initial heading
|
|
38
|
+
* @param {*} initialPosition initial position {lat: xx, lng: xx}
|
|
39
|
+
* @param {Number} initialHeading initial heading in radians and clockwise: north: 0, east = PI/2
|
|
40
|
+
* @param {Array} path an optional itinerary (list of 2D points [lng, lat])
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
startRelative(initialPosition, initialHeading, path = null) {
|
|
44
|
+
|
|
45
|
+
const initialPositionWgs84 = new WGS84UserPosition(
|
|
46
|
+
initialPosition.lat,
|
|
47
|
+
initialPosition.lng,
|
|
48
|
+
initialPosition.alt || DEFAULT_ALTITUDE
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// stop current location source if already started
|
|
52
|
+
if (this.locationSource) {
|
|
53
|
+
this.locationSource.stop();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.locationSource = new PdrLocationSource(this.callback, {
|
|
57
|
+
'stepdetectionlocker': true,
|
|
58
|
+
'smoother': true
|
|
59
|
+
});
|
|
60
|
+
this.locationSource.setLogger(this.logger);
|
|
61
|
+
|
|
62
|
+
if (path) {
|
|
63
|
+
const itinerary = Itinerary.fromPoints(path);
|
|
64
|
+
this.locationSource.enableMapMatching(itinerary);
|
|
65
|
+
this.locationSource.setItinerary(itinerary);
|
|
66
|
+
}
|
|
67
|
+
this.locationSource.setLocation(initialPositionWgs84);
|
|
68
|
+
this.locationSource.setHeading(initialHeading);
|
|
69
|
+
const promise = this.locationSource.start();
|
|
70
|
+
|
|
71
|
+
this.callbackOnStart();
|
|
72
|
+
return promise;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Starts navigation handler without any knowledge on starting point
|
|
77
|
+
* @param {Array} path an optional itinerary (list of 2D points [lng, lat])
|
|
78
|
+
* @public
|
|
79
|
+
*/
|
|
80
|
+
startAbsolute(path) {
|
|
81
|
+
// stop if already started
|
|
82
|
+
if (this.locationSource) {
|
|
83
|
+
this.locationSource.stop();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const itinerary = Itinerary.fromPoints(path);
|
|
87
|
+
this.locationSource = new GnssPdrLocationSource(this.callback);
|
|
88
|
+
this.locationSource.enableMapMatching(itinerary);
|
|
89
|
+
this.locationSource.setItinerary(itinerary);
|
|
90
|
+
this.locationSource.setLogger(this.logger);
|
|
91
|
+
|
|
92
|
+
const promise = this.locationSource.start();
|
|
93
|
+
this.callbackOnStart();
|
|
94
|
+
return promise;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
locateme(
|
|
98
|
+
options = {
|
|
99
|
+
ip: true,
|
|
100
|
+
browser: true,
|
|
101
|
+
attitude: true
|
|
102
|
+
}
|
|
103
|
+
) {
|
|
104
|
+
const useIPProvider = !options.hasOwnProperty('ip') || options.ip === true;
|
|
105
|
+
const useBrowserProvider = !options.hasOwnProperty('browser') || options.browser === true;
|
|
106
|
+
const useImu = !options.hasOwnProperty('attitude') || options.attitude === true;
|
|
107
|
+
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
const gnss = new GnssLocationSource(pose => {
|
|
110
|
+
resolve(pose);
|
|
111
|
+
this.callback(pose);
|
|
112
|
+
}, useImu);
|
|
113
|
+
|
|
114
|
+
const ip = new IPLocationSource(pose => {
|
|
115
|
+
resolve(pose);
|
|
116
|
+
this.callback(pose);
|
|
117
|
+
}, useImu);
|
|
118
|
+
|
|
119
|
+
if (
|
|
120
|
+
this.locationSource
|
|
121
|
+
&& LocationSource.getMostAccurateLocationSource(this.locationSource, gnss) === this.locationSource
|
|
122
|
+
) {
|
|
123
|
+
resolve(this.locationSource.getPose());
|
|
124
|
+
} else {
|
|
125
|
+
if (this.locationSource) {
|
|
126
|
+
this.locationSource.stop();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (useBrowserProvider) {
|
|
130
|
+
this.locationSource = gnss;
|
|
131
|
+
gnss.start().catch(e => {
|
|
132
|
+
if (useIPProvider) {
|
|
133
|
+
this.locationSource = ip;
|
|
134
|
+
ip.start();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
reject(e);
|
|
138
|
+
});
|
|
139
|
+
} else if (useIPProvider) {
|
|
140
|
+
this.locationSource = ip;
|
|
141
|
+
ip.start();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Stop relative, absolute or locateme navigation if started
|
|
150
|
+
* @public
|
|
151
|
+
*/
|
|
152
|
+
stop() {
|
|
153
|
+
if (
|
|
154
|
+
this.locationSource
|
|
155
|
+
) {
|
|
156
|
+
this.locationSource.stop();
|
|
157
|
+
this.locationSource = null;
|
|
158
|
+
}
|
|
159
|
+
this.callbackOnStop();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @see LocationSource#getProjectionOnNetwork()
|
|
165
|
+
* @public
|
|
166
|
+
*/
|
|
167
|
+
getProjectionOnNetwork(location) {
|
|
168
|
+
return this.locationSource.getProjectionOnNetwork(location);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @see LocationSource#getItineraryInfo()
|
|
173
|
+
* @public
|
|
174
|
+
*/
|
|
175
|
+
getItineraryInfo(location) {
|
|
176
|
+
return this.locationSource.getItineraryInfo(location);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
forceUserLocation(location) {
|
|
180
|
+
if (
|
|
181
|
+
this.locationSource
|
|
182
|
+
&& (!(this.locationSource instanceof FixedLocationImuLocationSource) || !location)
|
|
183
|
+
) {
|
|
184
|
+
this.locationSource.stop();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (location) {
|
|
188
|
+
if (
|
|
189
|
+
!this.locationSource
|
|
190
|
+
|| !(this.locationSource instanceof FixedLocationImuLocationSource)
|
|
191
|
+
) {
|
|
192
|
+
this.locationSource = new FixedLocationImuLocationSource(this.callback);
|
|
193
|
+
this.locationSource.start(location);
|
|
194
|
+
} else {
|
|
195
|
+
this.locationSource.setLocation(location);
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
this.locationSource = null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* @private
|
|
204
|
+
*/
|
|
205
|
+
callback(pose) {
|
|
206
|
+
this.callbackOnNewPose(pose);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Assign a SensorsLogger to LocationSource and start recording.
|
|
211
|
+
* This logger will be fed with sensors output.
|
|
212
|
+
* Logger is not working on locateme()
|
|
213
|
+
* @param {SensorsLogger} logger An instance of SensorsLogger.
|
|
214
|
+
* @public
|
|
215
|
+
*/
|
|
216
|
+
startLogger(logger) {
|
|
217
|
+
if (!(logger instanceof SensorsLogger)) {
|
|
218
|
+
throw new Error('logger is not an instance of SensorsLogger');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
this.logger = logger;
|
|
222
|
+
|
|
223
|
+
if (this.locationSource) {
|
|
224
|
+
this.locationSource.setLogger(this.logger);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Stop recording sensors
|
|
230
|
+
* @public
|
|
231
|
+
*/
|
|
232
|
+
stopLogger() {
|
|
233
|
+
this.logger = null;
|
|
234
|
+
if (this.locationSource) {
|
|
235
|
+
this.locationSource.setLogger(this.logger);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
static get DEFAULT_ALTITUDE() {
|
|
240
|
+
return DEFAULT_ALTITUDE;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export default NavigationHandler;
|
package/src/Pose.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Rotations, Utils as MathUtils, Quaternion
|
|
3
|
+
} from '@wemap/maths';
|
|
4
|
+
|
|
5
|
+
const rotxM90 = Quaternion.fromAxisAngle([1, 0, 0], -Math.PI / 2);
|
|
6
|
+
|
|
7
|
+
class Attitude {
|
|
8
|
+
|
|
9
|
+
quaternion = [1, 0, 0, 0];
|
|
10
|
+
quaternionThreeJsV = null;
|
|
11
|
+
headingV = null;
|
|
12
|
+
headingDegreesV = null;
|
|
13
|
+
eulerAnglesV = null;
|
|
14
|
+
eulerAnglesDegreesV = null;
|
|
15
|
+
|
|
16
|
+
constructor(quaternion) {
|
|
17
|
+
this.quaternion = quaternion;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get eulerAngles() {
|
|
21
|
+
if (!this.eulerAnglesV) {
|
|
22
|
+
this.eulerAnglesV = Rotations.quaternionToEulerZXY(this.quaternion);
|
|
23
|
+
}
|
|
24
|
+
return this.eulerAnglesV;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get eulerAnglesDegrees() {
|
|
28
|
+
if (!this.eulerAnglesDegreesV) {
|
|
29
|
+
this.eulerAnglesDegreesV = Rotations.quaternionToEulerZXYDegrees(this.quaternion);
|
|
30
|
+
}
|
|
31
|
+
return this.eulerAnglesDegreesV;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get heading() {
|
|
35
|
+
if (!this.headingV) {
|
|
36
|
+
this.headingV = Rotations.getHeadingFromQuaternion(this.quaternion) + MathUtils.deg2rad(window.orientation || 0);
|
|
37
|
+
}
|
|
38
|
+
return this.headingV;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get headingDegrees() {
|
|
42
|
+
if (!this.headingDegreesV) {
|
|
43
|
+
this.headingDegreesV = MathUtils.rad2deg(Rotations.getHeadingFromQuaternion(this.quaternion)) + (window.orientation || 0);
|
|
44
|
+
}
|
|
45
|
+
return this.headingDegreesV;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get quaternionThreeJs() {
|
|
49
|
+
if (!this.quaternionThreeJsV) {
|
|
50
|
+
this.quaternionThreeJsV = Quaternion.wxyz2xyzw(Quaternion.multiply(rotxM90, this.quaternion));
|
|
51
|
+
}
|
|
52
|
+
return this.quaternionThreeJsV;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
toMessage() {
|
|
56
|
+
return this.quaternion;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static fromMessage(message) {
|
|
60
|
+
return new Attitude(message);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export default Attitude;
|