gps_pvt 0.2.1 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -159,6 +159,14 @@ static std::string inspect_str(const VALUE &v){
159
159
  *week = self->week;
160
160
  *seconds = self->seconds;
161
161
  }
162
+ #if defined(SWIG)
163
+ int __cmp__(const GPS_Time<FloatT> &t) const {
164
+ return ((self->week < t.week) ? -1
165
+ : ((self->week > t.week) ? 1
166
+ : (self->seconds < t.seconds ? -1
167
+ : (self->seconds > t.seconds ? 1 : 0))));
168
+ }
169
+ #endif
162
170
  }
163
171
 
164
172
  %define MAKE_ACCESSOR(name, type)
@@ -413,8 +421,6 @@ struct GPS_Ephemeris : public GPS_SpaceNode<FloatT>::SatelliteProperties::Epheme
413
421
  %append_output(swig::from($1.latitude));
414
422
  %append_output(swig::from($1.longitude));
415
423
  }
416
- %ignore iono_correction() const;
417
- %ignore tropo_correction() const;
418
424
  int read(const char *fname) {
419
425
  std::fstream fin(fname, std::ios::in | std::ios::binary);
420
426
  typename RINEX_NAV_Reader<FloatT>::space_node_list_t space_nodes = {self};
@@ -800,6 +806,7 @@ struct GPS_Measurement {
800
806
  L1_RANGE_RATE_SIGMA,
801
807
  L1_SIGNAL_STRENGTH_dBHz,
802
808
  L1_LOCK_SEC,
809
+ L1_FREQUENCY,
803
810
  ITEMS_PREDEFINED,
804
811
  };
805
812
  void add(const int &prn, const int &key, const FloatT &value){
@@ -821,71 +828,15 @@ const type &get_ ## name () const {
821
828
  %enddef
822
829
  MAKE_ACCESSOR2(elevation_mask, FloatT);
823
830
  MAKE_ACCESSOR2(residual_mask, FloatT);
824
- MAKE_ACCESSOR2(f_10_7, FloatT);
825
831
  #undef MAKE_ACCESSOR2
826
832
  MAKE_VECTOR2ARRAY(int);
827
833
  %ignore cast_base;
828
- #ifdef SWIGRUBY
829
- %rename("ionospheric_models=") set_ionospheric_models;
830
- %rename("ionospheric_models") get_ionospheric_models;
831
- #endif
832
- %typemap(in) const std::vector<int> &models (std::vector<int> temp) {
833
- $1 = &temp;
834
- #ifdef SWIGRUBY
835
- if(RB_TYPE_P($input, T_ARRAY)){
836
- for(int i(0), i_max(RARRAY_LEN($input)); i < i_max; ++i){
837
- SWIG_Object obj(RARRAY_AREF($input, i));
838
- int v;
839
- if(SWIG_IsOK(SWIG_AsVal(int)(obj, &v))){
840
- temp.push_back(v);
841
- }else{
842
- SWIG_exception(SWIG_TypeError, "$*1_ltype is expected");
843
- }
844
- }
845
- }
846
- #endif
847
- }
848
834
  }
849
835
  %inline %{
850
836
  template <class FloatT>
851
837
  struct GPS_SolverOptions_Common {
852
- enum {
853
- IONOSPHERIC_KLOBUCHAR,
854
- IONOSPHERIC_SBAS,
855
- IONOSPHERIC_NTCM_GL,
856
- IONOSPHERIC_NONE, // which allows no correction
857
- IONOSPHERIC_MODELS,
858
- IONOSPHERIC_SKIP = IONOSPHERIC_MODELS, // which means delegating the next slot
859
- };
860
838
  virtual GPS_Solver_GeneralOptions<FloatT> *cast_general() = 0;
861
839
  virtual const GPS_Solver_GeneralOptions<FloatT> *cast_general() const = 0;
862
- std::vector<int> get_ionospheric_models() const {
863
- typedef GPS_Solver_GeneralOptions<FloatT> general_t;
864
- const general_t *general(this->cast_general());
865
- std::vector<int> res;
866
- for(int i(0); i < general_t::IONOSPHERIC_MODELS; ++i){
867
- int v((int)(general->ionospheric_models[i]));
868
- if(v == general_t::IONOSPHERIC_SKIP){break;}
869
- res.push_back(v);
870
- }
871
- return res;
872
- }
873
- std::vector<int> set_ionospheric_models(const std::vector<int> &models){
874
- typedef GPS_Solver_GeneralOptions<FloatT> general_t;
875
- general_t *general(this->cast_general());
876
- typedef typename general_t::ionospheric_model_t model_t;
877
- for(int i(0), j(0), j_max(models.size()); i < general_t::IONOSPHERIC_MODELS; ++i){
878
- model_t v(general_t::IONOSPHERIC_SKIP);
879
- if(j < j_max){
880
- if((models[j] >= 0) && (models[j] < general_t::IONOSPHERIC_SKIP)){
881
- v = (model_t)models[j];
882
- }
883
- ++j;
884
- }
885
- general->ionospheric_models[i] = v;
886
- }
887
- return get_ionospheric_models();
888
- }
889
840
  };
890
841
  %}
891
842
 
@@ -1022,7 +973,9 @@ struct SBAS_SolverOptions
1022
973
  SWIG_NewPointerObj(&geomat_.W,
1023
974
  $descriptor(Matrix<FloatT, Array2D_Dense<FloatT>, MatrixViewBase<> > *), 0),
1024
975
  SWIG_NewPointerObj(&geomat_.delta_r,
1025
- $descriptor(Matrix<FloatT, Array2D_Dense<FloatT>, MatrixViewBase<> > *), 0)};
976
+ $descriptor(Matrix<FloatT, Array2D_Dense<FloatT>, MatrixViewBase<> > *), 0),
977
+ SWIG_NewPointerObj(&res,
978
+ $descriptor(GPS_User_PVT<FloatT> *), 0)};
1026
979
  proc_call_throw_if_error(hook, sizeof(values) / sizeof(values[0]), values);
