gps_pvt 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/CHANGELOG.md +5 -0
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/Gemfile +10 -0
  6. data/README.md +86 -0
  7. data/Rakefile +86 -0
  8. data/bin/console +15 -0
  9. data/bin/setup +8 -0
  10. data/ext/gps_pvt/Coordinate/Coordinate_wrap.cxx +6613 -0
  11. data/ext/gps_pvt/GPS/GPS_wrap.cxx +16019 -0
  12. data/ext/gps_pvt/SylphideMath/SylphideMath_wrap.cxx +21050 -0
  13. data/ext/gps_pvt/extconf.rb +70 -0
  14. data/ext/ninja-scan-light/tool/navigation/EGM.h +2971 -0
  15. data/ext/ninja-scan-light/tool/navigation/GPS.h +2432 -0
  16. data/ext/ninja-scan-light/tool/navigation/GPS_Solver.h +479 -0
  17. data/ext/ninja-scan-light/tool/navigation/GPS_Solver_Base.h +1081 -0
  18. data/ext/ninja-scan-light/tool/navigation/GPS_Solver_MultiFrequency.h +199 -0
  19. data/ext/ninja-scan-light/tool/navigation/GPS_Solver_RAIM.h +210 -0
  20. data/ext/ninja-scan-light/tool/navigation/MagneticField.h +928 -0
  21. data/ext/ninja-scan-light/tool/navigation/NTCM.h +211 -0
  22. data/ext/ninja-scan-light/tool/navigation/RINEX.h +1781 -0
  23. data/ext/ninja-scan-light/tool/navigation/WGS84.h +186 -0
  24. data/ext/ninja-scan-light/tool/navigation/coordinate.h +406 -0
  25. data/ext/ninja-scan-light/tool/param/bit_array.h +145 -0
  26. data/ext/ninja-scan-light/tool/param/complex.h +558 -0
  27. data/ext/ninja-scan-light/tool/param/matrix.h +4049 -0
  28. data/ext/ninja-scan-light/tool/param/matrix_fixed.h +665 -0
  29. data/ext/ninja-scan-light/tool/param/matrix_special.h +562 -0
  30. data/ext/ninja-scan-light/tool/param/quaternion.h +765 -0
  31. data/ext/ninja-scan-light/tool/param/vector3.h +651 -0
  32. data/ext/ninja-scan-light/tool/swig/Coordinate.i +177 -0
  33. data/ext/ninja-scan-light/tool/swig/GPS.i +1102 -0
  34. data/ext/ninja-scan-light/tool/swig/SylphideMath.i +1234 -0
  35. data/ext/ninja-scan-light/tool/swig/extconf.rb +5 -0
  36. data/ext/ninja-scan-light/tool/swig/makefile +53 -0
  37. data/ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb +417 -0
  38. data/ext/ninja-scan-light/tool/swig/spec/SylphideMath_spec.rb +489 -0
  39. data/gps_pvt.gemspec +57 -0
  40. data/lib/gps_pvt/receiver.rb +375 -0
  41. data/lib/gps_pvt/ubx.rb +148 -0
  42. data/lib/gps_pvt/version.rb +5 -0
  43. data/lib/gps_pvt.rb +9 -0
  44. data/sig/gps_pvt.rbs +4 -0
  45. 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__ */