gps_pvt 0.2.1 → 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.
@@ -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