1027
980
  }while(false);
1028
981
  #endif
@@ -1075,6 +1028,116 @@ struct SBAS_SolverOptions
1075
1028
  }
1076
1029
  }
1077
1030
  %fragment("hook"{GPS_Solver<FloatT>});
1031
+ %ignore update_correction;
1032
+ #ifdef SWIGRUBY
1033
+ %fragment("correction"{GPS_Solver<FloatT>}, "header",
1034
+ fragment=SWIG_From_frag(int),
1035
+ fragment=SWIG_Traits_frag(FloatT)){
1036
+ template<>
1037
+ VALUE GPS_Solver<FloatT>::update_correction(
1038
+ const bool &update, const VALUE &hash){
1039
+ typedef range_correction_list_t list_t;
1040
+ static const VALUE k_root[] = {
1041
+ ID2SYM(rb_intern("gps_ionospheric")),
1042
+ ID2SYM(rb_intern("gps_tropospheric")),
1043
+ ID2SYM(rb_intern("sbas_ionospheric")),
1044
+ ID2SYM(rb_intern("sbas_tropospheric")),
1045
+ };
1046
+ static const VALUE k_opt(ID2SYM(rb_intern("options")));
1047
+ static const VALUE k_f_10_7(ID2SYM(rb_intern("f_10_7")));
1048
+ static const VALUE k_known(ID2SYM(rb_intern("known")));
1049
+ struct {
1050
+ VALUE sym;
1051
+ list_t::mapped_type::value_type obj;
1052
+ } item[] = {
1053
+ {ID2SYM(rb_intern("no_correction")), &base_t::no_correction},
1054
+ {ID2SYM(rb_intern("klobuchar")), &this->gps.solver.ionospheric_klobuchar},
1055
+ {ID2SYM(rb_intern("ntcm_gl")), &this->gps.solver.ionospheric_ntcm_gl},
1056
+ {ID2SYM(rb_intern("hopfield")), &this->gps.solver.tropospheric_simplified},
1057
+ {ID2SYM(rb_intern("sbas_igp")), &this->sbas.solver.ionospheric_sbas},
1058
+ {ID2SYM(rb_intern("sbas_tropo")), &this->sbas.solver.tropospheric_sbas},
1059
+ };
1060
+ list_t input;
1061
+ if(update){
1062
+ if(!RB_TYPE_P(hash, T_HASH)){
1063
+ throw std::runtime_error(
1064
+ std::string("Hash is expected, however ").append(inspect_str(hash)));
1065
+ }
1066
+ for(std::size_t i(0); i < sizeof(k_root) / sizeof(k_root[0]); ++i){
1067
+ VALUE ary = rb_hash_lookup(hash, k_root[i]);
1068
+ if(NIL_P(ary)){continue;}
1069
+ if(!RB_TYPE_P(ary, T_ARRAY)){
1070
+ ary = rb_ary_new_from_values(1, &ary);
1071
+ }
1072
+ for(int j(0); j < RARRAY_LEN(ary); ++j){
1073
+ std::size_t k(0);
1074
+ VALUE v(rb_ary_entry(ary, j));
1075
+ for(; k < sizeof(item) / sizeof(item[0]); ++k){
1076
+ if(v == item[k].sym){break;}
1077
+ }
1078
+ if(k >= sizeof(item) / sizeof(item[0])){
1079
+ continue; // TODO other than symbol
1080
+ }
1081
+ input[i].push_back(item[k].obj);
1082
+ }
1083
+ }
1084
+ VALUE opt(rb_hash_lookup(hash, k_opt));
1085
+ if(RB_TYPE_P(opt, T_HASH)){
1086
+ swig::asval(rb_hash_lookup(opt, k_f_10_7), // ntcm_gl
1087
+ &this->gps.solver.ionospheric_ntcm_gl.f_10_7);
1088
+ }
1089
+ }
1090
+ list_t output(update_correction(update, input));
1091
+ VALUE res = rb_hash_new();
1092
+ for(list_t::const_iterator it(output.begin()), it_end(output.end());
1093
+ it != it_end; ++it){
1094
+ VALUE k;
1095
+ if((it->first < 0) || (it->first >= (int)(sizeof(k_root) / sizeof(k_root[0])))){
1096
+ k = SWIG_From(int)(it->first);
1097
+ }else{
1098
+ k = k_root[it->first];
1099
+ }
1100
+ VALUE v = rb_ary_new();
1101
+ for(list_t::mapped_type::const_iterator
1102
+ it2(it->second.begin()), it2_end(it->second.end());
1103
+ it2 != it2_end; ++it2){
1104
+ std::size_t i(0);
1105
+ for(; i < sizeof(item) / sizeof(item[0]); ++i){
1106
+ if(*it2 == item[i].obj){break;}
1107
+ }
1108
+ if(i >= sizeof(item) / sizeof(item[0])){
1109
+ continue; // TODO other than built-in corrector
1110
+ }
1111
+ rb_ary_push(v, item[i].sym);
1112
+ }
1113
+ rb_hash_aset(res, k, v);
1114
+ }
1115
+ { // common options
1116
+ VALUE opt = rb_hash_new();
1117
+ rb_hash_aset(res, k_opt, opt);
1118
+ rb_hash_aset(opt, k_f_10_7, // ntcm_gl
1119
+ swig::from(this->gps.solver.ionospheric_ntcm_gl.f_10_7));
1120
+ }
1121
+ { // known models
1122
+ VALUE ary = rb_ary_new_capa((int)(sizeof(item) / sizeof(item[0])));
1123
+ for(std::size_t i(0); i < sizeof(item) / sizeof(item[0]); ++i){
1124
+ rb_ary_push(ary, item[i].sym);
1125
+ }
1126
+ rb_hash_aset(res, k_known, ary);
1127
+ }
1128
+ return res;
1129
+ }
1130
+ }
1131
+ %fragment("correction"{GPS_Solver<FloatT>});
1132
+ %rename("correction") get_correction;
1133
+ %rename("correction=") set_correction;
1134
+ VALUE get_correction() const {
1135
+ return const_cast<GPS_Solver<FloatT> *>(self)->update_correction(false, Qnil);
1136
+ }
1137
+ VALUE set_correction(VALUE hash){
1138
+ return self->update_correction(true, hash);
1139
+ }
1140
+ #endif
1078
1141
  }
