diff --git a/.gitignore b/.gitignore index 6ad0c78d2..bbac08655 100644 --- a/.gitignore +++ b/.gitignore @@ -14,10 +14,10 @@ build_gcc/ build/ bin/ doc/ -*/vcpkg/ -*/vcpkg_installed/ -*/Release/ -*/Debug/ +vcpkg/ +vcpkg_installed/ +Release/ +Debug/ *.sln *.vcxproj diff --git a/dynadjust/CMakeLists.txt b/dynadjust/CMakeLists.txt index 76e6957d9..595ce1c95 100644 --- a/dynadjust/CMakeLists.txt +++ b/dynadjust/CMakeLists.txt @@ -471,6 +471,8 @@ else() ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/include/openblas C:/vcpkg/installed/x64-windows/include C:/vcpkg/installed/x64-windows/include/openblas + C:/data/vcpkg/installed/x64-windows/include + C:/data/vcpkg/installed/x64-windows/include/openblas ) else() list(APPEND BLAS_INCLUDE_SEARCH_PATHS @@ -865,7 +867,15 @@ if (BUILD_TESTING) add_test (NAME test-urban-phased-network COMMAND $ urban.phased.adj urban.phased.adj.expected --skip-headers 62 -t 0.001) add_test (NAME test-urban-thread-network COMMAND $ urban_mt.phased-mt.adj urban_mt.phased-mt.adj.expected -t 0.01 -v) - # 8. gnss reference frame transformations + # 8. Source tag preservation in --export-xml (issue #317) + # Import XML data with tags and verify they are preserved in export + add_test (NAME import-source-test COMMAND $ -n srctest source-test-stn.xml source-test-msr.xml --export-xml -r GDA2020) + add_test (NAME check-source-import COMMAND bash check_source_tags.sh srctestmsr.xml CAMPAIGN_2023A CAMPAIGN_2023B EMPTY) + # Transform to a different frame and verify tags survive reftran + add_test (NAME reftran-source-test COMMAND $ srctest -r itrf2014 -e 01.01.2020 --export-xml) + add_test (NAME check-source-reftran COMMAND bash check_source_tags.sh srctest.ITRF2014.01.01.2020msr.xml CAMPAIGN_2023A CAMPAIGN_2023B EMPTY) + + # 9. gnss reference frame transformations add_test (NAME ref-gnss01-network COMMAND $ gnss -r itrf2014 -e 01.01.2020) add_test (NAME ref-gnss02-network COMMAND $ gnss -r itrf1988 -e 03.12.1995) add_test (NAME ref-gnss03-network COMMAND $ gnss -r itrf1989) @@ -1699,6 +1709,10 @@ if (BUILD_TESTING) set_tests_properties(ref-itrf-pmm-06 PROPERTIES DEPENDS ref-itrf-pmm-05) #set_tests_properties(ref-itrf-pmm-07 PROPERTIES DEPENDS ref-itrf-pmm-06) + set_tests_properties(check-source-import PROPERTIES DEPENDS import-source-test) + set_tests_properties(reftran-source-test PROPERTIES DEPENDS check-source-import) + set_tests_properties(check-source-reftran PROPERTIES DEPENDS reftran-source-test) + # Force all tests to run serially (prevent parallel execution) get_property(all_tests DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY TESTS) if(all_tests) diff --git a/dynadjust/dynadjust/dnaimport/dnainterop.cpp b/dynadjust/dynadjust/dnaimport/dnainterop.cpp index 73baaa490..ea21bfa9f 100644 --- a/dynadjust/dynadjust/dnaimport/dnainterop.cpp +++ b/dynadjust/dynadjust/dnaimport/dnainterop.cpp @@ -3949,6 +3949,7 @@ void dna_import::ImportStnsMsrsFromNetwork(vdnaStnPtr* vStations, vdnaMsrPtr* vM } msrPtr->SetMeasurementRec(binaryStn, it_msr, it_dbid); + msrPtr->ResolveSourceFile(bms_meta_.sourceFileMeta, bms_meta_.sourceFileCount); vMeasurements->push_back(msrPtr); } @@ -4107,8 +4108,9 @@ void dna_import::ImportStnsMsrsFromBlock(vdnaStnPtr* vStations, vdnaMsrPtr* vMea } msrPtr->SetMeasurementRec(binaryStn, it_msr, it_dbid); + msrPtr->ResolveSourceFile(bms_meta_.sourceFileMeta, bms_meta_.sourceFileCount); vMeasurements->push_back(msrPtr); - } + } } diff --git a/dynadjust/dynadjust/dnareftran/dnareftran.cpp b/dynadjust/dynadjust/dnareftran/dnareftran.cpp index a489995ff..03ddaa838 100644 --- a/dynadjust/dynadjust/dnareftran/dnareftran.cpp +++ b/dynadjust/dynadjust/dnareftran/dnareftran.cpp @@ -1938,7 +1938,8 @@ void dna_reftran::SerialiseDNA(const std::string& stnfilename, const std::string comment.append(", epoch ").append(datumTo_.GetEpoch_s()); comment.append(". Exported by reftran."); dna.set_dbid_ptr(&v_msr_db_map_); - dna.write_dna_files(&bstBinaryRecords_, &bmsBinaryRecords_, + dna.set_source_file_meta_ptr(bms_meta_.sourceFileMeta, bms_meta_.sourceFileCount); + dna.write_dna_files(&bstBinaryRecords_, &bmsBinaryRecords_, stnfilename, msrfilename, projectSettings_.g.network_name, datumTo_, projection, flagUnused, "Station coordinates" + comment, "GNSS measurements" + comment); } @@ -2124,6 +2125,7 @@ void dna_reftran::SerialiseDynaMLMsr(std::ofstream* xml_file) } msrPtr->SetMeasurementRec(bstBinaryRecords_, _it_msr, _it_dbid); + msrPtr->ResolveSourceFile(bms_meta_.sourceFileMeta, bms_meta_.sourceFileCount); msrPtr->WriteDynaMLMsr(xml_file, comment); } } diff --git a/dynadjust/include/config/dnatypes-structs.hpp b/dynadjust/include/config/dnatypes-structs.hpp index 6e6ec9fcf..1e91dfe1c 100644 --- a/dynadjust/include/config/dnatypes-structs.hpp +++ b/dynadjust/include/config/dnatypes-structs.hpp @@ -335,17 +335,67 @@ typedef struct input_file_meta { UINT16 datatype; // Input data type (station, measurement, both) } input_file_meta_t; +// Source file metadata for measurement provenance +typedef struct source_file_meta { + char filename[FILE_NAME_WIDTH+1]; +} source_file_meta_t; + // Binary file metadata typedef struct binary_file_meta { binary_file_meta () - : binCount(0), reduced(false), reftran(false), geoid(false), inputFileCount(0), inputFileMeta(NULL) {} + : binCount(0), reduced(false), reftran(false), geoid(false) + , inputFileCount(0), inputFileMeta(NULL) + , sourceFileCount(0), sourceFileMeta(nullptr) {} binary_file_meta (const std::string& app_name) - : binCount(0), reduced(false), reftran(false), geoid(false), inputFileCount(0), inputFileMeta(NULL) { + : binCount(0), reduced(false), reftran(false), geoid(false) + , inputFileCount(0), inputFileMeta(NULL) + , sourceFileCount(0), sourceFileMeta(nullptr) { snprintf(modifiedBy, sizeof(modifiedBy), "%s", app_name.c_str()); } ~binary_file_meta() { if (inputFileMeta != NULL) delete []inputFileMeta; + if (sourceFileMeta != nullptr) + delete []sourceFileMeta; + } + + binary_file_meta(const binary_file_meta&) = delete; + binary_file_meta& operator=(const binary_file_meta&) = delete; + + binary_file_meta(binary_file_meta&& rhs) noexcept + : binCount(rhs.binCount), reduced(rhs.reduced) + , reftran(rhs.reftran), geoid(rhs.geoid) + , inputFileCount(rhs.inputFileCount), inputFileMeta(rhs.inputFileMeta) + , sourceFileCount(rhs.sourceFileCount), sourceFileMeta(rhs.sourceFileMeta) + { + memcpy(modifiedBy, rhs.modifiedBy, sizeof(modifiedBy)); + memcpy(epsgCode, rhs.epsgCode, sizeof(epsgCode)); + memcpy(epoch, rhs.epoch, sizeof(epoch)); + rhs.inputFileMeta = nullptr; + rhs.sourceFileMeta = nullptr; + } + + binary_file_meta& operator=(binary_file_meta&& rhs) noexcept + { + if (this != &rhs) + { + delete []inputFileMeta; + delete []sourceFileMeta; + binCount = rhs.binCount; + reduced = rhs.reduced; + reftran = rhs.reftran; + geoid = rhs.geoid; + memcpy(modifiedBy, rhs.modifiedBy, sizeof(modifiedBy)); + memcpy(epsgCode, rhs.epsgCode, sizeof(epsgCode)); + memcpy(epoch, rhs.epoch, sizeof(epoch)); + inputFileCount = rhs.inputFileCount; + inputFileMeta = rhs.inputFileMeta; + sourceFileCount = rhs.sourceFileCount; + sourceFileMeta = rhs.sourceFileMeta; + rhs.inputFileMeta = nullptr; + rhs.sourceFileMeta = nullptr; + } + return *this; } std::uint64_t binCount; // number of records in the binary file bool reduced; // indicates whether the data is reduced(true) or raw(false) @@ -356,10 +406,13 @@ typedef struct binary_file_meta { bool geoid; // geoid separation values have been obtained std::uint64_t inputFileCount; // Number of source file metadata elements input_file_meta_t* inputFileMeta; // Source file metadata + std::uint64_t sourceFileCount; // Number of unique source files for measurements + source_file_meta_t* sourceFileMeta; // Source file metadata for measurement provenance } binary_file_meta_t; typedef std::vector vifm_t; typedef vifm_t::iterator it_vifm_t; +typedef std::vector vsfm_t; typedef std::vector vbfm_t; typedef vbfm_t::iterator it_vbfm_t; diff --git a/dynadjust/include/io/bms_file.cpp b/dynadjust/include/io/bms_file.cpp index 85f32f135..0f78268a3 100644 --- a/dynadjust/include/io/bms_file.cpp +++ b/dynadjust/include/io/bms_file.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -246,6 +247,29 @@ void BmsFile::WriteFile(const std::string& bms_filename, UINT32 msrIndex(0); try { + // Build unique source file index map + std::map sourceFileMap; + std::vector sourceFileList; + for (it_msr = vMeasurements->begin(); it_msr != vMeasurements->end(); ++it_msr) { + std::string src = it_msr->get()->GetSource(); + if (sourceFileMap.find(src) == sourceFileMap.end()) { + sourceFileMap[src] = static_cast(sourceFileList.size()); + sourceFileList.push_back(src); + } + it_msr->get()->SetSourceFileIndex(sourceFileMap[src]); + } + + // Populate source file metadata + bms_meta.sourceFileCount = sourceFileList.size(); + if (bms_meta.sourceFileMeta != nullptr) + delete[] bms_meta.sourceFileMeta; + bms_meta.sourceFileMeta = new source_file_meta_t[bms_meta.sourceFileCount]; + for (std::uint64_t i(0); i < bms_meta.sourceFileCount; ++i) { + memset(bms_meta.sourceFileMeta[i].filename, '\0', sizeof(bms_meta.sourceFileMeta[i].filename)); + snprintf(bms_meta.sourceFileMeta[i].filename, sizeof(bms_meta.sourceFileMeta[i].filename), + "%s", sourceFileList[i].c_str()); + } + WriteFileInfo(bms_file); WriteFileMetadata(bms_file, bms_meta); diff --git a/dynadjust/include/io/bms_file_loader.cpp b/dynadjust/include/io/bms_file_loader.cpp index a13684022..cac81c26b 100644 --- a/dynadjust/include/io/bms_file_loader.cpp +++ b/dynadjust/include/io/bms_file_loader.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -101,8 +102,8 @@ void BmsFileLoader::LoadFileMeta(const std::string& bms_filename, << bms_filename << "." << std::endl; try { - readFileInfo(bms_file); - readFileMetadata(bms_file, bms_meta); + ReadFileInfo(bms_file); + ReadFileMetadata(bms_file, bms_meta); } catch (const std::ios_base::failure& f) { ss << f.what(); throw std::runtime_error(ss.str()); @@ -142,8 +143,8 @@ std::uint64_t BmsFileLoader::LoadFile(const std::string& bms_filename, << bms_filename << "." << std::endl; try { - readFileInfo(bms_file); - readFileMetadata(bms_file, bms_meta); + ReadFileInfo(bms_file); + ReadFileMetadata(bms_file, bms_meta); vbinary_msr->reserve(bms_meta.binCount); for (msr = 0; msr < bms_meta.binCount; msr++) { @@ -198,8 +199,8 @@ void BmsFileLoader::WriteFile(const std::string& bms_filename, << "." << std::endl; try { - writeFileInfo(bms_file); - writeFileMetadata(bms_file, bms_meta); + WriteFileInfo(bms_file); + WriteFileMetadata(bms_file, bms_meta); measurements::it_vmsr_t it_msr(vbinary_msr->begin()); for (it_msr = vbinary_msr->begin(); it_msr != vbinary_msr->end(); ++it_msr) { @@ -245,8 +246,31 @@ void BmsFileLoader::WriteFile(const std::string& bms_filename, UINT32 msrIndex(0); try { - writeFileInfo(bms_file); - writeFileMetadata(bms_file, bms_meta); + // Build unique source file index map + std::map sourceFileMap; + std::vector sourceFileList; + for (it_msr = vMeasurements->begin(); it_msr != vMeasurements->end(); ++it_msr) { + std::string src = it_msr->get()->GetSource(); + if (sourceFileMap.find(src) == sourceFileMap.end()) { + sourceFileMap[src] = static_cast(sourceFileList.size()); + sourceFileList.push_back(src); + } + it_msr->get()->SetSourceFileIndex(sourceFileMap[src]); + } + + // Populate source file metadata + bms_meta.sourceFileCount = sourceFileList.size(); + if (bms_meta.sourceFileMeta != nullptr) + delete[] bms_meta.sourceFileMeta; + bms_meta.sourceFileMeta = new source_file_meta_t[bms_meta.sourceFileCount]; + for (std::uint64_t i(0); i < bms_meta.sourceFileCount; ++i) { + memset(bms_meta.sourceFileMeta[i].filename, '\0', sizeof(bms_meta.sourceFileMeta[i].filename)); + snprintf(bms_meta.sourceFileMeta[i].filename, sizeof(bms_meta.sourceFileMeta[i].filename), + "%s", sourceFileList[i].c_str()); + } + + WriteFileInfo(bms_file); + WriteFileMetadata(bms_file, bms_meta); for (it_msr = vMeasurements->begin(); it_msr != vMeasurements->end(); ++it_msr) { diff --git a/dynadjust/include/io/dnaiodna.cpp b/dynadjust/include/io/dnaiodna.cpp index b520fb3f4..e9b50fa3a 100644 --- a/dynadjust/include/io/dnaiodna.cpp +++ b/dynadjust/include/io/dnaiodna.cpp @@ -590,6 +590,7 @@ void dna_io_dna::write_msr_file(const vstn_t& vbinary_stn, pvmsr_t vbinary_msr, } msrPtr->SetMeasurementRec(vbinary_stn, _it_msr, _it_dbid); + msrPtr->ResolveSourceFile(sourceFileMeta_, sourceFileCount_); msrPtr->WriteDNAMsr(&dna_msr_file, dmw_, dml_); } diff --git a/dynadjust/include/io/dnaiodna.hpp b/dynadjust/include/io/dnaiodna.hpp index 16099d04d..bc0c2399b 100644 --- a/dynadjust/include/io/dnaiodna.hpp +++ b/dynadjust/include/io/dnaiodna.hpp @@ -103,10 +103,14 @@ class dna_io_dna : public DynadjustFile inline const dna_msr_fields dna_msr_widths() { return dmw_; } void set_dbid_ptr(pv_msr_database_id_map pv_msr_db_map); + void set_source_file_meta_ptr(const source_file_meta_t* sfm, std::uint64_t count) { + sourceFileMeta_ = sfm; + sourceFileCount_ = count; + } inline bool filespecifiedReferenceFrame() { return m_filespecifiedReferenceFrame_; } inline bool filespecifiedEpoch() { return m_filespecifiedEpoch_; } - + protected: private: @@ -143,6 +147,9 @@ class dna_io_dna : public DynadjustFile bool m_databaseIDsSet_; bool m_filespecifiedReferenceFrame_; bool m_filespecifiedEpoch_; + + const source_file_meta_t* sourceFileMeta_ = nullptr; + std::uint64_t sourceFileCount_ = 0; }; } // namespace iostreams diff --git a/dynadjust/include/io/dynadjust_file.cpp b/dynadjust/include/io/dynadjust_file.cpp index eb7aa52eb..ebc0f2806 100644 --- a/dynadjust/include/io/dynadjust_file.cpp +++ b/dynadjust/include/io/dynadjust_file.cpp @@ -101,9 +101,14 @@ void DynadjustFile::WriteFileMetadata(std::ofstream& file_stream, binary_file_me file_stream.write(reinterpret_cast(file_meta.inputFileMeta[i].filename), FILE_NAME_WIDTH); file_stream.write(reinterpret_cast(file_meta.inputFileMeta[i].epsgCode), STN_EPSG_WIDTH); file_stream.write(reinterpret_cast(file_meta.inputFileMeta[i].epoch), STN_EPOCH_WIDTH); - file_stream.write(reinterpret_cast(&file_meta.inputFileMeta[i].filetype), sizeof(UINT16)); - file_stream.write(reinterpret_cast(&file_meta.inputFileMeta[i].datatype), sizeof(UINT16)); + file_stream.write(reinterpret_cast(&file_meta.inputFileMeta[i].filetype), sizeof(UINT16)); + file_stream.write(reinterpret_cast(&file_meta.inputFileMeta[i].datatype), sizeof(UINT16)); } + + // Write source file count and source file meta (v1.1+) + file_stream.write(reinterpret_cast(&file_meta.sourceFileCount), sizeof(std::uint64_t)); + for (std::uint64_t i(0); i(file_meta.sourceFileMeta[i].filename), FILE_NAME_WIDTH); } @@ -133,11 +138,36 @@ void DynadjustFile::ReadFileMetadata(std::ifstream& file_stream, binary_file_met file_stream.read(reinterpret_cast(file_meta.inputFileMeta[i].filename), FILE_NAME_WIDTH); file_stream.read(reinterpret_cast(file_meta.inputFileMeta[i].epsgCode), STN_EPSG_WIDTH); file_stream.read(reinterpret_cast(file_meta.inputFileMeta[i].epoch), STN_EPOCH_WIDTH); - file_stream.read(reinterpret_cast(&file_meta.inputFileMeta[i].filetype), sizeof(UINT16)); - file_stream.read(reinterpret_cast(&file_meta.inputFileMeta[i].datatype), sizeof(UINT16)); + file_stream.read(reinterpret_cast(&file_meta.inputFileMeta[i].filetype), sizeof(UINT16)); + file_stream.read(reinterpret_cast(&file_meta.inputFileMeta[i].datatype), sizeof(UINT16)); + } + + // Read source file meta (v1.1+) + if (versionAtLeast(1, 1)) + { + file_stream.read(reinterpret_cast(&file_meta.sourceFileCount), sizeof(std::uint64_t)); + if (file_meta.sourceFileMeta != nullptr) + delete []file_meta.sourceFileMeta; + + file_meta.sourceFileMeta = new source_file_meta_t[file_meta.sourceFileCount]; + + for (std::uint64_t i(0); i(file_meta.sourceFileMeta[i].filename), FILE_NAME_WIDTH); + } + else + { + file_meta.sourceFileCount = 0; + file_meta.sourceFileMeta = nullptr; } } - + + +bool DynadjustFile::versionAtLeast(int major, int minor) const +{ + int vmajor = 0, vminor = 0; + sscanf(version_.c_str(), "%d.%d", &vmajor, &vminor); + return (vmajor > major) || (vmajor == major && vminor >= minor); +} void DynadjustFile::WriteVersion(std::ofstream& file_stream) { diff --git a/dynadjust/include/io/dynadjust_file.hpp b/dynadjust/include/io/dynadjust_file.hpp index 621032a0e..9e82f2c2b 100644 --- a/dynadjust/include/io/dynadjust_file.hpp +++ b/dynadjust/include/io/dynadjust_file.hpp @@ -37,7 +37,7 @@ #include #include -#define __FILE_VERSION__ "1.0" +#define __FILE_VERSION__ "1.1" namespace dynadjust { namespace iostreams { @@ -80,6 +80,8 @@ class DNATYPE_API DynadjustFile { protected: + bool versionAtLeast(int major, int minor) const; + void WriteVersion(std::ofstream& file_stream); void ReadVersion(std::ifstream& file_stream); void WriteDate(std::ofstream& file_stream); diff --git a/dynadjust/include/measurement_types/dnaangle.cpp b/dynadjust/include/measurement_types/dnaangle.cpp index cba8dce34..5372f4d67 100644 --- a/dynadjust/include/measurement_types/dnaangle.cpp +++ b/dynadjust/include/measurement_types/dnaangle.cpp @@ -250,6 +250,7 @@ UINT32 CDnaAngle::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_msr, m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); @@ -273,6 +274,7 @@ void CDnaAngle::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) c measRecord.term1 = m_drValue; measRecord.term2 = m_dStdDev * m_dStdDev; // convert to variance measRecord.measurementStations = m_MSmeasurementStations; + measRecord.sourceFileIndex = m_sourceFileIndex; measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); diff --git a/dynadjust/include/measurement_types/dnacoordinate.cpp b/dynadjust/include/measurement_types/dnacoordinate.cpp index 128052421..d7d488496 100644 --- a/dynadjust/include/measurement_types/dnacoordinate.cpp +++ b/dynadjust/include/measurement_types/dnacoordinate.cpp @@ -202,6 +202,7 @@ UINT32 CDnaCoordinate::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_ m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); @@ -224,6 +225,7 @@ void CDnaCoordinate::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrInd measRecord.term2 = m_dStdDev * m_dStdDev; // convert to variance measRecord.term3 = measRecord.term4 = 0.; measRecord.measurementStations = m_MSmeasurementStations; + measRecord.sourceFileIndex = m_sourceFileIndex; measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); diff --git a/dynadjust/include/measurement_types/dnadirection.cpp b/dynadjust/include/measurement_types/dnadirection.cpp index 3afdaab68..01060e397 100644 --- a/dynadjust/include/measurement_types/dnadirection.cpp +++ b/dynadjust/include/measurement_types/dnadirection.cpp @@ -599,6 +599,7 @@ UINT32 CDnaDirection::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_m m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); @@ -631,6 +632,7 @@ void CDnaDirection::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrInde // number of Directions in the parent cluster including the first measRecord.vectorCount1 = m_lRecordedTotal; measRecord.measurementStations = m_MSmeasurementStations; + measRecord.sourceFileIndex = m_sourceFileIndex; measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); diff --git a/dynadjust/include/measurement_types/dnadirectionset.cpp b/dynadjust/include/measurement_types/dnadirectionset.cpp index 093d9175d..3e72c344d 100644 --- a/dynadjust/include/measurement_types/dnadirectionset.cpp +++ b/dynadjust/include/measurement_types/dnadirectionset.cpp @@ -394,6 +394,7 @@ UINT32 CDnaDirectionSet::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& i m_lsetID = it_msr->clusterID; m_epoch = it_msr->epoch; + m_sourceFileIndex = it_msr->sourceFileIndex; m_vTargetDirections.clear(); m_vTargetDirections.resize(m_lRecordedTotal); @@ -417,6 +418,14 @@ UINT32 CDnaDirectionSet::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& i } +void CDnaDirectionSet::SetSourceFileIndex(const UINT32& idx) +{ + CDnaMeasurement::SetSourceFileIndex(idx); + for (auto& dir : m_vTargetDirections) + dir.SetSourceFileIndex(idx); +} + + void CDnaDirectionSet::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) const { measurement_t measRecord; @@ -442,6 +451,7 @@ void CDnaDirectionSet::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrI measRecord.clusterID = m_lsetID; measRecord.measurementStations = m_MSmeasurementStations; + measRecord.sourceFileIndex = m_sourceFileIndex; measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); diff --git a/dynadjust/include/measurement_types/dnadirectionset.hpp b/dynadjust/include/measurement_types/dnadirectionset.hpp index b8c8e8c56..ccebc9d33 100644 --- a/dynadjust/include/measurement_types/dnadirectionset.hpp +++ b/dynadjust/include/measurement_types/dnadirectionset.hpp @@ -95,6 +95,8 @@ class CDnaDirectionSet : public CDnaMeasurement void ClearDirections(); //bool IsRepeatedDirection(string); + virtual void SetSourceFileIndex(const UINT32& idx) override; + virtual UINT32 CalcBinaryRecordCount() const; virtual void WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) const; virtual UINT32 SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_msr, it_vdbid_t& dbidmap); diff --git a/dynadjust/include/measurement_types/dnadistance.cpp b/dynadjust/include/measurement_types/dnadistance.cpp index 300d90f85..f02a2f312 100644 --- a/dynadjust/include/measurement_types/dnadistance.cpp +++ b/dynadjust/include/measurement_types/dnadistance.cpp @@ -283,12 +283,13 @@ UINT32 CDnaDistance::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_ms m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); return 0; } - + void CDnaDistance::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) const { @@ -308,6 +309,7 @@ void CDnaDistance::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex measRecord.term3 = m_fInstHeight; measRecord.term4 = m_fTargHeight; measRecord.measurementStations = m_MSmeasurementStations; + measRecord.sourceFileIndex = m_sourceFileIndex; measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); diff --git a/dynadjust/include/measurement_types/dnagpsbaseline.cpp b/dynadjust/include/measurement_types/dnagpsbaseline.cpp index 81e5034ba..f1d5c501a 100644 --- a/dynadjust/include/measurement_types/dnagpsbaseline.cpp +++ b/dynadjust/include/measurement_types/dnagpsbaseline.cpp @@ -383,6 +383,7 @@ UINT32 CDnaGpsBaseline::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it m_dX = it_msr->term1; m_dSigmaXX = it_msr->term2; m_lRecordedTotal = it_msr->vectorCount1; + m_sourceFileIndex = it_msr->sourceFileIndex; it_msr++; @@ -427,6 +428,7 @@ void CDnaGpsBaseline::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIn measRecord.clusterID = m_lclusterID; measRecord.measurementStations = m_MSmeasurementStations; + measRecord.sourceFileIndex = m_sourceFileIndex; measRecord.scale1 = m_dPscale; measRecord.scale2 = m_dLscale; @@ -890,6 +892,7 @@ UINT32 CDnaGpsBaselineCluster::SetMeasurementRec(const vstn_t& binaryStn, it_vms m_referenceFrame = datumFromEpsgString(it_msr->epsgCode); m_epoch = it_msr->epoch; m_epsgCode = it_msr->epsgCode; + m_sourceFileIndex = it_msr->sourceFileIndex; m_dPscale = it_msr->scale1; m_dLscale = it_msr->scale2; @@ -917,6 +920,14 @@ UINT32 CDnaGpsBaselineCluster::SetMeasurementRec(const vstn_t& binaryStn, it_vms } +void CDnaGpsBaselineCluster::SetSourceFileIndex(const UINT32& idx) +{ + CDnaMeasurement::SetSourceFileIndex(idx); + for (auto& bsl : m_vGpsBaselines) + bsl.SetSourceFileIndex(idx); +} + + void CDnaGpsBaselineCluster::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) const { std::vector::const_iterator _it_bsl; diff --git a/dynadjust/include/measurement_types/dnagpsbaseline.hpp b/dynadjust/include/measurement_types/dnagpsbaseline.hpp index 7403314f5..9c00ff248 100644 --- a/dynadjust/include/measurement_types/dnagpsbaseline.hpp +++ b/dynadjust/include/measurement_types/dnagpsbaseline.hpp @@ -209,6 +209,8 @@ class CDnaGpsBaselineCluster : public CDnaMeasurement void ReserveGpsBaselinesCount(const UINT32& size); + virtual void SetSourceFileIndex(const UINT32& idx) override; + virtual UINT32 CalcBinaryRecordCount() const; virtual void WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) const; virtual UINT32 SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_msr, it_vdbid_t& dbidmap); diff --git a/dynadjust/include/measurement_types/dnagpspoint.cpp b/dynadjust/include/measurement_types/dnagpspoint.cpp index 744d039c1..0696c251a 100644 --- a/dynadjust/include/measurement_types/dnagpspoint.cpp +++ b/dynadjust/include/measurement_types/dnagpspoint.cpp @@ -490,8 +490,9 @@ UINT32 CDnaGpsPoint::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_ms m_dX = it_msr->term1; m_dSigmaXX = it_msr->term2; m_lRecordedTotal = it_msr->vectorCount1; + m_sourceFileIndex = it_msr->sourceFileIndex; SetCoordType(it_msr->coordType); - + // get data relating to Y, sigmaYY, sigmaXY it_msr++; @@ -538,7 +539,8 @@ void CDnaGpsPoint::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex measRecord.clusterID = m_lclusterID; measRecord.measurementStations = m_MSmeasurementStations; - + measRecord.sourceFileIndex = m_sourceFileIndex; + measRecord.scale1 = m_dPscale; measRecord.scale2 = m_dLscale; measRecord.scale3 = m_dHscale; @@ -1013,6 +1015,7 @@ UINT32 CDnaGpsPointCluster::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t m_referenceFrame = datumFromEpsgCode(LongFromString(it_msr->epsgCode)); m_epoch = it_msr->epoch; m_epsgCode = it_msr->epsgCode; + m_sourceFileIndex = it_msr->sourceFileIndex; m_dPscale = it_msr->scale1; m_dLscale = it_msr->scale2; @@ -1041,6 +1044,14 @@ UINT32 CDnaGpsPointCluster::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t } +void CDnaGpsPointCluster::SetSourceFileIndex(const UINT32& idx) +{ + CDnaMeasurement::SetSourceFileIndex(idx); + for (auto& pnt : m_vGpsPoints) + pnt.SetSourceFileIndex(idx); +} + + void CDnaGpsPointCluster::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) const { std::vector< CDnaGpsPoint >::const_iterator _it_pnt; diff --git a/dynadjust/include/measurement_types/dnagpspoint.hpp b/dynadjust/include/measurement_types/dnagpspoint.hpp index 7f92a1ab6..85e539a37 100644 --- a/dynadjust/include/measurement_types/dnagpspoint.hpp +++ b/dynadjust/include/measurement_types/dnagpspoint.hpp @@ -229,6 +229,8 @@ class CDnaGpsPointCluster : public CDnaMeasurement void AddGpsPoint(const CDnaMeasurement* pGpsPoint); //void ClearPoints(); + virtual void SetSourceFileIndex(const UINT32& idx) override; + virtual UINT32 CalcBinaryRecordCount() const; virtual void WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) const; virtual UINT32 SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_msr, it_vdbid_t& dbidmap); diff --git a/dynadjust/include/measurement_types/dnaheight.cpp b/dynadjust/include/measurement_types/dnaheight.cpp index 092d63c76..ae86d6c41 100644 --- a/dynadjust/include/measurement_types/dnaheight.cpp +++ b/dynadjust/include/measurement_types/dnaheight.cpp @@ -184,12 +184,13 @@ UINT32 CDnaHeight::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_msr, m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); return 0; } - + void CDnaHeight::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) const { @@ -208,6 +209,7 @@ void CDnaHeight::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) measRecord.term3 = 0.; measRecord.term4 = 0.; measRecord.measurementStations = m_MSmeasurementStations; + measRecord.sourceFileIndex = m_sourceFileIndex; measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); diff --git a/dynadjust/include/measurement_types/dnaheightdifference.cpp b/dynadjust/include/measurement_types/dnaheightdifference.cpp index 2979610e0..ba7e26db0 100644 --- a/dynadjust/include/measurement_types/dnaheightdifference.cpp +++ b/dynadjust/include/measurement_types/dnaheightdifference.cpp @@ -220,12 +220,13 @@ UINT32 CDnaHeightDifference::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_ m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); return 0; } - + void CDnaHeightDifference::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) const { @@ -243,6 +244,7 @@ void CDnaHeightDifference::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 measRecord.term1 = m_dValue; measRecord.term2 = m_dStdDev * m_dStdDev; // convert to variance measRecord.measurementStations = m_MSmeasurementStations; + measRecord.sourceFileIndex = m_sourceFileIndex; measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); diff --git a/dynadjust/include/measurement_types/dnameasurement.cpp b/dynadjust/include/measurement_types/dnameasurement.cpp index 3c243dd32..6608a4881 100644 --- a/dynadjust/include/measurement_types/dnameasurement.cpp +++ b/dynadjust/include/measurement_types/dnameasurement.cpp @@ -358,6 +358,7 @@ CDnaMeasurement::CDnaMeasurement() , m_residualPrec(0) , m_preAdjCorr(0) , m_epsgCode(DEFAULT_EPSG_S) + , m_sourceFileIndex(0) , m_epoch("") , m_bInsufficient(false) { @@ -388,6 +389,7 @@ CDnaMeasurement::CDnaMeasurement(CDnaMeasurement&& m) m_epoch = m.m_epoch; m_epsgCode = m.m_epsgCode; + m_sourceFileIndex = m.m_sourceFileIndex; m_msr_db_map = m.m_msr_db_map; @@ -418,6 +420,7 @@ CDnaMeasurement& CDnaMeasurement::operator= (CDnaMeasurement&& rhs) m_epoch = rhs.m_epoch; m_epsgCode = rhs.m_epsgCode; + m_sourceFileIndex = rhs.m_sourceFileIndex; m_msr_db_map = rhs.m_msr_db_map; diff --git a/dynadjust/include/measurement_types/dnameasurement.hpp b/dynadjust/include/measurement_types/dnameasurement.hpp index 29e1d0939..20bcf90e8 100644 --- a/dynadjust/include/measurement_types/dnameasurement.hpp +++ b/dynadjust/include/measurement_types/dnameasurement.hpp @@ -133,7 +133,7 @@ typedef std::shared_ptr< std::vector > vecCovariancePtr; typedef struct msr_t { msr_t() : measType('\0'), measStart(0), measurementStations(1), ignore(false), station1(0), station2(0) - , station3(0), vectorCount1(0), vectorCount2(0), clusterID(0), fileOrder(0) + , station3(0), vectorCount1(0), vectorCount2(0), clusterID(0), fileOrder(0), sourceFileIndex(0) , term1(0.), term2(0.), term3(0.), term4(0.), scale1(1.), scale2(1.), scale3(1.), scale4(1.) , measAdj(0.), measCorr(0.), measAdjPrec(0.), residualPrec(0.) , NStat(0.), TStat(0.), PelzerRel(0.) @@ -163,6 +163,7 @@ typedef struct msr_t { // number of non-ignored directions UINT32 clusterID; // cluster ID (which cluster this measurement belongs to) UINT32 fileOrder; // original file order + UINT32 sourceFileIndex; // index into source file metadata list double term1; // measurement, X, Y, Z, dX, dY, dZ value // direction double term2; // measurement, XX, XY or XZ variance @@ -312,6 +313,7 @@ class CDnaMeasurement inline std::string GetEpsg() const { return m_epsgCode; } inline std::string GetSource() const { return m_sourceFile; } + inline UINT32 GetSourceFileIndex() const { return m_sourceFileIndex; } inline bool GetInsufficient() const { return m_bInsufficient; } @@ -321,7 +323,8 @@ class CDnaMeasurement inline void SetEpsg(const std::string& e) { m_epsgCode = trimstr(e); } inline void SetSource(const std::string& source) { m_sourceFile = source; } - + virtual void SetSourceFileIndex(const UINT32& idx) { m_sourceFileIndex = idx; } + inline void SetInsufficient(const bool bval) { m_bInsufficient = bval; } inline void SetMsrIndex(const UINT32& order) { m_lmeasurementIndex = order; } @@ -450,6 +453,11 @@ class CDnaMeasurement virtual void SerialiseDatabaseMap(std::ofstream* os); + void ResolveSourceFile(const source_file_meta_t* sourceFiles, std::uint64_t sourceFileCount) { + if (sourceFileCount > 0 && m_sourceFileIndex < sourceFileCount) + m_sourceFile = sourceFiles[m_sourceFileIndex].filename; + } + protected: void coutMeasurement(std::ostream &os) const; @@ -474,6 +482,7 @@ class CDnaMeasurement std::string m_epsgCode; std::string m_sourceFile; + UINT32 m_sourceFileIndex; std::string m_epoch; diff --git a/sampleData/check_source_tags.sh b/sampleData/check_source_tags.sh new file mode 100755 index 000000000..783d28b7c --- /dev/null +++ b/sampleData/check_source_tags.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Check tags in exported DynaML XML. Exits 1 on any mismatch. +[ $# -lt 2 ] && { echo "Usage: $0 ..."; exit 1; } +f="$1"; shift +[ -f "$f" ] || { echo "FAIL: $f not found"; exit 1; } +r=0 +for s in "$@"; do + [[ $s == EMPTY ]] && pat='|' || pat="$s" + grep -Eq "$pat" "$f" && echo "PASS: $s in $f" || { echo "FAIL: $s not in $f"; r=1; } +done +exit $r diff --git a/sampleData/source-test-msr.xml b/sampleData/source-test-msr.xml new file mode 100644 index 000000000..1625bf553 --- /dev/null +++ b/sampleData/source-test-msr.xml @@ -0,0 +1,75 @@ + + + + G + CAMPAIGN_2023A + + GDA2020 + 01.01.2020 + 1.000 + 1.000 + 1.000 + 1.000 + ALIC + WARA + + 468295.1485 + 325610.7203 + -137952.1061 + 1.7012598619e-005 + -1.0467927495e-005 + 1.4195195035e-005 + 9.4335882750e-006 + -1.0196034054e-005 + 1.4284143617e-005 + + + + G + CAMPAIGN_2023B + + GDA2020 + 01.01.2020 + 1.000 + 1.000 + 1.000 + 1.000 + ALIC + ADE1 + + 112869.8951 + -745760.6165 + -1068115.2504 + 1.7457261805e-006 + -1.0335401280e-006 + 9.1178422963e-007 + 1.4855548902e-006 + -8.8699137178e-007 + 8.9829335174e-007 + + + + G + + + GDA2020 + 01.01.2020 + 1.000 + 1.000 + 1.000 + 1.000 + WARA + ADE1 + + -355425.2534 + -1071371.3368 + -930163.1443 + 2.5012598619e-005 + -1.2467927495e-005 + 1.6195195035e-005 + 1.1433588275e-005 + -1.2196034054e-005 + 1.6284143617e-005 + + + diff --git a/sampleData/source-test-stn.xml b/sampleData/source-test-stn.xml new file mode 100644 index 000000000..fbf45e826 --- /dev/null +++ b/sampleData/source-test-stn.xml @@ -0,0 +1,39 @@ + + + + ALIC + CCC + XYZ + + ALIC + -4052052.7379 + 4212835.9824 + -2545104.5871 + + ALIC + + + WARA + FFF + XYZ + + WARA + -3583757.5894 + 4538446.7027 + -2683056.6932 + + WARA + + + ADE1 + FFF + XYZ + + ADE1 + -3939182.8428 + 3467075.3659 + -3613219.8375 + + ADE1 + + diff --git a/tests/test_bms_file.cpp b/tests/test_bms_file.cpp index a7cce50ba..5ca2c489d 100644 --- a/tests/test_bms_file.cpp +++ b/tests/test_bms_file.cpp @@ -121,6 +121,8 @@ void create_test_binary_meta(binary_file_meta_t& meta, std::uint64_t binCount) { snprintf(meta.epoch, sizeof(meta.epoch), "01.01.2020"); meta.inputFileCount = 0; meta.inputFileMeta = nullptr; + meta.sourceFileCount = 0; + meta.sourceFileMeta = nullptr; } void create_test_input_file_meta(vifm_t& input_files) { @@ -482,5 +484,77 @@ TEST_CASE("Handle various measurement types", "[BmsFile][types]") { REQUIRE(loaded_measurements[i].term1 == 1000.0 + static_cast(i)); } + cleanup_temp_files(); +} + +TEST_CASE("Source file index round-trip", "[BmsFile][roundtrip][source]") { + BmsFile bms_loader; + vmsr_t measurements; + binary_file_meta_t meta; + + cleanup_temp_files(); + + // Create measurements with different source file indices + measurement_t msr1 = {}; + msr1.measType = 'S'; + msr1.measurementStations = 2; + snprintf(msr1.epsgCode, sizeof(msr1.epsgCode), "7843"); + snprintf(msr1.epoch, sizeof(msr1.epoch), "01.01.2020"); + msr1.station1 = 0; + msr1.station2 = 1; + msr1.fileOrder = 0; + msr1.term1 = 100.0; + msr1.sourceFileIndex = 0; + + measurement_t msr2 = {}; + msr2.measType = 'L'; + msr2.measurementStations = 2; + snprintf(msr2.epsgCode, sizeof(msr2.epsgCode), "7843"); + snprintf(msr2.epoch, sizeof(msr2.epoch), "01.01.2020"); + msr2.station1 = 1; + msr2.station2 = 2; + msr2.fileOrder = 1; + msr2.term1 = 5.0; + msr2.sourceFileIndex = 1; + + measurement_t msr3 = {}; + msr3.measType = 'H'; + msr3.measurementStations = 2; + snprintf(msr3.epsgCode, sizeof(msr3.epsgCode), "7843"); + snprintf(msr3.epoch, sizeof(msr3.epoch), "01.01.2020"); + msr3.station1 = 2; + msr3.station2 = 0; + msr3.fileOrder = 2; + msr3.term1 = 50.0; + msr3.sourceFileIndex = 0; + + measurements.push_back(msr1); + measurements.push_back(msr2); + measurements.push_back(msr3); + + create_test_binary_meta(meta, measurements.size()); + + meta.sourceFileCount = 2; + meta.sourceFileMeta = new source_file_meta_t[2](); + snprintf(meta.sourceFileMeta[0].filename, sizeof(meta.sourceFileMeta[0].filename), "network_a.xml"); + snprintf(meta.sourceFileMeta[1].filename, sizeof(meta.sourceFileMeta[1].filename), "network_b.xml"); + + bms_loader.WriteFile(TEMP_BMS_FILE, &measurements, meta); + REQUIRE(std::filesystem::exists(TEMP_BMS_FILE)); + + vmsr_t loaded_measurements; + binary_file_meta_t loaded_meta; + std::uint64_t count = bms_loader.LoadFile(TEMP_BMS_FILE, &loaded_measurements, loaded_meta); + + REQUIRE(count == 3); + REQUIRE(loaded_measurements[0].sourceFileIndex == 0); + REQUIRE(loaded_measurements[1].sourceFileIndex == 1); + REQUIRE(loaded_measurements[2].sourceFileIndex == 0); + + REQUIRE(loaded_meta.sourceFileCount == 2); + REQUIRE(loaded_meta.sourceFileMeta != nullptr); + REQUIRE(std::string(loaded_meta.sourceFileMeta[0].filename) == "network_a.xml"); + REQUIRE(std::string(loaded_meta.sourceFileMeta[1].filename) == "network_b.xml"); + cleanup_temp_files(); } \ No newline at end of file diff --git a/tests/test_bms_file_loader.cpp b/tests/test_bms_file_loader.cpp index 4c39b35c8..6200f9e5a 100644 --- a/tests/test_bms_file_loader.cpp +++ b/tests/test_bms_file_loader.cpp @@ -100,6 +100,8 @@ void create_test_binary_meta(binary_file_meta_t& meta, std::uint64_t binCount) { snprintf(meta.epoch, sizeof(meta.epoch), "01.01.2020"); meta.inputFileCount = 0; meta.inputFileMeta = nullptr; + meta.sourceFileCount = 0; + meta.sourceFileMeta = nullptr; } void create_test_input_file_meta(vifm_t& input_files) { @@ -461,5 +463,77 @@ TEST_CASE("Handle various measurement types", "[BmsFileLoader][types]") { REQUIRE(loaded_measurements[i].term1 == 1000.0 + static_cast(i)); } + cleanup_temp_files(); +} + +TEST_CASE("Source file index round-trip", "[BmsFileLoader][roundtrip][source]") { + BmsFileLoader bms_loader; + vmsr_t measurements; + binary_file_meta_t meta; + + cleanup_temp_files(); + + // Create measurements with different source file indices + measurement_t msr1 = {}; + msr1.measType = 'S'; + msr1.measurementStations = 2; + snprintf(msr1.epsgCode, sizeof(msr1.epsgCode), "7843"); + snprintf(msr1.epoch, sizeof(msr1.epoch), "01.01.2020"); + msr1.station1 = 0; + msr1.station2 = 1; + msr1.fileOrder = 0; + msr1.term1 = 100.0; + msr1.sourceFileIndex = 0; + + measurement_t msr2 = {}; + msr2.measType = 'L'; + msr2.measurementStations = 2; + snprintf(msr2.epsgCode, sizeof(msr2.epsgCode), "7843"); + snprintf(msr2.epoch, sizeof(msr2.epoch), "01.01.2020"); + msr2.station1 = 1; + msr2.station2 = 2; + msr2.fileOrder = 1; + msr2.term1 = 5.0; + msr2.sourceFileIndex = 1; + + measurement_t msr3 = {}; + msr3.measType = 'H'; + msr3.measurementStations = 2; + snprintf(msr3.epsgCode, sizeof(msr3.epsgCode), "7843"); + snprintf(msr3.epoch, sizeof(msr3.epoch), "01.01.2020"); + msr3.station1 = 2; + msr3.station2 = 0; + msr3.fileOrder = 2; + msr3.term1 = 50.0; + msr3.sourceFileIndex = 0; + + measurements.push_back(msr1); + measurements.push_back(msr2); + measurements.push_back(msr3); + + create_test_binary_meta(meta, measurements.size()); + + meta.sourceFileCount = 2; + meta.sourceFileMeta = new source_file_meta_t[2](); + snprintf(meta.sourceFileMeta[0].filename, sizeof(meta.sourceFileMeta[0].filename), "network_a.xml"); + snprintf(meta.sourceFileMeta[1].filename, sizeof(meta.sourceFileMeta[1].filename), "network_b.xml"); + + bms_loader.WriteFile(TEMP_BMS_FILE, &measurements, meta); + REQUIRE(std::filesystem::exists(TEMP_BMS_FILE)); + + vmsr_t loaded_measurements; + binary_file_meta_t loaded_meta; + std::uint64_t count = bms_loader.LoadFile(TEMP_BMS_FILE, &loaded_measurements, loaded_meta); + + REQUIRE(count == 3); + REQUIRE(loaded_measurements[0].sourceFileIndex == 0); + REQUIRE(loaded_measurements[1].sourceFileIndex == 1); + REQUIRE(loaded_measurements[2].sourceFileIndex == 0); + + REQUIRE(loaded_meta.sourceFileCount == 2); + REQUIRE(loaded_meta.sourceFileMeta != nullptr); + REQUIRE(std::string(loaded_meta.sourceFileMeta[0].filename) == "network_a.xml"); + REQUIRE(std::string(loaded_meta.sourceFileMeta[1].filename) == "network_b.xml"); + cleanup_temp_files(); } \ No newline at end of file