gps_pvt 0.1.7 → 0.2.3

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.
@@ -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
 
@@ -949,6 +966,7 @@ MAKE_TO_S(Matrix_Frozen)
949
966
  }
950
967
  #if defined(SWIGRUBY)
951
968
  %fragment(SWIG_AsVal_frag(unsigned int));
969
+ %fragment("check_value"{unsigned int});
952
970
  Matrix(const void *replacer){
953
971
  const SWIG_Object *value(static_cast<const SWIG_Object *>(replacer));
954
972
  static const ID id_r(rb_intern("row_size")), id_c(rb_intern("column_size"));
@@ -959,13 +977,10 @@ MAKE_TO_S(Matrix_Frozen)
959
977
  MatrixUtil::replace(res, replacer);
960
978
  return new Matrix<T, Array2D_Type, ViewType>(res);
961
979
  }else if(value && rb_respond_to(*value, id_r) && rb_respond_to(*value, id_c)){
962
- /* "unsigned" is remove because SWIG_AsVal(unsigned int)
963
- * can not detect less than zero in Windows Ruby devkit.
964
- */
965
- int r, c;
980
+ unsigned int r, c;
966
981
  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 < 0)
968
- || !SWIG_IsOK(SWIG_AsVal(int)(v_c, &c)) || (c < 0)){
982
+ if(!SWIG_IsOK(SWIG_AsVal(unsigned int)(v_r, &r)) || is_lt_zero_after_asval(r)
983
+ || !SWIG_IsOK(SWIG_AsVal(unsigned int)(v_c, &c)) || is_lt_zero_after_asval(c)){
969
984
  throw std::runtime_error(
970
985
  std::string("Unexpected length [")
971
986
  .append(inspect_str(v_r)).append(", ")
@@ -1147,6 +1162,8 @@ INSTANTIATE_MATRIX_EIGEN2(type, ctype, Array2D_Dense<type >, MatView_pt);
1147
1162
  #endif
1148
1163
 
1149
1164
  %define INSTANTIATE_MATRIX(type, suffix)
1165
+ %typemap(check, fragment="check_value"{unsigned int})
1166
+ const unsigned int & "raise_if_lt_zero_after_asval(*$1);"
1150
1167
  #if !defined(DO_NOT_INSTANTIATE_SCALAR_MATRIX)
1151
1168
  %extend Matrix_Frozen<type, Array2D_ScaledUnit<type >, MatViewBase> {
1152
1169
  const Matrix_Frozen<type, Array2D_ScaledUnit<type >, MatViewBase> &transpose() const {
@@ -1200,6 +1217,35 @@ INSTANTIATE_MATRIX_PARTIAL(type, Array2D_Dense<type >, MatView_pt, MatView_pt);
1200
1217
  %template(Matrix_Frozen ## suffix ## _pt) Matrix_Frozen<type, Array2D_Dense<type >, MatView_pt>;
1201
1218
  #endif
1202
1219
 
1220
+ %extend Matrix<type, Array2D_Dense<type > > {
1221
+ #if defined(SWIGRUBY)
1222
+ %bang resize;
1223
+ #endif
1224
+ %typemap(in, fragment="check_value"{unsigned int})
1225
+ unsigned int *r_p (unsigned int temp), unsigned int *c_p (unsigned int temp) {
1226
+ if(SWIG_IsOK(SWIG_AsVal(unsigned int)($input, &temp))){
1227
+ #if defined(SWIGRUBY)
1228
+ raise_if_lt_zero_after_asval(temp);
1229
+ #endif
1230
+ $1 = &temp;
1231
+ }
1232
+ #if defined(SWIGRUBY)
1233
+ else if(NIL_P($input)){$1 = NULL;}
1234
+ #endif
1235
+ else{SWIG_exception(SWIG_TypeError, "$*1_ltype is expected");}
1236
+ }
1237
+ Matrix<type, Array2D_Dense<type > > &resize(
1238
+ const unsigned int *r_p, const unsigned int *c_p){
1239
+ unsigned int r(r_p ? *r_p : $self->rows()), c(c_p ? *c_p : self->columns());
1240
+ Matrix<type, Array2D_Dense<type > > mat_new(r, c);
1241
+ unsigned int r_min(r), c_min(c);
1242
+ if(r_min > $self->rows()){r_min = $self->rows();}
1243
+ if(c_min > $self->columns()){c_min = $self->columns();}
1244
+ mat_new.partial(r_min, c_min).replace($self->partial(r_min, c_min), false);
1245
+ return (*($self) = mat_new);
1246
+ }
1247
+ };
1248
+
1203
1249
  %template(Matrix ## suffix) Matrix<type, Array2D_Dense<type > >;
1204
1250
  #if defined(SWIGRUBY)
1205
1251
  %fragment("init"{Matrix<type, Array2D_Dense<type > >}, "init") {
@@ -1212,6 +1258,7 @@ INSTANTIATE_MATRIX_PARTIAL(type, Array2D_Dense<type >, MatView_pt, MatView_pt);
1212
1258
  }
1213
1259
  %fragment("init"{Matrix<type, Array2D_Dense<type > >});
1214
1260
  #endif
1261
+ %typemap(check) const unsigned int &;
1215
1262
  %enddef
1216
1263
 
1217
1264
  INSTANTIATE_MATRIX(double, D);
@@ -221,7 +221,11 @@ __RINEX_OBS_TEXT__
221
221
  f.path
222
222
  },
223
223
  }}
224
- let(:solver){GPS::Solver::new}
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
@@ -255,7 +259,6 @@ __RINEX_OBS_TEXT__
255
259
  [:alpha, :beta].each{|k|
256
260
  puts "Iono #{k}: #{sn.iono_utc.send(k)}"
257
261
  }
258
- puts solver.gps_options.ionospheric_models
259
262
 
260
263
  meas.each{|prn, k, v|
261
264
  eph = sn.ephemeris(prn)
@@ -336,11 +339,21 @@ __RINEX_OBS_TEXT__
336
339
 
337
340
  it 'can be modified through hooks' do
338
341
  sn = solver.gps_space_node
342
+ expect(solver.correction[:gps_ionospheric]).to include(:klobuchar)
343
+ expect(solver.correction[:gps_tropospheric]).to include(:hopfield)
344
+ expect{solver.correction = nil}.to raise_error(RuntimeError)
345
+ expect{solver.correction = {
346
+ :gps_ionospheric => [:klobuchar, :no_correction],
347
+ :options => {:f_10_7 => 10},
348
+ }}.not_to raise_error
349
+ expect(solver.correction[:gps_ionospheric]).to include(:no_correction)
350
+ expect(solver.correction[:options][:f_10_7]).to eq(10)
339
351
  sn.read(input[:rinex_nav])
340
352
  t_meas = GPS::Time::new(1849, 172413)
341
353
  sn.update_all_ephemeris(t_meas)
342
- solver.hooks[:relative_property] = proc{|prn, rel_prop, rcv_e, t_arv, usr_pos, usr_vel|
354
+ solver.hooks[:relative_property] = proc{|prn, rel_prop, meas, rcv_e, t_arv, usr_pos, usr_vel|
343
355
  expect(input[:measurement]).to include(prn)
356
+ expect(meas).to be_a_kind_of(Hash)
344
357
  expect(t_arv).to be_a_kind_of(GPS::Time)
345
358
  expect(usr_pos).to be_a_kind_of(Coordinate::XYZ)
346
359
  expect(usr_vel).to be_a_kind_of(Coordinate::XYZ)
@@ -348,12 +361,30 @@ __RINEX_OBS_TEXT__
348
361
  weight = 1
349
362
  [weight, range_c, range_r, rate_rel_neg] + los_neg
350
363
  }
351
- solver.hooks[:update_position_solution] = proc{|*mats|
352
- mats.each{|mat|
364
+ solver.hooks[:update_position_solution] = proc{|mat_G, mat_W, mat_delta_r, temp_pvt|
365
+ expect(temp_pvt).to be_a_kind_of(GPS::PVT)
366
+ [mat_G, mat_W, mat_delta_r].each{|mat|
353
367
  expect(mat).to be_a_kind_of(SylphideMath::MatrixD)
368
+ expect(mat.rows).to be >= temp_pvt.used_satellites
369
+ expect(mat).to respond_to(:resize!)
354
370
  }
355
- mat_G, mat_W, mat_delta_r = mats
356
371
  }
372
+ solver.hooks[:satellite_position] = proc{
373
+ i = 0
374
+ proc{|prn, time, pos|
375
+ expect(input[:measurement]).to include(prn)
376
+ expect(pos).to be_a_kind_of(Coordinate::XYZ).or eq(nil)
377
+ # System_XYZ or [x,y,z] or nil(= unknown position) are acceptable
378
+ case (i += 1) % 5
379
+ when 0
380
+ nil
381
+ when 1
382
+ pos.to_a
383
+ else
384
+ pos
385
+ end
386
+ }
387
+ }.call
357
388
  pvt = solver.solve(
358
389
  input[:measurement].collect{|prn, items|
359
390
  items.collect{|k, v| [prn, k, v]}
@@ -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
- a = a_gen.call
48
- a.define_singleton_method(:row_size){-1}
49
- expect{ mat_type::new(a) }.to raise_error(RuntimeError)
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 'determinat, det' do
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
@@ -394,6 +411,23 @@ shared_examples 'Matrix' do
394
411
  }
395
412
  expect{mat[2] / mat[3]}.to raise_error(RuntimeError)
396
413
  end
414
+ it 'have resize!' do
415
+ [-1, :sym].each{|arg|
416
+ [[arg, nil], [nil, arg]].each{|args|
417
+ expect{mat[0].resize!(*args)}.to raise_error{|err|
418
+ expect(err).to be_a(TypeError).or be_a(ArgumentError)
419
+ }
420
+ }
421
+ }
422
+ mat_orig = mat[0].to_a
423
+ r, c = [:rows, :columns].collect{|f| mat[0].send(f)}
424
+ expect(mat[0].resize!(r, c).to_a).to eq(mat_orig)
425
+ expect(mat[0].resize!(r, nil).to_a).to eq(mat_orig)
426
+ expect(mat[0].resize!(nil, c).to_a).to eq(mat_orig)
427
+ expect(mat[0].resize!(nil, nil).to_a).to eq(mat_orig)
428
+ expect(mat[0].resize!(r * 2, c * 2).to_a).to \
429
+ eq(Matrix::build(r * 2, c * 2){|i, j| (i < r && j < c) ? mat_orig[i][j] : 0}.to_a)
430
+ end
397
431
  end
398
432
 
399
433
  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