gps_pvt 0.1.1
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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +10 -0
- data/README.md +86 -0
- data/Rakefile +86 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/ext/gps_pvt/Coordinate/Coordinate_wrap.cxx +6613 -0
- data/ext/gps_pvt/GPS/GPS_wrap.cxx +16019 -0
- data/ext/gps_pvt/SylphideMath/SylphideMath_wrap.cxx +21050 -0
- data/ext/gps_pvt/extconf.rb +70 -0
- data/ext/ninja-scan-light/tool/navigation/EGM.h +2971 -0
- data/ext/ninja-scan-light/tool/navigation/GPS.h +2432 -0
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver.h +479 -0
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver_Base.h +1081 -0
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver_MultiFrequency.h +199 -0
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver_RAIM.h +210 -0
- data/ext/ninja-scan-light/tool/navigation/MagneticField.h +928 -0
- data/ext/ninja-scan-light/tool/navigation/NTCM.h +211 -0
- data/ext/ninja-scan-light/tool/navigation/RINEX.h +1781 -0
- data/ext/ninja-scan-light/tool/navigation/WGS84.h +186 -0
- data/ext/ninja-scan-light/tool/navigation/coordinate.h +406 -0
- data/ext/ninja-scan-light/tool/param/bit_array.h +145 -0
- data/ext/ninja-scan-light/tool/param/complex.h +558 -0
- data/ext/ninja-scan-light/tool/param/matrix.h +4049 -0
- data/ext/ninja-scan-light/tool/param/matrix_fixed.h +665 -0
- data/ext/ninja-scan-light/tool/param/matrix_special.h +562 -0
- data/ext/ninja-scan-light/tool/param/quaternion.h +765 -0
- data/ext/ninja-scan-light/tool/param/vector3.h +651 -0
- data/ext/ninja-scan-light/tool/swig/Coordinate.i +177 -0
- data/ext/ninja-scan-light/tool/swig/GPS.i +1102 -0
- data/ext/ninja-scan-light/tool/swig/SylphideMath.i +1234 -0
- data/ext/ninja-scan-light/tool/swig/extconf.rb +5 -0
- data/ext/ninja-scan-light/tool/swig/makefile +53 -0
- data/ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb +417 -0
- data/ext/ninja-scan-light/tool/swig/spec/SylphideMath_spec.rb +489 -0
- data/gps_pvt.gemspec +57 -0
- data/lib/gps_pvt/receiver.rb +375 -0
- data/lib/gps_pvt/ubx.rb +148 -0
- data/lib/gps_pvt/version.rb +5 -0
- data/lib/gps_pvt.rb +9 -0
- data/sig/gps_pvt.rbs +4 -0
- metadata +117 -0
@@ -0,0 +1,1081 @@
|
|
1
|
+
/**
|
2
|
+
* @file GPS solver base
|
3
|
+
*/
|
4
|
+
/*
|
5
|
+
* Copyright (c) 2020, M.Naruoka (fenrir)
|
6
|
+
* All rights reserved.
|
7
|
+
*
|
8
|
+
* Redistribution and use in source and binary forms, with or without modification,
|
9
|
+
* are permitted provided that the following conditions are met:
|
10
|
+
*
|
11
|
+
* - Redistributions of source code must retain the above copyright notice,
|
12
|
+
* this list of conditions and the following disclaimer.
|
13
|
+
* - Redistributions in binary form must reproduce the above copyright notice,
|
14
|
+
* this list of conditions and the following disclaimer in the documentation
|
15
|
+
* and/or other materials provided with the distribution.
|
16
|
+
* - Neither the name of the naruoka.org nor the names of its contributors
|
17
|
+
* may be used to endorse or promote products derived from this software
|
18
|
+
* without specific prior written permission.
|
19
|
+
*
|
20
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
21
|
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
22
|
+
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
23
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
|
24
|
+
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
25
|
+
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
26
|
+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
27
|
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
28
|
+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
29
|
+
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
30
|
+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
31
|
+
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
32
|
+
*
|
33
|
+
*/
|
34
|
+
|
35
|
+
#ifndef __GPS_SOLVER_BASE_H__
|
36
|
+
#define __GPS_SOLVER_BASE_H__
|
37
|
+
|
38
|
+
#include <vector>
|
39
|
+
#include <map>
|
40
|
+
#include <utility>
|
41
|
+
|
42
|
+
#include <cmath>
|
43
|
+
#include <cstring>
|
44
|
+
#include <cstdlib>
|
45
|
+
|
46
|
+
#include "param/matrix.h"
|
47
|
+
#include "param/bit_array.h"
|
48
|
+
#include "GPS.h"
|
49
|
+
|
50
|
+
template <class FloatT>
|
51
|
+
struct GPS_Solver_Base {
|
52
|
+
virtual ~GPS_Solver_Base() {}
|
53
|
+
|
54
|
+
typedef FloatT float_t;
|
55
|
+
typedef Matrix<float_t> matrix_t;
|
56
|
+
|
57
|
+
typedef int prn_t;
|
58
|
+
|
59
|
+
typedef GPS_SpaceNode<float_t> space_node_t;
|
60
|
+
typedef typename space_node_t::gps_time_t gps_time_t;
|
61
|
+
|
62
|
+
typedef typename space_node_t::xyz_t xyz_t;
|
63
|
+
typedef typename space_node_t::llh_t llh_t;
|
64
|
+
typedef typename space_node_t::enu_t enu_t;
|
65
|
+
|
66
|
+
struct pos_t {
|
67
|
+
xyz_t xyz;
|
68
|
+
llh_t llh;
|
69
|
+
matrix_t ecef2enu() const {
|
70
|
+
float_t buf[3][3];
|
71
|
+
llh.rotation_ecef2enu(buf);
|
72
|
+
return matrix_t(3, 3, (float_t *)buf);
|
73
|
+
}
|
74
|
+
};
|
75
|
+
|
76
|
+
typedef std::vector<std::pair<prn_t, float_t> > prn_obs_t;
|
77
|
+
|
78
|
+
static prn_obs_t difference(
|
79
|
+
const prn_obs_t &operand, const prn_obs_t &argument,
|
80
|
+
const FloatT &scaling = FloatT(1)) {
|
81
|
+
prn_obs_t res;
|
82
|
+
for(typename prn_obs_t::const_iterator it(operand.begin()), it_end(operand.end()); it != it_end; ++it){
|
83
|
+
for(typename prn_obs_t::const_iterator it2(argument.begin()), it2_end(argument.end()); it2 != it2_end; ++it2){
|
84
|
+
if(it->first != it2->first){continue;}
|
85
|
+
res.push_back(std::make_pair(it->first, (it->second - it2->second) * scaling));
|
86
|
+
break;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
return res;
|
90
|
+
}
|
91
|
+
|
92
|
+
struct measurement_items_t {
|
93
|
+
enum {
|
94
|
+
L1_PSEUDORANGE,
|
95
|
+
L1_DOPPLER,
|
96
|
+
L1_CARRIER_PHASE,
|
97
|
+
L1_RANGE_RATE,
|
98
|
+
L1_PSEUDORANGE_SIGMA, // standard deviation(sigma)
|
99
|
+
L1_DOPPLER_SIGMA,
|
100
|
+
L1_CARRIER_PHASE_SIGMA,
|
101
|
+
L1_RANGE_RATE_SIGMA,
|
102
|
+
L1_SIGNAL_STRENGTH_dBHz,
|
103
|
+
L1_LOCK_SEC,
|
104
|
+
MEASUREMENT_ITEMS_PREDEFINED,
|
105
|
+
};
|
106
|
+
};
|
107
|
+
typedef std::map<prn_t, std::map<int, float_t> > measurement_t;
|
108
|
+
|
109
|
+
struct measurement_item_set_t {
|
110
|
+
struct {
|
111
|
+
int i, i_sigma;
|
112
|
+
} pseudorange, doppler, carrier_phase, range_rate;
|
113
|
+
int signal_strength, lock_sec;
|
114
|
+
};
|
115
|
+
static const measurement_item_set_t L1CA;
|
116
|
+
|
117
|
+
struct measurement_util_t {
|
118
|
+
static prn_obs_t gather(
|
119
|
+
const measurement_t &measurement,
|
120
|
+
const typename measurement_t::mapped_type::key_type &key,
|
121
|
+
const FloatT &scaling = FloatT(1)){
|
122
|
+
prn_obs_t res;
|
123
|
+
for(typename measurement_t::const_iterator it(measurement.begin()), it_end(measurement.end());
|
124
|
+
it != it_end; ++it){
|
125
|
+
typename measurement_t::mapped_type::const_iterator it2(it->second.find(key));
|
126
|
+
if(it2 == it->second.end()){continue;}
|
127
|
+
res.push_back(std::make_pair(it->first, it2->second * scaling));
|
128
|
+
}
|
129
|
+
return res;
|
130
|
+
}
|
131
|
+
static void merge(
|
132
|
+
measurement_t &measurement,
|
133
|
+
const prn_obs_t &new_item,
|
134
|
+
const typename measurement_t::mapped_type::key_type &key) {
|
135
|
+
for(typename prn_obs_t::const_iterator it(new_item.begin()), it_end(new_item.end());
|
136
|
+
it != it_end; ++it){
|
137
|
+
measurement[it->first].insert(std::make_pair(key, it->second));
|
138
|
+
}
|
139
|
+
}
|
140
|
+
};
|
141
|
+
|
142
|
+
/**
|
143
|
+
* Find value corresponding to key from key-value map
|
144
|
+
* of measurement_t::mapped_type
|
145
|
+
* @param values key-value map
|
146
|
+
* @param key key
|
147
|
+
* @param buf buffer into which found value is stored
|
148
|
+
* @return (float_t *) When value is found, pointer of buf will be returned.
|
149
|
+
* Otherwise, NULL is returned.
|
150
|
+
*/
|
151
|
+
static const float_t *find_value(
|
152
|
+
const typename measurement_t::mapped_type &values,
|
153
|
+
const typename measurement_t::mapped_type::key_type &key,
|
154
|
+
float_t &buf) {
|
155
|
+
typename measurement_t::mapped_type::const_iterator it;
|
156
|
+
if((it = values.find(key)) != values.end()){
|
157
|
+
return &(buf = it->second);
|
158
|
+
}
|
159
|
+
return NULL;
|
160
|
+
}
|
161
|
+
|
162
|
+
struct range_error_t {
|
163
|
+
enum {
|
164
|
+
#define make_entry(name) \
|
165
|
+
name, \
|
166
|
+
MASK_ ## name = (1 << name), \
|
167
|
+
DUMMY_ ## name = name
|
168
|
+
make_entry(RECEIVER_CLOCK),
|
169
|
+
make_entry(SATELLITE_CLOCK),
|
170
|
+
make_entry(IONOSPHERIC),
|
171
|
+
make_entry(TROPOSPHERIC),
|
172
|
+
#undef make_entry
|
173
|
+
NUM_OF_ERRORS,
|
174
|
+
};
|
175
|
+
int unknown_flag;
|
176
|
+
float_t value[NUM_OF_ERRORS];
|
177
|
+
static const range_error_t not_corrected;
|
178
|
+
};
|
179
|
+
|
180
|
+
// TODO These range and rate functions will be overridden in subclass to support multi-frequency
|
181
|
+
/**
|
182
|
+
* Extract range information from measurement per satellite
|
183
|
+
* @param values measurement[prn]
|
184
|
+
* @param buf buffer into which range is stored
|
185
|
+
* @param error optional argument in which error components of range will be returned
|
186
|
+
* @return If valid range information is found, the pointer of buf will be returned; otherwise NULL
|
187
|
+
*/
|
188
|
+
virtual const float_t *range(
|
189
|
+
const typename measurement_t::mapped_type &values, float_t &buf,
|
190
|
+
range_error_t *error = NULL) const {
|
191
|
+
if(error){
|
192
|
+
*error = range_error_t::not_corrected;
|
193
|
+
}
|
194
|
+
return find_value(values, measurement_items_t::L1_PSEUDORANGE, buf);
|
195
|
+
}
|
196
|
+
|
197
|
+
virtual const float_t *range_sigma(
|
198
|
+
const typename measurement_t::mapped_type &values, float_t &buf) const {
|
199
|
+
return find_value(values, measurement_items_t::L1_PSEUDORANGE_SIGMA, buf);
|
200
|
+
}
|
201
|
+
|
202
|
+
virtual const float_t *rate(
|
203
|
+
const typename measurement_t::mapped_type &values, float_t &buf) const {
|
204
|
+
const float_t *res;
|
205
|
+
if(res = find_value(values, measurement_items_t::L1_RANGE_RATE, buf)){
|
206
|
+
|
207
|
+
}else if(res = find_value(values, measurement_items_t::L1_DOPPLER, buf)){
|
208
|
+
// Fall back to doppler
|
209
|
+
buf *= -space_node_t::L1_WaveLength();
|
210
|
+
}
|
211
|
+
return res;
|
212
|
+
}
|
213
|
+
|
214
|
+
virtual const float_t *rate_sigma(
|
215
|
+
const typename measurement_t::mapped_type &values, float_t &buf) const {
|
216
|
+
const float_t *res;
|
217
|
+
if(res = find_value(values, measurement_items_t::L1_RANGE_RATE_SIGMA, buf)){
|
218
|
+
|
219
|
+
}else if(res = find_value(values, measurement_items_t::L1_DOPPLER_SIGMA, buf)){
|
220
|
+
// Fall back to doppler
|
221
|
+
buf *= space_node_t::L1_WaveLength();
|
222
|
+
}
|
223
|
+
return res;
|
224
|
+
}
|
225
|
+
|
226
|
+
/**
|
227
|
+
* Select appropriate solver, this is provision for GNSS extension
|
228
|
+
* @param prn satellite number
|
229
|
+
* @return self, however, it will be overridden by a subclass
|
230
|
+
*/
|
231
|
+
virtual const GPS_Solver_Base<FloatT> &select(const prn_t &prn) const {
|
232
|
+
return *this;
|
233
|
+
}
|
234
|
+
|
235
|
+
struct relative_property_t {
|
236
|
+
float_t weight; ///< How useful this information is. square value is required; thus, only positive value activates the other values.
|
237
|
+
float_t range_corrected; ///< corrected range just including delay, and excluding receiver/satellite error
|
238
|
+
float_t range_residual; ///< residual range
|
239
|
+
float_t rate_relative_neg; /// relative rate
|
240
|
+
float_t los_neg[3]; ///< line of site vector from satellite to user
|
241
|
+
};
|
242
|
+
|
243
|
+
/**
|
244
|
+
* Calculate relative range and rate information to a satellite
|
245
|
+
* This function will be overridden by a subclass to provide appropriate implementation
|
246
|
+
*
|
247
|
+
* @param prn satellite number
|
248
|
+
* @param measurement measurement (per satellite) containing pseudo range
|
249
|
+
* @param receiver_error (temporal solution of) receiver clock error in meter
|
250
|
+
* @param time_arrival time when signal arrive at receiver
|
251
|
+
* @param usr_pos (temporal solution of) user position
|
252
|
+
* @param usr_vel (temporal solution of) user velocity
|
253
|
+
* @return (relative_property_t) relative information
|
254
|
+
*/
|
255
|
+
virtual relative_property_t relative_property(
|
256
|
+
const prn_t &prn,
|
257
|
+
const typename measurement_t::mapped_type &measurement,
|
258
|
+
const float_t &receiver_error,
|
259
|
+
const gps_time_t &time_arrival,
|
260
|
+
const pos_t &usr_pos,
|
261
|
+
const xyz_t &usr_vel) const {
|
262
|
+
static const relative_property_t invalid = {0};
|
263
|
+
return invalid;
|
264
|
+
}
|
265
|
+
|
266
|
+
struct user_pvt_t {
|
267
|
+
enum {
|
268
|
+
ERROR_NO = 0,
|
269
|
+
ERROR_UNSOLVED,
|
270
|
+
ERROR_INVALID_IONO_MODEL,
|
271
|
+
ERROR_INSUFFICIENT_SATELLITES,
|
272
|
+
ERROR_POSITION_LS,
|
273
|
+
ERROR_POSITION_NOT_CONVERGED,
|
274
|
+
ERROR_DOP,
|
275
|
+
ERROR_VELOCITY_SKIPPED,
|
276
|
+
ERROR_VELOCITY_INSUFFICIENT_SATELLITES,
|
277
|
+
ERROR_VELOCITY_LS,
|
278
|
+
} error_code;
|
279
|
+
gps_time_t receiver_time;
|
280
|
+
pos_t user_position;
|
281
|
+
float_t receiver_error;
|
282
|
+
enu_t user_velocity_enu;
|
283
|
+
float_t receiver_error_rate;
|
284
|
+
struct dop_t {
|
285
|
+
float_t g, p, h, v, t;
|
286
|
+
dop_t() {}
|
287
|
+
dop_t(const matrix_t &C_enu)
|
288
|
+
: g(std::sqrt(C_enu.trace())),
|
289
|
+
p(std::sqrt(C_enu.partial(3, 3).trace())),
|
290
|
+
h(std::sqrt(C_enu.partial(2, 2).trace())),
|
291
|
+
v(std::sqrt(C_enu(2, 2))),
|
292
|
+
t(std::sqrt(C_enu(3, 3))) {}
|
293
|
+
} dop;
|
294
|
+
unsigned int used_satellites;
|
295
|
+
typedef BitArray<0x400> satellite_mask_t;
|
296
|
+
satellite_mask_t used_satellite_mask; ///< bit pattern(use=1, otherwise=0), PRN 1(LSB) to 32 for GPS
|
297
|
+
|
298
|
+
user_pvt_t()
|
299
|
+
: error_code(ERROR_UNSOLVED),
|
300
|
+
receiver_time(),
|
301
|
+
user_position(), receiver_error(0),
|
302
|
+
user_velocity_enu(), receiver_error_rate(0) {}
|
303
|
+
|
304
|
+
bool position_solved() const {
|
305
|
+
switch(error_code){
|
306
|
+
case ERROR_NO:
|
307
|
+
case ERROR_VELOCITY_SKIPPED:
|
308
|
+
case ERROR_VELOCITY_INSUFFICIENT_SATELLITES:
|
309
|
+
case ERROR_VELOCITY_LS:
|
310
|
+
return true;
|
311
|
+
}
|
312
|
+
return false;
|
313
|
+
}
|
314
|
+
bool velocity_solved() const {
|
315
|
+
return error_code == ERROR_NO;
|
316
|
+
}
|
317
|
+
};
|
318
|
+
|
319
|
+
struct options_t {
|
320
|
+
template <class OptionsT, class BaseSolverT = GPS_Solver_Base<float_t> >
|
321
|
+
struct merge_t : public BaseSolverT::options_t, OptionsT {
|
322
|
+
merge_t() : BaseSolverT::options_t(), OptionsT() {}
|
323
|
+
merge_t(
|
324
|
+
const typename BaseSolverT::options_t &opt_super,
|
325
|
+
const OptionsT &opt = OptionsT())
|
326
|
+
: BaseSolverT::options_t(opt_super), OptionsT(opt) {}
|
327
|
+
};
|
328
|
+
|
329
|
+
/**
|
330
|
+
* Flags to invalidate specific satellite
|
331
|
+
* Its index will be adjusted for PRN.
|
332
|
+
*/
|
333
|
+
template <int prn_begin, int prn_end>
|
334
|
+
struct exclude_prn_t
|
335
|
+
: public BitArray<prn_end - prn_begin + 1, unsigned int> {
|
336
|
+
typedef BitArray<prn_end - prn_begin + 1, unsigned int> super_t;
|
337
|
+
exclude_prn_t() : super_t() {
|
338
|
+
super_t::clear();
|
339
|
+
}
|
340
|
+
bool operator[](const int &prn) const {
|
341
|
+
return super_t::operator[](prn - prn_begin);
|
342
|
+
}
|
343
|
+
using super_t::set;
|
344
|
+
void set(const int &prn, const bool &bit = true) {
|
345
|
+
super_t::set(prn - prn_begin, bit);
|
346
|
+
}
|
347
|
+
using super_t::reset;
|
348
|
+
void reset(const int &prn) {set(prn, false);}
|
349
|
+
std::vector<int> excluded() const {
|
350
|
+
std::vector<int> res(super_t::indices_one());
|
351
|
+
for(std::vector<int>::iterator it(res.begin()), it_end(res.end());
|
352
|
+
it != it_end; ++it){
|
353
|
+
*it += prn_begin;
|
354
|
+
}
|
355
|
+
return res;
|
356
|
+
}
|
357
|
+
};
|
358
|
+
};
|
359
|
+
|
360
|
+
options_t available_options() const {
|
361
|
+
return options_t();
|
362
|
+
}
|
363
|
+
|
364
|
+
options_t available_options(const options_t &opt_wish) const {
|
365
|
+
return opt_wish;
|
366
|
+
}
|
367
|
+
|
368
|
+
options_t update_options(const options_t &opt_wish){
|
369
|
+
return opt_wish;
|
370
|
+
}
|
371
|
+
|
372
|
+
protected:
|
373
|
+
template <class MatrixT>
|
374
|
+
struct linear_solver_t {
|
375
|
+
MatrixT G; ///< Design matrix
|
376
|
+
/**
|
377
|
+
* Weighting (diagonal) matrix corresponding to inverse of covariance,
|
378
|
+
* whose (i, j) element is assumed to be 1/sigma_{i}^2 (i == j) or 0 (i != j)
|
379
|
+
*/
|
380
|
+
MatrixT W;
|
381
|
+
MatrixT delta_r; ///< Observation delta, i.e., observation minus measurement (y)
|
382
|
+
linear_solver_t(const MatrixT &G_, const MatrixT &W_, const MatrixT &delta_r_)
|
383
|
+
: G(G_), W(W_), delta_r(delta_r_) {}
|
384
|
+
/**
|
385
|
+
* Transform coordinate of design matrix G
|
386
|
+
* y = G x + v = G (R^{-1} x') + v = (G R^t) x' + v = G' x' + v,
|
387
|
+
* where R x = x' and R is a rotation matrix.
|
388
|
+
* For example, x is in ECEF with G being calculated in ECEF,
|
389
|
+
* and if x' in ENU is required,
|
390
|
+
* then, R is the ecef2enu matrix, because x' = ecef2enu x.
|
391
|
+
*
|
392
|
+
* @param G_ original design matrix
|
393
|
+
* @param rotation_matrix 3 by 3 rotation matrix
|
394
|
+
* @return transformed design matrix G'
|
395
|
+
*/
|
396
|
+
static matrix_t rotate_G(const MatrixT &G_, const matrix_t &rotation_matrix){
|
397
|
+
matrix_t res(G_.rows(), 4);
|
398
|
+
res.partial(G_.rows(), 3).replace(
|
399
|
+
G_.partial(G_.rows(), 3) * rotation_matrix.transpose());
|
400
|
+
res.partial(G_.rows(), 1, 0, 3).replace(G_.partial(G_.rows(), 1, 0, 3));
|
401
|
+
return res;
|
402
|
+
}
|
403
|
+
|
404
|
+
matrix_t G_rotated(const matrix_t &rotation_matrix) const {
|
405
|
+
return rotate_G(G, rotation_matrix);
|
406
|
+
}
|
407
|
+
|
408
|
+
/**
|
409
|
+
* Calculate C matrix, which is required to obtain DOP
|
410
|
+
* C = G^t * W * G
|
411
|
+
*
|
412
|
+
* @return C matrix
|
413
|
+
*/
|
414
|
+
matrix_t C() const {
|
415
|
+
return (G.transpose() * W * G).inverse();
|
416
|
+
}
|
417
|
+
/**
|
418
|
+
* Transform coordinate of matrix C, which will be used to calculate HDOP/VDOP
|
419
|
+
* C' = (G * R^t)^t W * (G * R^t) = R * G^t * W * G * R^t = R * C * R^t,
|
420
|
+
* where R is a rotation matrix, for example, ECEF to ENU.
|
421
|
+
*
|
422
|
+
* @param rotation_matrix 3 by 3 rotation matrix
|
423
|
+
* @return transformed matrix C'
|
424
|
+
* @see rotate_G
|
425
|
+
*/
|
426
|
+
static matrix_t rotate_C(const matrix_t &C, const matrix_t &rotation_matrix){
|
427
|
+
matrix_t res(4, 4);
|
428
|
+
res.partial(3, 3).replace( // upper left
|
429
|
+
rotation_matrix * C.partial(3, 3) * rotation_matrix.transpose());
|
430
|
+
res.partial(3, 1, 0, 3).replace( // upper right
|
431
|
+
rotation_matrix * C.partial(3, 1, 0, 3));
|
432
|
+
res.partial(1, 3, 3, 0).replace( // lower left
|
433
|
+
C.partial(1, 3, 3, 0) * rotation_matrix.transpose());
|
434
|
+
res(3, 3) = C(3, 3); // lower right
|
435
|
+
return res;
|
436
|
+
}
|
437
|
+
/**
|
438
|
+
* Solve x of linear equation (y = G x + v) to minimize sigma{v^t * v}
|
439
|
+
* where v =~ N(0, sigma), and y and G are observation delta (=delta_r variable)
|
440
|
+
* and a design matrix, respectively.
|
441
|
+
* This yields x = (G^t * W * G)^{-1} * (G^t * W) y = S y.
|
442
|
+
*
|
443
|
+
* 4 by row(y) S matrix (=(G^t * W * G)^{-1} * (G^t * W)) will be used to calculate protection level
|
444
|
+
* to investigate relationship between bias on each satellite and solution.
|
445
|
+
* residual v = (I - P) = (I - G S), where P = G S, which is irrelevant to rotation,
|
446
|
+
* because P = G R R^{t} S = G' S'.
|
447
|
+
*
|
448
|
+
* @param S (output) coefficient matrix to calculate solution, i.e., (G^t * W * G)^{-1} * (G^t * W)
|
449
|
+
* @return x vector
|
450
|
+
* @see rotate_S()
|
451
|
+
*/
|
452
|
+
inline matrix_t least_square(matrix_t &S) const {
|
453
|
+
matrix_t Gt_W(G.transpose() * W);
|
454
|
+
S = (Gt_W * G).inverse() * Gt_W;
|
455
|
+
return S * delta_r;
|
456
|
+
}
|
457
|
+
matrix_t least_square() const {
|
458
|
+
matrix_t S;
|
459
|
+
return least_square(S);
|
460
|
+
}
|
461
|
+
/**
|
462
|
+
* Transform coordinate of coefficient matrix of solution S
|
463
|
+
* x' = R x = R S y, where R is a rotation matrix, for example, ECEF to ENU.
|
464
|
+
* Be careful, R is not ENU to ECEF in the example, which should be consistent to rotate_G().
|
465
|
+
*
|
466
|
+
* @param S coefficient matrix of solution
|
467
|
+
* @param rotation_matrix 3 by 3 rotation matrix
|
468
|
+
* @return transformed coefficient matrix S'
|
469
|
+
* @see rotate_G
|
470
|
+
*/
|
471
|
+
static matrix_t rotate_S(const matrix_t &S, const matrix_t &rotation_matrix){
|
472
|
+
matrix_t res(4, S.columns());
|
473
|
+
res.partial(3, S.columns()).replace(rotation_matrix * S.partial(3, S.columns()));
|
474
|
+
for(unsigned int j(0), j_end(S.columns()); j < j_end; ++j){
|
475
|
+
res(3, j) = S(3, j);
|
476
|
+
}
|
477
|
+
return res;
|
478
|
+
}
|
479
|
+
/**
|
480
|
+
* Calculate linear effect from bias on each range measurement to horizontal/vertical estimation.
|
481
|
+
*
|
482
|
+
* @param S coefficient matrix of solution
|
483
|
+
* @param rotation_matrix 3 by 3 matrix, which makes S aligned to ENU or NED coordinates
|
484
|
+
* @return slopes matrix (1st and 2nd columns correspond to horizontal and vertical components, respectively)
|
485
|
+
* @see Eq.(5.26), (5.27) in @article{Mink, title={Performance of Receiver Autonomous Integrity Monitoring (RAIM) for Maritime Operations}, author={Mink, Michael}, pages={220} }
|
486
|
+
* Note: returned values are not performed to be multiplied by sqrt(N-4)
|
487
|
+
*/
|
488
|
+
matrix_t slope_HV(const matrix_t &S, const matrix_t &rotation_matrix = matrix_t::getI(3)) const {
|
489
|
+
matrix_t S_ENU_or_NED(rotate_S(S, rotation_matrix));
|
490
|
+
matrix_t res(G.rows(), 2); // 1st column = horizontal, 2nd column = vertical
|
491
|
+
matrix_t P(G * S);
|
492
|
+
for(unsigned int i(0), i_end(res.rows()); i < i_end; i++){
|
493
|
+
if(W(i, i) <= 0){
|
494
|
+
res(i, 0) = res(i, 1) = 0;
|
495
|
+
continue;
|
496
|
+
}
|
497
|
+
float_t denom(std::sqrt((-P(i, i) + 1) * W(i, i)));
|
498
|
+
res(i, 0) = std::sqrt( // horizontal
|
499
|
+
std::pow(S_ENU_or_NED(0, i), 2) + std::pow(S_ENU_or_NED(1, i), 2)) / denom;
|
500
|
+
res(i, 1) = std::abs(S_ENU_or_NED(2, i)) / denom; // vertical
|
501
|
+
}
|
502
|
+
return res;
|
503
|
+
}
|
504
|
+
/**
|
505
|
+
* Calculate weighted square sum of residual (WSSR) based on least square solution.
|
506
|
+
* v^t W v (= (y - G x)^t W (y - G x) = y^t W (I-P) y)
|
507
|
+
*
|
508
|
+
* @param x solution
|
509
|
+
* @return WSSR scalar
|
510
|
+
*/
|
511
|
+
float_t wssr(const matrix_t &x = least_square()) const {
|
512
|
+
matrix_t v(delta_r - G * x);
|
513
|
+
return (v.transpose() * W * v)(0, 0);
|
514
|
+
}
|
515
|
+
/**
|
516
|
+
* Calculate weighted square sum of residual (WSSR) based on least square solution
|
517
|
+
* with solution coefficient matrix (S).
|
518
|
+
* v^t W v (= (y - G x)^t W (y - G x) = y^t W (I-G*S) y)
|
519
|
+
*
|
520
|
+
* @param S coefficient matrix of solution
|
521
|
+
* @param W_dash (output) pointer to store scale factor matrix of y^t y,
|
522
|
+
* i.e., *W_dash = norm(t(I-G*S)) * W * norm(I-G*S)
|
523
|
+
* @return WSSR scalar
|
524
|
+
*/
|
525
|
+
float_t wssr_S(const matrix_t &S, matrix_t *W_dash = NULL) const {
|
526
|
+
matrix_t rhs(matrix_t::getI(W.rows()) - (G * S));
|
527
|
+
if(W_dash){
|
528
|
+
*W_dash = W.copy();
|
529
|
+
for(unsigned int i(0), i_end(rhs.rows()); i < i_end; ++i){
|
530
|
+
(*W_dash)(i, i) *= (rhs.rowVector(i) * rhs.rowVector(i).transpose())(0, 0);
|
531
|
+
}
|
532
|
+
}
|
533
|
+
return (delta_r.transpose() * W * rhs * delta_r)(0, 0);
|
534
|
+
}
|
535
|
+
typedef linear_solver_t<typename MatrixT::partial_offsetless_t> partial_t;
|
536
|
+
partial_t partial(unsigned int size) const {
|
537
|
+
if(size >= G.rows()){size = G.rows();}
|
538
|
+
return partial_t(
|
539
|
+
G.partial(size, 4), W.partial(size, size), delta_r.partial(size, 1));
|
540
|
+
}
|
541
|
+
typedef linear_solver_t<typename MatrixT::circular_t> exclude_t;
|
542
|
+
exclude_t exclude(const unsigned int &row) const {
|
543
|
+
unsigned int size(G.rows()), offset((row + 1) % size);
|
544
|
+
if(size >= 1){size--;}
|
545
|
+
// generate matrices having circular view
|
546
|
+
return exclude_t(
|
547
|
+
G.circular(offset, 0, size, 4),
|
548
|
+
W.circular(offset, offset, size, size),
|
549
|
+
delta_r.circular(offset, 0, size, 1));
|
550
|
+
}
|
551
|
+
template <class MatrixT2>
|
552
|
+
void copy_G_W_row(const linear_solver_t<MatrixT2> &src,
|
553
|
+
const unsigned int &i_src, const unsigned int &i_dst){
|
554
|
+
for(unsigned int j(0); j < 4; ++j){
|
555
|
+
G(i_dst, j) = src.G(i_src, j);
|
556
|
+
}
|
557
|
+
W(i_dst, i_dst) = src.W(i_src, i_src);
|
558
|
+
}
|
559
|
+
};
|
560
|
+
|
561
|
+
struct geometric_matrices_t : public linear_solver_t<matrix_t> {
|
562
|
+
typedef linear_solver_t<matrix_t> super_t;
|
563
|
+
geometric_matrices_t(const unsigned int &capacity)
|
564
|
+
: super_t(
|
565
|
+
matrix_t(capacity, 4), matrix_t(capacity, capacity), matrix_t(capacity, 1)) {
|
566
|
+
for(unsigned int i(0); i < capacity; ++i){
|
567
|
+
super_t::G(i, 3) = 1;
|
568
|
+
}
|
569
|
+
}
|
570
|
+
};
|
571
|
+
|
572
|
+
struct measurement2_item_t {
|
573
|
+
prn_t prn;
|
574
|
+
const typename measurement_t::mapped_type *k_v_map;
|
575
|
+
const GPS_Solver_Base<FloatT> *solver;
|
576
|
+
};
|
577
|
+
typedef std::vector<measurement2_item_t> measurement2_t;
|
578
|
+
|
579
|
+
|
580
|
+
/**
|
581
|
+
* Update position solution
|
582
|
+
* This function will be called multiple times in a solution calculation iteration.
|
583
|
+
* It may be overridden in a subclass for extraction of calculation source
|
584
|
+
* such as design matrix.
|
585
|
+
*
|
586
|
+
* @param geomat residual, desgin and weight matrices
|
587
|
+
* @param res (in,out) current solution to be updated
|
588
|
+
* @return (bool) If solution will be treated as the final solution, true is returned; otherwise false.
|
589
|
+
*/
|
590
|
+
virtual bool update_position_solution(
|
591
|
+
const geometric_matrices_t &geomat,
|
592
|
+
user_pvt_t &res) const {
|
593
|
+
|
594
|
+
// Least square
|
595
|
+
matrix_t delta_x(geomat.partial(res.used_satellites).least_square());
|
596
|
+
|
597
|
+
xyz_t delta_user_position(delta_x.partial(3, 1, 0, 0));
|
598
|
+
res.user_position.xyz += delta_user_position;
|
599
|
+
res.user_position.llh = res.user_position.xyz.llh();
|
600
|
+
|
601
|
+
const float_t &delta_receiver_error(delta_x(3, 0));
|
602
|
+
res.receiver_error += delta_receiver_error;
|
603
|
+
|
604
|
+
return ((delta_x.transpose() * delta_x)(0, 0) <= 1E-6); // equivalent to abs(x) <= 1E-3 [m]
|
605
|
+
}
|
606
|
+
|
607
|
+
struct user_pvt_opt_t {
|
608
|
+
/**
|
609
|
+
* how many iterations are required to perform coarse estimation before fine one;
|
610
|
+
* If initial position and clock error are goodly guessed, this will be zero.
|
611
|
+
*/
|
612
|
+
int warmup;
|
613
|
+
int max_trial;
|
614
|
+
bool estimate_velocity;
|
615
|
+
user_pvt_opt_t(
|
616
|
+
const bool &good_init = true,
|
617
|
+
const bool &with_velocity = true)
|
618
|
+
: warmup(good_init ? 0 : 5),
|
619
|
+
max_trial(10),
|
620
|
+
estimate_velocity(with_velocity) {}
|
621
|
+
};
|
622
|
+
|
623
|
+
/**
|
624
|
+
* Calculate User position/velocity by using associated solvers.
|
625
|
+
* This function can be overridden in a subclass.
|
626
|
+
*
|
627
|
+
* @param res (out) calculation results and matrices used for calculation
|
628
|
+
* @param measurement PRN, pseudo-range, and pseudo-range rate information
|
629
|
+
* associated with a specific solver corresponding to a satellite system
|
630
|
+
* @param receiver_time receiver time at measurement
|
631
|
+
* @param user_position_init initial solution of user position in XYZ meters and LLH
|
632
|
+
* @param receiver_error_init initial solution of receiver clock error in meters
|
633
|
+
* @param opt options for user PVT calculation
|
634
|
+
* @see update_ephemeris(), register_ephemeris
|
635
|
+
*/
|
636
|
+
virtual void user_pvt(
|
637
|
+
user_pvt_t &res,
|
638
|
+
const measurement2_t &measurement,
|
639
|
+
const gps_time_t &receiver_time,
|
640
|
+
const pos_t &user_position_init,
|
641
|
+
const float_t &receiver_error_init,
|
642
|
+
const user_pvt_opt_t &opt = user_pvt_opt_t()) const {
|
643
|
+
|
644
|
+
res.receiver_time = receiver_time;
|
645
|
+
|
646
|
+
// 1. Position calculation
|
647
|
+
|
648
|
+
res.user_position = user_position_init;
|
649
|
+
res.receiver_error = receiver_error_init;
|
650
|
+
|
651
|
+
geometric_matrices_t geomat(measurement.size());
|
652
|
+
typedef std::vector<std::pair<unsigned int, float_t> > index_obs_t;
|
653
|
+
index_obs_t idx_rate_rel; // [(index of measurement, relative rate), ...]
|
654
|
+
idx_rate_rel.reserve(measurement.size());
|
655
|
+
|
656
|
+
// If initialization is not appropriate, more iteration will be performed.
|
657
|
+
bool converged(false);
|
658
|
+
for(int i_trial(-opt.warmup); i_trial < opt.max_trial; i_trial++){
|
659
|
+
|
660
|
+
idx_rate_rel.clear();
|
661
|
+
unsigned int i_row(0), i_measurement(0);
|
662
|
+
res.used_satellite_mask.clear();
|
663
|
+
|
664
|
+
gps_time_t time_arrival(
|
665
|
+
receiver_time - (res.receiver_error / space_node_t::light_speed));
|
666
|
+
|
667
|
+
const bool coarse_estimation(i_trial <= 0);
|
668
|
+
for(typename measurement2_t::const_iterator it(measurement.begin()), it_end(measurement.end());
|
669
|
+
it != it_end;
|
670
|
+
++it, ++i_measurement){
|
671
|
+
|
672
|
+
static const xyz_t zero(0, 0, 0);
|
673
|
+
relative_property_t prop(it->solver->relative_property(
|
674
|
+
it->prn, *(it->k_v_map),
|
675
|
+
res.receiver_error, time_arrival,
|
676
|
+
res.user_position, zero));
|
677
|
+
|
678
|
+
if(prop.weight <= 0){
|
679
|
+
continue; // intentionally excluded satellite
|
680
|
+
}else{
|
681
|
+
res.used_satellite_mask.set(it->prn);
|
682
|
+
}
|
683
|
+
|
684
|
+
if(coarse_estimation){
|
685
|
+
prop.weight = 1;
|
686
|
+
}else{
|
687
|
+
idx_rate_rel.push_back(std::make_pair(i_measurement, prop.rate_relative_neg));
|
688
|
+
}
|
689
|
+
|
690
|
+
geomat.delta_r(i_row, 0) = prop.range_residual;
|
691
|
+
geomat.G(i_row, 0) = prop.los_neg[0];
|
692
|
+
geomat.G(i_row, 1) = prop.los_neg[1];
|
693
|
+
geomat.G(i_row, 2) = prop.los_neg[2];
|
694
|
+
geomat.W(i_row, i_row) = prop.weight;
|
695
|
+
|
696
|
+
++i_row;
|
697
|
+
}
|
698
|
+
|
699
|
+
if((res.used_satellites = i_row) < 4){
|
700
|
+
res.error_code = user_pvt_t::ERROR_INSUFFICIENT_SATELLITES;
|
701
|
+
return;
|
702
|
+
}
|
703
|
+
|
704
|
+
try{
|
705
|
+
converged = update_position_solution(geomat, res);
|
706
|
+
if((!coarse_estimation) && converged){break;}
|
707
|
+
}catch(const std::runtime_error &e){ // expect to detect matrix operation error
|
708
|
+
res.error_code = user_pvt_t::ERROR_POSITION_LS;
|
709
|
+
return;
|
710
|
+
}
|
711
|
+
}
|
712
|
+
|
713
|
+
if(!converged){
|
714
|
+
res.error_code = user_pvt_t::ERROR_POSITION_NOT_CONVERGED;
|
715
|
+
return;
|
716
|
+
}
|
717
|
+
|
718
|
+
try{
|
719
|
+
res.dop = typename user_pvt_t::dop_t(geomat.rotate_C(
|
720
|
+
geomat.partial(res.used_satellites).C(), res.user_position.ecef2enu()));
|
721
|
+
}catch(const std::runtime_error &e){ // expect to detect matrix operation error
|
722
|
+
res.error_code = user_pvt_t::ERROR_DOP;
|
723
|
+
return;
|
724
|
+
}
|
725
|
+
|
726
|
+
if(!opt.estimate_velocity){
|
727
|
+
res.error_code = user_pvt_t::ERROR_VELOCITY_SKIPPED;
|
728
|
+
return;
|
729
|
+
}
|
730
|
+
|
731
|
+
/* 2. Calculate velocity
|
732
|
+
* Check consistency between range and rate for velocity calculation,
|
733
|
+
* then, assign design and weight matrices
|
734
|
+
*/
|
735
|
+
geometric_matrices_t geomat2(res.used_satellites);
|
736
|
+
int i_range(0), i_rate(0);
|
737
|
+
|
738
|
+
for(typename index_obs_t::const_iterator it(idx_rate_rel.begin()), it_end(idx_rate_rel.end());
|
739
|
+
it != it_end;
|
740
|
+
++it, ++i_range){
|
741
|
+
|
742
|
+
float_t rate;
|
743
|
+
if(!(measurement[it->first].solver->rate(
|
744
|
+
*(measurement[it->first].k_v_map), // const version of measurement[PRN]
|
745
|
+
rate))){continue;}
|
746
|
+
|
747
|
+
// Copy design matrix and set rate
|
748
|
+
geomat2.copy_G_W_row(geomat, i_range, i_rate);
|
749
|
+
geomat2.delta_r(i_rate, 0) = rate + it->second;
|
750
|
+
|
751
|
+
++i_rate;
|
752
|
+
}
|
753
|
+
|
754
|
+
if(i_rate < 4){
|
755
|
+
res.error_code = user_pvt_t::ERROR_VELOCITY_INSUFFICIENT_SATELLITES;
|
756
|
+
return;
|
757
|
+
}
|
758
|
+
|
759
|
+
try{
|
760
|
+
// Least square
|
761
|
+
matrix_t sol(geomat2.partial(i_rate).least_square());
|
762
|
+
|
763
|
+
xyz_t vel_xyz(sol.partial(3, 1, 0, 0));
|
764
|
+
res.user_velocity_enu = enu_t::relative_rel(
|
765
|
+
vel_xyz, res.user_position.llh);
|
766
|
+
res.receiver_error_rate = sol(3, 0);
|
767
|
+
}catch(const std::runtime_error &e){ // expect to detect matrix operation error
|
768
|
+
res.error_code = user_pvt_t::ERROR_VELOCITY_LS;
|
769
|
+
return;
|
770
|
+
}
|
771
|
+
|
772
|
+
res.error_code = user_pvt_t::ERROR_NO;
|
773
|
+
}
|
774
|
+
|
775
|
+
public:
|
776
|
+
/**
|
777
|
+
* Calculate satellite position
|
778
|
+
*
|
779
|
+
* @param prn satellite number
|
780
|
+
* @param time GPS time
|
781
|
+
* @param res object to which results are stored
|
782
|
+
* @return If position is available, &res will be returned, otherwise NULL.
|
783
|
+
*/
|
784
|
+
virtual xyz_t *satellite_position(
|
785
|
+
const prn_t &prn,
|
786
|
+
const gps_time_t &time,
|
787
|
+
xyz_t &res) const {
|
788
|
+
return NULL;
|
789
|
+
}
|
790
|
+
|
791
|
+
/**
|
792
|
+
* Calculate User position/velocity with hint
|
793
|
+
* This is multi-constellation version,
|
794
|
+
* and reference implementation to be hidden by optimized one in sub class.
|
795
|
+
*
|
796
|
+
* @param res (out) calculation results and matrices used for calculation
|
797
|
+
* @param measurement PRN, pseudo-range, and pseudo-range rate information
|
798
|
+
* @param receiver_time receiver time at measurement
|
799
|
+
* @param user_position_init initial solution of user position in XYZ meters and LLH
|
800
|
+
* @param receiver_error_init initial solution of receiver clock error in meters
|
801
|
+
* @param good_init if true, initial position and clock error are goodly guessed.
|
802
|
+
* @param with_velocity if true, perform velocity estimation.
|
803
|
+
* @see update_ephemeris(), register_ephemeris
|
804
|
+
*/
|
805
|
+
virtual void user_pvt(
|
806
|
+
user_pvt_t &res,
|
807
|
+
const measurement_t &measurement,
|
808
|
+
const gps_time_t &receiver_time,
|
809
|
+
const pos_t &user_position_init,
|
810
|
+
const float_t &receiver_error_init,
|
811
|
+
const bool &good_init = true,
|
812
|
+
const bool &with_velocity = true) const {
|
813
|
+
|
814
|
+
measurement2_t measurement2;
|
815
|
+
measurement2.reserve(measurement.size());
|
816
|
+
for(typename measurement_t::const_iterator it(measurement.begin()), it_end(measurement.end());
|
817
|
+
it != it_end; ++it){
|
818
|
+
typename measurement2_t::value_type v = {
|
819
|
+
it->first, &(it->second), &select(it->first)}; // prn, measurement, solver
|
820
|
+
xyz_t sat;
|
821
|
+
if(!v.solver->satellite_position(v.prn, receiver_time, sat)){
|
822
|
+
// pre-check satellite availability; remove it when its position is unknown
|
823
|
+
continue;
|
824
|
+
}
|
825
|
+
measurement2.push_back(v);
|
826
|
+
}
|
827
|
+
user_pvt(
|
828
|
+
res,
|
829
|
+
measurement2, receiver_time, user_position_init, receiver_error_init,
|
830
|
+
user_pvt_opt_t(good_init, with_velocity));
|
831
|
+
}
|
832
|
+
|
833
|
+
template <class SolverT>
|
834
|
+
struct solver_interface_t {
|
835
|
+
const SolverT &solver;
|
836
|
+
solver_interface_t(const SolverT &solver_) : solver(solver_) {}
|
837
|
+
|
838
|
+
typedef typename SolverT::user_pvt_t pvt_t;
|
839
|
+
|
840
|
+
pvt_t user_pvt(
|
841
|
+
const measurement_t &measurement,
|
842
|
+
const gps_time_t &receiver_time,
|
843
|
+
const pos_t &user_position_init,
|
844
|
+
const float_t &receiver_error_init,
|
845
|
+
const bool &good_init = true,
|
846
|
+
const bool &with_velocity = true) const {
|
847
|
+
pvt_t res;
|
848
|
+
solver.user_pvt(res,
|
849
|
+
measurement, receiver_time,
|
850
|
+
user_position_init, receiver_error_init,
|
851
|
+
good_init, with_velocity);
|
852
|
+
return res;
|
853
|
+
}
|
854
|
+
|
855
|
+
/**
|
856
|
+
* Calculate User position/velocity with hint
|
857
|
+
*
|
858
|
+
* @param measurement PRN, pseudo-range, and pseudo-range rate information
|
859
|
+
* @param receiver_time receiver time at measurement
|
860
|
+
* @param user_position_init_xyz initial solution of user position in meters
|
861
|
+
* @param receiver_error_init initial solution of receiver clock error in meters
|
862
|
+
* @param good_init if true, initial position and clock error are goodly guessed.
|
863
|
+
* @param with_velocity if true, perform velocity estimation.
|
864
|
+
* @return calculation results and matrices used for calculation
|
865
|
+
* @see update_ephemeris(), register_ephemeris
|
866
|
+
*/
|
867
|
+
pvt_t user_pvt(
|
868
|
+
const measurement_t &measurement,
|
869
|
+
const gps_time_t &receiver_time,
|
870
|
+
const xyz_t &user_position_init_xyz,
|
871
|
+
const float_t &receiver_error_init,
|
872
|
+
const bool &good_init = true,
|
873
|
+
const bool &with_velocity = true) const {
|
874
|
+
pos_t user_position_init = {user_position_init_xyz, user_position_init_xyz.llh()};
|
875
|
+
return user_pvt(
|
876
|
+
measurement, receiver_time,
|
877
|
+
user_position_init, receiver_error_init,
|
878
|
+
good_init, with_velocity);
|
879
|
+
}
|
880
|
+
|
881
|
+
/**
|
882
|
+
* Calculate User position/velocity without hint
|
883
|
+
*
|
884
|
+
* @param measurement PRN, pseudo-range, and pseudo-range rate information
|
885
|
+
* @param receiver_time receiver time at measurement
|
886
|
+
* @return calculation results and matrices used for calculation
|
887
|
+
*/
|
888
|
+
pvt_t user_pvt(
|
889
|
+
const measurement_t &measurement,
|
890
|
+
const gps_time_t &receiver_time) const {
|
891
|
+
return user_pvt(measurement, receiver_time, xyz_t(), 0, false);
|
892
|
+
}
|
893
|
+
|
894
|
+
/**
|
895
|
+
* Calculate User position/velocity with hint
|
896
|
+
*
|
897
|
+
* @param prn_range PRN, pseudo-range list
|
898
|
+
* @param prn_rate PRN, pseudo-range rate list
|
899
|
+
* @param receiver_time receiver time at measurement
|
900
|
+
* @param user_position_init initial solution of user position in XYZ meters and LLH
|
901
|
+
* @param receiver_error_init initial solution of receiver clock error in meters
|
902
|
+
* @param good_init if true, initial position and clock error are goodly guessed.
|
903
|
+
* @param with_velocity if true, perform velocity estimation.
|
904
|
+
* @return calculation results and matrices used for calculation
|
905
|
+
* @see update_ephemeris(), register_ephemeris
|
906
|
+
*/
|
907
|
+
pvt_t user_pvt(
|
908
|
+
const prn_obs_t &prn_range,
|
909
|
+
const prn_obs_t &prn_rate,
|
910
|
+
const gps_time_t &receiver_time,
|
911
|
+
const pos_t &user_position_init,
|
912
|
+
const float_t &receiver_error_init,
|
913
|
+
const bool &good_init = true,
|
914
|
+
const bool &with_velocity = true) const {
|
915
|
+
measurement_t measurement;
|
916
|
+
measurement_util_t::merge(measurement, prn_range, measurement_items_t::L1_PSEUDORANGE);
|
917
|
+
measurement_util_t::merge(measurement, prn_rate, measurement_items_t::L1_RANGE_RATE);
|
918
|
+
return user_pvt(
|
919
|
+
measurement, receiver_time,
|
920
|
+
user_position_init, receiver_error_init,
|
921
|
+
good_init, with_velocity);
|
922
|
+
}
|
923
|
+
|
924
|
+
/**
|
925
|
+
* Calculate User position/velocity with hint
|
926
|
+
*
|
927
|
+
* @param prn_range PRN, pseudo-range list
|
928
|
+
* @param prn_rate PRN, pseudo-range rate list
|
929
|
+
* @param receiver_time receiver time at measurement
|
930
|
+
* @param user_position_init_xyz initial solution of user position in meters
|
931
|
+
* @param receiver_error_init initial solution of receiver clock error in meters
|
932
|
+
* @param good_init if true, initial position and clock error are goodly guessed.
|
933
|
+
* @param with_velocity if true, perform velocity estimation.
|
934
|
+
* @return calculation results and matrices used for calculation
|
935
|
+
* @see update_ephemeris(), register_ephemeris
|
936
|
+
*/
|
937
|
+
pvt_t user_pvt(
|
938
|
+
const prn_obs_t &prn_range,
|
939
|
+
const prn_obs_t &prn_rate,
|
940
|
+
const gps_time_t &receiver_time,
|
941
|
+
const xyz_t &user_position_init_xyz,
|
942
|
+
const float_t &receiver_error_init,
|
943
|
+
const bool &good_init = true,
|
944
|
+
const bool &with_velocity = true) const {
|
945
|
+
pos_t user_position_init = {user_position_init_xyz, user_position_init_xyz.llh()};
|
946
|
+
return user_pvt(
|
947
|
+
prn_range, prn_rate, receiver_time,
|
948
|
+
user_position_init, receiver_error_init,
|
949
|
+
good_init, with_velocity);
|
950
|
+
}
|
951
|
+
|
952
|
+
/**
|
953
|
+
* Calculate User position/velocity without hint
|
954
|
+
*
|
955
|
+
* @param prn_range PRN, pseudo-range list
|
956
|
+
* @param prn_rate PRN, pseudo-range rate list
|
957
|
+
* @param receiver_time receiver time at measurement
|
958
|
+
* @return calculation results and matrices used for calculation
|
959
|
+
*/
|
960
|
+
pvt_t user_pvt(
|
961
|
+
const prn_obs_t &prn_range,
|
962
|
+
const prn_obs_t &prn_rate,
|
963
|
+
const gps_time_t &receiver_time) const {
|
964
|
+
return user_pvt(prn_range, prn_rate, receiver_time, xyz_t(), 0, false);
|
965
|
+
}
|
966
|
+
|
967
|
+
/**
|
968
|
+
* Calculate User position with hint
|
969
|
+
*
|
970
|
+
* @param prn_range PRN, pseudo-range list
|
971
|
+
* @param receiver_time receiver time at measurement
|
972
|
+
* @param user_position_init initial solution of user position in meters
|
973
|
+
* @param receiver_error_init initial solution of receiver clock error in meters
|
974
|
+
* @param good_init if true, initial position and clock error are goodly guessed.
|
975
|
+
* @return calculation results and matrices used for calculation
|
976
|
+
*/
|
977
|
+
pvt_t user_position(
|
978
|
+
const prn_obs_t &prn_range,
|
979
|
+
const gps_time_t &receiver_time,
|
980
|
+
const xyz_t &user_position_init,
|
981
|
+
const float_t &receiver_error_init,
|
982
|
+
const bool &good_init = true) const {
|
983
|
+
|
984
|
+
return user_pvt(
|
985
|
+
prn_range, prn_obs_t(), receiver_time,
|
986
|
+
user_position_init, receiver_error_init,
|
987
|
+
good_init, false);
|
988
|
+
}
|
989
|
+
|
990
|
+
/**
|
991
|
+
* Calculate User position without hint
|
992
|
+
*
|
993
|
+
* @param prn_range PRN and pseudo range
|
994
|
+
* @param receiver_time receiver time at measurement
|
995
|
+
* @return calculation results and matrices used for calculation
|
996
|
+
*/
|
997
|
+
pvt_t user_position(
|
998
|
+
const prn_obs_t &prn_range,
|
999
|
+
const gps_time_t &receiver_time) const {
|
1000
|
+
return user_position(prn_range, receiver_time, xyz_t(), 0, false);
|
1001
|
+
}
|
1002
|
+
};
|
1003
|
+
|
1004
|
+
solver_interface_t<GPS_Solver_Base<FloatT> > solve() const {
|
1005
|
+
return solver_interface_t<GPS_Solver_Base<FloatT> >(*this);
|
1006
|
+
}
|
1007
|
+
|
1008
|
+
static typename user_pvt_t::dop_t dop(const matrix_t &C, const pos_t &user_position) {
|
1009
|
+
return typename user_pvt_t::dop_t(geometric_matrices_t::rotate_C(C, user_position.ecef2enu()));
|
1010
|
+
}
|
1011
|
+
};
|
1012
|
+
|
1013
|
+
template <class FloatT>
|
1014
|
+
const typename GPS_Solver_Base<FloatT>::measurement_item_set_t
|
1015
|
+
GPS_Solver_Base<FloatT>::L1CA = {
|
1016
|
+
#define make_entry(key) \
|
1017
|
+
GPS_Solver_Base<FloatT>::measurement_items_t::L1_ ## key
|
1018
|
+
#define make_entry2(key) { \
|
1019
|
+
make_entry(key), \
|
1020
|
+
make_entry(key ## _SIGMA)}
|
1021
|
+
make_entry2(PSEUDORANGE),
|
1022
|
+
make_entry2(DOPPLER),
|
1023
|
+
make_entry2(CARRIER_PHASE),
|
1024
|
+
make_entry2(RANGE_RATE),
|
1025
|
+
make_entry(SIGNAL_STRENGTH_dBHz),
|
1026
|
+
make_entry(LOCK_SEC),
|
1027
|
+
#undef make_entry2
|
1028
|
+
#undef make_entry
|
1029
|
+
};
|
1030
|
+
|
1031
|
+
template <class FloatT>
|
1032
|
+
const typename GPS_Solver_Base<FloatT>::range_error_t
|
1033
|
+
GPS_Solver_Base<FloatT>::range_error_t::not_corrected = {
|
1034
|
+
MASK_RECEIVER_CLOCK | MASK_SATELLITE_CLOCK | MASK_IONOSPHERIC | MASK_TROPOSPHERIC,
|
1035
|
+
{0},
|
1036
|
+
};
|
1037
|
+
|
1038
|
+
template <class FloatT, class PVT_BaseT = typename GPS_Solver_Base<FloatT>::user_pvt_t>
|
1039
|
+
struct GPS_PVT_Debug : public PVT_BaseT {
|
1040
|
+
typename GPS_Solver_Base<FloatT>::matrix_t G, W, delta_r;
|
1041
|
+
};
|
1042
|
+
|
1043
|
+
template <class FloatT, class SolverBaseT = GPS_Solver_Base<FloatT> >
|
1044
|
+
struct GPS_Solver_Base_Debug : public SolverBaseT {
|
1045
|
+
typedef SolverBaseT base_t;
|
1046
|
+
typedef GPS_Solver_Base_Debug<FloatT, SolverBaseT> self_t;
|
1047
|
+
virtual ~GPS_Solver_Base_Debug() {}
|
1048
|
+
|
1049
|
+
#if defined(__GNUC__) && (__GNUC__ < 5)
|
1050
|
+
#define inheritate_type(x) typedef typename base_t::x x;
|
1051
|
+
#else
|
1052
|
+
#define inheritate_type(x) using typename base_t::x;
|
1053
|
+
#endif
|
1054
|
+
inheritate_type(float_t);
|
1055
|
+
inheritate_type(geometric_matrices_t);
|
1056
|
+
#undef inheritate_type
|
1057
|
+
|
1058
|
+
typedef GPS_PVT_Debug<float_t, typename base_t::user_pvt_t> user_pvt_t;
|
1059
|
+
|
1060
|
+
typename base_t::template solver_interface_t<self_t> solve() const {
|
1061
|
+
return typename base_t::template solver_interface_t<self_t>(*this);
|
1062
|
+
}
|
1063
|
+
|
1064
|
+
protected:
|
1065
|
+
virtual bool update_position_solution(
|
1066
|
+
const geometric_matrices_t &geomat,
|
1067
|
+
typename GPS_Solver_Base<FloatT>::user_pvt_t &res) const {
|
1068
|
+
|
1069
|
+
// Least square
|
1070
|
+
if(!base_t::update_position_solution(geomat, res)){
|
1071
|
+
return false;
|
1072
|
+
}
|
1073
|
+
static_cast<user_pvt_t &>(res).G = geomat.G;
|
1074
|
+
static_cast<user_pvt_t &>(res).W = geomat.W;
|
1075
|
+
static_cast<user_pvt_t &>(res).delta_r = geomat.delta_r;
|
1076
|
+
return true;
|
1077
|
+
}
|
1078
|
+
};
|
1079
|
+
|
1080
|
+
|
1081
|
+
#endif /* __GPS_SOLVER_BASE_H__ */
|