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.
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__ */