• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

icsm-au / DynAdjust / 13494567994

24 Feb 2025 09:15AM UTC coverage: 81.168% (+2.0%) from 79.161%
13494567994

push

github

web-flow
Merge pull request #234 from icsm-au/1.2.8

Version 1.2.8 (fixes, ehnacements, improved datum management)

6131 of 8137 new or added lines in 90 files covered. (75.35%)

162 existing lines in 33 files now uncovered.

32214 of 39688 relevant lines covered (81.17%)

11775.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

59.3
/dynadjust/include/io/dnaiosnxread.cpp
1
//============================================================================
2
// Name         : dnaiosnxread.cpp
3
// Author       : Roger Fraser
4
// Contributors :
5
// Version      : 1.00
6
// Copyright    : Copyright 2017 Geoscience Australia
7
//
8
//                Licensed under the Apache License, Version 2.0 (the "License");
9
//                you may not use this file except in compliance with the License.
10
//                You may obtain a copy of the License at
11
//               
12
//                http ://www.apache.org/licenses/LICENSE-2.0
13
//               
14
//                Unless required by applicable law or agreed to in writing, software
15
//                distributed under the License is distributed on an "AS IS" BASIS,
16
//                WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
//                See the License for the specific language governing permissions and
18
//                limitations under the License.
19
//
20
// Description  : DynAdjust SINEX file read operations
21
//============================================================================
22

23
#include <include/io/dnaiosnx.hpp>
24
#include <include/functions/dnastringfuncs.hpp>
25
#include <include/functions/dnatemplatedatetimefuncs.hpp>
26
#include <include/measurement_types/dnagpspoint.hpp>
27