1079
1142
  %inline {
1080
1143
  template <class FloatT>
@@ -1105,11 +1168,20 @@ struct GPS_Solver
1105
1168
  }
1106
1169
  #endif
1107
1170
  GPS_Solver() : super_t(), gps(), sbas(), hooks() {
1108
- gps.solver.space_node_sbas = &sbas.space_node;
1109
- sbas.solver.space_node_gps = &gps.space_node;
1110
1171
  #ifdef SWIGRUBY
1111
1172
  hooks = rb_hash_new();
1112
1173
  #endif
1174
+ typename base_t::range_correction_t ionospheric, tropospheric;
1175
+ ionospheric.push_back(&sbas.solver.ionospheric_sbas);
1176
+ ionospheric.push_back(&gps.solver.ionospheric_klobuchar);
1177
+ tropospheric.push_back(&sbas.solver.tropospheric_sbas);
1178
+ tropospheric.push_back(&gps.solver.tropospheric_simplified);
1179
+ gps.solver.ionospheric_correction
1180
+ = sbas.solver.ionospheric_correction
1181
+ = ionospheric;
1182
+ gps.solver.tropospheric_correction
1183
+ = sbas.solver.tropospheric_correction
1184
+ = tropospheric;
1113
1185
  }
1114
1186
  GPS_SpaceNode<FloatT> &gps_space_node() {return gps.space_node;}
