gps_pvt 0.5.1 → 0.6.2

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.
@@ -0,0 +1,1189 @@
1
+ /*
2
+ * Copyright (c) 2022, M.Naruoka (fenrir)
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without modification,
6
+ * are permitted provided that the following conditions are met:
7
+ *
8
+ * - Redistributions of source code must retain the above copyright notice,
9
+ * this list of conditions and the following disclaimer.
10
+ * - Redistributions in binary form must reproduce the above copyright notice,
11
+ * this list of conditions and the following disclaimer in the documentation
12
+ * and/or other materials provided with the distribution.
13
+ * - Neither the name of the naruoka.org nor the names of its contributors
14
+ * may be used to endorse or promote products derived from this software
15
+ * without specific prior written permission.
16
+ *
17
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
21
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ */
31
+
32
+ /** @file
33
+ * @brief SP3 Reader/Writer, support ver.D
34
+ *
35
+ */
36
+
37
+ #ifndef __SP3_H__
38
+ #define __SP3_H__
39
+
40
+ #include <ctime>
41
+ #include <cstring>
42
+ #include <map>
43
+ #include <set>
44
+ #include <algorithm>
45
+ #include <stdexcept>
46
+
47
+ #include "util/text_helper.h"
48
+ #include "GPS.h"
49
+ #include "GPS_Solver_Base.h"
50
+
51
+ #include "param/vector3.h"
52
+ #include "algorithm/interpolate.h"
53
+
54
+ template <class FloatT>
55
+ struct SP3_Product {
56
+ struct prop_t {
57
+ Vector3<FloatT> xyz;
58
+ FloatT clk;
59
+ };
60
+ struct per_satellite_t {
61
+ typedef std::map<GPS_Time<FloatT>, prop_t> history_t;
62
+ history_t pos_history;
63
+ history_t vel_history;
64
+
65
+ static const struct interpolate_cnd_t {
66
+ std::size_t max_epochs; ///< maximum number of epochs used for interpolation
67
+ FloatT max_delta_t; ///< maximum acceptable absolute time difference between t and epoch
68
+ } interpolate_cnd_default;
69
+
70
+ mutable struct {
71
+ struct target_t {
72
+
73
+ typedef typename std::vector<std::pair<GPS_Time<FloatT>, prop_t> > buf_t;
74
+ buf_t buf;
75
+ GPS_Time<FloatT> t0;
76
+ std::vector<FloatT> dt;
77
+ bool updated_full_cnd;
78
+
79
+ target_t() : t0(0, 0), updated_full_cnd(false) {}
80
+
81
+ /**
82
+ * update interpolation source
83
+ * @param force_update If true, update is forcibly performed irrespective of current state
84
+ * @param cnd condition for source data selection
85
+ */
86
+ target_t &update(
87
+ const GPS_Time<FloatT> &t, const history_t &history,
88
+ const bool &force_update = false,
89
+ const interpolate_cnd_t &cnd = interpolate_cnd_default){
90
+
91
+ FloatT t_diff(t0 - t);
92
+ if((!force_update)
93
+ && ((std::abs(t_diff) <= 10)
94
+ || ((dt.size() >= 2)
95
+ && (std::abs(t_diff + dt[0]) <= std::abs(t_diff + dt[1])))) ){
96
+ return *this;
97
+ }
98
+
99
+ // If the 1st and 2nd nearest epochs are changed, then recalculate interpolation targets.
100
+ struct {
101
+ const GPS_Time<FloatT> &t_base;
102
+ bool operator()(
103
+ const typename history_t::value_type &rhs,
104
+ const typename history_t::value_type &lhs) const {
105
+ return std::abs(rhs.first - t_base) < std::abs(lhs.first - t_base);
106
+ }
107
+ } cmp = {(t0 = t)};
108
+
109
+ buf.resize(cnd.max_epochs);
110
+ dt.clear();
111
+ // extract t where t0-dt <= t <= t0+dt, then sort by ascending order of |t-t0|
112
+ for(typename buf_t::const_iterator
113
+ it(buf.begin()),
114
+ it_end(std::partial_sort_copy(
115
+ history.lower_bound(t - cnd.max_delta_t),
116
+ history.upper_bound(t + cnd.max_delta_t),
117
+ buf.begin(), buf.end(), cmp));
118
+ it != it_end; ++it){
119
+ dt.push_back(it->first - t0);
120
+ }
121
+ updated_full_cnd = (dt.size() >= cnd.max_epochs);
122
+
123
+ return *this;
124
+ }
125
+
126
+ template <class Ty, class Ty_Array>
127
+ Ty interpolate(
128
+ const GPS_Time<FloatT> &t, const Ty_Array &y,
129
+ Ty *derivative = NULL) const {
130
+ int order(dt.size() - 1);
131
+ do{
132
+ if(order > 0){break;}
133
+ if((order == 0) && (!derivative)){return y[0];}
134
+ throw std::range_error("Insufficient records for interpolation");
135
+ }while(false);
136
+ std::vector<Ty> y_buf(order), dy_buf(order);
137
+ interpolate_Neville(
138
+ dt, y, t - t0, y_buf, order,
139
+ &dy_buf, derivative ? 1 : 0);
140
+ if(derivative){*derivative = dy_buf[0];}
141
+ return y_buf[0];
142
+ }
143
+ Vector3<FloatT> interpolate_xyz(
144
+ const GPS_Time<FloatT> &t,
145
+ Vector3<FloatT> *derivative = NULL) const {
146
+ struct second_iterator : public buf_t::const_iterator {
147
+ second_iterator(const typename buf_t::const_iterator &it)
148
+ : buf_t::const_iterator(it) {}
149
+ const Vector3<FloatT> &operator[](const int &n) const {
150
+ return buf_t::const_iterator::operator[](n).second.xyz;
151
+ }
152
+ } xyz(buf.begin());
153
+ return interpolate(t, xyz, derivative);
154
+ }
155
+ FloatT interpolate_clk(
156
+ const GPS_Time<FloatT> &t,
157
+ FloatT *derivative = NULL) const {
158
+ struct second_iterator : public buf_t::const_iterator {
159
+ second_iterator(const typename buf_t::const_iterator &it)
160
+ : buf_t::const_iterator(it) {}
161
+ const FloatT &operator[](const int &n) const {
162
+ return buf_t::const_iterator::operator[](n).second.clk;
163
+ }
164
+ } clk(buf.begin());
165
+ return interpolate(t, clk, derivative);
166
+ }
167
+ } pos_clk, vel_rate;
168
+ } subset;
169
+
170
+ /**
171
+ * Precheck whether interpolate can be fully performed or not
172
+ *
173
+ * @return (bool) If interpolation condition is fully satisfied, then true is returned.
174
+ */
175
+ bool precheck(const GPS_Time<FloatT> &t) const {
176
+ // Only position/clock is checked, because velocity/rate can be calculated based on pos/clk.
177
+ subset.pos_clk.update(t, pos_history);
178
+ return subset.pos_clk.updated_full_cnd;
179
+ }
180
+
181
+ typename GPS_SpaceNode<FloatT>::SatelliteProperties::constellation_t
182
+ constellation(
183
+ const GPS_Time<FloatT> &t,
184
+ bool with_velocity = true) const {
185
+ typename GPS_SpaceNode<FloatT>::SatelliteProperties::constellation_t res;
186
+ if(with_velocity){
187
+ try{
188
+ res.velocity = subset.vel_rate.update(t, vel_history).interpolate_xyz(t);
189
+ with_velocity = false;
190
+ }catch(std::range_error &){}
191
+ }
192
+ Vector3<FloatT> vel;
193
+ res.position = subset.pos_clk.update(t, pos_history)
194
+ .interpolate_xyz(t, with_velocity ? &vel : NULL); // velocity fallback to use position
195
+ if(with_velocity){res.velocity = vel;}
196
+ return res;
197
+ }
198
+ typename GPS_SpaceNode<FloatT>::xyz_t position(
199
+ const GPS_Time<FloatT> &t) const {
200
+ return constellation(t, false).position;
201
+ }
202
+ typename GPS_SpaceNode<FloatT>::xyz_t velocity(
203
+ const GPS_Time<FloatT> &t) const {
204
+ return constellation(t, true).velocity;
205
+ }
206
+ FloatT clock_error(const GPS_Time<FloatT> &t) const {
207
+ return subset.pos_clk.update(t, pos_history).interpolate_clk(t);
208
+ }
209
+ FloatT clock_error_dot(const GPS_Time<FloatT> &t) const {
210
+ try{
211
+ return subset.vel_rate.update(t, vel_history).interpolate_clk(t);
212
+ }catch(std::range_error &){
213
+ FloatT res;
214
+ subset.pos_clk.update(t, pos_history).interpolate_clk(t, &res);
215
+ return res;
216
+ }
217
+ }
218
+
219
+ operator typename GPS_Solver_Base<FloatT>::satellite_t() const {
220
+ typedef typename GPS_Solver_Base<FloatT>::gps_time_t gt_t;
221
+ typedef typename GPS_Solver_Base<FloatT>::float_t float_t;
222
+ typedef typename GPS_Solver_Base<FloatT>::xyz_t xyz_t;
223
+ struct impl_t {
224
+ static inline const per_satellite_t &sat(const void *ptr) {
225
+ return *reinterpret_cast<const per_satellite_t *>(ptr);
226
+ }
227
+ static xyz_t position(const void *ptr, const gt_t &t_tx, const float_t &dt_transit) {
228
+ return sat(ptr).position(t_tx).after(dt_transit);
229
+ }
230
+ static xyz_t velocity(const void *ptr, const gt_t &t_tx, const float_t &dt_transit) {
231
+ return sat(ptr).velocity(t_tx).after(dt_transit);
232
+ }
233
+ static float_t clock_error(const void *ptr, const gt_t &t_tx) {
234
+ return sat(ptr).clock_error(t_tx);
235
+ }
236
+ static float_t clock_error_dot(const void *ptr, const gt_t &t_tx) {
237
+ return sat(ptr).clock_error_dot(t_tx);
238
+ }
239
+ };
240
+ typename GPS_Solver_Base<FloatT>::satellite_t res = {
241
+ this,
242
+ impl_t::position, impl_t::velocity,
243
+ impl_t::clock_error, impl_t::clock_error_dot
244
+ };
245
+ return res;
246
+ }
247
+ };
248
+ typedef std::map<int, per_satellite_t> satellites_t;
249
+ satellites_t satellites;
250
+
251
+ typedef std::set<GPS_Time<FloatT> > epochs_t;
252
+ epochs_t epochs() const {
253
+ epochs_t res;
254
+ struct time_iterator : public per_satellite_t::history_t::const_iterator {
255
+ time_iterator(
256
+ const typename per_satellite_t::history_t::const_iterator &it)
257
+ : per_satellite_t::history_t::const_iterator(it) {}
258
+ GPS_Time<FloatT> operator*() const {
259
+ return (*this)->first;
260
+ }
261
+ };
262
+ for(typename satellites_t::const_iterator
263
+ it(satellites.begin()), it_end(satellites.end());
264
+ it != it_end; ++it){
265
+ res.insert(
266
+ time_iterator(it->second.pos_history.begin()),
267
+ time_iterator(it->second.pos_history.end()));
268
+ res.insert(
269
+ time_iterator(it->second.vel_history.begin()),
270
+ time_iterator(it->second.vel_history.end()));
271
+ }
272
+ return res;
273
+ }
274
+
275
+ bool has_position() const {
276
+ for(typename satellites_t::const_iterator
277
+ it(satellites.begin()), it_end(satellites.end());
278
+ it != it_end; ++it){
279
+ if(!it->second.pos_history.empty()){return true;};
280
+ }
281
+ return false;
282
+ }
283
+ bool has_velocity() const {
284
+ for(typename satellites_t::const_iterator
285
+ it(satellites.begin()), it_end(satellites.end());
286
+ it != it_end; ++it){
287
+ if(!it->second.vel_history.empty()){return true;};
288
+ }
289
+ return false;
290
+ }
291
+
292
+ typename GPS_Solver_Base<FloatT>::satellite_t select(
293
+ const int &sat_id, const GPS_Time<FloatT> &t) const {
294
+ do{
295
+ typename satellites_t::const_iterator it(satellites.find(sat_id));
296
+ if(it == satellites.end()){break;}
297
+ if(!it->second.precheck(t)){break;}
298
+ return it->second;
299
+ }while(false);
300
+ return GPS_Solver_Base<FloatT>::satellite_t::unavailable();
301
+ }
302
+
303
+ enum system_t {
304
+ SYSTEM_GPS = (int)'\0' << 8,
305
+ SYSTEM_SBAS = SYSTEM_GPS,
306
+ SYSTEM_QZSS = SYSTEM_GPS,
307
+ SYSTEM_GLONASS = (int)'R' << 8,
308
+ SYSTEM_LEO = (int)'L' << 8,
309
+ SYSTEM_GALILEO = (int)'E' << 8,
310
+ SYSTEM_BEIDOU = (int)'C' << 8,
311
+ SYSTEM_IRNSS = (int)'I' << 8,
312
+ };
313
+
314
+ #define gen_func(sys) \
315
+ static typename GPS_Solver_Base<FloatT>::satellite_t select_ ## sys( \
316
+ const void *ptr, const int &prn, const GPS_Time<FloatT> &receiver_time){ \
317
+ return reinterpret_cast<const SP3_Product<FloatT> *>(ptr) \
318
+ ->select(prn + SYSTEM_ ## sys, receiver_time); \
319
+ }
320
+ gen_func(GPS);
321
+ gen_func(GLONASS);
322
+ gen_func(LEO);
323
+ gen_func(GALILEO);
324
+ gen_func(BEIDOU);
325
+ gen_func(IRNSS);
326
+ #undef gen_fun
327
+
328
+ /**
329
+ * push SP3 product to satellite selector
330
+ *
331
+ * @param slct satellite selector having impl and impl_select members
332
+ * @param sys target system, default is GPS
333
+ * @return (bool) If push is successfully performed, true will be returned.
334
+ */
335
+ template <class SelectorT>
336
+ bool push(SelectorT &slct, const system_t &sys = SYSTEM_GPS) const {
337
+ switch(sys){
338
+ case SYSTEM_GPS: // SBAS and QZSS are identically treated as GPS.
339
+ //case SYSTEM_SBAS:
340
+ //case SYSTEM_QZSS:
341
+ slct.impl_select = select_GPS; break;
342
+ case SYSTEM_GLONASS: slct.impl_select = select_GLONASS; break;
343
+ case SYSTEM_LEO: slct.impl_select = select_LEO; break;
344
+ case SYSTEM_GALILEO: slct.impl_select = select_GALILEO; break;
345
+ case SYSTEM_BEIDOU: slct.impl_select = select_BEIDOU; break;
346
+ case SYSTEM_IRNSS: slct.impl_select = select_IRNSS; break;
347
+ default: return false;
348
+ }
349
+ slct.impl = this;
350
+ return true;
351
+ }
352
+
353
+ struct satellite_count_t {
354
+ int gps, sbas, qzss, glonass, leo, galileo, beidou, irnss, unknown;
355
+ };
356
+ satellite_count_t satellite_count() const {
357
+ satellite_count_t res = {0};
358
+ for(typename satellites_t::const_iterator
359
+ it(satellites.begin()), it_end(satellites.end());
360
+ it != it_end; ++it){
361
+ switch(it->first & 0xFF00){
362
+ case SYSTEM_GPS: {
363
+ int id(it->first & 0xFF);
364
+ if(id < 100){++res.gps;}
365
+ else if(id < 192){++res.sbas;}
366
+ else{++res.qzss;}
367
+ break;
368
+ }
369
+ case SYSTEM_GLONASS: ++res.glonass; break;
370
+ case SYSTEM_LEO: ++res.leo; break;
371
+ case SYSTEM_GALILEO: ++res.galileo; break;
372
+ case SYSTEM_BEIDOU: ++res.beidou; break;
373
+ case SYSTEM_IRNSS: ++res.irnss; break;
374
+ default: ++res.unknown; break;
375
+ }
376
+ }
377
+ return res;
378
+ }
379
+ };
380
+
381
+ template <class FloatT>
382
+ const typename SP3_Product<FloatT>::per_satellite_t::interpolate_cnd_t
383
+ SP3_Product<FloatT>::per_satellite_t::interpolate_cnd_default = {
384
+ 9, // max_epochs
385
+ 60 * 60 * 2, // max_delta_t, default is 2 hr = 15 min interval records; (2 hr * 2 / (9 - 1) = 15 min)
386
+ };
387
+
388
+ template <class FloatT>
389
+ class SP3_Reader {
390
+ protected:
391
+ typename TextHelper<>::crlf_stream_t src;
392
+ public:
393
+ struct l1_t {
394
+ char version_symbol[2];
395
+ char pos_or_vel_flag[1];
396
+ int year_start;
397
+ int month_start;
398
+ int day_of_month_st;
399
+ int hour_start;
400
+ int minute_start;
401
+ FloatT second_start;
402
+ int number_of_epochs;
403
+ char data_used[5];
404
+ char coordinate_sys[5];
405
+ char orbit_type[3];
406
+ char agency[4];
407
+ l1_t &operator=(const GPS_Time<FloatT> &t) {
408
+ std::tm t_(t.c_tm());
409
+ year_start = t_.tm_year + 1900;
410
+ month_start = t_.tm_mon + 1;
411
+ day_of_month_st = t_.tm_mday;
412
+ hour_start = t_.tm_hour;
413
+ minute_start = t_.tm_min;
414
+ second_start = (t.seconds - (int)t.seconds) + t_.tm_sec;
415
+ return *this;
416
+ }
417
+ };
418
+ struct l2_t {
419
+ char symbols[2];
420
+ int gps_week;
421
+ FloatT seconds_of_week;
422
+ FloatT epoch_interval;
423
+ int mod_jul_day_st;
424
+ FloatT fractional_day;
425
+ };
426
+ struct l3_11_t {
427
+ char symbols[2];
428
+ int number_of_sats;
429
+ int sat_id[17];
430
+ };
431
+ struct l12_20_t {
432
+ char symbols[2];
433
+ int sat_accuracy[17];
434
+ };
435
+ struct l21_22_t {
436
+ char symbols[2];
437
+ char file_type[2];
438
+ char _2_characters[2];
439
+ char time_system[3];
440
+ char _3_characters[3];
441
+ char _4_characters[4][4];
442
+ char _5_characters[4][5];
443
+ };
444
+ struct l23_24_t {
445
+ char symbols[2];
446
+ FloatT base_for_pos_vel;
447
+ FloatT base_for_clk_rate;
448
+ FloatT _14_column_float;
449
+ FloatT _18_column_float;
450
+ };
451
+ struct l25_26_t {
452
+ char symbols[2];
453
+ int _4_column_int[4];
454
+ int _6_column_int[4];
455
+ int _9_column_int;
456
+ };
457
+ struct comment_t {
458
+ char symbols[2];
459
+ char comment[77];
460
+ };
461
+ struct epoch_t {
462
+ char symbols[2];
463
+ int year_start;
464
+ int month_start;
465
+ int day_of_month_st;
466
+ int hour_start;
467
+ int minute_start;
468
+ FloatT second_start;
469
+ std::tm c_tm() const {
470
+ std::tm res = {
471
+ (int)second_start,
472
+ minute_start,
473
+ hour_start,
474
+ day_of_month_st,
475
+ month_start - 1,
476
+ year_start - 1900,
477
+ };
478
+ std::mktime(&res);
479
+ return res;
480
+ }
481
+ operator GPS_Time<FloatT>() const {
482
+ return GPS_Time<FloatT>(c_tm(), second_start - (int)second_start);
483
+ }
484
+ epoch_t &operator=(const GPS_Time<FloatT> &t) {
485
+ std::tm t_(t.c_tm());
486
+ year_start = t_.tm_year + 1900;
487
+ month_start = t_.tm_mon + 1;
488
+ day_of_month_st = t_.tm_mday;
489
+ hour_start = t_.tm_hour;
490
+ minute_start = t_.tm_min;
491
+ second_start = (t.seconds - (int)t.seconds) + t_.tm_sec;
492
+ return *this;
493
+ }
494
+ };
495
+ struct position_clock_t {
496
+ char symbol[1];
497
+ int vehicle_id;
498
+ FloatT coordinate_km[3];
499
+ FloatT clock_us;
500
+ bool has_sdev;
501
+ int sdev_b_n_mm[3];
502
+ int c_sdev_b_n_psec;
503
+ char clock_event_flag[1];
504
+ char clock_pred_flag[1];
505
+ char maneuver_flag[1];
506
+ char orbit_pred_flag[1];
507
+ };
508
+ struct position_clock_correlation_t {
509
+ char symbols[2];
510
+ int sdev_mm[3];
511
+ int clk_sdev_psec;
512
+ int xy_correlation;
513
+ int xz_correlation;
514
+ int xc_correlation;
515
+ int yz_correlation;
516
+ int yc_correlation;
517
+ int zc_correlation;
518
+ };
519
+ struct velocity_rate_t {
520
+ char symbol[1];
521
+ int vehicle_id;
522
+ FloatT velocity_dm_s[3];
523
+ FloatT clock_rate_chg_100ps_s; // 10^-4 microseconds/second = 100 ps/s
524
+ bool has_sdev;
525
+ int vel_sdev_100nm_s[3];
526
+ int clkrate_sdev_10_4_ps_s; // 10^-4 ps/s
527
+ };
528
+ struct velocity_rate_correlation_t {
529
+ char symbols[2];
530
+ int vel_sdev_100nm_s[3];
531
+ int clkrate_sdev_10_4_ps_s;
532
+ int xy_correlation;
533
+ int xz_correlation;
534
+ int xc_correlation;
535
+ int yz_correlation;
536
+ int yc_correlation;
537
+ int zc_correlation;
538
+ };
539
+ struct parsed_t {
540
+ enum {
541
+ UNKNOWN,
542
+ L1,
543
+ L2,
544
+ L3_11,
545
+ L12_20,
546
+ L21_22,
547
+ L23_24,
548
+ L25_26,
549
+ COMMENT,
550
+ EPOCH,
551
+ POSITION_CLOCK,
552
+ POSITION_CLOCK_CORRELATION,
553
+ VELOCITY_RATE,
554
+ VELOCITY_RATE_CORRELATION,
555
+ PARSED_ITEMS,
556
+ } type;
557
+ union {
558
+ struct l1_t l1;
559
+ struct l2_t l2;
560
+ struct l3_11_t l3_11;
561
+ struct l12_20_t l12_20;
562
+ struct l21_22_t l21_22;
563
+ struct l23_24_t l23_24;
564
+ struct l25_26_t l25_26;
565
+ struct comment_t comment;
566
+ struct epoch_t epoch;
567
+ struct position_clock_t position_clock;
568
+ struct position_clock_correlation_t position_clock_correlation;
569
+ struct velocity_rate_t velocity_rate;
570
+ struct velocity_rate_correlation_t velocity_rate_correlation;
571
+ } item;
572
+ };
573
+
574
+ static const typename TextHelper<>::convert_item_t
575
+ l1_items[13],
576
+ l2_items[6],
577
+ l3_11_items[19],
578
+ l12_20_items[18],
579
+ l21_22_items[13],
580
+ l23_24_items[5],
581
+ l25_26_items[10],
582
+ comment_items[2],
583
+ epoch_items[7],
584
+ position_clock_items[14],
585
+ position_clock_correlation_items[11],
586
+ velocity_rate_items[10],
587
+ velocity_rate_correlation_items[11];
588
+
589
+ SP3_Reader(std::istream &in) : src(in) {}
590
+
591
+ bool has_next() {
592
+ return !(src.eof() || src.fail());
593
+ }
594
+
595
+ parsed_t parse_line() {
596
+ parsed_t res = {parsed_t::UNKNOWN, {0}};
597
+
598
+ char buf[0x100] = {0};
599
+ src.getline(buf, sizeof(buf));
600
+ std::string line(buf);
601
+
602
+ switch(buf[0]){
603
+ case '#':
604
+ switch(buf[1]){
605
+ case '#':
606
+ TextHelper<>::str2val(l2_items, line, &res.item);
607
+ res.type = parsed_t::L2;
608
+ break;
609
+ default:
610
+ TextHelper<>::str2val(l1_items, line, &res.item);
611
+ res.type = parsed_t::L1;
612
+ break;
613
+ }
614
+ break;
615
+ case '+':
616
+ switch(buf[1]){
617
+ case ' ':
618
+ TextHelper<>::str2val(l3_11_items, line, &res.item);
619
+ res.type = parsed_t::L3_11;
620
+ break;
621
+ case '+':
622
+ TextHelper<>::str2val(l12_20_items, line, &res.item);
623
+ res.type = parsed_t::L12_20;
624
+ break;
625
+ }
626
+ break;
627
+ case '%':
628
+ switch(buf[1]){
629
+ case 'c':
630
+ TextHelper<>::str2val(l21_22_items, line, &res.item);
631
+ res.type = parsed_t::L21_22;
632
+ break;
633
+ case 'f':
634
+ TextHelper<>::str2val(l23_24_items, line, &res.item);
635
+ res.type = parsed_t::L23_24;
636
+ break;
637
+ case 'i':
638
+ TextHelper<>::str2val(l25_26_items, line, &res.item);
639
+ res.type = parsed_t::L25_26;
640
+ break;
641
+ }
642
+ break;
643
+ case '/':
644
+ res.type = parsed_t::COMMENT; // TODO
645
+ break;
646
+ case '*':
647
+ TextHelper<>::str2val(epoch_items, line, &res.item);
648
+ res.type = parsed_t::EPOCH;
649
+ break;
650
+ case 'P':
651
+ res.item.position_clock.has_sdev = (line.length() > 60);
652
+ TextHelper<>::str2val(
653
+ position_clock_items,
654
+ (res.item.position_clock.has_sdev ? 14 : 6),
655
+ line, &res.item);
656
+ res.type = parsed_t::POSITION_CLOCK;
657
+ break;
658
+ case 'V':
659
+ res.item.velocity_rate.has_sdev = (line.length() > 60);
660
+ TextHelper<>::str2val(
661
+ velocity_rate_items,
662
+ (res.item.velocity_rate.has_sdev ? 10 : 6),
663
+ line, &res.item);
664
+ res.type = parsed_t::VELOCITY_RATE;
665
+ break;
666
+ case 'E':
667
+ switch(buf[1]){
668
+ case 'P':
669
+ TextHelper<>::str2val(position_clock_correlation_items, line, &res.item);
670
+ res.type = parsed_t::POSITION_CLOCK_CORRELATION;
671
+ break;
672
+ case 'V':
673
+ TextHelper<>::str2val(velocity_rate_correlation_items, line, &res.item);
674
+ res.type = parsed_t::VELOCITY_RATE_CORRELATION;
675
+ break;
676
+ }
677
+ break;
678
+ }
679
+
680
+ return res;
681
+ }
682
+
683
+ struct conv_t {
684
+ /**
685
+ * @param value satellite identifier. For GPS, SBAS, and QZSS, it is PRN number.
686
+ * For other satellite systems, it is ((prefix_char << 8) | satellite_number)
687
+ * like 20993 = ((82 << 8) | 1) indicating R01 (because 'R' = 82.
688
+ */
689
+ static bool sat_id(
690
+ std::string &buf, const int &offset, const int &length, void *value,
691
+ const int &opt = 0, const bool &str2val = true){
692
+ // format: a letter followed by a 2-digit integer between 01 and 99.
693
+ if(str2val){
694
+ if(!TextHelper<>::template format_t<int>::d(
695
+ buf, offset + 1, length - 1, value, opt, true)){
696
+ return false;
697
+ }
698
+ switch(buf[offset]){
699
+ case 'G': break; // GPS
700
+ case 'S': // Satellite-Based Augmentation System (SBAS) satellites
701
+ *static_cast<int *>(value) += 100; break;
702
+ case 'J': // QZSS, nn=PRN-192, ex) J01 => PRN=193
703
+ *static_cast<int *>(value) += 192; break;
704
+ case 'R': // GLONASS
705
+ case 'L': // Low-Earth Orbiting (LEO) satellites
706
+ case 'E': // Galileo
707
+ case 'C': // BeiDou
708
+ case 'I': // IRNSS
709
+ *static_cast<int *>(value) += ((int)buf[offset] << 8); break;
710
+ case ' ':
711
+ *static_cast<int *>(value) = 0; break; // TODO
712
+ default:
713
+ return false; // unsupported
714
+ }
715
+ return true;
716
+ }else{
717
+ int digit2(*static_cast<int *>(value));
718
+ if(digit2 < 0){return false;}
719
+ char prefix((char)((digit2 >> 8) & 0xFF));
720
+ do{
721
+ if(digit2 == 0){
722
+ prefix = ' ';
723
+ break;
724
+ }
725
+ if(digit2 <= 32){ // GPS
726
+ prefix = 'G';
727
+ break;
728
+ }
729
+ if(digit2 < 120){return false;}
730
+ if(digit2 <= 158){ // SBAS
731
+ prefix = 'S';
732
+ digit2 -= 100;
733
+ break;
734
+ }
735
+ if(digit2 < 193){return false;}
736
+ if(digit2 <= 206){ // QZSS
737
+ prefix = 'J';
738
+ digit2 -= 192;
739
+ break;
740
+ }
741
+ switch(prefix){
742
+ case 'R': // GLONASS
743
+ case 'L': // Low-Earth Orbiting (LEO) satellites
744
+ case 'E': // Galileo
745
+ case 'C': // BeiDou
746
+ case 'I': // IRNSS
747
+ digit2 &= 0xFF;
748
+ break;
749
+ default:
750
+ return false; // unsupported
751
+ }
752
+ }while(false);
753
+ buf[offset] = prefix;
754
+ return TextHelper<>::template format_t<int>::d(
755
+ buf, offset + 1, length - 1, &digit2, digit2 > 0 ? 1 : 0, false);
756
+ }
757
+ }
758
+ };
759
+
760
+ static int read_all(std::istream &in, SP3_Product<FloatT> &dst) {
761
+ SP3_Reader<FloatT> src(in);
762
+ typedef SP3_Product<FloatT> dst_t;
763
+ int res(0);
764
+ struct buf_t {
765
+ dst_t &dst;
766
+ int &res;
767
+ epoch_t epoch;
768
+ enum {
769
+ TS_UNKNOWN,
770
+ TS_GPS,
771
+ TS_UTC
772
+ } time_system;
773
+ struct pv_t {
774
+ position_clock_t pos;
775
+ velocity_rate_t vel;
776
+ pv_t(){pos.symbol[0] = vel.symbol[0] = 0;}
777
+ };
778
+ typedef std::map<int, pv_t> entries_t;
779
+ entries_t entries;
780
+ buf_t(dst_t &dst_, int &res_) : dst(dst_), res(res_), time_system(TS_UNKNOWN), entries(){
781
+ epoch.symbols[0] = 0;
782
+ }
783
+ void flush(){
784
+ if(!epoch.symbols[0]){return;}
785
+ GPS_Time<FloatT> gpst(epoch);
786
+ if(time_system == TS_UTC){
787
+ gpst += GPS_Time<FloatT>::guess_leap_seconds(gpst);
788
+ }
789
+ for(typename entries_t::const_iterator it(entries.begin()), it_end(entries.end());
790
+ it != it_end; ++it){
791
+ if(it->second.pos.symbol[0]){
792
+ typename dst_t::prop_t prop = {
793
+ Vector3<FloatT>(it->second.pos.coordinate_km) * 1E3,
794
+ it->second.pos.clock_us * 1E-6,
795
+ };
796
+ dst.satellites[it->first].pos_history.insert(std::make_pair(gpst, prop));
797
+ }
798
+ if(it->second.vel.symbol[0]){
799
+ typename dst_t::prop_t prop = {
800
+ Vector3<FloatT>(it->second.vel.velocity_dm_s) * 1E-1,
801
+ it->second.vel.clock_rate_chg_100ps_s * 1E-10,
802
+ };
803
+ dst.satellites[it->first].vel_history.insert(std::make_pair(gpst, prop));
804
+ }
805
+ ++res;
806
+ }
807
+ entries.clear();
808
+ }
809
+ } buf(dst, res);
810
+ while(src.has_next()){
811
+ parsed_t parsed(src.parse_line());
812
+ switch(parsed.type){
813
+ case parsed_t::L21_22: // check time system
814
+ if(buf.time_system != buf_t::TS_UNKNOWN){break;}
815
+ if(std::strncmp(parsed.item.l21_22.time_system, "GPS", 3) == 0){
816
+ buf.time_system = buf_t::TS_GPS;
817
+ }else if(std::strncmp(parsed.item.l21_22.time_system, "UTC", 3) == 0){
818
+ buf.time_system = buf_t::TS_UTC;
819
+ }
820
+ break;
821
+ case parsed_t::EPOCH:
822
+ buf.flush();
823
+ buf.epoch = parsed.item.epoch;
824
+ break;
825
+ case parsed_t::POSITION_CLOCK: {
826
+ int sat_id(parsed.item.position_clock.vehicle_id);
827
+ buf.entries[sat_id].pos = parsed.item.position_clock;
828
+ break;
829
+ }
830
+ case parsed_t::VELOCITY_RATE: {
831
+ int sat_id(parsed.item.velocity_rate.vehicle_id);
832
+ buf.entries[sat_id].vel = parsed.item.velocity_rate;
833
+ break;
834
+ }
835
+ default: break;
836
+ }
837
+ }
838
+ buf.flush();
839
+ return res;
840
+ }
841
+ };
842
+
843
+ #define GEN_C(offset, length, container_type, container_member) \
844
+ {TextHelper<>::template format_t<char>::c, offset, length, \
845
+ offsetof(container_type, container_member), 0}
846
+ #define GEN_I(offset, length, container_type, container_member) \
847
+ {TextHelper<>::template format_t<int>::d, offset, length, \
848
+ offsetof(container_type, container_member), 0}
849
+ #define GEN_I2(offset, length, container_type, container_member) \
850
+ {TextHelper<>::template format_t<int>::d_blank, offset, length, \
851
+ offsetof(container_type, container_member), 0}
852
+ #define GEN_E(offset, length, container_type, container_member, precision) \
853
+ {TextHelper<>::template format_t<FloatT>::f, offset, length, \
854
+ offsetof(container_type, container_member), precision}
855
+ #define GEN_sat(offset, length, container_type, container_member) \
856
+ {SP3_Reader<FloatT>::conv_t::sat_id, offset, length, \
857
+ offsetof(container_type, container_member)}
858
+
859
+ template <class FloatT>
860
+ const typename TextHelper<>::convert_item_t SP3_Reader<FloatT>::l1_items[13] = {
861
+ GEN_C(0, 2, l1_t, version_symbol),
862
+ GEN_C(2, 1, l1_t, pos_or_vel_flag),
863
+ GEN_I(3, 4, l1_t, year_start),
864
+ GEN_I(8, 2, l1_t, month_start),
865
+ GEN_I(11, 2, l1_t, day_of_month_st),
866
+ GEN_I(14, 2, l1_t, hour_start),
867
+ GEN_I(17, 2, l1_t, minute_start),
868
+ GEN_E(20, 11, l1_t, second_start, 8),
869
+ GEN_I(32, 7, l1_t, number_of_epochs),
870
+ GEN_C(40, 5, l1_t, data_used),
871
+ GEN_C(46, 5, l1_t, coordinate_sys),
872
+ GEN_C(52, 3, l1_t, orbit_type),
873
+ GEN_C(56, 4, l1_t, agency),
874
+ };
875
+ template <class FloatT>
876
+ const typename TextHelper<>::convert_item_t SP3_Reader<FloatT>::l2_items[6] = {
877
+ GEN_C(0, 2, l2_t, symbols),
878
+ GEN_I(3, 4, l2_t, gps_week),
879
+ GEN_E(8, 15, l2_t, seconds_of_week, 8),
880
+ GEN_E(24, 14, l2_t, epoch_interval, 8),
881
+ GEN_I(39, 5, l2_t, mod_jul_day_st),
882
+ GEN_E(45, 15, l2_t, fractional_day, 13),
883
+ };
884
+ template <class FloatT>
885
+ const typename TextHelper<>::convert_item_t SP3_Reader<FloatT>::l3_11_items[19] = {
886
+ GEN_C(0, 2, l3_11_t, symbols),
887
+ GEN_I2(3, 3, l3_11_t, number_of_sats),
888
+ GEN_sat(9, 3, l3_11_t, sat_id[0]),
889
+ GEN_sat(12, 3, l3_11_t, sat_id[1]),
890
+ GEN_sat(15, 3, l3_11_t, sat_id[2]),
891
+ GEN_sat(18, 3, l3_11_t, sat_id[3]),
892
+ GEN_sat(21, 3, l3_11_t, sat_id[4]),
893
+ GEN_sat(24, 3, l3_11_t, sat_id[5]),
894
+ GEN_sat(27, 3, l3_11_t, sat_id[6]),
895
+ GEN_sat(30, 3, l3_11_t, sat_id[7]),
896
+ GEN_sat(33, 3, l3_11_t, sat_id[8]),
897
+ GEN_sat(36, 3, l3_11_t, sat_id[9]),
898
+ GEN_sat(39, 3, l3_11_t, sat_id[10]),
899
+ GEN_sat(42, 3, l3_11_t, sat_id[11]),
900
+ GEN_sat(45, 3, l3_11_t, sat_id[12]),
901
+ GEN_sat(48, 3, l3_11_t, sat_id[13]),
902
+ GEN_sat(51, 3, l3_11_t, sat_id[14]),
903
+ GEN_sat(54, 3, l3_11_t, sat_id[15]),
904
+ GEN_sat(57, 3, l3_11_t, sat_id[16]),
905
+ };
906
+ template <class FloatT>
907
+ const typename TextHelper<>::convert_item_t SP3_Reader<FloatT>::l12_20_items[18] = {
908
+ GEN_C(0, 2, l12_20_t, symbols),
909
+ GEN_I(9, 3, l12_20_t, sat_accuracy[0]),
910
+ GEN_I(12, 3, l12_20_t, sat_accuracy[1]),
911
+ GEN_I(15, 3, l12_20_t, sat_accuracy[2]),
912
+ GEN_I(18, 3, l12_20_t, sat_accuracy[3]),
913
+ GEN_I(21, 3, l12_20_t, sat_accuracy[4]),
914
+ GEN_I(24, 3, l12_20_t, sat_accuracy[5]),
915
+ GEN_I(27, 3, l12_20_t, sat_accuracy[6]),
916
+ GEN_I(30, 3, l12_20_t, sat_accuracy[7]),
917
+ GEN_I(33, 3, l12_20_t, sat_accuracy[8]),
918
+ GEN_I(36, 3, l12_20_t, sat_accuracy[9]),
919
+ GEN_I(39, 3, l12_20_t, sat_accuracy[10]),
920
+ GEN_I(42, 3, l12_20_t, sat_accuracy[11]),
921
+ GEN_I(45, 3, l12_20_t, sat_accuracy[12]),
922
+ GEN_I(48, 3, l12_20_t, sat_accuracy[13]),
923
+ GEN_I(51, 3, l12_20_t, sat_accuracy[14]),
924
+ GEN_I(54, 3, l12_20_t, sat_accuracy[15]),
925
+ GEN_I(57, 3, l12_20_t, sat_accuracy[16]),
926
+ };
927
+ template <class FloatT>
928
+ const typename TextHelper<>::convert_item_t SP3_Reader<FloatT>::l21_22_items[13] = {
929
+ GEN_C(0, 2, l21_22_t, symbols),
930
+ GEN_C(3, 2, l21_22_t, file_type),
931
+ GEN_C(6, 2, l21_22_t, _2_characters),
932
+ GEN_C(9, 3, l21_22_t, time_system),
933
+ GEN_C(13, 3, l21_22_t, _3_characters),
934
+ GEN_C(17, 4, l21_22_t, _4_characters[0]),
935
+ GEN_C(22, 4, l21_22_t, _4_characters[1]),
936
+ GEN_C(27, 4, l21_22_t, _4_characters[2]),
937
+ GEN_C(32, 4, l21_22_t, _4_characters[3]),
938
+ GEN_C(37, 5, l21_22_t, _5_characters[0]),
939
+ GEN_C(43, 5, l21_22_t, _5_characters[1]),
940
+ GEN_C(49, 5, l21_22_t, _5_characters[2]),
941
+ GEN_C(55, 5, l21_22_t, _5_characters[3]),
942
+ };
943
+ template <class FloatT>
944
+ const typename TextHelper<>::convert_item_t SP3_Reader<FloatT>::l23_24_items[5] = {
945
+ GEN_C(0, 2, l23_24_t, symbols),
946
+ GEN_E(3, 10, l23_24_t, base_for_pos_vel, 7),
947
+ GEN_E(14, 12, l23_24_t, base_for_clk_rate, 9),
948
+ GEN_E(27, 14, l23_24_t, _14_column_float, 11),
949
+ GEN_E(42, 18, l23_24_t, _18_column_float, 15),
950
+ };
951
+ template <class FloatT>
952
+ const typename TextHelper<>::convert_item_t SP3_Reader<FloatT>::l25_26_items[10] = {
953
+ GEN_C(0, 2, l25_26_t, symbols),
954
+ GEN_I(3, 4, l25_26_t, _4_column_int[0]),
955
+ GEN_I(8, 4, l25_26_t, _4_column_int[1]),
956
+ GEN_I(13, 4, l25_26_t, _4_column_int[2]),
957
+ GEN_I(18, 4, l25_26_t, _4_column_int[3]),
958
+ GEN_I(23, 6, l25_26_t, _6_column_int[0]),
959
+ GEN_I(30, 6, l25_26_t, _6_column_int[1]),
960
+ GEN_I(37, 6, l25_26_t, _6_column_int[2]),
961
+ GEN_I(44, 6, l25_26_t, _6_column_int[3]),
962
+ GEN_I(51, 9, l25_26_t, _9_column_int),
963
+ };
964
+ template <class FloatT>
965
+ const typename TextHelper<>::convert_item_t SP3_Reader<FloatT>::comment_items[2] = {
966
+ GEN_C(0, 2, comment_t, symbols),
967
+ GEN_C(3, 57, comment_t, comment),
968
+ };
969
+ template <class FloatT>
970
+ const typename TextHelper<>::convert_item_t SP3_Reader<FloatT>::epoch_items[7] = {
971
+ GEN_C(0, 2, epoch_t, symbols),
972
+ GEN_I(3, 4, epoch_t, year_start),
973
+ GEN_I(8, 2, epoch_t, month_start),
974
+ GEN_I(11, 2, epoch_t, day_of_month_st),
975
+ GEN_I(14, 2, epoch_t, hour_start),
976
+ GEN_I(17, 2, epoch_t, minute_start),
977
+ GEN_E(20, 11, epoch_t, second_start, 8),
978
+ };
979
+ template <class FloatT>
980
+ const typename TextHelper<>::convert_item_t SP3_Reader<FloatT>::position_clock_items[14] = {
981
+ GEN_C(0, 1, position_clock_t, symbol),
982
+ GEN_sat(1, 3, position_clock_t, vehicle_id),
983
+ GEN_E(4, 14, position_clock_t, coordinate_km[0], 6),
984
+ GEN_E(18, 14, position_clock_t, coordinate_km[1], 6),
985
+ GEN_E(32, 14, position_clock_t, coordinate_km[2], 6),
986
+ GEN_E(46, 14, position_clock_t, clock_us, 6),
987
+ GEN_I(61, 2, position_clock_t, sdev_b_n_mm[0]),
988
+ GEN_I(64, 2, position_clock_t, sdev_b_n_mm[1]),
989
+ GEN_I(67, 2, position_clock_t, sdev_b_n_mm[2]),
990
+ GEN_I(70, 3, position_clock_t, c_sdev_b_n_psec),
991
+ GEN_C(74, 1, position_clock_t, clock_event_flag),
992
+ GEN_C(75, 1, position_clock_t, clock_pred_flag),
993
+ GEN_C(78, 1, position_clock_t, maneuver_flag),
994
+ GEN_C(79, 1, position_clock_t, orbit_pred_flag),
995
+ };
996
+ template <class FloatT>
997
+ const typename TextHelper<>::convert_item_t SP3_Reader<FloatT>::position_clock_correlation_items[11] = {
998
+ GEN_C(0, 2, position_clock_correlation_t, symbols),
999
+ GEN_I(4, 4, position_clock_correlation_t, sdev_mm[0]),
1000
+ GEN_I(9, 4, position_clock_correlation_t, sdev_mm[1]),
1001
+ GEN_I(14, 4, position_clock_correlation_t, sdev_mm[2]),
1002
+ GEN_I(19, 7, position_clock_correlation_t, clk_sdev_psec),
1003
+ GEN_I(27, 8, position_clock_correlation_t, xy_correlation),
1004
+ GEN_I(36, 8, position_clock_correlation_t, xz_correlation),
1005
+ GEN_I(45, 8, position_clock_correlation_t, xc_correlation),
1006
+ GEN_I(54, 8, position_clock_correlation_t, yz_correlation),
1007
+ GEN_I(63, 8, position_clock_correlation_t, yc_correlation),
1008
+ GEN_I(72, 8, position_clock_correlation_t, zc_correlation),
1009
+ };
1010
+ template <class FloatT>
1011
+ const typename TextHelper<>::convert_item_t SP3_Reader<FloatT>::velocity_rate_items[10] = {
1012
+ GEN_C(0, 1, velocity_rate_t, symbol),
1013
+ GEN_sat(1, 3, velocity_rate_t, vehicle_id),
1014
+ GEN_E(4, 14, velocity_rate_t, velocity_dm_s[0], 6),
1015
+ GEN_E(18, 14, velocity_rate_t, velocity_dm_s[1], 6),
1016
+ GEN_E(32, 14, velocity_rate_t, velocity_dm_s[2], 6),
1017
+ GEN_E(46, 14, velocity_rate_t, clock_rate_chg_100ps_s, 6),
1018
+ GEN_I(61, 2, velocity_rate_t, vel_sdev_100nm_s[0]),
1019
+ GEN_I(64, 2, velocity_rate_t, vel_sdev_100nm_s[1]),
1020
+ GEN_I(67, 2, velocity_rate_t, vel_sdev_100nm_s[2]),
1021
+ GEN_I(70, 3, velocity_rate_t, clkrate_sdev_10_4_ps_s),
1022
+ };
1023
+ template <class FloatT>
1024
+ const typename TextHelper<>::convert_item_t SP3_Reader<FloatT>::velocity_rate_correlation_items[11] = {
1025
+ GEN_C(0, 2, velocity_rate_correlation_t, symbols),
1026
+ GEN_I(4, 4, velocity_rate_correlation_t, vel_sdev_100nm_s[0]),
1027
+ GEN_I(9, 4, velocity_rate_correlation_t, vel_sdev_100nm_s[1]),
1028
+ GEN_I(14, 4, velocity_rate_correlation_t, vel_sdev_100nm_s[2]),
1029
+ GEN_I(19, 7, velocity_rate_correlation_t, clkrate_sdev_10_4_ps_s),
1030
+ GEN_I(27, 8, velocity_rate_correlation_t, xy_correlation),
1031
+ GEN_I(36, 8, velocity_rate_correlation_t, xz_correlation),
1032
+ GEN_I(45, 8, velocity_rate_correlation_t, xc_correlation),
1033
+ GEN_I(54, 8, velocity_rate_correlation_t, yz_correlation),
1034
+ GEN_I(63, 8, velocity_rate_correlation_t, yc_correlation),
1035
+ GEN_I(72, 8, velocity_rate_correlation_t, zc_correlation),
1036
+ };
1037
+
1038
+ #undef GEN_C
1039
+ #undef GEN_I
1040
+ #undef GEN_I2
1041
+ #undef GEN_E
1042
+ #undef GEN_sat
1043
+
1044
+ template <class FloatT>
1045
+ class SP3_Writer {
1046
+ public:
1047
+ typedef SP3_Reader<FloatT> reader_t;
1048
+ static std::string print_line(const typename reader_t::parsed_t &parsed){
1049
+ static const struct {
1050
+ const typename TextHelper<>::convert_item_t *items;
1051
+ int items_num;
1052
+ const char *symbol;
1053
+ } table[reader_t::parsed_t::PARSED_ITEMS] = {
1054
+ {0}, // UNKNOWN
1055
+ #define MAKE_ENTRY(k, str) \
1056
+ {reader_t::k, sizeof(reader_t::k) / sizeof(reader_t::k[0]), str}
1057
+ MAKE_ENTRY(l1_items, "#"), // L1
1058
+ MAKE_ENTRY(l2_items, "##"), // L2
1059
+ MAKE_ENTRY(l3_11_items, "+ "), // L3_11
1060
+ MAKE_ENTRY(l12_20_items, "++"), // L12_20
1061
+ MAKE_ENTRY(l21_22_items, "%c"), // L21_22
1062
+ MAKE_ENTRY(l23_24_items, "%f"), // L23_24
1063
+ MAKE_ENTRY(l25_26_items, "%i"), // L25_26
1064
+ MAKE_ENTRY(comment_items, "/"), // COMMENT
1065
+ MAKE_ENTRY(epoch_items, "*"), // EPOCH
1066
+ MAKE_ENTRY(position_clock_items, "P"), // POSITION_CLOCK
1067
+ MAKE_ENTRY(position_clock_correlation_items, "EP"), // POSITION_CLOCK_CORRELATION
1068
+ MAKE_ENTRY(velocity_rate_items, "V"), // VELOCITY_RATE
1069
+ MAKE_ENTRY(velocity_rate_correlation_items, "EV"), // VELOCITY_RATE_CORRELATION
1070
+ #undef MAKE_ENTRY
1071
+ };
1072
+
1073
+ int res_length(60);
1074
+ int items_num(table[parsed.type].items_num);
1075
+ switch(parsed.type){
1076
+ case reader_t::parsed_t::POSITION_CLOCK:
1077
+ if(parsed.item.position_clock.has_sdev){res_length = 80;}
1078
+ else{items_num = 6;}
1079
+ break;
1080
+ case reader_t::parsed_t::VELOCITY_RATE:
1081
+ if(parsed.item.velocity_rate.has_sdev){res_length = 80;}
1082
+ else{items_num = 6;}
1083
+ break;
1084
+ case reader_t::parsed_t::POSITION_CLOCK_CORRELATION:
1085
+ case reader_t::parsed_t::VELOCITY_RATE_CORRELATION:
1086
+ res_length = 80;
1087
+ break;
1088
+ default:
1089
+ break;
1090
+ }
1091
+ std::string res(res_length, ' ');
1092
+ if((items_num <= 0)
1093
+ || (!TextHelper<>::val2str(table[parsed.type].items, items_num, res, &parsed.item))){
1094
+ return std::string();
1095
+ }
1096
+ res.replace(0, std::strlen(table[parsed.type].symbol), table[parsed.type].symbol);
1097
+ return res;
1098
+ }
1099
+
1100
+ static void dump_default(
1101
+ std::ostream &out, typename reader_t::parsed_t &item){
1102
+ out << print_line(item) << std::endl;
1103
+ }
1104
+
1105
+ static int write_all(
1106
+ std::ostream &out, const SP3_Product<FloatT> &src,
1107
+ void (*dump)(
1108
+ std::ostream &, typename reader_t::parsed_t &) = dump_default) {
1109
+ typedef SP3_Product<FloatT> src_t;
1110
+ typedef typename src_t::epochs_t epochs_t;
1111
+ epochs_t epochs(src.epochs());
1112
+ if(epochs.empty()){return 0;}
1113
+ { // 1st line
1114
+ typename reader_t::parsed_t header = {reader_t::parsed_t::L1};
1115
+ header.item.l1 = *epochs.begin();
1116
+ header.item.l1.number_of_epochs = epochs.size();
1117
+ header.item.l1.pos_or_vel_flag[0] = src.has_velocity() ? 'V' : 'P';
1118
+ dump(out, header);
1119
+ }
1120
+ { // 2nd line
1121
+ typename reader_t::parsed_t first_epoch = {reader_t::parsed_t::L2};
1122
+ GPS_Time<FloatT> t0(*epochs.begin());
1123
+ first_epoch.item.l2.gps_week = t0.week;
1124
+ first_epoch.item.l2.seconds_of_week = t0.seconds;
1125
+ first_epoch.item.l2.epoch_interval
1126
+ = (epochs.size() > 1) ? (*(++(epochs.begin())) - t0) : 0;
1127
+ FloatT day_of_week(t0.seconds / 86400);
1128
+ first_epoch.item.l2.mod_jul_day_st
1129
+ = 44244 + (t0.week * 7) + (int)day_of_week;
1130
+ first_epoch.item.l2.fractional_day
1131
+ = day_of_week - (int)day_of_week;
1132
+ dump(out, first_epoch);
1133
+ }
1134
+ typedef typename src_t::satellites_t sats_t;
1135
+ { // 3rd line
1136
+ int sats(src.satellites.size());
1137
+ typename reader_t::parsed_t sat_list = {reader_t::parsed_t::L3_11};
1138
+ sat_list.item.l3_11.number_of_sats = sats;
1139
+ typename sats_t::const_iterator
1140
+ it(src.satellites.begin()), it_end(src.satellites.end());
1141
+ int lines((sats + 16) / 17);
1142
+ for(int i(lines < 5 ? 5 : lines); i > 0; --i){
1143
+ for(int j(0); j < 17; ++j){
1144
+ sat_list.item.l3_11.sat_id[j] = ((it == it_end) ? 0 : (it++)->first);
1145
+ }
1146
+ dump(out, sat_list);
1147
+ sat_list.item.l3_11.number_of_sats = 0;
1148
+ }
1149
+ }
1150
+ typename reader_t::parsed_t
1151
+ pos = {reader_t::parsed_t::POSITION_CLOCK},
1152
+ vel = {reader_t::parsed_t::VELOCITY_RATE},
1153
+ time = {reader_t::parsed_t::EPOCH};
1154
+ pos.item.position_clock.has_sdev = false;
1155
+ vel.item.velocity_rate.has_sdev = false;
1156
+ int entries(0);
1157
+ for(typename epochs_t::const_iterator it(epochs.begin()), it_end(epochs.end());
1158
+ it != it_end; ++it){
1159
+ time.item.epoch = *it;
1160
+ dump(out, time);
1161
+ for(typename sats_t::const_iterator
1162
+ it2(src.satellites.begin()), it2_end(src.satellites.end());
1163
+ it2 != it2_end; ++it2){
1164
+ typename src_t::per_satellite_t::history_t::const_iterator it_entry;
1165
+ if((it_entry = it2->second.pos_history.find(*it))
1166
+ == it2->second.pos_history.end()){continue;}
1167
+ ++entries;
1168
+ pos.item.position_clock.vehicle_id = it2->first;
1169
+ for(int i(0); i < 3; ++i){
1170
+ pos.item.position_clock.coordinate_km[i] = it_entry->second.xyz[i] * 1E-3;
1171
+ }
1172
+ pos.item.position_clock.clock_us = it_entry->second.clk * 1E6;
1173
+ dump(out, pos);
1174
+ if((it_entry = it2->second.vel_history.find(*it))
1175
+ == it2->second.vel_history.end()){continue;}
1176
+ vel.item.velocity_rate.vehicle_id = it2->first;
1177
+ for(int i(0); i < 3; ++i){
1178
+ vel.item.velocity_rate.velocity_dm_s[i] = it_entry->second.xyz[i] * 1E1;
1179
+ }
1180
+ vel.item.velocity_rate.clock_rate_chg_100ps_s = it_entry->second.clk * 1E10;
1181
+ dump(out, vel);
1182
+ }
1183
+ }
1184
+ return entries;
1185
+ }
1186
+ };
1187
+
1188
+
1189
+ #endif /* #define __SP3_H__ */