gps_pvt 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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__ */
|