1115
1187
  GPS_SolverOptions<FloatT> &gps_options() {return gps.options;}
@@ -1149,6 +1221,40 @@ struct GPS_Solver
1149
1221
  const_cast<sbas_t &>(sbas).solver.update_options(sbas.options);
1150
1222
  return super_t::solve().user_pvt(measurement.items, receiver_time);
1151
1223
  }
1224
+ typedef
1225
+ std::map<int, std::vector<const typename base_t::range_corrector_t *> >
1226
+ range_correction_list_t;
1227
+ range_correction_list_t update_correction(
1228
+ const bool &update,
1229
+ const range_correction_list_t &list = range_correction_list_t()){
1230
+ range_correction_list_t res;
1231
+ typename base_t::range_correction_t *root[] = {
1232
+ &gps.solver.ionospheric_correction,
1233
+ &gps.solver.tropospheric_correction,
1234
+ &sbas.solver.ionospheric_correction,
1235
+ &sbas.solver.tropospheric_correction,
1236
+ };
1237
+ for(std::size_t i(0); i < sizeof(root) / sizeof(root[0]); ++i){
1238
+ do{
1239
+ if(!update){break;}
1240
+ typename range_correction_list_t::const_iterator it(list.find(i));
1241
+ if(it == list.end()){break;}
1242
+ root[i]->clear();
1243
+ for(typename range_correction_list_t::mapped_type::const_iterator
1244
+ it2(it->second.begin()), it2_end(it->second.end());
1245
+ it2 != it2_end; ++it2){
1246
+ root[i]->push_back(*it2);
1247
+ }
1248
+ }while(false);
1249
+ for(typename base_t::range_correction_t::const_iterator
1250
+ it(root[i]->begin()), it_end(root[i]->end());
1251
+ it != it_end; ++it){
1252
+ res[i].push_back(*it);
1253
+ }
1254
+ }
1255
+ return res;
1256
+ }
1257
+ SWIG_Object update_correction(const bool &update, const SWIG_Object &hash);
1152
1258
  };
1153
1259
  }
1154
1260
 
@@ -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,6 +339,15 @@ __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)
@@ -349,11 +361,13 @@ __RINEX_OBS_TEXT__
349
361
  weight = 1
350
362
  [weight, range_c, range_r, rate_rel_neg] + los_neg
351
363
  }
352
- solver.hooks[:update_position_solution] = proc{|*mats|
353
- 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|
354
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!)
355
370
  }
356
- mat_G, mat_W, mat_delta_r = mats
357
371
  }
358
372
  solver.hooks[:satellite_position] = proc{
359
373
  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
- 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