gps_pvt 0.2.1 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +33 -5
- data/Rakefile +0 -0
- data/exe/gps_pvt +63 -0
- data/ext/gps_pvt/GPS/GPS_wrap.cxx +784 -745
- data/ext/gps_pvt/SylphideMath/SylphideMath_wrap.cxx +723 -436
- data/ext/ninja-scan-light/tool/navigation/GPS.h +15 -44
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver.h +61 -147
- data/ext/ninja-scan-light/tool/navigation/GPS_Solver_Base.h +56 -2
- data/ext/ninja-scan-light/tool/navigation/SBAS.h +2 -2
- data/ext/ninja-scan-light/tool/navigation/SBAS_Solver.h +55 -78
- data/ext/ninja-scan-light/tool/param/bit_array.h +4 -3
- data/ext/ninja-scan-light/tool/swig/GPS.i +255 -63
- data/ext/ninja-scan-light/tool/swig/SylphideMath.i +91 -21
- data/ext/ninja-scan-light/tool/swig/spec/GPS_spec.rb +25 -5
- data/ext/ninja-scan-light/tool/swig/spec/SylphideMath_spec.rb +51 -7
- data/gps_pvt.gemspec +63 -0
- data/lib/gps_pvt/receiver.rb +84 -40
- data/lib/gps_pvt/version.rb +1 -1
- metadata +7 -6
@@ -238,6 +238,23 @@ INSTANTIATE_COMPLEX(double, D);
|
|
238
238
|
|
239
239
|
#undef INSTANTIATE_COMPLEX
|
240
240
|
|
241
|
+
#if defined(SWIGRUBY)
|
242
|
+
/* Work around of miss detection of negative value on Windows Ruby (devkit).
|
243
|
+
* This results from SWIG_AsVal(unsigned int) depends on SWIG_AsVal(unsigned long),
|
244
|
+
* and sizeof(long) == sizeof(int).
|
245
|
+
*/
|
246
|
+
%fragment("check_value"{unsigned int}, "header"){
|
247
|
+
inline bool is_lt_zero_after_asval(const unsigned int &i){
|
248
|
+
return ((sizeof(unsigned int) == sizeof(unsigned long)) && ((UINT_MAX >> 1) <= i));
|
249
|
+
}
|
250
|
+
void raise_if_lt_zero_after_asval(const unsigned int &i){
|
251
|
+
if(is_lt_zero_after_asval(i)){
|
252
|
+
SWIG_exception(SWIG_ValueError, "Expected positive value.");
|
253
|
+
}
|
254
|
+
}
|
255
|
+
}
|
256
|
+
#endif
|
257
|
+
|
241
258
|
#define DO_NOT_INSTANTIATE_SCALAR_MATRIX
|
242
259
|
#define USE_MATRIX_VIEW_FILTER
|
243
260
|
|
@@ -420,8 +437,6 @@ class Matrix : public Matrix_Frozen<T, Array2D_Type, ViewType> {
|
|
420
437
|
#endif
|
421
438
|
|
422
439
|
typedef Matrix<T, Array2D_Type, ViewType> self_t;
|
423
|
-
self_t &swapRows(const unsigned int &row1, const unsigned int &row2);
|
424
|
-
self_t &swapColumns(const unsigned int &column1, const unsigned int &column2);
|
425
440
|
};
|
426
441
|
|
427
442
|
%inline {
|
@@ -949,6 +964,7 @@ MAKE_TO_S(Matrix_Frozen)
|
|
949
964
|
}
|
950
965
|
#if defined(SWIGRUBY)
|
951
966
|
%fragment(SWIG_AsVal_frag(unsigned int));
|
967
|
+
%fragment("check_value"{unsigned int});
|
952
968
|
Matrix(const void *replacer){
|
953
969
|
const SWIG_Object *value(static_cast<const SWIG_Object *>(replacer));
|
954
970
|
static const ID id_r(rb_intern("row_size")), id_c(rb_intern("column_size"));
|
@@ -959,13 +975,10 @@ MAKE_TO_S(Matrix_Frozen)
|
|
959
975
|
MatrixUtil::replace(res, replacer);
|
960
976
|
return new Matrix<T, Array2D_Type, ViewType>(res);
|
961
977
|
}else if(value && rb_respond_to(*value, id_r) && rb_respond_to(*value, id_c)){
|
962
|
-
|
963
|
-
* can not detect less than zero in Windows Ruby devkit.
|
964
|
-
*/
|
965
|
-
int r, c;
|
978
|
+
unsigned int r, c;
|
966
979
|
VALUE v_r(rb_funcall(*value, id_r, 0, 0)), v_c(rb_funcall(*value, id_c, 0, 0));
|
967
|
-
if(!SWIG_IsOK(SWIG_AsVal(int)(v_r, &r)) || (r
|
968
|
-
|| !SWIG_IsOK(SWIG_AsVal(int)(v_c, &c)) || (c
|
980
|
+
if(!SWIG_IsOK(SWIG_AsVal(unsigned int)(v_r, &r)) || is_lt_zero_after_asval(r)
|
981
|
+
|| !SWIG_IsOK(SWIG_AsVal(unsigned int)(v_c, &c)) || is_lt_zero_after_asval(c)){
|
969
982
|
throw std::runtime_error(
|
970
983
|
std::string("Unexpected length [")
|
971
984
|
.append(inspect_str(v_r)).append(", ")
|
@@ -980,7 +993,23 @@ MAKE_TO_S(Matrix_Frozen)
|
|
980
993
|
}
|
981
994
|
#endif
|
982
995
|
|
983
|
-
|
996
|
+
/*
|
997
|
+
* Returning (*this) requires special care;
|
998
|
+
* "self_t &func(){return (*this);}" in a C++ file may generate
|
999
|
+
* a new wrapped object deleted by GC in the target language
|
1000
|
+
* unless the care.
|
1001
|
+
*
|
1002
|
+
* Work around 1)
|
1003
|
+
* %typemap(in, numinputs=0) self_t *self_p "";
|
1004
|
+
* %typemap(argout) self_t *self_p "$result = self;";
|
1005
|
+
* void func(self_t *self_p){...}
|
1006
|
+
*
|
1007
|
+
* Work around 2) (useful without overwrite of the original source, but may overfit)
|
1008
|
+
* %typemap(out) self_t & "$result = self;"
|
1009
|
+
* self_t &func(){...; return *$self;}
|
1010
|
+
*/
|
1011
|
+
%typemap(in, numinputs=0) self_t *self_p "";
|
1012
|
+
%typemap(argout) self_t *self_p "$result = self;";
|
984
1013
|
|
985
1014
|
T &__setitem__(const unsigned int &row, const unsigned int &column, const T &value) {
|
986
1015
|
return (($self)->operator()(row, column) = value);
|
@@ -997,41 +1026,50 @@ MAKE_TO_S(Matrix_Frozen)
|
|
997
1026
|
#endif
|
998
1027
|
%rename("scalar") getScalar;
|
999
1028
|
%rename("I") getI;
|
1000
|
-
|
1001
|
-
|
1029
|
+
|
1030
|
+
void swap_rows(
|
1031
|
+
self_t *self_p,
|
1032
|
+
const unsigned int &r1, const unsigned int &r2){
|
1033
|
+
$self->swapRows(r1, r2);
|
1034
|
+
}
|
1035
|
+
void swap_columns(
|
1036
|
+
self_t *self_p,
|
1037
|
+
const unsigned int &c1, const unsigned int &c2){
|
1038
|
+
$self->swapColumns(c1, c2);
|
1039
|
+
}
|
1002
1040
|
|
1003
1041
|
template <class T2, class Array2D_Type2, class ViewType2>
|
1004
|
-
|
1005
|
-
|
1042
|
+
void replace(
|
1043
|
+
self_t *self_p,
|
1044
|
+
const Matrix_Frozen<T2, Array2D_Type2, ViewType2> &matrix){
|
1045
|
+
$self->replace(matrix);
|
1006
1046
|
}
|
1007
1047
|
INSTANTIATE_MATRIX_FUNC(replace, replace);
|
1008
1048
|
|
1009
|
-
|
1049
|
+
void replace(self_t *self_p, const void *replacer = NULL){
|
1010
1050
|
if(!MatrixUtil::replace(*$self, replacer)){
|
1011
1051
|
throw std::runtime_error("Unsupported replacement");
|
1012
1052
|
}
|
1013
|
-
return *$self;
|
1014
1053
|
}
|
1015
1054
|
|
1016
|
-
|
1055
|
+
void replace(self_t *self_p, const T *serialized){
|
1017
1056
|
if(!MatrixUtil::replace(*$self, serialized)){
|
1018
1057
|
throw std::runtime_error("Unsupported replacement");
|
1019
1058
|
}
|
1020
|
-
return *$self;
|
1021
1059
|
}
|
1022
1060
|
|
1023
1061
|
#ifdef SWIGRUBY
|
1024
|
-
%bang
|
1025
|
-
%bang
|
1062
|
+
%bang swap_rows;
|
1063
|
+
%bang swap_columns;
|
1026
1064
|
%rename("replace!") replace;
|
1027
1065
|
|
1028
|
-
|
1066
|
+
void map_bang(
|
1067
|
+
self_t *self_p,
|
1029
1068
|
void (*each_func)(
|
1030
1069
|
const T &src, T *dst,
|
1031
1070
|
const unsigned int &i, const unsigned int &j),
|
1032
1071
|
const typename MatrixUtil::each_which_t &each_which = MatrixUtil::EACH_ALL){
|
1033
1072
|
MatrixUtil::each(*$self, each_func, each_which, $self);
|
1034
|
-
return *$self;
|
1035
1073
|
}
|
1036
1074
|
%rename("map!") map_bang;
|
1037
1075
|
%alias map_bang "collect!,map_with_index!,collect_with_index!";
|
@@ -1147,6 +1185,8 @@ INSTANTIATE_MATRIX_EIGEN2(type, ctype, Array2D_Dense<type >, MatView_pt);
|
|
1147
1185
|
#endif
|
1148
1186
|
|
1149
1187
|
%define INSTANTIATE_MATRIX(type, suffix)
|
1188
|
+
%typemap(check, fragment="check_value"{unsigned int})
|
1189
|
+
const unsigned int & "raise_if_lt_zero_after_asval(*$1);"
|
1150
1190
|
#if !defined(DO_NOT_INSTANTIATE_SCALAR_MATRIX)
|
1151
1191
|
%extend Matrix_Frozen<type, Array2D_ScaledUnit<type >, MatViewBase> {
|
1152
1192
|
const Matrix_Frozen<type, Array2D_ScaledUnit<type >, MatViewBase> &transpose() const {
|
@@ -1200,6 +1240,35 @@ INSTANTIATE_MATRIX_PARTIAL(type, Array2D_Dense<type >, MatView_pt, MatView_pt);
|
|
1200
1240
|
%template(Matrix_Frozen ## suffix ## _pt) Matrix_Frozen<type, Array2D_Dense<type >, MatView_pt>;
|
1201
1241
|
#endif
|
1202
1242
|
|
1243
|
+
%extend Matrix<type, Array2D_Dense<type > > {
|
1244
|
+
#if defined(SWIGRUBY)
|
1245
|
+
%bang resize;
|
1246
|
+
#endif
|
1247
|
+
%typemap(in, fragment="check_value"{unsigned int})
|
1248
|
+
unsigned int *r_p (unsigned int temp), unsigned int *c_p (unsigned int temp) {
|
1249
|
+
if(SWIG_IsOK(SWIG_AsVal(unsigned int)($input, &temp))){
|
1250
|
+
#if defined(SWIGRUBY)
|
1251
|
+
raise_if_lt_zero_after_asval(temp);
|
1252
|
+
#endif
|
1253
|
+
$1 = &temp;
|
1254
|
+
}
|
1255
|
+
#if defined(SWIGRUBY)
|
1256
|
+
else if(NIL_P($input)){$1 = NULL;}
|
1257
|
+
#endif
|
1258
|
+
else{SWIG_exception(SWIG_TypeError, "$*1_ltype is expected");}
|
1259
|
+
}
|
1260
|
+
Matrix<type, Array2D_Dense<type > > &resize(
|
1261
|
+
const unsigned int *r_p, const unsigned int *c_p){
|
1262
|
+
unsigned int r(r_p ? *r_p : $self->rows()), c(c_p ? *c_p : self->columns());
|
1263
|
+
Matrix<type, Array2D_Dense<type > > mat_new(r, c);
|
1264
|
+
unsigned int r_min(r), c_min(c);
|
1265
|
+
if(r_min > $self->rows()){r_min = $self->rows();}
|
1266
|
+
if(c_min > $self->columns()){c_min = $self->columns();}
|
1267
|
+
mat_new.partial(r_min, c_min).replace($self->partial(r_min, c_min), false);
|
1268
|
+
return (*($self) = mat_new);
|
1269
|
+
}
|
1270
|
+
};
|
1271
|
+
|
1203
1272
|
%template(Matrix ## suffix) Matrix<type, Array2D_Dense<type > >;
|
1204
1273
|
#if defined(SWIGRUBY)
|
1205
1274
|
%fragment("init"{Matrix<type, Array2D_Dense<type > >}, "init") {
|
@@ -1212,6 +1281,7 @@ INSTANTIATE_MATRIX_PARTIAL(type, Array2D_Dense<type >, MatView_pt, MatView_pt);
|
|
1212
1281
|
}
|
1213
1282
|
%fragment("init"{Matrix<type, Array2D_Dense<type > >});
|
1214
1283
|
#endif
|
1284
|
+
%typemap(check) const unsigned int &;
|
1215
1285
|
%enddef
|
1216
1286
|
|
1217
1287
|
INSTANTIATE_MATRIX(double, D);
|
@@ -221,7 +221,11 @@ __RINEX_OBS_TEXT__
|
|
221
221
|
f.path
|
222
222
|
},
|
223
223
|
}}
|
224
|
-
let(:solver){
|
224
|
+
let(:solver){
|
225
|
+
res = GPS::Solver::new
|
226
|
+
res.correction = {:gps_ionospheric => :klobuchar, :gps_tropospheric => :hopfield}
|
227
|
+
res
|
228
|
+
}
|
225
229
|
|
226
230
|
describe 'demo' do
|
227
231
|
it 'calculates position without any error' do
|
@@ -249,13 +253,13 @@ __RINEX_OBS_TEXT__
|
|
249
253
|
t_meas = GPS::Time::new(1849, 172413)
|
250
254
|
puts "Measurement time: #{t_meas.to_a} (a.k.a #{"%d/%d/%d %02d:%02d:%02d UTC"%[*t_meas.c_tm]})"
|
251
255
|
expect(t_meas.c_tm).to eq([2015, 6, 15, 23, 53, 33])
|
256
|
+
expect(GPS::Time::new(0, t_meas.serialize)).to eq(t_meas)
|
252
257
|
|
253
258
|
sn.update_all_ephemeris(t_meas)
|
254
259
|
|
255
260
|
[:alpha, :beta].each{|k|
|
256
261
|
puts "Iono #{k}: #{sn.iono_utc.send(k)}"
|
257
262
|
}
|
258
|
-
puts solver.gps_options.ionospheric_models
|
259
263
|
|
260
264
|
meas.each{|prn, k, v|
|
261
265
|
eph = sn.ephemeris(prn)
|
@@ -336,6 +340,20 @@ __RINEX_OBS_TEXT__
|
|
336
340
|
|
337
341
|
it 'can be modified through hooks' do
|
338
342
|
sn = solver.gps_space_node
|
343
|
+
expect(solver.correction[:gps_ionospheric]).to include(:klobuchar)
|
344
|
+
expect(solver.correction[:gps_tropospheric]).to include(:hopfield)
|
345
|
+
expect{solver.correction = nil}.to raise_error(RuntimeError)
|
346
|
+
expect{solver.correction = {
|
347
|
+
:gps_ionospheric => [proc{|t, usr_pos, sat_pos|
|
348
|
+
expect(t).to be_a_kind_of(GPS::Time)
|
349
|
+
expect(usr_pos).to be_a_kind_of(Coordinate::XYZ) unless usr_pos
|
350
|
+
expect(sat_pos).to be_a_kind_of(Coordinate::ENU) unless sat_pos
|
351
|
+
false
|
352
|
+
}, :klobuchar, :no_correction],
|
353
|
+
:options => {:f_10_7 => 10},
|
354
|
+
}}.not_to raise_error
|
355
|
+
expect(solver.correction[:gps_ionospheric]).to include(:no_correction)
|
356
|
+
expect(solver.correction[:options][:f_10_7]).to eq(10)
|
339
357
|
sn.read(input[:rinex_nav])
|
340
358
|
t_meas = GPS::Time::new(1849, 172413)
|
341
359
|
sn.update_all_ephemeris(t_meas)
|
@@ -349,11 +367,13 @@ __RINEX_OBS_TEXT__
|
|
349
367
|
weight = 1
|
350
368
|
[weight, range_c, range_r, rate_rel_neg] + los_neg
|
351
369
|
}
|
352
|
-
solver.hooks[:update_position_solution] = proc{
|
353
|
-
|
370
|
+
solver.hooks[:update_position_solution] = proc{|mat_G, mat_W, mat_delta_r, temp_pvt|
|
371
|
+
expect(temp_pvt).to be_a_kind_of(GPS::PVT)
|
372
|
+
[mat_G, mat_W, mat_delta_r].each{|mat|
|
354
373
|
expect(mat).to be_a_kind_of(SylphideMath::MatrixD)
|
374
|
+
expect(mat.rows).to be >= temp_pvt.used_satellites
|
375
|
+
expect(mat).to respond_to(:resize!)
|
355
376
|
}
|
356
|
-
mat_G, mat_W, mat_delta_r = mats
|
357
377
|
}
|
358
378
|
solver.hooks[:satellite_position] = proc{
|
359
379
|
i = 0
|
@@ -19,6 +19,12 @@ shared_examples 'Matrix' do
|
|
19
19
|
expect( mat_type::new(*params[:rc]).rows ).to equal(params[:rc][0])
|
20
20
|
expect( mat_type::new(*params[:rc]).columns ).to equal(params[:rc][1])
|
21
21
|
end
|
22
|
+
it 'declines negative number argument' do
|
23
|
+
[[-1, 1], [1, -1]].each{|sf|
|
24
|
+
r, c = params[:rc].zip(sf).collect{|v1, v2| v1 * v2}
|
25
|
+
expect{ mat_type::new(r, c) }.to raise_error(ArgumentError)
|
26
|
+
}
|
27
|
+
end
|
22
28
|
it 'accepts ([[]])' do
|
23
29
|
expect{ mat_type::new(compare_with) }.not_to raise_error
|
24
30
|
expect( mat_type::new(compare_with).rows ).to equal(params[:rc][0])
|
@@ -44,9 +50,11 @@ shared_examples 'Matrix' do
|
|
44
50
|
a.define_singleton_method(:[]){|i, j| raise(IndexError) if i != j; 0}
|
45
51
|
expect{ mat_type::new(a) }.to raise_error(IndexError)
|
46
52
|
|
47
|
-
|
48
|
-
|
49
|
-
|
53
|
+
[:row_size, :column_size].each{|f|
|
54
|
+
a = a_gen.call
|
55
|
+
a.define_singleton_method(f){-1}
|
56
|
+
expect{ mat_type::new(a) }.to raise_error(RuntimeError)
|
57
|
+
}
|
50
58
|
end
|
51
59
|
it 'is invoked with I, identity, unit' do
|
52
60
|
[:I, :identity, :unit].each{|f|
|
@@ -132,7 +140,7 @@ shared_examples 'Matrix' do
|
|
132
140
|
expect(mat[:square].sum).to eq(Matrix[*mat[:square].to_a].sum)
|
133
141
|
expect(mat[:not_square].sum).to eq(Matrix[*mat[:not_square].to_a].sum)
|
134
142
|
end
|
135
|
-
it '
|
143
|
+
it 'determinant, det' do
|
136
144
|
[:determinant, :det].each{|f|
|
137
145
|
#expect(mat[:square].send(f)).to eq(Matrix[*mat[:square].to_a].det)
|
138
146
|
expect{mat[:not_square].send(f)}.to raise_error(RuntimeError)
|
@@ -163,6 +171,15 @@ shared_examples 'Matrix' do
|
|
163
171
|
}
|
164
172
|
}
|
165
173
|
end
|
174
|
+
it 'is inaccessible and unchangeable with negative number arguments' do
|
175
|
+
[[-1, 0], [0, -1]].each{|i, j|
|
176
|
+
[[:[], i, j], [:[]=, i, j, 0]].each{|args|
|
177
|
+
expect{ mat[0].send(*args) }.to raise_error{|err|
|
178
|
+
expect(err).to be_a(RangeError).or be_a(ArgumentError)
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}
|
182
|
+
end
|
166
183
|
end
|
167
184
|
|
168
185
|
describe 'elements' do
|
@@ -197,9 +214,19 @@ shared_examples 'Matrix' do
|
|
197
214
|
it 'is swappable with swap_rows! or swap_cloumns!' do
|
198
215
|
mat_builtin = Matrix[*compare_with[0]]
|
199
216
|
[:swap_rows!, :swap_columns!].each.with_index{|func, i|
|
200
|
-
params[:rc][i].times.to_a.combination(2).to_a{|a, b|
|
201
|
-
mat[0].send(func, a, b)
|
202
|
-
|
217
|
+
params[:rc][i].times.to_a.combination(2).to_a.each{|a, b|
|
218
|
+
mat_mod = mat[0].send(func, a, b)
|
219
|
+
case func
|
220
|
+
when :swap_rows!
|
221
|
+
idxs = mat_builtin.row_count.times.to_a
|
222
|
+
idxs[a], idxs[b] = [b, a]
|
223
|
+
mat_builtin = Matrix::rows(mat_builtin.row_vectors.values_at(*idxs))
|
224
|
+
when :swap_columns!
|
225
|
+
idxs = mat_builtin.column_count.times.to_a
|
226
|
+
idxs[a], idxs[b] = [b, a]
|
227
|
+
mat_builtin = Matrix::columns(mat_builtin.column_vectors.values_at(*idxs))
|
228
|
+
end
|
229
|
+
expect(mat_mod).to equal(mat[0])
|
203
230
|
expect(mat[0].to_a).to eq(mat_builtin.to_a)
|
204
231
|
}
|
205
232
|
}
|
@@ -394,6 +421,23 @@ shared_examples 'Matrix' do
|
|
394
421
|
}
|
395
422
|
expect{mat[2] / mat[3]}.to raise_error(RuntimeError)
|
396
423
|
end
|
424
|
+
it 'have resize!' do
|
425
|
+
[-1, :sym].each{|arg|
|
426
|
+
[[arg, nil], [nil, arg]].each{|args|
|
427
|
+
expect{mat[0].resize!(*args)}.to raise_error{|err|
|
428
|
+
expect(err).to be_a(TypeError).or be_a(ArgumentError)
|
429
|
+
}
|
430
|
+
}
|
431
|
+
}
|
432
|
+
mat_orig = mat[0].to_a
|
433
|
+
r, c = [:rows, :columns].collect{|f| mat[0].send(f)}
|
434
|
+
expect(mat[0].resize!(r, c).to_a).to eq(mat_orig)
|
435
|
+
expect(mat[0].resize!(r, nil).to_a).to eq(mat_orig)
|
436
|
+
expect(mat[0].resize!(nil, c).to_a).to eq(mat_orig)
|
437
|
+
expect(mat[0].resize!(nil, nil).to_a).to eq(mat_orig)
|
438
|
+
expect(mat[0].resize!(r * 2, c * 2).to_a).to \
|
439
|
+
eq(Matrix::build(r * 2, c * 2){|i, j| (i < r && j < c) ? mat_orig[i][j] : 0}.to_a)
|
440
|
+
end
|
397
441
|
end
|
398
442
|
|
399
443
|
describe 'decomposition' do
|
data/gps_pvt.gemspec
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/gps_pvt/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "gps_pvt"
|
7
|
+
spec.version = GPS_PVT::VERSION
|
8
|
+
spec.authors = ["fenrir(M.Naruoka)"]
|
9
|
+
spec.email = ["fenrir.naru@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "GPS position, velocity, and time (PVT) solver"
|
12
|
+
spec.description = "This module calculate PVT by using raw observation obtained from a GPS receiver"
|
13
|
+
spec.homepage = "https://github.com/fenrir-naru/gps_pvt"
|
14
|
+
spec.required_ruby_version = ">= 2.3.0"
|
15
|
+
|
16
|
+
#spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
17
|
+
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
19
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
20
|
+
#spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
21
|
+
|
22
|
+
spec.extensions = ["ext/gps_pvt/extconf.rb"]
|
23
|
+
|
24
|
+
# Specify which files should be added to the gem when it is released.
|
25
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
26
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
27
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
28
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
spec.bindir = "exe"
|
32
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
33
|
+
spec.require_paths = ["lib"]
|
34
|
+
|
35
|
+
spec.files += proc{
|
36
|
+
require 'pathname'
|
37
|
+
base_dir = Pathname::new(File::absolute_path(File.dirname(__FILE__)))
|
38
|
+
# get an array of submodule dirs by executing 'pwd' inside each submodule
|
39
|
+
`git submodule --quiet foreach pwd`.split($/).collect{|dir|
|
40
|
+
# issue git ls-files in submodule's directory
|
41
|
+
`git -C #{dir} ls-files -v`.split($/).collect{|f|
|
42
|
+
next nil unless f =~ /^H */ # consider git sparse checkout
|
43
|
+
# get relative path
|
44
|
+
f = Pathname::new(File::join(dir, $'))
|
45
|
+
begin
|
46
|
+
(f.relative? ? f : f.relative_path_from(base_dir)).to_s
|
47
|
+
rescue
|
48
|
+
# Patch for Windows drive letter problem
|
49
|
+
base_dir = Pathname::new(base_dir.to_s.sub(/^([^\/])+:\//){"/#{$1}/"})
|
50
|
+
f.relative_path_from(base_dir).to_s
|
51
|
+
end
|
52
|
+
}.compact
|
53
|
+
}.flatten
|
54
|
+
}.call
|
55
|
+
|
56
|
+
# Uncomment to register a new dependency of your gem
|
57
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
58
|
+
spec.add_development_dependency "rake"
|
59
|
+
spec.add_development_dependency "rake-compiler"
|
60
|
+
|
61
|
+
# For more information and examples about making a new gem, checkout our
|
62
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
63
|
+
end
|
data/lib/gps_pvt/receiver.rb
CHANGED
@@ -7,15 +7,22 @@ require_relative 'GPS'
|
|
7
7
|
|
8
8
|
module GPS_PVT
|
9
9
|
class Receiver
|
10
|
+
|
11
|
+
GPS::Time.send(:define_method, :utc){ # send as work around of old Ruby
|
12
|
+
res = c_tm(GPS::Time::guess_leap_seconds(self))
|
13
|
+
res[-1] += (seconds % 1)
|
14
|
+
res
|
15
|
+
}
|
16
|
+
|
10
17
|
def self.pvt_items(opt = {})
|
11
18
|
opt = {
|
12
19
|
:system => [[:GPS, 1..32]],
|
13
20
|
:satellites => (1..32).to_a,
|
14
21
|
}.merge(opt)
|
15
22
|
[[
|
16
|
-
[:week, :itow_rcv, :year, :month, :mday, :hour, :min, :
|
23
|
+
[:week, :itow_rcv, :year, :month, :mday, :hour, :min, :sec_rcv_UTC],
|
17
24
|
proc{|pvt|
|
18
|
-
[:week, :seconds, :
|
25
|
+
[:week, :seconds, :utc].collect{|f| pvt.receiver_time.send(f)}.flatten
|
19
26
|
}
|
20
27
|
]] + [[
|
21
28
|
[:receiver_clock_error_meter, :longitude, :latitude, :height, :rel_E, :rel_N, :rel_U],
|
@@ -47,23 +54,31 @@ class Receiver
|
|
47
54
|
]] + [
|
48
55
|
[:used_satellites, proc{|pvt| pvt.used_satellites}],
|
49
56
|
] + opt[:system].collect{|sys, range|
|
50
|
-
|
51
|
-
|
57
|
+
range = range.kind_of?(Array) ? proc{
|
58
|
+
# check whether inputs can be converted to Range
|
59
|
+
next nil if range.empty?
|
60
|
+
a, b = range.minmax
|
61
|
+
((b - a) == (range.length - 1)) ? (a..b) : range
|
62
|
+
}.call : range
|
63
|
+
next nil unless range
|
64
|
+
bit_flip, label = case range
|
65
|
+
when Array
|
66
|
+
[proc{|res, i|
|
52
67
|
res[i] = "1" if i = range.index(i)
|
53
68
|
res
|
54
|
-
}
|
55
|
-
|
69
|
+
}, range.collect{|pen| pen & 0xFF}.reverse.join('+')]
|
70
|
+
when Range
|
56
71
|
base_prn = range.min
|
57
|
-
proc{|res, i|
|
72
|
+
[proc{|res, i|
|
58
73
|
res[i - base_prn] = "1" if range.include?(i)
|
59
74
|
res
|
60
|
-
}
|
75
|
+
}, [:max, :min].collect{|f| range.send(f) & 0xFF}.join('..')]
|
61
76
|
end
|
62
|
-
["#{sys}_PRN", proc{|pvt|
|
77
|
+
["#{sys}_PRN(#{label})", proc{|pvt|
|
63
78
|
pvt.used_satellite_list.inject("0" * range.size, &bit_flip) \
|
64
79
|
.scan(/.{1,8}/).join('_').reverse
|
65
80
|
}]
|
66
|
-
} + [[
|
81
|
+
}.compact + [[
|
67
82
|
opt[:satellites].collect{|prn, label|
|
68
83
|
[:range_residual, :weight, :azimuth, :elevation, :slopeH, :slopeV].collect{|str|
|
69
84
|
"#{str}(#{label || prn})"
|
@@ -73,12 +88,12 @@ class Receiver
|
|
73
88
|
next ([nil] * 6 * opt[:satellites].size) unless pvt.position_solved?
|
74
89
|
sats = pvt.used_satellite_list
|
75
90
|
r, w = [:delta_r, :W].collect{|f| pvt.send(f)}
|
76
|
-
opt[:satellites].collect{|
|
77
|
-
next ([nil] * 6) unless i2 = sats.index(
|
91
|
+
opt[:satellites].collect{|prn, label|
|
92
|
+
next ([nil] * 6) unless i2 = sats.index(prn)
|
78
93
|
[r[i2, 0], w[i2, i2]] +
|
79
94
|
[:azimuth, :elevation].collect{|f|
|
80
|
-
pvt.send(f)[
|
81
|
-
} + [pvt.slopeH[
|
95
|
+
pvt.send(f)[prn] / Math::PI * 180
|
96
|
+
} + [pvt.slopeH[prn], pvt.slopeV[prn]]
|
82
97
|
}.flatten
|
83
98
|
},
|
84
99
|
]] + [[
|
@@ -106,16 +121,19 @@ class Receiver
|
|
106
121
|
opt = {
|
107
122
|
:satellites => (1..32).to_a,
|
108
123
|
}.merge(opt)
|
124
|
+
keys = [:PSEUDORANGE, :RANGE_RATE, :DOPPLER, :FREQUENCY].collect{|k|
|
125
|
+
GPS::Measurement.const_get("L1_#{k}".to_sym)
|
126
|
+
}
|
109
127
|
[[
|
110
128
|
opt[:satellites].collect{|prn, label|
|
111
129
|
[:L1_range, :L1_rate].collect{|str| "#{str}(#{label || prn})"}
|
112
130
|
}.flatten,
|
113
131
|
proc{|meas|
|
114
|
-
meas_hash =
|
115
|
-
opt[:satellites].collect{|prn|
|
116
|
-
|
117
|
-
|
118
|
-
|
132
|
+
meas_hash = meas.to_hash
|
133
|
+
opt[:satellites].collect{|prn, label|
|
134
|
+
pr, rate, doppler, freq = keys.collect{|k| meas_hash[prn][k] rescue nil}
|
135
|
+
freq ||= GPS::SpaceNode.L1_Frequency
|
136
|
+
[pr, rate || ((doppler * GPS::SpaceNode::light_speed / freq) rescue nil)]
|
119
137
|
}
|
120
138
|
}
|
121
139
|
]]
|
@@ -135,6 +153,14 @@ class Receiver
|
|
135
153
|
rel_prop
|
136
154
|
}
|
137
155
|
@debug = {}
|
156
|
+
solver_opts = [:gps_options, :sbas_options].collect{|target|
|
157
|
+
@solver.send(target)
|
158
|
+
}
|
159
|
+
solver_opts.each{|opt|
|
160
|
+
# default solver options
|
161
|
+
opt.elevation_mask = 0.0 / 180 * Math::PI # 0 deg (use satellite over horizon)
|
162
|
+
opt.residual_mask = 1E4 # 10 km (without residual filter, practically)
|
163
|
+
}
|
138
164
|
output_options = {
|
139
165
|
:system => [[:GPS, 1..32], [:QZSS, 193..202]],
|
140
166
|
:satellites => (1..32).to_a + (193..202).to_a, # [idx, ...] or [[idx, label], ...] is acceptable
|
@@ -160,6 +186,13 @@ class Receiver
|
|
160
186
|
when :identical # same as default
|
161
187
|
next true
|
162
188
|
end
|
189
|
+
when :elevation_mask_deg
|
190
|
+
raise "Unknown elevation mask angle: #{v}" unless elv_deg = (Float(v) rescue nil)
|
191
|
+
$stderr.puts "Elevation mask: #{elv_deg} deg"
|
192
|
+
solver_opts.each{|opt|
|
193
|
+
opt.elevation_mask = elv_deg / 180 * Math::PI # 0 deg (use satellite over horizon)
|
194
|
+
}
|
195
|
+
next true
|
163
196
|
when :base_station
|
164
197
|
crd, sys = v.split(/ *, */).collect.with_index{|item, i|
|
165
198
|
case item
|
@@ -193,9 +226,9 @@ class Receiver
|
|
193
226
|
sys, svid = case spec
|
194
227
|
when Integer
|
195
228
|
[nil, spec]
|
196
|
-
when
|
197
|
-
[$1.upcase.to_sym, (
|
198
|
-
when
|
229
|
+
when /^([a-zA-Z]+)(?::(-?\d+))?$/
|
230
|
+
[$1.upcase.to_sym, (Integer($2) rescue nil)]
|
231
|
+
when /^-?\d+$/
|
199
232
|
[nil, $&.to_i]
|
200
233
|
else
|
201
234
|
next false
|
@@ -206,27 +239,37 @@ class Receiver
|
|
206
239
|
else
|
207
240
|
(k == :with) ? :include : :exclude
|
208
241
|
end
|
209
|
-
|
210
|
-
|
211
|
-
[svid || ((1..32).to_a + (193..202).to_a)].flatten.each{
|
212
|
-
@solver.gps_options.send(mode, svid)
|
213
|
-
}
|
214
|
-
elsif (sys == :SBAS) || (svid && (120..158).include?(svid)) then
|
215
|
-
prns = [svid || (120..158).to_a].flatten
|
216
|
-
unless (i = output_options[:system].index{|sys, range| sys == :SBAS}) then
|
242
|
+
update_output = proc{|sys_target, prns, labels|
|
243
|
+
unless (i = output_options[:system].index{|sys, range| sys == sys_target}) then
|
217
244
|
i = -1
|
218
|
-
output_options[:system] << [
|
245
|
+
output_options[:system] << [sys_target, []]
|
219
246
|
else
|
220
|
-
output_options[:system][i].reject!{|prn| prns.include?(prn)}
|
247
|
+
output_options[:system][i][1].reject!{|prn| prns.include?(prn)}
|
221
248
|
end
|
222
249
|
output_options[:satellites].reject!{|prn, label| prns.include?(prn)}
|
223
250
|
if mode == :include then
|
224
251
|
output_options[:system][i][1] += prns
|
225
|
-
output_options[:
|
252
|
+
output_options[:system][i][1].sort!
|
253
|
+
output_options[:satellites] += (labels ? prns.zip(labels) : prns)
|
254
|
+
output_options[:satellites].sort!{|a, b| [a].flatten[0] <=> [b].flatten[0]}
|
226
255
|
end
|
256
|
+
}
|
257
|
+
check_sys_svid = proc{|sys_target, range_in_sys, offset|
|
258
|
+
next range_in_sys.include?(svid - (offset || 0)) unless sys # svid is specified without system
|
259
|
+
next false unless sys == sys_target
|
260
|
+
next true unless svid # All satellites in a target system (svid == nil)
|
261
|
+
range_in_sys.include?(svid)
|
262
|
+
}
|
263
|
+
if check_sys_svid.call(:GPS, 1..32) then
|
264
|
+
[svid || (1..32).to_a].flatten.each{|prn| @solver.gps_options.send(mode, prn)}
|
265
|
+
elsif check_sys_svid.call(:SBAS, 120..158) then
|
266
|
+
prns = [svid || (120..158).to_a].flatten
|
267
|
+
update_output.call(:SBAS, prns)
|
227
268
|
prns.each{|prn| @solver.sbas_options.send(mode, prn)}
|
269
|
+
elsif check_sys_svid.call(:QZSS, 193..202) then
|
270
|
+
[svid || (193..202).to_a].flatten.each{|prn| @solver.gps_options.send(mode, prn)}
|
228
271
|
else
|
229
|
-
|
272
|
+
raise "Unknown satellite: #{spec}"
|
230
273
|
end
|
231
274
|
$stderr.puts "#{mode.capitalize} satellite: #{[sys, svid].compact.join(':')}"
|
232
275
|
}
|
@@ -235,10 +278,6 @@ class Receiver
|
|
235
278
|
false
|
236
279
|
}
|
237
280
|
raise "Unknown receiver options: #{options.inspect}" unless options.empty?
|
238
|
-
proc{|opt|
|
239
|
-
opt.elevation_mask = 0.0 / 180 * Math::PI # 0 deg
|
240
|
-
opt.residual_mask = 1E4 # 10 km
|
241
|
-
}.call(@solver.gps_options)
|
242
281
|
@output = {
|
243
282
|
:pvt => Receiver::pvt_items(output_options),
|
244
283
|
:meas => Receiver::meas_items(output_options),
|
@@ -316,6 +355,11 @@ class Receiver
|
|
316
355
|
[:azimuth, :elevation, :slopeH, :slopeV].each{|k|
|
317
356
|
eval("define_method(:#{k}){@#{k} || self.post_solution(:@#{k})}")
|
318
357
|
}
|
358
|
+
define_method(:other_state){
|
359
|
+
# If a design matrix G has columns larger than 4,
|
360
|
+
# other states excluding position and time are estimated.
|
361
|
+
(self.G.rows <= 4) ? [] : (self.S * self.delta_r).transpose.to_a[0][4..-1]
|
362
|
+
}
|
319
363
|
}
|
320
364
|
|
321
365
|
proc{
|
@@ -365,7 +409,7 @@ class Receiver
|
|
365
409
|
ubx = UBX::new(open(ubx_fname))
|
366
410
|
ubx_kind = Hash::new(0)
|
367
411
|
|
368
|
-
after_run = b || proc{|pvt| puts pvt.to_s}
|
412
|
+
after_run = b || proc{|pvt| puts pvt.to_s if pvt}
|
369
413
|
|
370
414
|
gnss_serial = proc{|svid, sys|
|
371
415
|
if sys then # new numbering
|
@@ -489,7 +533,7 @@ class Receiver
|
|
489
533
|
end
|
490
534
|
|
491
535
|
def parse_rinex_obs(fname, &b)
|
492
|
-
after_run = b || proc{|pvt| puts pvt.to_s}
|
536
|
+
after_run = b || proc{|pvt| puts pvt.to_s if pvt}
|
493
537
|
$stderr.print "Reading RINEX observation file (%s)"%[fname]
|
494
538
|
types = nil
|
495
539
|
count = 0
|
data/lib/gps_pvt/version.rb
CHANGED