gps_pvt 0.1.7 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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