28
namespace dynadjust { 
29
namespace iostreams {
30

31
void dna_io_snx::parse_sinex(std::ifstream** snx_file, const std::string& fileName, vdnaStnPtr* vStations, PUINT32 stnCount, 
7✔
32
                                        vdnaMsrPtr* vMeasurements, PUINT32 msrCount, PUINT32 clusterID,
33
                                        StnTally& parsestn_tally, MsrTally& parsemsr_tally, UINT32& fileOrder,
34
                                        CDnaDatum& datum, bool applyDiscontinuities, 
35
                                        v_discontinuity_tuple* stn_discontinuities, bool& m_discontsSortedbyName,
36
                                        UINT32& lineNo, UINT32& columnNo, _PARSE_STATUS_& parseStatus)
37
{
38
        parseStatus = PARSE_SUCCESS;
7✔
39
        
40
        (*stnCount) = 0;
7✔
41
        (*msrCount) = 0;
7✔
42
        parsestn_tally.initialise();
7✔
43
        parsemsr_tally.initialise();
7✔
44

45
        lineNo = 0;
7✔
46
        columnNo = 0;
7✔
47

48
        containsVelocities_ = false;
7✔
49
        applyDiscontinuities_ = applyDiscontinuities;
7✔
50
        containsDiscontinuities_ = false;
7✔
51

52
        // read header line and extract epoch
53
        if (!parse_sinex_header(snx_file, datum, lineNo))
7✔
54
        {
NEW
55
                std::stringstream ss;
×
NEW
56
                ss << "parse_sinex(): The SINEX file  " << fileName << " did not contain a header record." << std::endl;
×
NEW
57
                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
UNCOV
58
        }
×
59

60
        try {
7✔
61
                
62
                // read data
63
                parse_sinex_data(snx_file, vStations, stnCount, vMeasurements, msrCount, clusterID,
7✔
64
                                        parsestn_tally, parsemsr_tally, datum, 
65
                                        stn_discontinuities, m_discontsSortedbyName,
66
                                        fileOrder, lineNo, columnNo);
67
        }
NEW
68
        catch (const std::ios_base::failure& f) {
×
69
                if ((*snx_file)->eof())
×
70
                        return;
×
NEW
71
                std::stringstream ss;
×
NEW
72
                ss << "parse_sinex(): An error was encountered when attempting to read SINEX file  " << fileName << "." << std::endl << "  " << f.what();
×
NEW
73
                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
74
        }
×
NEW
75
        catch (const std::runtime_error& f) {
×
NEW
76
                std::stringstream ss;
×
77
                ss << "  - line " << lineNo;
×
NEW
78
                ss << ", column " <<  columnNo << std::endl;
×
79
                ss << "  - " << f.what();
×
NEW
80
                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
81
        }
×
82
}
83
        
84

85
void dna_io_snx::parse_discontinuity_file(std::ifstream* snx_file, const std::string& fileName,
4✔
86
        v_discontinuity_tuple* stn_discontinuities, bool& m_discontsSortedbyName,
87
        UINT32& lineNo, UINT32& columnNo, _PARSE_STATUS_& parseStatus)
88
{
89
        parseStatus = PARSE_SUCCESS;
4✔
90
        
91
        lineNo = 0;
4✔
92
        columnNo = 0;
4✔
93

94
        CDnaDatum datum;
4✔
95

96
        containsVelocities_ = false;
4✔
97
        containsDiscontinuities_ = false;
4✔
98

99
        std::string sBuf, tmp;
4✔
100

101
        try {
4✔
102
                // As the purpose of the discontinuity file is to provide dates on when discontinuities occur,
103
                // there is no real imperative to capture the date from the header line
104
                // Check for it anyway
105
                parse_sinex_header(&snx_file, datum, lineNo);
4✔
106

107
                while (!snx_file->eof())                        // while EOF not found
12✔
108
                {
109
                        lineNo++;
12✔
110
                        getline((*snx_file), sBuf);
12✔
111

112
                        // End of data?
113
                        if (boost::iequals(sBuf.substr(0, 7), ENDSNX))
24✔
114
                                break;
115

116
                        try {
8✔
117

118
                                if (sBuf.substr(0, 1) == "+")
16✔
119
                                        if (sBuf.substr(0, 23) == "+SOLUTION/DISCONTINUITY")
8✔
120
                                                parse_sinex_discontinuities(snx_file, stn_discontinuities, m_discontsSortedbyName,
4✔
121
                                                        lineNo, columnNo);
122
                        }
123
                        catch (...) {
×
NEW
124
                                std::stringstream ss;
×
NEW
125
                                ss << "parse_sinex_data(): Could not extract data from the record:  " << std::endl << "    " << sBuf << ".";
×
NEW
126
                                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
UNCOV
127
                        }                
×
128
                }
129
        
130
        }
NEW
131
        catch (const std::ios_base::failure& f) {
×
132
                if (snx_file->eof())
×
133
                        return;
×
NEW
134
                std::stringstream ss;
×
NEW
135
                ss << "parse_discontinuity_file(): An error was encountered when attempting to read SINEX file  " << fileName << "." << std::endl << "  " << f.what();
×
NEW
136
                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
137
        }
×
NEW
138
        catch (const std::runtime_error& f) {
×
NEW
139
                std::stringstream ss;
×
140
                ss << "  - line " << lineNo;
×
NEW
141
                ss << ", column " <<  columnNo << std::endl;
×
142
                ss << "  - " << f.what();
×
NEW
143
                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
144
        }
×
145
}
8✔
146
        
147

148
// Expects yy as two digit year, and doy as three digit day of year
149
std::stringstream dna_io_snx::parse_date_from_yy_doy(const UINT32& yy, const UINT32& doy, DATE_FORMAT_TYPE date_type, const std::string& separator)
17,949✔
150
{
151
        std::stringstream ss_doy, ss_year, ss_date;
17,949✔
152

153
        // format the day of year so that boost date_input_facet is happy
154
        if (doy < 100)
17,949✔
155
                ss_doy << "0";
5,272✔
156
        if (doy < 10)
17,949✔
157
                ss_doy << "0";
376✔
158

159
        ss_doy << doy;
17,949✔
160

161
        // create four-character year?
162
        switch (date_type)
17,949✔
163
        {
164
        case yyyy_doy:
17,949✔
165
        case doy_yyyy:
17,949✔
166
                //        SINEX time specification:
167
                //         _______________________________________________________________ 
168
                //        | Time:        | YY:DDD:SSSSS. "UTC"                                        |        I2.2,                |
169
                //        |                | YY = last 2 digits of the year,                |        1H:,I3.3,        |
170
                //        |                | if YY <= 50 implies 21-st century,        |        1H:,I5.5        |
171
                //        |                | if YY > 50 implies 20-th century,                |                                |
172
                //        |                | DDD = 3-digit day in year,                        |                                |
173
                //        |                | SSSSS = 5-digit seconds in day.                |                                |
174
                //        |_______|_______________________________________|_______________|
175
                if (yy <= 50)
17,949✔
176
                        ss_year << "20";
15,381✔
177
                else //(average_year > 50)
178
                        ss_year << "19";
2,568✔
179
                break;
180
        default:
181
                break;
182
        }
183

184
        if (yy < 10)
17,949✔
185
                ss_year << "0";
7,720✔
186
        ss_year << yy;
17,949✔
187

188
        switch (date_type)
17,949✔
189
        {
190
        case yyyy_doy:
×
191
        case yy_doy:
×
192
                ss_date << ss_year.str() << separator << ss_doy.str();
×
193
                break;
×
194
        case doy_yy:
17,949✔
195
        case doy_yyyy:
17,949✔
196
                ss_date << ss_doy.str() << separator << ss_year.str();
35,898✔
197
                break;
17,949✔
198
        }
199

200
        return ss_date;
17,949✔
201
}
17,949✔
202

203
// date is a string "YY:DDD:SSSSS"
204
std::stringstream dna_io_snx::parse_date_from_string(const std::string& date_str, DATE_FORMAT_TYPE date_type, DATE_TERMINAL_TYPE terminal_type, const std::string& separator)
27,221✔
205
{
206
        // Year
207
        UINT32 yy = LongFromString<UINT32>(date_str.substr(0, 2));
27,221✔
208
        
209
        // Day of year (assume a colon separates the two values)
210
        UINT32 doy = LongFromString<UINT32>(date_str.substr(3, 3));
27,221✔
211

212
        if (yy == 0 && doy == 0)
27,221✔
213
        {
214
                boost::gregorian::date today(boost::gregorian::day_clock::local_day());
9,300✔
215
                std::stringstream date_ss;
9,300✔
216

217
                switch (terminal_type)
9,300✔
218
                {
219
                case date_from:
4,648✔
220
                        // Set a date before the advent of GPS
221
                        date_ss << "001 " << TIME_IMMEMORIAL;
4,648✔
222
                        return date_ss;
4,648✔
223
                case date_to:
4,652✔
224
                        // Set a date 100 years into the future
225
                        date_ss << "365 " << (today.year() + 100);
4,652✔
226
                        return date_ss;
4,652✔
227
                }
228
        }
9,300✔
229

230
        return parse_date_from_yy_doy(yy, doy, date_type, separator);
17,921✔
231
}
232

233
bool dna_io_snx::parse_sinex_header(std::ifstream** snx_file, CDnaDatum& datum, UINT32& lineNo)
11✔
234
{
235
        /*
236
        %=SNX 2.00 AUS 11:182:57860 IGS 11:157:00000 11:157:86370 P 00054 0 S          
237
        */
238
        char c = static_cast<char>((*snx_file)->peek());
11✔
239
        if (c != '%')
11✔
240
        {
241
                // The SINEX file doesn't have a header.
242
                return false;
243
        }        
244

245
        std::string sBuf, sinex_code;
7✔
246
        
247
        UINT32 average_year, average_doy;
7✔
248
        std::stringstream ss;
7✔
249

250
        lineNo++;
7✔
251
        try {
7✔
252
                getline((**snx_file), sBuf);
7✔
253
        }
254
        catch (...) {
×
NEW
255
                ss << "parse_sinex_header(): Could not read from the SINEX file.  " << std::endl << ".";
×
NEW
256
                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
UNCOV
257
        }
×
258

259
        try {
7✔
260
                // capture epoch of data, calculate average and test for year cross over
261
                year_doy_Average(
7✔
262
                        LongFromString<UINT32>(sBuf.substr(32, 2)), 
14✔
263
                        LongFromString<UINT32>(sBuf.substr(45, 2)), 
14✔
264
                        LongFromString<UINT32>(sBuf.substr(35, 3)), 
14✔
265
                        LongFromString<UINT32>(sBuf.substr(48, 3)),
7✔
266
                        average_year, 
267
                        average_doy);
268

269
                ss = parse_date_from_yy_doy(average_year, average_doy, doy_yyyy, std::string(" "));
14✔
270
                datum.SetEpoch(dateFromStringstream_doy_year<boost::gregorian::date, std::stringstream>(ss));
7✔
271
        }
272
        catch (...)
×
273
        {
NEW
274
                ss << "parse_sinex_header(): Could not extract date and time information from the header record:  " << std::endl << "    " << sBuf << ".";
×
NEW
275
                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
UNCOV
276
        }
×
277

278
        return true;
7✔
279
}
21✔
280
        
281

282
void dna_io_snx::parse_sinex_data(std::ifstream** snx_file, vdnaStnPtr* vStations, PUINT32 stnCount, 
7✔
283
                                        vdnaMsrPtr* vMeasurements, PUINT32 msrCount, PUINT32 clusterID,
284
                                        StnTally& parsestn_tally, MsrTally& parsemsr_tally, CDnaDatum& datum, 
285
                                        v_discontinuity_tuple* stn_discontinuities, bool& m_discontsSortedbyName,
286
                                        UINT32& fileOrder, UINT32& lineNo, UINT32& columnNo)
287
{
288
        std::string sBuf, tmp;
7✔
289
        
290
        vStations->clear();
7✔
291
        vMeasurements->clear();
7✔
292

293
        siteIDsRead_ = false;
7✔
294
        solutionEpochsRead_ = false;
7✔
295

296
        while (!(*snx_file)->eof())                        // while EOF not found
133✔
297
        {
298
                lineNo++;
126✔
299
                getline((**snx_file), sBuf);
126✔
300

301
                // End of data?
302
                if (boost::iequals(sBuf.substr(0, 7), ENDSNX))
252✔
303
                        break;
304

305
                try {
119✔
306

307
                        if (sBuf.substr(0, 1) == "+")
238✔
308
                                parse_sinex_block(snx_file, sBuf.c_str(), vStations, stnCount, vMeasurements, msrCount, clusterID,
35✔
309
                                        parsestn_tally, parsemsr_tally, datum, fileOrder, lineNo, columnNo);
310
                }
NEW
311
                catch (std::runtime_error& e) {
×
NEW
312
                        std::stringstream ss;
×
NEW
313
                        ss << "parse_sinex_data(): Error parsing SINEX file:  " << std::endl << 
×
314
                                "    " << e.what();
×
NEW
315
                        throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
316
                }
×
317
                catch (...) {
×
NEW
318
                        std::stringstream ss;
×
NEW
319
                        ss << "parse_sinex_data(): Could not extract data from the record:  " << std::endl << "    " << sBuf << ".";
×
NEW
320
                        throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
UNCOV
321
                }                
×
322
        }
323

324
        // Update station names in station and measurement vectors using discontinuity file 
325
        if (applyDiscontinuities_)
7✔
326
                // true if  --discontinuity-file sinex_discontinuities.snx
327
                format_station_names(stn_discontinuities, m_discontsSortedbyName, 
3✔
328
                        vStations, vMeasurements);
329
}
14✔
330
        
331

332
void dna_io_snx::format_station_names(v_discontinuity_tuple* stn_discontinuities, bool& m_discontsSortedbyName,
3✔
333
        vdnaStnPtr* vStations, vdnaMsrPtr* vMeasurements)
334
{
335
        UINT32 site, doy, year;
3✔
336
        std::string site_name;
3✔
337

338
#ifdef _MSDEBUG
339
        std::string date_str;
340
#endif
341

342
        _it_vdiscontinuity_tuple _it_discont(stn_discontinuities->end()), _it_discont_site;
3✔
343

344
        boost::gregorian::date site_date;
3✔
345

346
        if (!m_discontsSortedbyName)
3✔
347
        {
348
                std::sort(stn_discontinuities->begin(), stn_discontinuities->end(),
3✔
349
                        CompareSiteTuplesByName<discontinuity_tuple>());
350
                m_discontsSortedbyName = true;
3✔
351
        }
352
        std::sort(siteOccurrence_.begin(), siteOccurrence_.end(),
3✔
353
                CompareSiteTuplesByName<site_id>());
354

355
        _it_discont = stn_discontinuities->begin();
3✔
356

357
        for (site=0; site<siteOccurrence_.size(); ++site)
12✔
358
        {
359
                site_name = siteOccurrence_.at(site).site_name;
9✔
360

361
                // At this point, _it_discont will point to either:
362
                //  1. the first element in the vector
363
                //  2. the first occurrence of discontinuity for site_name
364
                // To prevent searching through the entire array again on 2. , check if 
365
                // _it_discont->site_name = site_name
366
                if (!boost::equals(site_name, _it_discont->site_name))
9✔
367
                {
368
                        // Save the iterator
369
                        _it_discont_site = _it_discont;
9✔
370

371
                        if ((_it_discont = binary_search_discontinuity_site(
9✔
372
                                _it_discont, 
373
                                stn_discontinuities->end(), 
374
                                site_name)) == stn_discontinuities->end())
27✔
375
                        {
376
                                // Site not found in discontinuity file, reset iterator to last saved state
377
                                _it_discont = _it_discont_site;
×
378
                                continue;
×
379
                        }
380
                }
381
                
382
                // Is this site a discontinuity site?
383
                if (!_it_discont->discontinuity_exists)
9✔
384
                        continue;
×
385

386
                // Capture the (start date of the site)
387
                site_date = dateFromString_doy_year<boost::gregorian::date, std::string>(siteOccurrence_.at(site).formatted_date);
9✔
388
                
389
#ifdef _MSDEBUG
390
                //date_str = stringFromDate(_it_discont->date_start, "%y:%j:00000");
391
#endif
392
                _it_discont_site = _it_discont;
9✔
393

394
                // find the next occurrence of this site
395
                while (boost::equals(site_name, _it_discont_site->site_name))
33✔
396
                {
397
                        // Test if the start epoch of this site is within this discontinuity window
398
                        if (site_date >= _it_discont_site->date_start &&
33✔
399
                                site_date < _it_discont_site->date_end)
33✔
400
//                        if (siteOccurrence_.at(site).solution_id == _it_discont_site->solution_id)
401
                        {
402
                                std::stringstream ss;
9✔
403
                                doy = _it_discont_site->date_start.day_of_year();
9✔
404
                                year = _it_discont_site->date_start.year();
9✔
405
                                ss << siteOccurrence_.at(site).site_name << "_";
9✔
406

407
                                // format date using the start of the window defining the 
408
                                // period up to which a discontinuity has been identified
409
                                ss << year;
9✔
410
                                if  (doy < 100)
9✔
411
                                        ss << "0";
×
UNCOV
412
                                if (doy < 10)
×
413
                                        ss << "0";
×
414
                                ss << doy;
9✔
415
                                
416
                                siteOccurrence_.at(site).formatted_name = ss.str();
9✔
417
                                //TRACE("Site %s\n", siteOccurrence_.at(site).formatted_name.c_str());
418
                                break;
9✔
419
                        }
9✔
420
                        _it_discont_site++;
24✔
421
                }
422
        }
423

424
        if (siteOccurrence_.size() != vStations->size() ||
3✔
425
                siteOccurrence_.size() != vMeasurements->at(0)->GetTotal())
3✔
426
        {
NEW
427
                std::stringstream ss;
×
428
                ss << "format_station_names(): Inconsistent number of stations in SINEX file.";
×
NEW
429
                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
430
        }
×
431

432
        _it_vsite_id_tuple _it_site(siteOccurrence_.begin());
3✔
433

434
        // sort on file index (default sort)
435
        std::sort(siteOccurrence_.begin(), siteOccurrence_.end(),
3✔
436
                CompareSiteTuples<site_id>());
437

438
        // Now update station vector
439
        for_each(vStations->begin(), vStations->end(),
3✔
440
                [&_it_site](const dnaStnPtr& stn) {
9✔
441
                        if (!boost::equals(_it_site->site_name, _it_site->formatted_name))
9✔
442
                        {
443
                                stn->SetOriginalName();
9✔
444
                                stn->SetName(_it_site->formatted_name);
9✔
445
                        }
446
                        _it_site++;
9✔
447
        });
9✔
448

449
        // re-initialise iterator
450
        _it_site = siteOccurrence_.begin();
3✔
451
        
452
        _it_vdnamsrptr _it_msr(vMeasurements->begin());
3✔
453
        std::vector<CDnaGpsPoint>* vgpsPnts(_it_msr->get()->GetPoints_ptr());
3✔
454

455
        // Now update measurement vector
456
        for_each(vgpsPnts->begin(), vgpsPnts->end(),
3✔
457
                [&_it_site](CDnaGpsPoint& pnt) {
9✔
458
                        if (!boost::equals(_it_site->site_name, _it_site->formatted_name))
9✔
459
                                pnt.SetFirst(_it_site->formatted_name);
9✔
460
                        _it_site++;
9✔
461
        });
9✔
462
}
3✔
463

464

465

466
        
467
void dna_io_snx::parse_sinex_block(std::ifstream** snx_file, const char* sinexRec, vdnaStnPtr* vStations, PUINT32 stnCount, 
35✔
468
                                        vdnaMsrPtr* vMeasurements, PUINT32 msrCount, PUINT32 clusterID,
469
                                        StnTally& parsestn_tally, MsrTally& parsemsr_tally, CDnaDatum& datum, 
470
                                        UINT32& fileOrder, UINT32& lineNo, UINT32& columnNo)
471
{
472
        // write comments
473
        if (strncmp(sinexRec, "+FILE/REFERENCE", 15) == 0 || 
35✔
474
                strncmp(sinexRec, "+SITE/RECEIVER", 14) == 0 || 
35✔
475
                strncmp(sinexRec, "+SITE/ANTENNA", 13) == 0 || 
35✔
476
                strncmp(sinexRec, "+SITE/GPS_PHASE_CENTER", 22) == 0 || 
35✔
477
                strncmp(sinexRec, "+SITE/ECCENTRICITY", 18) == 0)
35✔
478
        {
479
                // nothing to do.
480
                return;
481
        }
482

483
        // According to the SINEX standard:
484
        // - BLOCKS start with "+" in the first column followed by standardized block labels.
485
        // - Each BLOCK ends with "-" and the block label. 
486
        // - BLOCK data starts in column 2 or higher. 
487
        // - BLOCKS can be in any order, provided that they start with (+) and 
488
        //   end with (-) BLOCK labels.
489
        //
490
        // Therefore, don't assume the SOLUTION/* block order is followed.
491
        // However, assume SITE/ID comes first, so that discontinuities can be parsed in an
492
        // intelligent way.
493

494
        if (strncmp(sinexRec, "+SITE/ID", 8) == 0)
35✔
495
        {
496
                // OK, found general site list
497
                //parse_sinex_sites(snx_file, lineNo, columnNo);
498
                return;
499
        }
500

501
        if (strncmp(sinexRec, "+SOLUTION/EPOCHS", 16) == 0)
28✔
502
        {
503
                // OK, found epochs associated with stations
504
                parse_sinex_epochs(snx_file, lineNo, columnNo);
7✔
505
                return;
7✔
506
        }
507

508
        if (strncmp(sinexRec, "+SOLUTION/ESTIMATE", 18) == 0)
21✔
509
        {
510
                // OK, found some stations
511
                parse_sinex_stn(snx_file, sinexRec, vStations, stnCount,
7✔
512
                        parsestn_tally, datum, fileOrder, lineNo, columnNo);
513
                return;
7✔
514
        }
515

516
        if (strncmp(sinexRec, "+SOLUTION/MATRIX_ESTIMATE", 25) == 0)
14✔
517
        {
518
                // Ok, found some measurements
519
                parse_sinex_msr(snx_file, sinexRec, vStations, vMeasurements, clusterID, msrCount,
7✔
520
                        parsemsr_tally, datum, lineNo);
521
                return;
7✔
522
        }
523
}
524
        
525

526
void dna_io_snx::parse_sinex_discontinuities(std::ifstream* snx_file, 
4✔
527
                                        v_discontinuity_tuple* stn_discontinuities, bool& m_discontsSortedbyName,
528
                                        UINT32& lineNo, UINT32& columnNo)
529
{
530
        std::string sBuf("+"), stn, stn_prev(""), model;
4✔
531
        UINT32 file_rec(0), solution_id;
4✔
532
        boost::gregorian::date discont_start_date, discont_end_date;
4✔
533

534
        std::stringstream ss, discont_from, discont_to;
4✔
535

536
        stn_discontinuities->clear();
4✔
537
        m_discontsSortedbyName = false;
4✔
538

539
        while (sBuf.at(0) != '-')
19,040✔
540
        {
541
                lineNo++;
19,040✔
542
                getline((*snx_file), sBuf);
19,040✔
543

544
                // A comment
545
                if (sBuf.at(0) == '*')
19,040✔
546
                        continue;
4✔
547

548
                if (sBuf.at(0) == '-')
19,036✔
549
                        break;
550

551
                try {
19,032✔
552
                        // station
553
                        stn = trimstr(sBuf.substr(1, 4));                                // station name
38,064✔
554

555
                        // solution ID
556
                        solution_id = val_uint<UINT32, std::string>(
19,032✔
557
                                trimstr(sBuf.substr(9, 4)));                                // solution id
38,064✔
558

559
                        // model
560
                        // Commonly used models for time series analysis are:
561
                        // "M" Models Codes for coordinates time series Analysis:
562
                        //      P = Position
563
                        //            V = Velocity
564
                        //            A = Annual Periodicity
565
                        //            S = Semi-Annual Periodicity
566
                        //            E = Exponential Decay
567
                        //            X = Exclude/Delete
568
                        model = trimstr(sBuf.substr(42, 1));                                // model
38,064✔
569

570
                        // Is this a discontinuity in position?  If not, continue to next
571
                        if (!boost::iequals(model, "P"))
19,032✔
572
                                continue;
5,432✔
573

574
                        // If there are multiple solutions, then there must be a discontinuity, but only if
575
                        // the model is the same.  For this version of DynAdjust, work with position only (i.e. disregard velocity)
576
                        if (solution_id > 1)
13,600✔
577
                                containsDiscontinuities_ = true;
8,952✔
578

579
                        ////////////////////////////////////////////////////////////////////////////////////////////
580
                        // Dates FROM and TO define the temporal extents of time series divided by discontinuities.
581
                        // To understand how to apply these dates, the following rules apply:
582
                        //   - If FROM and TO both equal 00:000:00000, there is no known (or recorded) discontinuity 
583
                        //     at this site (in which case, this site does not need renaming)
584
                        //   - If FROM equals 00:000:00000 and TO is a realistic date, then this is the first leg
585
                        //     in a sequence of time series, in which case solution_id should equal 1
586
                        //   - If FROM and TO are realistic dates, then this is an intermediate leg in a sequence
587
                        //   - If FROM is a realistic date and TO equals 00:000:00000, then this is the last leg in
588
                        //     the sequence of time series.
589

590
                        // start epoch (yy:doy:sssss)
591
                        discont_from = parse_date_from_string(trimstr(sBuf.substr(16, 6)), doy_yyyy, date_from, std::string(" "));
54,400✔
592
                        discont_start_date = dateFromStringstream_doy_year<boost::gregorian::date, std::stringstream>(discont_from);
13,600✔
593

594
                        // end epoch (yy:doy:sssss)
595
                        discont_to = parse_date_from_string(trimstr(sBuf.substr(29, 6)), doy_yyyy, date_to, std::string(" "));
54,400✔
596
                        discont_end_date = dateFromStringstream_doy_year<boost::gregorian::date, std::stringstream>(discont_to);
13,600✔
597

598
                        ////////////////////////////////////////////////////////////////////////////////////////////
599

600
                        stn_discontinuities->push_back(
13,600✔
601
                                discontinuity_tuple_t<UINT32, std::string, boost::gregorian::date, bool>(
13,600✔
602
                                file_rec,                        // file_index 
603
                                solution_id,                // solution_id
604
                                stn,                                // site_name
605
                                discont_start_date,        // discontinuity start
606
                                discont_end_date,        // discontinuity end
607
                                false)                                 // discontinuity_exists
608
                                );
609

610
                        file_rec++;
13,600✔
611
                }
612
                catch (...) {
×
613
                        ss.str("");
×
614
                        columnNo = 1;
×
NEW
615
                        ss << "parse_sinex_discontinuities(): Could not extract station name from the record:  " << std::endl << "    " << sBuf << ".";
×
NEW
616
                        throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
UNCOV
617
                }                        
×
618
        }
619

620
        // No discontinuities to handle?
621
        if (!containsDiscontinuities_)
4✔
622
                return;        
×
623

624
        // sort the station discontinuities on station name
625
        std::sort(stn_discontinuities->begin(), stn_discontinuities->end(), 
4✔
626
                CompareSiteTuplesByName<discontinuity_tuple>());
627
        
628
        // Identify if discontinuity exists (by equal station names) and 
629
        // set flag to indicate that this station is a known discontinuity station
630
        _it_vdiscontinuity_tuple it_discont, it_discont_next;
4✔
631
        for (it_discont = stn_discontinuities->begin();
13,600✔
632
                it_discont != stn_discontinuities->end(); it_discont++)
13,600✔
633
        {
634
                it_discont_next =  it_discont + 1;
13,600✔
635

636
                if (it_discont_next == stn_discontinuities->end())
13,600✔
637
                        break;
638
                
639
                // Are station names equal?
640
                if (boost::equals(it_discont->site_name, it_discont_next->site_name))
13,596✔
641
                {
642
                        // Then this is a discontinuity site!
643
                        it_discont->discontinuity_exists = true;
8,956✔
644
                        it_discont_next->discontinuity_exists = true;
8,956✔
645
                }
646
        }
647

648
        // re-sort the station discontinuities back to the original file order
649
        std::sort(stn_discontinuities->begin(), stn_discontinuities->end(),
4✔
650
                CompareSiteTuples<discontinuity_tuple>());
651
        m_discontsSortedbyName = false;
4✔
652
}
20✔
653
        
654

655
// Read sites
656
void dna_io_snx::parse_sinex_sites(std::ifstream** snx_file, UINT32& lineNo, UINT32& columnNo)
×
657
{
NEW
658
        std::string sBuf("+"), stn, domes;
×
659
        
660
        // Has the SOLUTION/EPOCHS block already been read?  If so, no need
661
        // to continue with this block - just skip to the end.
662
        if (solutionEpochsRead_)
×
663
        {
664
                // Skip to the end of the SITE/ID block
665
                while (sBuf.at(0) != '-')
×
666
                {
667
                        lineNo++;
×
668
                        getline((**snx_file), sBuf);
×
669
                }
670
                siteIDsRead_ = true;
×
671
                return;
×
672
        }
673
        
674
        siteOccurrence_.clear();
×
675

676
        UINT32 file_rec(0);
×
NEW
677
        std::stringstream ss;
×
678

679
        while (sBuf.at(0) != '-')
×
680
        {
681
                lineNo++;
×
682
                getline((**snx_file), sBuf);
×
683

684
                // A comment
685
                if (sBuf.at(0) == '*')
×
686
                        continue;
×
687

688
                if (sBuf.at(0) == '-')
×
689
                        break;
690

691
                try {
×
692
                        // station
693
                        stn = trimstr(sBuf.substr(1, 4));
×
694

695
                        // domes
696
                        domes = trimstr(sBuf.substr(9, 9));
×
697

698
                        // set default values (amended later)
699
                        siteOccurrence_.push_back(
×
NEW
700
                                site_id_tuple_t<UINT32, std::string, bool>(
×
701
                                        file_rec,                // file_index, 
702
                                        1,                                // solution_id (set to 1 since SITE/ID doesn't hold solution_id)
×
703
                                        stn,                        // station name
704
                                        stn,                        // formatted_name
705
                                        "",                                // formatted_date
706
                                        false)                         // false (amended later to be true if last occurrence)                        
707
                                );
708

709
                        file_rec++;
×
710
                }
711
                catch (...) {
×
712
                        ss.str("");
×
713
                        columnNo = 1;
×
NEW
714
                        ss << "parse_sinex_discontinuities(): Could not extract station name from the record:  " << std::endl << "    " << sBuf << ".";
×
NEW
715
                        throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
UNCOV
716
                }                        
×
717
        }
718

719
        siteIDsRead_ = true;
×
720

721
        reduce_sinex_sites();
×
722
}
×
723
        
724

725
// Read station epochs
726
void dna_io_snx::parse_sinex_epochs(std::ifstream** snx_file, UINT32& lineNo, UINT32& columnNo)
7✔
727
{
728
        std::string sBuf("+"), stn, site, date_start;
7✔
729
        UINT32 file_rec(0), solution_id;
7✔
730

731
        std::stringstream ss;
7✔
732

733
        if (!siteIDsRead_)
7✔
734
                siteOccurrence_.clear();
7✔
735

736

737
        while (sBuf.at(0) != '-')
35✔
738
        {
739
                lineNo++;
35✔
740
                getline((**snx_file), sBuf);
35✔
741

742
                // A comment
743
                if (sBuf.at(0) == '*')
35✔
744
                        continue;
7✔
745

746
                if (sBuf.at(0) == '-')
28✔
747
                        break;
748

749
                try {
21✔
750
                        // station
751
                        stn = trimstr(sBuf.substr(1, 4));
42✔
752
                        // solution ID
753
                        solution_id = val_uint<UINT32, std::string>(trimstr(sBuf.substr(9, 4)));
42✔
754
                        // Get start epoch (yy:doy:sssss) of data window used to process this site
755
                        date_start = parse_date_from_string(trimstr(sBuf.substr(16, 6)), doy_yyyy, date_from, std::string(" ")).str();
126✔
756
                }
757
                catch (...) {
×
758
                        ss.str("");
×
759
                        columnNo = 1;
×
NEW
760
                        ss << "parse_sinex_epochs(): Could not extract station name from the record:  " << std::endl << "    " << sBuf << ".";
×
NEW
761
                        throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
UNCOV
762
                }
×
763

764
                // Perform some file consistency checks
765
                if (siteIDsRead_)
21✔
766
                {
767
                        try {
×
768
                                // Is the count the same?  This will cause an exception of file_rec is equal to or greater than 
769
                                // siteOccurrence_.size()
770
                                site = siteOccurrence_.at(file_rec).site_name;
×
771
                        }
772
                        catch (...) {
×
773
                                ss.str("");
×
774
                                columnNo = 1;
×
NEW
775
                                ss << "parse_sinex_epochs(): The number of sites in SITE/ID and SOLUTION/EPOCHS" << std::endl <<
×
NEW
776
                                        "    is inconsistent:  " << std::endl << 
×
NEW
777
                                        "    " << "SITE/ID block has " << siteOccurrence_.size() << " sites" << std::endl <<
×
NEW
778
                                        "    " << "SOLUTION/EPOCHS block contains additional sites not listed in SITE/ID.  Next record is:" << std::endl <<
×
779
                                        "    " << sBuf << ".";
×
NEW
780
                                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
781
                        }
×
782
                
NEW
783
                        if (!boost::equals(site, stn))
×
784
                        {
785
                                ss.str("");
×
786
                                columnNo = 1;
×
NEW
787
                                ss << "parse_sinex_epochs(): The order of sites in SITE/ID and SOLUTION/EPOCHS" << std::endl <<
×
NEW
788
                                        "    is inconsistent:  " << std::endl << 
×
NEW
789
                                        "    " << "Index " << file_rec << " in SITE/ID block is " << siteOccurrence_.at(file_rec).site_name << std::endl <<
×
790
                                        "    " << "Index " << file_rec << " in SOLUTION/EPOCHS block is " << stn << ".";
×
NEW
791
                                throw boost::enable_current_exception(std::runtime_error(ss.str()));                        
×
792
                        }
793
                }
794

795
                try {        
21✔
796
                        // Has the SITE/ID block been read before the SOLUTION/EPOCHS block?  
797
                        // If so, simply update the records
798
                        if (siteIDsRead_)
21✔
799
                        {
800
                                siteOccurrence_.at(file_rec).solution_id = solution_id;
×
801
                                // The mean date is only necessary for handling discontinuities
802
                                siteOccurrence_.at(file_rec).formatted_date = date_start;        
×
803
                        }
804
                        else
805
                        {
806
                                // Create new elements and add to the vector
807
                                siteOccurrence_.push_back(
42✔
808
                                        site_id_tuple_t<UINT32, std::string, bool>(
42✔
809
                                        file_rec,                // file_index, 
810
                                        solution_id,        // solution_id
811
                                        stn,                        // station name
812
                                        stn,                        // formatted_name
813
                                        date_start,                // formatted_date
814
                                        false)                         // false (amended later to be true if last occurrence)                        
815
                                        );
816
                        }                        
817

818
                        file_rec++;
21✔
819
                }
820
                catch (...) {
×
821
                        ss.str("");
×
822
                        columnNo = 1;
×
NEW
823
                        ss << "parse_sinex_epochs(): Failed to add a new record to the site occurrence list." << std::endl <<
×
824
                                "    " << sBuf << ".";
×
NEW
825
                        throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
826
                }
×
827
        }
828

829
        solutionEpochsRead_ = true;
7✔
830

831
        if (!siteIDsRead_)
7✔
832
                reduce_sinex_sites();
7✔
833
}
35✔
834
        
835
void dna_io_snx::reduce_sinex_sites()
7✔
836
{
837
        // sort the sites on station name
838
        std::sort(siteOccurrence_.begin(), siteOccurrence_.end(), 
7✔
839
                CompareSiteTuplesByName<site_id>());
840

841
        // Identify if discontinuity exists from internal evidence (i.e. equal station names).  If so,
842
        // increment the station occurrence to indicate that this station is duplicated because
843
        // of discontinuity.
844
        _it_vsite_id_tuple it_site, it_site_next;
7✔
845
        for (it_site = siteOccurrence_.begin();
21✔
846
                it_site != siteOccurrence_.end(); it_site++)
21✔
847
        {
848
                it_site_next =  it_site + 1;
21✔
849

850
                if (it_site_next == siteOccurrence_.end())
21✔
851
                {
852
                        // this is the last occurrence
853
                        it_site->last_occurrence = true;
7✔
854
                        break;
7✔
855
                }
856

857
                // if this station occurs again:
858
                //   - increment the next occurrence of it
859
                //   - set the index of the first occurrence
860
                if (boost::equals(it_site->site_name, it_site_next->site_name))
14✔
861
                {
862
                        // increment next occurrence.  Note, if SOLUTION/EPOCHS block has been read, 
863
                        // this will have already been set
864
                        if (!solutionEpochsRead_)
×
865
                                it_site_next->solution_id = it_site->solution_id + 1;
×
866

867
                        // If this code is reached, then there must be a discontinuities in the file!
868
                        containsDiscontinuities_ = true;
×
869
                }
870
                else
871
                        // this is the last occurrence
872
                        it_site->last_occurrence = true;
14✔
873

874
        }
875

876
        // sort on file index (default sort)
877
        std::sort(siteOccurrence_.begin(), siteOccurrence_.end(),
7✔
878
                CompareSiteTuples<site_id>());
879
}
7✔
880
        
881
void dna_io_snx::parse_sinex_stn(std::ifstream** snx_file, const char* sinexRec, vdnaStnPtr* vStations, PUINT32 stnCount,
7✔
882
                                        StnTally& parsestn_tally, CDnaDatum& datum,
883
                                        UINT32& fileOrder, UINT32& lineNo, UINT32& columnNo)
884
{
885
        std::stringstream ss;
7✔
886

887
        if (applyDiscontinuities_ && !solutionEpochsRead_)
7✔
888
        {
889
                ss.str("");
×
NEW
890
                ss << "parse_sinex_stn(): Cannot apply discontinuities to the stations" << std::endl <<
×
NEW
891
                        "    if the station epochs have not been loaded beforehand.  To rectify this problem," << std::endl <<
×
892
                        "    reformat the SINEX file so that the +SOLUTION/EPOCHS block appears before the +SOLUTION/ESTIMATE block.";
×
NEW
893
                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
894
        }
895

896
        std::string sBuf(sinexRec), site, tmp, stn;
7✔
897

898
        dnaStnPtr stn_ptr;        
7✔
899
        UINT32 yy, doy, file_rec(0);
7✔
900

901
        fileOrder = 0;
7✔
902
        uniqueStationCount_ = 0;
7✔
903

904
        while (sBuf.at(0) != '-')
77✔
905
        {
906
                lineNo++;
77✔
907

908
                getline((**snx_file), sBuf);
77✔
909

910
                // A comment
911
                if (sBuf.at(0) == '*')
77✔
912
                        continue;
7✔
913
                
914
                if (sBuf.at(0) == '-')
70✔
915
                        break;
916

917
                if (sBuf.substr(7, 3) == "VEL")
126✔
918
                        containsVelocities_ = true;
×
919

920
                if (sBuf.substr(7, 3) != "STA")
126✔
921
                        continue;
×
922

923
                // Capture the X coordinate
924
                if (sBuf.substr(7, 4) == "STAX")
126✔
925
                {
926
                        try {
21✔
927
                                // get the measurement station name (will be 4 characters)
928
                                stn = trimstr(sBuf.substr(14, 4));
42✔
929

930
                                // reset CDnaStation instance (stn_ptr) with new station name
931
                                stn_ptr.reset(new CDnaStation(
21✔
932
                                        stn,                                                                        // station name
933
                                        "FFF",                                                                        // Constraints
934
                                        XYZ_type, 0.0, 0.0, 0.0, 0.0, "",                // Type, X, Y, Z, Height, HemisphereZOne
42✔
935
                                        trimstr(sBuf.substr(14, 4)),                        // description
63✔
936
                                        ss.str()));                                                                // comment
105✔
937

938
                                stn_ptr->SetReferenceFrame(datum.GetName());
21✔
939
                                stn_ptr->SetEpsg(datum.GetEpsgCode_s());
21✔
940

941
                                yy = LongFromString<UINT32>(sBuf.substr(27, 2)), 
21✔
942
                                doy = LongFromString<UINT32>(sBuf.substr(30, 3)), 
42✔
943
                                ss = parse_date_from_yy_doy(yy, doy, doy_yyyy, std::string(" "));
42✔
944
                                stn_ptr->SetEpoch(stringFromDate(dateFromStringstream_doy_year<boost::gregorian::date, std::stringstream>(ss)));
21✔
945

946
                                stn_ptr->SetfileOrder(fileOrder++);
21✔
947
                                stn_ptr->SetXAxis_d(DoubleFromString<double>(trimstr(sBuf.substr(47, 21))));
42✔
948
                                stn_ptr->SetXAxisStdDev_d(DoubleFromString<double>(trimstr(sBuf.substr(68, 12))));
42✔
949
                        }
NEW
950
                        catch (const std::runtime_error& f) {
×
951
                                ss.str("");
×
952
                                ss << "  - line " << lineNo;
×
NEW
953
                                ss << ", column " <<  columnNo << std::endl;
×
954
                                ss << "  - " << f.what();
×
NEW
955
                                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
956
                        }
×
957
                        catch (...) {
×
958
                                ss.str("");
×
959
                                columnNo = 47;
×
NEW
960
                                ss << "parse_sinex_stn(): Could not extract X coordinate estimate from the record:  " << std::endl << "    " << sBuf << ".";
×
NEW
961
                                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
UNCOV
962
                        }
×
963
                        continue;
21✔
964
                }
21✔
965

966
                // Perform some file consistency checks
967
                try {
42✔
968
                        // Is the count the same?  This will cause an exception of file_rec is equal to or greater than 
969
                        // siteOccurrence_.size()
970
                        site = siteOccurrence_.at(file_rec).site_name;
42✔
971
                }
972
                catch (...) {
×
973
                        ss.str("");
×
974
                        columnNo = 1;
×
NEW
975
                        ss << "parse_sinex_stn(): The number of sites in SOLUTION/EPOCHS and SOLUTION/ESTIMATE" << std::endl <<
×
NEW
976
                                "    is inconsistent:  " << std::endl << 
×
NEW
977
                                "    " << "SOLUTION/EPOCHS block has " << siteOccurrence_.size() << " sites" << std::endl <<
×
NEW
978
                                "    " << "SOLUTION/ESTIMATE block contains additional sites not listed in SOLUTION/EPOCHS.  Next record is:" << std::endl <<
×
979
                                "    " << sBuf << ".";
×
NEW
980
                        throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
981
                }
×
982

983
                if (!boost::equals(site, stn))
42✔
984
                {
985
                        ss.str("");
×
986
                        columnNo = 1;
×
NEW
987
                        ss << "parse_sinex_stn(): The order of sites in SOLUTION/EPOCHS and SOLUTION/ESTIMATE" << std::endl <<
×
NEW
988
                                "    is inconsistent:  " << std::endl << 
×
NEW
989
                                "    " << "Index " << file_rec << " in SOLUTION/EPOCHS block is " << siteOccurrence_.at(file_rec).site_name << std::endl <<
×
990
                                "    " << "Index " << file_rec << " in SOLUTION/ESTIMATE block is " << stn << ".";
×
NEW
991
                        throw boost::enable_current_exception(std::runtime_error(ss.str()));                        
×
992
                }
993

994
                // Capture the Y coordinate
995
                if (sBuf.substr(7, 4) == "STAY")
84✔
996
                {
997
                        try {
21✔
998
                                stn_ptr->SetYAxis_d(DoubleFromString<double>(trimstr(sBuf.substr(47, 21))));
42✔
999
                                stn_ptr->SetYAxisStdDev_d(DoubleFromString<double>(trimstr(sBuf.substr(68, 12))));
42✔
1000
                        }
1001
                        catch (...) {
×
1002
                                ss.str("");
×
1003
                                columnNo = 47;
×
NEW
1004
                                ss << "parse_sinex_stn(): Could not extract Y coordinate estimate from the record:  " << std::endl << "    " << sBuf << ".";
×
NEW
1005
                                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
UNCOV
1006
                        }
×
1007
                        continue;
21✔
1008
                }
1009

1010
                // Capture the Z coordinate
1011
                if (sBuf.substr(7, 4) == "STAZ")
42✔
1012
                {
1013
                        try {
21✔
1014
                                stn_ptr->SetZAxis_d(DoubleFromString<double>(trimstr(sBuf.substr(47, 21))));
42✔
1015
                                stn_ptr->SetZAxisStdDev_d(DoubleFromString<double>(trimstr(sBuf.substr(68, 12))));
42✔
1016
                        }
1017
                        catch (...) {
×
1018
                                ss.str("");
×
1019
                                columnNo = 47;
×
NEW
1020
                                ss << "parse_sinex_stn(): Could not extract Z coordinate estimate from the record:  " << std::endl << "    " << sBuf << ".";
×
NEW
1021
                                throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
UNCOV
1022
                        }                                
×
1023

1024
                        // Add this station to the vStations vector
1025
                        (*stnCount)++;
21✔
1026
                        file_rec++;
21✔
1027
                        vStations->push_back(stn_ptr);
21✔
1028
                        parsestn_tally.addstation("FFF");
42✔
1029
                }                        
1030
        }
1031
        
1032
        // override header epoch (which is really only the start epoch of the data used) with the epoch from
1033
        // the estimates. As per the SINEX standard, this is the epoch at which the estimated parameters are valid.
1034
        if (!vStations->empty())
7✔
1035
                datum.SetEpoch(vStations->at(0)->GetEpoch());
14✔
1036

1037
        // Do we need to remove sites in the case of unwanted discontinuities?
1038
        if (containsDiscontinuities_ && !applyDiscontinuities_)
7✔
1039
        {
1040
                // Go in reverse direction so that stnCount can be used
1041
                for (ptrdiff_t i = static_cast<ptrdiff_t>(*stnCount); i>0; --i)
×
1042
                {
1043
                        if (!siteOccurrence_.at(i-1).last_occurrence)
×
1044
                        {
1045
                                vStations->erase(vStations->begin() + (i - 1));
×
1046
                                parsestn_tally.removestation("FFF");
×
1047
                        }
1048
                }
1049
                (*stnCount) = static_cast<UINT32>(vStations->size());
×
1050
        }
1051
}
35✔
1052
        
1053

1054
void dna_io_snx::parse_sinex_msr(std::ifstream** snx_file, const char* sinexRec, vdnaStnPtr* vStations, vdnaMsrPtr* vMeasurements, PUINT32 clusterID, PUINT32 msrCount,
7✔
1055
                                        MsrTally& parsemsr_tally, CDnaDatum& datum, UINT32& lineNo)
1056
{
1057
        std::stringstream ss;
7✔
1058

1059
        if (applyDiscontinuities_ && !solutionEpochsRead_)
7✔
1060
        {
1061
                ss.str("");
×
NEW
1062
                ss << "parse_sinex_msr(): Cannot apply discontinuities to the measurements" << std::endl <<
×
NEW
1063
                        "    if the station epochs have not been loaded beforehand.  To rectify this problem," << std::endl <<
×
1064
                        "    reformat the SINEX file so that the +SOLUTION/EPOCHS block appears before the +SOLUTION/ESTIMATE block.";
×
NEW
1065
                        throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
1066
        }
1067

1068
        std::string sBuf(sinexRec), tmp;
7✔
1069
        parsemsr_tally.Y = static_cast<UINT32>(vStations->size() * 3);
7✔
1070
        (*msrCount) = static_cast<UINT32>(vStations->size() * 3);
7✔
1071

1072
        UINT32 i, count, param1, param2, dimension(static_cast<UINT32>(vStations->size() * 3));
7✔
1073
        double dparam[3];
7✔
1074

1075
        UINT32 dimension_all = dimension;
7✔
1076

1077
        if (containsDiscontinuities_)
7✔
1078
                dimension_all = static_cast<UINT32>(siteOccurrence_.size() * 3);
×
1079

1080
        if (containsVelocities_)
7✔
1081
                dimension_all *= 2;
×
1082

1083
        // If this file contains discontinuity sites and the user wishes to retain them,
1084
        // then the size of the variance matrix will be the size of the stn discontinuity
1085
        // vector, which was parsed in parse_sinex_discontinuities(...)
1086
        if (containsDiscontinuities_ && applyDiscontinuities_)
7✔
1087
                dimension = static_cast<UINT32>(siteOccurrence_.size() * 3);
×
1088

1089
        // default covariance matrix assumes 
1090
        matrix_2d covariance_all(dimension_all, dimension_all);
7✔
1091
        char cBuf[MAX_RECORD_LENGTH];
1092
        char* p;
1093

1094
        // Get all covariances (whether discontinuities or velocities exist or not) and store
1095
        // in covariance_all.  The unwanted elements will be stripped later
1096
        while (sBuf.at(0) != '-')
140✔
1097
        {
1098
                lineNo++;
140✔
1099
                (*snx_file)->getline(cBuf, MAX_RECORD_LENGTH);
140✔
1100

1101
                // Comments
1102
                if (cBuf[0] == '*')
140✔
1103
                        continue;
7✔
1104
                
1105
                if (cBuf[0] == '-')
133✔
1106
                        break;
1107

1108
                p = cBuf;
1109
                while (*p == ' ')
756✔
1110
                        p++;
630✔
1111

1112
                if ((count = GetFields(p, ' ', true, "ddfff", &param1, &param2, &dparam[0], &dparam[1], &dparam[2])) < 3)
126✔
1113
                {
1114
                        ss.str("");
×
1115
                        ss << "parse_sinex_msr: Failed to read covariance elements from the record  " << cBuf << ".";
×
NEW
1116
                        throw boost::enable_current_exception(std::runtime_error(ss.str()));
×
1117
                }
1118

1119
                // if the number of covariance elements has reached the number of stations, break out.
1120
                if (param1 > dimension_all)
126✔
1121
                        break;
1122

1123
                // Fill covariance matrix.
1124
                for (i=0; i<(count-2); i++)
441✔
1125
                        covariance_all.put(param1-1, param2-1+i, dparam[i]);                        
315✔
1126
        }
1127

1128
        // Remember, DynAdjust requires the upper diagonal part
1129
        if (sinexRec[26] == LOWER_TRIANGLE)
7✔
1130
                covariance_all.fillupper();
7✔
1131

1132
        matrix_2d covariance(dimension, dimension);
7✔
1133

1134
        UINT32 stn_discont;
7✔
1135
        
1136
        // At this point, velocities and/or discontinuities may be present in covariance_all.
1137
        // Hence, the purpose of this block is to remove:
1138
        //        1. velocity elements (DynAdjust doesn't handle velocities yet)
1139
        //  2. discontinuities if they exist and the user doesn't want them (on account of not 
1140
        //     providing a discontinuities file)
1141
        UINT32 row_v, col_v, row, col;
7✔
1142
        if (containsVelocities_ || (containsDiscontinuities_ && !applyDiscontinuities_))
7✔
1143
        {
1144
                for (row_v=0, row=0; row_v<dimension_all; ++row_v)
×
1145
                {
1146
                        // Do we need to skip this site (in the case of unwanted discontinuities?
1147
                        if (containsDiscontinuities_ && !applyDiscontinuities_)
×
1148
                        {
1149
                                stn_discont = (UINT32)floor(row_v / 3.);
×
1150
                                if (containsVelocities_)
×
1151
                                        stn_discont /= 2;
×
1152

1153
                                // Is this the last occurrence?  If not, skip it
1154
                                if (!siteOccurrence_.at(stn_discont).last_occurrence)
×
1155
                                {
1156
                                        // skip to z component
1157
                                        row_v += 2;
×
1158
                                        // If this site also includes velocities, then skip to 
1159
                                        // the z component of the velocity
1160
                                        if (containsVelocities_)
×
1161
                                                row_v += 3;
×
1162
#ifdef _MSDEBUG
1163
                                        //TRACE("Skip to row:   %d\n", row_v+1);
1164
#endif
1165
                                        continue;
×
1166
                                }
1167
#ifdef _MSDEBUG
1168
                                //else
1169
                                        //TRACE("Keep this row: %d\n", row_v);
1170
#endif
1171
                        }
1172

1173
                        // At this point, unnecessary rows (discontinuity sites with or without velocities) have been skipped
1174
                        // Now copy the data
1175
                        // X row
1176
                        for (col_v=row_v, col=row; col_v<dimension_all; ++col_v)
×
1177
                        {
1178
                                // Do we need to skip this site (in the case of unwanted discontinuities?
1179
                                if (containsDiscontinuities_ && !applyDiscontinuities_)
×
1180
                                {
1181
                                        stn_discont = (UINT32)floor((col_v+1) / 3.);
×
1182
                                        if (containsVelocities_)
×
1183
                                                stn_discont /= 2;
×
1184
                                        
1185
                                        // Is this the last occurrence?  If not, skip it
1186
                                        if (!siteOccurrence_.at(stn_discont).last_occurrence)
×
1187
                                        {
1188
                                                // skip to z component
1189
                                                col_v += 2;
×
1190
                                                // If this site also includes velocities, then skip to 
1191
                                                // the z component of the velocity
1192
                                                if (containsVelocities_)
×
1193
                                                        col_v += 3;
×
1194
                                                continue;
×
1195
                                        }
1196
                                }
1197

1198
                                covariance.put(row, col++, covariance_all.get(row_v, col_v++));
×
1199
                                covariance.put(row, col++, covariance_all.get(row_v, col_v++));
×
1200
                                covariance.put(row, col++, covariance_all.get(row_v, col_v));
×
1201

1202
                                // skip velocity elements
1203
                                if (containsVelocities_)
×
1204
                                        col_v += 3;
×
1205
                        }
1206

1207
                        row_v++;
×
1208
                        row++;
×
1209

1210

1211
                        // Y row
1212
                        for (col_v=row_v, col=row; col_v<dimension_all; ++col_v)
×
1213
                        {
1214
                                // Do we need to skip this site (in the case of unwanted discontinuities?
1215
                                if (containsDiscontinuities_ && !applyDiscontinuities_)
×
1216
                                {
1217
                                        stn_discont = (UINT32)floor((col_v+1) / 3.);
×
1218
                                        if (containsVelocities_)
×
1219
                                                stn_discont /= 2;
×
1220

1221
                                        // Is this the last occurrence?  If not, skip it
1222
                                        if (!siteOccurrence_.at(stn_discont).last_occurrence)
×
1223
                                        {
1224
                                                // skip to z component
1225
                                                col_v += 2;
×
1226
                                                // If this site also includes velocities, then skip to 
1227
                                                // the z component of the velocity
1228
                                                if (containsVelocities_)
×
1229
                                                        col_v += 3;
×
1230
                                                continue;
×
1231
                                        }
1232
                                }
1233

1234
                                covariance.put(row, col++, covariance_all.get(row_v, col_v++));
×
1235
                                covariance.put(row, col++, covariance_all.get(row_v, col_v));
×
1236
                                if (col-row > 2)
×
1237
                                        covariance.put(row, col++, covariance_all.get(row_v, ++col_v));
×
1238
                                
1239
                                // skip velocity elements
1240
                                if (containsVelocities_)
×
1241
                                        col_v += 3;
×
1242
                        }
1243

1244
                        row_v++;
×
1245
                        row++;
×
1246

1247
                        // Z row
1248
                        for (col_v=row_v, col=row; col_v<dimension_all; ++col_v)
×
1249
                        {
1250
                                // Do we need to skip this site (in the case of unwanted discontinuities?
1251
                                if (containsDiscontinuities_ && !applyDiscontinuities_)
×
1252
                                {
1253
                                        stn_discont = (UINT32)floor((col_v+1) / 3.);
×
1254
                                        if (containsVelocities_)
×
1255
                                                stn_discont /= 2;
×
1256

1257
                                        // Is this the last occurrence?  If not, skip it
1258
                                        if (!siteOccurrence_.at(stn_discont).last_occurrence)
×
1259
                                        {
1260
                                                // skip to z component
1261
                                                col_v += 2;
×
1262
                                                // If this site also includes velocities, then skip to 
1263
                                                // the z component of the velocity
1264
                                                if (containsVelocities_)
×
1265
                                                        col_v += 3;
×
1266
                                                continue;
×
1267
                                        }
1268
                                }
1269

1270
                                covariance.put(row, col++, covariance_all.get(row_v, col_v));
×
1271
                                if (col-row > 1)
×
1272
                                        covariance.put(row, col++, covariance_all.get(row_v, ++col_v));
×
1273
                                if (col-row > 2)
×
1274
                                        covariance.put(row, col++, covariance_all.get(row_v, ++col_v));
×
1275
                        
1276
                                // skip velocity elements
1277
                                if (containsVelocities_)
×
1278
                                        col_v += 3;
×
1279
                        }
1280

1281
                        // skip velocity elements
1282
                        if (containsVelocities_)
×
1283
                                row_v += 3;
×
1284
                        row++;
×
1285
                }
1286
        }
1287
        else
1288
                covariance = covariance_all;
7✔
1289

1290
        dnaMsrPtr dnaGpsPoint;
7✔
1291
        dnaMsrPtr dnaGpsPointCluster;
7✔
1292
        dnaCovariancePtr dnaPointCovariance;
7✔
1293

1294
        dnaGpsPointCluster.reset(new CDnaGpsPointCluster(++(*clusterID), datum.GetName(), datum.GetEpoch_s()));
21✔
1295

1296
        dnaGpsPointCluster->SetFirst(vStations->at(0)->GetName());
7✔
1297
        dnaGpsPointCluster->SetTarget("");
14✔
1298
        dnaGpsPointCluster->SetIgnore(false);
7✔
1299
        dnaGpsPointCluster->SetTotal(static_cast<UINT32>(vStations->size()));
7✔
1300
        dnaGpsPointCluster->SetCoordType(XYZ_type);
14✔
1301
        dnaGpsPointCluster->SetPscale(1.);
7✔
1302
        dnaGpsPointCluster->SetLscale(1.);
7✔
1303
        dnaGpsPointCluster->SetHscale(1.);
7✔
1304
        dnaGpsPointCluster->SetVscale(1.);
7✔
1305

1306
        dnaGpsPointCluster->SetReferenceFrame(datum.GetName());
7✔
1307
        dnaGpsPointCluster->SetEpsg(datum.GetEpsgCode_s());
7✔
1308
        dnaGpsPointCluster->SetEpoch(vStations->at(0)->GetEpoch());
7✔
1309

1310
        UINT32 cov_count, ci;
7✔
1311

1312
        for (UINT32 k, c, p(0); p<vStations->size(); ++p)
28✔
1313
        {
1314
                dnaGpsPoint.reset(new CDnaGpsPoint);
21✔
1315
                dnaGpsPoint->SetType("Y");
42✔
1316
        
1317
                dnaGpsPoint->SetIgnore(dnaGpsPointCluster->GetIgnore());
21✔
1318
                
1319
                dnaGpsPoint->SetFirst(vStations->at(p)->GetName());
21✔
1320
                dnaGpsPoint->SetTarget("");
42✔
1321
                dnaGpsPoint->SetCoordType("XYZ");
42✔
1322
                dnaGpsPoint->SetRecordedTotal(static_cast<UINT32>(vStations->size()));
21✔
1323

1324
                dnaGpsPoint->SetReferenceFrame(datum.GetName());
21✔
1325
                dnaGpsPoint->SetEpsg(datum.GetEpsgCode_s());
21✔
1326
                dnaGpsPoint->SetEpoch(vStations->at(p)->GetEpoch());
21✔
1327

1328
                dnaGpsPoint->SetPscale(dnaGpsPointCluster->GetPscale());
21✔
1329
                dnaGpsPoint->SetLscale(dnaGpsPointCluster->GetLscale());
21✔
1330
                dnaGpsPoint->SetHscale(dnaGpsPointCluster->GetHscale());
21✔
1331
                dnaGpsPoint->SetVscale(dnaGpsPointCluster->GetVscale());
21✔
1332
                dnaGpsPoint->SetClusterID(dnaGpsPointCluster->GetClusterID());
21✔
1333

1334
                dnaGpsPoint->SetXAxis(vStations->at(p)->GetXAxis());
21✔
1335
                dnaGpsPoint->SetYAxis(vStations->at(p)->GetYAxis());
21✔
1336
                dnaGpsPoint->SetZAxis(vStations->at(p)->GetZAxis());
21✔
1337

1338
                k = p * 3;
21✔
1339

1340
                dnaGpsPoint->SetSigmaXX(covariance.get(k,k));
21✔
1341
                dnaGpsPoint->SetSigmaXY(covariance.get(k,k+1));
21✔
1342
                dnaGpsPoint->SetSigmaXZ(covariance.get(k,k+2));
21✔
1343
                dnaGpsPoint->SetSigmaYY(covariance.get(k+1,k+1));
21✔
1344
                dnaGpsPoint->SetSigmaYZ(covariance.get(k+1,k+2));
21✔
1345
                dnaGpsPoint->SetSigmaZZ(covariance.get(k+2,k+2));
21✔
1346

1347
                cov_count = static_cast<UINT32>(vStations->size()) - p - 1;
21✔
1348
                
1349
                // add covariances
1350
                for (c=0; c<cov_count; ++c)
42✔
1351
                {
1352
                        ci = 3 + k + (c * 3);
21✔
1353

1354
                        dnaPointCovariance.reset(new CDnaCovariance);
21✔
1355
                        dnaPointCovariance->SetType("Y");
42✔
1356

1357
                        dnaPointCovariance->SetM11(covariance.get(k, ci));
21✔
1358
                        dnaPointCovariance->SetM12(covariance.get(k, ci+1));
21✔
1359
                        dnaPointCovariance->SetM13(covariance.get(k, ci+2));
21✔
1360
                        dnaPointCovariance->SetM21(covariance.get(k+1, ci));
21✔
1361
                        dnaPointCovariance->SetM22(covariance.get(k+1, ci+1));
21✔
1362
                        dnaPointCovariance->SetM23(covariance.get(k+1, ci+2));
21✔
1363
                        dnaPointCovariance->SetM31(covariance.get(k+2, ci));
21✔
1364
                        dnaPointCovariance->SetM32(covariance.get(k+2, ci+1));
21✔
1365
                        dnaPointCovariance->SetM33(covariance.get(k+2, ci+2));
21✔
1366

1367
                        dnaPointCovariance->SetClusterID(dnaGpsPointCluster->GetClusterID());
21✔
1368

1369
                        dnaGpsPoint->AddPointCovariance(dnaPointCovariance.get());
21✔
1370
                }
1371
                dnaGpsPointCluster->AddGpsPoint(dnaGpsPoint.get());
21✔
1372
        }
1373
        vMeasurements->push_back(dnaGpsPointCluster);
7✔
1374
}
35✔
1375

1376

1377
} // dnaiostreams
1378
} // dynadjust
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc