diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 5c6cb0704..9bfa2a6aa 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -1145,8 +1145,9 @@ jobs: # Source Intel OneAPI environment source /opt/intel/oneapi/setvars.sh - # Modify PATH to use our binaries + # Modify PATH and LD_LIBRARY_PATH to use our binaries export PATH="../bin:$PATH" + export LD_LIBRARY_PATH="../bin:${LD_LIBRARY_PATH:-}" # Run the test script and capture output set +e # Don't exit on error @@ -1169,8 +1170,9 @@ jobs: # Source Intel OneAPI environment source /opt/intel/oneapi/setvars.sh - # Modify PATH to use our binaries + # Modify PATH and LD_LIBRARY_PATH to use our binaries export PATH="../bin:$PATH" + export LD_LIBRARY_PATH="../bin:${LD_LIBRARY_PATH:-}" # Run the test script and capture output set +e # Don't exit on error diff --git a/dynadjust/CMakeLists.txt b/dynadjust/CMakeLists.txt index b5982f5ec..0d55c5373 100644 --- a/dynadjust/CMakeLists.txt +++ b/dynadjust/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.15) -project(dynadjust VERSION 1.3.0) +project(dynadjust VERSION 1.4.1) option(USE_MKL "Use Intel MKL (Linux and Windows)" OFF) option(BUILD_TESTING "Enable testing" OFF) @@ -59,17 +59,23 @@ if(BUILD_TESTING) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) endif() -# Set output directories for all platforms to use ../bin/ -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../bin) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../bin) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../bin) +# Set output directories for all platforms to use ../bin/ (unless overridden) +if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../bin) +endif() +if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../bin) +endif() +if(NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../bin) +endif() # For multi-config generators (Visual Studio, Xcode) foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG_UPPER) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_SOURCE_DIR}/../bin) - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_SOURCE_DIR}/../bin) - set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_SOURCE_DIR}/../bin) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) endforeach() if (BUILD_STATIC) @@ -189,7 +195,7 @@ else() endif() message(STATUS "Found XercesC (${XercesC_INCLUDE_DIRS} ${XercesC_LIBRARIES})") -# Static builds need ICU for Xerces transcoding (glibc iconv can't dlopen gconv modules in static binaries) +# Static builds need ICU for Xerces transcoding (glibc iconv can't dlopen gconv modules in static binaries). if(BUILD_STATIC AND UNIX AND NOT APPLE) find_package(ICU COMPONENTS uc data) if(ICU_FOUND) @@ -300,10 +306,13 @@ message(STATUS "Found Boost version: ${Boost_VERSION}") # Set up libraries # ---------------------------------------------------------------------------- +find_package(Threads REQUIRED) + set(DNA_LIBRARIES ${Boost_LIBRARIES} ${XercesC_LIBRARIES} ${XERCES_ICU_LIBRARIES} + Threads::Threads ) # ---------------------------------------------------------------------------- @@ -608,6 +617,23 @@ if(BUILD_STATIC) endif() endforeach() + # For MKL builds, add static MKL libraries (MKL::MKL is an imported + # target that the generic .a finder above cannot resolve) + if(USE_MKL AND MKL_FOUND AND DEFINED ENV{MKLROOT}) + set(MKL_LIB_DIR "$ENV{MKLROOT}/lib") + if(ILP64) + set(MKL_INTERFACE_LIB "${MKL_LIB_DIR}/libmkl_intel_ilp64.a") + else() + set(MKL_INTERFACE_LIB "${MKL_LIB_DIR}/libmkl_intel_lp64.a") + endif() + list(APPEND STATIC_LIBS_LIST + "-Wl,--start-group" + "${MKL_INTERFACE_LIB}" + "${MKL_LIB_DIR}/libmkl_sequential.a" + "${MKL_LIB_DIR}/libmkl_core.a" + "-Wl,--end-group") + endif() + message("") message(STATUS "STATIC_LIBS_LIST=${STATIC_LIBS_LIST}") @@ -916,6 +942,13 @@ if (BUILD_TESTING) target_link_libraries(test_snx_file_writer PRIVATE ${DNA_LIBRARIES}) target_compile_definitions(test_snx_file_writer PRIVATE __BINARY_NAME__="test_snx_file_writer" __BINARY_DESC__="Unit tests for SNX file writer") + # Test: test_format_elapsed_time + add_executable(test_format_elapsed_time + ${UNIT_TEST_DIR}/test_format_elapsed_time.cpp + ) + target_include_directories(test_format_elapsed_time PRIVATE ${UNIT_TEST_DIR} ${CMAKE_SOURCE_DIR}/include) + target_compile_definitions(test_format_elapsed_time PRIVATE __BINARY_NAME__="test_format_elapsed_time" __BINARY_DESC__="Unit tests for FormatElapsedTime helper") + # Register unit tests with CTest add_test(NAME unit-MatrixTest COMMAND $) add_test(NAME unit-MsrToStnSortTest COMMAND $) @@ -931,6 +964,7 @@ if (BUILD_TESTING) add_test(NAME unit-AslFileLoaderTest COMMAND $) add_test(NAME unit-BmsFileLoaderTest COMMAND $) add_test(NAME unit-SnxFileWriterTest COMMAND $) + add_test(NAME unit-FormatElapsedTimeTest COMMAND $) # ........................................................................ # Functional tests @@ -974,7 +1008,10 @@ if (BUILD_TESTING) add_test (NAME import-urban-network COMMAND $ -n urban urban-network.stn urban-network.msr --flag-unused-stations) add_test (NAME geoid-urban-network COMMAND $ urban -g urban-network-geoid.gsb --export-dna-geo) add_test (NAME segment-urban-network COMMAND $ urban --min 50 --max 150 --test-integrity) - add_test (NAME adjust-urban-network-verbose COMMAND $ urban --verbose 3) + add_test (NAME import-urban-network-verbose COMMAND $ -n urban_verbose urban-network.stn urban-network.msr --flag-unused-stations) + add_test (NAME geoid-urban-network-verbose COMMAND $ urban_verbose -g urban-network-geoid.gsb --export-dna-geo) + add_test (NAME segment-urban-network-verbose COMMAND $ urban_verbose --min 50 --max 150 --test-integrity) + add_test (NAME adjust-urban-network-verbose COMMAND $ urban_verbose --verbose 3) add_test (NAME adjust-urban-network COMMAND $ urban --output-adj-msr --phased --stn-corrections --export-sinex-file --export-xml-stn-file --export-dna-stn-file --output-pos-uncertainty --export-dna-msr --export-xml-msr) add_test (NAME plot-urban-network-01 COMMAND $ urban --phased --label-sta --correction-arrows --label-corr --compute-corrections --scale-arrows 10.5 --error-ellipse --positional-uncertainty --scale-ellipse-c 10.5) add_test (NAME plot-urban-network-02 COMMAND $ urban --phased --label-sta --label-constraints --correction-arrows --label-corr --compute-corrections --scale-arrows 10.5 --error-ellipse --positional-uncertainty --scale-ellipse-c 10.5 --block-number 2 --alternate-name) @@ -1098,7 +1135,7 @@ if (BUILD_TESTING) add_test (NAME test-urban-phased-network COMMAND $ urban.phased.adj urban.phased.adj.expected --skip-to-marker "M Station 1" -t 0.001) add_test (NAME test-urban-thread-network COMMAND $ urban_mt.phased-mt.adj urban_mt.phased-mt.adj.expected --skip-to-marker "M Station 1" -t 0.01 -v) - # 8. Source tag preservation in --export-xml (issue #317) + # 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) @@ -1106,6 +1143,28 @@ if (BUILD_TESTING) 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) + # 8a. XML import with and without xsi:noNamespaceSchemaLocation (issue #346) + # Verify dnaimport handles XML files regardless of whether the schema location + # attribute is present. Strip the attribute and import each sample data pair. + + # gnss-network: with schema location (original files) + add_test (NAME import-xml-gnss-with-xsd COMMAND $ -n xsd_gnss gnss-networkstn.xml gnss-networkmsr.xml -r GDA2020) + # gnss-network: without schema location + add_test (NAME import-xml-gnss-strip COMMAND bash strip_schema_location.sh gnss-networkstn.xml gnss-networkstn-noxsd.xml) + add_test (NAME import-xml-gnss-strip-msr COMMAND bash strip_schema_location.sh gnss-networkmsr.xml gnss-networkmsr-noxsd.xml) + add_test (NAME import-xml-gnss-without-xsd COMMAND $ -n xsd_gnss gnss-networkstn-noxsd.xml gnss-networkmsr-noxsd.xml -r GDA2020) + set_tests_properties(import-xml-gnss-strip import-xml-gnss-strip-msr PROPERTIES FIXTURES_SETUP gnss_strip) + set_tests_properties(import-xml-gnss-without-xsd PROPERTIES FIXTURES_REQUIRED gnss_strip) + + # urban-network: with schema location + add_test (NAME import-xml-urban-with-xsd COMMAND $ -n xsd_urban urban-networkstn.xml urban-networkmsr.xml -r GDA2020) + # urban-network: without schema location + add_test (NAME import-xml-urban-strip COMMAND bash strip_schema_location.sh urban-networkstn.xml urban-networkstn-noxsd.xml) + add_test (NAME import-xml-urban-strip-msr COMMAND bash strip_schema_location.sh urban-networkmsr.xml urban-networkmsr-noxsd.xml) + add_test (NAME import-xml-urban-without-xsd COMMAND $ -n xsd_urban urban-networkstn-noxsd.xml urban-networkmsr-noxsd.xml -r GDA2020) + set_tests_properties(import-xml-urban-strip import-xml-urban-strip-msr PROPERTIES FIXTURES_SETUP urban_strip) + set_tests_properties(import-xml-urban-without-xsd PROPERTIES FIXTURES_REQUIRED urban_strip) + # 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) @@ -1744,16 +1803,8 @@ if (BUILD_TESTING) add_test (NAME import-no-frame COMMAND $ -n sample -r itrf1975 dsg.stn dsg.msr) # no stations and measurements (generated above) add_test (NAME import-no-data COMMAND $ -n null ./null.stn ./null.msr) - # no DynaML.xsd - remove file in platform-independent manner (using CMake command) by setting up a test dependency - add_test (NAME import-no-xsd-file-remove COMMAND ${CMAKE_COMMAND} -E remove DynaML.xsd) - add_test (NAME import-no-xsd-file COMMAND $ -n gnss gnss-networkstn.xml gnss-networkmsr.xml) - set_tests_properties(import-no-xsd-file PROPERTIES DEPENDS import-no-xsd-file-remove TIMEOUT 5) - # Restore DynaML.xsd after testing its absence - add_test (NAME import-no-xsd-file-restore COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../sampleData/DynaML.xsd DynaML.xsd) - set_tests_properties(import-no-xsd-file-restore PROPERTIES DEPENDS import-no-xsd-file) # no geoid file add_test (NAME import-no-geoid COMMAND $ -n geoid --geo-file ./nofile.geo) - set_tests_properties(import-no-geoid PROPERTIES DEPENDS import-no-xsd-file-restore) # no project file add_test (NAME import-no-project COMMAND $ -p ./nofile.dnaproj) # measurement stns not in station file @@ -1765,7 +1816,7 @@ if (BUILD_TESTING) add_test (NAME import-misc-network-size COMMAND $ -n contig --import-contiguous-stn-msr 5000 --export-dna) set_tests_properties(import-no-option import-invalid-option import-invalid-file import-nearby import-no-files import-no-frame - import-no-data import-no-xsd-file import-no-geoid import-no-project import-no-stns import-no-help + import-no-data import-no-geoid import-no-project import-no-stns import-no-help import-misc-block-size import-misc-network-size import-misc-07 PROPERTIES WILL_FAIL TRUE) @@ -1942,6 +1993,9 @@ if (BUILD_TESTING) set_tests_properties( import-urban-network geoid-urban-network segment-urban-network adjust-urban-network PROPERTIES RUN_SERIAL TRUE) + set_tests_properties( + import-urban-network-verbose geoid-urban-network-verbose segment-urban-network-verbose adjust-urban-network-verbose + PROPERTIES RUN_SERIAL TRUE) set_tests_properties( import-urban-network-thread reftran-urban-network-thread geoid-urban-network-thread segment-urban-network-thread adjust-urban-network-thread-01 PROPERTIES RUN_SERIAL TRUE) @@ -1986,7 +2040,7 @@ if (BUILD_TESTING) unit-AmlFileLoaderTest unit-BmsFileTest unit-NetworkDataLoaderTest unit-MeasurementProcessorTest unit-DynAdjustPrinterTest unit-GNSSNstatSortTest unit-BstFileLoaderTest unit-AslFileLoaderTest unit-BmsFileLoaderTest - unit-SnxFileWriterTest + unit-SnxFileWriterTest unit-FormatElapsedTimeTest ) set_tests_properties(${UNIT_TESTS} PROPERTIES RUN_SERIAL FALSE @@ -2007,4 +2061,3 @@ endif () message ("") message ("") - diff --git a/dynadjust/cmake/StaticBuildOptimizations.cmake b/dynadjust/cmake/StaticBuildOptimizations.cmake index 2f3d45716..7bac0b495 100644 --- a/dynadjust/cmake/StaticBuildOptimizations.cmake +++ b/dynadjust/cmake/StaticBuildOptimizations.cmake @@ -6,20 +6,59 @@ function(optimize_static_target TARGET_NAME) if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") set_property(TARGET ${TARGET_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) endif() - + # Platform-specific optimizations if(UNIX AND NOT APPLE) - # Linux-specific optimizations target_link_options(${TARGET_NAME} PRIVATE - -static - -Wl,--gc-sections # Remove unused sections - -Wl,--as-needed # Only link libraries that are actually used - -Wl,-O2 # Optimize at link time - -Wl,--strip-all # Strip all symbols + -static-libgcc + -static-libstdc++ + -Wl,--as-needed + -Wl,-O2 + -Wl,--strip-all + # glibc 2.34+ merged libpthread into libc; libpthread.a is an + # empty stub. libstdc++.a references ALL pthread functions via + # weak aliases (__gthrw_), and glibc defines them as WEAK (W) + # in libc.a. LLD will not pull a weak-defined archive member to + # satisfy a weak-undefined reference, leaving every pthread + # symbol at address 0 and crashing at runtime. + # Fix: add a strong undefined reference (-u) for each symbol. + # This forces LLD to pull in each object from libc.a, making + # the weak-defined symbol available for the weak refs to bind to. + -Wl,-u,pthread_once + -Wl,-u,pthread_getspecific + -Wl,-u,pthread_setspecific + -Wl,-u,pthread_create + -Wl,-u,pthread_join + -Wl,-u,pthread_equal + -Wl,-u,pthread_self + -Wl,-u,pthread_detach + -Wl,-u,pthread_cancel + -Wl,-u,pthread_mutex_lock + -Wl,-u,pthread_mutex_trylock + -Wl,-u,pthread_mutex_unlock + -Wl,-u,pthread_mutex_init + -Wl,-u,pthread_mutex_destroy + -Wl,-u,pthread_cond_init + -Wl,-u,pthread_cond_broadcast + -Wl,-u,pthread_cond_signal + -Wl,-u,pthread_cond_wait + -Wl,-u,pthread_cond_timedwait + -Wl,-u,pthread_cond_destroy + -Wl,-u,pthread_key_create + -Wl,-u,pthread_key_delete + -Wl,-u,pthread_mutexattr_init + -Wl,-u,pthread_mutexattr_settype + -Wl,-u,pthread_mutexattr_destroy + -Wl,-u,pthread_attr_init + -Wl,-u,pthread_attr_destroy + -Wl,-u,pthread_attr_setdetachstate + -Wl,-u,pthread_exit + -Wl,-u,__pthread_key_create ) + target_link_libraries(${TARGET_NAME} PRIVATE pthread m dl) target_compile_options(${TARGET_NAME} PRIVATE - -ffunction-sections # Put each function in its own section - -fdata-sections # Put each data item in its own section + -ffunction-sections + -fdata-sections ) elseif(APPLE) # macOS-specific optimizations diff --git a/dynadjust/dynadjust.rc b/dynadjust/dynadjust.rc index a0eaebbac..2bbab4905 100644 --- a/dynadjust/dynadjust.rc +++ b/dynadjust/dynadjust.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,3,0,0 - PRODUCTVERSION 1,3,0,0 + FILEVERSION 1,4,1,0 + PRODUCTVERSION 1,4,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -42,12 +42,12 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "DynAdjust is a rigorous, high performance least squares adjustment application." - VALUE "FileVersion", "1.3.0.0" + VALUE "FileVersion", "1.4.1.0" VALUE "InternalName", "DynAdjust" VALUE "LegalCopyright", "Copyright 2025 Geoscience Australia" VALUE "OriginalFilename", "DynAdjust" VALUE "ProductName", "DynAdjust" - VALUE "ProductVersion", "1.3.0.0" + VALUE "ProductVersion", "1.4.1.0" END END BLOCK "VarFileInfo" diff --git a/dynadjust/dynadjust/dnaadjust/dnaadjust-multi.cpp b/dynadjust/dynadjust/dnaadjust/dnaadjust-multi.cpp index d0df76cb7..a188ed5d6 100644 --- a/dynadjust/dynadjust/dnaadjust/dnaadjust-multi.cpp +++ b/dynadjust/dynadjust/dnaadjust/dnaadjust-multi.cpp @@ -94,11 +94,9 @@ void dna_adjust::AdjustPhasedMultiThread() initialiseIteration(); std::string corr_msg; - std::ostringstream ss; UINT32 i; bool iterate(true); - std::chrono::milliseconds iteration_time(std::chrono::milliseconds(0)); cpu_timer it_time, tot_time; #if defined(__ICC) || defined(__INTEL_COMPILER) // Intel compiler @@ -169,7 +167,6 @@ void dna_adjust::AdjustPhasedMultiThread() for_each(mt_adjust_threads.begin(), mt_adjust_threads.end(), std::mem_fn(&std::thread::join)); #endif // This point is reached when the threads have finished - iteration_time = std::chrono::duration_cast(it_time.elapsed().wall); //delete mt_adjust_threads; #if defined(__ICC) || defined(__INTEL_COMPILER) // Intel compiler @@ -195,18 +192,12 @@ void dna_adjust::AdjustPhasedMultiThread() if (IsCancelled()) break; - ss.str(""); - if (iteration_time >= std::chrono::seconds(1)) { - auto seconds = std::chrono::duration_cast(iteration_time); - ss << seconds.count() << "s"; - } else { - ss << iteration_time.count() << "ms"; - } + std::string iteration_time_str = FormatElapsedTime(it_time.elapsed().wall.count() / 1.0e9); /////////////////////////////////// // protected write to adj file (not needed here since write to // adj file at this stage is via single thread - adj_file << std::setw(PRINT_VAR_PAD) << std::left << "Elapsed time" << ss.str() << std::endl; + adj_file << std::setw(PRINT_VAR_PAD) << std::left << "Elapsed time" << iteration_time_str << std::endl; OutputLargestCorrection(corr_msg); /////////////////////////////////// @@ -214,6 +205,7 @@ void dna_adjust::AdjustPhasedMultiThread() debug_file << concurrentAdjustments.print_adjusted_blocks(); iterationCorrections_.add_message(corr_msg); + iterationTimes_.add_message(iteration_time_str); iterationQueue_.push_and_notify(CurrentIteration()); // currentIteration begins at 1, so not zero-indexed // continue iterating? @@ -303,7 +295,7 @@ void dna_adjust::SolveMT(bool COMPUTE_INVERSE, const UINT32& block) { // Compute inverse of normals (aposteriori variance matrix) // (AT * V-1 * A)-1 - FormInverseVarianceMatrix(&(v_normalsR_.at(block)), false); + FormInverseVarianceMatrix(&(v_normalsR_.at(block)), false, true); } // compute weighted "measured minus computed" @@ -312,7 +304,10 @@ void dna_adjust::SolveMT(bool COMPUTE_INVERSE, const UINT32& block) // Solve corrections from normal equations v_correctionsR_.at(block).redim(v_designR_.at(block).columns(), 1); - v_correctionsR_.at(block).multiply(v_normalsR_.at(block), "N", At_Vinv_m, "N"); + if (v_normalsR_.at(block).is_symmetric()) + v_correctionsR_.at(block).multiply_sym(v_normalsR_.at(block), At_Vinv_m); + else + v_correctionsR_.at(block).multiply(v_normalsR_.at(block), "N", At_Vinv_m, "N"); // debug output? if (projectSettings_.g.verbose > 3) @@ -842,4 +837,3 @@ void dna_adjust::PrepareAdjustmentMultiThread() } // namespace networkadjust } // namespace dynadjust - diff --git a/dynadjust/dynadjust/dnaadjust/dnaadjust-stage.cpp b/dynadjust/dynadjust/dnaadjust/dnaadjust-stage.cpp index e0e1175d9..4be2c0422 100644 --- a/dynadjust/dynadjust/dnaadjust/dnaadjust-stage.cpp +++ b/dynadjust/dynadjust/dnaadjust/dnaadjust-stage.cpp @@ -159,6 +159,8 @@ void dna_adjust::DeserialiseBlockFromMappedFile(const UINT32& block, const int f return; } + const auto profile_start = profileTimings_ ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; + va_list vlist; va_start(vlist, file_count); @@ -173,7 +175,7 @@ void dna_adjust::DeserialiseBlockFromMappedFile(const UINT32& block, const int f break; case sf_normals_r: addr = normalsR_map_.GetBlockRegionAddr(block); - v_normalsR_.at(block).ReadMappedFileRegion(addr); + v_normalsR_.at(block).AttachMappedFileRegion(addr); break; case sf_atvinv: v_AtVinv_.at(block).allocate(); @@ -183,47 +185,47 @@ void dna_adjust::DeserialiseBlockFromMappedFile(const UINT32& block, const int f break; case sf_meas_minus_comp: addr = measMinusComp_map_.GetBlockRegionAddr(block); - v_measMinusComp_.at(block).ReadMappedFileRegion(addr); + v_measMinusComp_.at(block).AttachMappedFileRegion(addr); break; case sf_estimated_stns: addr = estimatedStations_map_.GetBlockRegionAddr(block); - v_estimatedStations_.at(block).ReadMappedFileRegion(addr); + v_estimatedStations_.at(block).AttachMappedFileRegion(addr); break; case sf_original_stns: addr = originalStations_map_.GetBlockRegionAddr(block); - v_originalStations_.at(block).ReadMappedFileRegion(addr); + v_originalStations_.at(block).AttachMappedFileRegion(addr); break; case sf_rigorous_stns: addr = rigorousStations_map_.GetBlockRegionAddr(block); - v_rigorousStations_.at(block).ReadMappedFileRegion(addr); + v_rigorousStations_.at(block).AttachMappedFileRegion(addr); break; case sf_junction_vars: addr = junctionVariances_map_.GetBlockRegionAddr(block); - v_junctionVariances_.at(block).ReadMappedFileRegion(addr); + v_junctionVariances_.at(block).AttachMappedFileRegion(addr); break; case sf_junction_vars_f: addr = junctionVariancesFwd_map_.GetBlockRegionAddr(block); - v_junctionVariancesFwd_.at(block).ReadMappedFileRegion(addr); + v_junctionVariancesFwd_.at(block).AttachMappedFileRegion(addr); break; case sf_junction_ests_f: addr = junctionEstimatesFwd_map_.GetBlockRegionAddr(block); - v_junctionEstimatesFwd_.at(block).ReadMappedFileRegion(addr); + v_junctionEstimatesFwd_.at(block).AttachMappedFileRegion(addr); break; case sf_junction_ests_r: addr = junctionEstimatesRev_map_.GetBlockRegionAddr(block); - v_junctionEstimatesRev_.at(block).ReadMappedFileRegion(addr); + v_junctionEstimatesRev_.at(block).AttachMappedFileRegion(addr); break; case sf_rigorous_vars: addr = rigorousVariances_map_.GetBlockRegionAddr(block); - v_rigorousVariances_.at(block).ReadMappedFileRegion(addr); + v_rigorousVariances_.at(block).AttachMappedFileRegion(addr); break; case sf_prec_adj_msrs: addr = precAdjMsrs_map_.GetBlockRegionAddr(block); - v_precAdjMsrsFull_.at(block).ReadMappedFileRegion(addr); + v_precAdjMsrsFull_.at(block).AttachMappedFileRegion(addr); break; case sf_corrections: addr = corrections_map_.GetBlockRegionAddr(block); - v_corrections_.at(block).ReadMappedFileRegion(addr); + v_corrections_.at(block).AttachMappedFileRegion(addr); if (v_blockMeta_.at(block)._blockLast) v_correctionsR_.at(block).allocate(); @@ -232,6 +234,14 @@ void dna_adjust::DeserialiseBlockFromMappedFile(const UINT32& block, const int f } } va_end(vlist); + + if (profileTimings_) + { + const auto elapsed = std::chrono::steady_clock::now() - profile_start; + profileStageLoadNs_.fetch_add( + static_cast(std::chrono::duration_cast(elapsed).count()), + std::memory_order_relaxed); + } } void dna_adjust::SerialiseBlockToMappedFile(const UINT32& block, const int file_count, ...) @@ -240,14 +250,16 @@ void dna_adjust::SerialiseBlockToMappedFile(const UINT32& block, const int file_ { // serialise all. That is, call this function again, but with // arguments for all files - SerialiseBlockToMappedFile(block, 16, - sf_normals, sf_normals_r, sf_atvinv, sf_design, sf_meas_minus_comp, + SerialiseBlockToMappedFile(block, 12, + sf_normals_r, sf_meas_minus_comp, sf_estimated_stns, sf_original_stns, sf_rigorous_stns, sf_junction_vars, sf_junction_vars_f, sf_junction_ests_f, sf_junction_ests_r, sf_rigorous_vars, sf_prec_adj_msrs, sf_corrections); return; } + const auto profile_start = profileTimings_ ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; + va_list vlist; va_start(vlist, file_count); @@ -257,16 +269,10 @@ void dna_adjust::SerialiseBlockToMappedFile(const UINT32& block, const int file_ { switch (va_arg(vlist, int)) { - case sf_normals: - break; case sf_normals_r: addr = normalsR_map_.GetBlockRegionAddr(block); v_normalsR_.at(block).WriteMappedFileRegion(addr); break; - case sf_atvinv: - break; - case sf_design: - break; case sf_meas_minus_comp: addr = measMinusComp_map_.GetBlockRegionAddr(block); v_measMinusComp_.at(block).WriteMappedFileRegion(addr); @@ -314,6 +320,14 @@ void dna_adjust::SerialiseBlockToMappedFile(const UINT32& block, const int file_ } } va_end(vlist); + + if (profileTimings_) + { + const auto elapsed = std::chrono::steady_clock::now() - profile_start; + profileStageStoreNs_.fetch_add( + static_cast(std::chrono::duration_cast(elapsed).count()), + std::memory_order_relaxed); + } } @@ -395,7 +409,11 @@ void dna_adjust::OpenStageFileStreams(const int file_count, ...) } std::stringstream ss; - ss << projectSettings_.g.output_folder << FOLDER_SLASH << projectSettings_.g.network_name << "-"; + if (projectSettings_.a.stage_path.empty()) + ss << projectSettings_.g.output_folder; + else + ss << projectSettings_.a.stage_path; + ss << FOLDER_SLASH << projectSettings_.g.network_name << "-"; std::string filePath(ss.str()); v_stageFileStreams_.clear(); @@ -851,6 +869,46 @@ void dna_adjust::OffloadBlockToMappedFile(const UINT32& block) // Unload block matrix data from memory UnloadBlock(block); + + // Advise kernel that mapped pages for this block are no longer needed, + // freeing page cache for upcoming blocks + AdviseBlockDontNeed(block); +} + + +void dna_adjust::AdviseBlockDontNeed(const UINT32& block) +{ + using advice = boost::interprocess::mapped_region::advice_types; + normalsR_map_.AdviseRegion(block, advice::advice_dontneed); + measMinusComp_map_.AdviseRegion(block, advice::advice_dontneed); + estimatedStations_map_.AdviseRegion(block, advice::advice_dontneed); + originalStations_map_.AdviseRegion(block, advice::advice_dontneed); + rigorousStations_map_.AdviseRegion(block, advice::advice_dontneed); + junctionVariances_map_.AdviseRegion(block, advice::advice_dontneed); + junctionVariancesFwd_map_.AdviseRegion(block, advice::advice_dontneed); + junctionEstimatesFwd_map_.AdviseRegion(block, advice::advice_dontneed); + junctionEstimatesRev_map_.AdviseRegion(block, advice::advice_dontneed); + rigorousVariances_map_.AdviseRegion(block, advice::advice_dontneed); + precAdjMsrs_map_.AdviseRegion(block, advice::advice_dontneed); + corrections_map_.AdviseRegion(block, advice::advice_dontneed); +} + + +void dna_adjust::AdviseBlockWillNeed(const UINT32& block) +{ + using advice = boost::interprocess::mapped_region::advice_types; + normalsR_map_.AdviseRegion(block, advice::advice_willneed); + measMinusComp_map_.AdviseRegion(block, advice::advice_willneed); + estimatedStations_map_.AdviseRegion(block, advice::advice_willneed); + originalStations_map_.AdviseRegion(block, advice::advice_willneed); + rigorousStations_map_.AdviseRegion(block, advice::advice_willneed); + junctionVariances_map_.AdviseRegion(block, advice::advice_willneed); + junctionVariancesFwd_map_.AdviseRegion(block, advice::advice_willneed); + junctionEstimatesFwd_map_.AdviseRegion(block, advice::advice_willneed); + junctionEstimatesRev_map_.AdviseRegion(block, advice::advice_willneed); + rigorousVariances_map_.AdviseRegion(block, advice::advice_willneed); + precAdjMsrs_map_.AdviseRegion(block, advice::advice_willneed); + corrections_map_.AdviseRegion(block, advice::advice_willneed); } @@ -896,7 +954,7 @@ void dna_adjust::UnloadBlock(const UINT32& block, const int file_count, ...) if (file_count == 0) { // deserialise all - UnloadBlock(block, 16, + UnloadBlock(block, 15, sf_normals, sf_normals_r, sf_atvinv, sf_design, sf_meas_minus_comp, sf_estimated_stns, sf_original_stns, sf_rigorous_stns, sf_junction_vars, sf_junction_vars_f, sf_junction_ests_f, sf_junction_ests_r, @@ -907,58 +965,60 @@ void dna_adjust::UnloadBlock(const UINT32& block, const int file_count, ...) va_list vlist; va_start(vlist, file_count); - // Unload block matrix data from memory + // Unload block matrix data from memory by freeing buffers. + // Use deallocate() instead of explicit destructor calls to keep + // the matrix objects in a valid state for later reuse. for (UINT16 file(0); file + #include #include @@ -32,6 +34,9 @@ concurrent_queue combineAdjustmentQueue; concurrent_queue prepareAdjustmentQueue; std::exception_ptr fwd_error, rev_error, cmb_error, prep_error; +void dna_adjust::SetMaxBlasThreads(int n) { dynadjust::math::set_max_blas_threads(n); } +int dna_adjust::GetMaxBlasThreads() { return dynadjust::math::get_max_blas_threads(); } + dna_adjust::dna_adjust() : isPreparing_(false) , isAdjusting_(false) @@ -42,7 +47,12 @@ dna_adjust::dna_adjust() , isAdjustmentQuestionable_(false) , blockCount_(1) , currentBlock_(0) + , lastBlockElapsedMs_(0) , total_time_(0) + , profileTimings_(std::getenv("DYNADJUST_PROFILE") != nullptr) + , profileUpdateNormalsNs_(0) + , profileStageLoadNs_(0) + , profileStageStoreNs_(0) , adjustStatus_(ADJUST_SUCCESS) , currentIteration_(0) , datum_(DEFAULT_EPSG_U) @@ -136,7 +146,7 @@ dna_adjust::dna_adjust() // Initialize the printer printer_ = std::make_unique(*this); } - + dna_adjust::~dna_adjust() { @@ -172,8 +182,15 @@ UINT32& dna_adjust::incrementIteration() return ++currentIteration_; } -void dna_adjust::initialiseIteration(const UINT32& iteration) -{ +void dna_adjust::decrementIteration() +{ + std::lock_guard lock(current_iterationMutex); + if (currentIteration_ > 0) + --currentIteration_; +} + +void dna_adjust::initialiseIteration(const UINT32& iteration) +{ std::lock_guard lock(current_iterationMutex); currentIteration_ = iteration; } @@ -195,6 +212,9 @@ void dna_adjust::InitialiseAdjustment() adjustStatus_ = ADJUST_SUCCESS; statusMessages_.clear(); + profileUpdateNormalsNs_.store(0, std::memory_order_relaxed); + profileStageLoadNs_.store(0, std::memory_order_relaxed); + profileStageStoreNs_.store(0, std::memory_order_relaxed); currentBlock_ = 0; initialiseIteration(); @@ -240,6 +260,7 @@ void dna_adjust::PrepareAdjustment(const project_settings& projectSettings) isPreparing_ = true; isAdjusting_ = true; isCombining_ = false; + rebuildingDesign_ = false; isFirstTimeAdjustment_ = true; projectSettings_ = projectSettings; @@ -528,14 +549,14 @@ void dna_adjust::UpdateAdjustment(bool iterate) // Hence, only design and msr-comp are required so as to compute stats if (!iterate) continue; - + switch (projectSettings_.a.adjust_mode) { case PhasedMode: case Phased_Block_1Mode: v_normals_.at(block).zero(); UpdateNormals(block, false); - + if (projectSettings_.a.multi_thread) { v_estimatedStationsR_.at(block) = v_rigorousStations_.at(block); @@ -545,9 +566,9 @@ void dna_adjust::UpdateAdjustment(bool iterate) } // Back up normals. This copy contains the contributions from all - // apriori measurement variances, excluding parameter station + // apriori measurement variances, excluding parameter station // variances and junction station variances - v_normalsR_.at(block) = v_normals_.at(block); + v_normalsR_.at(block) = v_normals_.at(block); AddConstraintStationstoNormalsForward(block); break; @@ -742,17 +763,17 @@ void dna_adjust::PrepareStationandVarianceMatrices(const UINT32& block) // resize rigorous coordinate estimate array v_rigorousStations_.at(block).redim(v_unknownsCount_.at(block), 1); - - // resize rigorous variances - v_rigorousVariances_.at(block).redim(v_unknownsCount_.at(block), v_unknownsCount_.at(block)); + + if (projectSettings_.a.stage || projectSettings_.a.multi_thread) + v_rigorousVariances_.at(block).redim_packed(v_unknownsCount_.at(block)); // resize junction variances - v_junctionVariances_.at(block).redim(j, j); + v_junctionVariances_.at(block).redim_packed(j); // resize junction station coordinate estimate arrays if (!v_blockMeta_.at(block)._blockLast && !v_blockMeta_.at(block)._blockIsolated) { - v_junctionVariancesFwd_.at(block).redim(j, j); + v_junctionVariancesFwd_.at(block).redim_packed(j); v_junctionEstimatesFwd_.at(block).redim(j, 1); v_junctionEstimatesRev_.at(block+1).redim(j, 1); } @@ -860,11 +881,10 @@ void dna_adjust::PrepareDesignAndMsrMnsCmpMatrices(const UINT32& block) // Simultaneous, phased (multithreaded and block1) adjustments // Redim all matrices - v_normals_.at(block).redim(v_unknownsCount_.at(block), v_unknownsCount_.at(block)); + v_normals_.at(block).redim_packed(v_unknownsCount_.at(block)); v_design_.at(block).redim(v_measurementCount_.at(block), v_unknownsCount_.at(block)); v_corrections_.at(block).redim(v_unknownsCount_.at(block), 1); - v_precAdjMsrsFull_.at(block).redim(v_measurementVarianceCount_.at(block), 1); // All blocks in v_correctionsR_ are used for multi thread mode, but only the last is used // in single thread mode @@ -897,9 +917,7 @@ void dna_adjust::PrepareDesignAndMsrMnsCmpMatricesStage(const UINT32& block) if (projectSettings_.a.recreate_stage_files || !bms_meta_.reduced) { // Redim NormalsR - v_normalsR_.at(block).redim( - v_unknownsCount_.at(block), - v_unknownsCount_.at(block)); + v_normalsR_.at(block).redim_packed(v_unknownsCount_.at(block)); // Redimension the corrections matrices v_corrections_.at(block).redim(v_unknownsCount_.at(block), 1); @@ -930,7 +948,41 @@ void dna_adjust::PrepareDesignAndMsrMnsCmpMatricesStage(const UINT32& block) // Creating new memory mapped files? Form the design matrices FillDesignNormalMeasurementsMatrices(true, block, false); } - + + +void dna_adjust::RebuildDesignAndAtVinv(const UINT32& block) +{ + UINT32 pseudoMsrElemCount(0); + + if (!v_blockMeta_.at(block)._blockIsolated) + { + UINT32 pseudoMsrCount = static_cast(v_JSL_.at(block).size()); + if (!v_blockMeta_.at(block)._blockFirst) + pseudoMsrCount += static_cast(v_JSL_.at(block-1).size()); + pseudoMsrElemCount = pseudoMsrCount * 3; + } + + v_design_.at(block).redim(v_measurementCount_.at(block), v_unknownsCount_.at(block)); + v_AtVinv_.at(block).redim( + v_unknownsCount_.at(block), + v_measurementCount_.at(block) + pseudoMsrElemCount); + + if (pseudoMsrElemCount > 0) + v_AtVinv_.at(block).shrink(0, pseudoMsrElemCount); + + assert(v_design_.at(block).getbuffer() != nullptr && "RebuildDesignAtVinv: design null after redim"); + assert(v_AtVinv_.at(block).getbuffer() != nullptr && "RebuildDesignAtVinv: AtVinv null after redim"); + assert(v_estimatedStations_.at(block).getbuffer() != nullptr && "RebuildDesignAtVinv: estimatedStations null"); + assert(v_measMinusComp_.at(block).getbuffer() != nullptr && "RebuildDesignAtVinv: measMinusComp null"); + + rebuildingDesign_ = true; + FillDesignNormalMeasurementsMatrices(true, block, false, true); + rebuildingDesign_ = false; + + assert(v_design_.at(block).getbuffer() != nullptr && "RebuildDesignAtVinv: design null after Fill"); + assert(v_AtVinv_.at(block).getbuffer() != nullptr && "RebuildDesignAtVinv: AtVinv null after Fill"); +} + // Re-form At * V-1 for next block using estimated junction parameter station variances // nextBlock = currentBlock+1 @@ -1233,9 +1285,9 @@ void dna_adjust::UpdateAtVinv(pit_vmsr_t _it_msr, const UINT32& stn1, const UINT std::fixed << std::setprecision(16) << std::setw(26) << variance << std::endl; // Build At * V-1 - AtVinv->put(stn1, design_row, variance * design->get(design_row, stn1)); - AtVinv->put(stn1+1, design_row, variance * design->get(design_row, stn1+1)); - AtVinv->put(stn1+2, design_row, variance * design->get(design_row, stn1+2)); + AtVinv->dense_put(stn1, design_row, variance * design->dense_get(design_row, stn1)); + AtVinv->dense_put(stn1+1, design_row, variance * design->dense_get(design_row, stn1+1)); + AtVinv->dense_put(stn1+2, design_row, variance * design->dense_get(design_row, stn1+2)); // Single station measurements switch ((*_it_msr)->measType) @@ -1250,16 +1302,16 @@ void dna_adjust::UpdateAtVinv(pit_vmsr_t _it_msr, const UINT32& stn1, const UINT } // Two station measurements - AtVinv->put(stn2, design_row, variance * design->get(design_row, stn2)); - AtVinv->put(stn2+1, design_row, variance * design->get(design_row, stn2+1)); - AtVinv->put(stn2+2, design_row, variance * design->get(design_row, stn2+2)); + AtVinv->dense_put(stn2, design_row, variance * design->dense_get(design_row, stn2)); + AtVinv->dense_put(stn2+1, design_row, variance * design->dense_get(design_row, stn2+1)); + AtVinv->dense_put(stn2+2, design_row, variance * design->dense_get(design_row, stn2+2)); switch ((*_it_msr)->measType) { case 'A': // Horizontal angle - AtVinv->put(stn3, design_row, variance * design->get(design_row, stn3)); - AtVinv->put(stn3+1, design_row, variance * design->get(design_row, stn3+1)); - AtVinv->put(stn3+2, design_row, variance * design->get(design_row, stn3+2)); + AtVinv->dense_put(stn3, design_row, variance * design->dense_get(design_row, stn3)); + AtVinv->dense_put(stn3+1, design_row, variance * design->dense_get(design_row, stn3+1)); + AtVinv->dense_put(stn3+2, design_row, variance * design->dense_get(design_row, stn3+2)); } } @@ -1269,27 +1321,27 @@ void dna_adjust::UpdateAtVinv_D(const UINT32& stn1, const UINT32& stn2, const UI UINT32& design_row, UINT32& design_row_begin, matrix_2d* Vinv, matrix_2d* design, matrix_2d* AtVinv) { + const double angle_variance = Vinv->dense_get(angle, angle); for (UINT32 j, i(0); i<3; ++i) // for each coordinate element (x, y, z) { // add variances - AtVinv->elementadd(stn1+i, design_row, // station1 - design->get(design_row, stn1+i) * Vinv->get(angle, angle)); - AtVinv->elementadd(stn2+i, design_row, // station2 - design->get(design_row, stn2+i) * Vinv->get(angle, angle)); - AtVinv->elementadd(stn3+i, design_row, // station3 - design->get(design_row, stn3+i) * Vinv->get(angle, angle)); + const double design1 = design->dense_get(design_row, stn1+i); + const double design2 = design->dense_get(design_row, stn2+i); + const double design3 = design->dense_get(design_row, stn3+i); + + AtVinv->dense_add(stn1+i, design_row, design1 * angle_variance); // station1 + AtVinv->dense_add(stn2+i, design_row, design2 * angle_variance); // station2 + AtVinv->dense_add(stn3+i, design_row, design3 * angle_variance); // station3 // add covariances for (j=0; jelementadd(stn1+i, design_row_begin+j, // station1 - design->get(design_row, stn1+i) * Vinv->get(angle, j)); - AtVinv->elementadd(stn2+i, design_row_begin+j, // station2 - design->get(design_row, stn2+i) * Vinv->get(angle, j)); - AtVinv->elementadd(stn3+i, design_row_begin+j, // station3 - design->get(design_row, stn3+i) * Vinv->get(angle, j)); + const double angle_covariance = Vinv->dense_get(angle, j); + AtVinv->dense_add(stn1+i, design_row_begin+j, design1 * angle_covariance); // station1 + AtVinv->dense_add(stn2+i, design_row_begin+j, design2 * angle_covariance); // station2 + AtVinv->dense_add(stn3+i, design_row_begin+j, design3 * angle_covariance); // station3 } } } @@ -1302,6 +1354,7 @@ void dna_adjust::UpdateAtVinv_D(const UINT32& stn1, const UINT32& stn2, const UI // - PrepareFwdAdj (used by adjust_forward_thread) void dna_adjust::UpdateNormals(const UINT32& block, bool MT_ReverseOrCombine) { + const auto profile_start = profileTimings_ ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; UINT32 stn1, stn2, stn3, design_row(0); it_vUINT32 _it_block_msr; @@ -1382,22 +1435,59 @@ void dna_adjust::UpdateNormals(const UINT32& block, bool MT_ReverseOrCombine) "'." << std::endl; SignalExceptionAdjustment(ss.str(), block); } - } + } + + if (profileTimings_) + { + const auto elapsed = std::chrono::steady_clock::now() - profile_start; + profileUpdateNormalsNs_.fetch_add( + static_cast(std::chrono::duration_cast(elapsed).count()), + std::memory_order_relaxed); + } } +namespace { + +inline void add_normal_3x3_from_design_row(UINT32 design_row, UINT32 atvinv_station, UINT32 design_station, + matrix_2d* normals, const matrix_2d* design, const matrix_2d* AtVinv) +{ + const double* atvinv_col = AtVinv->dense_ptr(atvinv_station, design_row); + + for (UINT32 col = 0; col < 3; ++col) + { + const double design_value = *design->dense_ptr(design_row, design_station + col); + normals->lower_add(atvinv_station, design_station + col, + atvinv_col[0] * design_value); + normals->lower_add(atvinv_station + 1, design_station + col, + atvinv_col[1] * design_value); + normals->lower_add(atvinv_station + 2, design_station + col, + atvinv_col[2] * design_value); + } +} + +inline void add_normal_3x3_from_atvinv_columns(UINT32 design_row, UINT32 atvinv_station, UINT32 normal_station, + double scale, matrix_2d* normals, const matrix_2d* AtVinv) +{ + for (UINT32 col = 0; col < 3; ++col) + { + const double* atvinv_col = AtVinv->dense_ptr(atvinv_station, design_row + col); + normals->lower_add(atvinv_station, normal_station + col, + scale * atvinv_col[0]); + normals->lower_add(atvinv_station + 1, normal_station + col, + scale * atvinv_col[1]); + normals->lower_add(atvinv_station + 2, normal_station + col, + scale * atvinv_col[2]); + } +} + +} // namespace + void dna_adjust::AddMsrtoNormalsVar(const UINT32& design_row, const UINT32& stn, matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv) { // Add weighted measurement contributions to normal matrix - for (UINT32 row, col(0); col<3; ++col) - { - for (row=0; row<3; ++row) - { - normals->elementadd(stn+row, stn+col, - AtVinv->get(stn+row, design_row) * design->get(design_row, stn+col)); - } - } + add_normal_3x3_from_design_row(design_row, stn, stn, normals, design, AtVinv); } @@ -1405,50 +1495,20 @@ void dna_adjust::AddMsrtoNormalsCoVar2(const UINT32& design_row, const UINT32& s matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv) { // Add covariance terms (station 1 and station 2) to normal matrix - for (UINT32 row, col(0); col<3; ++col) - { - for (row=0; row<3; ++row) - { - // 1-2 - normals->elementadd(stn1+row, stn2+col, - AtVinv->get(stn1+row, design_row) * design->get(design_row, stn2+col)); - - normals->elementadd(stn2+row, stn1+col, - AtVinv->get(stn2+row, design_row) * design->get(design_row, stn1+col)); - } - } + add_normal_3x3_from_design_row(design_row, stn1, stn2, normals, design, AtVinv); + add_normal_3x3_from_design_row(design_row, stn2, stn1, normals, design, AtVinv); } void dna_adjust::AddMsrtoNormalsCoVar3(const UINT32& design_row, const UINT32& stn1, const UINT32& stn2, const UINT32& stn3, matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv) { // Add covariance terms (station 1, station 2, station 3) to normal matrix - for (UINT32 row, col(0); col<3; ++col) - { - for (row=0; row<3; ++row) - { - // 1-2 - normals->elementadd(stn1+row, stn2+col, - AtVinv->get(stn1+row, design_row) * design->get(design_row, stn2+col)); - - normals->elementadd(stn2+row, stn1+col, - AtVinv->get(stn2+row, design_row) * design->get(design_row, stn1+col)); - - // 1-3 - normals->elementadd(stn1+row, stn3+col, - AtVinv->get(stn1+row, design_row) * design->get(design_row, stn3+col)); - - normals->elementadd(stn3+row, stn1+col, - AtVinv->get(stn3+row, design_row) * design->get(design_row, stn1+col)); - - // 2-3 - normals->elementadd(stn2+row, stn3+col, - AtVinv->get(stn2+row, design_row) * design->get(design_row, stn3+col)); - - normals->elementadd(stn3+row, stn2+col, - AtVinv->get(stn3+row, design_row) * design->get(design_row, stn2+col)); - } - } + add_normal_3x3_from_design_row(design_row, stn1, stn2, normals, design, AtVinv); + add_normal_3x3_from_design_row(design_row, stn2, stn1, normals, design, AtVinv); + add_normal_3x3_from_design_row(design_row, stn1, stn3, normals, design, AtVinv); + add_normal_3x3_from_design_row(design_row, stn3, stn1, normals, design, AtVinv); + add_normal_3x3_from_design_row(design_row, stn2, stn3, normals, design, AtVinv); + add_normal_3x3_from_design_row(design_row, stn3, stn2, normals, design, AtVinv); } @@ -1471,7 +1531,7 @@ void dna_adjust::UpdateNormals_A(const UINT32& stn1, const UINT32& stn2, const U void dna_adjust::UpdateNormals_D(const UINT32& block, it_vmsr_t& _it_msr, UINT32& design_row, matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv) { - UINT32 row, col, a, angle_count(_it_msr->vectorCount2 - 1); + UINT32 a, angle_count(_it_msr->vectorCount2 - 1); UINT32 skip(0), ignored(_it_msr->vectorCount1 - _it_msr->vectorCount2); std::vector stations; @@ -1522,22 +1582,11 @@ void dna_adjust::UpdateNormals_D(const UINT32& block, it_vmsr_t& _it_msr, UINT32 } // station 1 - for (col=0; col<3; ++col) - for (row=0; row<3; ++row) - normals->elementadd(stn1+row, stn1+col, - AtVinv->get(stn1+row, design_row+a) * design->get(design_row+a, stn1+col)); - // + add_normal_3x3_from_design_row(design_row+a, stn1, stn1, normals, design, AtVinv); // station 2 - for (col=0; col<3; ++col) - for (row=0; row<3; ++row) - normals->elementadd(stn2+row, stn2+col, - AtVinv->get(stn2+row, design_row+a) * design->get(design_row+a, stn2+col)); - // + add_normal_3x3_from_design_row(design_row+a, stn2, stn2, normals, design, AtVinv); // station 3 - for (col=0; col<3; ++col) - for (row=0; row<3; ++row) - normals->elementadd(stn3+row, stn3+col, - AtVinv->get(stn3+row, design_row+a) * design->get(design_row+a, stn3+col)); + add_normal_3x3_from_design_row(design_row+a, stn3, stn3, normals, design, AtVinv); if (a+1 == angle_count) break; @@ -1568,17 +1617,8 @@ void dna_adjust::UpdateNormals_D(const UINT32& block, it_vmsr_t& _it_msr, UINT32 if (stn2 == stn1) continue; - for (col=0; col<3; ++col) - { - for (row=0; row<3; ++row) - { - // 1-2 - normals->elementadd(stn1+row, stn2+col, - AtVinv->get(stn1+row, design_row+a) * design->get(design_row+a, stn2+col)); - normals->elementadd(stn2+row, stn1+col, - AtVinv->get(stn2+row, design_row+a) * design->get(design_row+a, stn1+col)); - } - } + add_normal_3x3_from_design_row(design_row+a, stn1, stn2, normals, design, AtVinv); + add_normal_3x3_from_design_row(design_row+a, stn2, stn1, normals, design, AtVinv); } } it_angle++; @@ -1615,39 +1655,21 @@ void dna_adjust::UpdateNormals_HIJPQR(const UINT32& stn1, UINT32& design_row, void dna_adjust::UpdateNormals_G(const UINT32& stn1, const UINT32& stn2, UINT32& design_row, matrix_2d* normals, matrix_2d* AtVinv) { - UINT32 col, row; - // station 2 - for (col=0; col<3; ++col) - for (row=0; row<3; ++row) - normals->elementadd(stn2+row, stn2+col, - // AtVinv->get(stn2+row, design_row+col) * design->get(design_row+col, stn2+col)); - // No need to multiply by 1 as stn2 design element is always 1. See UpdateDesignMeasMatrices_GX() - AtVinv->get(stn2+row, design_row+col)); + // No need to multiply by 1 as stn2 design element is always 1. See UpdateDesignMeasMatrices_GX() + add_normal_3x3_from_atvinv_columns(design_row, stn2, stn2, 1., normals, AtVinv); // station 1 - for (col=0; col<3; ++col) - for (row=0; row<3; ++row) - normals->elementadd(stn1+row, stn1+col, - // AtVinv->get(stn1+row, design_row+col) * design->get(design_row+col, stn1+col)); - // No need to multiply by -1 as stn1 design element is always -1. See UpdateDesignMeasMatrices_GX() - -AtVinv->get(stn1+row, design_row+col)); + // No need to multiply by -1 as stn1 design element is always -1. See UpdateDesignMeasMatrices_GX() + add_normal_3x3_from_atvinv_columns(design_row, stn1, stn1, -1., normals, AtVinv); // covariance terms (station 1 and station 2) - for (col=0; col<3; ++col) - for (row=0; row<3; ++row) - normals->elementadd(stn1+row, stn2+col, - // AtVinv->get(stn1+row, design_row+col) * design->get(design_row+col, stn2+col)); - // No need to multiply as stn2 is always 1. See UpdateDesignMeasMatrices_GX() - AtVinv->get(stn1+row, design_row+col)); + // No need to multiply as stn2 is always 1. See UpdateDesignMeasMatrices_GX() + add_normal_3x3_from_atvinv_columns(design_row, stn1, stn2, 1., normals, AtVinv); // covariance terms (station 2 and station 1) - for (col=0; col<3; ++col) - for (row=0; row<3; ++row) - normals->elementadd(stn2+row, stn1+col, - // AtVinv->get(stn2+row, design_row+col) * design->get(design_row+col, stn1+col)); - // No need to multiply by -1 as stn1 design element is always -1. See UpdateDesignMeasMatrices_GX() - -AtVinv->get(stn2+row, design_row+col)); + // No need to multiply by -1 as stn1 design element is always -1. See UpdateDesignMeasMatrices_GX() + add_normal_3x3_from_atvinv_columns(design_row, stn2, stn1, -1., normals, AtVinv); design_row += 3; } @@ -2112,6 +2134,7 @@ _ADJUST_STATUS_ dna_adjust::AdjustNetwork() isIterationComplete_ = false; isAdjustmentQuestionable_ = false; iterationCorrections_.clear_messages(); + iterationTimes_.clear_messages(); if (projectSettings_.o._database_ids) LoadDatabaseId(); @@ -2383,7 +2406,12 @@ void dna_adjust::AdjustSimultaneous() { adjustStatus_ = ADJUST_SUCCESS; initialiseIteration(); - + + // Reset oscillation diagnostics + corrPrev_.clear(); + stnOscCount_.clear(); + oscHistory_.clear(); + std::ostringstream ss; std::string corr_msg; std::chrono::milliseconds elapsed_time(std::chrono::milliseconds(0)); @@ -2429,9 +2457,11 @@ void dna_adjust::AdjustSimultaneous() // Compute and print largest correction maxCorr_ = v_corrections_.at(0).compute_maximum_value(); OutputLargestCorrection(corr_msg); + UpdateIterationDiagnostics(); // update data for messages iterationCorrections_.add_message(corr_msg); + iterationTimes_.add_message(FormatElapsedTime(it_time.elapsed().wall.count() / 1.0e9)); iterationQueue_.push_and_notify(CurrentIteration()); // currentIteration begins at 1, so not zero-indexed isIterationComplete_ = true; @@ -2489,7 +2519,7 @@ void dna_adjust::ValidateandFinaliseAdjustment(cpu_timer& tot_time) fabs(maxCorr_) > projectSettings_.a.iteration_threshold) adjustStatus_ = ADJUST_MAX_ITERATIONS_EXCEEDED; - // Back up simultaneous rigorous variance estimates (for serialising + // Back up simultaneous rigorous variance estimates (for serialising // to disk at SerialiseAdjustedVarianceMatrices() ), so that executing adjust // in report-results mode has access to the latest variance estimates switch (projectSettings_.a.adjust_mode) @@ -2507,6 +2537,7 @@ void dna_adjust::ValidateandFinaliseAdjustment(cpu_timer& tot_time) printer_->PrintAdjustmentStatus(); // Compute and print time taken to run adjustment PrintAdjustmentTime(tot_time, total_time); + PrintPerformanceProfile(); } void dna_adjust::PrintAdjustmentTime(cpu_timer& time, _TIMER_TYPE_ timerType) @@ -2520,17 +2551,39 @@ void dna_adjust::PrintAdjustmentTime(cpu_timer& time, _TIMER_TYPE_ timerType) printer_->PrintAdjustmentTime(time, static_cast(timerType)); } +void dna_adjust::PrintPerformanceProfile() const +{ + if (!profileTimings_) + return; + + const auto ns_to_ms = [](uint64_t ns) { + return static_cast(ns) / 1000000.0; + }; + + std::cerr << "DynAdjust profile timings:" + << " update_normals=" << std::fixed << std::setprecision(3) + << ns_to_ms(profileUpdateNormalsNs_.load(std::memory_order_relaxed)) << "ms" + << " stage_load=" << ns_to_ms(profileStageLoadNs_.load(std::memory_order_relaxed)) << "ms" + << " stage_store=" << ns_to_ms(profileStageStoreNs_.load(std::memory_order_relaxed)) << "ms" + << std::endl; +} + void dna_adjust::AdjustPhased() { initialiseIteration(); + // Reset oscillation diagnostics + corrPrev_.clear(); + stnOscCount_.clear(); + oscHistory_.clear(); + std::string corr_msg; std::ostringstream ss; UINT32 i; bool iterate(true); cpu_timer it_time, tot_time; - + // do until convergence criteria is met for (i=0; iPrintIteration(incrementIteration()); - + it_time.start(); AdjustPhasedForward(); @@ -2567,8 +2620,10 @@ void dna_adjust::AdjustPhased() // Calculate and print largest adjustment correction and station ID OutputLargestCorrection(corr_msg); - + UpdateIterationDiagnostics(); + iterationCorrections_.add_message(corr_msg); + iterationTimes_.add_message(FormatElapsedTime(it_time.elapsed().wall.count() / 1.0e9)); iterationQueue_.push_and_notify(CurrentIteration()); // currentIteration begins at 1, so not zero-indexed isIterationComplete_ = true; @@ -2581,7 +2636,7 @@ void dna_adjust::AdjustPhased() // Similar to PrepareAdjustment, UpdateAdjustment prepares every block // in the network so that forward and reverse adjustments can commence // at the same time. - UpdateAdjustment(iterate); + UpdateAdjustment(iterate); if (IsCancelled()) break; @@ -2590,7 +2645,7 @@ void dna_adjust::AdjustPhased() { // Compute network statistics ComputeStatisticsOnIteration(); - + // Print statistics summary to adj file printer_->PrintStatistics(false); } @@ -2645,8 +2700,9 @@ void dna_adjust::AdjustPhasedBlock1() if (fabs(maxCorr_) > projectSettings_.a.iteration_threshold) adjustStatus_ = ADJUST_THRESHOLD_EXCEEDED; - + iterationCorrections_.add_message(corr_msg); + iterationTimes_.add_message(FormatElapsedTime(it_time.elapsed().wall.count() / 1.0e9)); iterationQueue_.push_and_notify(CurrentIteration()); // currentIteration begins at 1, so not zero-indexed ValidateandFinaliseAdjustment(tot_time); @@ -2695,9 +2751,10 @@ void dna_adjust::AdjustPhasedForward() UINT32 currentBlock(0); - // For staged adjustments, load the first block from mapped memory file + // For staged adjustments, prefetch and load the first block from mapped memory file if (projectSettings_.a.stage) { + AdviseBlockWillNeed(currentBlock); DeserialiseBlockFromMappedFile(currentBlock); RebuildNormals(currentBlock, __forward__, true, true); if (IsCancelled()) @@ -2735,16 +2792,21 @@ void dna_adjust::AdjustPhasedForward() // At this point, whether first iteration or not, if currentBlock is the first block, // the normals will have been initialised. For all later blocks, the normals will contain // the contribution of junction station coordinates and variances from preceding blocks. - // In either case, the block is ready for adjustment. Junction station coordinates and + // In either case, the block is ready for adjustment. Junction station coordinates and // variances are carried forward below + assert(v_design_.at(currentBlock).getbuffer() != nullptr && "AdjFwd: design null before SolveTry"); + assert(v_AtVinv_.at(currentBlock).getbuffer() != nullptr && "AdjFwd: AtVinv null before SolveTry"); + assert(v_normals_.at(currentBlock).getbuffer() != nullptr && "AdjFwd: normals null before SolveTry"); + assert(v_measMinusComp_.at(currentBlock).getbuffer() != nullptr && "AdjFwd: measMinusComp null before SolveTry"); + // Least Squares Solution SolveTry(true, currentBlock); // Does the user want to print adjusted measurements // on each iteration? - if (projectSettings_.o._adj_msr_iteration || - projectSettings_.o._adj_stn_iteration || + if (projectSettings_.o._adj_msr_iteration || + projectSettings_.o._adj_stn_iteration || projectSettings_.o._cmp_msr_iteration) adj_file << " done." << std::endl; @@ -2766,21 +2828,21 @@ void dna_adjust::AdjustPhasedForward() // OK, now shrink matrices back to normal size ShrinkForwardMatrices(currentBlock); - // Carry the estimated junction station coordinates and variances + // Carry the estimated junction station coordinates and variances // to the next block for applicable blocks only. For staged - // Deserialise the next block from mapped files and rebuild + // Deserialise the next block from mapped files and rebuild // normals CarryForwardJunctions(currentBlock, currentBlock+1); // For staged adjustments, write to disk and unload matrix data if (projectSettings_.a.stage) - // Don't offload last block since the reverse adjustment + // Don't offload last block since the reverse adjustment // will need this block if (currentBlock < (blockCount_ - 1)) OffloadBlockToMappedFile(currentBlock); } } - + void dna_adjust::PurgeMatricesFromDisk() { @@ -2885,8 +2947,7 @@ void dna_adjust::PrepareAdjustmentBlock(const UINT32 block, const UINT32 thread_ v_normalsR_.at(block) = v_normals_.at(block); if (projectSettings_.a.multi_thread) { - v_normalsRC_.at(block).redim( - v_normalsR_.at(block).rows(), v_normalsR_.at(block).columns()); + v_normalsRC_.at(block).redim_packed(v_normalsR_.at(block).rows()); } switch (projectSettings_.a.adjust_mode) @@ -2940,6 +3001,8 @@ void dna_adjust::ShrinkForwardMatrices(const UINT32 currentBlock) { pseudoMsrElemCount = static_cast(v_JSL_.at(currentBlock-1).size() * 3); + assert(v_measMinusComp_.at(currentBlock).getbuffer() != nullptr && "ShrinkFwd: measMinusComp null"); + assert(v_AtVinv_.at(currentBlock).getbuffer() != nullptr && "ShrinkFwd: AtVinv null"); v_measMinusComp_.at(currentBlock).shrink(pseudoMsrElemCount, 0); v_AtVinv_.at(currentBlock).shrink(0, pseudoMsrElemCount); } @@ -2974,7 +3037,9 @@ void dna_adjust::UpdateEstimatesForward(const UINT32 currentBlock) // Now copy 'estimated' coordinates to 'rigorous' for comparison on the next iteration v_rigorousStations_.at(currentBlock) = v_estimatedStations_.at(currentBlock); + assert(v_normals_.at(currentBlock).getbuffer() != nullptr && "UpdateEstFwd: normals null before copy to rigorousVariances"); v_rigorousVariances_.at(currentBlock) = v_normals_.at(currentBlock); + assert(v_rigorousVariances_.at(currentBlock).getbuffer() != nullptr && "UpdateEstFwd: rigorousVariances null after copy"); // Temporarily hold corrections for last block. These corrections will be restored // in UpdateEstimatesFinal() @@ -3024,9 +3089,10 @@ void dna_adjust::CarryForwardJunctions(const UINT32 thisBlock, const UINT32 next if (v_blockMeta_.at(nextBlock)._blockIsolated) return; - // For staged adjustments, load block info for nextBlock + // For staged adjustments, prefetch and load block info for nextBlock if (projectSettings_.a.stage) { + AdviseBlockWillNeed(nextBlock); DeserialiseBlockFromMappedFile(nextBlock); RebuildNormals(nextBlock, __forward__, true, true); } @@ -3061,7 +3127,7 @@ bool dna_adjust::PrepareAdjustmentReverse(const UINT32 currentBlock, bool MT_Rev // OK. currentBlock is the last block, and this is the commencement of // a reverse run, so reset coordinates - matrix_2d* estimatedStations(&v_estimatedStations_.at(currentBlock)); + matrix_2d* estimatedStations(&v_estimatedStations_.at(currentBlock)); if (MT_ReverseOrCombine) { @@ -3069,15 +3135,23 @@ bool dna_adjust::PrepareAdjustmentReverse(const UINT32 currentBlock, bool MT_Rev } else { - // Restore back up copy of normals for reverse adjustment in + // Restore back up copy of normals for reverse adjustment in // single thread mode + assert(v_normalsR_.at(currentBlock).getbuffer() != nullptr && "PrepareAdjReverse: normalsR null before copy"); + assert(v_normalsR_.at(currentBlock).rows() > 0 && "PrepareAdjReverse: normalsR has 0 rows"); v_normals_.at(currentBlock) = v_normalsR_.at(currentBlock); + assert(v_normals_.at(currentBlock).getbuffer() != nullptr && "PrepareAdjReverse: normals null after copy"); } + assert(v_originalStations_.at(currentBlock).getbuffer() != nullptr && "PrepareAdjReverse: originalStations null"); // Reset coordinates to original values *estimatedStations = v_originalStations_.at(currentBlock); - + + assert(v_design_.at(currentBlock).rows() > 0 && "PrepareAdjReverse: design has 0 rows"); + assert(v_AtVinv_.at(currentBlock).rows() > 0 && "PrepareAdjReverse: AtVinv has 0 rows"); + assert(v_measMinusComp_.at(currentBlock).rows() > 0 && "PrepareAdjReverse: measMinusComp has 0 rows"); + AddConstraintStationstoNormalsReverse(currentBlock, MT_ReverseOrCombine); return true; @@ -3417,6 +3491,11 @@ void dna_adjust::AdjustPhasedReverseCombine() if (projectSettings_.g.verbose > 3) debug_file << "In isolation" << std::endl; + assert(v_design_.at(currentBlock).getbuffer() != nullptr && "design null before SolveTry (reverse combine)"); + assert(v_AtVinv_.at(currentBlock).getbuffer() != nullptr && "AtVinv null before SolveTry (reverse combine)"); + assert(v_normals_.at(currentBlock).getbuffer() != nullptr && "normals null before SolveTry (reverse combine)"); + assert(v_measMinusComp_.at(currentBlock).getbuffer() != nullptr && "measMinusComp null before SolveTry (reverse combine)"); + // Backup normals prior to inversion for re-use in combination // adjustment... only if a combination is required BackupNormals(currentBlock, false); @@ -3429,7 +3508,7 @@ void dna_adjust::AdjustPhasedReverseCombine() // Add corrections to estimates. // If --output-adj-iter-stat argument is supplied or currentBlock - // is the first block, then compute geographic coordinates, recompute + // is the first block, then compute geographic coordinates, recompute // meas-minus-computed vector, then print statistics. // Output of stats also prints largest correction. // If --output-adj-iter-stn argument is supplied, print station coords @@ -3437,16 +3516,16 @@ void dna_adjust::AdjustPhasedReverseCombine() // Debug and diagnose (if required) debug_BlockInformation(currentBlock, "(reverse, in isolation)"); - + // Carry the estimated junction station coordinates and variances to the next block and // combine. CarryReverseEstimates will return false if currentBlock is the first block - // or an isolated block. If estimates can be carried, CarryReverseEstimates updates + // or an isolated block. If estimates can be carried, CarryReverseEstimates updates // geographic coords (if req'd), recomputes meas-minus-comp vector and updates normals for // the next block. if (CarryReverseJunctions(currentBlock, currentBlock-1, false)) - { + { ////////////////////////////////////////////////////////////////////////// - // Combination Adjustment + // Combination Adjustment // // Perform combination on all blocks except the first and last as they will // be rigorous from reverse and forward adjustments respectively @@ -3454,13 +3533,13 @@ void dna_adjust::AdjustPhasedReverseCombine() // First, carry the junction station estimates and variances of the next block // (obtained during the forward pass). These were copied to v_junctionEstimatesFwd_ // and v_junctionVariances_ in CarryStnEstimatesandVariancesForward(..) during - // the forward pass. + // the forward pass. if (PrepareAdjustmentCombine(currentBlock, pseudomsrJSLCount, false)) { isCombining_ = true; if (projectSettings_.g.verbose > 3) debug_file << "Rigorous" << std::endl; - + if (projectSettings_.o._adj_stn_iteration) if (!v_blockMeta_.at(currentBlock)._blockFirst) adj_file << std::endl << std::left << "Adjusting block " << currentBlock+1 << " (reverse, rigorous)... "; @@ -3501,7 +3580,7 @@ void dna_adjust::AdjustPhasedReverseCombine() } // for (UINT32 block=0; blockgetbuffer() != nullptr && "UpdateEstFinal: AtVinv null"); + assert(measMinusComp->getbuffer() != nullptr && "UpdateEstFinal: measMinusComp null"); + assert(aposterioriVariances->getbuffer() != nullptr && "UpdateEstFinal: aposterioriVariances null"); if (v_blockMeta_.at(currentBlock)._blockFirst) { @@ -3758,13 +3844,16 @@ bool dna_adjust::CarryReverseJunctions(const UINT32 currentBlock, const UINT32 n v_normals_.at(nextBlock) = v_normalsR_.at(nextBlock); } - // For staged adjustments, load blocks info for nextBlock + // For staged adjustments, prefetch and load blocks info for nextBlock if (projectSettings_.a.stage) + { + AdviseBlockWillNeed(nextBlock); DeserialiseBlockFromMappedFile(nextBlock); + } // Next, update estimates for next block to original estimates *estimatedStationsNext = v_originalStations_.at(nextBlock); - + // For staged adjustments, rebuild design and AtVinv if (projectSettings_.a.stage) RebuildNormals(nextBlock, __reverse__, false, false); @@ -3788,25 +3877,25 @@ bool dna_adjust::CarryReverseJunctions(const UINT32 currentBlock, const UINT32 n // go through each of the measurements in the binary measurements file and formulate partial derivatives -void dna_adjust::FillDesignNormalMeasurementsMatrices(bool buildnewMatrices, const UINT32& block, bool MT_ReverseOrCombine) +void dna_adjust::FillDesignNormalMeasurementsMatrices(bool buildnewMatrices, const UINT32& block, bool MT_ReverseOrCombine, bool skipNormals) { UINT32 design_row(0); - + it_vUINT32 _it_block_msr; - it_vmsr_t _it_msr; + it_vmsr_t _it_msr; for (_it_block_msr=v_CML_.at(block).begin(); _it_block_msr!=v_CML_.at(block).end(); ++_it_block_msr) { if (InitialiseandValidateMsrPointer(_it_block_msr, _it_msr)) continue; - // When a target direction is found, continue to next element. + // When a target direction is found, continue to next element. if (_it_msr->measType == 'D') if (_it_msr->vectorCount2 < 1) continue; // Build AtVinv, Normals and Meas minus Comp vectors - UpdateDesignNormalMeasMatrices(&_it_msr, design_row, buildnewMatrices, block, MT_ReverseOrCombine); + UpdateDesignNormalMeasMatrices(&_it_msr, design_row, buildnewMatrices, block, MT_ReverseOrCombine, skipNormals); } } @@ -3815,6 +3904,9 @@ void dna_adjust::FillDesignNormalMeasurementsMatrices(bool buildnewMatrices, con // pre-adjustment value, otherwise back up the current value. bool dna_adjust::InitialiseMeasurement(pit_vmsr_t _it_msr, bool buildnewMatrices) { + if (rebuildingDesign_) + return false; + if (buildnewMatrices) { // Was this measurement reduced from previous @@ -3835,7 +3927,7 @@ bool dna_adjust::InitialiseMeasurement(pit_vmsr_t _it_msr, bool buildnewMatrices } -void dna_adjust::UpdateDesignNormalMeasMatrices(pit_vmsr_t _it_msr, UINT32& design_row, bool buildnewMatrices, const UINT32& block, bool MT_ReverseOrCombine) +void dna_adjust::UpdateDesignNormalMeasMatrices(pit_vmsr_t _it_msr, UINT32& design_row, bool buildnewMatrices, const UINT32& block, bool MT_ReverseOrCombine, bool skipNormals) { std::stringstream ss; @@ -3851,101 +3943,101 @@ void dna_adjust::UpdateDesignNormalMeasMatrices(pit_vmsr_t _it_msr, UINT32& desi design = &v_designR_.at(block); AtVinv = &v_AtVinvR_.at(block); measMinusComp = &v_measMinusCompR_.at(block); - } + } switch ((*_it_msr)->measType) { case 'A': // Horizontal angle UpdateDesignNormalMeasMatrices_A(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'B': // Geodetic azimuth case 'K': // Astronomic azimuth - // Note: UpdateDesignNormalMeasMatrices_BK reduces K measurements to the geodetic reference frame, + // Note: UpdateDesignNormalMeasMatrices_BK reduces K measurements to the geodetic reference frame, // after which UpdateDesignNormalMeasMatrices_BK treats K measurements as B measurements upon forming // design matrix elements. UpdateDesignNormalMeasMatrices_BK(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'C': // Chord dist UpdateDesignNormalMeasMatrices_C(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; - case 'D': // Direction set + case 'D': // Direction set UpdateDesignNormalMeasMatrices_D(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'E': // Ellipsoid arc UpdateDesignNormalMeasMatrices_E(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'G': // GPS Baseline UpdateDesignNormalMeasMatrices_G(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'H': // Orthometric height // Note: UpdateDesignNormalMeasMatrices_H reduces term1 to ellipsoid height, after which // UpdateDesignNormalMeasMatrices_HR is used to form design elements. UpdateDesignNormalMeasMatrices_H(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'I': // Astronomic latitude // Note: UpdateDesignNormalMeasMatrices_I reduces term1 to geodetic latitude, after which // UpdateDesignNormalMeasMatrices_IP is used to form design elements. UpdateDesignNormalMeasMatrices_I(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'J': // Astronomic longitude // Note: UpdateDesignNormalMeasMatrices_J reduces term1 to geodetic longitude, after which // UpdateDesignNormalMeasMatrices_JQ is used to form design elements. UpdateDesignNormalMeasMatrices_J(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'L': // Level difference UpdateDesignNormalMeasMatrices_L(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'M': // MSL arc UpdateDesignNormalMeasMatrices_M(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'P': // Geodetic latitude // Note: UpdateDesignNormalMeasMatrices_P archives the raw measurement, after which // UpdateDesignNormalMeasMatrices_IP is used to form design elements. UpdateDesignNormalMeasMatrices_P(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'Q': // Geodetic longitude // Note: UpdateDesignNormalMeasMatrices_Q archives the raw measurement, after which // UpdateDesignNormalMeasMatrices_JQ is used to form design elements. UpdateDesignNormalMeasMatrices_Q(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'R': // Ellipsoidal height // Note: UpdateDesignNormalMeasMatrices_R archives the raw measurement, after which // UpdateDesignNormalMeasMatrices_HR is used to form design elements. UpdateDesignNormalMeasMatrices_R(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'S': // Slope distance UpdateDesignNormalMeasMatrices_S(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; case 'V': // Zenith distance UpdateDesignNormalMeasMatrices_V(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'X': // GPS Baseline cluster UpdateDesignNormalMeasMatrices_X(_it_msr, design_row, block, - measMinusComp, estimatedStations, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'Y': // GPS Point cluster UpdateDesignNormalMeasMatrices_Y(_it_msr, design_row, block, - measMinusComp, estimatedStations, design, AtVinv, buildnewMatrices); - break; + measMinusComp, estimatedStations, design, AtVinv, buildnewMatrices, skipNormals); + break; case 'Z': // Vertical angle UpdateDesignNormalMeasMatrices_Z(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); break; default: ss << "UpdateDesignNormalMeasMatrices(): Unknown measurement type - '" << @@ -4652,8 +4744,8 @@ void dna_adjust::AddMsrtoMeasMinusComp(pit_vmsr_t _it_msr, const UINT32& design_ void dna_adjust::UpdateDesignNormalMeasMatrices_A(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); UINT32 stn2(GetBlkMatrixElemStn2(block, _it_msr)); @@ -4679,11 +4771,11 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_A(pit_vmsr_t _it_msr, UINT32& de &direction12, &direction13, &local_12e, &local_12n, &local_13e, &local_13n)); - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { // deflections available? if (fabs(stn1_it->verticalDef) > E4_SEC_DEFLECTION || fabs(stn1_it->meridianDef) > E4_SEC_DEFLECTION) @@ -4802,7 +4894,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_A(pit_vmsr_t _it_msr, UINT32& de UpdateAtVinv(_it_msr, stn1, stn2, stn3, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_A(stn1, stn2, stn3, design_row, normals, design, AtVinv); else @@ -4811,8 +4903,8 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_A(pit_vmsr_t _it_msr, UINT32& de void dna_adjust::UpdateDesignNormalMeasMatrices_BK(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); UINT32 stn2(GetBlkMatrixElemStn2(block, _it_msr)); @@ -4834,11 +4926,11 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_BK(pit_vmsr_t _it_msr, UINT32& d stn1_it->currentLongitude, &local_12e, &local_12n)); - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - - if (buildnewMatrices) + + if (buildnewMatrices && !rebuildingDesign_) { // deflections available? if ((*_it_msr)->measType == 'K' && // Astro @@ -4906,19 +4998,19 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_BK(pit_vmsr_t _it_msr, UINT32& d // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, stn2, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_BCEKLMSVZ(stn1, stn2, design_row, normals, design, AtVinv); else design_row++; } - + void dna_adjust::UpdateDesignNormalMeasMatrices_C(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - // Initialise measurement and test if no further calculations are + // Initialise measurement and test if no further calculations are // required (as in stage mode) if (InitialiseMeasurement(_it_msr, buildnewMatrices)) return; @@ -4929,13 +5021,13 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_C(pit_vmsr_t _it_msr, UINT32& de // Now call UpdateDesignNormalMeasMatrices_CEM UpdateDesignNormalMeasMatrices_CEM(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } void dna_adjust::UpdateDesignNormalMeasMatrices_CEM(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); UINT32 stn2(GetBlkMatrixElemStn2(block, _it_msr)); @@ -4971,17 +5063,17 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_CEM(pit_vmsr_t _it_msr, UINT32& // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, stn2, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_BCEKLMSVZ(stn1, stn2, design_row, normals, design, AtVinv); else design_row++; } - + void dna_adjust::UpdateDesignNormalMeasMatrices_D(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { it_vmsr_t _it_msr_first(*_it_msr); UINT32 design_row_begin(design_row); @@ -5030,7 +5122,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_D(pit_vmsr_t _it_msr, UINT32& de it_angle->station3 = (*_it_msr)->station2; - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { // Was this measurement reduced from previous // adjustment, which was serialised to disk? @@ -5063,7 +5155,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_D(pit_vmsr_t _it_msr, UINT32& de UpdateDesignNormalMeasMatrices_A(&it_angle, design_row, block, measMinusComp, estimatedStations, 0, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { // Update derived angle, corrected for deflection of vertical (*_it_msr)->scale1 = it_angle->term1; @@ -5134,14 +5226,14 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_D(pit_vmsr_t _it_msr, UINT32& de it_angle++; } - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_D(block, _it_msr_first, design_row_begin, normals, design, AtVinv); } void dna_adjust::UpdateDesignNormalMeasMatrices_E(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { // Initialise measurement and test if no further calculations are // required (as in stage mode) @@ -5177,7 +5269,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_E(pit_vmsr_t _it_msr, UINT32& de // Now that the ellipsoid arc has been reduced to a chord, call UpdateDesignNormalMeasMatrices_CEM UpdateDesignNormalMeasMatrices_CEM(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } void dna_adjust::UpdateDesignMeasMatrices_GX(pit_vmsr_t _it_msr, UINT32& design_row, @@ -5251,8 +5343,8 @@ void dna_adjust::UpdateDesignMeasMatrices_GX(pit_vmsr_t _it_msr, UINT32& design_ } void dna_adjust::UpdateDesignNormalMeasMatrices_G(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { it_vmsr_t _it_msr_first(*_it_msr); UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); @@ -5274,7 +5366,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_G(pit_vmsr_t _it_msr, UINT32& de // For first time run or staged adjustment mode - if (buildnewMatrices || projectSettings_.a.stage) + if (buildnewMatrices || projectSettings_.a.stage || skipNormals) { // Load GPS Variance matrix. For the first run, scaling is applied // and the variances are written to the binary measurements list. @@ -5283,23 +5375,23 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_G(pit_vmsr_t _it_msr, UINT32& de if (buildnewMatrices && projectSettings_.a.stage) return; - + // Build At * V-1 AtVinv->replace(stn1, design_row_begin, var_cart * -1); AtVinv->replace(stn2, design_row_begin, var_cart); - if (buildnewMatrices && !projectSettings_.a.stage) + if (buildnewMatrices && !projectSettings_.a.stage && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_G(stn1, stn2, design_row_begin, normals, AtVinv); } - + design_row++; } - + void dna_adjust::UpdateDesignNormalMeasMatrices_M(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { // Initialise measurement and test if no further calculations are // required (as in stage mode) @@ -5324,22 +5416,22 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_M(pit_vmsr_t _it_msr, UINT32& de // Now that the MSL arc has been reduced to a chord, call UpdateDesignNormalMeasMatrices_CEM UpdateDesignNormalMeasMatrices_CEM(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } // Like zenith distances and vertical angles, the relationship between slope distances and the // coordinates of p1 and p2 requires a little extra work to take into consideration instrument // and target height. For the measurements-minus-computed vector, the "computed" distance -// is the true distance between the instrument and target, and so must take into consideration +// is the true distance between the instrument and target, and so must take into consideration // instrument and target heights. However, the dX, dY, dZ components for the partial // derivatives represent the true geometric difference between the two stations (not // instrument and target). void dna_adjust::UpdateDesignNormalMeasMatrices_S(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { // preAdjMeas is used to store original measured MSL arc distance - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { // This is the first time adjust has run, so // back up the raw measurement @@ -5349,7 +5441,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_S(pit_vmsr_t _it_msr, UINT32& de return; } - UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); + UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); UINT32 stn2(GetBlkMatrixElemStn2(block, _it_msr)); it_vstn_t_const stn1_it(bstBinaryRecords_.begin() + (*_it_msr)->station1); @@ -5357,7 +5449,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_S(pit_vmsr_t _it_msr, UINT32& de // compute dX, dY, dZ for instrument height (ih) and target height (th) double dXih, dYih, dZih, dXth, dYth, dZth; CartesianElementsFromInstrumentHeight((*_it_msr)->term3, // instrument height - &dXih, &dYih, &dZih, + &dXih, &dYih, &dZih, stn1_it->currentLatitude, stn1_it->currentLongitude); CartesianElementsFromInstrumentHeight((*_it_msr)->term4, // target height @@ -5385,7 +5477,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_S(pit_vmsr_t _it_msr, UINT32& de // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, stn2, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_BCEKLMSVZ(stn1, stn2, design_row, normals, design, AtVinv); else @@ -5402,10 +5494,10 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_S(pit_vmsr_t _it_msr, UINT32& de // partial derivatives represent the true geometric difference between the two stations (not // instrument and target). void dna_adjust::UpdateDesignNormalMeasMatrices_V(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); + UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); UINT32 stn2(GetBlkMatrixElemStn2(block, _it_msr)); it_vstn_t_const stn1_it(bstBinaryRecords_.begin() + (*_it_msr)->station1); @@ -5413,11 +5505,11 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_V(pit_vmsr_t _it_msr, UINT32& de double local_12e, local_12n, local_12up; - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { // deflections available? if (fabs(stn1_it->verticalDef) > E4_SEC_DEFLECTION || fabs(stn1_it->meridianDef) > E4_SEC_DEFLECTION) @@ -5493,7 +5585,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_V(pit_vmsr_t _it_msr, UINT32& de // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, stn2, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_BCEKLMSVZ(stn1, stn2, design_row, normals, design, AtVinv); else @@ -5506,15 +5598,15 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_V(pit_vmsr_t _it_msr, UINT32& de // Like zenith distances, the relationship between slope distances and the // coordinates of p1 and p2 requires a little extra work to take into consideration instrument // and target height. For the measurements-minus-computed vector, the "computed" distance -// is the true distance between the instrument and target, and so must take into consideration -// instrument and target heights. However, the dX, dY, dZ components for the partial -// derivatives represent the true geometric difference between the two stations (not +// is the true distance between the instrument and target, and so must take into consideration +// instrument and target heights. However, the dX, dY, dZ components for the partial +// derivatives represent the true geometric difference between the two stations (not // instrument and target). void dna_adjust::UpdateDesignNormalMeasMatrices_Z(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); + UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); UINT32 stn2(GetBlkMatrixElemStn2(block, _it_msr)); it_vstn_t_const stn1_it(bstBinaryRecords_.begin() + (*_it_msr)->station1); @@ -5522,11 +5614,11 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_Z(pit_vmsr_t _it_msr, UINT32& de double local_12e, local_12n, local_12up; - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { // deflections available? if (fabs(stn1_it->verticalDef) > E4_SEC_DEFLECTION || fabs(stn1_it->meridianDef) > E4_SEC_DEFLECTION) @@ -5602,21 +5694,21 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_Z(pit_vmsr_t _it_msr, UINT32& de // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, stn2, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_BCEKLMSVZ(stn1, stn2, design_row, normals, design, AtVinv); else design_row++; } - + // Orthometric height difference. -// This function requires geoid-ellipsoid separation in order to reduce -// to ellipsoidal height difference counterpart. +// This function requires geoid-ellipsoid separation in order to reduce +// to ellipsoidal height difference counterpart. // Hence, run geoid with -f, -s and -n options void dna_adjust::UpdateDesignNormalMeasMatrices_L(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); UINT32 stn2(GetBlkMatrixElemStn2(block, _it_msr)); @@ -5641,11 +5733,11 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_L(pit_vmsr_t _it_msr, UINT32& de &h1, &h2, &nu1, &nu2, &Zn1, &Zn2, datum_.GetEllipsoidRef())); - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { // N value available? if (fabs(stn1_it->geoidSep) > PRECISION_1E4 || @@ -5676,7 +5768,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_L(pit_vmsr_t _it_msr, UINT32& de // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, stn2, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_BCEKLMSVZ(stn1, stn2, design_row, normals, design, AtVinv); else @@ -5684,17 +5776,17 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_L(pit_vmsr_t _it_msr, UINT32& de } void dna_adjust::UpdateDesignNormalMeasMatrices_I(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { it_vstn_t_const stn1_it(bstBinaryRecords_.begin() + (*_it_msr)->station1); - + // deflections available? if (fabs(stn1_it->meridianDef) > E4_SEC_DEFLECTION) { @@ -5709,22 +5801,22 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_I(pit_vmsr_t _it_msr, UINT32& de } UpdateDesignNormalMeasMatrices_IP(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } void dna_adjust::UpdateDesignNormalMeasMatrices_J(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { it_vstn_t_const stn1_it(bstBinaryRecords_.begin() + (*_it_msr)->station1); - + // deflections available? if (fabs(stn1_it->verticalDef) > E4_SEC_DEFLECTION) { @@ -5740,27 +5832,27 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_J(pit_vmsr_t _it_msr, UINT32& de } UpdateDesignNormalMeasMatrices_JQ(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } void dna_adjust::UpdateDesignNormalMeasMatrices_P(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - // Initialise measurement and test if no further calculations are + // Initialise measurement and test if no further calculations are // required (as in stage mode) if (InitialiseMeasurement(_it_msr, buildnewMatrices)) return; UpdateDesignNormalMeasMatrices_IP(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } - + void dna_adjust::UpdateDesignNormalMeasMatrices_IP(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); @@ -5806,31 +5898,31 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_IP(pit_vmsr_t _it_msr, UINT32& d // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, 0, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_HIJPQR(stn1, design_row, normals, design, AtVinv); else design_row++; } - + void dna_adjust::UpdateDesignNormalMeasMatrices_Q(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - // Initialise measurement and test if no further calculations are + // Initialise measurement and test if no further calculations are // required (as in stage mode) if (InitialiseMeasurement(_it_msr, buildnewMatrices)) return; UpdateDesignNormalMeasMatrices_JQ(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } - + void dna_adjust::UpdateDesignNormalMeasMatrices_JQ(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); @@ -5858,26 +5950,26 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_JQ(pit_vmsr_t _it_msr, UINT32& d // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, 0, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_HIJPQR(stn1, design_row, normals, design, AtVinv); else design_row++; } - + void dna_adjust::UpdateDesignNormalMeasMatrices_H(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - // Initialise measurement. No need to test if no further calculations are + // Initialise measurement. No need to test if no further calculations are // required (as in stage mode), as this is done later (below) InitialiseMeasurement(_it_msr, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !rebuildingDesign_) { it_vstn_t_const stn1_it(bstBinaryRecords_.begin() + (*_it_msr)->station1); - + // N value available? if (fabs(stn1_it->geoidSep) > PRECISION_1E4) { @@ -5891,13 +5983,13 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_H(pit_vmsr_t _it_msr, UINT32& de } UpdateDesignNormalMeasMatrices_HR(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } void dna_adjust::UpdateDesignNormalMeasMatrices_HR(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { UINT32 stn1(GetBlkMatrixElemStn1(block, _it_msr)); @@ -5931,31 +6023,31 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_HR(pit_vmsr_t _it_msr, UINT32& d // Update AtVinv based on new design matrix elements UpdateAtVinv(_it_msr, stn1, 0, 0, design_row, design, AtVinv, buildnewMatrices); - if (buildnewMatrices) + if (buildnewMatrices && !skipNormals) // Add weighted measurement contributions to normal matrix UpdateNormals_HIJPQR(stn1, design_row, normals, design, AtVinv); else design_row++; } - + void dna_adjust::UpdateDesignNormalMeasMatrices_R(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { - // Initialise measurement and test if no further calculations are + // Initialise measurement and test if no further calculations are // required (as in stage mode) if (InitialiseMeasurement(_it_msr, buildnewMatrices)) return; UpdateDesignNormalMeasMatrices_HR(_it_msr, design_row, block, - measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices); + measMinusComp, estimatedStations, normals, design, AtVinv, buildnewMatrices, skipNormals); } - + void dna_adjust::UpdateDesignNormalMeasMatrices_X(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { it_vmsr_t _it_msr_first(*_it_msr); @@ -5992,7 +6084,7 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_X(pit_vmsr_t _it_msr, UINT32& de matrix_2d var_cart(baseline_count * 3, baseline_count * 3); - if (buildnewMatrices || projectSettings_.a.stage) + if (buildnewMatrices || projectSettings_.a.stage || skipNormals) { // Load apriori variance matrix, and assign to binary measurement // If required, apply scalars @@ -6002,10 +6094,10 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_X(pit_vmsr_t _it_msr, UINT32& de return; } - // If this method is called via PrepareAdjustment() and the adjustment + // If this method is called via PrepareAdjustment() and the adjustment // mode is staged, then don't update the AtVinv matrix. This will be // done during an adjustment via AdjustPhasedForward(). - if (!buildnewMatrices && !projectSettings_.a.stage) + if (!buildnewMatrices && !projectSettings_.a.stage && !skipNormals) return; UINT32 cluster_cov; @@ -6064,11 +6156,11 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_X(pit_vmsr_t _it_msr, UINT32& de _it_msr_temp += 3; } - if (projectSettings_.a.stage || !buildnewMatrices) + if (projectSettings_.a.stage || !buildnewMatrices || skipNormals) return; - + _it_msr_temp = _it_msr_first; - + matrix_2d tmp0(3, 3); // Build At * V-1 * A variances @@ -6147,8 +6239,8 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_X(pit_vmsr_t _it_msr, UINT32& de void dna_adjust::UpdateDesignNormalMeasMatrices_Y(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, - matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices) + matrix_2d* measMinusComp, matrix_2d* estimatedStations, + matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, bool skipNormals) { it_vmsr_t _it_msr_first(*_it_msr); it_vmsr_t tmp_msr; @@ -6361,23 +6453,23 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_Y(pit_vmsr_t _it_msr, UINT32& de matrix_2d var_cart(point_count * 3, point_count * 3); - if (buildnewMatrices || projectSettings_.a.stage) + if (buildnewMatrices || projectSettings_.a.stage || skipNormals) { // Load apriori variance matrix, and assign to binary measurement - // If required, propagate to cartesian reference frame and apply + // If required, propagate to cartesian reference frame and apply // scalars LoadVarianceMatrix_Y(_it_msr_first, &var_cart, coordType); - + // If preparing for a stage adjustment, return // Normals will be built for each block as needed if (buildnewMatrices && projectSettings_.a.stage) return; } - - // If this method is called via PrepareAdjustment() and the adjustment + + // If this method is called via PrepareAdjustment() and the adjustment // mode is staged, then don't update the AtVinv matrix. This will be // done during an adjustment via AdjustPhasedForward(). - if (!buildnewMatrices && !projectSettings_.a.stage) + if (!buildnewMatrices && !projectSettings_.a.stage && !skipNormals) return; UINT32 cluster_cov; @@ -6420,9 +6512,9 @@ void dna_adjust::UpdateDesignNormalMeasMatrices_Y(pit_vmsr_t _it_msr, UINT32& de design_row_begin += 3; } - if (projectSettings_.a.stage || !buildnewMatrices) + if (projectSettings_.a.stage || !buildnewMatrices || skipNormals) return; - + _it_msr_temp = _it_msr_first; // Add to At * V-1 * A @@ -6485,6 +6577,13 @@ void dna_adjust::SolveTry(bool COMPUTE_INVERSE, const UINT32& block) void dna_adjust::Solve(bool COMPUTE_INVERSE, const UINT32& block) { + assert(v_design_.at(block).rows() > 0 && "Solve: design has 0 rows"); + assert(v_design_.at(block).columns() > 0 && "Solve: design has 0 columns"); + assert(v_AtVinv_.at(block).rows() > 0 && "Solve: AtVinv has 0 rows"); + assert(v_AtVinv_.at(block).columns() > 0 && "Solve: AtVinv has 0 columns"); + assert(v_normals_.at(block).rows() > 0 && "Solve: normals has 0 rows"); + assert(v_measMinusComp_.at(block).rows() > 0 && "Solve: measMinusComp has 0 rows"); + // debug matrices if required debug_SolutionInformation(block); @@ -6504,33 +6603,24 @@ void dna_adjust::Solve(bool COMPUTE_INVERSE, const UINT32& block) // the normal matrix before inversion and subsequently reversing the effect. // - // 1. Create scalar matrix - matrix_2d *S = nullptr, *SN = nullptr; + // 1. Scale normals to unity using diagonal scaling + UINT32 n = v_normals_.at(block).rows(); + std::vector s_diag; if (projectSettings_.a.scale_normals_to_unity) { - S = new matrix_2d(v_normals_.at(block).rows(), v_normals_.at(block).rows()); - SN = new matrix_2d(v_normals_.at(block).rows(), v_normals_.at(block).rows()); - for (UINT32 i(0); iput(i, i, sqrt(v_normals_.at(block).get(i, i))); - // 2. Scale Normals to reduce the diagonal elements of Normals to unity - //SN->multiply(*S, v_normals_.at(block)); - SN->multiply(*S, "N", v_normals_.at(block), "N"); - - //v_normals_.at(block).multiply(*SN, *S); - v_normals_.at(block).multiply(*SN, "N", *S, "N"); + s_diag.resize(n); + for (UINT32 i(0); imultiply(*S, v_normals_.at(block)); - SN->multiply(*S, "N", v_normals_.at(block), "N"); - - //v_normals_.at(block).multiply(*SN, *S); - v_normals_.at(block).multiply(*SN, "N", *S, "N"); - - delete S; - delete SN; + v_normals_.at(block).scale_symmetric_diagonal(s_diag.data()); } ////////////////// } @@ -6567,10 +6650,13 @@ void dna_adjust::Solve(bool COMPUTE_INVERSE, const UINT32& block) // compute weighted "measured minus computed" matrix_2d At_Vinv_m(v_design_.at(block).columns(), 1); At_Vinv_m.multiply(v_AtVinv_.at(block), "N", v_measMinusComp_.at(block), "N"); - + // Solve corrections from normal equations v_corrections_.at(block).redim(v_design_.at(block).columns(), 1); - v_corrections_.at(block).multiply(v_normals_.at(block), "N", At_Vinv_m, "N"); + if (v_normals_.at(block).is_symmetric()) + v_corrections_.at(block).multiply_sym(v_normals_.at(block), At_Vinv_m); + else + v_corrections_.at(block).multiply(v_normals_.at(block), "N", At_Vinv_m, "N"); if (projectSettings_.g.verbose > 0) { @@ -6854,16 +6940,15 @@ void dna_adjust::ComputeAdjustedMsrPrecisions() if (projectSettings_.a.stage) { DeserialiseBlockFromMappedFile(block, 7, - sf_normals, sf_rigorous_vars, + sf_normals, sf_rigorous_vars, sf_design, sf_atvinv, sf_estimated_stns, sf_meas_minus_comp, sf_prec_adj_msrs); v_normals_.at(block) = v_rigorousVariances_.at(block); FillDesignNormalMeasurementsMatrices(false, block, false); } - // Compute adjusted measurement precisions (v_precAdjMsrsFull_) - // from design and rigorous station variances + // from design and rigorous station variances ComputePrecisionAdjMsrs(block); // Update measurement records and Pelzer's Global reliability @@ -6877,9 +6962,9 @@ void dna_adjust::ComputeAdjustedMsrPrecisions() case SimultaneousMode: case Phased_Block_1Mode: // only block 1 is rigorous // Compute adjusted measurement precisions (v_precAdjMsrsFull_) - // from design and rigorous station variances + // from design and rigorous station variances ComputePrecisionAdjMsrs(); - + // Update measurement records and Pelzer's Global reliability UpdateMsrRecords(); break; @@ -7239,7 +7324,6 @@ void dna_adjust::ComputeChiSquareNetwork() } } - void dna_adjust::ComputeChiSquarePhased(const UINT32& block) { // Compute adjusted measurement statistics @@ -7339,7 +7423,341 @@ void dna_adjust::OutputLargestCorrection(std::string& formatted_msg) formatted_msg = ss.str(); } - + +void dna_adjust::UpdateIterationDiagnostics() +{ + for (UINT32 block = 0; block < blockCount_; ++block) + { + // For staged adjustments, load corrections from mapped file + if (projectSettings_.a.stage) + DeserialiseBlockFromMappedFile(block, 1, sf_corrections); + + UINT32 nParams = v_corrections_.at(block).rows(); + UINT32 nStations = nParams / 3; + + for (UINT32 s = 0; s < nStations; ++s) + { + UINT32 stnIdx = v_parameterStationList_.at(block).at(s); + UINT32 base = s * 3; + + double cx = v_corrections_.at(block).get(base, 0); + double cy = v_corrections_.at(block).get(base + 1, 0); + double cz = v_corrections_.at(block).get(base + 2, 0); + + double magCurr = sqrt(cx*cx + cy*cy + cz*cz); + + auto prevIt = corrPrev_.find(stnIdx); + if (prevIt == corrPrev_.end()) + { + // First time seeing this station — store and move on + corrPrev_[stnIdx] = {cx, cy, cz}; + continue; + } + + double px = prevIt->second.cx; + double py = prevIt->second.cy; + double pz = prevIt->second.cz; + double magPrev = sqrt(px*px + py*py + pz*pz); + + // Update stored corrections + prevIt->second = {cx, cy, cz}; + + // Skip tiny corrections (sub-millimetre) + if (magCurr < 0.001 && magPrev < 0.001) + { + stnOscCount_[stnIdx] = 0; + continue; + } + + // Dot product and cosine of angle between correction vectors + double dot = cx*px + cy*py + cz*pz; + double denom = magCurr * magPrev; + double cosAngle = (denom > 1e-30) ? dot / denom : 0.0; + + // Magnitude ratio + double ratio = (magPrev > 1e-30) ? magCurr / magPrev : 0.0; + + // Oscillation: anti-parallel (cos < -0.5), similar magnitude (ratio 0.3–3.0) + if (cosAngle < -0.5 && ratio > 0.3 && ratio < 3.0) + stnOscCount_[stnIdx]++; + else + stnOscCount_[stnIdx] = 0; + + if (stnOscCount_[stnIdx] >= 2) + { + // Convert to local (e, n, up) + matrix_2d cart(3, 1), local(3, 1); + cart.put(0, 0, cx); + cart.put(1, 0, cy); + cart.put(2, 0, cz); + Rotate_CartLocal(cart, &local, + bstBinaryRecords_.at(stnIdx).currentLatitude, + bstBinaryRecords_.at(stnIdx).currentLongitude); + + double localMag = sqrt(local.get(0,0)*local.get(0,0) + + local.get(1,0)*local.get(1,0) + local.get(2,0)*local.get(2,0)); + + auto hit = oscHistory_.find(stnIdx); + if (hit == oscHistory_.end()) + { + OscillationRecord rec; + rec.stnBstIdx = stnIdx; + rec.firstIteration = CurrentIteration(); + rec.lastIteration = CurrentIteration(); + rec.maxCycles = stnOscCount_[stnIdx]; + rec.firstMag = localMag; + rec.lastMag = localMag; + rec.lastE = local.get(0, 0); + rec.lastN = local.get(1, 0); + rec.lastUp = local.get(2, 0); + oscHistory_[stnIdx] = rec; + } + else + { + hit->second.lastIteration = CurrentIteration(); + hit->second.maxCycles = stnOscCount_[stnIdx]; + hit->second.lastMag = localMag; + hit->second.lastE = local.get(0, 0); + hit->second.lastN = local.get(1, 0); + hit->second.lastUp = local.get(2, 0); + } + } + } + + // For staged adjustments, unload corrections + if (projectSettings_.a.stage) + UnloadBlock(block, 1, sf_corrections); + } +} + +void dna_adjust::PrintOscillationSummary() +{ + if (oscHistory_.empty()) + return; + + // Collect records with meaningful magnitude, sort descending + std::vector sorted; + for (auto& kv : oscHistory_) + { + double peak = std::max(kv.second.firstMag, kv.second.lastMag); + if (peak >= 0.1) + sorted.push_back(&kv.second); + } + + if (sorted.empty()) + return; + + std::sort(sorted.begin(), sorted.end(), + [](const OscillationRecord* a, const OscillationRecord* b) { + return std::max(a->firstMag, a->lastMag) > std::max(b->firstMag, b->lastMag); + }); + + // Cap at 20 stations + size_t limit = std::min(sorted.size(), static_cast(20)); + + std::cout << std::endl; + std::cout << "+ Oscillating stations detected (" << sorted.size() << " total, showing top " + << limit << "):" << std::endl; + + for (size_t i = 0; i < limit; ++i) + { + const auto* rec = sorted[i]; + + // Classify direction + double horizMag = sqrt(rec->lastE * rec->lastE + rec->lastN * rec->lastN); + double vertMag = fabs(rec->lastUp); + std::string direction; + if (vertMag < 0.01 * horizMag) + direction = "horizontal"; + else if (horizMag < 0.01 * vertMag) + direction = "vertical"; + else + direction = "3D"; + + std::cout << " - " << bstBinaryRecords_.at(rec->stnBstIdx).stationName + << std::fixed << std::setprecision(1) + << " — " << rec->firstMag << "m to " << rec->lastMag << "m" + << ", " << direction + << ", " << rec->maxCycles << " cycles" + << " (iterations " << rec->firstIteration << "-" << rec->lastIteration << ")" + << std::endl; + } +} + +bool dna_adjust::MeasurementTouchesOscillatingStation(const UINT32& msrIndex) const +{ + if (oscHistory_.empty() || msrIndex >= bmsBinaryRecords_.size()) + return false; + + std::vector msrStations; + GetMsrStations(bmsBinaryRecords_, msrIndex, msrStations); + + for (const auto& stnIndex : msrStations) + { + if (oscHistory_.find(stnIndex) != oscHistory_.end()) + return true; + } + + return false; +} + +std::string dna_adjust::MeasurementStationNames(const UINT32& msrIndex) const +{ + if (msrIndex >= bmsBinaryRecords_.size()) + return "(measurement index unavailable)"; + + std::vector msrStations; + GetMsrStations(bmsBinaryRecords_, msrIndex, msrStations); + + std::string stationNames; + for (const auto& stnIndex : msrStations) + { + if (stnIndex >= bstBinaryRecords_.size()) + continue; + + if (!stationNames.empty()) + stationNames += " -> "; + stationNames += bstBinaryRecords_.at(stnIndex).stationName; + } + + if (stationNames.empty()) + return "(stations unavailable)"; + + return stationNames; +} + +void dna_adjust::PrintSuspectMeasurementSummary(std::ostream& os, size_t limit) const +{ + struct SuspectMeasurementRecord { + UINT32 msrIndex; + char measType; + UINT32 clusterID; + UINT32 fileOrder; + double absNStat; + double nStat; + double tStat; + double measCorr; + double residualPrec; + double pelzerRel; + bool exceedsCritical; + bool touchesOscillatingStation; + std::string stationNames; + }; + + if (bmsBinaryRecords_.empty() || limit == 0) + return; + + std::vector records; + records.reserve(bmsBinaryRecords_.size()); + + for (UINT32 msrIndex = 0; msrIndex < bmsBinaryRecords_.size(); ++msrIndex) + { + const auto& msr = bmsBinaryRecords_.at(msrIndex); + if (msr.ignore || + !std::isfinite(msr.NStat) || + !std::isfinite(msr.residualPrec) || + msr.residualPrec <= 0.0) + continue; + + const double absNStat = fabs(msr.NStat); + const bool exceedsCritical = absNStat > criticalValue_; + const bool touchesOscillatingStation = MeasurementTouchesOscillatingStation(msrIndex); + + if (!exceedsCritical && !touchesOscillatingStation) + continue; + + records.push_back({ + msrIndex, + msr.measType, + msr.clusterID, + msr.fileOrder, + absNStat, + msr.NStat, + msr.TStat, + msr.measCorr, + msr.residualPrec, + msr.PelzerRel, + exceedsCritical, + touchesOscillatingStation, + MeasurementStationNames(msrIndex) + }); + } + + if (records.empty()) + return; + + std::vector oscillatingRecords; + std::vector outlierRecords; + oscillatingRecords.reserve(records.size()); + outlierRecords.reserve(records.size()); + + for (const auto& record : records) + { + if (record.touchesOscillatingStation) + oscillatingRecords.push_back(&record); + if (record.exceedsCritical && !record.touchesOscillatingStation) + outlierRecords.push_back(&record); + } + + auto sortByNStat = [](const SuspectMeasurementRecord* lhs, + const SuspectMeasurementRecord* rhs) { + if (lhs->absNStat == rhs->absNStat) + return lhs->msrIndex < rhs->msrIndex; + return lhs->absNStat > rhs->absNStat; + }; + + std::sort(oscillatingRecords.begin(), oscillatingRecords.end(), sortByNStat); + std::sort(outlierRecords.begin(), outlierRecords.end(), sortByNStat); + + std::ios::fmtflags oldFlags = os.flags(); + std::streamsize oldPrecision = os.precision(); + + auto printRecord = [&os](const SuspectMeasurementRecord& record) { + os << " - " << record.measType + << " msr " << record.msrIndex + << " cluster " << record.clusterID + << " file-order " << record.fileOrder + << " " << record.stationNames + << ": N=" << std::fixed << std::setprecision(2) << record.nStat; + + if (std::isfinite(record.tStat) && fabs(record.tStat) > 0.0) + os << ", T=" << std::fixed << std::setprecision(2) << record.tStat; + + os << ", corr=" << std::scientific << std::setprecision(3) << record.measCorr + << ", residual precision=" << std::scientific << std::setprecision(3) << record.residualPrec + << ", Pelzer=" << std::fixed << std::setprecision(2) << record.pelzerRel; + + if (record.exceedsCritical) + os << ", exceeds critical"; + if (record.touchesOscillatingStation) + os << ", touches oscillating station"; + + os << std::endl; + }; + + auto printList = [&os, &printRecord, limit](const std::string& title, + const std::vector& list) { + if (list.empty()) + return; + + const size_t listLimit = std::min(list.size(), limit); + os << std::endl << "+ " << title << " (" << list.size() + << " total, showing top " << listLimit << "):" << std::endl; + + for (size_t i = 0; i < listLimit; ++i) + printRecord(*list.at(i)); + }; + + if (!oscillatingRecords.empty()) + printList("Suspect measurements connected to oscillating stations", oscillatingRecords); + + printList(oscillatingRecords.empty() ? "Largest measurement N-statistics" : + "Largest remaining measurement N-statistics", outlierRecords); + + os.flags(oldFlags); + os.precision(oldPrecision); +} + void dna_adjust::ComputePrecisionAdjMsrs(const UINT32& block /*= 0*/) { if (projectSettings_.a.report_mode) @@ -7348,6 +7766,7 @@ void dna_adjust::ComputePrecisionAdjMsrs(const UINT32& block /*= 0*/) // A*V-1*At, where: // - A is design matrix // - V is the inverse of the normals (i.e. precision of estimates) + v_precAdjMsrsFull_.at(block).redim(v_measurementVarianceCount_.at(block), 1); v_precAdjMsrsFull_.at(block).zero(); UINT32 design_row(0); @@ -8027,19 +8446,19 @@ void dna_adjust::ComputeChiSquare_D(it_vmsr_t& _it_msr, UINT32& measurement_inde } -void dna_adjust::FormInverseVarianceMatrix(matrix_2d* vmat, bool LOWER_IS_CLEARED) +void dna_adjust::FormInverseVarianceMatrix(matrix_2d* vmat, bool LOWER_IS_CLEARED, bool mark_symmetric) { if (vmat->rows() == 1) { vmat->put(0, 0, 1./vmat->get(0, 0)); return; } - + // As of version 3.2.0, force all inversions to use MKL. This change // is enforced for two reasons: // 1. Sweep, Gaussian inverse and numerical recipes cholesky - // all require matrix data to be stored in row wise fashion, upper - // triangle only, whereas contiguous matrix class stores matrix data + // all require matrix data to be stored in row wise fashion, upper + // triangle only, whereas contiguous matrix class stores matrix data // in column wise fashion, lower triangle. // 2. Sweep, Gaussian never really offered a stable solution. // The following switch is kept in case future development warrants @@ -8049,8 +8468,8 @@ void dna_adjust::FormInverseVarianceMatrix(matrix_2d* vmat, bool LOWER_IS_CLEARE // TODO: All functions which load variance matrix from binary files // store the data in upper triangular form. This could be changed to - // lower triangular form, thus alleviating the need to pass - // LOWER_IS_CLEARED. That is, force all operations to use a lower + // lower triangular form, thus alleviating the need to pass + // LOWER_IS_CLEARED. That is, force all operations to use a lower // triangular matrix. Not sure if this would create an efficiency or not. switch (projectSettings_.a.inverse_method_msr) { @@ -8062,8 +8481,7 @@ void dna_adjust::FormInverseVarianceMatrix(matrix_2d* vmat, bool LOWER_IS_CLEARE // break; case Cholesky_mkl: default: - // Inversion using Intel MKL - vmat->cholesky_inverse(LOWER_IS_CLEARED); + vmat->cholesky_inverse(LOWER_IS_CLEARED, mark_symmetric); break; // choleskyinverse broke once the storage order of the matrix buffer was // changed from row-wise to column wise. diff --git a/dynadjust/dynadjust/dnaadjust/dnaadjust.hpp b/dynadjust/dynadjust/dnaadjust/dnaadjust.hpp index 9039f7711..ed02c0f46 100644 --- a/dynadjust/dynadjust/dnaadjust/dnaadjust.hpp +++ b/dynadjust/dynadjust/dnaadjust/dnaadjust.hpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -102,6 +103,7 @@ namespace networkadjust { extern std::mutex maxCorrMutex; using dynadjust::cpu_timer; +using dynadjust::FormatElapsedTime; // forward declaration of dna_adjust class dna_adjust; @@ -288,16 +290,26 @@ class dna_adjust { // void PrintEstimatedStationCoordinatestoDNAXML_Y(const std::string& msrFile, // INPUT_FILE_TYPE t); + void PrintOscillationSummary(); + void PrintSuspectMeasurementSummary(std::ostream& os = std::cout, + size_t limit = 20) const; void CloseOutputFiles(); void UpdateBinaryFiles(); UINT32 CurrentIteration() const; UINT32& incrementIteration(); + void decrementIteration(); void initialiseIteration(const UINT32& iteration = 0); inline UINT32 CurrentBlock() const { return currentBlock_; }; - inline void SetcurrentBlock(const UINT32 b) { currentBlock_ = b; }; + inline void SetcurrentBlock(const UINT32 b) { + auto now = std::chrono::steady_clock::now(); + if (b != currentBlock_ && blockStartTime_.time_since_epoch().count() > 0) + lastBlockElapsedMs_ = std::chrono::duration_cast(now - blockStartTime_).count(); + currentBlock_ = b; + blockStartTime_ = now; + }; inline void SetmaxCorr(const double c) { @@ -311,6 +323,15 @@ class dna_adjust { } inline bool processingForward() { return forward_; } inline bool processingCombine() { return isCombining_; } + inline int64_t LastBlockElapsedMs() const { return lastBlockElapsedMs_; } + inline UINT32 CurrentBlockStationCount() const { + if (currentBlock_ < v_blockStationsMap_.size()) + return static_cast(v_blockStationsMap_.at(currentBlock_).size()); + return 0; + } + static void SetMaxBlasThreads(int n); + static int GetMaxBlasThreads(); + inline int GetDegreesOfFreedom() const { return degreesofFreedom_; } inline UINT32 GetMeasurementCount() const { return measurementParams_; } inline UINT32 GetUnknownsCount() const { return unknownParams_; } @@ -350,6 +371,12 @@ class dna_adjust { return iterationCorrections_.get_message(iteration); // safe guard return iterationCorrections_.get_message(iteration - 1); }; + + inline std::string GetIterationTime(const UINT32& iteration) const { + if (iteration == 0) + return iterationTimes_.get_message(iteration); // safe guard + return iterationTimes_.get_message(iteration - 1); + }; ///////////////////////////// concurrent_ofstream concurrent_adj_ofstream; @@ -380,14 +407,21 @@ class dna_adjust { bool isAdjusting_; bool isCombining_; bool forward_; + bool rebuildingDesign_; bool isFirstTimeAdjustment_; bool isIterationComplete_; bool isAdjustmentQuestionable_; UINT32 blockCount_; UINT32 currentBlock_; + std::chrono::steady_clock::time_point blockStartTime_; + int64_t lastBlockElapsedMs_; std::chrono::milliseconds total_time_; + bool profileTimings_; + std::atomic profileUpdateNormalsNs_; + std::atomic profileStageLoadNs_; + std::atomic profileStageStoreNs_; _ADJUST_STATUS_ adjustStatus_; vstring statusMessages_; UINT32 currentIteration_; @@ -437,6 +471,7 @@ class dna_adjust { void UpdateAdjustment(bool iterate); void ValidateandFinaliseAdjustment(cpu_timer& tot_time); void PrintAdjustmentTime(cpu_timer& time, _TIMER_TYPE_); + void PrintPerformanceProfile() const; void InitialiseAdjustment(); void SetDefaultReferenceFrame(); void LoadNetworkFiles(); @@ -499,6 +534,8 @@ class dna_adjust { void DeserialiseBlockFromMappedFile(const UINT32& block, const int count = 0, ...); void UnloadBlock(const UINT32& block, const int file_count = 0, ...); + void AdviseBlockDontNeed(const UINT32& block); + void AdviseBlockWillNeed(const UINT32& block); void PurgeMatricesFromDisk(); // Helpers @@ -534,7 +571,8 @@ class dna_adjust { void UpdateDesignNormalMeasMatrices(pit_vmsr_t _it_msr, UINT32& design_row, bool buildnewMatrices, const UINT32& block, - bool MT_ReverseOrCombine); + bool MT_ReverseOrCombine, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_A(pit_vmsr_t _it_msr, UINT32& design_row, @@ -542,40 +580,45 @@ class dna_adjust { matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_BK(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_C(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_CEM( pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, matrix_2d* AtVinv, - bool buildnewMatrices); + bool buildnewMatrices, bool skipNormals = false); void UpdateDesignNormalMeasMatrices_D(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_E(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignMeasMatrices_GX(pit_vmsr_t _it_msr, UINT32& design_row, matrix_2d* measMinusComp, matrix_2d* estimatedStations, @@ -587,113 +630,130 @@ class dna_adjust { matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_H(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_HR(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_I(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_IP(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_J(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_JQ(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_L(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_M(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_P(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_Q(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_R(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_S(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_V(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_X( pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_Y( pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, - matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* design, matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateDesignNormalMeasMatrices_Z(pit_vmsr_t _it_msr, UINT32& design_row, const UINT32& block, matrix_2d* measMinusComp, matrix_2d* estimatedStations, matrix_2d* normals, matrix_2d* design, - matrix_2d* AtVinv, bool buildnewMatrices); + matrix_2d* AtVinv, bool buildnewMatrices, + bool skipNormals = false); void UpdateIgnoredMeasurements(pit_vmsr_t _it_msr, bool storeOriginalMeasurement); @@ -827,7 +887,9 @@ class dna_adjust { void PrepareDesignAndMsrMnsCmpMatricesStage(const UINT32& block); void FillDesignNormalMeasurementsMatrices(bool buildnewMatrices, const UINT32& block, - bool MT_ReverseOrCombine); + bool MT_ReverseOrCombine, + bool skipNormals = false); + void RebuildDesignAndAtVinv(const UINT32& block); // void RecomputeMeasurementsCommonJunctions(const UINT32& nextBlock, const // UINT32& thisBlock, const UINT32& prevBlock); @@ -861,11 +923,11 @@ class dna_adjust { bool printHeader); void ComputeAdjMsrBlockOnIteration(const UINT32& block); - void ComputeAdjustedMsrPrecisions(); + void ComputeAdjustedMsrPrecisions(); - void ComputeChiSquareNetwork(); - void ComputeChiSquare(const UINT32& block); - void ComputeChiSquareSimultaneous(); + void ComputeChiSquareNetwork(); + void ComputeChiSquare(const UINT32& block); + void ComputeChiSquareSimultaneous(); void ComputeChiSquarePhased(const UINT32& block); void ComputeTestStat(const double& dof, double& chiUpper, double& chiLower, @@ -929,7 +991,7 @@ class dna_adjust { matrix_2d* measMinusComp); void - FormInverseVarianceMatrix(matrix_2d* vmat, bool LOWER_IS_CLEARED = false); + FormInverseVarianceMatrix(matrix_2d* vmat, bool LOWER_IS_CLEARED = false, bool mark_symmetric = false); void FormInverseGPSVarianceMatrix(const it_vmsr_t& _it_msr, matrix_2d* vmat); bool @@ -1118,6 +1180,7 @@ class dna_adjust { bool allStationsFixed_; message_bank iterationCorrections_; + message_bank iterationTimes_; // For each block vUINT32 v_ContiguousNetList_; // vector of contiguous network IDs @@ -1205,6 +1268,27 @@ class dna_adjust { v_mat_2d v_corrections_; // vector of residuals matrices v_mat_2d v_correctionsR_; // vector of residuals matrices + // ---------------------------------------------- + // Iteration diagnostics — oscillation detection + struct StationCorrRecord { + double cx, cy, cz; // Cartesian corrections + }; + struct OscillationRecord { + UINT32 stnBstIdx; // BST record index + UINT32 firstIteration; + UINT32 lastIteration; + UINT32 maxCycles; + double firstMag; + double lastMag; + double lastE, lastN, lastUp; + }; + std::map corrPrev_; // keyed by BST station index + std::map stnOscCount_; // keyed by BST station index + std::map oscHistory_; // keyed by BST station index + void UpdateIterationDiagnostics(); + bool MeasurementTouchesOscillatingStation(const UINT32& msrIndex) const; + std::string MeasurementStationNames(const UINT32& msrIndex) const; + // ---------------------------------------------- // Adjustment functions and variables for staged adjustment diff --git a/dynadjust/dynadjust/dnaadjust/dnaadjust_printer.cpp b/dynadjust/dynadjust/dnaadjust/dnaadjust_printer.cpp index 0d40a6c4f..2d65253d9 100644 --- a/dynadjust/dynadjust/dnaadjust/dnaadjust_printer.cpp +++ b/dynadjust/dynadjust/dnaadjust/dnaadjust_printer.cpp @@ -80,22 +80,13 @@ void DynAdjustPrinter::PrintIteration(const UINT32& iteration) { } void DynAdjustPrinter::PrintAdjustmentTime(cpu_timer& time, int timer_type) { - // calculate and print total time - auto elapsed = time.elapsed(); - double seconds = elapsed.wall.count() / 1.0e9; - - std::stringstream ss; - if (seconds >= 1.0) { - ss << std::fixed << std::setprecision(3) << seconds << "s"; - } else { - ss << std::fixed << std::setprecision(3) << (seconds * 1000.0) << "ms"; - } + std::string formatted = FormatElapsedTime(time.elapsed().wall.count() / 1.0e9); if (timer_type == 0) // iteration_time equivalent - adjust_.adj_file << std::setw(PRINT_VAR_PAD) << std::left << "Elapsed time" << ss.str() << std::endl; + adjust_.adj_file << std::setw(PRINT_VAR_PAD) << std::left << "Elapsed time" << formatted << std::endl; else { - adjust_.adj_file << std::setw(PRINT_VAR_PAD) << std::left << "Total time" << ss.str() << std::endl << std::endl; + adjust_.adj_file << std::setw(PRINT_VAR_PAD) << std::left << "Total time" << formatted << std::endl << std::endl; } } diff --git a/dynadjust/dynadjust/dnaadjustwrapper/CMakeLists.txt b/dynadjust/dynadjust/dnaadjustwrapper/CMakeLists.txt index f0d64b45b..0600bcb26 100644 --- a/dynadjust/dynadjust/dnaadjustwrapper/CMakeLists.txt +++ b/dynadjust/dynadjust/dnaadjustwrapper/CMakeLists.txt @@ -87,8 +87,8 @@ if(BUILD_STATIC) "-framework CoreFoundation") else() target_link_options(${STATIC_TARGET_NAME} PRIVATE - -static) - + -static-libgcc + -static-libstdc++) endif() elseif(WIN32) # On Windows with MSVC diff --git a/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustprogress.cpp b/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustprogress.cpp index 06361ff25..171b378f3 100644 --- a/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustprogress.cpp +++ b/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustprogress.cpp @@ -21,6 +21,7 @@ #include #include +#include /// \cond #include @@ -323,13 +324,14 @@ void dna_adjust_progress_thread::processAdjustment() ss.str(""); ss << " Iteration " << std::right << std::setw(2) << std::fixed << std::setprecision(0) << currentIteration; ss << ", max station corr: " << std::right << std::setw(PROGRESS_ADJ_BLOCK_12) << - _dnaAdj->GetMaxCorrection(currentIteration) << std::endl; - + _dnaAdj->GetMaxCorrection(currentIteration); + ss << ", time: " << _dnaAdj->GetIterationTime(currentIteration) << std::endl; + coutMessage(ss.str()); } std::this_thread::sleep_for(std::chrono::milliseconds(80)); } - + break; case Phased_Block_1Mode: case PhasedMode: @@ -350,12 +352,13 @@ void dna_adjust_progress_thread::processAdjustment() ss.str(""); ss << " Iteration " << std::right << std::setw(2) << std::fixed << std::setprecision(0) << currentIteration; - ss << ", max station corr: " << std::right << std::setw(PROGRESS_ADJ_BLOCK_12) << _dnaAdj->GetMaxCorrection(currentIteration) << std::endl; - + ss << ", max station corr: " << std::right << std::setw(PROGRESS_ADJ_BLOCK_12) << _dnaAdj->GetMaxCorrection(currentIteration); + ss << ", time: " << _dnaAdj->GetIterationTime(currentIteration) << std::endl; + sst.str(""); if (first_time) sst << std::setw(PROGRESS_ADJ_BLOCK_28) << std::left << " "; - sst << PROGRESS_BACKSPACE_28 << std::setw(PROGRESS_ADJ_BLOCK_28) << std::left << ss.str(); + sst << PROGRESS_BACKSPACE_28 << std::left << ss.str(); coutMessage(sst.str()); first_time = true; } @@ -364,24 +367,33 @@ void dna_adjust_progress_thread::processAdjustment() // print new block to screen when adjusting only if (block != currentBlock && _dnaAdj->IsAdjusting()) - { + { ss.str(""); ss << " Iteration " << std::right << std::setw(2) << std::fixed << std::setprecision(0) << _dnaAdj->CurrentIteration(); if (_p->a.multi_thread && !_dnaAdj->processingCombine()) ss << std::left << std::setw(13) << ", adjusting..."; else - ss << ", block " << std::left << std::setw(6) << std::fixed << std::setprecision(0) << _dnaAdj->CurrentBlock() + 1; - - sst.str(""); - if (first_time) { - sst << std::setw(PROGRESS_ADJ_BLOCK_28) << std::left << " "; - first_time = false; + ss << ", block " << std::left << std::setw(4) << std::fixed << std::setprecision(0) << _dnaAdj->CurrentBlock() + 1; + UINT32 stnCount = _dnaAdj->CurrentBlockStationCount(); + if (stnCount > 0) + { + int max_t = dna_adjust::GetMaxBlasThreads(); + ss << " (" << std::right << std::setw(5) << stnCount << " stns, "; + if (max_t > 0) + ss << std::setw(2) << max_t << "T"; + else + ss << "auto"; + ss << ")"; + } + int64_t elapsedMs = _dnaAdj->LastBlockElapsedMs(); + if (elapsedMs > 0) + ss << " " << std::fixed << std::setprecision(1) << (elapsedMs / 1000.0) << "s"; } - - sst << PROGRESS_BACKSPACE_28 << std::setw(PROGRESS_ADJ_BLOCK_28) << std::left << ss.str(); - coutMessage(sst.str()); + ss << std::endl; + coutMessage(ss.str()); + first_time = true; currentBlock = block; } @@ -416,4 +428,3 @@ void dna_adjust_progress_thread::coutMessage(const std::string& message) std::cout.flush(); cout_mutex.unlock(); } - diff --git a/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustwrapper.cpp b/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustwrapper.cpp index d8c3710de..20a6bfca4 100644 --- a/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustwrapper.cpp +++ b/dynadjust/dynadjust/dnaadjustwrapper/dnaadjustwrapper.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ /// \endcond #include +#include #include #include #include @@ -60,6 +62,14 @@ using namespace dynadjust::iostreams; extern bool running; extern std::mutex cout_mutex; +static dna_adjust* g_netAdjust = nullptr; + +void sigint_handler(int) +{ + if (g_netAdjust) + g_netAdjust->CancelAdjustment(); +} + using namespace dynadjust; using namespace dynadjust::epsg; @@ -86,8 +96,9 @@ void PrintSummaryMessage(dna_adjust* netAdjust, const project_settings* p, boost break; std::stringstream ss(""); ss << " Iteration " << std::right << std::setw(2) << std::fixed << std::setprecision(0) << currentIteration; - ss << ", max station corr: " << std::right << std::setw(12) << netAdjust->GetMaxCorrection(currentIteration) << std::endl; - std::cout << PROGRESS_BACKSPACE_28 << std::setw(28) << std::left << ss.str(); + ss << ", max station corr: " << std::right << std::setw(12) << netAdjust->GetMaxCorrection(currentIteration); + ss << ", time: " << netAdjust->GetIterationTime(currentIteration) << std::endl; + std::cout << PROGRESS_BACKSPACE_28 << std::left << ss.str(); } if (p->a.report_mode) @@ -198,75 +209,75 @@ void DeserialiseVarianceMatrices(dna_adjust* netAdjust, const project_settings* void GenerateStatistics(dna_adjust* netAdjust, const project_settings* p) { // Generate statistics - // Don't produce statistics only for block 1 only adjustments - if (p->a.adjust_mode != Phased_Block_1Mode) + if (!p->g.quiet) { - if (!p->g.quiet) + std::cout << "+ Generating statistics..."; + std::cout.flush(); + } + netAdjust->GenerateStatistics(); + if (!p->g.quiet) + { + std::cout << " done." << std::endl; + + // Don't print detailed statistics for block 1 only adjustments + if (p->a.adjust_mode == Phased_Block_1Mode) + return; + + std::cout << "+ Adjustment results:" << std::endl << std::endl; + std::cout << "+" << OUTPUTLINE << std::endl; + std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Number of unknown parameters" << std::fixed << std::setprecision(0) << netAdjust->GetUnknownsCount(); + if (netAdjust->GetAllFixed()) + std::cout << " (All stations held constrained)"; + std::cout << std::endl; + + std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Number of measurements" << std::fixed << std::setprecision(0) << netAdjust->GetMeasurementCount(); + + if (netAdjust->GetPotentialOutlierCount() > 0) { - std::cout << "+ Generating statistics..."; - std::cout.flush(); + std::cout << " (" << netAdjust->GetPotentialOutlierCount() << " potential outlier"; + if (netAdjust->GetPotentialOutlierCount() > 1) + std::cout << "s"; + std::cout << ")"; } - netAdjust->GenerateStatistics(); - if (!p->g.quiet) - { - std::cout << " done." << std::endl; - - std::cout << "+ Adjustment results:" << std::endl << std::endl; - std::cout << "+" << OUTPUTLINE << std::endl; - std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Number of unknown parameters" << std::fixed << std::setprecision(0) << netAdjust->GetUnknownsCount(); - if (netAdjust->GetAllFixed()) - std::cout << " (All stations held constrained)"; - std::cout << std::endl; + std::cout << std::endl; + std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Degrees of freedom" << std::fixed << std::setprecision(0) << netAdjust->GetDegreesOfFreedom() << std::endl; + std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Chi squared" << std::fixed << std::setprecision(2) << netAdjust->GetChiSquared() << std::endl; + std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Rigorous sigma zero" << std::fixed << std::setprecision(3) << netAdjust->GetSigmaZero() << std::endl; + std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Global (Pelzer) Reliability" << std::fixed << std::setw(8) << std::setprecision(3) << netAdjust->GetGlobalPelzerRel() << "(excludes non redundant measurements)" << std::endl << std::endl; - std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Number of measurements" << std::fixed << std::setprecision(0) << netAdjust->GetMeasurementCount(); - - if (netAdjust->GetPotentialOutlierCount() > 0) - { - std::cout << " (" << netAdjust->GetPotentialOutlierCount() << " potential outlier"; - if (netAdjust->GetPotentialOutlierCount() > 1) - std::cout << "s"; - std::cout << ")"; - } - std::cout << std::endl; - std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Degrees of freedom" << std::fixed << std::setprecision(0) << netAdjust->GetDegreesOfFreedom() << std::endl; - std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Chi squared" << std::fixed << std::setprecision(2) << netAdjust->GetChiSquared() << std::endl; - std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Rigorous sigma zero" << std::fixed << std::setprecision(3) << netAdjust->GetSigmaZero() << std::endl; - std::cout << std::setw(PRINT_VAR_PAD) << std::left << " Global (Pelzer) Reliability" << std::fixed << std::setw(8) << std::setprecision(3) << netAdjust->GetGlobalPelzerRel() << "(excludes non redundant measurements)" << std::endl << std::endl; - - std::stringstream ss(""); - ss << std::left << " Chi-Square test (" << std::setprecision(1) << std::fixed << p->a.confidence_interval << "%)"; - std::cout << std::setw(PRINT_VAR_PAD) << std::left << ss.str(); - ss.str(""); - ss << std::fixed << std::setprecision(3) << - netAdjust->GetChiSquaredLowerLimit() << " < " << - netAdjust->GetSigmaZero() << " < " << - netAdjust->GetChiSquaredUpperLimit(); - std::cout << std::setw(CHISQRLIMITS) << std::left << ss.str(); - ss.str(""); - - if (netAdjust->GetDegreesOfFreedom() < 1) - ss << "NO REDUNDANCY"; - else + std::stringstream ss(""); + ss << std::left << " Chi-Square test (" << std::setprecision(1) << std::fixed << p->a.confidence_interval << "%)"; + std::cout << std::setw(PRINT_VAR_PAD) << std::left << ss.str(); + ss.str(""); + ss << std::fixed << std::setprecision(3) << + netAdjust->GetChiSquaredLowerLimit() << " < " << + netAdjust->GetSigmaZero() << " < " << + netAdjust->GetChiSquaredUpperLimit(); + std::cout << std::setw(CHISQRLIMITS) << std::left << ss.str(); + ss.str(""); + + if (netAdjust->GetDegreesOfFreedom() < 1) + ss << "NO REDUNDANCY"; + else + { + ss << "*** "; + switch (netAdjust->GetTestResult()) { - ss << "*** "; - switch (netAdjust->GetTestResult()) - { - case test_stat_pass: - ss << "PASSED"; // within upper and lower - break; - case test_stat_warning: - ss << "WARNING"; // less than lower limit - break; - case test_stat_fail: - ss << "FAILED"; // greater than upper limit - break; - } - ss << " ***"; + case test_stat_pass: + ss << "PASSED"; // within upper and lower + break; + case test_stat_warning: + ss << "WARNING"; // less than lower limit + break; + case test_stat_fail: + ss << "FAILED"; // greater than upper limit + break; } - - std::cout << std::setw(PASS_FAIL) << std::right << ss.str() << std::endl; - std::cout << "+" << OUTPUTLINE << std::endl << std::endl; + ss << " ***"; } + + std::cout << std::setw(PASS_FAIL) << std::right << ss.str() << std::endl; + std::cout << "+" << OUTPUTLINE << std::endl << std::endl; } else std::cout << std::endl; @@ -867,6 +878,10 @@ int main(int argc, char* argv[]) "Recreate memory mapped files.") (PURGE_STAGE_FILES, "Purge memory mapped files from disk upon adjustment completion.") + (STAGE_PATH, boost::program_options::value(&p.a.stage_path), + "Directory for memory mapped stage files. Default is the output folder.") + (MAX_THREADS, boost::program_options::value(&p.a.max_threads), + "Maximum number of BLAS threads for linear algebra operations. Default is 0 (auto).") ; output_options.add_options() @@ -1327,7 +1342,12 @@ int main(int argc, char* argv[]) try { running = true; - int nthreads_la = init_linear_algebra_threads(); + // Install SIGINT handler for graceful cancellation + g_netAdjust = &netAdjust; + std::signal(SIGINT, sigint_handler); + + int nthreads_la = init_linear_algebra_threads(p.a.max_threads); + dna_adjust::SetMaxBlasThreads(nthreads_la); std::thread progress(dna_adjust_progress_thread(&netAdjust, &p)); // Do adjustment using linear algebra threads @@ -1354,7 +1374,10 @@ int main(int argc, char* argv[]) PrintSummaryMessage(&netAdjust, &p, &elapsed_time); if (netAdjust.GetStatus() > ADJUST_THRESHOLD_EXCEEDED) + { + netAdjust.PrintOscillationSummary(); return ADJUST_SUCCESS; + } // Generate statistics GenerateStatistics(&netAdjust, &p); @@ -1401,6 +1424,9 @@ int main(int argc, char* argv[]) return EXIT_FAILURE; } + netAdjust.PrintOscillationSummary(); + netAdjust.PrintSuspectMeasurementSummary(std::cout); + if (!p.g.quiet) std::cout << std::endl << "+ Open " << leafStr(p.o._adj_file) << " to view the adjustment details." << std::endl << std::endl; @@ -1424,4 +1450,3 @@ int main(int argc, char* argv[]) return ADJUST_SUCCESS; } - diff --git a/dynadjust/dynadjust/dnaadjustwrapper/threading_init.hpp b/dynadjust/dynadjust/dnaadjustwrapper/threading_init.hpp index 4c42f2cf7..4bf0aa91f 100644 --- a/dynadjust/dynadjust/dnaadjustwrapper/threading_init.hpp +++ b/dynadjust/dynadjust/dnaadjustwrapper/threading_init.hpp @@ -36,42 +36,56 @@ #if defined(USE_MKL) || defined(__MKL__) #include -#elif defined(OPENBLAS_VERSION) || defined(__OPENBLAS_CONFIG_H) || defined(OPENBLAS_LOPT_H) +#elif !defined(__APPLE__) + +#include + +#if defined(OPENBLAS_VERSION) || defined(__OPENBLAS_CONFIG_H) || defined(OPENBLAS_LOPT_H) #ifndef USE_OPENBLAS #define USE_OPENBLAS #endif -#include +#elif defined(__has_include) +#if __has_include() +#ifndef USE_OPENBLAS +#define USE_OPENBLAS +#endif #include +#endif +#endif +#ifdef USE_OPENBLAS extern "C" { void openblas_set_num_threads(int); } +#endif #elif defined(__APPLE__) #include #endif /// \endcond +inline int positive_env_int(const char* name) { + const char* env = std::getenv(name); + if (!env || !*env) return 0; + int value = std::atoi(env); + return value > 0 ? value : 0; +} + inline int init_linear_algebra_threads(int requested_threads = 0) { int n = requested_threads; -#if defined(_OPENMP) if (n <= 0) { - if (const char* env = std::getenv("OMP_NUM_THREADS"); env && *env) { - int v = std::atoi(env); - if (v > 0) n = v; - } - } - if (n <= 0) n = std::max(1, omp_get_max_threads()); + n = positive_env_int("OMP_NUM_THREADS"); +#if defined(USE_MKL) || defined(__MKL__) + if (n <= 0) n = positive_env_int("MKL_NUM_THREADS"); +#elif defined(USE_OPENBLAS) + if (n <= 0) n = positive_env_int("OPENBLAS_NUM_THREADS"); #elif defined(__APPLE__) - if (n == 1) { - BLASSetThreading(BLAS_THREADING_SINGLE_THREADED); - } else { - BLASSetThreading(BLAS_THREADING_MULTI_THREADED); - } -#else - if (n <= 0) n = 1; + if (n <= 0) n = positive_env_int("VECLIB_MAXIMUM_THREADS"); #endif + } + + if (n <= 0) return 0; #if defined(_OPENMP) omp_set_dynamic(0); @@ -83,7 +97,13 @@ inline int init_linear_algebra_threads(int requested_threads = 0) { omp_set_num_threads(n); #endif -#ifdef USE_MKL +#if defined(__APPLE__) + if (n == 1) { + BLASSetThreading(BLAS_THREADING_SINGLE_THREADED); + } else { + BLASSetThreading(BLAS_THREADING_MULTI_THREADED); + } +#elif defined(USE_MKL) || defined(__MKL__) mkl_set_dynamic(0); mkl_set_num_threads(n); #if defined(MKL_THREAD_LOCAL) diff --git a/dynadjust/dynadjust/dnadiff/CMakeLists.txt b/dynadjust/dynadjust/dnadiff/CMakeLists.txt index 5b48c520b..819588f42 100644 --- a/dynadjust/dynadjust/dnadiff/CMakeLists.txt +++ b/dynadjust/dynadjust/dnadiff/CMakeLists.txt @@ -24,8 +24,8 @@ else() # Link statically if(UNIX) if(NOT APPLE) - SET_TARGET_PROPERTIES(${STATIC_TARGET_NAME} PROPERTIES LINK_FLAGS "-static") - target_link_libraries (${STATIC_TARGET_NAME} pthread) + target_link_options(${STATIC_TARGET_NAME} PRIVATE -static-libgcc -static-libstdc++) + target_link_libraries(${STATIC_TARGET_NAME} pthread) endif() endif() diff --git a/dynadjust/dynadjust/dnageoidwrapper/CMakeLists.txt b/dynadjust/dynadjust/dnageoidwrapper/CMakeLists.txt index 1223d5fdc..24fb8efbf 100644 --- a/dynadjust/dynadjust/dnageoidwrapper/CMakeLists.txt +++ b/dynadjust/dynadjust/dnageoidwrapper/CMakeLists.txt @@ -84,8 +84,8 @@ if(BUILD_STATIC) "-framework CoreFoundation") else() target_link_options(${STATIC_TARGET_NAME} PRIVATE - -static) - + -static-libgcc + -static-libstdc++) endif() elseif(WIN32) # On Windows with MSVC diff --git a/dynadjust/dynadjust/dnaimport/dnainterop.cpp b/dynadjust/dynadjust/dnaimport/dnainterop.cpp index ee4cd7ee6..e53569c4e 100644 --- a/dynadjust/dynadjust/dnaimport/dnainterop.cpp +++ b/dynadjust/dynadjust/dnaimport/dnainterop.cpp @@ -21,8 +21,6 @@ #include -//#include - using namespace dynadjust::epsg; MsrTally g_map_tally; @@ -176,7 +174,8 @@ void dna_import::BuildExtractStationsList(const std::string& stnList, pvstring v } -void dna_import::InitialiseDatum(const std::string& reference_frame, const std::string epoch) +void dna_import::InitialiseDatum(const std::string& reference_frame, const std::string epoch, + const std::string observation_epoch) { try { // Take the default reference frame, set either by the user or @@ -198,11 +197,13 @@ void dna_import::InitialiseDatum(const std::string& reference_frame, const std:: if (datum_.GetEpoch() == timeImmemorial()) m_strProjectDefaultEpoch = ""; + m_strProjectObservationEpoch = observation_epoch; + // Update binary file meta // Note: the following rule applies each time a set of files is loaded via import: // * This method (InitialiseDatum) is called (from dnaimportwrapper) before any files are loaded. // By default, the bst & bms meta are initialised with the reference frame and reference epoch. - // * The datum and epoch within the first file (if present) is used to set the default project + // * The datum and epoch within the first file (if present) is used to set the default project // datum. If a datum isn't provided in the first input file, e.g. SINEX file, the default datum // (GDA2020) is used. As each subsequent file is loaded, the default frame and epoch are assumed. // * After all files have been loaded, InitialiseDatum is called again to set the metadata. @@ -212,6 +213,56 @@ void dna_import::InitialiseDatum(const std::string& reference_frame, const std:: snprintf(bms_meta_.epsgCode, sizeof(bms_meta_.epsgCode), "%s", m_strProjectDefaultEpsg.substr(0, STN_EPSG_WIDTH).c_str()); snprintf(bst_meta_.epoch, sizeof(bst_meta_.epoch), "%s", m_strProjectDefaultEpoch.substr(0, STN_EPOCH_WIDTH).c_str()); snprintf(bms_meta_.epoch, sizeof(bms_meta_.epoch), "%s", m_strProjectDefaultEpoch.substr(0, STN_EPOCH_WIDTH).c_str()); + // observation_epoch is immutable; record the CLI-supplied project-level value (empty if not supplied). + snprintf(bst_meta_.observation_epoch, sizeof(bst_meta_.observation_epoch), "%s", m_strProjectObservationEpoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(bms_meta_.observation_epoch, sizeof(bms_meta_.observation_epoch), "%s", m_strProjectObservationEpoch.substr(0, STN_EPOCH_WIDTH).c_str()); +} + + +void dna_import::ApplyProjectObservationEpoch(vdnaMsrPtr* vMeasurements) +{ + if (m_strProjectObservationEpoch.empty() || vMeasurements == nullptr) + return; + + const std::string& obsEpoch = m_strProjectObservationEpoch; + + // A measurement's observation_epoch is treated as "not explicitly set" when it is + // empty or equal to the reference-frame epoch (the auto-default from SetEpoch). + auto needs_override = [](const std::string& msrObsEpoch, const std::string& msrEpoch) { + return msrObsEpoch.empty() || msrObsEpoch == msrEpoch; + }; + + for (auto& msr_ptr : *vMeasurements) + { + if (!msr_ptr) + continue; + + if (needs_override(msr_ptr->GetObservationEpoch(), msr_ptr->GetEpoch())) + msr_ptr->SetObservationEpoch(obsEpoch); + + // GPS baseline cluster (G/X): propagate to every baseline element + if (auto* baselines = msr_ptr->GetBaselines_ptr()) + { + for (auto& bsl : *baselines) + { + if (needs_override(bsl.GetObservationEpoch(), bsl.GetEpoch())) + bsl.SetObservationEpoch(obsEpoch); + } + } + + // GPS point cluster (Y): propagate to every point element + if (auto* points = msr_ptr->GetPoints_ptr()) + { + for (auto& pnt : *points) + { + if (needs_override(pnt.GetObservationEpoch(), pnt.GetEpoch())) + pnt.SetObservationEpoch(obsEpoch); + } + } + + // Direction set (D): directions inherit from the parent measurement; no per-direction + // observation_epoch field exists beyond what the set-level value already carries. + } } @@ -400,18 +451,6 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU _filespecifiedreferenceframe = false; _filespecifiedepoch = false; - // Check if DynaML.xsd exists in the current directory - // This prevents the XML parser from hanging when the schema file is missing - if (!std::filesystem::exists("DynaML.xsd")) - { - import_file_mutex.unlock(); - std::stringstream ss; - ss << "ParseXML(): DynaML.xsd schema file not found in the current directory." << std::endl; - ss << " The XML parser requires this file to validate XML input files." << std::endl; - ss << " Please ensure DynaML.xsd is present in the working directory."; - SignalExceptionParse(ss.str(), 0); - } - try { // Instantiate individual parsers. @@ -450,7 +489,7 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU StationCoord_p.parsers (string_p, string_p, string_p, Height_p, string_p, GeoidModel_p); DnaMeasurement_p.parsers (string_p, string_p, string_p, string_p, string_p, string_p, string_p, string_p, - string_p, string_p, Directions_p, string_p, string_p, string_p, GPSBaseline_p, string_p, string_p, + string_p, string_p, Directions_p, string_p, string_p, string_p, string_p, GPSBaseline_p, string_p, string_p, string_p, Clusterpoint_p, string_p, string_p, string_p, string_p, (projectSettings_.i.prefer_single_x_as_g == TRUE ? true : false)); @@ -473,7 +512,7 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU ::xml_schema::document doc_p (DnaXmlFormat_p, "DnaXmlFormat"); DnaXmlFormat_p.pre(); - doc_p.parse (*ifsInputFILE_); + doc_p.parse (*ifsInputFILE_, ::xml_schema::flags::dont_validate); DnaXmlFormat_p.post_DnaXmlFormat (vStations, vMeasurements); // unlock after parsing @@ -553,6 +592,7 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU { std::stringstream ss; ss << "The default input file reference frame \"" << referenceframe_p.str() << "\" is not recognised."; + import_file_mutex.unlock(); SignalExceptionParse(static_cast(ss.str()), 0); } @@ -580,6 +620,7 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU } std::stringstream ss; ss << "ParseXML(): An std::ios_base failure was encountered while parsing " << fileName << "." << std::endl << " " << f.what(); + import_file_mutex.unlock(); SignalExceptionParse(static_cast(ss.str()), 0); } catch (const std::system_error& e) @@ -598,12 +639,14 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU } std::stringstream ss; ss << "ParseXML(): An std::ios_base failure was encountered while parsing " << fileName << "." << std::endl << " " << e.what(); + import_file_mutex.unlock(); SignalExceptionParse(static_cast(ss.str()), 0); } - catch (const XMLInteropException& e) + catch (const XMLInteropException& e) { std::stringstream ss; ss << "ParseXML(): An exception was encountered while parsing " << fileName << "." << std::endl << " " << e.what(); + import_file_mutex.unlock(); SignalExceptionParse(static_cast(ss.str()), 0); } catch (const ::xml_schema::parsing& e) @@ -611,7 +654,7 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU std::stringstream ss(""); ss << e.what(); - ::xsd::cxx::parser::diagnostics::const_iterator _it; + ::xsd::cxx::parser::diagnostics::const_iterator _it; for (_it=e.diagnostics().begin(); _it!=e.diagnostics().end(); _it++) { ss << std::endl; @@ -620,19 +663,29 @@ void dna_import::ParseXML(const std::string& fileName, vdnaStnPtr* vStations, PU ss << ", severity " << _it->severity() << std::endl; ss << " - " << _it->message(); } + import_file_mutex.unlock(); SignalExceptionParse(ss.str(), 0); } catch (const ::xml_schema::exception& e) { std::stringstream ss; ss << "ParseXML(): An xml_schema exception was encountered while parsing " << fileName << "." << std::endl << " " << e.what(); + import_file_mutex.unlock(); SignalExceptionParse(static_cast(ss.str()), 0); } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "ParseXML(): An error was encountered while parsing " << fileName << "." << std::endl << " " << e.what(); + import_file_mutex.unlock(); + SignalExceptionParse(ss.str(), 0); + } catch (...) { std::stringstream ss; ss << "ParseXML(): An unknown error was encountered while parsing " << fileName << "." << std::endl; - SignalExceptionParse(ss.str(), 0); + import_file_mutex.unlock(); + SignalExceptionParse(ss.str(), 0); } if (parseStatus_ != PARSE_SUCCESS) @@ -818,12 +871,16 @@ void dna_import::ApplyDiscontinuitiesMeasurements(vdnaMsrPtr* vMeasurements) continue; } - // Check if an epoch been provided with this measurement - if (_it_msr->get()->GetEpoch().empty()) + // Prefer the observation epoch for discontinuity matching; fall back + // to reference-frame epoch if the file did not supply an observation epoch. + std::string match_epoch = _it_msr->get()->GetObservationEpoch(); + if (match_epoch.empty()) + match_epoch = _it_msr->get()->GetEpoch(); + if (match_epoch.empty()) continue; // Capture the epoch of the measurement - site_date = dateFromString(_it_msr->get()->GetEpoch()); + site_date = dateFromString(match_epoch); // 2. Handle 'first' station for every measurement type stn1 = _it_msr->get()->GetFirst(); @@ -930,8 +987,15 @@ void dna_import::ApplyDiscontinuitiesMeasurements_GX(std::vectorend(); _it_msr++) { + // Prefer observation epoch; fall back to reference-frame epoch. + std::string match_epoch = _it_msr->GetObservationEpoch(); + if (match_epoch.empty()) + match_epoch = _it_msr->GetEpoch(); + if (match_epoch.empty()) + continue; + // Capture the start date of the site - site_date = dateFromString(_it_msr->GetEpoch()); + site_date = dateFromString(match_epoch); // Station 1 stn1 = _it_msr->GetFirst(); @@ -983,8 +1047,15 @@ void dna_import::ApplyDiscontinuitiesMeasurements_Y(std::vector* v _it_msr != vGpsPoints->end(); _it_msr++) { + // Prefer observation epoch; fall back to reference-frame epoch. + std::string match_epoch = _it_msr->GetObservationEpoch(); + if (match_epoch.empty()) + match_epoch = _it_msr->GetEpoch(); + if (match_epoch.empty()) + continue; + // Capture the start date of the site - site_date = dateFromString(_it_msr->GetEpoch()); + site_date = dateFromString(match_epoch); // Station 1 stn1 = _it_msr->GetFirst(); @@ -1148,7 +1219,7 @@ void dna_import::ParseDNA(const std::string& fileName, vdnaStnPtr* vStations, PU projectSettings_.r.epoch = fileEpoch; m_strProjectDefaultEpoch = fileEpoch; - InitialiseDatum(projectSettings_.i.reference_frame, projectSettings_.i.epoch); + InitialiseDatum(projectSettings_.i.reference_frame, projectSettings_.i.epoch, projectSettings_.i.observation_epoch); } } @@ -1739,12 +1810,18 @@ void dna_import::ParseDNAMSRLinear(const std::string& sBuf, dnaMsrPtr& msr_ptr) // Epoch msr_ptr->SetEpoch(ParseEpochValue(sBuf, "ParseDNAMSRLinear")); + // Observation epoch (DNA v3.02 column, optional) + { + std::string obs_epoch = ParseObsEpochValue(sBuf, "ParseDNAMSRLinear"); + if (!obs_epoch.empty()) + msr_ptr->SetObservationEpoch(obs_epoch); + } // Capture msr_id and cluster_id (for database referencing) ParseDatabaseIds(sBuf, "ParseDNAMSRLinear", msr_ptr->GetTypeC()); msr_ptr->SetDatabaseMap(m_msr_db_map); - // instrument and target heights only make sense for + // instrument and target heights only make sense for // slope distances, vertical angles and zenith distances switch (msr_ptr->GetTypeC()) { @@ -1806,6 +1883,12 @@ void dna_import::ParseDNAMSRCoordinate(const std::string& sBuf, dnaMsrPtr& msr_p // Epoch msr_ptr->SetEpoch(ParseEpochValue(sBuf, "ParseDNAMSRLinear")); + // Observation epoch (DNA v3.02 column, optional) + { + std::string obs_epoch = ParseObsEpochValue(sBuf, "ParseDNAMSRCoordinate"); + if (!obs_epoch.empty()) + msr_ptr->SetObservationEpoch(obs_epoch); + } // Capture msr_id and cluster_id (for database referencing), then set // database id info @@ -1821,6 +1904,7 @@ void dna_import::ParseDNAMSRGPSBaselines(std::string& sBuf, dnaMsrPtr& msr_ptr, bslTmp.SetReferenceFrame(msr_ptr->GetReferenceFrame()); bslTmp.SetEpoch(msr_ptr->GetEpoch()); + bslTmp.SetObservationEpoch(msr_ptr->GetObservationEpoch()); // Measurement type std::string tmp; @@ -1943,10 +2027,18 @@ void dna_import::ParseDNAMSRGPSBaselines(std::string& sBuf, dnaMsrPtr& msr_ptr, // Set the baseline epoch bslTmp.SetEpoch(tmp); } + + // Observation epoch (DNA v3.02 column, optional; overrides default) + std::string obs_epoch = ParseObsEpochValue(sBuf, "ParseDNAMSRGPSBaselines"); + if (!obs_epoch.empty()) + { + msr_ptr->SetObservationEpoch(obs_epoch); + bslTmp.SetObservationEpoch(obs_epoch); + } } catch (std::runtime_error& e) { std::stringstream ss; - ss << "ParseDNAMSRGPSBaselines(): Error parsing epoch: " << + ss << "ParseDNAMSRGPSBaselines(): Error parsing epoch: " << std::endl << " " << e.what(); SignalExceptionParseDNA(ss.str(), "", dml_.msr_gps_epoch); } @@ -2042,6 +2134,7 @@ void dna_import::ParseDNAMSRGPSPoints(std::string& sBuf, dnaMsrPtr& msr_ptr, boo pntTmp.SetReferenceFrame(msr_ptr->GetReferenceFrame()); pntTmp.SetEpoch(msr_ptr->GetEpoch()); + pntTmp.SetObservationEpoch(msr_ptr->GetObservationEpoch()); // Measurement type std::string tmp; @@ -2136,6 +2229,7 @@ void dna_import::ParseDNAMSRGPSPoints(std::string& sBuf, dnaMsrPtr& msr_ptr, boo // Set the point frame pntTmp.SetReferenceFrame(projectSettings_.i.reference_frame); pntTmp.SetEpoch(msr_ptr->GetEpoch()); + pntTmp.SetObservationEpoch(msr_ptr->GetObservationEpoch()); } else //if (!projectSettings_.i.override_input_rfame) { @@ -2153,7 +2247,7 @@ void dna_import::ParseDNAMSRGPSPoints(std::string& sBuf, dnaMsrPtr& msr_ptr, boo } catch (std::runtime_error& e) { std::stringstream ss; - ss << "ParseDNAMSRGPSPoints(): Error parsing reference frame: " << std::endl << + ss << "ParseDNAMSRGPSPoints(): Error parsing reference frame: " << std::endl << " " << e.what(); SignalExceptionParseDNA(ss.str(), "", dml_.msr_gps_reframe); } @@ -2179,10 +2273,18 @@ void dna_import::ParseDNAMSRGPSPoints(std::string& sBuf, dnaMsrPtr& msr_ptr, boo // Set the point epoch pntTmp.SetEpoch(tmp); } + + // Observation epoch (DNA v3.02 column, optional; overrides default) + std::string obs_epoch = ParseObsEpochValue(sBuf, "ParseDNAMSRGPSPoints"); + if (!obs_epoch.empty()) + { + msr_ptr->SetObservationEpoch(obs_epoch); + pntTmp.SetObservationEpoch(obs_epoch); + } } catch (std::runtime_error& e) { std::stringstream ss; - ss << "ParseDNAMSRGPSPoints(): Error parsing epoch: " << std::endl << + ss << "ParseDNAMSRGPSPoints(): Error parsing epoch: " << std::endl << " " << e.what(); SignalExceptionParseDNA(ss.str(), "", dml_.msr_gps_epoch); } @@ -2691,7 +2793,7 @@ std::string dna_import::ParseEpochValue(const std::string& sBuf, const std::stri { if (sBuf.length() <= dml_.msr_gps_epoch) return ""; - + std::string epoch; try { if (sBuf.length() > static_cast(dml_.msr_gps_epoch + dmw_.msr_gps_epoch)) @@ -2707,6 +2809,30 @@ std::string dna_import::ParseEpochValue(const std::string& sBuf, const std::stri return epoch; } +std::string dna_import::ParseObsEpochValue(const std::string& sBuf, const std::string& calling_function) +{ + // DNA v3.01 (and earlier) files don't have an observation-epoch column; + // in that case the field layout leaves msr_gps_obs_epoch at 0. + if (dml_.msr_gps_obs_epoch == 0 || dmw_.msr_gps_obs_epoch == 0) + return ""; + if (sBuf.length() <= dml_.msr_gps_obs_epoch) + return ""; + + std::string obs_epoch; + try { + if (sBuf.length() > static_cast(dml_.msr_gps_obs_epoch + dmw_.msr_gps_obs_epoch)) + obs_epoch = trimstr(sBuf.substr(dml_.msr_gps_obs_epoch, dmw_.msr_gps_obs_epoch)); + else + obs_epoch = trimstr(sBuf.substr(dml_.msr_gps_obs_epoch)); + } + catch (...) { + SignalExceptionParseDNA(calling_function + "(): Could not extract observation epoch from the record: ", + sBuf, dml_.msr_gps_obs_epoch); + } + + return obs_epoch; +} + std::string dna_import::ParseGPSMsrValue(const std::string& sBuf, const std::string& element, const std::string& calling_function) { try { @@ -2770,6 +2896,12 @@ void dna_import::ParseDNAMSRAngular(const std::string& sBuf, dnaMsrPtr& msr_ptr) // Epoch msr_ptr->SetEpoch(ParseEpochValue(sBuf, "ParseDNAMSRAngular")); + // Observation epoch (DNA v3.02 column, optional) + { + std::string obs_epoch = ParseObsEpochValue(sBuf, "ParseDNAMSRAngular"); + if (!obs_epoch.empty()) + msr_ptr->SetObservationEpoch(obs_epoch); + } // Capture msr_id and cluster_id (for database referencing), then set // database id info @@ -2849,6 +2981,12 @@ UINT32 dna_import::ParseDNAMSRDirections(std::string& sBuf, dnaMsrPtr& msr_ptr, // Epoch msr_ptr->SetEpoch(ParseEpochValue(sBuf, "ParseDNAMSRDirections")); + // Observation epoch (DNA v3.02 column, optional) + { + std::string obs_epoch = ParseObsEpochValue(sBuf, "ParseDNAMSRDirections"); + if (!obs_epoch.empty()) + msr_ptr->SetObservationEpoch(obs_epoch); + } // Capture msr_id and cluster_id (for database referencing) ParseDatabaseIds(sBuf, "ParseDNAMSRDirections", msr_ptr->GetTypeC()); diff --git a/dynadjust/dynadjust/dnaimport/dnainterop.hpp b/dynadjust/dynadjust/dnaimport/dnainterop.hpp index 7c4a4f201..5376282f1 100644 --- a/dynadjust/dynadjust/dnaimport/dnainterop.hpp +++ b/dynadjust/dynadjust/dnaimport/dnainterop.hpp @@ -180,8 +180,16 @@ class dna_import { inline void ResetFileOrder() const { g_fileOrder = 0; } inline bool filespecifiedReferenceFrame() const { return _filespecifiedreferenceframe; } inline bool filespecifiedEpoch() const { return _filespecifiedepoch; } - void InitialiseDatum(const std::string& reference_frame, const std::string epoch=""); - + void InitialiseDatum(const std::string& reference_frame, const std::string epoch="", + const std::string observation_epoch=""); + + // Applies the project-level observation epoch (from --observation-epoch) to every + // measurement whose observation_epoch is empty or equal to its reference-frame epoch + // (i.e. was auto-defaulted). Explicit file-level values set via + // or DNA v3.02 column 25 that differ from epoch are preserved. No-op if the CLI flag + // was not supplied. + void ApplyProjectObservationEpoch(vdnaMsrPtr* vMeasurements); + void PrintMeasurementsToStations(std::string& m2s_file, MsrTally* parsemsrTally, std::string& bst_file, std::string& bms_file, std::string& aml_file, pvASLPtr vAssocStnList); @@ -236,6 +244,7 @@ class dna_import { std::string ParseScaleHValue(const std::string& sBuf, const std::string& calling_function); std::string ParseRefFrameValue(const std::string& sBuf, const std::string& calling_function); std::string ParseEpochValue(const std::string& sBuf, const std::string& calling_function); + std::string ParseObsEpochValue(const std::string& sBuf, const std::string& calling_function); void ParseDatabaseIds(const std::string& sBuf, const std::string& calling_function, const char msrType); void ParseDatabaseClusterId(const std::string& sBuf, const std::string& calling_function); @@ -338,6 +347,7 @@ class dna_import { std::string m_strProjectDefaultEpsg; std::string m_strProjectDefaultEpoch; + std::string m_strProjectObservationEpoch; // Project-level observation epoch from --observation-epoch (immutable; may be empty) std::string m_msrComments; vvUINT32 v_ISL_; // Inner stations diff --git a/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.cxx b/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.cxx index a861f3941..a660c3e70 100644 --- a/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.cxx +++ b/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.cxx @@ -10,8 +10,6 @@ #include #include -#include - using namespace dynadjust::measurements; using namespace dynadjust::epsg; using namespace dynadjust::exception; @@ -595,6 +593,30 @@ void DnaMeasurement_pimpl::Epoch(const ::std::string& Epoch) } } +void DnaMeasurement_pimpl::EpochOfObservation(const ::std::string& EpochOfObservation) +{ + if (!_dnaCurrentMsr) + throw XMLInteropException("\"Type\" element must be the first element within \"DnaMeasurement\".", 0); + + try + { + if (EpochOfObservation.empty()) + return; + + _dnaCurrentMsr->SetObservationEpoch(EpochOfObservation); + } + catch (const std::runtime_error& e) { + std::stringstream ss(""); + ss << e.what(); + ss << " - Measurement type: " << _dnaCurrentMsr->GetType() << std::endl << + " - From: " << _dnaCurrentMsr->GetFirst() << std::endl << + " - To: " << _dnaCurrentMsr->GetTarget() << std::endl << + " - Reference frame: " << _dnaCurrentMsr->GetReferenceFrame() << std::endl << + " - Epoch of observation: " << EpochOfObservation << std::endl; + throw XMLInteropException(ss.str(), 0); + } +} + void DnaMeasurement_pimpl::ReferenceFrame(const ::std::string& ReferenceFrame) { if (!_dnaCurrentMsr) @@ -942,6 +964,19 @@ DnaXmlFormat_pimpl::DnaXmlFormat_pimpl(std::ifstream* is, PUINT32 clusterID, con _referenceframe = referenceframe; _epoch = epoch; + // Initialise file EPSG and epoch from the default reference frame. + // Previously these were set by the XSD attribute defaults during + // validation. With dont_validate, the attribute parsers are only + // called when the attributes are present in the XML, so we need + // sensible fallbacks. + try { + _fileEpsg = epsgStringFromName(_referenceframe); + } + catch (...) { + _fileEpsg.clear(); + } + _fileEpoch = epoch; + // capture first file flag _firstFile = firstFile; diff --git a/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.hxx b/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.hxx index 7f66ecd63..58d1e8213 100644 --- a/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.hxx +++ b/dynadjust/dynadjust/dnaimport/dnaparser_pimpl.hxx @@ -91,6 +91,7 @@ public: virtual void Directions (); virtual void Vscale (const ::std::string&); virtual void Epoch (const ::std::string&); + virtual void EpochOfObservation (const ::std::string&); virtual void ReferenceFrame (const ::std::string&); virtual void GPSBaseline (); virtual void Hscale (const ::std::string&); diff --git a/dynadjust/dynadjust/dnaimport/dnaparser_pskel.cxx b/dynadjust/dynadjust/dnaimport/dnaparser_pskel.cxx index 27cdc8667..b56389d46 100644 --- a/dynadjust/dynadjust/dnaimport/dnaparser_pskel.cxx +++ b/dynadjust/dynadjust/dnaimport/dnaparser_pskel.cxx @@ -38,8 +38,6 @@ #include -#include - using namespace dynadjust::measurements; // Clusterpoint_pskel @@ -260,6 +258,11 @@ void DnaMeasurement_pskel::Epoch_parser (::xml_schema::string_pskel& p) this->Epoch_parser_ = &p; } +void DnaMeasurement_pskel::EpochOfObservation_parser (::xml_schema::string_pskel& p) +{ + this->EpochOfObservation_parser_ = &p; +} + void DnaMeasurement_pskel::GPSBaseline_parser (::GPSBaseline_pskel& p) { this->GPSBaseline_parser_ = &p; @@ -320,6 +323,7 @@ void DnaMeasurement_pskel::parsers ( ::Directions_pskel& Directions, ::xml_schema::string_pskel& Vscale, ::xml_schema::string_pskel& Epoch, + ::xml_schema::string_pskel& EpochOfObservation, ::xml_schema::string_pskel& ReferenceFrame, ::GPSBaseline_pskel& GPSBaseline, ::xml_schema::string_pskel& Hscale, @@ -345,6 +349,7 @@ void DnaMeasurement_pskel::parsers ( this->Directions_parser_ = &Directions; this->Vscale_parser_ = &Vscale; this->Epoch_parser_ = &Epoch; + this->EpochOfObservation_parser_ = &EpochOfObservation; this->ReferenceFrame_parser_ = &ReferenceFrame; this->GPSBaseline_parser_ = &GPSBaseline; this->Hscale_parser_ = &Hscale; @@ -372,6 +377,7 @@ DnaMeasurement_pskel::DnaMeasurement_pskel () Directions_parser_ (0), Vscale_parser_ (0), Epoch_parser_ (0), + EpochOfObservation_parser_ (0), ReferenceFrame_parser_ (0), GPSBaseline_parser_ (0), Hscale_parser_ (0), @@ -1328,6 +1334,10 @@ void DnaMeasurement_pskel::Epoch (const ::std::string&) { } +void DnaMeasurement_pskel::EpochOfObservation (const ::std::string&) +{ +} + void DnaMeasurement_pskel::ReferenceFrame (const ::std::string&) { } @@ -1515,6 +1525,16 @@ bool DnaMeasurement_pskel::_start_element_impl (const ::xml_schema::ro_string& n return true; } + if (n == "EpochOfObservation" && ns.empty ()) + { + this->::xml_schema::complex_content::context_.top ().parser_ = this->EpochOfObservation_parser_; + + if (this->EpochOfObservation_parser_) + this->EpochOfObservation_parser_->pre (); + + return true; + } + if (n == "ReferenceFrame" && ns.empty ()) { this->::xml_schema::complex_content::context_.top ().parser_ = this->ReferenceFrame_parser_; @@ -1739,6 +1759,14 @@ bool DnaMeasurement_pskel::_end_element_impl (const ::xml_schema::ro_string& ns, return true; } + if (n == "EpochOfObservation" && ns.empty ()) + { + if (this->EpochOfObservation_parser_) + this->EpochOfObservation (this->EpochOfObservation_parser_->post_string ()); + + return true; + } + if (n == "ReferenceFrame" && ns.empty ()) { if (this->ReferenceFrame_parser_) diff --git a/dynadjust/dynadjust/dnaimport/dnaparser_pskel.hxx b/dynadjust/dynadjust/dnaimport/dnaparser_pskel.hxx index 79911766b..30fdd8643 100644 --- a/dynadjust/dynadjust/dnaimport/dnaparser_pskel.hxx +++ b/dynadjust/dynadjust/dnaimport/dnaparser_pskel.hxx @@ -467,6 +467,7 @@ public: virtual void Directions (); virtual void Vscale (const ::std::string&); virtual void Epoch (const ::std::string&); + virtual void EpochOfObservation (const ::std::string&); virtual void ReferenceFrame (const ::std::string&); virtual void GPSBaseline (); virtual void Hscale (const ::std::string&); @@ -558,6 +559,7 @@ public: void Directions_parser (::Directions_pskel&); void Vscale_parser (::xml_schema::string_pskel&); void Epoch_parser (::xml_schema::string_pskel&); + void EpochOfObservation_parser (::xml_schema::string_pskel&); void ReferenceFrame_parser (::xml_schema::string_pskel&); void GPSBaseline_parser (::GPSBaseline_pskel&); void Hscale_parser (::xml_schema::string_pskel&); @@ -583,6 +585,7 @@ public: ::Directions_pskel& /* Directions */, ::xml_schema::string_pskel& /* Vscale */, ::xml_schema::string_pskel& /* Epoch */, + ::xml_schema::string_pskel& /* EpochOfObservation */, ::xml_schema::string_pskel& /* ReferenceFrame */, ::GPSBaseline_pskel& /* GPSBaseline */, ::xml_schema::string_pskel& /* Hscale */, @@ -630,6 +633,7 @@ protected: ::Directions_pskel* Directions_parser_; ::xml_schema::string_pskel* Vscale_parser_; ::xml_schema::string_pskel* Epoch_parser_; + ::xml_schema::string_pskel* EpochOfObservation_parser_; ::xml_schema::string_pskel* ReferenceFrame_parser_; ::GPSBaseline_pskel* GPSBaseline_parser_; ::xml_schema::string_pskel* Hscale_parser_; diff --git a/dynadjust/dynadjust/dnaimportwrapper/dnaimportwrapper.cpp b/dynadjust/dynadjust/dnaimportwrapper/dnaimportwrapper.cpp index cb5b6c1c8..ffd48a39c 100644 --- a/dynadjust/dynadjust/dnaimportwrapper/dnaimportwrapper.cpp +++ b/dynadjust/dynadjust/dnaimportwrapper/dnaimportwrapper.cpp @@ -318,6 +318,29 @@ int ParseCommandLineOptions(const int& argc, char* argv[], const boost::program_ p.i.user_supplied_epoch = 1; } + if (vm.count(OBSERVATION_EPOCH)) { + // Get today's date? + if (iequals(p.i.observation_epoch, "today")) + p.i.observation_epoch = stringFromToday(); + // Has the user supplied the year only? + else if (p.i.observation_epoch.rfind(".") == std::string::npos) + p.i.observation_epoch.insert(0, "01.01."); + + if (p.i.observation_epoch.length() < 10) { + std::string dateStr = FormatDateString(p.i.observation_epoch); + if (dateStr.empty()) { + std::cout << std::endl + << "- Error: Cannot parse observation epoch '" << p.i.observation_epoch << "'." << std::endl + << " Please supply date in the format dd.mm.yyyy" << std::endl + << std::endl; + return EXIT_FAILURE; + } + p.i.observation_epoch = dateStr; + } + + p.i.user_supplied_observation_epoch = 1; + } + ////////////////////////////////////////////////////////////////////////////// // Data screening options if (vm.count(GET_MSRS_TRANSCENDING_BOX)) p.i.include_transcending_msrs = 1; @@ -1029,7 +1052,7 @@ int ImportDataFiles(dna_import& parserDynaML, vdnaStnPtr* vStations, vdnaMsrPtr* // Initialise the 'default' datum (frame and epoch) for the project, from the first file, unless the // frame and epoch have been set by the user, in which case InitialiseDatum has already initialised // the datum. - parserDynaML.InitialiseDatum(p.i.reference_frame, p.i.epoch); + parserDynaML.InitialiseDatum(p.i.reference_frame, p.i.epoch, p.i.observation_epoch); } catch (const XMLInteropException& e) { std::stringstream ss; ss << "- Error: "; @@ -1280,11 +1303,15 @@ int main(int argc, char* argv[]) { (std::string("Project epoch for all stations and measurements when input files do not " "specify an epoch. Default is ") + p.i.epoch + ".") - .c_str())(OVERRIDE_INPUT_FRAME, - (std::string("Replace the reference frame specified in the input files with " - "the reference frame specified by arg in --") + - std::string(REFERENCE_FRAME)) - .c_str()); + .c_str())(OBSERVATION_EPOCH, boost::program_options::value(&p.i.observation_epoch), + "Default observation epoch (immutable under dnareftran) for " + "measurements that do not supply one. Format dd.mm.yyyy. " + "Use special value \"today\" to capture the current date.")( + OVERRIDE_INPUT_FRAME, + (std::string("Replace the reference frame specified in the input files with " + "the reference frame specified by arg in --") + + std::string(REFERENCE_FRAME)) + .c_str()); data_screening_options.add_options()( BOUNDING_BOX, boost::program_options::value(&p.i.bounding_box), @@ -1511,7 +1538,7 @@ int main(int argc, char* argv[]) { // See comments in InitialiseDatum() try { // Initialise the 'default' datum for the project. - parserDynaML.InitialiseDatum(p.i.reference_frame, p.i.epoch); + parserDynaML.InitialiseDatum(p.i.reference_frame, p.i.epoch, p.i.observation_epoch); } catch (const XMLInteropException& e) { std::cout << std::endl << cmd_line_banner; imp_file << std::endl << cmd_line_banner; @@ -1678,6 +1705,25 @@ int main(int argc, char* argv[]) { if (vm.count(STATION_DISCONTINUITY_FILE)) { p.i.apply_discontinuities = true; + // Static datums have no meaningful reference-frame epoch, so the discontinuity + // matcher will fall back to that (possibly incorrect) value unless the user + // supplies an observation epoch per-measurement or via --observation-epoch. + // See issue #325. + if (isEpsgDatumStatic(epsgCode) && !p.i.user_supplied_observation_epoch) { + std::stringstream wss; + wss << std::endl + << "- Warning: --discontinuity-file is in use with static datum " << p.i.reference_frame + << "." << std::endl + << " Station-instance allocation relies on 'Epoch of Observation'. Supply it per-measurement" << std::endl + << " via / DNA column 25, or set a project default via --observation-epoch." << std::endl + << " Without it, the reference-frame epoch will be used as a fallback, which may be incorrect" << std::endl + << " for a static datum." << std::endl + << std::endl; + if (!p.g.quiet) + std::cout << wss.str(); + imp_file << wss.str(); + } + // Does it exist? if (!std::filesystem::exists(p.i.stn_discontinuityfile)) { std::filesystem::path discontPath(p.i.stn_discontinuityfile); @@ -1739,20 +1785,11 @@ int main(int argc, char* argv[]) { } // Import data as normal else { - // Change current directory to the import folder - // A hack to circumvent the problem caused by importing DynaML files in - // different directories to where import is run from, causing errors - // because DynaML.xsd cannot be found. - std::filesystem::path currentPath(std::filesystem::current_path()); - std::filesystem::current_path(std::filesystem::path(p.g.input_folder)); - // Import all data as-is. // All filtering is performed later below if (ImportDataFiles(parserDynaML, &vStations, &vMeasurements, &vstationsTotal, &vmeasurementsTotal, &imp_file, &vinput_file_meta, &parsestnTally, &parsemsrTally, errorCount, p) != EXIT_SUCCESS) return EXIT_FAILURE; - - current_path(currentPath); } epsgCode = epsgCodeFromName(p.i.reference_frame); @@ -2544,6 +2581,12 @@ int main(int argc, char* argv[]) { } } + // Apply the project-level observation_epoch (from --observation-epoch) to measurements + // whose observation_epoch is empty or auto-defaulted from the reference-frame epoch. + // No-op when --observation-epoch was not supplied. + if (p.i.user_supplied_observation_epoch) + parserDynaML.ApplyProjectObservationEpoch((vdnaMsrPtr*)&vmeasurementsTotal); + // Create binary measurement file. // Binary measurement file can be serialised without stations if (msrCount > 0) { diff --git a/dynadjust/dynadjust/dnaplotwrapper/CMakeLists.txt b/dynadjust/dynadjust/dnaplotwrapper/CMakeLists.txt index 4cc3bf6c3..bd5d6697b 100644 --- a/dynadjust/dynadjust/dnaplotwrapper/CMakeLists.txt +++ b/dynadjust/dynadjust/dnaplotwrapper/CMakeLists.txt @@ -83,8 +83,8 @@ if(BUILD_STATIC) "-framework CoreFoundation") else() target_link_options(${STATIC_TARGET_NAME} PRIVATE - -static) - + -static-libgcc + -static-libstdc++) endif() elseif(WIN32) # On Windows with MSVC diff --git a/dynadjust/dynadjust/dnareftran/dnareftran.cpp b/dynadjust/dynadjust/dnareftran/dnareftran.cpp index 03ddaa838..c21a1ec28 100644 --- a/dynadjust/dynadjust/dnareftran/dnareftran.cpp +++ b/dynadjust/dynadjust/dnareftran/dnareftran.cpp @@ -82,8 +82,6 @@ void dna_reftran::TransformBinaryFiles(const std::string& bstFile, const std::st // Identify and apply any substitutions for WGS84 in the list of measurements ApplyMeasurementFrameSubstitutions(); - datumTo_.SetDatumFromName(newFrame, newEpoch); - // 2. Transform measurements first (because pre-transformed station // coordinates are required) TransformMeasurementRecords(newFrame, newEpoch); @@ -312,6 +310,14 @@ void dna_reftran::LoadWGS84FrameSubstitutions() frameSubstitution.reset(new WGS84_ITRF2014); _frameSubstitutions.push_back(frameSubstitution); + // SIRGAS95 to ITRF94 + frameSubstitution.reset(new SIRGAS95_ITRF94); + _frameSubstitutions.push_back(frameSubstitution); + + // SIRGAS2000 to ITRF2000 + frameSubstitution.reset(new SIRGAS2000_ITRF2000); + _frameSubstitutions.push_back(frameSubstitution); + std::sort(_frameSubstitutions.begin(), _frameSubstitutions.end(), CompareSubstituteOnFrameName< frame_substitutions_t, std::string>()); @@ -382,6 +388,49 @@ void dna_reftran::LogFrameSubstitutions(std::vector& substit } +void dna_reftran::ApplyToFrameSubstitution() +{ + std::string epsgSubstitute; + + std::string strEpsgTo(datumTo_.GetEpsgCode_s()); + std::string strEpochTo(datumTo_.GetEpoch_s()); + + _v_frame_substitutions.clear(); + + try { + if (IsolateandApplySubstitute(strEpsgTo, strEpochTo, epsgSubstitute)) + { + _v_frame_substitutions.push_back(string_string_pair( + datumFromEpsgString(std::string(strEpsgTo)), + datumFromEpsgString(epsgSubstitute))); + datumTo_.SetDatum(epsgSubstitute); + datumTo_.SetEpoch(strEpochTo); + } + } catch (const RefTranException& e) { + std::stringstream error_msg; + error_msg << std::endl + << " - Target frame: " << datumFromEpsgString(strEpsgTo) << " doesn't exist." + << std::endl; + + switch (e.exception_type()) { + case REFTRAN_WGS84_TRANS_UNSUPPORTED: { + std::stringstream throw_msg; + throw_msg << e.what() << error_msg.str() << std::endl; + throw RefTranException(throw_msg.str(), REFTRAN_WGS84_TRANS_UNSUPPORTED); + break; + } + default: throw RefTranException(e.what()); break; + } + } + + if (_v_frame_substitutions.empty()) + return; + if (projectSettings_.g.verbose < 2) + return; + + LogFrameSubstitutions(_v_frame_substitutions, "Frame"); +} + void dna_reftran::ApplyStationFrameSubstitutions() { @@ -572,6 +621,7 @@ void dna_reftran::WriteBinaryStationFile(const std::string& bstfileName) snprintf(bst_meta_.modifiedBy, sizeof(bst_meta_.modifiedBy), "%s", __BINARY_NAME__); snprintf(bst_meta_.epsgCode, sizeof(bst_meta_.epsgCode), "%s", strEpsg.substr(0, STN_EPSG_WIDTH).c_str()); snprintf(bst_meta_.epoch, sizeof(bst_meta_.epoch), "%s", strEpoch.substr(0, STN_EPOCH_WIDTH).c_str()); + // observation_epoch is immutable across transformations - intentionally not overwritten. bst_meta_.reftran = true; try { @@ -606,6 +656,7 @@ void dna_reftran::WriteBinaryMeasurementFile(const std::string& bmsfileName) snprintf(bms_meta_.modifiedBy, sizeof(bms_meta_.modifiedBy), "%s", __BINARY_NAME__); snprintf(bms_meta_.epsgCode, sizeof(bms_meta_.epsgCode), "%s", strEpsg.substr(0, STN_EPSG_WIDTH).c_str()); snprintf(bms_meta_.epoch, sizeof(bms_meta_.epoch), "%s", strEpoch.substr(0, STN_EPOCH_WIDTH).c_str()); + // observation_epoch is immutable across transformations - intentionally not overwritten. bms_meta_.reftran = true; try { @@ -1455,8 +1506,10 @@ void dna_reftran::TransformStationRecords(const std::string& newFrame, const std #endif try { - // 1. Get the datum (and epoch) of the desired system - datumTo_.SetDatumFromName(newFrame, newEpoch); + // 1. Get the datum (and epoch) of the desired system and apply the + // appropriate substitution (required to determine the transformation parameters) + datumTo_.SetDatumFromName(newFrame, newEpoch); + ApplyToFrameSubstitution(); // 2. For every station, get the datum, then transform // TransformStation takes @@ -1481,6 +1534,7 @@ void dna_reftran::TransformStationRecords(const std::string& newFrame, const std // d. Update meta snprintf(stn_it->epsgCode, sizeof(stn_it->epsgCode), "%s", datumTo_.GetEpsgCode_s().c_str()); snprintf(stn_it->epoch, sizeof(stn_it->epoch), "%s", datumTo_.GetEpoch_s().c_str()); + // observation_epoch is immutable across transformations - intentionally not overwritten. transformationPerformed_ = true; m_stnsTransformed++; } @@ -1556,15 +1610,15 @@ void dna_reftran::TransformStation(it_vstn_t& stn_it, const CDnaDatum& datumFrom void dna_reftran::TransformMeasurementRecords(const std::string& newFrame, const std::string& newEpoch) { - it_vmsr_t msr_it; + it_vmsr_t msr_it = bmsBinaryRecords_.end(); CDnaDatum datumFrom; data_type_ = msr_data; - + // Create the transformation parameters to be used for the // entire set of measurement records. If a measurement is in a different // frame, obtain new parameters transformation_parameter_set transformationParameters; - + transformationPerformed_ = false; m_msrsTransformed = m_msrsNotTransformed = 0; @@ -1572,11 +1626,12 @@ void dna_reftran::TransformMeasurementRecords(const std::string& newFrame, const TRACE("\nTransforming measurements...\n\n"); #endif + // Setup before the measurement loop - errors here cannot reference a specific measurement + datumTo_.SetDatumFromName(newFrame, newEpoch); + ApplyToFrameSubstitution(); + try { - // 1. Get the datum (and epoch) of the desired system - datumTo_.SetDatumFromName(newFrame, newEpoch); - - // 2. For every measurement, get the datum, determine parameters, then transform + // For every measurement, get the datum, determine parameters, then transform for (msr_it=bmsBinaryRecords_.begin(); msr_it!=bmsBinaryRecords_.end(); ++msr_it) { // a. ignore measurements not subject to geodetic datum @@ -1741,6 +1796,8 @@ void dna_reftran::TransformMeasurement_GX(it_vmsr_t& msr_it, const CDnaDatum& da msr_it->term1 = coordinates2_mod.get(0, 0) - coordinates1_mod.get(0, 0); //TRACE("\nTransformed baseline\n"); //TRACE("%.4f\n", msr_it->term1); + // observation_epoch is immutable across transformations - the three X/Y/Z + // component writes below intentionally overwrite only epsgCode and epoch. snprintf(msr_it->epsgCode, sizeof(msr_it->epsgCode), "%s", datumTo_.GetEpsgCode_s().c_str()); snprintf(msr_it->epoch, sizeof(msr_it->epoch), "%s", datumTo_.GetEpoch_s().c_str()); msr_it++; @@ -1831,6 +1888,8 @@ void dna_reftran::TransformMeasurement_Y(it_vmsr_t& msr_it, const CDnaDatum& dat } // Assign 'transformed' elements + // observation_epoch is immutable across transformations - the three X/Y/Z + // component writes below intentionally overwrite only epsgCode and epoch. msr_it->term1 = coordinates_mod.get(0, 0); snprintf(msr_it->epsgCode, sizeof(msr_it->epsgCode), "%s", datumTo_.GetEpsgCode_s().c_str()); snprintf(msr_it->epoch, sizeof(msr_it->epoch), "%s", datumTo_.GetEpoch_s().c_str()); diff --git a/dynadjust/dynadjust/dnareftran/dnareftran.hpp b/dynadjust/dynadjust/dnareftran/dnareftran.hpp index 1f629e0ba..9cc9d224f 100644 --- a/dynadjust/dynadjust/dnareftran/dnareftran.hpp +++ b/dynadjust/dynadjust/dnareftran/dnareftran.hpp @@ -197,6 +197,7 @@ class dna_reftran { void LogFrameSubstitutions(std::vector& substitutions, const std::string& type); void ApplyStationFrameSubstitutions(); void ApplyMeasurementFrameSubstitutions(); + void ApplyToFrameSubstitution(); bool IsolateandApplySubstitute(const std::string& epsgCode, const std::string& epoch, std::string& epsgSubstitute); @@ -224,8 +225,9 @@ class dna_reftran { v_plate_motion_cartesians plate_motion_cartesians_; // Helmert parameters computed from Euler parameters vframeSubsPtr _frameSubstitutions; // Reference frame substitutions - std::vector _v_stn_substitutions; // station substitutions made - std::vector _v_msr_substitutions; // station substitutions made + std::vector _v_stn_substitutions; // station substitutions made + std::vector _v_msr_substitutions; // station substitutions made + std::vector _v_frame_substitutions; // frameTo substitution made v_string_uint32_pair vplateMap_; // Plate Map index sorted on plate ID diff --git a/dynadjust/dynadjust/dnareftranwrapper/CMakeLists.txt b/dynadjust/dynadjust/dnareftranwrapper/CMakeLists.txt index 97b495d0e..e16d5d20a 100644 --- a/dynadjust/dynadjust/dnareftranwrapper/CMakeLists.txt +++ b/dynadjust/dynadjust/dnareftranwrapper/CMakeLists.txt @@ -85,8 +85,8 @@ if(BUILD_STATIC) "-framework CoreFoundation") else() target_link_options(${STATIC_TARGET_NAME} PRIVATE - -static) - + -static-libgcc + -static-libstdc++) endif() elseif(WIN32) # On Windows with MSVC diff --git a/dynadjust/dynadjust/dnasegmentwrapper/CMakeLists.txt b/dynadjust/dynadjust/dnasegmentwrapper/CMakeLists.txt index 77d527bb1..c7cfe7657 100644 --- a/dynadjust/dynadjust/dnasegmentwrapper/CMakeLists.txt +++ b/dynadjust/dynadjust/dnasegmentwrapper/CMakeLists.txt @@ -90,8 +90,8 @@ if(BUILD_STATIC) "-framework CoreFoundation") else() target_link_options(${STATIC_TARGET_NAME} PRIVATE - -static) - + -static-libgcc + -static-libstdc++) endif() elseif(WIN32) # On Windows with MSVC diff --git a/dynadjust/dynadjust/dynadjust/CMakeLists.txt b/dynadjust/dynadjust/dynadjust/CMakeLists.txt index 569b3c903..7ee9a8ad7 100644 --- a/dynadjust/dynadjust/dynadjust/CMakeLists.txt +++ b/dynadjust/dynadjust/dynadjust/CMakeLists.txt @@ -70,14 +70,9 @@ if(BUILD_STATIC) #CURL::libcurl "-framework CoreFoundation") else() - # On Linux/Unix, we can use full static linking target_link_options(${STATIC_TARGET_NAME} PRIVATE - -Wl,-Bstatic - -fno-builtin -static-libgcc - -static-libstdc++ - -static) - + -static-libstdc++) endif() elseif(WIN32) # On Windows with MSVC diff --git a/dynadjust/include/config/dnaoptions-interface.hpp b/dynadjust/include/config/dnaoptions-interface.hpp index d80b81615..06c100706 100644 --- a/dynadjust/include/config/dnaoptions-interface.hpp +++ b/dynadjust/include/config/dnaoptions-interface.hpp @@ -151,8 +151,12 @@ const char* const LSQ_INVERSE_METHOD = "inversion-method"; const char* const SCALE_NORMAL_UNITY = "scale-normals-to-unity"; const char* const PURGE_STAGE_FILES = "purge-stage-files"; const char* const RECREATE_STAGE_FILES = "create-stage-files"; +const char* const STAGE_PATH = "stage-path"; const char* const UPDATE_ORIGINAL_STN_FILE = "update-orig-stn-file"; +// Threading options +const char* const MAX_THREADS = "max-threads"; + const char* const SEG_MIN_INNER_STNS = "min-inner-stns"; const char* const SEG_THRESHOLD_STNS = "max-block-stns"; const char* const SEG_STARTING_STN = "starting-stns"; @@ -171,6 +175,7 @@ const char* const REFERENCE_FRAME = "reference-frame"; const char* const REFERENCE_FRAME_R = "reference-frame,r"; const char* const EPOCH = "epoch"; const char* const EPOCH_E = "epoch,e"; +const char* const OBSERVATION_EPOCH = "observation-epoch"; const char* const OVERRIDE_INPUT_FRAME = "override-input-ref-frame"; const char* const TECTONIC_PLATE_BDY_FILE = "plate-boundary-file"; const char* const TECTONIC_PLATE_BDY_FILE_B = "plate-boundary-file,b"; diff --git a/dynadjust/include/config/dnaoptions.hpp b/dynadjust/include/config/dnaoptions.hpp index 77e21eeae..8196d9c39 100644 --- a/dynadjust/include/config/dnaoptions.hpp +++ b/dynadjust/include/config/dnaoptions.hpp @@ -185,7 +185,7 @@ struct general_settings : private boost::equality_comparable { struct import_settings : private boost::equality_comparable { public: import_settings() - : reference_frame(DEFAULT_DATUM), epoch(DEFAULT_EPOCH), user_supplied_frame(0), user_supplied_epoch(0), override_input_rfame(0) + : reference_frame(DEFAULT_DATUM), epoch(DEFAULT_EPOCH), observation_epoch(""), user_supplied_frame(0), user_supplied_epoch(0), user_supplied_observation_epoch(0), override_input_rfame(0) , test_integrity(0), verify_coordinates(0), export_dynaml(0), export_from_bfiles(0) , export_single_xml_file(0), prefer_single_x_as_g(0), export_asl_file(0), export_aml_file(0), export_map_file(0) , export_dna_files(0), export_discont_file(0), import_geo_file(0), simulate_measurements(0), split_clusters(0), include_transcending_msrs(0) @@ -232,9 +232,11 @@ struct import_settings : private boost::equality_comparable { public: std::string reference_frame; // Project reference frame - used primarily for reductions on the ellipsoid. - std::string epoch; // Project epoch + std::string epoch; // Project epoch (of reference frame) + std::string observation_epoch; // Default observation epoch applied to measurements with no file-level value UINT16 user_supplied_frame; // User has supplied a frame - use this to change the default frame UINT16 user_supplied_epoch; // User has supplied a epoch - use this to change the default epoch + UINT16 user_supplied_observation_epoch; // User has supplied an observation epoch via --observation-epoch UINT16 override_input_rfame; // Override reference frame specified in input files using the default or user supplied frame. UINT16 test_integrity; // Test integrity of network UINT16 verify_coordinates; // Test integrity of coordinates @@ -430,7 +432,8 @@ struct adjust_settings : private boost::equality_comparable { , iteration_threshold((float)0.0005), free_std_dev(10.0), fixed_std_dev(PRECISION_1E6), station_constraints("") , map_file(""), bst_file(""), bms_file(""), seg_file(""), comments("") , command_line_arguments("") - , type_b_global (""), type_b_file ("") {} + , type_b_global (""), type_b_file ("") + , max_threads(0) {} private: // Disallow use of compiler generated equality operator. @@ -471,6 +474,7 @@ struct adjust_settings : private boost::equality_comparable { UINT16 scale_normals_to_unity; // Scale normals to unity prior to inversion bool purge_stage_files; // Purge memory mapped files from disk upon adjustment completion. UINT16 recreate_stage_files; // Recreate memory mapped files. + std::string stage_path; // Directory for memory mapped stage files (default: output_folder). float iteration_threshold; // Convergence limit double free_std_dev; // SD for free stations double fixed_std_dev; // SD for fixed stations @@ -483,6 +487,9 @@ struct adjust_settings : private boost::equality_comparable { std::string command_line_arguments; std::string type_b_global; // Comma delimited string containing Type b uncertainties to be applied to all uncertainties computed from an adjustment std::string type_b_file; // File path to Type b uncertainties to be applied to specific site uncertainties computed from an adjustment + + // Threading + int max_threads; // Maximum BLAS threads (0 = auto) }; // datum and geoid settings diff --git a/dynadjust/include/config/dnatypes-structs.hpp b/dynadjust/include/config/dnatypes-structs.hpp index 1e91dfe1c..4ddc68ee2 100644 --- a/dynadjust/include/config/dnatypes-structs.hpp +++ b/dynadjust/include/config/dnatypes-structs.hpp @@ -283,6 +283,7 @@ typedef struct stn_t { memset(epsgCode, '\0', sizeof(epsgCode)); snprintf(epsgCode, sizeof(epsgCode), "7843"); memset(epoch, '\0', sizeof(epoch)); + memset(observation_epoch, '\0', sizeof(observation_epoch)); memset(plate, '\0', sizeof(plate)); } @@ -311,9 +312,13 @@ typedef struct stn_t { UINT32 clusterID; // cluster ID (which cluster this station belongs to) UINT16 unusedStation; // is this station unused? char epsgCode[STN_EPSG_WIDTH]; // epsg ID, i.e. NNNNN (where NNNNN is in the range 0-32767) - char epoch[STN_EPOCH_WIDTH]; // date, i.e. "DD.MM.YYYY" (10 chars) - // if datum is dynamic, Epoch is YYYY MM DD - // if datum is static, Epoch is ignored + char epoch[STN_EPOCH_WIDTH]; // Epoch of Reference Frame, i.e. "DD.MM.YYYY" (10 chars) + // Mutable via dnareftran; reflects the reference frame's epoch. + // If datum is static, Epoch is ignored. + char observation_epoch[STN_EPOCH_WIDTH]; // Epoch of Observation, i.e. "DD.MM.YYYY" (10 chars) + // Immutable under reftran. Timestamp at which the station + // coordinates were observed/measured. Used for discontinuity + // station-instance allocation. Empty if not supplied. char plate[STN_PLATE_WIDTH]; // Tectonic plate identifier. Typically two characters. } station_t; @@ -330,7 +335,8 @@ typedef v_stn_string::iterator it_stn_string; typedef struct input_file_meta { char filename[FILE_NAME_WIDTH+1]; // Input file path char epsgCode[STN_EPSG_WIDTH+1]; // Input file epsg ID, i.e. NNNNN (where NNNNN is in the range 0-32767). "Mixed" if stations are on different reference frames - char epoch[STN_EPOCH_WIDTH+1]; // Input file epoch + char epoch[STN_EPOCH_WIDTH+1]; // Input file reference frame epoch (mutable) + char observation_epoch[STN_EPOCH_WIDTH+1]; // Input file observation epoch (immutable) UINT16 filetype; // Input file type (geodesyml, dynaml, dna, csv, sinex) UINT16 datatype; // Input data type (station, measurement, both) } input_file_meta_t; @@ -371,6 +377,7 @@ typedef struct binary_file_meta { memcpy(modifiedBy, rhs.modifiedBy, sizeof(modifiedBy)); memcpy(epsgCode, rhs.epsgCode, sizeof(epsgCode)); memcpy(epoch, rhs.epoch, sizeof(epoch)); + memcpy(observation_epoch, rhs.observation_epoch, sizeof(observation_epoch)); rhs.inputFileMeta = nullptr; rhs.sourceFileMeta = nullptr; } @@ -388,6 +395,7 @@ typedef struct binary_file_meta { memcpy(modifiedBy, rhs.modifiedBy, sizeof(modifiedBy)); memcpy(epsgCode, rhs.epsgCode, sizeof(epsgCode)); memcpy(epoch, rhs.epoch, sizeof(epoch)); + memcpy(observation_epoch, rhs.observation_epoch, sizeof(observation_epoch)); inputFileCount = rhs.inputFileCount; inputFileMeta = rhs.inputFileMeta; sourceFileCount = rhs.sourceFileCount; @@ -401,7 +409,8 @@ typedef struct binary_file_meta { bool reduced; // indicates whether the data is reduced(true) or raw(false) char modifiedBy[MOD_NAME_WIDTH+1]; // the program that modified this file char epsgCode[STN_EPSG_WIDTH+1]; // epsg ID, i.e. NNNNN (where NNNNN is in the range 0-32767). "Mixed" if stations are on different reference frames - char epoch[STN_EPOCH_WIDTH+1]; // date, i.e. "DD.MM.YYYY" (10 chars) + char epoch[STN_EPOCH_WIDTH+1]; // Epoch of Reference Frame (mutable) + char observation_epoch[STN_EPOCH_WIDTH+1]; // Epoch of Observation (immutable; preserved across reftran) bool reftran; // the data has been transformed to another frame and/or epoch bool geoid; // geoid separation values have been obtained std::uint64_t inputFileCount; // Number of source file metadata elements diff --git a/dynadjust/include/config/dnaversion.hpp b/dynadjust/include/config/dnaversion.hpp index 15d54874b..fa54bd226 100644 --- a/dynadjust/include/config/dnaversion.hpp +++ b/dynadjust/include/config/dnaversion.hpp @@ -204,8 +204,8 @@ const char* const __plot_dll_name__ = "libdnaplot.so"; // 2.2.0.a1 02020001 Alpha release // 2.2.0.b2 02020002 Beta release // 2.2.0.rc2 02020003 Release candidate 1 (unless bug notification is received, this is taken to be the final release) -#define __BINARY_VERSION__ "1.3.0" -#define __SHORT_VERSION__ "10300" // used to record DynAdjust version in binary file header +#define __BINARY_VERSION__ "1.4.1" +#define __SHORT_VERSION__ "10401" // used to record DynAdjust version in binary file header // define executable name #define __GLOBAL_BINARY_NAME__ __dynadjust_app_name__ @@ -310,7 +310,7 @@ const char* const __plot_dll_name__ = "libdnaplot.so"; #if defined(__clang__) // Clang compiler #define __COMPILER__ "Clang" - #define __COMPILER_VERSION__ __VERSION__ + #define __COMPILER_VERSION__ __clang_version__ #elif defined(__GNUC__) || defined(__GNUG__) // GNU GCC #define __COMPILER__ "GNU GCC" #define __COMPILER_VERSION__ __VERSION__ diff --git a/dynadjust/include/functions/dnaiostreamfuncs.hpp b/dynadjust/include/functions/dnaiostreamfuncs.hpp index 75a011ed4..71d0b38ea 100644 --- a/dynadjust/include/functions/dnaiostreamfuncs.hpp +++ b/dynadjust/include/functions/dnaiostreamfuncs.hpp @@ -132,7 +132,7 @@ void print_file_header( stream << std::setw(PRINT_VAR_PAD) << std::left << "File created:"; std::ostringstream datetime_ss; boost::posix_time::time_facet* p_time_output = new boost::posix_time::time_facet; - std::locale special_locale (std::locale(""), p_time_output); + std::locale special_locale (std::locale::classic(), p_time_output); // special_locale takes ownership of the p_time_output facet datetime_ss.imbue (special_locale); (*p_time_output).format("%A, %d %B %Y, %X"); diff --git a/dynadjust/include/functions/dnatemplatedatetimefuncs.hpp b/dynadjust/include/functions/dnatemplatedatetimefuncs.hpp index 789507910..10c9a97e6 100644 --- a/dynadjust/include/functions/dnatemplatedatetimefuncs.hpp +++ b/dynadjust/include/functions/dnatemplatedatetimefuncs.hpp @@ -37,6 +37,7 @@ #include #include +#include const UINT32 TIME_IMMEMORIAL = 1900; @@ -548,7 +549,7 @@ T formattedDateTimeString() { std::stringstream datetime_ss, stream; boost::posix_time::time_facet* p_time_output = new boost::posix_time::time_facet; - std::locale special_locale (std::locale(""), p_time_output); + std::locale special_locale (std::locale::classic(), p_time_output); // special_locale takes ownership of the p_time_output facet datetime_ss.imbue (special_locale); @@ -562,39 +563,8 @@ T formattedDateTimeString() template S formatedElapsedTime(boost::posix_time::milliseconds* elapsed_time, S app_message) { - std::ostringstream ss_time; - boost::posix_time::ptime pt(boost::posix_time::ptime(boost::gregorian::day_clock::local_day(), *elapsed_time)); - - if (*elapsed_time < boost::posix_time::seconds(3)) - { - boost::posix_time::time_facet* facet(new boost::posix_time::time_facet("%s")); - ss_time.imbue(std::locale(ss_time.getloc(), facet)); - ss_time.str(""); - ss_time << pt << "s"; - } - else if (*elapsed_time < boost::posix_time::seconds(61)) - { - boost::posix_time::time_facet* facet(new boost::posix_time::time_facet("%S")); - ss_time.imbue(std::locale(ss_time.getloc(), facet)); - ss_time.str(""); - ss_time << pt << "s"; - } - else - ss_time << boost::posix_time::seconds(static_cast(elapsed_time->total_seconds())); - - size_t pos = std::string::npos; - std::string time_message = ss_time.str(); - while ((pos = time_message.find("0s")) != std::string::npos) - time_message = time_message.substr(0, pos) + "s"; - - time_message = app_message + time_message + "."; - - if ((pos = time_message.find(" 00.")) != std::string::npos) - time_message = time_message.replace(pos, 4, " 0."); - if ((pos = time_message.find(" 0.s")) != std::string::npos) - time_message = time_message.replace(pos, 4, " 0s"); - - return time_message; + double seconds = static_cast(elapsed_time->total_milliseconds()) / 1000.0; + return app_message + dynadjust::FormatElapsedTime(seconds) + "."; } diff --git a/dynadjust/include/functions/dnatemplatematrixfuncs.hpp b/dynadjust/include/functions/dnatemplatematrixfuncs.hpp index 05e0df853..092c7d896 100644 --- a/dynadjust/include/functions/dnatemplatematrixfuncs.hpp +++ b/dynadjust/include/functions/dnatemplatematrixfuncs.hpp @@ -56,7 +56,6 @@ void GetDirectionsVarianceMatrix(msr_t_Iterator begin, matrix_2d* vmat) UINT32 a, angle_count(bmsRecord->vectorCount2 - 1); // number of directions excluding the RO UINT32 skip(0), ignored(bmsRecord->vectorCount1 - bmsRecord->vectorCount2); - vmat->zero(); vmat->redim(angle_count, angle_count); bmsRecord++; @@ -90,8 +89,7 @@ void GetGPSVarianceMatrix(const msr_t_Iterator begin, matrix_2d* vmat) { msr_t_Iterator bmsRecord(begin); UINT32 variance_dim(bmsRecord->vectorCount1 * 3), covariance_dim, cov; - vmat->zero(); - vmat->redim(variance_dim, variance_dim); + vmat->redim(variance_dim, variance_dim); for (UINT32 var(0), cov_elem; var -#include -#include #include +#include +#include namespace dynadjust { // High-precision timer class to replace boost::timer::cpu_timer class cpu_timer { -public: + public: struct cpu_times { std::chrono::nanoseconds wall; std::chrono::nanoseconds user; @@ -39,39 +39,56 @@ class cpu_timer { }; cpu_timer() { start(); } - - void start() { - start_time_ = std::chrono::high_resolution_clock::now(); - } - - void resume() { - start(); - } - + + void start() { start_time_ = std::chrono::high_resolution_clock::now(); } + + void resume() { start(); } + void stop() { // For compatibility, but no-op since we calculate elapsed on demand } - + cpu_times elapsed() const { auto end_time = std::chrono::high_resolution_clock::now(); auto wall_duration = std::chrono::duration_cast(end_time - start_time_); - return {wall_duration, wall_duration, wall_duration}; // For simplicity, user and system = wall + return {wall_duration, wall_duration, wall_duration}; // For simplicity, user and system = wall } - + std::string format(int places = 6) const { auto times = elapsed(); double wall_seconds = times.wall.count() / 1e9; - + std::ostringstream oss; oss << std::fixed << std::setprecision(places); oss << wall_seconds << "s wall"; return oss.str(); } -private: + private: std::chrono::high_resolution_clock::time_point start_time_; }; +// Format an elapsed duration (in seconds) for display. +// < 1 second -> "X.XXXms" +// 1..60 second -> "X.XXXs" +// >= 60 second -> "hh:mm:ss" +inline std::string FormatElapsedTime(double seconds) { + std::ostringstream oss; + if (seconds >= 60.0) { + long total_seconds = static_cast(seconds); + long hours = total_seconds / 3600; + long minutes = (total_seconds % 3600) / 60; + long secs = total_seconds % 60; + oss << std::setfill('0') << std::setw(2) << hours << ':' << std::setw(2) << minutes << ':' << std::setw(2) + << secs; + } else if (seconds >= 1.0) { + oss << std::fixed << std::setprecision(3) << seconds << 's'; + } else { + oss << std::fixed << std::setprecision(3) << (seconds * 1000.0) << "ms"; + } + return oss.str(); +} + } // namespace dynadjust -#endif // DNATIMER_H_ \ No newline at end of file +#endif // DNATIMER_H_ diff --git a/dynadjust/include/io/DynaML-schema.cxx b/dynadjust/include/io/DynaML-schema.cxx deleted file mode 100644 index 8f1fa74c7..000000000 --- a/dynadjust/include/io/DynaML-schema.cxx +++ /dev/null @@ -1,969 +0,0 @@ -// Automatically generated. Do not edit. -// - -/// \cond -#include -#include -/// \endcond - -#if XERCES_GRAMMAR_SERIALIZATION_LEVEL != 7 -# error incompatible Xerces-C++ version detected -#endif - -extern const XMLByte DynaML_schema[11430UL] = -{ - 0x07, 0x00, 0x06, 0x86, 0x00, 0x06, 0x1e, 0x00, 0x02, 0x68, 0x00, 0xff, - 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, - 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x78, 0x6d, 0x6c, 0x2f, 0x55, 0x6b, 0x6e, - 0x6f, 0x77, 0x6e, 0x4e, 0x53, 0x24, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3a, - 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, - 0x2f, 0x58, 0x4d, 0x4c, 0x2f, 0x31, 0x39, 0x39, 0x38, 0x2f, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x1d, 0x00, 0x68, 0x74, 0x74, - 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, - 0x72, 0x67, 0x2f, 0x32, 0x30, 0x30, 0x30, 0x2f, 0x78, 0x6d, 0x6c, 0x6e, - 0x73, 0x2f, 0x00, 0x20, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, - 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x32, - 0x30, 0x30, 0x31, 0x2f, 0x58, 0x4d, 0x4c, 0x53, 0x00, 0xff, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x0d, 0x00, 0x2c, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, - 0x6e, 0x6f, 0x6e, 0x43, 0x30, 0x09, 0x00, 0x2c, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x30, 0x00, 0x30, 0x00, 0x43, 0x3a, 0x5c, 0x44, 0x61, - 0x74, 0x61, 0x5c, 0x76, 0x73, 0x31, 0x30, 0x5c, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x73, 0x5c, 0x64, 0x79, 0x6e, 0x61, 0x6e, 0x65, 0x74, - 0x5f, 0x33, 0x5f, 0x30, 0x33, 0x5f, 0x30, 0x30, 0x2f, 0x44, 0x79, 0x6e, - 0x61, 0x4d, 0x4c, 0x2e, 0x78, 0x73, 0x64, 0x01, 0x00, 0x58, 0x00, 0x06, - 0x00, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x02, 0x00, 0x78, 0x73, 0x01, - 0x00, 0x59, 0x00, 0x01, 0x00, 0x5a, 0x00, 0x07, 0x00, 0x53, 0x69, 0x67, - 0x6d, 0x61, 0x58, 0x58, 0x00, 0x07, 0x00, 0x02, 0x53, 0x00, 0xff, 0x69, - 0x67, 0x6d, 0x61, 0x58, 0x59, 0x00, 0x07, 0x00, 0x53, 0x69, 0x67, 0x6d, - 0x61, 0x58, 0x5a, 0x00, 0x07, 0x00, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x59, - 0x59, 0x00, 0x07, 0x00, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x59, 0x5a, 0x00, - 0x07, 0x00, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x5a, 0x5a, 0x00, 0x0f, 0x00, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x6f, 0x76, 0x61, 0x72, 0x69, 0x61, - 0x6e, 0x63, 0x65, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, - 0x43, 0x31, 0x09, 0x00, 0x2c, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, - 0x31, 0x00, 0x03, 0x00, 0x6d, 0x31, 0x31, 0x00, 0x03, 0x00, 0x6d, 0x31, - 0x32, 0x00, 0x03, 0x00, 0x6d, 0x31, 0x33, 0x00, 0x03, 0x00, 0x6d, 0x32, - 0x31, 0x00, 0x03, 0x00, 0x6d, 0x32, 0x32, 0x00, 0x03, 0x00, 0x6d, 0x32, - 0x33, 0x00, 0x03, 0x00, 0x6d, 0x33, 0x31, 0x00, 0x02, 0x03, 0x00, 0x02, - 0x6d, 0x00, 0xff, 0x33, 0x32, 0x00, 0x03, 0x00, 0x6d, 0x33, 0x33, 0x00, - 0x0c, 0x00, 0x2c, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, - 0x74, 0x73, 0x07, 0x00, 0x2c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x00, - 0x0c, 0x00, 0x2c, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x0b, 0x00, 0x2c, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, - 0x43, 0x32, 0x09, 0x00, 0x2c, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, - 0x32, 0x00, 0x06, 0x00, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x06, 0x00, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x05, 0x00, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x00, 0x06, 0x00, 0x53, 0x74, 0x64, 0x44, 0x65, 0x76, 0x0f, 0x00, - 0x2c, 0x44, 0x6e, 0x61, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x00, - 0xff, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x33, 0x09, 0x00, 0x2c, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, - 0x6e, 0x43, 0x33, 0x00, 0x04, 0x00, 0x54, 0x79, 0x70, 0x65, 0x0e, 0x00, - 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x46, 0x72, 0x61, - 0x6d, 0x65, 0x05, 0x00, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x00, 0x05, 0x00, - 0x46, 0x69, 0x72, 0x73, 0x74, 0x00, 0x06, 0x00, 0x53, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x05, 0x00, 0x54, 0x68, 0x69, 0x72, 0x64, 0x00, 0x0a, 0x00, - 0x49, 0x6e, 0x73, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x0a, 0x00, - 0x54, 0x61, 0x72, 0x67, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x05, 0x00, - 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x00, 0x0a, 0x00, 0x44, 0x69, 0x72, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x06, 0x00, 0x56, 0x00, 0xff, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x0b, 0x00, 0x47, 0x50, 0x53, 0x42, 0x61, 0x73, - 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x34, 0x09, 0x00, 0x2c, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, - 0x6e, 0x43, 0x34, 0x00, 0x0d, 0x00, 0x47, 0x50, 0x53, 0x43, 0x6f, 0x76, - 0x61, 0x72, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x00, 0x08, 0x00, 0x5f, 0x5f, - 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x35, 0x09, 0x00, 0x2c, 0x5f, 0x5f, 0x41, - 0x6e, 0x6f, 0x6e, 0x43, 0x35, 0x00, 0x06, 0x00, 0x48, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x06, 0x00, 0x4c, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x06, 0x00, - 0x50, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x0c, 0x00, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x06, 0x00, 0x43, 0x6f, - 0x6f, 0x72, 0x64, 0x73, 0x06, 0x00, 0x53, 0x00, 0xff, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x0b, 0x00, 0x2c, 0x44, 0x6e, 0x61, 0x53, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, - 0x43, 0x36, 0x09, 0x00, 0x2c, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, - 0x36, 0x00, 0x04, 0x00, 0x4e, 0x61, 0x6d, 0x65, 0x0b, 0x00, 0x43, 0x6f, - 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x00, 0x0c, 0x00, - 0x53, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6f, 0x72, 0x64, - 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x37, 0x09, 0x00, - 0x2c, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x37, 0x00, 0x05, 0x00, - 0x58, 0x41, 0x78, 0x69, 0x73, 0x00, 0x05, 0x00, 0x59, 0x41, 0x78, 0x69, - 0x73, 0x00, 0x06, 0x00, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x0e, 0x00, - 0x48, 0x65, 0x6d, 0x69, 0x73, 0x00, 0xff, 0x70, 0x68, 0x65, 0x72, 0x65, - 0x5a, 0x6f, 0x6e, 0x65, 0x0b, 0x00, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x0d, 0x00, 0x2c, 0x44, 0x6e, 0x61, - 0x58, 0x6d, 0x6c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x00, 0x08, 0x00, - 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x38, 0x09, 0x00, 0x2c, 0x5f, - 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x38, 0x00, 0x0a, 0x00, 0x44, 0x6e, - 0x61, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0e, 0x00, 0x44, 0x6e, - 0x61, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x53, 0x39, 0x09, 0x00, - 0x2c, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x53, 0x39, 0x00, 0x06, 0x00, - 0x2c, 0x46, 0x69, 0x72, 0x73, 0x74, 0x0c, 0x00, 0x2c, 0x47, 0x50, 0x53, - 0x42, 0x61, 0x73, 0x00, 0xff, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x0e, 0x00, - 0x2c, 0x47, 0x50, 0x53, 0x43, 0x6f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, - 0x63, 0x65, 0x07, 0x00, 0x2c, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, - 0x0f, 0x00, 0x2c, 0x48, 0x65, 0x6d, 0x69, 0x73, 0x70, 0x68, 0x65, 0x72, - 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x00, 0x07, 0x00, 0x2c, 0x48, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x00, 0x07, 0x00, 0x2c, 0x49, 0x67, 0x6e, 0x6f, 0x72, - 0x65, 0x00, 0x0b, 0x00, 0x2c, 0x49, 0x6e, 0x73, 0x74, 0x48, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x00, 0x07, 0x00, 0x2c, 0x4c, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x00, 0x05, 0x00, 0x2c, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x10, 0x00, - 0x2c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x6f, 0x76, 0x61, 0x72, 0x69, - 0x61, 0x6e, 0x63, 0x65, 0x07, 0x00, 0x2c, 0x50, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x00, 0x02, 0x07, 0x00, 0x02, 0x2c, 0x00, 0xff, 0x53, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x00, 0x08, 0x00, 0x2c, 0x53, 0x69, 0x67, 0x6d, 0x61, - 0x58, 0x58, 0x08, 0x00, 0x2c, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x58, 0x59, - 0x08, 0x00, 0x2c, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x58, 0x5a, 0x08, 0x00, - 0x2c, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x59, 0x59, 0x08, 0x00, 0x2c, 0x53, - 0x69, 0x67, 0x6d, 0x61, 0x59, 0x5a, 0x08, 0x00, 0x2c, 0x53, 0x69, 0x67, - 0x6d, 0x61, 0x5a, 0x5a, 0x0d, 0x00, 0x2c, 0x53, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x00, 0x07, 0x00, 0x2c, 0x53, - 0x74, 0x64, 0x44, 0x65, 0x76, 0x00, 0x0b, 0x00, 0x2c, 0x54, 0x61, 0x72, - 0x67, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x07, 0x00, 0x2c, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x06, 0x00, 0x2c, 0x54, 0x68, 0x69, - 0x72, 0x64, 0x06, 0x00, 0x2c, 0x00, 0xff, 0x54, 0x6f, 0x74, 0x61, 0x6c, - 0x05, 0x00, 0x2c, 0x54, 0x79, 0x70, 0x65, 0x00, 0x06, 0x00, 0x2c, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x07, 0x00, 0x2c, 0x56, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x00, 0x02, 0x00, 0x2c, 0x58, 0x06, 0x00, 0x2c, 0x58, 0x41, 0x78, - 0x69, 0x73, 0x02, 0x00, 0x2c, 0x59, 0x06, 0x00, 0x2c, 0x59, 0x41, 0x78, - 0x69, 0x73, 0x02, 0x00, 0x2c, 0x5a, 0x04, 0x00, 0x2c, 0x6d, 0x31, 0x31, - 0x04, 0x00, 0x2c, 0x6d, 0x31, 0x32, 0x04, 0x00, 0x2c, 0x6d, 0x31, 0x33, - 0x04, 0x00, 0x2c, 0x6d, 0x32, 0x31, 0x04, 0x00, 0x2c, 0x6d, 0x32, 0x32, - 0x04, 0x00, 0x2c, 0x6d, 0x32, 0x33, 0x04, 0x00, 0x2c, 0x6d, 0x33, 0x31, - 0x04, 0x00, 0x2c, 0x6d, 0x33, 0x32, 0x04, 0x00, 0x2c, 0x6d, 0x33, 0x33, - 0x07, 0x00, 0x2c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x00, 0x0f, 0x00, - 0x2c, 0x52, 0x65, 0x00, 0x95, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, - 0x46, 0x72, 0x61, 0x6d, 0x65, 0x00, 0x06, 0x00, 0x2c, 0x45, 0x70, 0x6f, - 0x63, 0x68, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x06, 0x01, 0x00, 0x06, - 0x01, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0x0d, 0x00, 0x02, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x47, 0x72, 0x61, 0x6d, 0x6d, 0x61, 0x72, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x06, 0x01, 0x00, 0x06, 0xfe, - 0xff, 0xff, 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x17, 0x00, 0x02, - 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x61, 0x74, 0x79, - 0x70, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x01, - 0x00, 0x0b, 0x10, 0x00, 0x0e, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, - 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, 0xff, 0xff, - 0x1d, 0x00, 0x0e, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0x08, - 0x00, 0x02, 0x5f, 0x00, 0x87, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x53, 0x39, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x0f, 0xfe, 0xff, 0xff, 0xff, - 0x02, 0x00, 0x06, 0x10, 0x00, 0x02, 0x4d, 0x00, 0x9d, 0x65, 0x61, 0x73, - 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x46, 0x69, 0x6c, 0x65, - 0x0c, 0x00, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x46, 0x69, - 0x6c, 0x65, 0xfe, 0xff, 0xff, 0xff, 0x6d, 0x00, 0x06, 0x35, 0x00, 0x06, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, 0x00, 0x02, 0x53, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x44, 0x65, 0x63, 0x6c, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, - 0x02, 0x51, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x02, 0x08, 0x00, 0x06, 0x0e, - 0x00, 0x02, 0x06, 0x00, 0x02, 0x54, 0x00, 0x8a, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x01, 0x00, 0x01, 0x00, 0x1a, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, - 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, - 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x14, 0x00, 0x02, 0x0c, - 0x00, 0x02, 0x43, 0x00, 0x90, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x09, 0x04, - 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x02, 0x43, 0x6f, - 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x01, 0x00, 0x81, 0x01, 0x00, 0x10, 0x04, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, 0x00, 0x95, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x30, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, - 0x6e, 0x43, 0x30, 0x00, 0x00, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, - 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x02, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x4e, 0x6f, 0x64, - 0x65, 0x00, 0x04, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x09, 0x00, 0x02, 0x01, 0x00, 0x02, 0x58, - 0x00, 0x02, 0x01, 0x00, 0x06, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, - 0x80, 0x08, 0x00, 0x06, 0x09, 0x00, 0x02, 0x01, 0x00, 0x02, 0x58, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x02, 0x00, 0x0a, 0x05, 0x00, - 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, - 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, - 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x09, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x59, 0x00, 0x02, 0x01, 0x00, 0x06, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x09, 0x00, 0x02, 0x01, 0x00, 0x02, 0x59, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x03, 0x00, 0x0a, 0x05, - 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, - 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, - 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, - 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x09, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x5a, 0x00, 0x02, 0x01, 0x00, 0x06, 0x0a, 0x00, 0x01, 0x80, - 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x09, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x5a, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, 0x00, - 0x0a, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, - 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, - 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, - 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, - 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, 0x67, 0x6d, 0x61, 0x58, - 0x58, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, - 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, - 0x8c, 0x69, 0x67, 0x6d, 0x61, 0x58, 0x58, 0x00, 0x01, 0x00, 0x01, 0x00, - 0x05, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, 0x67, 0x6d, - 0x61, 0x58, 0x59, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, - 0x53, 0x00, 0x8c, 0x69, 0x67, 0x6d, 0x61, 0x58, 0x59, 0x00, 0x01, 0x00, - 0x01, 0x00, 0x06, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, - 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, - 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, - 0x67, 0x6d, 0x61, 0x58, 0x5a, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, - 0x00, 0x02, 0x53, 0x00, 0x8c, 0x69, 0x67, 0x6d, 0x61, 0x58, 0x5a, 0x00, - 0x01, 0x00, 0x01, 0x00, 0x07, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, - 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, - 0x88, 0x69, 0x67, 0x6d, 0x61, 0x59, 0x59, 0x00, 0x01, 0x00, 0x05, 0x0a, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, - 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x8c, 0x69, 0x67, 0x6d, 0x61, 0x59, - 0x59, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x09, 0x05, 0x00, 0x02, - 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, - 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, - 0x53, 0x00, 0x88, 0x69, 0x67, 0x6d, 0x61, 0x59, 0x5a, 0x00, 0x01, 0x00, - 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x8c, 0x69, 0x67, 0x6d, - 0x61, 0x59, 0x5a, 0x00, 0x01, 0x00, 0x01, 0x00, 0x09, 0x00, 0x09, 0x05, - 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, - 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, - 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, - 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, - 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, 0x67, 0x6d, 0x61, 0x5a, 0x5a, 0x00, - 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x8c, 0x69, - 0x67, 0x6d, 0x61, 0x5a, 0x5a, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00, - 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, - 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, - 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, - 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x17, 0x00, - 0x02, 0x0f, 0x00, 0x02, 0x50, 0x00, 0x90, 0x6f, 0x69, 0x6e, 0x74, 0x43, - 0x6f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x00, 0x01, 0x00, - 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x17, 0x00, 0x02, 0x0f, 0x00, 0x02, 0x50, 0x00, 0x94, 0x6f, 0x69, 0x6e, - 0x74, 0x43, 0x6f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x00, - 0x01, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x09, 0x04, 0x00, 0x02, 0x01, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x10, - 0x00, 0x01, 0x80, 0x01, 0x00, 0x81, 0x01, 0x00, 0x0f, 0x01, 0x00, 0x02, - 0x04, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, 0x00, - 0x95, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x31, 0x00, 0x08, 0x00, - 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x31, 0x00, 0x00, 0xfd, 0xff, - 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0x00, 0x03, 0x12, 0x00, 0x01, 0x80, - 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, - 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, - 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, - 0x02, 0x6d, 0x00, 0x84, 0x31, 0x31, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, - 0x03, 0x00, 0x02, 0x6d, 0x00, 0x88, 0x31, 0x31, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x0c, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, - 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x31, 0x32, 0x00, - 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x88, 0x31, - 0x32, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x09, 0x05, 0x00, 0x02, - 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, - 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, - 0x6d, 0x00, 0x84, 0x31, 0x33, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, - 0x00, 0x02, 0x6d, 0x00, 0x88, 0x31, 0x33, 0x00, 0x01, 0x00, 0x01, 0x00, - 0x0e, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x32, 0x31, 0x00, - 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x88, 0x32, - 0x31, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x09, 0x05, 0x00, 0x02, - 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, - 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, - 0x6d, 0x00, 0x84, 0x32, 0x32, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, - 0x00, 0x02, 0x6d, 0x00, 0x88, 0x32, 0x32, 0x00, 0x01, 0x00, 0x01, 0x00, - 0x10, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x32, 0x33, 0x00, - 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x88, 0x32, - 0x33, 0x00, 0x01, 0x00, 0x01, 0x00, 0x11, 0x00, 0x09, 0x05, 0x00, 0x02, - 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, - 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, - 0x6d, 0x00, 0x84, 0x33, 0x31, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, - 0x00, 0x02, 0x6d, 0x00, 0x88, 0x33, 0x31, 0x00, 0x01, 0x00, 0x01, 0x00, - 0x12, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x33, 0x32, 0x00, - 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x88, 0x33, - 0x32, 0x00, 0x01, 0x00, 0x01, 0x00, 0x13, 0x00, 0x09, 0x05, 0x00, 0x02, - 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, - 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, - 0x6d, 0x00, 0x84, 0x33, 0x33, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, - 0x00, 0x02, 0x6d, 0x00, 0x88, 0x33, 0x33, 0x00, 0x01, 0x00, 0x01, 0x00, - 0x14, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x15, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x06, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x02, 0x53, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x41, 0x74, 0x74, 0x44, 0x65, 0x66, 0x4c, 0x69, 0x73, 0x74, - 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x16, 0xfe, 0xff, 0xff, 0xff, 0x09, - 0x00, 0x06, 0x4f, 0x00, 0x02, 0x53, 0x00, 0x02, 0x57, 0x00, 0x02, 0x5b, - 0x00, 0x02, 0x5f, 0x00, 0x02, 0x63, 0x00, 0x02, 0x67, 0x00, 0x02, 0x6b, - 0x00, 0x02, 0x6f, 0x00, 0x02, 0x73, 0x00, 0x12, 0xfd, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0x01, 0x01, 0x00, 0x05, 0xff, 0xff, 0xff, 0xff, 0x15, 0x00, - 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x06, 0x71, - 0x00, 0x01, 0x80, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x16, 0xfe, 0xff, - 0xff, 0xff, 0x0a, 0x00, 0x06, 0x1e, 0x00, 0x02, 0x22, 0x00, 0x02, 0x26, - 0x00, 0x02, 0x2a, 0x00, 0x02, 0x2e, 0x00, 0x02, 0x32, 0x00, 0x02, 0x36, - 0x00, 0x02, 0x3a, 0x00, 0x02, 0x3e, 0x00, 0x02, 0x42, 0x00, 0x02, 0x76, - 0x00, 0x12, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x15, 0x00, 0x02, - 0x0d, 0x00, 0x02, 0x47, 0x00, 0x92, 0x50, 0x53, 0x43, 0x6f, 0x76, 0x61, - 0x72, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x00, 0x01, 0x00, 0x01, 0x00, 0x29, - 0x00, 0x09, 0x04, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x80, 0x01, 0x00, - 0x81, 0x01, 0x00, 0x0f, 0x05, 0x00, 0x02, 0x04, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, 0x00, 0x95, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x35, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, - 0x6e, 0x43, 0x35, 0x00, 0x00, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, - 0xff, 0x00, 0x03, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x31, - 0x31, 0x00, 0x01, 0x00, 0x05, 0x4f, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, - 0x00, 0x84, 0x31, 0x32, 0x00, 0x01, 0x00, 0x05, 0x53, 0x00, 0x0e, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, - 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x31, 0x33, 0x00, 0x01, 0x00, 0x05, - 0x57, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x32, 0x31, - 0x00, 0x01, 0x00, 0x05, 0x5b, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, - 0x00, 0x84, 0x32, 0x32, 0x00, 0x01, 0x00, 0x05, 0x5f, 0x00, 0x0e, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, - 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x32, 0x33, 0x00, 0x01, 0x00, 0x05, - 0x63, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x33, 0x31, - 0x00, 0x01, 0x00, 0x05, 0x67, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, 0x03, 0x00, 0x02, 0x6d, - 0x00, 0x84, 0x33, 0x32, 0x00, 0x01, 0x00, 0x05, 0x6b, 0x00, 0x0e, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0b, 0x00, 0x02, - 0x03, 0x00, 0x02, 0x6d, 0x00, 0x84, 0x33, 0x33, 0x00, 0x01, 0x00, 0x05, - 0x6f, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x15, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x06, 0x71, 0x00, 0x01, 0x80, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, - 0x16, 0xfe, 0xff, 0xff, 0xff, 0x09, 0x00, 0x06, 0x4f, 0x00, 0x02, 0x53, - 0x00, 0x02, 0x57, 0x00, 0x02, 0x5b, 0x00, 0x02, 0x5f, 0x00, 0x02, 0x63, - 0x00, 0x02, 0x67, 0x00, 0x02, 0x6b, 0x00, 0x02, 0x6f, 0x00, 0x02, 0x96, - 0x00, 0x12, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x12, 0x00, 0x02, - 0x0a, 0x00, 0x02, 0x44, 0x00, 0x8e, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x01, 0x00, 0x01, 0x00, 0x18, 0x00, 0x09, 0x04, 0x00, - 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, - 0xff, 0xff, 0x10, 0x00, 0x01, 0x80, 0x01, 0x00, 0x81, 0x01, 0x00, 0x0f, - 0x02, 0x00, 0x02, 0x04, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x09, 0x00, - 0x02, 0x2c, 0x00, 0x95, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x32, - 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x32, 0x00, - 0x00, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0x00, 0x03, 0x12, - 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, - 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x49, 0x00, 0x86, 0x67, 0x6e, - 0x6f, 0x72, 0x65, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x49, - 0x00, 0x8a, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x01, 0x00, 0x01, 0x00, 0x19, - 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, - 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, - 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, - 0x02, 0x06, 0x00, 0x02, 0x54, 0x00, 0x86, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x01, 0x00, 0x05, 0x0b, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, - 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x56, 0x00, - 0x86, 0x61, 0x6c, 0x75, 0x65, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, - 0x00, 0x02, 0x56, 0x00, 0x8a, 0x61, 0x6c, 0x75, 0x65, 0x00, 0x01, 0x00, - 0x01, 0x00, 0x1b, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, - 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, - 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x53, 0x00, 0x86, 0x74, - 0x64, 0x44, 0x65, 0x76, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, - 0x53, 0x00, 0x8a, 0x74, 0x64, 0x44, 0x65, 0x76, 0x01, 0x00, 0x01, 0x00, - 0x1c, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x15, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x06, 0x71, 0x00, 0x01, 0x80, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x16, - 0xfe, 0xff, 0xff, 0xff, 0x04, 0x00, 0x06, 0xa0, 0x00, 0x02, 0x0b, 0x00, - 0x02, 0xa6, 0x00, 0x02, 0xaa, 0x00, 0x02, 0xad, 0x00, 0x12, 0xfd, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x13, 0x00, 0x02, 0x0b, 0x00, 0x02, 0x44, - 0x00, 0x90, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x17, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, - 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, - 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0c, 0x00, 0x02, 0x04, - 0x00, 0x02, 0x4e, 0x00, 0x88, 0x61, 0x6d, 0x65, 0x01, 0x00, 0x01, 0x00, - 0x2f, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x54, 0x00, 0x8a, - 0x6f, 0x74, 0x61, 0x6c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x26, 0x00, 0x09, - 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, - 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, 0xff, - 0xff, 0x67, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x6b, 0x00, 0x02, 0xfe, - 0xff, 0xff, 0xff, 0xa0, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x6f, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, - 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x48, 0x00, - 0x8a, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x01, 0x00, 0x01, 0x00, 0x2a, 0x00, - 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, - 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, - 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, - 0xff, 0xff, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x43, 0x00, 0x8a, 0x6f, 0x6f, - 0x72, 0x64, 0x73, 0x01, 0x00, 0x01, 0x00, 0x16, 0x00, 0x09, 0x05, 0x00, - 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, - 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, - 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, 0xff, 0xff, 0x0a, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x16, 0x00, - 0x02, 0x0e, 0x00, 0x02, 0x44, 0x00, 0x92, 0x6e, 0x61, 0x4d, 0x65, 0x61, - 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x01, 0x00, 0x01, 0x00, - 0x1d, 0x00, 0x09, 0x04, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x80, 0x01, - 0x00, 0x81, 0x01, 0x00, 0x0f, 0x03, 0x00, 0x02, 0x04, 0x00, 0x02, 0xfe, - 0xff, 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, 0x00, 0x95, 0x5f, 0x5f, 0x41, - 0x6e, 0x6f, 0x6e, 0x43, 0x33, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x33, 0x00, 0x00, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, - 0xff, 0xff, 0x00, 0x03, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, - 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, - 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, - 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, - 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, - 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x0c, 0x00, 0x02, 0x04, 0x00, 0x02, 0x54, 0x00, 0x84, - 0x79, 0x70, 0x65, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0c, 0x00, 0x02, 0x04, 0x00, 0x02, 0x54, - 0x00, 0x88, 0x79, 0x70, 0x65, 0x01, 0x00, 0x01, 0x00, 0x1e, 0x00, 0x09, - 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, - 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, - 0x00, 0x02, 0x49, 0x00, 0x86, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x01, 0x00, - 0x05, 0xa0, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x05, 0x01, 0x00, 0x02, 0x04, - 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x16, - 0x00, 0x02, 0x0e, 0x00, 0x02, 0x52, 0x00, 0x8e, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x01, 0x00, 0x05, - 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x16, - 0x00, 0x02, 0x0e, 0x00, 0x02, 0x52, 0x00, 0x92, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x01, 0x00, 0x01, - 0x00, 0x1f, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, - 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x05, 0x01, 0x00, 0x02, 0x04, 0x00, - 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, - 0x02, 0x05, 0x00, 0x02, 0x45, 0x00, 0x86, 0x70, 0x6f, 0x63, 0x68, 0x00, - 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x45, 0x00, 0x8a, 0x70, - 0x6f, 0x63, 0x68, 0x00, 0x01, 0x00, 0x01, 0x00, 0x20, 0x00, 0x09, 0x05, - 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, - 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, - 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, - 0x00, 0x05, 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x46, - 0x00, 0x86, 0x69, 0x72, 0x73, 0x74, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, - 0x05, 0x00, 0x02, 0x46, 0x00, 0x8a, 0x69, 0x72, 0x73, 0x74, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x21, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, - 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, - 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x53, 0x00, 0x86, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, - 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, - 0x02, 0x53, 0x00, 0x8a, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x01, 0x00, 0x01, - 0x00, 0x22, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, - 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x54, 0x00, 0x86, 0x68, 0x69, - 0x72, 0x64, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x54, - 0x00, 0x8a, 0x68, 0x69, 0x72, 0x64, 0x00, 0x01, 0x00, 0x01, 0x00, 0x23, - 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, - 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, - 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, - 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, - 0x00, 0x02, 0x05, 0x00, 0x02, 0x56, 0x00, 0x86, 0x61, 0x6c, 0x75, 0x65, - 0x00, 0x01, 0x00, 0x05, 0xa6, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x53, - 0x00, 0x86, 0x74, 0x64, 0x44, 0x65, 0x76, 0x01, 0x00, 0x05, 0xaa, 0x00, - 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, - 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x12, - 0x00, 0x02, 0x0a, 0x00, 0x02, 0x49, 0x00, 0x8a, 0x6e, 0x73, 0x74, 0x48, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, - 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x12, 0x00, 0x02, 0x0a, 0x00, - 0x02, 0x49, 0x00, 0x8e, 0x6e, 0x73, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x01, 0x00, 0x01, 0x00, 0x24, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, - 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, - 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x12, 0x00, 0x02, 0x0a, 0x00, 0x02, 0x54, - 0x00, 0x8a, 0x61, 0x72, 0x67, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x01, - 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x12, 0x00, 0x02, 0x0a, 0x00, 0x02, 0x54, 0x00, 0x8e, 0x61, 0x72, - 0x67, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x01, 0x00, 0x01, 0x00, 0x25, - 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, - 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, - 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, - 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, - 0x00, 0x02, 0x05, 0x00, 0x02, 0x54, 0x00, 0x86, 0x6f, 0x74, 0x61, 0x6c, - 0x00, 0x01, 0x00, 0x05, 0xb3, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x12, 0x00, 0x02, 0x0a, 0x00, 0x02, 0x44, - 0x00, 0x8a, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x01, - 0x00, 0x05, 0x98, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x56, 0x00, 0x86, - 0x73, 0x63, 0x61, 0x6c, 0x65, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, - 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, - 0x02, 0x56, 0x00, 0x8a, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x01, 0x00, 0x01, - 0x00, 0x27, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, - 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x13, 0x00, 0x02, 0x0b, 0x00, 0x02, 0x47, 0x00, 0x8c, 0x50, 0x53, - 0x42, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x01, 0x00, 0x05, - 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x13, - 0x00, 0x02, 0x0b, 0x00, 0x02, 0x47, 0x00, 0x90, 0x50, 0x53, 0x42, 0x61, - 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x01, 0x00, 0x01, 0x00, 0x28, - 0x00, 0x09, 0x04, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x80, 0x01, 0x00, - 0x81, 0x01, 0x00, 0x0f, 0x04, 0x00, 0x02, 0x04, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, 0x00, 0x95, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x34, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, - 0x6e, 0x43, 0x34, 0x00, 0x00, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, - 0xff, 0x00, 0x03, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x09, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x58, 0x00, 0x02, 0x01, 0x00, 0x06, 0x1e, - 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x09, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x59, 0x00, 0x02, 0x01, 0x00, 0x06, 0x22, - 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, - 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x09, 0x00, 0x02, 0x01, 0x00, 0x02, 0x5a, 0x00, 0x02, 0x01, 0x00, 0x06, - 0x26, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, 0x67, - 0x6d, 0x61, 0x58, 0x58, 0x00, 0x01, 0x00, 0x05, 0x2a, 0x00, 0x0e, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, - 0x07, 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, 0x67, 0x6d, 0x61, 0x58, 0x59, - 0x00, 0x01, 0x00, 0x05, 0x2e, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, - 0x00, 0x88, 0x69, 0x67, 0x6d, 0x61, 0x58, 0x5a, 0x00, 0x01, 0x00, 0x05, - 0x32, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, 0x67, - 0x6d, 0x61, 0x59, 0x59, 0x00, 0x01, 0x00, 0x05, 0x36, 0x00, 0x0e, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, - 0x07, 0x00, 0x02, 0x53, 0x00, 0x88, 0x69, 0x67, 0x6d, 0x61, 0x59, 0x5a, - 0x00, 0x01, 0x00, 0x05, 0x3a, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0f, 0x00, 0x02, 0x07, 0x00, 0x02, 0x53, - 0x00, 0x88, 0x69, 0x67, 0x6d, 0x61, 0x5a, 0x5a, 0x00, 0x01, 0x00, 0x05, - 0x3e, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x15, 0x00, 0x02, 0x0d, 0x00, 0x02, 0x47, 0x00, 0x8e, 0x50, 0x53, - 0x43, 0x6f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x00, 0x01, - 0x00, 0x05, 0x78, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x05, 0xff, 0xff, 0xff, - 0xff, 0x15, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x06, 0x71, 0x00, 0x01, 0x80, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, - 0x16, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x06, 0x1e, 0x00, 0x02, 0x22, - 0x00, 0x02, 0x26, 0x00, 0x02, 0x2a, 0x00, 0x02, 0x2e, 0x00, 0x02, 0x32, - 0x00, 0x02, 0x36, 0x00, 0x02, 0x3a, 0x00, 0x02, 0x3e, 0x00, 0x02, 0x78, - 0x00, 0x02, 0x21, 0x01, 0x00, 0x11, 0xfd, 0xff, 0xff, 0xff, 0x00, 0x0b, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, 0x00, - 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, - 0x02, 0x06, 0x00, 0x02, 0x48, 0x00, 0x86, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x01, 0x00, 0x05, 0xb5, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x05, 0x01, 0x00, - 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x4c, 0x00, 0x86, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x4c, - 0x00, 0x8a, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x01, 0x00, 0x01, 0x00, 0x2b, - 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, - 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, - 0x0b, 0x01, 0x01, 0x00, 0x05, 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, - 0x00, 0x02, 0x50, 0x00, 0x86, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x01, 0x00, - 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x50, 0x00, 0x8a, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x01, 0x00, 0x01, 0x00, 0x2c, 0x00, 0x09, 0x05, 0x00, 0x02, - 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, - 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x05, - 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x14, 0x00, 0x02, 0x0c, 0x00, 0x02, 0x43, 0x00, 0x8c, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x01, - 0x00, 0x05, 0x0e, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x43, 0x00, 0x86, - 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x01, 0x00, 0x05, 0xb7, 0x00, 0x0e, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, 0x00, 0x02, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, 0x02, - 0x06, 0x00, 0x02, 0x53, 0x00, 0x86, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x01, - 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x0e, 0x00, 0x02, 0x06, 0x00, 0x02, 0x53, 0x00, 0x8a, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x01, 0x00, 0x01, 0x00, 0x2d, 0x00, 0x09, 0x05, 0x00, - 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, - 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, - 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x24, 0x00, 0x02, 0x01, 0x01, - 0x00, 0x01, 0x01, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0x00, 0x03, 0x71, - 0x00, 0x01, 0x80, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x16, 0xfe, 0xff, - 0xff, 0xff, 0x15, 0x00, 0x06, 0xd2, 0x00, 0x02, 0xa0, 0x00, 0x02, 0xd8, - 0x00, 0x02, 0xdc, 0x00, 0x02, 0xe0, 0x00, 0x02, 0xe4, 0x00, 0x02, 0xe8, - 0x00, 0x02, 0xa6, 0x00, 0x02, 0xaa, 0x00, 0x02, 0xf0, 0x00, 0x02, 0xf4, - 0x00, 0x02, 0xb3, 0x00, 0x02, 0x98, 0x00, 0x02, 0xfc, 0x00, 0x03, 0x01, - 0x00, 0x01, 0xb5, 0x00, 0x02, 0x27, 0x01, 0x00, 0x01, 0x2b, 0x01, 0x00, - 0x01, 0x0e, 0x00, 0x02, 0xb7, 0x00, 0x02, 0x33, 0x01, 0x00, 0x01, 0x36, - 0x01, 0x00, 0x11, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x0a, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, - 0x02, 0x06, 0x00, 0x02, 0x48, 0x00, 0x8a, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x01, 0x00, 0x01, 0x00, 0x33, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x02, 0xfe, - 0xff, 0xff, 0xff, 0x36, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x3a, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0xe8, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0xe4, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x2b, 0x01, 0x00, 0x01, 0xfe, - 0xff, 0xff, 0xff, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x59, 0x00, 0x8a, 0x41, - 0x78, 0x69, 0x73, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00, 0x09, 0x05, - 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, - 0xff, 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, - 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfe, 0xff, 0xff, 0xff, - 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x14, - 0x00, 0x02, 0x0c, 0x00, 0x02, 0x53, 0x00, 0x90, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x01, 0x00, 0x01, 0x00, 0x30, - 0x00, 0x09, 0x04, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x80, 0x01, 0x00, - 0x81, 0x01, 0x00, 0x0f, 0x07, 0x00, 0x02, 0x04, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, 0x00, 0x95, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x37, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, - 0x6e, 0x43, 0x37, 0x00, 0x00, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, - 0xff, 0x00, 0x03, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0c, 0x00, 0x02, 0x04, 0x00, 0x02, 0x4e, 0x00, 0x84, 0x61, - 0x6d, 0x65, 0x01, 0x00, 0x05, 0xb1, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x58, - 0x00, 0x86, 0x41, 0x78, 0x69, 0x73, 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, - 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, - 0x05, 0x00, 0x02, 0x58, 0x00, 0x8a, 0x41, 0x78, 0x69, 0x73, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x31, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, - 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, - 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, 0x59, 0x00, 0x86, - 0x41, 0x78, 0x69, 0x73, 0x00, 0x01, 0x00, 0x05, 0x3a, 0x01, 0x00, 0x0d, - 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, - 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, - 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0e, 0x00, - 0x02, 0x06, 0x00, 0x02, 0x48, 0x00, 0x86, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x01, 0x00, 0x05, 0x38, 0x01, 0x00, 0x0d, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x16, 0x00, 0x02, 0x0e, 0x00, 0x02, 0x48, - 0x00, 0x8e, 0x65, 0x6d, 0x69, 0x73, 0x70, 0x68, 0x65, 0x72, 0x65, 0x5a, - 0x6f, 0x6e, 0x65, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x16, 0x00, 0x02, 0x0e, 0x00, 0x02, 0x48, - 0x00, 0x92, 0x65, 0x6d, 0x69, 0x73, 0x70, 0x68, 0x65, 0x72, 0x65, 0x5a, - 0x6f, 0x6e, 0x65, 0x01, 0x00, 0x01, 0x00, 0x34, 0x00, 0x09, 0x05, 0x00, - 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, - 0xff, 0xff, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, - 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, - 0x05, 0x01, 0x00, 0x02, 0x15, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x02, 0x01, 0x00, 0x06, 0x71, 0x00, 0x01, 0x80, 0xfe, 0xff, 0xff, - 0xff, 0x1d, 0x00, 0x16, 0xfe, 0xff, 0xff, 0xff, 0x05, 0x00, 0x06, 0xb1, - 0x00, 0x02, 0x47, 0x01, 0x00, 0x01, 0x3a, 0x01, 0x00, 0x01, 0x38, 0x01, - 0x00, 0x01, 0x4f, 0x01, 0x00, 0x01, 0x52, 0x01, 0x00, 0x11, 0xfd, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xaa, 0x00, 0x02, 0xfe, 0xff, 0xff, - 0xff, 0x4f, 0x01, 0x00, 0x01, 0xfe, 0xff, 0xff, 0xff, 0x4f, 0x00, 0x02, - 0xfe, 0xff, 0xff, 0xff, 0x53, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x57, - 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x14, 0x00, 0x02, 0x0c, 0x00, 0x02, 0x44, - 0x00, 0x90, 0x6e, 0x61, 0x58, 0x6d, 0x6c, 0x46, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x01, 0x00, 0x01, 0x00, 0x35, 0x00, 0x09, 0x04, 0x00, 0x02, 0x01, - 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, - 0x10, 0x00, 0x01, 0x80, 0x01, 0x00, 0x81, 0x01, 0x00, 0x0f, 0x08, 0x00, - 0x02, 0x04, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, - 0x00, 0x95, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x38, 0x00, 0x08, - 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, 0x6e, 0x43, 0x38, 0x00, 0x00, 0xfd, - 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0x00, 0x03, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x12, 0x00, 0x02, 0x0a, 0x00, 0x02, 0x44, - 0x00, 0x8a, 0x6e, 0x61, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x01, - 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, - 0x06, 0x12, 0x00, 0x02, 0x0a, 0x00, 0x02, 0x44, 0x00, 0x8e, 0x6e, 0x61, - 0x53, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x01, 0x00, 0x01, 0x00, 0x2e, - 0x00, 0x09, 0x04, 0x00, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x80, 0x01, 0x00, - 0x81, 0x01, 0x00, 0x0f, 0x06, 0x00, 0x02, 0x04, 0x00, 0x02, 0xfe, 0xff, - 0xff, 0xff, 0x09, 0x00, 0x02, 0x2c, 0x00, 0x95, 0x5f, 0x5f, 0x41, 0x6e, - 0x6f, 0x6e, 0x43, 0x36, 0x00, 0x08, 0x00, 0x5f, 0x5f, 0x41, 0x6e, 0x6f, - 0x6e, 0x43, 0x36, 0x00, 0x00, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, - 0xff, 0x00, 0x03, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, - 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x00, 0x03, 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x00, 0x03, - 0x02, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, - 0x00, 0x06, 0x0c, 0x00, 0x02, 0x04, 0x00, 0x02, 0x4e, 0x00, 0x84, 0x61, - 0x6d, 0x65, 0x01, 0x00, 0x05, 0xb1, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, - 0x01, 0x80, 0x08, 0x00, 0x06, 0x13, 0x00, 0x02, 0x0b, 0x00, 0x02, 0x43, - 0x00, 0x8c, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, - 0x00, 0x01, 0x00, 0x05, 0x0a, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x13, 0x00, 0x02, 0x0b, 0x00, 0x02, 0x43, 0x00, 0x90, - 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x15, 0x00, 0x09, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, - 0xfe, 0xff, 0xff, 0xff, 0x00, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, - 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, - 0x08, 0x00, 0x06, 0x0c, 0x00, 0x02, 0x04, 0x00, 0x02, 0x54, 0x00, 0x84, - 0x79, 0x70, 0x65, 0x01, 0x00, 0x05, 0xd2, 0x00, 0x0e, 0x01, 0x01, 0x00, - 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x01, - 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, 0x80, - 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x14, 0x00, 0x02, 0x0c, 0x00, - 0x02, 0x53, 0x00, 0x8c, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, - 0x6f, 0x72, 0x64, 0x01, 0x00, 0x05, 0x3c, 0x01, 0x00, 0x0d, 0x01, 0x01, - 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x12, 0x00, 0x01, - 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x13, 0x00, 0x02, 0x0b, - 0x00, 0x02, 0x44, 0x00, 0x8c, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x00, 0x01, 0x00, 0x05, 0xaf, 0x00, 0x0e, 0x01, 0x01, - 0x00, 0x05, 0x01, 0x00, 0x02, 0x15, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x00, 0x02, 0x01, 0x00, 0x06, 0x71, 0x00, 0x01, 0x80, 0xfe, 0xff, - 0xff, 0xff, 0x1d, 0x00, 0x16, 0xfe, 0xff, 0xff, 0xff, 0x05, 0x00, 0x06, - 0xb1, 0x00, 0x02, 0x65, 0x01, 0x00, 0x01, 0xd2, 0x00, 0x02, 0x3c, 0x01, - 0x00, 0x01, 0xaf, 0x00, 0x02, 0x6e, 0x01, 0x00, 0x11, 0xfd, 0xff, 0xff, - 0xff, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, - 0x02, 0x12, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, - 0x16, 0x00, 0x02, 0x0e, 0x00, 0x02, 0x44, 0x00, 0x8e, 0x6e, 0x61, 0x4d, - 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x01, 0x00, - 0x05, 0xb9, 0x00, 0x0e, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x01, - 0x00, 0x02, 0x24, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x03, 0x71, 0x00, 0x01, 0x80, 0xfe, 0xff, - 0xff, 0xff, 0x1d, 0x00, 0x06, 0x03, 0x00, 0x06, 0xff, 0xff, 0xff, 0xff, - 0x0c, 0x00, 0x02, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x41, 0x74, 0x74, - 0x44, 0x65, 0x66, 0x00, 0x03, 0x0a, 0x00, 0x07, 0xfe, 0xff, 0xff, 0xff, - 0x00, 0x06, 0x05, 0x00, 0x02, 0x47, 0x00, 0x85, 0x44, 0x41, 0x39, 0x34, - 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x03, 0x02, - 0x00, 0x02, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x16, 0x00, 0x02, - 0x0e, 0x00, 0x02, 0x72, 0x00, 0x8f, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, - 0x63, 0x65, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x01, 0x00, 0xff, 0xff, 0xff, - 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x00, 0x07, 0x74, 0x01, 0x00, 0x82, 0x80, 0x00, 0x00, 0x81, 0x0a, 0x00, - 0x06, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x06, 0x0a, 0x00, 0x02, 0x30, 0x00, - 0x89, 0x31, 0x2e, 0x30, 0x31, 0x2e, 0x31, 0x39, 0x39, 0x34, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x03, 0x02, 0x00, 0x02, 0x0c, - 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0d, 0x00, 0x02, 0x05, 0x00, 0x02, - 0x65, 0x00, 0x87, 0x70, 0x6f, 0x63, 0x68, 0x00, 0x01, 0x00, 0xff, 0xff, - 0xff, 0xff, 0x06, 0x00, 0x02, 0x73, 0x00, 0x85, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x00, 0x07, 0x74, 0x01, 0x00, 0x00, 0x80, 0x02, 0x00, 0x02, 0x0a, - 0x00, 0x07, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x06, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x03, 0x02, 0x00, - 0x02, 0x0c, 0x00, 0x01, 0x80, 0x08, 0x00, 0x06, 0x0c, 0x00, 0x02, 0x04, - 0x00, 0x02, 0x74, 0x00, 0x85, 0x79, 0x70, 0x65, 0x01, 0x00, 0xfe, 0xff, - 0xff, 0xff, 0x00, 0x03, 0x06, 0x00, 0x0a, 0x03, 0x00, 0x06, 0xfe, 0xff, - 0xff, 0xff, 0x02, 0x00, 0x06, 0x5a, 0x01, 0x00, 0x01, 0xb9, 0x00, 0x02, - 0x73, 0x01, 0x00, 0x11, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, - 0xd2, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x33, 0x01, 0x00, 0x01, 0xfe, - 0xff, 0xff, 0xff, 0xd8, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0xdc, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x81, 0x01, 0x00, 0x00, 0xfe, 0xff, - 0xff, 0xff, 0xa6, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x3e, 0x00, 0x02, - 0xfe, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x65, - 0x01, 0x00, 0x01, 0xfe, 0xff, 0xff, 0xff, 0x47, 0x01, 0x00, 0x01, 0xfe, - 0xff, 0xff, 0xff, 0xf4, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x1e, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x22, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x26, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x27, 0x01, 0x00, 0x01, 0xfe, - 0xff, 0xff, 0xff, 0x5b, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x5f, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x63, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0xe0, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x5a, 0x01, 0x00, 0x01, 0xfe, - 0xff, 0xff, 0xff, 0x42, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x2a, 0x00, - 0x02, 0xfe, 0xff, 0xff, 0xff, 0x2e, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, - 0x32, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x6d, 0x00, 0x0e, 0xfe, 0xff, - 0xff, 0xff, 0x00, 0x03, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x0e, 0xfe, - 0xff, 0xff, 0xff, 0x1d, 0x00, 0x06, 0x09, 0x00, 0x06, 0x44, 0x00, 0x02, - 0x9a, 0x00, 0x02, 0xbb, 0x00, 0x02, 0x02, 0x01, 0x00, 0x01, 0x7a, 0x00, - 0x02, 0x5c, 0x01, 0x00, 0x01, 0x3e, 0x01, 0x00, 0x01, 0x56, 0x01, 0x00, - 0x01, 0x11, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0x0d, 0x00, 0x0e, 0xfe, - 0xff, 0xff, 0xff, 0x0d, 0x00, 0x0e, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, - 0x0e, 0xfe, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x12, 0x01, 0x00, 0x02, 0x03, - 0x00, 0x06, 0xfe, 0xff, 0xff, 0xff, 0x01, 0x00, 0x06, 0x30, 0x00, 0x02, - 0x43, 0x00, 0xaf, 0x3a, 0x5c, 0x44, 0x61, 0x74, 0x61, 0x5c, 0x76, 0x73, - 0x31, 0x30, 0x5c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x5c, - 0x64, 0x79, 0x6e, 0x61, 0x6e, 0x65, 0x74, 0x5f, 0x33, 0x5f, 0x30, 0x33, - 0x5f, 0x30, 0x30, 0x2f, 0x44, 0x79, 0x6e, 0x61, 0x4d, 0x4c, 0x2e, 0x78, - 0x73, 0x64, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, - 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, - 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, - 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, - 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, - 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x7f, - 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x63 -}; - diff --git a/dynadjust/include/io/DynaML-schema.hxx b/dynadjust/include/io/DynaML-schema.hxx deleted file mode 100644 index 0f8fedebf..000000000 --- a/dynadjust/include/io/DynaML-schema.hxx +++ /dev/null @@ -1,8 +0,0 @@ -// Automatically generated. Do not edit. -// - -/// \cond -#include -/// \endcond - -extern const XMLByte DynaML_schema[11430UL]; diff --git a/dynadjust/include/io/bms_file.cpp b/dynadjust/include/io/bms_file.cpp index 0f78268a3..6b961705a 100644 --- a/dynadjust/include/io/bms_file.cpp +++ b/dynadjust/include/io/bms_file.cpp @@ -147,6 +147,14 @@ std::uint64_t BmsFile::LoadFile(const std::string& bms_filename, ReadFileInfo(bms_file); ReadFileMetadata(bms_file, bms_meta); + // measurement_t grew by observation_epoch in v1.2; record layout requires a v1.2 file. + if (!versionAtLeast(1, 2)) { + std::ostringstream os; + os << "BMS file version " << GetVersion() + << " predates observation_epoch support (v1.2); please re-run dnaimport."; + throw std::runtime_error(os.str()); + } + vbinary_msr->reserve(bms_meta.binCount); for (msr = 0; msr < bms_meta.binCount; msr++) { bms_file.read(reinterpret_cast(&measRecord), diff --git a/dynadjust/include/io/bst_file.cpp b/dynadjust/include/io/bst_file.cpp index 185ad46de..e1c757985 100644 --- a/dynadjust/include/io/bst_file.cpp +++ b/dynadjust/include/io/bst_file.cpp @@ -128,6 +128,14 @@ std::uint64_t BstFile::LoadFile(const std::string& bst_filename, ReadFileInfo(bst_file); ReadFileMetadata(bst_file, bst_meta); + // station_t grew by observation_epoch in v1.2; bulk I/O requires a v1.2 layout. + if (!versionAtLeast(1, 2)) { + std::ostringstream os; + os << "BST file version " << GetVersion() + << " predates observation_epoch support (v1.2); please re-run dnaimport."; + throw std::runtime_error(os.str()); + } + vbinary_stn->resize(bst_meta.binCount); static_assert(std::is_trivially_copyable_v, "station_t must be trivially copyable for bulk I/O"); diff --git a/dynadjust/include/io/dnaiodnatypes.hpp b/dynadjust/include/io/dnaiodnatypes.hpp index b2828d6a0..500656f0e 100644 --- a/dynadjust/include/io/dnaiodnatypes.hpp +++ b/dynadjust/include/io/dnaiodnatypes.hpp @@ -94,6 +94,7 @@ typedef struct { UINT32 msr_targ_ht; UINT32 msr_id_msr; UINT32 msr_id_cluster; + UINT32 msr_gps_obs_epoch; // DNA v3.02+ column; 0 if file predates v3.02 } dna_msr_fields; // DNA version 1.00 @@ -440,6 +441,82 @@ class dna_msr_fields_301 }; +// DNA version 3.02 - adds a 25th column for the observation epoch. +// Columns 0-23 are identical to version 3.01. +template +struct _dna_msr_fields_302_ +{ + static const U _locations_[25]; + static const U _widths_[25]; +}; + +template +const U _dna_msr_fields_302_::_locations_[25] = +{ + 0, // msr type + 1, // ignore + 2, // instrument name + 22, // target 1 + 42, // target 2 + 62, // linear measurement + 62, // gps measurement + 82, // gps vcv 1 + 102, // gps vcv 2 + 122, // gps vcv 3 + 62, // gps vscale + 72, // gps pscale + 82, // gps lscale + 92, // gps hscale + 102, // gps ref frame + 122, // gps epoch (reference-frame epoch) + 76, // angular d + 80, // angular m + 82, // angular s + 90, // standard deviation + 99, // inst height + 106, // target height + 142, // msr id + 152, // cluster id + 162 // gps observation epoch +}; + +template +const U _dna_msr_fields_302_::_widths_[25] = +{ + 1, // msr type + 1, // ignore + 20, // instrument name + 20, // target 1 + 20, // target 2 + 14, // linear measurement + 20, // gps measurement + 20, // gps vcv 1 + 20, // gps vcv 2 + 20, // gps vcv 3 + 10, // gps vscale + 10, // gps pscale + 10, // gps lscale + 10, // gps hscale + 20, // gps ref frame + 20, // gps epoch (reference-frame epoch) + 4, // angular d + 2, // angular m + 8, // angular s + 9, // standard deviation + 7, // inst height + 7, // target height + 10, // msr id + 10, // cluster id + 20 // gps observation epoch +}; + +class dna_msr_fields_302 + : public _dna_msr_fields_302_ +{ +public: +}; + + template void assignDNASTNFieldParameters(const UINT16* locs, const UINT16* widths, @@ -523,7 +600,9 @@ void assignDNAMSRFieldParameters(const UINT16* locs, const UINT16* widths, dflocs.msr_targ_ht = locs[21]; dflocs.msr_id_msr = locs[22]; dflocs.msr_id_cluster = locs[23]; - + // Default the 3.02-introduced field to 0; a v3.02 branch overrides it below. + dflocs.msr_gps_obs_epoch = 0; + dfwidths.msr_type = widths[0]; dfwidths.msr_ignore = widths[1]; dfwidths.msr_inst = widths[2]; @@ -548,14 +627,26 @@ void assignDNAMSRFieldParameters(const UINT16* locs, const UINT16* widths, dfwidths.msr_targ_ht = widths[21]; dfwidths.msr_id_msr = widths[22]; dfwidths.msr_id_cluster = widths[23]; - + dfwidths.msr_gps_obs_epoch = 0; } template -void determineDNAMSRFieldParameters(const std::string& version, +void determineDNAMSRFieldParameters(const std::string& version, dna_msr_fields& dflocs, dna_msr_fields& dfwidths, const U u=0) { + if (iequals(version, "3.02")) + { + assignDNAMSRFieldParameters(dna_msr_fields_302::_locations_, + dna_msr_fields_302::_widths_, + dflocs, dfwidths); + // The 3.02-introduced observation-epoch column is beyond the shared + // 24-entry assigner, so set it explicitly here. + dflocs.msr_gps_obs_epoch = dna_msr_fields_302::_locations_[24]; + dfwidths.msr_gps_obs_epoch = dna_msr_fields_302::_widths_[24]; + return; + } + if (iequals(version, "3.01")) { assignDNAMSRFieldParameters(dna_msr_fields_301::_locations_, diff --git a/dynadjust/include/io/dnaiosnxread.cpp b/dynadjust/include/io/dnaiosnxread.cpp index 83727cc74..42088811d 100644 --- a/dynadjust/include/io/dnaiosnxread.cpp +++ b/dynadjust/include/io/dnaiosnxread.cpp @@ -939,10 +939,15 @@ void DnaIoSnx::ParseSinexStn(std::ifstream** snx_file, const char* sinexRec, vdn stn_ptr->SetReferenceFrame(datum.GetName()); stn_ptr->SetEpsg(datum.GetEpsgCode_s()); - yy = LongFromString(sBuf.substr(27, 2)), - doy = LongFromString(sBuf.substr(30, 3)), + yy = LongFromString(sBuf.substr(27, 2)), + doy = LongFromString(sBuf.substr(30, 3)), ss = ParseDateFromYyDoy(yy, doy, doy_yyyy, std::string(" ")); - stn_ptr->SetEpoch(stringFromDate(dateFromStringstream_doy_year(ss))); + { + std::string sinex_epoch = stringFromDate(dateFromStringstream_doy_year(ss)); + stn_ptr->SetEpoch(sinex_epoch); + // SINEX STAX YY:DOY is the observation data window start — record it as the immutable observation epoch + stn_ptr->SetObservationEpoch(sinex_epoch); + } stn_ptr->SetfileOrder(fileOrder++); stn_ptr->SetXAxis_d(DoubleFromString(trimstr(sBuf.substr(47, 21)))); @@ -1310,6 +1315,7 @@ void DnaIoSnx::ParseSinexMsr(std::ifstream** snx_file, const char* sinexRec, vdn dnaGpsPointCluster->SetReferenceFrame(datum.GetName()); dnaGpsPointCluster->SetEpsg(datum.GetEpsgCode_s()); dnaGpsPointCluster->SetEpoch(vStations->at(0)->GetEpoch()); + dnaGpsPointCluster->SetObservationEpoch(vStations->at(0)->GetObservationEpoch()); UINT32 cov_count, ci; @@ -1328,6 +1334,7 @@ void DnaIoSnx::ParseSinexMsr(std::ifstream** snx_file, const char* sinexRec, vdn dnaGpsPoint->SetReferenceFrame(datum.GetName()); dnaGpsPoint->SetEpsg(datum.GetEpsgCode_s()); dnaGpsPoint->SetEpoch(vStations->at(p)->GetEpoch()); + dnaGpsPoint->SetObservationEpoch(vStations->at(p)->GetObservationEpoch()); dnaGpsPoint->SetPscale(dnaGpsPointCluster->GetPscale()); dnaGpsPoint->SetLscale(dnaGpsPointCluster->GetLscale()); diff --git a/dynadjust/include/io/dynadjust_file.cpp b/dynadjust/include/io/dynadjust_file.cpp index ebc0f2806..e98927ae8 100644 --- a/dynadjust/include/io/dynadjust_file.cpp +++ b/dynadjust/include/io/dynadjust_file.cpp @@ -83,24 +83,28 @@ void DynadjustFile::ReadFileInfo(std::ifstream& file_stream) void DynadjustFile::WriteFileMetadata(std::ofstream& file_stream, binary_file_meta_t& file_meta) { // Write the metadata - file_stream.write(reinterpret_cast(&file_meta.binCount), sizeof(std::uint64_t)); - file_stream.write(reinterpret_cast(&file_meta.reduced), sizeof(bool)); - file_stream.write(reinterpret_cast(file_meta.modifiedBy), MOD_NAME_WIDTH); - - // Write the epsg code and epoch + file_stream.write(reinterpret_cast(&file_meta.binCount), sizeof(std::uint64_t)); + file_stream.write(reinterpret_cast(&file_meta.reduced), sizeof(bool)); + file_stream.write(reinterpret_cast(file_meta.modifiedBy), MOD_NAME_WIDTH); + + // Write the epsg code and epochs (reference frame + observation) file_stream.write(reinterpret_cast(file_meta.epsgCode), STN_EPSG_WIDTH); file_stream.write(reinterpret_cast(file_meta.epoch), STN_EPOCH_WIDTH); + // v1.2+: write observation_epoch after reference-frame epoch + file_stream.write(reinterpret_cast(file_meta.observation_epoch), STN_EPOCH_WIDTH); file_stream.write(reinterpret_cast(&file_meta.reftran), sizeof(bool)); file_stream.write(reinterpret_cast(&file_meta.geoid), sizeof(bool)); // Write file count and file meta - file_stream.write(reinterpret_cast(&file_meta.inputFileCount), sizeof(std::uint64_t)); + file_stream.write(reinterpret_cast(&file_meta.inputFileCount), sizeof(std::uint64_t)); for (std::uint64_t i(0); i(file_meta.inputFileMeta[i].filename), FILE_NAME_WIDTH); + 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); + // v1.2+: observation_epoch per input file + file_stream.write(reinterpret_cast(file_meta.inputFileMeta[i].observation_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)); } @@ -114,15 +118,25 @@ void DynadjustFile::WriteFileMetadata(std::ofstream& file_stream, binary_file_me void DynadjustFile::ReadFileMetadata(std::ifstream& file_stream, binary_file_meta_t& file_meta) { + const bool has_observation_epoch = versionAtLeast(1, 2); + // Read the metadata - file_stream.read(reinterpret_cast(&file_meta.binCount), sizeof(std::uint64_t)); - file_stream.read(reinterpret_cast(&file_meta.reduced), sizeof(bool)); + file_stream.read(reinterpret_cast(&file_meta.binCount), sizeof(std::uint64_t)); + file_stream.read(reinterpret_cast(&file_meta.reduced), sizeof(bool)); file_stream.read(reinterpret_cast(file_meta.modifiedBy), MOD_NAME_WIDTH); // Read the epsg code and epoch file_stream.read(reinterpret_cast(file_meta.epsgCode), STN_EPSG_WIDTH); file_stream.read(reinterpret_cast(file_meta.epoch), STN_EPOCH_WIDTH); + // v1.2+: observation_epoch follows the reference-frame epoch. + // Older files fall back to observation_epoch = epoch so legacy files remain loadable + // without changing semantics (discontinuity matching uses epoch if observation is empty). + if (has_observation_epoch) + file_stream.read(reinterpret_cast(file_meta.observation_epoch), STN_EPOCH_WIDTH); + else + memcpy(file_meta.observation_epoch, file_meta.epoch, STN_EPOCH_WIDTH); + file_stream.read(reinterpret_cast(&file_meta.reftran), sizeof(bool)); file_stream.read(reinterpret_cast(&file_meta.geoid), sizeof(bool)); @@ -132,12 +146,16 @@ void DynadjustFile::ReadFileMetadata(std::ifstream& file_stream, binary_file_met delete []file_meta.inputFileMeta; file_meta.inputFileMeta = new input_file_meta_t[file_meta.inputFileCount]; - + for (std::uint64_t i(0); i(file_meta.inputFileMeta[i].filename), FILE_NAME_WIDTH); + 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); + if (has_observation_epoch) + file_stream.read(reinterpret_cast(file_meta.inputFileMeta[i].observation_epoch), STN_EPOCH_WIDTH); + else + memcpy(file_meta.inputFileMeta[i].observation_epoch, 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)); } diff --git a/dynadjust/include/io/dynadjust_file.hpp b/dynadjust/include/io/dynadjust_file.hpp index 9e82f2c2b..f1c7d046a 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.1" +#define __FILE_VERSION__ "1.2" namespace dynadjust { namespace iostreams { diff --git a/dynadjust/include/math/dnamatrix_contiguous.cpp b/dynadjust/include/math/dnamatrix_contiguous.cpp index 2d66cf738..07b2d5a6f 100644 --- a/dynadjust/include/math/dnamatrix_contiguous.cpp +++ b/dynadjust/include/math/dnamatrix_contiguous.cpp @@ -19,7 +19,9 @@ // Description : DynAdjust Matrix library //============================================================================ +#include #include +#include #include #include #include @@ -29,6 +31,11 @@ namespace dynadjust { namespace math { +// BLAS thread count selected during application start-up. +static int g_max_blas_threads = 0; + +void set_max_blas_threads(int n) { g_max_blas_threads = n; } +int get_max_blas_threads() { return g_max_blas_threads; } std::ostream& operator<<(std::ostream& os, const matrix_2d& rhs) { if (os.iword(0) == binary) { @@ -44,18 +51,25 @@ std::ostream& operator<<(std::ostream& os, const matrix_2d& rhs) { os.write(reinterpret_cast(&rhs._mem_rows), sizeof(UINT32)); os.write(reinterpret_cast(&rhs._mem_cols), sizeof(UINT32)); + // Alignment padding — keeps data region at 8-byte aligned offset (24 bytes) + // Must match WriteMappedFileRegion / ReadMappedFileRegion / AttachMappedFileRegion layout + const UINT32 pad = 0; + os.write(reinterpret_cast(&pad), sizeof(UINT32)); + UINT32 c, r; switch (rhs._matrixType) { case mtx_lower: - // output lower triangular part of a square matrix if (rhs._mem_rows != rhs._mem_cols) throw std::runtime_error("matrix_2d operator<< (): Matrix is not square."); - // print each column - for (c = 0; c < rhs._mem_cols; ++c) - os.write(reinterpret_cast(rhs.getelementref(c, c)), (rhs._mem_rows - c) * sizeof(double)); - + if (rhs._packed) { + os.write(reinterpret_cast(rhs._buffer), + matrix_2d::packed_size(rhs._mem_rows) * sizeof(double)); + } else { + for (c = 0; c < rhs._mem_cols; ++c) + os.write(reinterpret_cast(rhs.getelementref(c, c)), (rhs._mem_rows - c) * sizeof(double)); + } break; case mtx_sparse: break; case mtx_full: @@ -108,7 +122,7 @@ void out_of_memory_handler() { } matrix_2d::matrix_2d() - : _mem_cols(0), _mem_rows(0), _cols(0), _rows(0), _buffer(0), _maxvalCol(0), _maxvalRow(0), _matrixType(mtx_full) { + : _mem_cols(0), _mem_rows(0), _cols(0), _rows(0), _buffer(0), _owns_buffer(true), _maxvalCol(0), _maxvalRow(0), _matrixType(mtx_full), _symmetric(false), _packed(false) { std::set_new_handler(out_of_memory_handler); // if this class were to be modified to use templates, each @@ -125,9 +139,12 @@ matrix_2d::matrix_2d(const UINT32& rows, const UINT32& columns) _cols(columns), _rows(rows), _buffer(0), + _owns_buffer(true), _maxvalCol(0), _maxvalRow(0), - _matrixType(mtx_full) { + _matrixType(mtx_full), + _symmetric(false), + _packed(false) { std::set_new_handler(out_of_memory_handler); allocate(_rows, _cols); @@ -140,9 +157,12 @@ matrix_2d::matrix_2d(const UINT32& rows, const UINT32& columns, const double dat _cols(columns), _rows(rows), _buffer(0), + _owns_buffer(true), _maxvalCol(0), _maxvalRow(0), - _matrixType(matrix_type) { + _matrixType(matrix_type), + _symmetric(false), + _packed(false) { std::set_new_handler(out_of_memory_handler); std::stringstream ss; @@ -197,17 +217,26 @@ matrix_2d::matrix_2d(const matrix_2d& newmat) _cols(newmat.columns()), _rows(newmat.rows()), _buffer(0), + _owns_buffer(true), _maxvalCol(newmat.maxvalueCol()), _maxvalRow(newmat.maxvalueRow()), - _matrixType(newmat.matrixType()) { + _matrixType(newmat.matrixType()), + _symmetric(newmat._symmetric), + _packed(newmat._packed) { std::set_new_handler(out_of_memory_handler); - allocate(_mem_rows, _mem_cols); - - const double* ptr = newmat.getbuffer(); - - // copy buffer - memcpy(_buffer, ptr, newmat.buffersize()); + if (_packed) { + std::size_t ps = packed_size(_mem_rows); + _buffer = static_cast(std::malloc(ps * sizeof(double))); + if (!_buffer) throw NetMemoryException("Insufficient memory for packed matrix copy."); + memcpy(_buffer, newmat.getbuffer(), ps * sizeof(double)); + } else { + // Allocate without zeroing — memcpy immediately overwrites entire buffer + std::size_t total_size = static_cast(_mem_rows) * _mem_cols; + _buffer = static_cast(std::malloc(total_size * sizeof(double))); + if (!_buffer) throw NetMemoryException("Insufficient memory for matrix copy."); + memcpy(_buffer, newmat.getbuffer(), total_size * sizeof(double)); + } } matrix_2d::~matrix_2d() { @@ -216,14 +245,21 @@ matrix_2d::~matrix_2d() { } std::size_t matrix_2d::get_size() { + // 8 UINT32s: matrixType, rows, cols, mem_rows, mem_cols, _pad, maxvalRow, maxvalCol + // The padding UINT32 ensures the data region starts at an 8-byte aligned offset (24 bytes) + // from the region base, enabling in-place mmap buffer attachment. size_t size = - (7 * sizeof(UINT32)); // UINT32 _matrixType, _mem_cols, _mem_rows, _cols, _rows, _maxvalRow, _maxvalCol + (8 * sizeof(UINT32)); switch (_matrixType) { case mtx_lower: size += sumOfConsecutiveIntegers(_mem_rows) * sizeof(double); break; case mtx_sparse: break; case mtx_full: - default: size += buffersize(); + default: + if (_packed) + size += packed_size(_mem_rows) * sizeof(double); + else + size += buffersize(); } return size; } @@ -241,7 +277,6 @@ void matrix_2d::ReadMappedFileRegion(void* addr) { switch (_matrixType) { case mtx_sparse: - // _mem_cols and _mem_rows equal _cols and _rows _mem_rows = _rows; _mem_cols = _cols; break; @@ -250,10 +285,21 @@ void matrix_2d::ReadMappedFileRegion(void* addr) { default: _mem_rows = *data_U++; _mem_cols = *data_U++; + ++data_U; // skip alignment padding UINT32 break; } - allocate(_mem_rows, _mem_cols); + if (_matrixType == mtx_lower && _mem_rows == _mem_cols) { + deallocate(); + _packed = true; + _symmetric = true; + std::size_t ps = packed_size(_mem_rows); + _buffer = static_cast(std::calloc(ps, sizeof(double))); + if (!_buffer) throw NetMemoryException("Insufficient memory for packed matrix read."); + } else { + _packed = false; + allocate(_mem_rows, _mem_cols); + } double* data_d; int* data_i; @@ -292,15 +338,21 @@ void matrix_2d::ReadMappedFileRegion(void* addr) { return; break; case mtx_lower: + assert(_mem_rows == _mem_cols && "ReadMappedFileRegion(mtx_lower): matrix must be square"); data_d = reinterpret_cast(data_U); - // read each column - for (c = 0; c < _mem_cols; ++c) { - memcpy(getelementref(c, c), data_d, (_mem_rows - c) * sizeof(double)); - data_d += (_mem_rows - c); + if (_packed) { + std::size_t ps = packed_size(_mem_rows); + memcpy(_buffer, data_d, ps * sizeof(double)); + data_d += ps; + } else { + for (c = 0; c < _mem_cols; ++c) { + memcpy(getbuffer(c, c), data_d, (_mem_rows - c) * sizeof(double)); + data_d += (_mem_rows - c); + } } - fillupper(); + _symmetric = true; break; case mtx_full: default: @@ -342,9 +394,32 @@ void matrix_2d::WriteMappedFileRegion(void* addr) { default: *data_U++ = _mem_rows; *data_U++ = _mem_cols; + *data_U++ = 0; // alignment padding break; } + // In-place mmap: data is already in the region, only update footer + if (!_owns_buffer) { + // Calculate footer position by skipping past data + double* data_d; + switch (_matrixType) { + case mtx_lower: + if (_packed) + data_d = reinterpret_cast(data_U) + packed_size(_mem_rows); + else + data_d = reinterpret_cast(data_U) + sumOfConsecutiveIntegers(_mem_rows); + break; + case mtx_full: + default: + data_d = reinterpret_cast(data_U) + static_cast(_mem_rows) * _mem_cols; + break; + } + PUINT32 footer = reinterpret_cast(data_d); + *footer++ = _maxvalRow; + *footer = _maxvalCol; + return; + } + double* data_d; int* data_i; @@ -384,10 +459,15 @@ void matrix_2d::WriteMappedFileRegion(void* addr) { case mtx_lower: data_d = reinterpret_cast(data_U); - // print each column - for (c = 0; c < _mem_cols; ++c) { - memcpy(data_d, getbuffer(c, c), (_mem_rows - c) * sizeof(double)); - data_d += (_mem_rows - c); + if (_packed) { + std::size_t ps = packed_size(_mem_rows); + memcpy(data_d, _buffer, ps * sizeof(double)); + data_d += ps; + } else { + for (c = 0; c < _mem_cols; ++c) { + memcpy(data_d, getbuffer(c, c), (_mem_rows - c) * sizeof(double)); + data_d += (_mem_rows - c); + } } break; case mtx_full: @@ -408,16 +488,111 @@ void matrix_2d::WriteMappedFileRegion(void* addr) { *data_U = _maxvalCol; } -void matrix_2d::allocate() { allocate(_mem_rows, _mem_cols); } -// creates memory for desired "memory size", not matrix dimensions -void matrix_2d::allocate(const UINT32& rows, const UINT32& columns) { - //_method_ = "allocate"; +void matrix_2d::AttachMappedFileRegion(void* addr) { + // Read header metadata — same layout as ReadMappedFileRegion + PUINT32 data_U = reinterpret_cast(addr); + _matrixType = *data_U++; + _rows = *data_U++; + _cols = *data_U++; + switch (_matrixType) { + case mtx_sparse: + _mem_rows = _rows; + _mem_cols = _cols; + // Sparse cannot use in-place — fall back to copy path + ReadMappedFileRegion(addr); + return; + case mtx_lower: + case mtx_full: + default: + _mem_rows = *data_U++; + _mem_cols = *data_U++; + ++data_U; // skip alignment padding + break; + } + + // Release any existing buffer deallocate(); - // an exception will be thrown by out_of_memory_handler - // if memory cannot be allocated + // Point _buffer directly at the data region in the mmap + _buffer = reinterpret_cast(data_U); + _owns_buffer = false; + + // Set packed/symmetric flags for lower-triangular matrices + if (_matrixType == mtx_lower && _mem_rows == _mem_cols) { + _packed = true; + _symmetric = true; + } else { + _packed = false; + } + + // Read footer (maxvalRow, maxvalCol) from after the data + double* data_d; + switch (_matrixType) { + case mtx_lower: + data_d = _buffer + packed_size(_mem_rows); + break; + case mtx_full: + default: + data_d = _buffer + static_cast(_mem_rows) * _mem_cols; + break; + } + + PUINT32 footer = reinterpret_cast(data_d); + _maxvalRow = *footer++; + _maxvalCol = *footer; +} + + +void matrix_2d::DetachMappedFileRegion(void* addr) { + if (_owns_buffer || _buffer == nullptr) + return; + + // Write updated footer (maxvalRow, maxvalCol) to the mmap region. + // Header: 6 UINT32s (matrixType, rows, cols, mem_rows, mem_cols, pad) = 24 bytes. + // Skip past data to find the footer position. + double* data_d; + switch (_matrixType) { + case mtx_lower: + data_d = _buffer + packed_size(_mem_rows); + break; + case mtx_full: + default: + data_d = _buffer + static_cast(_mem_rows) * _mem_cols; + break; + } + + PUINT32 footer = reinterpret_cast(data_d); + *footer++ = _maxvalRow; + *footer = _maxvalCol; + + // Detach without freeing the mmap memory + _buffer = nullptr; + _owns_buffer = true; +} + + +void matrix_2d::allocate() { + if (_matrixType == mtx_lower && _mem_rows == _mem_cols && _mem_rows > 0) { + deallocate(); + _packed = true; + _symmetric = true; + std::size_t ps = packed_size(_mem_rows); + _buffer = static_cast(std::calloc(ps, sizeof(double))); + if (!_buffer) { + std::stringstream ss; + ss << "Insufficient memory for a packed " << _mem_rows << " x " << _mem_rows << " matrix."; + throw NetMemoryException(ss.str()); + } + return; + } + allocate(_mem_rows, _mem_cols); +} + +// creates memory for desired "memory size", not matrix dimensions +void matrix_2d::allocate(const UINT32& rows, const UINT32& columns) { + deallocate(); buy(rows, columns, &_buffer); } @@ -429,26 +604,27 @@ void matrix_2d::buy(const UINT32& rows, const UINT32& columns, double** mem_spac __row__ = rows; __col__ = columns; - // an exception will be thrown by out_of_memory_handler - // if memory cannot be allocated + // calloc returns zero-initialised memory. For large allocations glibc + // satisfies calloc via mmap, whose anonymous pages are already zero-filled + // by the kernel — so calloc skips the redundant memset that was previously + // triggering page-fault storms (97 % of CPU time on the NSW benchmark). std::size_t total_size = static_cast(rows) * static_cast(columns); - (*mem_space) = new double[total_size]; + (*mem_space) = static_cast(std::calloc(total_size, sizeof(double))); if ((*mem_space) == nullptr) { std::stringstream ss; ss << "Insufficient memory for a " << rows << " x " << columns << " matrix."; throw NetMemoryException(ss.str()); } - - // Initialize memory to zero to prevent uninitialized values - std::memset((*mem_space), 0, total_size * sizeof(double)); } void matrix_2d::deallocate() { if (_buffer != nullptr) { - delete[] _buffer; + if (_owns_buffer) + std::free(_buffer); _buffer = nullptr; } + _owns_buffer = true; } matrix_2d matrix_2d::submatrix(const UINT32& row_begin, const UINT32& col_begin, const UINT32& rows, @@ -514,9 +690,14 @@ void matrix_2d::submatrix(const UINT32& row_begin, const UINT32& col_begin, matr } void matrix_2d::redim(const UINT32& rows, const UINT32& columns) { + if (_packed) { + deallocate(); + _packed = false; + } + _symmetric = false; // if new matrix size is smaller than or equal to the previous // matrix size, then simply change dimensions and return - if (rows <= _mem_rows && columns <= _mem_cols) { + if (_buffer != nullptr && rows <= _mem_rows && columns <= _mem_cols) { // Zero out the unused portions when reusing buffer // Zero partial columns (rows beyond new row count) for (UINT32 col = 0; col < columns && col < _mem_cols; ++col) { @@ -550,23 +731,46 @@ void matrix_2d::redim(const UINT32& rows, const UINT32& columns) { buy(rows, columns, &new_buffer); // Copy old data to new buffer if there was any + bool old_owns = _owns_buffer; if (old_buffer != nullptr && old_rows > 0 && old_cols > 0) { for (UINT32 col = 0; col < old_cols && col < columns; ++col) { for (UINT32 row = 0; row < old_rows && row < rows; ++row) { new_buffer[col * rows + row] = old_buffer[col * old_rows + row]; } } - // Delete old buffer - delete[] old_buffer; + if (old_owns) + std::free(old_buffer); } _buffer = new_buffer; - + _owns_buffer = true; _rows = _mem_rows = rows; _cols = _mem_cols = columns; } +void matrix_2d::redim_packed(const UINT32& n) { + std::size_t ps = packed_size(n); + + if (_buffer != nullptr && _packed && n <= _mem_rows) { + std::memset(_buffer, 0, ps * sizeof(double)); + _rows = _cols = _mem_rows = _mem_cols = n; + return; + } + + deallocate(); + _rows = _cols = _mem_rows = _mem_cols = n; + _packed = true; + _symmetric = true; + _matrixType = mtx_lower; + _buffer = static_cast(std::calloc(ps, sizeof(double))); + if (!_buffer) { + std::stringstream ss; + ss << "Insufficient memory for a packed " << n << " x " << n << " matrix."; + throw NetMemoryException(ss.str()); + } +} + void matrix_2d::shrink(const UINT32& rows, const UINT32& columns) { if (rows > _rows || columns > _cols) { std::stringstream ss; @@ -641,21 +845,53 @@ void matrix_2d::copybuffer(const UINT32& rowstart, const UINT32& columnstart, co } } -void matrix_2d::copyelements(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d& src, - const UINT32& row_src, const UINT32& column_src, const UINT32& rows, - const UINT32& columns) { +void matrix_2d::copyelements_generic(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d& src, + const UINT32& row_src, const UINT32& column_src, const UINT32& rows, + const UINT32& columns) { + assert(_buffer != nullptr && src._buffer != nullptr); + assert(row_dest + rows <= _mem_rows && "copyelements_generic(): dest row overflow"); + assert(column_dest + columns <= _mem_cols && "copyelements_generic(): dest col overflow"); + assert(row_src + rows <= src._mem_rows && "copyelements_generic(): src row overflow"); + assert(column_src + columns <= src._mem_cols && "copyelements_generic(): src col overflow"); + // Fast path: packed source → dense dest (avoids per-element get/put overhead) + if (src._packed && !_packed) { + for (UINT32 c = 0; c < columns; ++c) { + double* d = _buffer + static_cast(column_dest + c) * _mem_rows + row_dest; + UINT32 sc = column_src + c; + for (UINT32 r = 0; r < rows; ++r) { + UINT32 sr = row_src + r, sj = sc; + if (sr < sj) std::swap(sr, sj); + d[r] = src._buffer[packed_index(src._rows, sr, sj)]; + } + } + return; + } + // Fast path: dense source → packed dest + if (_packed && !src._packed && !src._symmetric) { + for (UINT32 c = 0; c < columns; ++c) { + const double* s = src._buffer + static_cast(column_src + c) * src._mem_rows + row_src; + UINT32 dc = column_dest + c; + for (UINT32 r = 0; r < rows; ++r) { + UINT32 dr = row_dest + r; + if (dr >= dc) + _buffer[packed_index(_rows, dr, dc)] = s[r]; + } + } + return; + } + // Fallback for remaining packed/symmetric combinations + if (src._symmetric || src._packed || _packed) { + for (UINT32 r = 0; r < rows; ++r) + for (UINT32 c = 0; c < columns; ++c) + put(row_dest + r, column_dest + c, src.get(row_src + r, column_src + c)); + return; + } UINT32 cd(0), cs(0), colend_dest(column_dest + columns); for (cd = column_dest, cs = column_src; cd < colend_dest; ++cd, ++cs) memcpy(getelementref(row_dest, cd), src.getbuffer(row_src, cs), static_cast(rows) * sizeof(double)); } -void matrix_2d::copyelements(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d* src, - const UINT32& row_src, const UINT32& column_src, const UINT32& rows, - const UINT32& columns) { - copyelements(row_dest, column_dest, *src, row_src, column_src, rows, columns); -} - void matrix_2d::sweep(UINT32 k1, UINT32 k2) { double eps(1.0e-8), d; UINT32 i, j, k, it; @@ -705,10 +941,47 @@ matrix_2d matrix_2d::sweepinverse() { return *this; } -matrix_2d matrix_2d::cholesky_inverse(bool LOWER_IS_CLEARED /*=false*/) { +matrix_2d matrix_2d::cholesky_inverse(bool LOWER_IS_CLEARED /*=false*/, bool mark_symmetric /*=false*/) { if (_rows < 1) return *this; if (_rows != _cols) throw std::runtime_error("cholesky_inverse(): Matrix is not square."); + assert(_buffer != nullptr && "cholesky_inverse(): null buffer"); + assert(_mem_rows >= _rows && "cholesky_inverse(): mem_rows < rows"); + assert(_mem_cols >= _cols && "cholesky_inverse(): mem_cols < cols"); + assert(!(mark_symmetric && LOWER_IS_CLEARED) && + "cholesky_inverse(): mark_symmetric with LOWER_IS_CLEARED not supported"); + + if (_packed) { + // Unpack to full format for fast blocked dpotrf/dpotri, then repack. + // dpptrf/dpptri are element-by-element and orders of magnitude slower. + lapack_int n = _rows; + std::size_t full_size = static_cast(n) * n; + + // Reuse thread-local workspace to avoid repeated allocation/deallocation. + // dpotrf/dpotri with uplo='L' only read the lower triangle, so no + // memset is needed — the unpack loop sets all lower-triangle elements. + static thread_local std::vector chol_workspace; + if (chol_workspace.size() < full_size) + chol_workspace.resize(full_size); + double* full = chol_workspace.data(); + + for (UINT32 j = 0; j < _rows; ++j) + for (UINT32 i = j; i < _rows; ++i) + full[static_cast(j) * n + i] = _buffer[packed_index(_rows, i, j)]; + + char uplo = LOWER_TRIANGLE; + lapack_int info, lda = n; + LAPACK_FUNC(dpotrf)(&uplo, &n, full, &lda, &info); + if (info != 0) throw MatrixInversionFailure("Matrix inversion failed, the matrix is singular."); + LAPACK_FUNC(dpotri)(&uplo, &n, full, &lda, &info); + if (info != 0) throw MatrixInversionFailure("Matrix inversion failed, the matrix is singular."); + + for (UINT32 j = 0; j < _rows; ++j) + for (UINT32 i = j; i < _rows; ++i) + _buffer[packed_index(_rows, i, j)] = full[static_cast(j) * n + i]; + return *this; + } + char uplo(LOWER_TRIANGLE); if (LOWER_IS_CLEARED) uplo = UPPER_TRIANGLE; @@ -727,23 +1000,181 @@ matrix_2d matrix_2d::cholesky_inverse(bool LOWER_IS_CLEARED /*=false*/) { if (info != 0) throw MatrixInversionFailure("Matrix inversion failed, the matrix is singular."); - if (LOWER_IS_CLEARED) + if (mark_symmetric) { + _symmetric = true; + } else if (LOWER_IS_CLEARED) { filllower(); - else + } else { fillupper(); + } + + return *this; +} + +matrix_2d matrix_2d::cholesky_factor(bool LOWER_IS_CLEARED /*=false*/) { + if (_rows < 1) return *this; + if (_rows != _cols) throw std::runtime_error("cholesky_factor(): Matrix is not square."); + + assert(_buffer != nullptr && "cholesky_factor(): null buffer"); + assert(_mem_rows >= _rows && "cholesky_factor(): mem_rows < rows"); + assert(_mem_cols >= _cols && "cholesky_factor(): mem_cols < cols"); + + if (_packed) { + // Unpack to full format for dpotrf (same approach as cholesky_inverse). + // The factor L is stored back in packed format. + lapack_int n = _rows; + std::size_t full_size = static_cast(n) * n; + + // Reuse thread-local workspace; no memset needed (dpotrf reads lower triangle only) + static thread_local std::vector chol_workspace; + if (chol_workspace.size() < full_size) + chol_workspace.resize(full_size); + double* full = chol_workspace.data(); + + for (UINT32 j = 0; j < _rows; ++j) + for (UINT32 i = j; i < _rows; ++i) + full[static_cast(j) * n + i] = _buffer[packed_index(_rows, i, j)]; + + char uplo = LOWER_TRIANGLE; + lapack_int info, lda = n; + LAPACK_FUNC(dpotrf)(&uplo, &n, full, &lda, &info); + if (info != 0) throw MatrixInversionFailure("Cholesky factorisation failed, the matrix is singular."); + + // Store the L factor back in packed format + for (UINT32 j = 0; j < _rows; ++j) + for (UINT32 i = j; i < _rows; ++i) + _buffer[packed_index(_rows, i, j)] = full[static_cast(j) * n + i]; + return *this; + } + + char uplo(LOWER_TRIANGLE); + if (LOWER_IS_CLEARED) uplo = UPPER_TRIANGLE; + + lapack_int info, n = _rows; + lapack_int lda = _mem_rows; + + LAPACK_FUNC(dpotrf)(&uplo, &n, _buffer, &lda, &info); + + if (info != 0) + throw MatrixInversionFailure("Cholesky factorisation failed, the matrix is singular."); return *this; } +void matrix_2d::cholesky_solve(matrix_2d& rhs, bool LOWER_IS_CLEARED /*=false*/) { + assert(_rows == _cols && "cholesky_solve(): factor matrix is not square"); + assert(_rows == rhs._rows && "cholesky_solve(): dimension mismatch"); + assert(_buffer != nullptr && "cholesky_solve(): null factor buffer"); + assert(rhs._buffer != nullptr && "cholesky_solve(): null rhs buffer"); + + if (_packed) { + // Unpack the L factor to full format for dpotrs. + lapack_int n = _rows; + std::size_t full_size = static_cast(n) * n; + + // Reuse thread-local workspace; no memset needed (dpotrs reads lower triangle only) + static thread_local std::vector chol_workspace; + if (chol_workspace.size() < full_size) + chol_workspace.resize(full_size); + double* full = chol_workspace.data(); + + for (UINT32 j = 0; j < _rows; ++j) + for (UINT32 i = j; i < _rows; ++i) + full[static_cast(j) * n + i] = _buffer[packed_index(_rows, i, j)]; + + char uplo = LOWER_TRIANGLE; + lapack_int nrhs = rhs._cols; + lapack_int lda = n; + lapack_int ldb = rhs._mem_rows; + lapack_int info; + + LAPACK_FUNC(dpotrs)(&uplo, &n, &nrhs, full, &lda, rhs._buffer, &ldb, &info); + + if (info != 0) + throw MatrixInversionFailure("Cholesky solve failed."); + return; + } + + char uplo(LOWER_TRIANGLE); + if (LOWER_IS_CLEARED) uplo = UPPER_TRIANGLE; + + lapack_int n = _rows; + lapack_int nrhs = rhs._cols; + lapack_int lda = _mem_rows; + lapack_int ldb = rhs._mem_rows; + lapack_int info; + + LAPACK_FUNC(dpotrs)(&uplo, &n, &nrhs, _buffer, &lda, rhs._buffer, &ldb, &info); + + if (info != 0) + throw MatrixInversionFailure("Cholesky solve failed."); +} + +double matrix_2d::dot(const matrix_2d& other) const { + assert(_cols == 1 && other._cols == 1 && "dot(): both matrices must be column vectors"); + assert(_rows == other._rows && "dot(): dimension mismatch"); + assert(_buffer != nullptr && other._buffer != nullptr && "dot(): null buffer"); + + double result = 0.0; + for (UINT32 i = 0; i < _rows; ++i) + result += get(i, 0) * other.get(i, 0); + return result; +} + matrix_2d matrix_2d::scale(const double& scalar) { + if (_packed) { + std::size_t ps = packed_size(_rows); + for (std::size_t k = 0; k < ps; ++k) + _buffer[k] *= scalar; + return *this; + } UINT32 i, j; for (i = 0; i < _rows; ++i) for (j = 0; j < _cols; ++j) *getelementref(i, j) *= scalar; return *this; } -void matrix_2d::blockadd(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, - const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols) { +void matrix_2d::scale_symmetric_diagonal(const double* diag) { + assert(_symmetric && "scale_symmetric_diagonal(): matrix must be symmetric"); + if (_packed) { + for (UINT32 j = 0; j < _rows; ++j) + for (UINT32 i = j; i < _rows; ++i) + _buffer[packed_index(_rows, i, j)] *= diag[i] * diag[j]; + return; + } + for (UINT32 j = 0; j < _rows; ++j) + for (UINT32 i = j; i < _rows; ++i) { + double s = diag[i] * diag[j]; + *getelementref(i, j) *= s; + } +} + +void matrix_2d::blockadd_generic(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, + const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols) { + // Fast path: dense source → packed dest (avoids per-element get/elementadd overhead) + if (_packed && !mat_src._packed && !mat_src._symmetric) { + for (UINT32 c = 0; c < cols; ++c) { + const double* s = mat_src._buffer + static_cast(col_src + c) * mat_src._mem_rows + row_src; + UINT32 dc = col_dest + c; + for (UINT32 r = 0; r < rows; ++r) { + UINT32 dr = row_dest + r; + if (dr >= dc) + _buffer[packed_index(_rows, dr, dc)] += s[r]; + } + } + return; + } + // Fast path: both dense (non-packed, non-symmetric) — use direct buffer arithmetic + if (!_packed && !mat_src._packed && !mat_src._symmetric) { + for (UINT32 c = 0; c < cols; ++c) { + double* d = _buffer + static_cast(col_dest + c) * _mem_rows + row_dest; + const double* s = mat_src._buffer + static_cast(col_src + c) * mat_src._mem_rows + row_src; + for (UINT32 r = 0; r < rows; ++r) + d[r] += s[r]; + } + return; + } + // Fallback: generic per-element path UINT32 i_dest, j_dest, i_src, j_src; UINT32 i_dest_end(row_dest + rows), j_dest_end(col_dest + cols); @@ -752,9 +1183,8 @@ void matrix_2d::blockadd(const UINT32& row_dest, const UINT32& col_dest, const m elementadd(i_dest, j_dest, mat_src.get(i_src, j_src)); } -// Same as blockadd, but adds transpose. mat_src must be square. -void matrix_2d::blockTadd(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, - const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols) { +void matrix_2d::blockTadd_generic(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, + const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols) { UINT32 i_dest, j_dest, i_src, j_src; UINT32 i_dest_end(row_dest + rows), j_dest_end(col_dest + cols); @@ -773,79 +1203,153 @@ void matrix_2d::blocksubtract(const UINT32& row_dest, const UINT32& col_dest, co elementsubtract(i_dest, j_dest, mat_src.get(i_src, j_src)); } -// clearlower() void matrix_2d::clearlower() { + assert(_buffer != nullptr); + assert(!_packed && "clearlower(): not valid for packed storage"); // Sets lower triangle elements to zero UINT32 col, row; - for (row = 1, col = 0; col < _mem_cols; ++col, ++row) + for (row = 1, col = 0; col < _mem_cols && row < _mem_rows; ++col, ++row) memset(getelementref(row, col), 0, (static_cast(_mem_rows) - row) * sizeof(double)); } -// clearupper() void matrix_2d::clearupper() { + assert(!_packed && "clearupper(): not valid for packed storage"); // Sets upper triangle elements to zero - UINT32 col, row; - for (row = 0; row < _rows; ++row) - for (col = row + 1; col < _cols; ++col) put(row, col, 0.0); + // Column-major: for each column, zero the rows above the diagonal + for (UINT32 col = 1; col < _cols; ++col) + memset(_buffer + col * _mem_rows, 0, col * sizeof(double)); } -// filllower() void matrix_2d::filllower() { - // copies upper triangle to lower triangle - UINT32 column, row; - for (row = 1; row < _rows; row++) - for (column = 0; column < row; column++) put(row, column, get(column, row)); + assert(_buffer != nullptr); + assert(!_packed && "filllower(): not valid for packed storage"); + assert(_rows == _cols && "filllower(): matrix must be square"); + // Copy upper triangle to lower: A(row,col) = A(col,row) for row > col + // Column-major: A(r,c) = _buffer[c * _mem_rows + r] + // Outer loop over destination columns so writes are sequential in memory. + const std::size_t mr = _mem_rows; + constexpr UINT32 BLK = 64; + for (UINT32 cb = 0; cb < _cols; cb += BLK) { + UINT32 ce = std::min(cb + BLK, _cols); + for (UINT32 rb = ce; rb < _rows; rb += BLK) { + UINT32 re = std::min(rb + BLK, _rows); + for (UINT32 col = cb; col < ce; col++) { + double* dst = _buffer + col * mr; + for (UINT32 row = rb; row < re; row++) + dst[row] = _buffer[row * mr + col]; + } + } + } + // Diagonal blocks: row > col within the same block + for (UINT32 cb = 0; cb < _cols; cb += BLK) { + UINT32 ce = std::min(cb + BLK, _cols); + for (UINT32 col = cb; col < ce; col++) { + double* dst = _buffer + col * mr; + for (UINT32 row = col + 1; row < ce; row++) + dst[row] = _buffer[row * mr + col]; + } + } } -// fillupper() void matrix_2d::fillupper() { - // copies lower triangle to upper triangle - UINT32 column, row; - for (row = 1; row < _rows; row++) - for (column = 0; column < row; column++) put(column, row, get(row, column)); + assert(_buffer != nullptr); + assert(!_packed && "fillupper(): not valid for packed storage"); + assert(_rows == _cols && "fillupper(): matrix must be square"); + // Copy lower triangle to upper: A(r,c) = A(c,r) for r < c + // Column-major: A(r,c) = _buffer[c * _mem_rows + r] + // + // Outer loop over destination columns so writes are sequential in memory. + // Reads are strided (one element per source column) but write-combining + // and store buffers make sequential writes much faster than sequential reads + // for large matrices. + const std::size_t mr = _mem_rows; + constexpr UINT32 BLK = 64; + for (UINT32 cb = 0; cb < _cols; cb += BLK) { + UINT32 ce = std::min(cb + BLK, _cols); + for (UINT32 rb = 0; rb < ce; rb += BLK) { + UINT32 re = std::min(rb + BLK, ce); + for (UINT32 col = cb; col < ce; col++) { + double* dst = _buffer + col * mr; + UINT32 r0 = rb; + UINT32 r1 = std::min(re, col); + for (UINT32 row = r0; row < r1; row++) + dst[row] = _buffer[row * mr + col]; + } + } + } } -// zero() -void matrix_2d::zero() { memset(_buffer, 0, buffersize()); } +void matrix_2d::zero() { + assert(_buffer != nullptr && "zero(): null buffer"); + if (_packed) + memset(_buffer, 0, packed_size(_mem_rows) * sizeof(double)); + else + memset(_buffer, 0, buffersize()); +} // zero() void matrix_2d::zero(const UINT32& row_begin, const UINT32& col_begin, const UINT32& rows, const UINT32& columns) { + assert(_buffer != nullptr && "zero(sub): null buffer"); + assert(row_begin + rows <= _mem_rows && "zero(sub): row overflow"); + assert(col_begin + columns <= _mem_cols && "zero(sub): col overflow"); UINT32 col(0), col_end(col_begin + columns); for (col = col_begin; col < col_end; ++col) memset(getelementref(row_begin, col), 0, rows * sizeof(double)); } matrix_2d& matrix_2d::operator=(const matrix_2d& rhs) { - // Overloaded assignment operator if (this == &rhs) return *this; - // If rhs data can fit within limits of this matrix, copy - // and return. Otherwise, allocate new memory + if (rhs._packed) { + std::size_t ps = packed_size(rhs._rows); + if (_packed && _mem_rows >= rhs._rows) { + _rows = _cols = rhs._rows; + memcpy(_buffer, rhs._buffer, ps * sizeof(double)); + } else { + deallocate(); + _rows = _cols = _mem_rows = _mem_cols = rhs._rows; + _buffer = static_cast(std::malloc(ps * sizeof(double))); + if (!_buffer) throw NetMemoryException("Insufficient memory for packed matrix assignment."); + memcpy(_buffer, rhs._buffer, ps * sizeof(double)); + } + _packed = true; + _symmetric = true; + _matrixType = rhs._matrixType; + _maxvalCol = rhs.maxvalueCol(); + _maxvalRow = rhs.maxvalueRow(); + return *this; + } + + // rhs is not packed + if (_packed) { + deallocate(); + _packed = false; + _mem_rows = _mem_cols = 0; + } + if (_mem_rows >= rhs.rows() && _mem_cols >= rhs.columns()) { - // don't change _mem_rows or _mem_cols. Simply update - // visible dimensions and copy buffer _rows = rhs.rows(); _cols = rhs.columns(); copybuffer(_rows, _cols, rhs); - _maxvalCol = rhs.maxvalueCol(); // col of max value - _maxvalRow = rhs.maxvalueRow(); // row of max value + _maxvalCol = rhs.maxvalueCol(); + _maxvalRow = rhs.maxvalueRow(); + _symmetric = rhs._symmetric; return *this; } - // Okay, rhs is larger, so allocate new memory. Call free - // memory first before changing row and column dimensions! deallocate(); - _mem_rows = rhs.memRows(); // change memory limits + _mem_rows = rhs.memRows(); _mem_cols = rhs.memColumns(); - _rows = rhs.rows(); // change matrix dimensions + _rows = rhs.rows(); _cols = rhs.columns(); allocate(_mem_rows, _mem_cols); copybuffer(_rows, _cols, rhs); - _maxvalCol = rhs.maxvalueCol(); // col of max value - _maxvalRow = rhs.maxvalueRow(); // row of max value + _maxvalCol = rhs.maxvalueCol(); + _maxvalRow = rhs.maxvalueRow(); + _symmetric = rhs._symmetric; return *this; } @@ -875,6 +1379,10 @@ matrix_2d matrix_2d::add(const matrix_2d& rhs) { // multiplies this matrix by rhs and stores the result in a new matrix // Uses Intel MKL dgemm matrix_2d matrix_2d::multiply(const char* lhs_trans, const matrix_2d& rhs, const char* rhs_trans) { + assert(!_symmetric && "multiply(dgemm): LHS is symmetric — use multiply_sym instead"); + assert(!rhs._symmetric && "multiply(dgemm): RHS is symmetric — upper triangle is unpopulated"); + assert(_buffer != nullptr && rhs._buffer != nullptr); + matrix_2d m(_rows, rhs.columns()); const double one = 1.0; @@ -914,6 +1422,10 @@ matrix_2d matrix_2d::multiply(const char* lhs_trans, const matrix_2d& rhs, const // Uses Intel MKL dgemm matrix_2d matrix_2d::multiply(const matrix_2d& lhs, const char* lhs_trans, const matrix_2d& rhs, const char* rhs_trans) { + assert(!lhs._symmetric && "multiply(dgemm): LHS is symmetric — use multiply_sym instead"); + assert(!rhs._symmetric && "multiply(dgemm): RHS is symmetric — upper triangle is unpopulated"); + assert(lhs._buffer != nullptr && rhs._buffer != nullptr && _buffer != nullptr); + const double one = 1.0; const double zero = 0.0; @@ -947,6 +1459,48 @@ matrix_2d::multiply(const matrix_2d& lhs, const char* lhs_trans, const matrix_2d return *this; } // Multiply() +// C (this) = sym_lhs * rhs, using dsymm or dspmv (packed) +matrix_2d matrix_2d::multiply_sym(const matrix_2d& sym_lhs, const matrix_2d& rhs) { + lapack_int m = sym_lhs.rows(); + lapack_int n = rhs.columns(); + + assert(sym_lhs._symmetric && "multiply_sym(): LHS must be marked symmetric"); + assert(sym_lhs._buffer != nullptr && "multiply_sym(): LHS null buffer"); + assert(rhs._buffer != nullptr && "multiply_sym(): RHS null buffer"); + assert(_buffer != nullptr && "multiply_sym(): result null buffer"); + assert(_buffer != sym_lhs._buffer && _buffer != rhs._buffer && + "multiply_sym(): result must not alias inputs"); + + if (sym_lhs.columns() != sym_lhs.rows()) + throw std::runtime_error("multiply_sym(): LHS matrix is not square."); + if (static_cast(rhs.rows()) != m) + throw std::runtime_error("multiply_sym(): Matrix dimensions are incompatible."); + if (_rows != static_cast(m) || _cols != static_cast(n)) + throw std::runtime_error("multiply_sym(): Result matrix dimensions are incompatible."); + + if (sym_lhs._packed) { + for (lapack_int j = 0; j < n; ++j) { + BLAS_FUNC(dspmv)(CblasColMajor, CblasLower, + m, 1.0, sym_lhs._buffer, + rhs._buffer + j * rhs._mem_rows, 1, + 0.0, _buffer + j * _mem_rows, 1); + } + return *this; + } + + assert(sym_lhs.memRows() >= sym_lhs.rows() && "multiply_sym(): LHS LDA < M"); + assert(rhs.memRows() >= rhs.rows() && "multiply_sym(): RHS LDA < M"); + assert(_mem_rows >= _rows && "multiply_sym(): result LDA < M"); + + BLAS_FUNC(dsymm)(CblasColMajor, CblasLeft, CblasLower, + m, n, 1.0, + sym_lhs.getbuffer(), sym_lhs.memRows(), + rhs.getbuffer(), rhs.memRows(), + 0.0, _buffer, _mem_rows); + + return *this; +} + // Transpose() matrix_2d matrix_2d::transpose(const matrix_2d& matA) { if ((matA.columns() != _rows) || (matA.rows() != _cols)) @@ -967,9 +1521,19 @@ matrix_2d matrix_2d::transpose() { return m; } // Transpose() -// computes and retains the maximum value in the matrix double matrix_2d::compute_maximum_value() { _maxvalCol = _maxvalRow = 0; + if (_packed) { + for (UINT32 j = 0; j < _rows; ++j) { + for (UINT32 i = j; i < _rows; ++i) { + if (fabs(get(i, j)) > fabs(get(_maxvalRow, _maxvalCol))) { + _maxvalRow = i; + _maxvalCol = j; + } + } + } + return get(_maxvalRow, _maxvalCol); + } UINT32 col, row; for (row = 0; row < _rows; ++row) { for (col = 0; col < _cols; col++) { diff --git a/dynadjust/include/math/dnamatrix_contiguous.hpp b/dynadjust/include/math/dnamatrix_contiguous.hpp index be4d79c85..c79fea5d9 100644 --- a/dynadjust/include/math/dnamatrix_contiguous.hpp +++ b/dynadjust/include/math/dnamatrix_contiguous.hpp @@ -23,6 +23,7 @@ #define DNAMATRIX_CONTIGUOUS_H_ /// \cond +#include #include /// \endcond @@ -165,14 +166,26 @@ static_assert(sizeof(lapack_int) == 4, "LP64 interface requires 32-bit integers" extern "C" { void LAPACK_FUNC(dpotrf)(const char* uplo, const lapack_int* n, double* a, const lapack_int* lda, lapack_int* info); void LAPACK_FUNC(dpotri)(const char* uplo, const lapack_int* n, double* a, const lapack_int* lda, lapack_int* info); +void LAPACK_FUNC(dpptrf)(const char* uplo, const lapack_int* n, double* ap, lapack_int* info); +void LAPACK_FUNC(dpptri)(const char* uplo, const lapack_int* n, double* ap, lapack_int* info); void LAPACK_FUNC(dsytrf)(const char* uplo, const lapack_int* n, double* a, const lapack_int* lda, lapack_int* ipiv, double* work, const lapack_int* lwork, lapack_int* info); void LAPACK_FUNC(dsytri)(const char* uplo, const lapack_int* n, double* a, const lapack_int* lda, const lapack_int* ipiv, double* work, lapack_int* info); +void LAPACK_FUNC(dpotrs)(const char* uplo, const lapack_int* n, const lapack_int* nrhs, + const double* a, const lapack_int* lda, double* b, const lapack_int* ldb, lapack_int* info); void BLAS_FUNC(dgemm)(const enum CBLAS_ORDER ORDER, const enum CBLAS_TRANSPOSE TRANSA, const enum CBLAS_TRANSPOSE TRANSB, const lapack_int M, const lapack_int N, const lapack_int K, const double ALPHA, const double* A, const lapack_int LDA, const double* B, const lapack_int LDB, const double BETA, double* C, const lapack_int LDC); +void BLAS_FUNC(dsymm)(const enum CBLAS_ORDER ORDER, const enum CBLAS_SIDE SIDE, const enum CBLAS_UPLO UPLO, + const lapack_int M, const lapack_int N, + const double ALPHA, const double* A, const lapack_int LDA, const double* B, const lapack_int LDB, + const double BETA, double* C, const lapack_int LDC); +void BLAS_FUNC(dspmv)(const enum CBLAS_ORDER ORDER, const enum CBLAS_UPLO UPLO, + const lapack_int N, const double ALPHA, const double* AP, + const double* X, const lapack_int INCX, + const double BETA, double* Y, const lapack_int INCY); } #endif @@ -187,6 +200,10 @@ class MatrixInversionFailure : public std::runtime_error { using std::runtime_error::runtime_error; }; +// Set/get the maximum BLAS thread count used by matrix operations. +void set_max_blas_threads(int n); +int get_max_blas_threads(); + class matrix_2d; typedef std::vector v_mat_2d, *pv_mat_2d; typedef v_mat_2d::iterator _it_v_mat_2d; @@ -217,12 +234,62 @@ class matrix_2d : public new_handler_support { inline UINT32 columns() const { return _cols; } inline double* getbuffer() const { return _buffer; } + inline double dense_get(const UINT32& row, const UINT32& column) const { + assert(_buffer != nullptr); + assert(!_packed && "dense_get(): matrix must not use packed storage"); + assert(!_symmetric && "dense_get(): matrix must not use symmetric element redirection"); + assert(row < _rows && "dense_get(): row out of bounds"); + assert(column < _cols && "dense_get(): column out of bounds"); + return _buffer[static_cast(column) * _mem_rows + row]; + } + + inline void dense_put(const UINT32& row, const UINT32& column, const double& value) { + assert(_buffer != nullptr); + assert(!_packed && "dense_put(): matrix must not use packed storage"); + assert(!_symmetric && "dense_put(): matrix must not use symmetric element redirection"); + assert(row < _rows && "dense_put(): row out of bounds"); + assert(column < _cols && "dense_put(): column out of bounds"); + _buffer[static_cast(column) * _mem_rows + row] = value; + } + + inline void dense_add(const UINT32& row, const UINT32& column, const double& increment) { + assert(_buffer != nullptr); + assert(!_packed && "dense_add(): matrix must not use packed storage"); + assert(!_symmetric && "dense_add(): matrix must not use symmetric element redirection"); + assert(row < _rows && "dense_add(): row out of bounds"); + assert(column < _cols && "dense_add(): column out of bounds"); + _buffer[static_cast(column) * _mem_rows + row] += increment; + } + + inline double* dense_ptr(const UINT32& row, const UINT32& column) const { + assert(_buffer != nullptr); + assert(!_packed && "dense_ptr(): matrix must not use packed storage"); + assert(!_symmetric && "dense_ptr(): matrix must not use symmetric element redirection"); + assert(row < _rows && "dense_ptr(): row out of bounds"); + assert(column < _cols && "dense_ptr(): column out of bounds"); + return _buffer + static_cast(column) * _mem_rows + row; + } + // element retrieval // see DNAMATRIX_ROW_WISE inline double& get(const UINT32& row, const UINT32& column) const { + assert(_buffer != nullptr); + assert(row < _mem_rows && "get(): row out of bounds"); + assert(column < _mem_cols && "get(): column out of bounds"); + if (_packed) { + UINT32 i = row, j = column; + if (i < j) { i = column; j = row; } + return _buffer[packed_index(_rows, i, j)]; + } + if (_symmetric && row < column) + return DNAMATRIX_ELEMENT(_buffer, _mem_rows, _mem_cols, column, row); return DNAMATRIX_ELEMENT(_buffer, _mem_rows, _mem_cols, row, column); } inline double* getbuffer(const UINT32& row, const UINT32& column) const { + assert(_buffer != nullptr); + assert(!_packed && "getbuffer(row,col): not valid for packed storage"); + assert(row < _mem_rows && "getbuffer(): row out of bounds"); + assert(column < _mem_cols && "getbuffer(): column out of bounds"); return _buffer + DNAMATRIX_INDEX(_mem_rows, _mem_cols, row, column); } @@ -236,9 +303,29 @@ class matrix_2d : public new_handler_support { inline UINT32 maxvalueCol() const { return _maxvalCol; } inline double* getelementref(const UINT32& row, const UINT32& column) const { + assert(_buffer != nullptr); + assert(row < _mem_rows && "getelementref(): row out of bounds"); + assert(column < _mem_cols && "getelementref(): column out of bounds"); + if (_packed) { + UINT32 i = row, j = column; + if (i < j) { i = column; j = row; } + return &_buffer[packed_index(_rows, i, j)]; + } + if (_symmetric && row < column) + return &(DNAMATRIX_ELEMENT(_buffer, _mem_rows, _mem_cols, column, row)); return &(DNAMATRIX_ELEMENT(_buffer, _mem_rows, _mem_cols, row, column)); } inline double* getelementref(const UINT32& row, const UINT32& column) { + assert(_buffer != nullptr); + assert(row < _mem_rows && "getelementref(): row out of bounds"); + assert(column < _mem_cols && "getelementref(): column out of bounds"); + if (_packed) { + UINT32 i = row, j = column; + if (i < j) { i = column; j = row; } + return &_buffer[packed_index(_rows, i, j)]; + } + if (_symmetric && row < column) + return &(DNAMATRIX_ELEMENT(_buffer, _mem_rows, _mem_cols, column, row)); return &(DNAMATRIX_ELEMENT(_buffer, _mem_rows, _mem_cols, row, column)); } @@ -250,34 +337,139 @@ class matrix_2d : public new_handler_support { inline void maxvalueCol(const UINT32& c) { _maxvalCol = c; } inline void put(const UINT32& row, const UINT32& column, const double& value) { + assert(_buffer != nullptr); + assert(row < _mem_rows && "put(): row out of bounds"); + assert(column < _mem_cols && "put(): column out of bounds"); + if (_packed) { + UINT32 i = row, j = column; + if (i < j) { i = column; j = row; } + _buffer[packed_index(_rows, i, j)] = value; + return; + } DNAMATRIX_ELEMENT(_buffer, _mem_rows, _mem_cols, row, column) = value; } inline UINT32 matrixType() const { return _matrixType; } inline void matrixType(const UINT32 t) { _matrixType = t; } + inline bool is_symmetric() const { return _symmetric; } + inline void set_symmetric(bool s) { + assert((!s || _rows == _cols) && "set_symmetric(true): matrix must be square"); + _symmetric = s; + } + + inline bool is_packed() const { return _packed; } + + static inline std::size_t packed_index(UINT32 n, UINT32 i, UINT32 j) { + return static_cast(j) * n - static_cast(j) * (j - 1) / 2 + (i - j); + } + + static inline std::size_t packed_size(UINT32 n) { + return static_cast(n) * (n + 1) / 2; + } + // Matrix functions - void copyelements(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d& src, const UINT32& row_src, - const UINT32& column_src, const UINT32& rows, const UINT32& columns); - void copyelements(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d* src, const UINT32& row_src, - const UINT32& column_src, const UINT32& rows, const UINT32& columns); + inline void copyelements(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d& src, + const UINT32& row_src, const UINT32& column_src, + const UINT32& rows, const UINT32& columns) { + assert(_buffer != nullptr && src._buffer != nullptr); + assert(row_dest + rows <= _mem_rows && "copyelements(): dest row overflow"); + assert(column_dest + columns <= _mem_cols && "copyelements(): dest col overflow"); + assert(row_src + rows <= src._mem_rows && "copyelements(): src row overflow"); + assert(column_src + columns <= src._mem_cols && "copyelements(): src col overflow"); + if (rows == 3 && columns == 3 && !src._symmetric && !src._packed && !_packed) { + for (UINT32 c = 0; c < 3; ++c) { + double* dst = _buffer + static_cast(column_dest + c) * _mem_rows + row_dest; + const double* s = src._buffer + static_cast(column_src + c) * src._mem_rows + row_src; + dst[0] = s[0]; dst[1] = s[1]; dst[2] = s[2]; + } + return; + } + copyelements_generic(row_dest, column_dest, src, row_src, column_src, rows, columns); + } + void copyelements_generic(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d& src, + const UINT32& row_src, const UINT32& column_src, + const UINT32& rows, const UINT32& columns); + inline void copyelements(const UINT32& row_dest, const UINT32& column_dest, const matrix_2d* src, + const UINT32& row_src, const UINT32& column_src, + const UINT32& rows, const UINT32& columns) { + copyelements(row_dest, column_dest, *src, row_src, column_src, rows, columns); + } inline void elementadd(const UINT32& row, const UINT32& column, const double& increment) { + assert(row < _rows && column < _cols && "elementadd(): out of bounds"); + if (_packed && row < column) return; *getelementref(row, column) += increment; } + inline void lower_add(const UINT32& row, const UINT32& column, const double& increment) { + assert(_buffer != nullptr); + assert(row < _rows && column < _cols && "lower_add(): out of bounds"); + if (_packed) { + if (row < column) return; + _buffer[packed_index(_rows, row, column)] += increment; + return; + } + if (_symmetric && row < column) { + _buffer[static_cast(row) * _mem_rows + column] += increment; + return; + } + _buffer[static_cast(column) * _mem_rows + row] += increment; + } + inline void elementsubtract(const UINT32& row, const UINT32& column, const double& decrement) { + assert(row < _rows && column < _cols && "elementsubtract(): out of bounds"); + if (_packed && row < column) return; *getelementref(row, column) -= decrement; } inline void elementmultiply(const UINT32& row, const UINT32& column, const double& scale) { + assert(row < _rows && column < _cols && "elementmultiply(): out of bounds"); *getelementref(row, column) *= scale; } - void blockadd(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, const UINT32& row_src, - const UINT32& col_src, const UINT32& rows, const UINT32& cols); - void blockTadd(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, const UINT32& row_src, - const UINT32& col_src, const UINT32& rows, const UINT32& cols); + inline void blockadd(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, + const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols) { + assert(_buffer != nullptr && mat_src._buffer != nullptr); + assert(row_dest + rows <= _mem_rows && "blockadd(): dest row overflow"); + assert(col_dest + cols <= _mem_cols && "blockadd(): dest col overflow"); + assert(row_src + rows <= mat_src._mem_rows && "blockadd(): src row overflow"); + assert(col_src + cols <= mat_src._mem_cols && "blockadd(): src col overflow"); + if (rows == 3 && cols == 3 && !mat_src._symmetric && !mat_src._packed && !_packed) { + for (UINT32 c = 0; c < 3; ++c) { + double* dst = _buffer + static_cast(col_dest + c) * _mem_rows + row_dest; + const double* src = mat_src._buffer + static_cast(col_src + c) * mat_src._mem_rows + row_src; + dst[0] += src[0]; dst[1] += src[1]; dst[2] += src[2]; + } + return; + } + blockadd_generic(row_dest, col_dest, mat_src, row_src, col_src, rows, cols); + } + void blockadd_generic(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, + const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols); + + inline void blockTadd(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, + const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols) { + assert(_buffer != nullptr && mat_src._buffer != nullptr); + assert(row_dest + rows <= _mem_rows && "blockTadd(): dest row overflow"); + assert(col_dest + cols <= _mem_cols && "blockTadd(): dest col overflow"); + assert(row_src + cols <= mat_src._mem_rows && "blockTadd(): src row overflow (transposed)"); + assert(col_src + rows <= mat_src._mem_cols && "blockTadd(): src col overflow (transposed)"); + if (rows == 3 && cols == 3 && !_packed) { + for (UINT32 c = 0; c < 3; ++c) { + double* dst = _buffer + static_cast(col_dest + c) * _mem_rows + row_dest; + const std::size_t smr = mat_src._mem_rows; + const double* src_r0 = mat_src._buffer + static_cast(row_src) * smr + col_src + c; + dst[0] += src_r0[0]; + dst[1] += src_r0[smr]; + dst[2] += src_r0[2 * smr]; + } + return; + } + blockTadd_generic(row_dest, col_dest, mat_src, row_src, col_src, rows, cols); + } + void blockTadd_generic(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, + const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols); void blocksubtract(const UINT32& row_dest, const UINT32& col_dest, const matrix_2d& mat_src, const UINT32& row_src, const UINT32& col_src, const UINT32& rows, const UINT32& cols); @@ -287,13 +479,19 @@ class matrix_2d : public new_handler_support { matrix_2d multiply(const char* lhs_trans, const matrix_2d& rhs, const char* rhs_trans); // multiplication matrix_2d multiply(const matrix_2d& lhs, const char* lhs_trans, const matrix_2d& rhs, const char* rhs_trans); // multiplication + // C = sym_lhs * rhs using dsymm (sym_lhs must be symmetric, lower triangle populated) + matrix_2d multiply_sym(const matrix_2d& sym_lhs, const matrix_2d& rhs); matrix_2d sweepinverse(); // Sweep inverse (good for rotation matrices) - matrix_2d cholesky_inverse(bool LOWER_IS_CLEARED = false); // Cholesky inverse + matrix_2d cholesky_inverse(bool LOWER_IS_CLEARED = false, bool mark_symmetric = false); // Cholesky inverse + matrix_2d cholesky_factor(bool LOWER_IS_CLEARED = false); // Cholesky factor only (dpotrf), no inverse + void cholesky_solve(matrix_2d& rhs, bool LOWER_IS_CLEARED = false); // Solve from pre-factored matrix (dpotrs) + double dot(const matrix_2d& other) const; // Inner product of two column vectors matrix_2d transpose(const matrix_2d&); // Transpose matrix_2d transpose(); // '' matrix_2d scale(const double& scalar); // scale + void scale_symmetric_diagonal(const double* diag); // overloaded operators // equality @@ -347,6 +545,7 @@ class matrix_2d : public new_handler_support { void setsize(const UINT32& rows, const UINT32& columns); // sets matrix size to rows * columns only (buffer not allocated any memory) void redim(const UINT32& rows, const UINT32& columns); // redimensions matrix to rows * columns + void redim_packed(const UINT32& n); // redimensions to packed symmetric n×n void replace(const UINT32& rowstart, const UINT32& columnstart, const matrix_2d& newmat); void replace(const UINT32& rowstart, const UINT32& columnstart, const UINT32& rows, const UINT32& columns, const matrix_2d& newmat); @@ -369,6 +568,14 @@ class matrix_2d : public new_handler_support { // Writing to memory mapped file void WriteMappedFileRegion(void* addr); + // In-place mmap: attach _buffer directly to mmap data region (no memcpy) + void AttachMappedFileRegion(void* addr); + + // In-place mmap: write footer back, detach _buffer + void DetachMappedFileRegion(void* addr); + + inline bool owns_buffer() const { return _owns_buffer; } + // debug #ifdef _MSDEBUG void trace(const std::string& comment, const std::string& format) const; @@ -376,12 +583,14 @@ class matrix_2d : public new_handler_support { const UINT32& row_begin, const UINT32& col_begin, const UINT32& rows, const UINT32& columns) const; #endif + void deallocate(); + private: inline std::size_t buffersize() const { + if (_packed) + return packed_size(_mem_rows) * sizeof(double); return static_cast(_mem_rows) * static_cast(_mem_cols) * sizeof(double); } - - void deallocate(); void buy(const UINT32& rows, const UINT32& columns, double** mem_space); void copybuffer(const UINT32& rows, const UINT32& columns, const matrix_2d& oldmat); void copybuffer(const UINT32& rowstart, const UINT32& columnstart, const UINT32& rows, const UINT32& columns, @@ -395,11 +604,14 @@ class matrix_2d : public new_handler_support { UINT32 _cols; // number of actual cols UINT32 _rows; // number of actual rows double* _buffer; // matrix buffer elements + bool _owns_buffer; // true if _buffer is heap-allocated (false when attached to mmap) UINT32 _maxvalCol; // col of max value UINT32 _maxvalRow; // row of max value UINT32 _matrixType; // full, upper/lower, sparse + bool _symmetric; // only lower triangle populated (upper is mirror) + bool _packed; // packed column-major lower-triangle storage (n*(n+1)/2 elements) }; } // namespace math diff --git a/dynadjust/include/measurement_types/dnaangle.cpp b/dynadjust/include/measurement_types/dnaangle.cpp index 5372f4d67..59b795bda 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_observation_epoch = it_msr->observation_epoch; m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); @@ -278,6 +279,7 @@ void CDnaAngle::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) c measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&measRecord), sizeof(measurement_t)); } diff --git a/dynadjust/include/measurement_types/dnacoordinate.cpp b/dynadjust/include/measurement_types/dnacoordinate.cpp index d7d488496..27f5b20ca 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_observation_epoch = it_msr->observation_epoch; m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); @@ -229,6 +230,7 @@ void CDnaCoordinate::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrInd measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&measRecord), sizeof(measurement_t)); } diff --git a/dynadjust/include/measurement_types/dnadirection.cpp b/dynadjust/include/measurement_types/dnadirection.cpp index 01060e397..f204754bb 100644 --- a/dynadjust/include/measurement_types/dnadirection.cpp +++ b/dynadjust/include/measurement_types/dnadirection.cpp @@ -65,6 +65,7 @@ CDnaDirection::CDnaDirection(CDnaDirection&& d) m_msr_db_map = d.m_msr_db_map; m_epoch = d.m_epoch; + m_observation_epoch = d.m_observation_epoch; } // move assignment operator @@ -599,13 +600,14 @@ UINT32 CDnaDirection::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_m m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); - + return 0; } - + void CDnaDirection::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) const { @@ -636,6 +638,7 @@ void CDnaDirection::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrInde measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&measRecord), sizeof(measurement_t)); } diff --git a/dynadjust/include/measurement_types/dnadirectionset.cpp b/dynadjust/include/measurement_types/dnadirectionset.cpp index 3e72c344d..f412adb03 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_observation_epoch = it_msr->observation_epoch; m_sourceFileIndex = it_msr->sourceFileIndex; m_vTargetDirections.clear(); @@ -455,6 +456,7 @@ void CDnaDirectionSet::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrI measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&measRecord), sizeof(measurement_t)); diff --git a/dynadjust/include/measurement_types/dnadistance.cpp b/dynadjust/include/measurement_types/dnadistance.cpp index f02a2f312..c50d1fb3d 100644 --- a/dynadjust/include/measurement_types/dnadistance.cpp +++ b/dynadjust/include/measurement_types/dnadistance.cpp @@ -283,6 +283,7 @@ UINT32 CDnaDistance::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_ms m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); @@ -313,6 +314,7 @@ void CDnaDistance::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&measRecord), sizeof(measurement_t)); } diff --git a/dynadjust/include/measurement_types/dnagpsbaseline.cpp b/dynadjust/include/measurement_types/dnagpsbaseline.cpp index f1d5c501a..68e203102 100644 --- a/dynadjust/include/measurement_types/dnagpsbaseline.cpp +++ b/dynadjust/include/measurement_types/dnagpsbaseline.cpp @@ -76,7 +76,8 @@ CDnaGpsBaseline::CDnaGpsBaseline(CDnaGpsBaseline&& g) m_referenceFrame = g.m_referenceFrame; m_epsgCode = g.m_epsgCode; m_epoch = g.m_epoch; - + m_observation_epoch = g.m_observation_epoch; + m_dX = g.m_dX; m_dY = g.m_dY; m_dZ = g.m_dZ; @@ -114,6 +115,7 @@ CDnaGpsBaseline& CDnaGpsBaseline::operator= (CDnaGpsBaseline&& rhs) m_referenceFrame = rhs.m_referenceFrame; m_epsgCode = rhs.m_epsgCode; m_epoch = rhs.m_epoch; + m_observation_epoch = rhs.m_observation_epoch; m_dX = rhs.m_dX; m_dY = rhs.m_dY; @@ -371,6 +373,7 @@ UINT32 CDnaGpsBaseline::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it m_dVscale = it_msr->scale4; m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_epsgCode = it_msr->epsgCode; m_referenceFrame = datumFromEpsgString(it_msr->epsgCode); @@ -437,6 +440,7 @@ void CDnaGpsBaseline::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIn snprintf(measRecord.epsgCode, sizeof(measRecord.epsgCode), "%s", m_epsgCode.substr(0, STN_EPSG_WIDTH).c_str()); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); // X measRecord.measAdj = m_measAdj; @@ -485,7 +489,7 @@ void CDnaGpsBaseline::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIn // now write covariance elements std::vector::const_iterator _it_cov; for (_it_cov=m_vGpsCovariances.begin(); _it_cov!=m_vGpsCovariances.end(); ++_it_cov) - _it_cov->WriteBinaryMsr(binary_stream, msrIndex, m_epsgCode, m_epoch); + _it_cov->WriteBinaryMsr(binary_stream, msrIndex, m_epsgCode, m_epoch, m_observation_epoch); } void CDnaGpsBaseline::SerialiseDatabaseMap(std::ofstream* os) @@ -650,17 +654,18 @@ CDnaGpsBaselineCluster::CDnaGpsBaselineCluster(CDnaGpsBaselineCluster&& g) m_dVscale = g.m_dVscale; m_lclusterID = g.m_lclusterID; m_MSmeasurementStations = g.m_MSmeasurementStations; - + m_referenceFrame = g.m_referenceFrame; m_epsgCode = g.m_epsgCode; m_epoch = g.m_epoch; + m_observation_epoch = g.m_observation_epoch; m_msr_db_map = g.m_msr_db_map; m_dbidmap = g.m_dbidmap; } -// move assignment operator +// move assignment operator CDnaGpsBaselineCluster& CDnaGpsBaselineCluster::operator= (CDnaGpsBaselineCluster&& rhs) { // check for assignment to self! @@ -674,6 +679,7 @@ CDnaGpsBaselineCluster& CDnaGpsBaselineCluster::operator= (CDnaGpsBaselineCluste m_referenceFrame = rhs.m_referenceFrame; m_epsgCode = rhs.m_epsgCode; m_epoch = rhs.m_epoch; + m_observation_epoch = rhs.m_observation_epoch; m_dPscale = rhs.m_dPscale; m_dLscale = rhs.m_dLscale; @@ -891,6 +897,7 @@ UINT32 CDnaGpsBaselineCluster::SetMeasurementRec(const vstn_t& binaryStn, it_vms m_referenceFrame = datumFromEpsgString(it_msr->epsgCode); m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_epsgCode = it_msr->epsgCode; m_sourceFileIndex = it_msr->sourceFileIndex; diff --git a/dynadjust/include/measurement_types/dnagpspoint.cpp b/dynadjust/include/measurement_types/dnagpspoint.cpp index 0696c251a..0f2e8a05a 100644 --- a/dynadjust/include/measurement_types/dnagpspoint.cpp +++ b/dynadjust/include/measurement_types/dnagpspoint.cpp @@ -78,6 +78,7 @@ CDnaGpsPoint::CDnaGpsPoint(CDnaGpsPoint&& p) m_referenceFrame = p.m_referenceFrame; m_epsgCode = p.m_epsgCode; m_epoch = p.m_epoch; + m_observation_epoch = p.m_observation_epoch; m_dX = p.m_dX; m_dY = p.m_dY; @@ -95,7 +96,7 @@ CDnaGpsPoint::CDnaGpsPoint(CDnaGpsPoint&& p) m_dVscale = p.m_dVscale; SetCoordType(p.m_strCoordType); - + m_lclusterID = p.m_lclusterID; m_MSmeasurementStations = p.m_MSmeasurementStations; @@ -117,6 +118,7 @@ CDnaGpsPoint& CDnaGpsPoint::operator= (CDnaGpsPoint&& rhs) m_referenceFrame = rhs.m_referenceFrame; m_epsgCode = rhs.m_epsgCode; m_epoch = rhs.m_epoch; + m_observation_epoch = rhs.m_observation_epoch; m_dX = rhs.m_dX; m_dY = rhs.m_dY; @@ -134,7 +136,7 @@ CDnaGpsPoint& CDnaGpsPoint::operator= (CDnaGpsPoint&& rhs) m_dVscale = rhs.m_dVscale; SetCoordType(rhs.m_strCoordType); - + m_lclusterID = rhs.m_lclusterID; m_MSmeasurementStations = rhs.m_MSmeasurementStations; @@ -478,12 +480,13 @@ UINT32 CDnaGpsPoint::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_ms m_dVscale = it_msr->scale4; m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_epsgCode = it_msr->epsgCode; m_referenceFrame = datumFromEpsgCode(LongFromString(it_msr->epsgCode)); m_lclusterID = it_msr->clusterID; m_MSmeasurementStations = (MEASUREMENT_STATIONS)it_msr->measurementStations; - + // X, sigmaXX m_bIgnore = it_msr->ignore; m_strType = it_msr->measType; @@ -548,6 +551,7 @@ void CDnaGpsPoint::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex snprintf(measRecord.epsgCode, sizeof(measRecord.epsgCode), "%s", m_epsgCode.substr(0, STN_EPSG_WIDTH).c_str()); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); // X measRecord.measAdj = m_measAdj; @@ -596,7 +600,7 @@ void CDnaGpsPoint::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex // now write covariance elements std::vector::const_iterator _it_cov; for (_it_cov=m_vPointCovariances.begin(); _it_cov!=m_vPointCovariances.end(); ++_it_cov) - _it_cov->WriteBinaryMsr(binary_stream, msrIndex, m_epsgCode, m_epoch); + _it_cov->WriteBinaryMsr(binary_stream, msrIndex, m_epsgCode, m_epoch, m_observation_epoch); } @@ -756,6 +760,7 @@ CDnaGpsPointCluster::CDnaGpsPointCluster(CDnaGpsPointCluster&& p) m_referenceFrame = p.m_referenceFrame; m_epsgCode = p.m_epsgCode; m_epoch = p.m_epoch; + m_observation_epoch = p.m_observation_epoch; m_msr_db_map = p.m_msr_db_map; @@ -776,6 +781,7 @@ CDnaGpsPointCluster& CDnaGpsPointCluster::operator= (CDnaGpsPointCluster&& rhs) m_referenceFrame = rhs.m_referenceFrame; m_epsgCode = rhs.m_epsgCode; m_epoch = rhs.m_epoch; + m_observation_epoch = rhs.m_observation_epoch; m_dPscale = rhs.m_dPscale; m_dLscale = rhs.m_dLscale; @@ -1014,6 +1020,7 @@ UINT32 CDnaGpsPointCluster::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t m_referenceFrame = datumFromEpsgCode(LongFromString(it_msr->epsgCode)); m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_epsgCode = it_msr->epsgCode; m_sourceFileIndex = it_msr->sourceFileIndex; diff --git a/dynadjust/include/measurement_types/dnaheight.cpp b/dynadjust/include/measurement_types/dnaheight.cpp index ae86d6c41..3ffecad56 100644 --- a/dynadjust/include/measurement_types/dnaheight.cpp +++ b/dynadjust/include/measurement_types/dnaheight.cpp @@ -184,6 +184,7 @@ UINT32 CDnaHeight::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_msr, m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); @@ -213,6 +214,7 @@ void CDnaHeight::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex) measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&measRecord), sizeof(measurement_t)); } diff --git a/dynadjust/include/measurement_types/dnaheightdifference.cpp b/dynadjust/include/measurement_types/dnaheightdifference.cpp index ba7e26db0..c8c15a8a1 100644 --- a/dynadjust/include/measurement_types/dnaheightdifference.cpp +++ b/dynadjust/include/measurement_types/dnaheightdifference.cpp @@ -220,6 +220,7 @@ UINT32 CDnaHeightDifference::SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_ m_dStdDev = sqrt(it_msr->term2); m_epoch = it_msr->epoch; + m_observation_epoch = it_msr->observation_epoch; m_sourceFileIndex = it_msr->sourceFileIndex; CDnaMeasurement::SetDatabaseMap(*dbidmap); @@ -248,6 +249,7 @@ void CDnaHeightDifference::WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 measRecord.fileOrder = ((*msrIndex)++); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&measRecord), sizeof(measurement_t)); } diff --git a/dynadjust/include/measurement_types/dnameasurement.cpp b/dynadjust/include/measurement_types/dnameasurement.cpp index 6608a4881..6a76d77ea 100644 --- a/dynadjust/include/measurement_types/dnameasurement.cpp +++ b/dynadjust/include/measurement_types/dnameasurement.cpp @@ -221,19 +221,20 @@ UINT32 CDnaCovariance::SetMeasurementRec(const vstn_t&, it_vmsr_t& it_msr) } -void CDnaCovariance::WriteBinaryMsr(std::ofstream *binary_stream, PUINT32 msrIndex, const std::string& epsgCode, const std::string& epoch) const +void CDnaCovariance::WriteBinaryMsr(std::ofstream *binary_stream, PUINT32 msrIndex, const std::string& epsgCode, const std::string& epoch, const std::string& observation_epoch) const { *msrIndex += 3; measurement_t measRecord; // Common measRecord.measType = GetTypeC(); - measRecord.station1 = m_lstn1Index; - measRecord.station2 = m_lstn2Index; + measRecord.station1 = m_lstn1Index; + measRecord.station2 = m_lstn2Index; measRecord.clusterID = m_lclusterID; - + snprintf(measRecord.epsgCode, sizeof(measRecord.epsgCode), "%s", epsgCode.substr(0, STN_EPSG_WIDTH).c_str()); snprintf(measRecord.epoch, sizeof(measRecord.epoch), "%s", epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + snprintf(measRecord.observation_epoch, sizeof(measRecord.observation_epoch), "%s", observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); // X measRecord.measStart = xCov; @@ -360,6 +361,7 @@ CDnaMeasurement::CDnaMeasurement() , m_epsgCode(DEFAULT_EPSG_S) , m_sourceFileIndex(0) , m_epoch("") + , m_observation_epoch("") , m_bInsufficient(false) { } @@ -388,6 +390,7 @@ CDnaMeasurement::CDnaMeasurement(CDnaMeasurement&& m) m_preAdjCorr = m.m_preAdjCorr; m_epoch = m.m_epoch; + m_observation_epoch = m.m_observation_epoch; m_epsgCode = m.m_epsgCode; m_sourceFileIndex = m.m_sourceFileIndex; @@ -419,6 +422,7 @@ CDnaMeasurement& CDnaMeasurement::operator= (CDnaMeasurement&& rhs) m_preAdjCorr = rhs.m_preAdjCorr; m_epoch = rhs.m_epoch; + m_observation_epoch = rhs.m_observation_epoch; m_epsgCode = rhs.m_epsgCode; m_sourceFileIndex = rhs.m_sourceFileIndex; @@ -501,6 +505,16 @@ void CDnaMeasurement::SerialiseDatabaseMap(std::ofstream* os) void CDnaMeasurement::SetEpoch(const std::string& epoch) { m_epoch = epoch; + // Default the (immutable) observation epoch to the reference-frame epoch + // when it has not been explicitly supplied. Legacy DNA v3.01 / DynaML + // files without thus behave identically to before. + if (m_observation_epoch.empty()) + m_observation_epoch = epoch; +} + +void CDnaMeasurement::SetObservationEpoch(const std::string& observation_epoch) +{ + m_observation_epoch = observation_epoch; } } // namespace measurements diff --git a/dynadjust/include/measurement_types/dnameasurement.hpp b/dynadjust/include/measurement_types/dnameasurement.hpp index 20bcf90e8..dd8c69a4a 100644 --- a/dynadjust/include/measurement_types/dnameasurement.hpp +++ b/dynadjust/include/measurement_types/dnameasurement.hpp @@ -144,15 +144,20 @@ typedef struct msr_t { memset(epsgCode, '\0', sizeof(epsgCode)); snprintf(epsgCode, sizeof(epsgCode), DEFAULT_EPSG_S); memset(epoch, '\0', sizeof(epoch)); + memset(observation_epoch, '\0', sizeof(observation_epoch)); } char measType; // 'A', 'S', 'X', ... , etc. char measStart; // Start of a measurement (0=start or X, 1=Y, 2=Z, 3=covX, 4=covY, 5=covZ) char measurementStations; // One-, two- or three-station measurement char epsgCode[7]; // epsg ID, i.e. NNNNN (where NNNNN is in the range 0-32767) - char epoch[12]; // date, i.e. "DD.MM.YYYY" (10 chars) - // if datum is dynamic, Epoch is YYYY MM DD - // if datum is static, Epoch is ignored + char epoch[STN_EPOCH_WIDTH]; // Epoch of Reference Frame, i.e. "DD.MM.YYYY" (10 chars) + // Mutable via dnareftran. + // If datum is static, Epoch is ignored. + char observation_epoch[STN_EPOCH_WIDTH]; // Epoch of Observation, i.e. "DD.MM.YYYY" (10 chars) + // Immutable under reftran. Timestamp at which the measurement + // was observed. Used for discontinuity station-instance + // allocation. Empty if not supplied. char coordType[4]; // "LLH", "UTM", ... , etc. bool ignore; UINT32 station1; // stations 1, 2 and 3 are indices to @@ -221,7 +226,7 @@ class CDnaCovariance inline bool GetIgnore() const { return m_bIgnore; } inline virtual UINT32 CalcBinaryRecordCount() const { return 3; } - void WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex, const std::string& epsgCode, const std::string& epoch) const; + void WriteBinaryMsr(std::ofstream* binary_stream, PUINT32 msrIndex, const std::string& epsgCode, const std::string& epoch, const std::string& observation_epoch) const; virtual UINT32 SetMeasurementRec(const vstn_t& binaryStn, it_vmsr_t& it_msr); virtual void WriteDynaMLMsr(std::ofstream* dynaml_stream) const; virtual void WriteDNAMsr(std::ofstream* dna_stream, @@ -374,6 +379,7 @@ class CDnaMeasurement virtual inline std::string GetReferenceFrame() const { return ""; } inline std::string GetEpoch() const { return m_epoch; } + inline std::string GetObservationEpoch() const { return m_observation_epoch; } virtual inline std::vector* GetBaselines_ptr() { return 0; } virtual inline std::vector* GetDirections_ptr() { return 0; } @@ -402,6 +408,7 @@ class CDnaMeasurement virtual void SetReferenceFrame(const std::string&) {} void SetEpoch(const std::string& epoch); + void SetObservationEpoch(const std::string& observation_epoch); virtual void SetLscale(const std::string&) {} virtual void SetLscale(const double&) {} @@ -485,7 +492,8 @@ class CDnaMeasurement UINT32 m_sourceFileIndex; std::string m_epoch; - + std::string m_observation_epoch; + msr_database_id_map m_msr_db_map; bool m_bInsufficient; diff --git a/dynadjust/include/measurement_types/dnastation.cpp b/dynadjust/include/measurement_types/dnastation.cpp index c728fa278..83a4d455f 100644 --- a/dynadjust/include/measurement_types/dnastation.cpp +++ b/dynadjust/include/measurement_types/dnastation.cpp @@ -120,7 +120,7 @@ CDnaStation::CDnaStation(const std::string& referenceframe, const std::string& e , m_fgeoidSep(0.), m_dmeridianDef(0.), m_dverticalDef(0.) , m_lfileOrder(0), m_lnameOrder(0) , m_zone(0), m_unusedStation(INVALID_STATION) - , m_referenceFrame(referenceframe), m_epoch(epoch) + , m_referenceFrame(referenceframe), m_epoch(epoch), m_observation_epoch(epoch) , m_constraintType(free_3D) { m_epsgCode = epsgStringFromName(referenceframe); @@ -170,6 +170,7 @@ CDnaStation::CDnaStation(const CDnaStation& newStation) m_referenceFrame = newStation.m_referenceFrame; m_epsgCode = newStation.m_epsgCode; m_epoch = newStation.m_epoch; + m_observation_epoch = newStation.m_observation_epoch; m_constraintType = newStation.m_constraintType; } @@ -215,6 +216,7 @@ CDnaStation::CDnaStation(const std::string& strName, const std::string& strConst m_referenceFrame = DEFAULT_DATUM; m_epsgCode = DEFAULT_EPSG_S; m_epoch = ""; + m_observation_epoch = ""; } CDnaStation& CDnaStation::operator =(const CDnaStation& rhs) @@ -261,9 +263,10 @@ CDnaStation& CDnaStation::operator =(const CDnaStation& rhs) m_referenceFrame = rhs.m_referenceFrame; m_epsgCode = rhs.m_epsgCode; m_epoch = rhs.m_epoch; + m_observation_epoch = rhs.m_observation_epoch; m_constraintType = rhs.m_constraintType; - + return *this; } @@ -675,6 +678,7 @@ void CDnaStation::WriteBinaryStn(std::ofstream* binary_stream, const UINT16 bUnu stationRecord.unusedStation = bUnused; strcpy(stationRecord.epoch, m_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); + strcpy(stationRecord.observation_epoch, m_observation_epoch.substr(0, STN_EPOCH_WIDTH).c_str()); strcpy(stationRecord.epsgCode, m_epsgCode.substr(0, STN_EPSG_WIDTH).c_str()); binary_stream->write(reinterpret_cast(&stationRecord), sizeof(station_t)); @@ -948,6 +952,7 @@ void CDnaStation::SetStationRec(const station_t& stationRecord) m_unusedStation = (stationRecord.unusedStation == VALID_STATION ? true : false); m_epoch = stationRecord.epoch; + m_observation_epoch = stationRecord.observation_epoch; m_epsgCode = stationRecord.epsgCode; m_referenceFrame = datumFromEpsgCode(LongFromString(m_epsgCode)); } diff --git a/dynadjust/include/measurement_types/dnastation.hpp b/dynadjust/include/measurement_types/dnastation.hpp index aa8d8452c..8051e6a8b 100644 --- a/dynadjust/include/measurement_types/dnastation.hpp +++ b/dynadjust/include/measurement_types/dnastation.hpp @@ -276,9 +276,18 @@ class CDnaStation inline std::string GetReferenceFrame() const { return m_referenceFrame; } inline std::string GetEpoch() const { return m_epoch; } - + inline std::string GetObservationEpoch() const { return m_observation_epoch; } + inline void SetReferenceFrame(const std::string& r) { m_referenceFrame = trimstr(r); } - inline void SetEpoch(const std::string& e) { m_epoch = trimstr(e); } + // Default observation_epoch to epoch when unset so legacy inputs preserve + // prior behaviour; once set, observation_epoch is immutable under reftran. + inline void SetEpoch(const std::string& e) + { + m_epoch = trimstr(e); + if (m_observation_epoch.empty()) + m_observation_epoch = m_epoch; + } + inline void SetObservationEpoch(const std::string& e) { m_observation_epoch = trimstr(e); } inline void SetEpsg(const std::string& e) { m_epsgCode = trimstr(e); } std::string m_strName; @@ -325,6 +334,7 @@ class CDnaStation std::string m_referenceFrame; std::string m_epsgCode; std::string m_epoch; + std::string m_observation_epoch; CONSTRAINT_TYPE m_constraintType; }; diff --git a/dynadjust/include/memory/dnafile_mapping.cpp b/dynadjust/include/memory/dnafile_mapping.cpp index 1a301b315..675e3c600 100644 --- a/dynadjust/include/memory/dnafile_mapping.cpp +++ b/dynadjust/include/memory/dnafile_mapping.cpp @@ -22,7 +22,13 @@ #include -namespace dynadjust { +#ifdef __linux__ +#include +#include +#include +#endif + +namespace dynadjust { namespace memory { //block_map_t::block_map_t() @@ -72,15 +78,33 @@ block_map_t::block_map_t(const block_map_t &p) void block_map_t::MapRegion(FileMapPtr file_map_ptr) { region_ptr_.reset( new boost::interprocess::mapped_region( - *file_map_ptr, - boost::interprocess::read_write, - region_offset_, + *file_map_ptr, + boost::interprocess::read_write, + region_offset_, data_size_ ) ); } +void block_map_t::AdviseSequential() { + if (region_ptr_) + region_ptr_->advise(boost::interprocess::mapped_region::advice_sequential); +} + + +void block_map_t::AdviseDontNeed() { + if (region_ptr_) + region_ptr_->advise(boost::interprocess::mapped_region::advice_dontneed); +} + + +void block_map_t::AdviseWillNeed() { + if (region_ptr_) + region_ptr_->advise(boost::interprocess::mapped_region::advice_willneed); +} + + // class to hold addresses and sizes for all matrices // in a vector of segmented blocks vmat_file_map::vmat_file_map() @@ -129,10 +153,54 @@ void vmat_file_map::CreateFileMap() } -void vmat_file_map::MapRegion(const UINT32 block) +void vmat_file_map::MapRegion(const UINT32 block) { vblockMapRegions_.at(block).MapRegion(file_map_ptr_); } + + +void vmat_file_map::AdviseRegion(const UINT32 block, boost::interprocess::mapped_region::advice_types advice) +{ + auto& region = vblockMapRegions_.at(block); +#ifdef __linux__ + // Boost's mapped_region::advise calls madvise() without + // page-aligning the address. Stage regions are packed + // end-to-end so only the first region's address is page- + // aligned by chance; madvise on the rest fails silently + // with EINVAL. Round start up and end down to whole + // pages, then call madvise directly. For DONTNEED also + // flush dirty pages asynchronously so the kernel can + // evict them promptly. + if (region.region_ptr_ && region.data_size_ > 0) { + const std::size_t page_size = + static_cast(sysconf(_SC_PAGESIZE)); + auto base = reinterpret_cast( + region.region_ptr_->get_address()); + std::uintptr_t aligned_start = + (base + page_size - 1) & ~(page_size - 1); + std::uintptr_t aligned_end = + (base + region.data_size_) & ~(page_size - 1); + if (aligned_end > aligned_start) { + void* addr = reinterpret_cast(aligned_start); + std::size_t len = aligned_end - aligned_start; + int madv = MADV_NORMAL; + switch (advice) { + case boost::interprocess::mapped_region::advice_sequential: + madv = MADV_SEQUENTIAL; break; + case boost::interprocess::mapped_region::advice_willneed: + madv = MADV_WILLNEED; break; + case boost::interprocess::mapped_region::advice_dontneed: + msync(addr, len, MS_ASYNC); + madv = MADV_DONTNEED; break; + default: break; + } + madvise(addr, len, madv); + return; + } + } +#endif + region.region_ptr_->advise(advice); +} } // namespace memory diff --git a/dynadjust/include/memory/dnafile_mapping.hpp b/dynadjust/include/memory/dnafile_mapping.hpp index ff33a8eb2..abd08859e 100644 --- a/dynadjust/include/memory/dnafile_mapping.hpp +++ b/dynadjust/include/memory/dnafile_mapping.hpp @@ -64,12 +64,17 @@ class block_map_t inline size_t GetDataSize() const { return data_size_; } inline size_t GetRegionOffset() const { return region_offset_; } inline size_t GetCumulativeRegionOffset() const { return region_offset_ + data_size_; } - + inline void SetDataSize(const size_t& size) { data_size_ = size; } inline void SetRegionOffset(const size_t& size) { region_offset_ = size; } - + void MapRegion(FileMapPtr file_map_ptr); + // Memory advisory hints for mapped regions + void AdviseSequential(); + void AdviseDontNeed(); + void AdviseWillNeed(); + size_t data_size_; // Size of this matrix. size_t region_offset_; // Offset from the beginning of the region MapRegPtr region_ptr_; // shared pointer to the region @@ -92,10 +97,11 @@ class vmat_file_map { void setnewFilePath(const std::string& filePath, bool remove_mapped_file); void CreateFileMap(); void MapRegion(const UINT32 block); + void AdviseRegion(const UINT32 block, boost::interprocess::mapped_region::advice_types advice); inline FileMapPtr getFileMapPtr() const { return file_map_ptr_; } - inline void* GetBlockRegionAddr(const UINT32 block) const { - return vblockMapRegions_.at(block).region_ptr_->get_address(); + inline void* GetBlockRegionAddr(const UINT32 block) const { + return vblockMapRegions_.at(block).region_ptr_->get_address(); } vmat_file_map(const vmat_file_map&); // prevent copying diff --git a/dynadjust/include/parameters/dnaconsts-datums.hpp b/dynadjust/include/parameters/dnaconsts-datums.hpp index 86fa177b1..48659d030 100644 --- a/dynadjust/include/parameters/dnaconsts-datums.hpp +++ b/dynadjust/include/parameters/dnaconsts-datums.hpp @@ -104,9 +104,15 @@ const UINT16 NAD83_CSRS_V6_i_xyz = 8250; // XYZ const UINT16 NAD83_CSRS_V7_i = 8254; //LatLonEht const UINT16 NAD83_CSRS_V7_i_xyz = 8253; // XYZ const UINT16 NAD83_CSRS_V8_i = 10413; // LatLonEht -const UINT16 NAD83_CSRS_V8_i_xyz = 10412; // XYZ - +const UINT16 NAD83_CSRS_V8_i_xyz = 10412; // XYZ +const UINT16 YACARE_ROUUSAMS_i = 4309; // LatLon (2D) only +const UINT16 SIRGAS_ROU98_i_xyz = 5379; // XYZ +const UINT16 SIRGAS_ROU98_i = 5380; // LatLonEht +const UINT16 SIRGAS_1995_i_xyz = 4974; // XYZ +const UINT16 SIRGAS_1995_i = 4975; // LatLonEht +const UINT16 SIRGAS_2000_i_xyz = 4988; // XYZ +const UINT16 SIRGAS_2000_i = 4989; // LatLonEht const char* const AGD66_c = "4202"; const char* const AGD84_c = "4203"; @@ -154,7 +160,13 @@ const char* const NAD83_CSRS_v6_c = "8250"; const char* const NAD83_CSRS_v7_c = "8253"; const char* const NAD83_CSRS_v8_c = "10412"; -const char* const AGD66_epoch = "01.01.1966"; +const char* const YACARE_ROUUSAMS_c = "4309"; +// epsg strings for SIRGAS provide XYZ definition only +const char* const SIRGAS_ROU98_c = "5379"; +const char* const SIRGAS_1995_c = "4974"; +const char* const SIRGAS_2000_c = "4988"; + +const char* const AGD66_epoch = "01.01.1966"; const char* const AGD84_epoch = "01.01.1984"; const char* const GDA94_epoch = "01.01.1994"; const char* const GDA2020_epoch = "01.01.2020"; @@ -193,6 +205,11 @@ const char* const NAD83_CSRS_V6_epoch = "01.01.2010"; const char* const NAD83_CSRS_V7_epoch = "01.01.2010"; const char* const NAD83_CSRS_V8_epoch = "01.01.2010"; +const char* const YACARE_ROUUSAMS_epoch = "01.01.1963"; // Yacare ROU by U.S. Army Map Service was established in 1963. +const char* const SIRGAS_ROU98_epoch = "03.07.1995"; // 1995.5 +const char* const SIRGAS_1995_epoch = "01.04.1995"; // 1995.4 +const char* const SIRGAS_2000_epoch = "01.04.2000"; // 2000.4 + const char* const AGD66_s = "AGD66"; const char* const AGD84_s = "AGD84"; const char* const GDA94_s = "GDA94"; @@ -280,5 +297,15 @@ const char* const NAD83_CSRS_V8_alias1_s = "NAD83 (CSRS) v8"; const char* const NAD83_CSRS_V8_alias2_s = "NAD83(CSRS)V8"; const char* const NAD83_CSRS_V8_alias3_s = "NAD83 (CSRS) V8"; +const char* const YACARE_ROUUSAMS_s = "YACARE ROUUSAMS"; // Yacare ROU by U.S. Army Map Service was established in 1963. +const char* const YACARE_ROUUSAMS_alias_s = "YACARE-ROUUSAMS"; +const char* const SIRGAS_ROU98_s = "SIRGAS ROU98"; +const char* const SIRGAS_ROU98_alias_s = "SIRGAS-ROU98"; +const char* const SIRGAS_1995_s = "SIRGAS95"; +const char* const SIRGAS_1995_alias1_s = "SIRGAS 95"; +const char* const SIRGAS_1995_alias2_s = "SIRGAS 1995"; +const char* const SIRGAS_2000_s = "SIRGAS2000"; +const char* const SIRGAS_2000_alias_s = "SIRGAS 2000"; + #endif // DNACONSTS_DATUMS_HPP diff --git a/dynadjust/include/parameters/dnadatumprojectionparam.hpp b/dynadjust/include/parameters/dnadatumprojectionparam.hpp index 7d2006e00..fa9689ba4 100644 --- a/dynadjust/include/parameters/dnadatumprojectionparam.hpp +++ b/dynadjust/include/parameters/dnadatumprojectionparam.hpp @@ -35,16 +35,20 @@ #include // GRS80 parameters -const double GRS80_a = 6378137.0; // Semi major axis (a) -const double GRS80_inv_f = 298.257222101; // Inverse flattening (1/f) +const double GRS80_a = 6378137.0; // Semi major axis (a) +const double GRS80_inv_f = 298.257222101; // Inverse flattening (1/f) // WGS84 parameters -const double WGS84_a = 6378137.0; // Semi major axis (a) -const double WGS84_inv_f = 298.25722360; // Inverse flattening (1/f) +const double WGS84_a = 6378137.0; // Semi major axis (a) +const double WGS84_inv_f = 298.25722360; // Inverse flattening (1/f) // ANS parameters const double ANS_a = 6378160.0; // Semi major axis (a) -const double ANS_inv_f = 298.25; // Inverse flattening (1/f) +const double ANS_inv_f = 298.25; // Inverse flattening (1/f) + +// International 1924 parameters +const double International24_a = 6378388.0; // Semi major axis (a) +const double International24_inv_f = 297.0; // Inverse flattening (1/f) // UTM parameters const double FALSE_E = 500000.0; // False Easting diff --git a/dynadjust/include/parameters/dnaepsg.hpp b/dynadjust/include/parameters/dnaepsg.hpp index 6dd36cd05..dfc9b9da4 100644 --- a/dynadjust/include/parameters/dnaepsg.hpp +++ b/dynadjust/include/parameters/dnaepsg.hpp @@ -218,6 +218,19 @@ U epsgCodeFromName(const S& datumName) iequals(datumName, NAD83_CSRS_V8_alias2_s) || iequals(datumName, NAD83_CSRS_V8_alias3_s)) return NAD83_CSRS_V8_i_xyz; + // SIRGAS + if (iequals(datumName, YACARE_ROUUSAMS_s)) + return YACARE_ROUUSAMS_i; + if (iequals(datumName, SIRGAS_ROU98_s) || + iequals(datumName, SIRGAS_ROU98_alias_s)) + return SIRGAS_ROU98_i_xyz; + if (iequals(datumName, SIRGAS_1995_s) || + iequals(datumName, SIRGAS_1995_alias1_s) || + iequals(datumName, SIRGAS_1995_alias2_s)) + return SIRGAS_1995_i_xyz; + if (iequals(datumName, SIRGAS_2000_s) || + iequals(datumName, SIRGAS_2000_alias_s)) + return SIRGAS_2000_i_xyz; std::stringstream ss; ss << " epsgCodeFromName: '" << datumName << "' is not a supported reference frame label." << std::endl; @@ -341,6 +354,18 @@ S epsgStringFromName(const S& datumName) case NAD83_CSRS_V8_i: case NAD83_CSRS_V8_i_xyz: return NAD83_CSRS_v8_c; + // SIRGAS + case YACARE_ROUUSAMS_i: + return YACARE_ROUUSAMS_c; + case SIRGAS_ROU98_i: + case SIRGAS_ROU98_i_xyz: + return SIRGAS_ROU98_c; + case SIRGAS_1995_i: + case SIRGAS_1995_i_xyz: + return SIRGAS_1995_c; + case SIRGAS_2000_i: + case SIRGAS_2000_i_xyz: + return SIRGAS_2000_c; } std::stringstream ss; @@ -388,6 +413,14 @@ bool isEpsgDatumStatic(const U& epsgCode) case NAD83_CSRS_V7_i_xyz: case NAD83_CSRS_V8_i: case NAD83_CSRS_V8_i_xyz: + //SIRGAS + case YACARE_ROUUSAMS_i: + case SIRGAS_ROU98_i: + case SIRGAS_ROU98_i_xyz: + case SIRGAS_1995_i: + case SIRGAS_1995_i_xyz: + case SIRGAS_2000_i: + case SIRGAS_2000_i_xyz: return true; // ITRF.... case ITRF1988_i_xyz: @@ -516,6 +549,13 @@ void spheroidFromEpsgCode(const U& epsgCode, epsg_spheroid& ellipsoid) case NAD83_CSRS_V7_i: case NAD83_CSRS_V8_i_xyz: case NAD83_CSRS_V8_i: + // SIRGAS + case SIRGAS_ROU98_i: // Note: epsg.org has incorrectly assigned WGS 84 ellipsoid to SIRGAS ROU98 + case SIRGAS_ROU98_i_xyz: + case SIRGAS_1995_i: + case SIRGAS_1995_i_xyz: + case SIRGAS_2000_i: + case SIRGAS_2000_i_xyz: // authority ellipsoid.authority_.first = "EPSG"; ellipsoid.authority_.second = "7019"; @@ -524,6 +564,16 @@ void spheroidFromEpsgCode(const U& epsgCode, epsg_spheroid& ellipsoid) ellipsoid.name_ = "GRS 1980"; ellipsoid.semi_major_ = GRS80_a; break; + // SIRGAS (OLD) + case YACARE_ROUUSAMS_i: + // authority + ellipsoid.authority_.first = "EPSG"; + ellipsoid.authority_.second = "7022"; + // ellipsoid params + ellipsoid.inv_flattening_ = International24_inv_f; + ellipsoid.name_ = "International 1924"; + ellipsoid.semi_major_ = International24_a; + break; // WGS84 case WGS84_transit_i: case WGS84_transit_i_xyz: @@ -670,6 +720,18 @@ std::string referenceepochFromEpsgCode(const U& epsgCode) case NAD83_CSRS_V8_i_xyz: case NAD83_CSRS_V8_i: return NAD83_CSRS_V8_epoch; + // SIRGAS + case YACARE_ROUUSAMS_i: + return YACARE_ROUUSAMS_epoch; + case SIRGAS_ROU98_i: + case SIRGAS_ROU98_i_xyz: + return SIRGAS_ROU98_epoch; + case SIRGAS_1995_i: + case SIRGAS_1995_i_xyz: + return SIRGAS_1995_epoch; + case SIRGAS_2000_i: + case SIRGAS_2000_i_xyz: + return SIRGAS_2000_epoch; default: std::stringstream ss; ss << " referenceepochFromEpsgCode: EPSG code '" << epsgCode << "' is not a supported EPSG code." << std::endl; @@ -799,6 +861,17 @@ S datumFromEpsgCode(const U& epsgCode) case NAD83_CSRS_V8_i_xyz: case NAD83_CSRS_V8_i: return NAD83_CSRS_V8_s; + case YACARE_ROUUSAMS_i: + return YACARE_ROUUSAMS_s; + case SIRGAS_ROU98_i_xyz: + case SIRGAS_ROU98_i: + return SIRGAS_ROU98_s; + case SIRGAS_1995_i_xyz: + case SIRGAS_1995_i: + return SIRGAS_1995_s; + case SIRGAS_2000_i_xyz: + case SIRGAS_2000_i: + return SIRGAS_2000_s; default: std::stringstream ss; ss << " datumFromEpsgCode: EPSG code '" << epsgCode << "' is not a supported EPSG code." << std::endl; @@ -908,6 +981,14 @@ bool validateEpsgCode(const U& epsgCode) case NAD83_CSRS_V7_i: case NAD83_CSRS_V8_i_xyz: case NAD83_CSRS_V8_i: + // SIRGAS + case YACARE_ROUUSAMS_i: + case SIRGAS_ROU98_i_xyz: + case SIRGAS_ROU98_i: + case SIRGAS_1995_i_xyz: + case SIRGAS_1995_i: + case SIRGAS_2000_i_xyz: + case SIRGAS_2000_i: return true; default: std::stringstream ss; diff --git a/dynadjust/include/parameters/dnaframesubstitutions.hpp b/dynadjust/include/parameters/dnaframesubstitutions.hpp index 8a8fb1ade..a2de4275f 100644 --- a/dynadjust/include/parameters/dnaframesubstitutions.hpp +++ b/dynadjust/include/parameters/dnaframesubstitutions.hpp @@ -381,6 +381,39 @@ class WGS84_ITRF2014 : public frame_substitutions_t virtual ~WGS84_ITRF2014() {} }; +// SIRGAS95 to ITRF94 +template +class SIRGAS95_ITRF94 : public frame_substitutions_t { + public: + SIRGAS95_ITRF94() { + frame_substitutions::frame_name = SIRGAS_1995_s; + frame_substitutions::frame_epsg = SIRGAS_1995_i_xyz; + frame_substitutions::frame_desc = ""; + frame_substitutions::substitute_name = ITRF1994_s; + frame_substitutions::substitute_epsg = ITRF1994_i_xyz; + + frame_substitutions::from_epoch = dateFromString("01.01.1900"); + frame_substitutions::to_epoch = boost::gregorian::day_clock::local_day() + boost::gregorian::years(100); + }; + virtual ~SIRGAS95_ITRF94() {} +}; + +// SIRGAS2000 to ITRF2000 +template +class SIRGAS2000_ITRF2000 : public frame_substitutions_t { + public: + SIRGAS2000_ITRF2000() { + frame_substitutions::frame_name = SIRGAS_2000_s; + frame_substitutions::frame_epsg = SIRGAS_2000_i_xyz; + frame_substitutions::frame_desc = ""; + frame_substitutions::substitute_name = ITRF2000_s; + frame_substitutions::substitute_epsg = ITRF2000_i_xyz; + + frame_substitutions::from_epoch = dateFromString("01.01.1900"); + frame_substitutions::to_epoch = boost::gregorian::day_clock::local_day() + boost::gregorian::years(100); + }; + virtual ~SIRGAS2000_ITRF2000() {} +}; // FUNCTIONS diff --git a/dynadjust/include/thread/dnathreading.hpp b/dynadjust/include/thread/dnathreading.hpp index 4db9a1d7a..30bde8ff2 100644 --- a/dynadjust/include/thread/dnathreading.hpp +++ b/dynadjust/include/thread/dnathreading.hpp @@ -373,8 +373,7 @@ class message_bank std::lock_guard lock(message_mutex); if (i >= messages.size()) { - T msg(0); - return msg; + return T{}; } return messages.at(i); } diff --git a/resources/make_dynadjust_gcc.sh b/resources/make_dynadjust_gcc.sh index 99a9ba35a..ada1e8a40 100755 --- a/resources/make_dynadjust_gcc.sh +++ b/resources/make_dynadjust_gcc.sh @@ -139,10 +139,10 @@ fi # opt installation folder OPT_DYNADJUST_PATH=/opt/dynadjust OPT_DYNADJUST_GCC_PATH=/opt/dynadjust/gcc -DYNADJUST_INSTALL_PATH=/opt/dynadjust/gcc/1_3_0 +DYNADJUST_INSTALL_PATH=/opt/dynadjust/gcc/1_4_0 # version info -_version="1.3.0" +_version="1.4.0" echo -e "\n===========================================================================" echo -e "DynAdjust $_version build configuration options..." diff --git a/sampleData/DynaML.xsd b/sampleData/DynaML.xsd index 33d4d66c6..58dd535c3 100644 --- a/sampleData/DynaML.xsd +++ b/sampleData/DynaML.xsd @@ -43,6 +43,7 @@ + @@ -194,6 +195,7 @@ + diff --git a/sampleData/strip_schema_location.sh b/sampleData/strip_schema_location.sh new file mode 100755 index 000000000..a8984e2dc --- /dev/null +++ b/sampleData/strip_schema_location.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# Strip xsi:noNamespaceSchemaLocation attribute from DynaML XML files. +# Usage: strip_schema_location.sh +[ $# -ne 2 ] && { echo "Usage: $0 "; exit 1; } +[ -f "$1" ] || { echo "FAIL: $1 not found"; exit 1; } +sed 's/ xsi:noNamespaceSchemaLocation="DynaML\.xsd"//' "$1" > "$2" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ae23ce475..5e55b4a56 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -219,6 +219,32 @@ target_compile_definitions(test_gnss_nstat_sort PRIVATE __BINARY_DESC__="Unit tests for GNSS n-stat sort in alternate units" ) +# Test 11: FormatElapsedTime helper (issue #351) +add_executable(test_format_elapsed_time + test_format_elapsed_time.cpp +) + +target_compile_definitions(test_format_elapsed_time PRIVATE + __BINARY_NAME__="test_format_elapsed_time" + __BINARY_DESC__="Unit tests for FormatElapsedTime helper (issue #351)" +) + +# Test 12: DynadjustFile v1.1 back-compat test (issue #325) +add_executable(test_dynadjust_file + test_dynadjust_file.cpp + ${IO_COMMON_SOURCES} +) + +target_link_libraries(test_dynadjust_file + ${PLATFORM_LIBS} + ${Boost_LIBRARIES} +) + +target_compile_definitions(test_dynadjust_file PRIVATE + __BINARY_NAME__="test_dynadjust_file" + __BINARY_DESC__="Unit tests for DynadjustFile v1.1 back-compat (issue #325)" +) + # Enable testing enable_testing() @@ -233,16 +259,18 @@ add_test(NAME NetworkDataLoaderTest COMMAND test_network_data_loader) add_test(NAME MeasurementProcessorTest COMMAND test_measurement_processor) add_test(NAME DynAdjustPrinterTest COMMAND test_dnaadjust_printer) add_test(NAME GNSSNstatSortTest COMMAND test_gnss_nstat_sort) +add_test(NAME FormatElapsedTimeTest COMMAND test_format_elapsed_time) +add_test(NAME DynadjustFileTest COMMAND test_dynadjust_file) # Custom target to run all tests add_custom_target(run_tests COMMAND ${CMAKE_CTEST_COMMAND} --verbose - DEPENDS test_matrix test_msr_to_stn_sort test_bst_file test_asl_file test_aml_file_loader test_bms_file test_network_data_loader test_measurement_processor test_dnaadjust_printer test_gnss_nstat_sort + DEPENDS test_matrix test_msr_to_stn_sort test_bst_file test_asl_file test_aml_file_loader test_bms_file test_network_data_loader test_measurement_processor test_dnaadjust_printer test_gnss_nstat_sort test_format_elapsed_time test_dynadjust_file COMMENT "Running all tests" ) # Custom target equivalent to 'make all' add_custom_target(tests_all - DEPENDS test_matrix test_msr_to_stn_sort test_bst_file test_asl_file test_aml_file_loader test_bms_file test_network_data_loader test_measurement_processor test_dnaadjust_printer test_gnss_nstat_sort + DEPENDS test_matrix test_msr_to_stn_sort test_bst_file test_asl_file test_aml_file_loader test_bms_file test_network_data_loader test_measurement_processor test_dnaadjust_printer test_gnss_nstat_sort test_format_elapsed_time test_dynadjust_file COMMENT "Building all tests" ) diff --git a/tests/test_bms_file.cpp b/tests/test_bms_file.cpp index 89fad4a0f..2c41aa054 100644 --- a/tests/test_bms_file.cpp +++ b/tests/test_bms_file.cpp @@ -553,5 +553,39 @@ TEST_CASE("Source file index round-trip", "[BmsFile][roundtrip][source]") { 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(); +} + +TEST_CASE("observation_epoch round-trip preserves distinct values", "[BmsFile][observation_epoch]") { + BmsFile bms_loader; + vmsr_t measurements; + binary_file_meta_t meta; + + cleanup_temp_files(); + + measurement_t msr = {}; + msr.measType = 'G'; + msr.measurementStations = 2; + snprintf(msr.epsgCode, sizeof(msr.epsgCode), "7843"); + snprintf(msr.epoch, sizeof(msr.epoch), "01.01.2020"); + snprintf(msr.observation_epoch, sizeof(msr.observation_epoch), "15.06.2015"); + msr.station1 = 0; + msr.station2 = 1; + msr.term1 = 1234.5; + measurements.push_back(msr); + + create_test_binary_meta(meta, measurements.size()); + snprintf(meta.observation_epoch, sizeof(meta.observation_epoch), "15.06.2015"); + + bms_loader.WriteFile(TEMP_BMS_FILE, &measurements, meta); + + vmsr_t loaded; + binary_file_meta_t loaded_meta; + bms_loader.LoadFile(TEMP_BMS_FILE, &loaded, loaded_meta); + + REQUIRE(std::string(loaded[0].epoch) == "01.01.2020"); + REQUIRE(std::string(loaded[0].observation_epoch) == "15.06.2015"); + REQUIRE(std::string(loaded_meta.observation_epoch) == "15.06.2015"); + cleanup_temp_files(); } \ No newline at end of file diff --git a/tests/test_bst_file.cpp b/tests/test_bst_file.cpp index e80993062..222d1563d 100644 --- a/tests/test_bst_file.cpp +++ b/tests/test_bst_file.cpp @@ -224,3 +224,42 @@ TEST_CASE("Write and read back synthetic data", "[BstFile]") { cleanup_temp_files(); } + +TEST_CASE("observation_epoch round-trip preserves distinct epoch values", "[BstFile][observation_epoch]") { + BstFile bst_loader; + vstn_t stations; + binary_file_meta_t bst_meta; + + cleanup_temp_files(); + + station_t stn; + snprintf(stn.stationName, sizeof(stn.stationName), "OBS001"); + snprintf(stn.stationNameOrig, sizeof(stn.stationNameOrig), "OBS001_2015"); + snprintf(stn.epoch, sizeof(stn.epoch), "01.01.2020"); + snprintf(stn.observation_epoch, sizeof(stn.observation_epoch), "15.06.2015"); + stn.initialLatitude = -35.0; + stn.currentLatitude = -35.0; + stn.initialLongitude = 149.0; + stn.currentLongitude = 149.0; + stn.initialHeight = 100.0; + stn.currentHeight = 100.0; + stn.zone = 55; + stn.fileOrder = 1; + stn.nameOrder = 1; + stations.push_back(stn); + + create_test_binary_meta(bst_meta, static_cast(stations.size())); + snprintf(bst_meta.observation_epoch, sizeof(bst_meta.observation_epoch), "15.06.2015"); + + bst_loader.WriteFile(TEMP_BST_FILE, &stations, bst_meta); + + vstn_t loaded; + binary_file_meta_t loaded_meta; + bst_loader.LoadFile(TEMP_BST_FILE, &loaded, loaded_meta); + + REQUIRE(std::string(loaded[0].epoch) == "01.01.2020"); + REQUIRE(std::string(loaded[0].observation_epoch) == "15.06.2015"); + REQUIRE(std::string(loaded_meta.observation_epoch) == "15.06.2015"); + + cleanup_temp_files(); +} diff --git a/tests/test_format_elapsed_time.cpp b/tests/test_format_elapsed_time.cpp new file mode 100644 index 000000000..45f99e88a --- /dev/null +++ b/tests/test_format_elapsed_time.cpp @@ -0,0 +1,36 @@ +#define TESTING_MAIN +#ifndef __BINARY_NAME__ +#define __BINARY_NAME__ "test_format_elapsed_time" +#endif +#ifndef __BINARY_DESC__ +#define __BINARY_DESC__ "Unit tests for FormatElapsedTime helper (issue #351)" +#endif + +#include "testing.hpp" + +#include "../dynadjust/include/functions/dnatimer.hpp" + +using dynadjust::FormatElapsedTime; + +TEST_CASE("Sub-second durations report milliseconds", "[time][issue351]") { + REQUIRE(FormatElapsedTime(0.000078) == "0.078ms"); + REQUIRE(FormatElapsedTime(0.0) == "0.000ms"); + REQUIRE(FormatElapsedTime(0.5) == "500.000ms"); + REQUIRE(FormatElapsedTime(0.999) == "999.000ms"); +} + +TEST_CASE("Durations between 1 and 60 seconds report seconds", "[time][issue351]") { + REQUIRE(FormatElapsedTime(1.0) == "1.000s"); + REQUIRE(FormatElapsedTime(12.580) == "12.580s"); + REQUIRE(FormatElapsedTime(59.999) == "59.999s"); +} + +TEST_CASE("Durations of 60 seconds or more report hh:mm:ss", "[time][issue351]") { + REQUIRE(FormatElapsedTime(60.0) == "00:01:00"); + REQUIRE(FormatElapsedTime(68.0) == "00:01:08"); + REQUIRE(FormatElapsedTime(139.419) == "00:02:19"); + REQUIRE(FormatElapsedTime(3600.0) == "01:00:00"); + REQUIRE(FormatElapsedTime(5841.0) == "01:37:21"); + REQUIRE(FormatElapsedTime(26697.777) == "07:24:57"); + REQUIRE(FormatElapsedTime(36000.0) == "10:00:00"); +} diff --git a/tests/test_matrix.cpp b/tests/test_matrix.cpp index c96d32dfa..ae7169cfd 100644 --- a/tests/test_matrix.cpp +++ b/tests/test_matrix.cpp @@ -22,6 +22,7 @@ #define TESTING_MAIN #include +#include #include #include "math/dnamatrix_contiguous.hpp" @@ -587,3 +588,947 @@ TEST_CASE("Cholesky inverse with LOWER_IS_CLEARED", "[matrix_2d]") { REQUIRE(abs(inverse.get(0, 2) - inverse.get(2, 0)) < 1e-10); REQUIRE(abs(inverse.get(1, 2) - inverse.get(2, 1)) < 1e-10); } + +// ============================================================================ +// Symmetric matrix support +// ============================================================================ + +TEST_CASE("Symmetric flag default is false", "[matrix_2d][symmetric]") { + matrix_2d mat(3, 3); + REQUIRE(mat.is_symmetric() == false); +} + +TEST_CASE("Symmetric flag set and get", "[matrix_2d][symmetric]") { + matrix_2d mat(3, 3); + mat.set_symmetric(true); + REQUIRE(mat.is_symmetric() == true); + mat.set_symmetric(false); + REQUIRE(mat.is_symmetric() == false); +} + +TEST_CASE("Symmetric flag propagates through copy constructor", "[matrix_2d][symmetric]") { + matrix_2d mat(3, 3); + mat.put(0, 0, 1.0); mat.put(1, 0, 2.0); mat.put(1, 1, 3.0); + mat.put(2, 0, 4.0); mat.put(2, 1, 5.0); mat.put(2, 2, 6.0); + mat.set_symmetric(true); + + matrix_2d copy(mat); + REQUIRE(copy.is_symmetric() == true); + REQUIRE(copy.get(0, 1) == 2.0); // reflects from lower +} + +TEST_CASE("Symmetric flag propagates through operator=", "[matrix_2d][symmetric]") { + matrix_2d mat(3, 3); + mat.put(0, 0, 1.0); mat.put(1, 0, 2.0); mat.put(1, 1, 3.0); + mat.set_symmetric(true); + + matrix_2d other(3, 3); + other = mat; + REQUIRE(other.is_symmetric() == true); + REQUIRE(other.get(0, 1) == 2.0); +} + +TEST_CASE("Symmetric get reflects upper to lower", "[matrix_2d][symmetric]") { + // Populate only the lower triangle + diagonal + matrix_2d mat(4, 4); + mat.put(0, 0, 1.0); + mat.put(1, 0, 2.0); mat.put(1, 1, 3.0); + mat.put(2, 0, 4.0); mat.put(2, 1, 5.0); mat.put(2, 2, 6.0); + mat.put(3, 0, 7.0); mat.put(3, 1, 8.0); mat.put(3, 2, 9.0); mat.put(3, 3, 10.0); + mat.set_symmetric(true); + + // Diagonal + REQUIRE(mat.get(0, 0) == 1.0); + REQUIRE(mat.get(1, 1) == 3.0); + REQUIRE(mat.get(2, 2) == 6.0); + REQUIRE(mat.get(3, 3) == 10.0); + + // Lower triangle (direct) + REQUIRE(mat.get(1, 0) == 2.0); + REQUIRE(mat.get(2, 0) == 4.0); + REQUIRE(mat.get(3, 2) == 9.0); + + // Upper triangle (reflected from lower) + REQUIRE(mat.get(0, 1) == 2.0); + REQUIRE(mat.get(0, 2) == 4.0); + REQUIRE(mat.get(0, 3) == 7.0); + REQUIRE(mat.get(1, 2) == 5.0); + REQUIRE(mat.get(1, 3) == 8.0); + REQUIRE(mat.get(2, 3) == 9.0); +} + +TEST_CASE("Symmetric getelementref reflects upper to lower", "[matrix_2d][symmetric]") { + matrix_2d mat(3, 3); + mat.put(0, 0, 10.0); + mat.put(1, 0, 20.0); mat.put(1, 1, 30.0); + mat.put(2, 0, 40.0); mat.put(2, 1, 50.0); mat.put(2, 2, 60.0); + mat.set_symmetric(true); + + // const version + const matrix_2d& cmat = mat; + REQUIRE(*cmat.getelementref(0, 2) == 40.0); // reflects (0,2) -> (2,0) + REQUIRE(*cmat.getelementref(2, 0) == 40.0); // direct + + // non-const version + REQUIRE(*mat.getelementref(1, 2) == 50.0); // reflects (1,2) -> (2,1) +} + +TEST_CASE("Symmetric redim clears flag", "[matrix_2d][symmetric]") { + matrix_2d mat(3, 3); + mat.set_symmetric(true); + mat.redim(4, 4); + REQUIRE(mat.is_symmetric() == false); +} + +TEST_CASE("Cholesky inverse with mark_symmetric", "[matrix_2d][symmetric]") { + matrix_2d mat(3, 3); + mat.put(0, 0, 4.0); + mat.put(0, 1, -1.0); + mat.put(0, 2, -1.0); + mat.put(1, 0, -1.0); + mat.put(1, 1, 3.0); + mat.put(1, 2, -1.0); + mat.put(2, 0, -1.0); + mat.put(2, 1, -1.0); + mat.put(2, 2, 2.0); + + matrix_2d inverse = mat.cholesky_inverse(false, true); + + REQUIRE(inverse.is_symmetric() == true); + // Values should match the non-symmetric cholesky inverse + REQUIRE(abs(inverse.get(0, 0) - 0.384615) < 0.0001); + REQUIRE(abs(inverse.get(1, 1) - 0.538462) < 0.0001); + REQUIRE(abs(inverse.get(2, 2) - 0.846154) < 0.0001); + // Upper triangle via symmetric reflection + REQUIRE(abs(inverse.get(0, 1) - 0.230769) < 0.0001); + REQUIRE(abs(inverse.get(0, 2) - 0.307692) < 0.0001); + REQUIRE(abs(inverse.get(1, 2) - 0.384615) < 0.0001); + // Symmetry + REQUIRE(abs(inverse.get(0, 1) - inverse.get(1, 0)) < 1e-10); + REQUIRE(abs(inverse.get(0, 2) - inverse.get(2, 0)) < 1e-10); + REQUIRE(abs(inverse.get(1, 2) - inverse.get(2, 1)) < 1e-10); +} + +TEST_CASE("Cholesky mark_symmetric matches full inverse", "[matrix_2d][symmetric]") { + // Larger 5x5 SPD matrix + matrix_2d full(5, 5); + matrix_2d sym(5, 5); + double spd[] = { + 10, 1, 2, 0, 1, + 1, 8, 1, 2, 0, + 2, 1, 7, 1, 1, + 0, 2, 1, 6, 1, + 1, 0, 1, 1, 5 + }; + for (UINT32 i = 0; i < 5; ++i) + for (UINT32 j = 0; j < 5; ++j) { + full.put(i, j, spd[i * 5 + j]); + sym.put(i, j, spd[i * 5 + j]); + } + + full.cholesky_inverse(false, false); // full inverse with fillupper + sym.cholesky_inverse(false, true); // symmetric inverse + + REQUIRE(sym.is_symmetric() == true); + REQUIRE(full.is_symmetric() == false); + + // All elements should match + for (UINT32 i = 0; i < 5; ++i) + for (UINT32 j = 0; j < 5; ++j) + REQUIRE(abs(sym.get(i, j) - full.get(i, j)) < 1e-12); +} + +TEST_CASE("multiply_sym matches dgemm multiply", "[matrix_2d][symmetric]") { + // Create a symmetric 4x4 matrix and a 4x2 rhs + matrix_2d A(4, 4); + double spd[] = { + 5, 1, 2, 0, + 1, 4, 1, 1, + 2, 1, 6, 1, + 0, 1, 1, 3 + }; + for (UINT32 i = 0; i < 4; ++i) + for (UINT32 j = 0; j < 4; ++j) + A.put(i, j, spd[i * 4 + j]); + + matrix_2d B(4, 2); + B.put(0, 0, 1.0); B.put(0, 1, 5.0); + B.put(1, 0, 2.0); B.put(1, 1, 6.0); + B.put(2, 0, 3.0); B.put(2, 1, 7.0); + B.put(3, 0, 4.0); B.put(3, 1, 8.0); + + // dgemm result + matrix_2d C_full(4, 2); + C_full.multiply(A, "N", B, "N"); + + // dsymm result — only lower triangle in A_sym + matrix_2d A_sym(4, 4); + for (UINT32 i = 0; i < 4; ++i) + for (UINT32 j = 0; j <= i; ++j) + A_sym.put(i, j, spd[i * 4 + j]); + A_sym.set_symmetric(true); + + matrix_2d C_sym(4, 2); + C_sym.multiply_sym(A_sym, B); + + for (UINT32 i = 0; i < 4; ++i) + for (UINT32 j = 0; j < 2; ++j) + REQUIRE(abs(C_sym.get(i, j) - C_full.get(i, j)) < 1e-12); +} + +TEST_CASE("multiply_sym with column vector", "[matrix_2d][symmetric]") { + // This mirrors the Solve() hot path: N_inv * At_Vinv_m + matrix_2d N(3, 3); + N.put(0, 0, 4.0); + N.put(0, 1, -1.0); + N.put(0, 2, -1.0); + N.put(1, 0, -1.0); + N.put(1, 1, 3.0); + N.put(1, 2, -1.0); + N.put(2, 0, -1.0); + N.put(2, 1, -1.0); + N.put(2, 2, 2.0); + + // Full inverse + matrix_2d N_full(N); + N_full.cholesky_inverse(false, false); + + // Symmetric inverse + matrix_2d N_sym(N); + N_sym.cholesky_inverse(false, true); + + matrix_2d rhs(3, 1); + rhs.put(0, 0, 1.0); + rhs.put(1, 0, 2.0); + rhs.put(2, 0, 3.0); + + // dgemm + matrix_2d result_full(3, 1); + result_full.multiply(N_full, "N", rhs, "N"); + + // dsymm + matrix_2d result_sym(3, 1); + result_sym.multiply_sym(N_sym, rhs); + + for (UINT32 i = 0; i < 3; ++i) + REQUIRE(abs(result_sym.get(i, 0) - result_full.get(i, 0)) < 1e-12); +} + +TEST_CASE("Symmetric copyelements 3x3 from lower triangle", "[matrix_2d][symmetric]") { + matrix_2d src(6, 6); + // Fill lower triangle only + for (UINT32 i = 0; i < 6; ++i) + for (UINT32 j = 0; j <= i; ++j) + src.put(i, j, static_cast(i * 10 + j)); + src.set_symmetric(true); + + matrix_2d dst(3, 3); + + // Copy from lower triangle region (row >= col) — should work directly + dst.copyelements(0, 0, src, 3, 0, 3, 3); + REQUIRE(dst.get(0, 0) == 30.0); + REQUIRE(dst.get(1, 0) == 40.0); + REQUIRE(dst.get(2, 0) == 50.0); + REQUIRE(dst.get(0, 1) == 31.0); + REQUIRE(dst.get(1, 1) == 41.0); + REQUIRE(dst.get(2, 1) == 51.0); + REQUIRE(dst.get(0, 2) == 32.0); + REQUIRE(dst.get(1, 2) == 42.0); + REQUIRE(dst.get(2, 2) == 52.0); +} + +TEST_CASE("Symmetric copyelements 3x3 from upper triangle", "[matrix_2d][symmetric]") { + matrix_2d src(6, 6); + // Fill lower triangle only: src(i,j) = i*10+j for j<=i + for (UINT32 i = 0; i < 6; ++i) + for (UINT32 j = 0; j <= i; ++j) + src.put(i, j, static_cast(i * 10 + j)); + src.set_symmetric(true); + + matrix_2d dst(3, 3); + + // Copy from upper triangle region (row_src=0, col_src=3) + // dst(r,c) = src.get(r, 3+c) which reflects to src(3+c, r) = (3+c)*10 + r + dst.copyelements(0, 0, src, 0, 3, 3, 3); + REQUIRE(dst.get(0, 0) == 30.0); // src.get(0,3) = src(3,0) = 30 + REQUIRE(dst.get(1, 0) == 31.0); // src.get(1,3) = src(3,1) = 31 + REQUIRE(dst.get(2, 0) == 32.0); // src.get(2,3) = src(3,2) = 32 + REQUIRE(dst.get(0, 1) == 40.0); // src.get(0,4) = src(4,0) = 40 + REQUIRE(dst.get(1, 1) == 41.0); // src.get(1,4) = src(4,1) = 41 + REQUIRE(dst.get(2, 1) == 42.0); // src.get(2,4) = src(4,2) = 42 + REQUIRE(dst.get(0, 2) == 50.0); // src.get(0,5) = src(5,0) = 50 + REQUIRE(dst.get(1, 2) == 51.0); // src.get(1,5) = src(5,1) = 51 + REQUIRE(dst.get(2, 2) == 52.0); // src.get(2,5) = src(5,2) = 52 +} + +TEST_CASE("Symmetric blockadd 3x3 from upper triangle", "[matrix_2d][symmetric]") { + matrix_2d src(6, 6); + for (UINT32 i = 0; i < 6; ++i) + for (UINT32 j = 0; j <= i; ++j) + src.put(i, j, static_cast(i * 10 + j)); + src.set_symmetric(true); + + matrix_2d dst(3, 3); + // Pre-fill with 1.0 to verify addition + for (UINT32 i = 0; i < 3; ++i) + for (UINT32 j = 0; j < 3; ++j) + dst.put(i, j, 1.0); + + // blockadd from upper triangle (row_src=0, col_src=3) + dst.blockadd(0, 0, src, 0, 3, 3, 3); + + // dst(r,c) = 1.0 + src.get(r, 3+c) + REQUIRE(dst.get(0, 0) == 31.0); // 1 + 30 + REQUIRE(dst.get(1, 0) == 32.0); // 1 + 31 + REQUIRE(dst.get(2, 0) == 33.0); // 1 + 32 + REQUIRE(dst.get(0, 1) == 41.0); // 1 + 40 + REQUIRE(dst.get(1, 1) == 42.0); // 1 + 41 + REQUIRE(dst.get(2, 1) == 43.0); // 1 + 42 + REQUIRE(dst.get(0, 2) == 51.0); // 1 + 50 + REQUIRE(dst.get(1, 2) == 52.0); // 1 + 51 + REQUIRE(dst.get(2, 2) == 53.0); // 1 + 52 +} + +TEST_CASE("Symmetric copyelements generic from upper triangle", "[matrix_2d][symmetric]") { + matrix_2d src(8, 8); + for (UINT32 i = 0; i < 8; ++i) + for (UINT32 j = 0; j <= i; ++j) + src.put(i, j, static_cast(i * 10 + j)); + src.set_symmetric(true); + + // Copy a 4x4 block from upper triangle (row=0, col=4) + matrix_2d dst(4, 4); + dst.copyelements(0, 0, src, 0, 4, 4, 4); + + // Verify via get() which handles reflection + for (UINT32 i = 0; i < 4; ++i) + for (UINT32 j = 0; j < 4; ++j) + REQUIRE(abs(dst.get(i, j) - src.get(i, 4 + j)) < 1e-15); +} + +TEST_CASE("End-to-end: cholesky mark_symmetric then multiply_sym", "[matrix_2d][symmetric]") { + // Simulate the full Solve() path + matrix_2d N(3, 3); + N.put(0, 0, 4.0); N.put(0, 1, -1.0); N.put(0, 2, -1.0); + N.put(1, 0, -1.0); N.put(1, 1, 3.0); N.put(1, 2, -1.0); + N.put(2, 0, -1.0); N.put(2, 1, -1.0); N.put(2, 2, 2.0); + + matrix_2d N_ref(N); + + // Reference: full inverse + dgemm + N_ref.cholesky_inverse(false, false); + + matrix_2d rhs(3, 1); + rhs.put(0, 0, 10.0); + rhs.put(1, 0, 20.0); + rhs.put(2, 0, 30.0); + + matrix_2d ref_result(3, 1); + ref_result.multiply(N_ref, "N", rhs, "N"); + + // Test: symmetric inverse + dsymm + N.cholesky_inverse(false, true); + REQUIRE(N.is_symmetric() == true); + + matrix_2d sym_result(3, 1); + sym_result.multiply_sym(N, rhs); + + for (UINT32 i = 0; i < 3; ++i) + REQUIRE(abs(sym_result.get(i, 0) - ref_result.get(i, 0)) < 1e-12); +} + +// ============================================================================ +// Packed symmetric matrix storage +// ============================================================================ + +TEST_CASE("Packed allocation has correct buffer size", "[matrix_2d][packed]") { + matrix_2d mat; + mat.redim_packed(4); + REQUIRE(mat.rows() == 4); + REQUIRE(mat.columns() == 4); + REQUIRE(mat.is_packed() == true); + REQUIRE(mat.is_symmetric() == true); + // n*(n+1)/2 = 10 elements + REQUIRE(mat.memRows() == 4); +} + +TEST_CASE("Packed element access: put and get reflect symmetry", "[matrix_2d][packed]") { + matrix_2d mat; + mat.redim_packed(4); + + mat.put(0, 0, 1.0); + mat.put(1, 0, 2.0); mat.put(1, 1, 3.0); + mat.put(2, 0, 4.0); mat.put(2, 1, 5.0); mat.put(2, 2, 6.0); + mat.put(3, 0, 7.0); mat.put(3, 1, 8.0); mat.put(3, 2, 9.0); mat.put(3, 3, 10.0); + + // Diagonal + REQUIRE(mat.get(0, 0) == 1.0); + REQUIRE(mat.get(1, 1) == 3.0); + REQUIRE(mat.get(2, 2) == 6.0); + REQUIRE(mat.get(3, 3) == 10.0); + + // Lower triangle (direct) + REQUIRE(mat.get(1, 0) == 2.0); + REQUIRE(mat.get(2, 0) == 4.0); + REQUIRE(mat.get(3, 2) == 9.0); + + // Upper triangle (reflected from lower) + REQUIRE(mat.get(0, 1) == 2.0); + REQUIRE(mat.get(0, 2) == 4.0); + REQUIRE(mat.get(0, 3) == 7.0); + REQUIRE(mat.get(1, 2) == 5.0); + REQUIRE(mat.get(1, 3) == 8.0); + REQUIRE(mat.get(2, 3) == 9.0); +} + +TEST_CASE("Packed put via upper triangle maps to lower", "[matrix_2d][packed]") { + matrix_2d mat; + mat.redim_packed(3); + + mat.put(0, 1, 42.0); + REQUIRE(mat.get(1, 0) == 42.0); + REQUIRE(mat.get(0, 1) == 42.0); +} + +TEST_CASE("Packed elementadd skips upper triangle", "[matrix_2d][packed]") { + matrix_2d mat; + mat.redim_packed(3); + + mat.elementadd(1, 0, 10.0); + mat.elementadd(0, 1, 5.0); // skipped — upper triangle + REQUIRE(mat.get(1, 0) == 10.0); + REQUIRE(mat.get(0, 1) == 10.0); + + mat.elementadd(1, 0, 3.0); // lower triangle — accumulates + REQUIRE(mat.get(1, 0) == 13.0); +} + +TEST_CASE("Packed cholesky_inverse matches full inverse", "[matrix_2d][packed]") { + matrix_2d full(3, 3); + full.put(0, 0, 4.0); full.put(0, 1, -1.0); full.put(0, 2, -1.0); + full.put(1, 0, -1.0); full.put(1, 1, 3.0); full.put(1, 2, -1.0); + full.put(2, 0, -1.0); full.put(2, 1, -1.0); full.put(2, 2, 2.0); + + matrix_2d packed; + packed.redim_packed(3); + packed.put(0, 0, 4.0); + packed.put(1, 0, -1.0); packed.put(1, 1, 3.0); + packed.put(2, 0, -1.0); packed.put(2, 1, -1.0); packed.put(2, 2, 2.0); + + full.cholesky_inverse(); + packed.cholesky_inverse(); + + REQUIRE(packed.is_packed() == true); + for (UINT32 i = 0; i < 3; ++i) + for (UINT32 j = 0; j < 3; ++j) + REQUIRE(abs(packed.get(i, j) - full.get(i, j)) < 1e-10); +} + +TEST_CASE("Packed cholesky_inverse throws on indefinite matrix", "[matrix_2d][packed]") { + matrix_2d mat; + mat.redim_packed(2); + mat.put(0, 0, 2.0); + mat.put(1, 0, 1.0); + mat.put(1, 1, -1.0); + + bool caught = false; + try { mat.cholesky_inverse(); } + catch (const MatrixInversionFailure&) { caught = true; } + REQUIRE(caught); +} + +TEST_CASE("Packed multiply_sym with vector matches full result", "[matrix_2d][packed]") { + matrix_2d full(3, 3); + full.put(0, 0, 4.0); full.put(0, 1, -1.0); full.put(0, 2, -1.0); + full.put(1, 0, -1.0); full.put(1, 1, 3.0); full.put(1, 2, -1.0); + full.put(2, 0, -1.0); full.put(2, 1, -1.0); full.put(2, 2, 2.0); + + matrix_2d packed; + packed.redim_packed(3); + packed.put(0, 0, 4.0); + packed.put(1, 0, -1.0); packed.put(1, 1, 3.0); + packed.put(2, 0, -1.0); packed.put(2, 1, -1.0); packed.put(2, 2, 2.0); + + matrix_2d rhs(3, 1); + rhs.put(0, 0, 1.0); rhs.put(1, 0, 2.0); rhs.put(2, 0, 3.0); + + matrix_2d result_full(3, 1); + result_full.multiply(full, "N", rhs, "N"); + + matrix_2d result_packed(3, 1); + result_packed.multiply_sym(packed, rhs); + + for (UINT32 i = 0; i < 3; ++i) + REQUIRE(abs(result_packed.get(i, 0) - result_full.get(i, 0)) < 1e-12); +} + +TEST_CASE("Packed multiply_sym with multi-column rhs", "[matrix_2d][packed]") { + matrix_2d packed; + packed.redim_packed(4); + double spd[] = {5, 1, 2, 0, 1, 4, 1, 1, 2, 1, 6, 1, 0, 1, 1, 3}; + for (UINT32 i = 0; i < 4; ++i) + for (UINT32 j = 0; j <= i; ++j) + packed.put(i, j, spd[i * 4 + j]); + + matrix_2d full(4, 4); + for (UINT32 i = 0; i < 4; ++i) + for (UINT32 j = 0; j < 4; ++j) + full.put(i, j, spd[i * 4 + j]); + + matrix_2d B(4, 2); + B.put(0, 0, 1.0); B.put(0, 1, 5.0); + B.put(1, 0, 2.0); B.put(1, 1, 6.0); + B.put(2, 0, 3.0); B.put(2, 1, 7.0); + B.put(3, 0, 4.0); B.put(3, 1, 8.0); + + matrix_2d C_full(4, 2); + C_full.multiply(full, "N", B, "N"); + + matrix_2d C_packed(4, 2); + C_packed.multiply_sym(packed, B); + + for (UINT32 i = 0; i < 4; ++i) + for (UINT32 j = 0; j < 2; ++j) + REQUIRE(abs(C_packed.get(i, j) - C_full.get(i, j)) < 1e-12); +} + +TEST_CASE("Packed scale_symmetric_diagonal", "[matrix_2d][packed]") { + matrix_2d packed; + packed.redim_packed(3); + packed.put(0, 0, 4.0); + packed.put(1, 0, -1.0); packed.put(1, 1, 3.0); + packed.put(2, 0, -1.0); packed.put(2, 1, -1.0); packed.put(2, 2, 2.0); + + matrix_2d full(3, 3); + full.put(0, 0, 4.0); full.put(0, 1, -1.0); full.put(0, 2, -1.0); + full.put(1, 0, -1.0); full.put(1, 1, 3.0); full.put(1, 2, -1.0); + full.put(2, 0, -1.0); full.put(2, 1, -1.0); full.put(2, 2, 2.0); + full.set_symmetric(true); + + double diag[] = {2.0, 3.0, 0.5}; + packed.scale_symmetric_diagonal(diag); + full.scale_symmetric_diagonal(diag); + + for (UINT32 i = 0; i < 3; ++i) + for (UINT32 j = 0; j < 3; ++j) + REQUIRE(abs(packed.get(i, j) - full.get(i, j)) < 1e-12); +} + +TEST_CASE("Packed blockadd diagonal block", "[matrix_2d][packed]") { + matrix_2d packed; + packed.redim_packed(4); + + // Diagonal blocks only write lower triangle via elementadd + // (mirrors how AddMsrtoNormalsVar works) + packed.elementadd(1, 1, 10.0); + packed.elementadd(2, 1, 20.0); + packed.elementadd(2, 2, 40.0); + + REQUIRE(packed.get(1, 1) == 10.0); + REQUIRE(packed.get(2, 1) == 20.0); + REQUIRE(packed.get(1, 2) == 20.0); + REQUIRE(packed.get(2, 2) == 40.0); + REQUIRE(packed.get(0, 0) == 0.0); +} + +TEST_CASE("Packed submatrix extraction", "[matrix_2d][packed]") { + matrix_2d packed; + packed.redim_packed(4); + packed.put(0, 0, 1.0); + packed.put(1, 0, 2.0); packed.put(1, 1, 3.0); + packed.put(2, 0, 4.0); packed.put(2, 1, 5.0); packed.put(2, 2, 6.0); + packed.put(3, 0, 7.0); packed.put(3, 1, 8.0); packed.put(3, 2, 9.0); packed.put(3, 3, 10.0); + + matrix_2d sub = packed.submatrix(1, 1, 2, 2); + REQUIRE(sub.rows() == 2); + REQUIRE(sub.columns() == 2); + REQUIRE(sub.get(0, 0) == 3.0); + REQUIRE(sub.get(0, 1) == 5.0); + REQUIRE(sub.get(1, 0) == 5.0); + REQUIRE(sub.get(1, 1) == 6.0); +} + +TEST_CASE("Packed copy constructor", "[matrix_2d][packed]") { + matrix_2d orig; + orig.redim_packed(3); + orig.put(0, 0, 1.0); + orig.put(1, 0, 2.0); orig.put(1, 1, 3.0); + orig.put(2, 0, 4.0); orig.put(2, 1, 5.0); orig.put(2, 2, 6.0); + + matrix_2d copy(orig); + REQUIRE(copy.is_packed() == true); + REQUIRE(copy.is_symmetric() == true); + for (UINT32 i = 0; i < 3; ++i) + for (UINT32 j = 0; j < 3; ++j) + REQUIRE(copy.get(i, j) == orig.get(i, j)); +} + +TEST_CASE("Packed operator= packed-to-packed", "[matrix_2d][packed]") { + matrix_2d a; + a.redim_packed(3); + a.put(0, 0, 10.0); a.put(1, 0, 20.0); a.put(1, 1, 30.0); + a.put(2, 0, 40.0); a.put(2, 1, 50.0); a.put(2, 2, 60.0); + + matrix_2d b; + b.redim_packed(2); + b = a; + REQUIRE(b.is_packed() == true); + REQUIRE(b.rows() == 3); + for (UINT32 i = 0; i < 3; ++i) + for (UINT32 j = 0; j < 3; ++j) + REQUIRE(b.get(i, j) == a.get(i, j)); +} + +TEST_CASE("Packed operator= full-to-packed preserves full", "[matrix_2d][packed]") { + matrix_2d full(3, 3); + full.put(0, 0, 1.0); full.put(0, 1, 2.0); full.put(0, 2, 3.0); + full.put(1, 0, 4.0); full.put(1, 1, 5.0); full.put(1, 2, 6.0); + full.put(2, 0, 7.0); full.put(2, 1, 8.0); full.put(2, 2, 9.0); + + matrix_2d packed; + packed.redim_packed(3); + packed = full; + // Assigning a non-packed to a packed should convert to non-packed + REQUIRE(packed.is_packed() == false); + for (UINT32 i = 0; i < 3; ++i) + for (UINT32 j = 0; j < 3; ++j) + REQUIRE(packed.get(i, j) == full.get(i, j)); +} + +TEST_CASE("Packed redim_packed reuses buffer when shrinking", "[matrix_2d][packed]") { + matrix_2d mat; + mat.redim_packed(5); + mat.put(0, 0, 99.0); + + mat.redim_packed(3); + REQUIRE(mat.rows() == 3); + REQUIRE(mat.is_packed() == true); + REQUIRE(mat.get(0, 0) == 0.0); +} + +TEST_CASE("Packed zero", "[matrix_2d][packed]") { + matrix_2d mat; + mat.redim_packed(3); + mat.put(0, 0, 1.0); mat.put(1, 0, 2.0); mat.put(2, 2, 3.0); + mat.zero(); + for (UINT32 i = 0; i < 3; ++i) + for (UINT32 j = 0; j < 3; ++j) + REQUIRE(mat.get(i, j) == 0.0); +} + +TEST_CASE("Packed scale", "[matrix_2d][packed]") { + matrix_2d mat; + mat.redim_packed(3); + mat.put(0, 0, 2.0); + mat.put(1, 0, 3.0); mat.put(1, 1, 4.0); + mat.put(2, 0, 5.0); mat.put(2, 1, 6.0); mat.put(2, 2, 7.0); + mat.scale(2.0); + + REQUIRE(mat.get(0, 0) == 4.0); + REQUIRE(mat.get(1, 0) == 6.0); + REQUIRE(mat.get(2, 2) == 14.0); + REQUIRE(mat.get(0, 1) == 6.0); // reflected +} + +TEST_CASE("Packed end-to-end: cholesky then multiply_sym", "[matrix_2d][packed]") { + matrix_2d full(3, 3); + full.put(0, 0, 4.0); full.put(0, 1, -1.0); full.put(0, 2, -1.0); + full.put(1, 0, -1.0); full.put(1, 1, 3.0); full.put(1, 2, -1.0); + full.put(2, 0, -1.0); full.put(2, 1, -1.0); full.put(2, 2, 2.0); + + matrix_2d packed; + packed.redim_packed(3); + packed.put(0, 0, 4.0); + packed.put(1, 0, -1.0); packed.put(1, 1, 3.0); + packed.put(2, 0, -1.0); packed.put(2, 1, -1.0); packed.put(2, 2, 2.0); + + // Reference + full.cholesky_inverse(false, false); + matrix_2d rhs(3, 1); + rhs.put(0, 0, 10.0); rhs.put(1, 0, 20.0); rhs.put(2, 0, 30.0); + matrix_2d ref_result(3, 1); + ref_result.multiply(full, "N", rhs, "N"); + + // Packed path + packed.cholesky_inverse(); + matrix_2d packed_result(3, 1); + packed_result.multiply_sym(packed, rhs); + + for (UINT32 i = 0; i < 3; ++i) + REQUIRE(abs(packed_result.get(i, 0) - ref_result.get(i, 0)) < 1e-12); +} + +TEST_CASE("Packed copyelements from packed source", "[matrix_2d][packed]") { + matrix_2d packed; + packed.redim_packed(6); + for (UINT32 i = 0; i < 6; ++i) + for (UINT32 j = 0; j <= i; ++j) + packed.put(i, j, static_cast(i * 10 + j)); + + matrix_2d dst(3, 3); + dst.copyelements(0, 0, packed, 3, 0, 3, 3); + + REQUIRE(dst.get(0, 0) == 30.0); + REQUIRE(dst.get(1, 0) == 40.0); + REQUIRE(dst.get(2, 0) == 50.0); + REQUIRE(dst.get(0, 1) == 31.0); + REQUIRE(dst.get(1, 1) == 41.0); + REQUIRE(dst.get(2, 1) == 51.0); +} + +TEST_CASE("Packed 5x5 cholesky_inverse matches full", "[matrix_2d][packed]") { + double spd[] = { + 10, 1, 2, 0, 1, + 1, 8, 1, 2, 0, + 2, 1, 7, 1, 1, + 0, 2, 1, 6, 1, + 1, 0, 1, 1, 5 + }; + + matrix_2d full(5, 5); + matrix_2d packed; + packed.redim_packed(5); + + for (UINT32 i = 0; i < 5; ++i) + for (UINT32 j = 0; j < 5; ++j) { + full.put(i, j, spd[i * 5 + j]); + if (j <= i) + packed.put(i, j, spd[i * 5 + j]); + } + + full.cholesky_inverse(false, false); + packed.cholesky_inverse(); + + for (UINT32 i = 0; i < 5; ++i) + for (UINT32 j = 0; j < 5; ++j) + REQUIRE(abs(packed.get(i, j) - full.get(i, j)) < 1e-12); +} + +// ================================================================ +// In-place mmap buffer tests (AttachMappedFileRegion / DetachMappedFileRegion) +// ================================================================ + +TEST_CASE("In-place mmap round-trip: full matrix", "[matrix_2d][mmap]") { + // Create a 4x3 full matrix + matrix_2d orig(4, 3); + for (UINT32 r = 0; r < 4; ++r) + for (UINT32 c = 0; c < 3; ++c) + orig.put(r, c, (r + 1) * 10.0 + c); + orig.compute_maximum_value(); + + // Allocate a buffer to simulate an mmap region + std::size_t region_size = orig.get_size(); + std::vector region(region_size, 0); + void* addr = region.data(); + + // Write the matrix to the "mmap" region + orig.WriteMappedFileRegion(addr); + + // Attach in-place — should point _buffer at the data in the region + matrix_2d attached; + attached.AttachMappedFileRegion(addr); + + REQUIRE(!attached.owns_buffer()); + REQUIRE(attached.rows() == 4); + REQUIRE(attached.columns() == 3); + REQUIRE(attached.matrixType() == mtx_full); + + // Verify all elements match + for (UINT32 r = 0; r < 4; ++r) + for (UINT32 c = 0; c < 3; ++c) + REQUIRE(attached.get(r, c) == orig.get(r, c)); + + // Modify in-place (writes directly to region) + attached.put(2, 1, 999.0); + REQUIRE(attached.get(2, 1) == 999.0); + + // Detach + attached.DetachMappedFileRegion(addr); + REQUIRE(attached.empty()); + REQUIRE(attached.owns_buffer()); + + // Read back with copy-based Read to verify data persisted + matrix_2d readback; + readback.ReadMappedFileRegion(addr); + REQUIRE(readback.get(2, 1) == 999.0); + REQUIRE(readback.get(0, 0) == 10.0); +} + +TEST_CASE("In-place mmap round-trip: packed lower-triangular", "[matrix_2d][mmap][packed]") { + // Create a 4x4 packed lower-triangular matrix + matrix_2d orig; + orig.redim_packed(4); + for (UINT32 i = 0; i < 4; ++i) + for (UINT32 j = 0; j <= i; ++j) + orig.put(i, j, (i + 1) * 10.0 + j); + orig.compute_maximum_value(); + + std::size_t region_size = orig.get_size(); + std::vector region(region_size, 0); + void* addr = region.data(); + + orig.WriteMappedFileRegion(addr); + + matrix_2d attached; + attached.AttachMappedFileRegion(addr); + + REQUIRE(!attached.owns_buffer()); + REQUIRE(attached.is_packed()); + REQUIRE(attached.is_symmetric()); + REQUIRE(attached.rows() == 4); + + // Verify all elements + for (UINT32 i = 0; i < 4; ++i) + for (UINT32 j = 0; j <= i; ++j) + REQUIRE(attached.get(i, j) == orig.get(i, j)); + + // Symmetry: upper = lower + REQUIRE(attached.get(0, 1) == attached.get(1, 0)); + + // Modify in-place + attached.put(3, 0, -7.5); + + // WriteMappedFileRegion on in-place buffer should only write footer + attached.WriteMappedFileRegion(addr); + + // deallocate detaches without freeing + attached.deallocate(); + REQUIRE(attached.empty()); + REQUIRE(attached.owns_buffer()); + + // Read back to verify + matrix_2d readback; + readback.ReadMappedFileRegion(addr); + REQUIRE(readback.get(3, 0) == -7.5); + REQUIRE(readback.get(1, 0) == 20.0); +} + +TEST_CASE("In-place mmap: deallocate does not crash", "[matrix_2d][mmap]") { + matrix_2d orig(3, 3); + for (UINT32 i = 0; i < 9; ++i) + orig.put(i / 3, i % 3, i + 1.0); + + std::size_t region_size = orig.get_size(); + std::vector region(region_size, 0); + void* addr = region.data(); + orig.WriteMappedFileRegion(addr); + + matrix_2d attached; + attached.AttachMappedFileRegion(addr); + REQUIRE(!attached.owns_buffer()); + + // deallocate should not crash (no delete[] on mmap pointer) + attached.deallocate(); + REQUIRE(attached.empty()); + REQUIRE(attached.owns_buffer()); + + // Re-attach and verify data still intact + attached.AttachMappedFileRegion(addr); + REQUIRE(attached.get(1, 1) == 5.0); + attached.deallocate(); +} + +TEST_CASE("In-place mmap: copy constructor makes owned copy", "[matrix_2d][mmap]") { + matrix_2d orig(2, 2); + orig.put(0, 0, 1.0); orig.put(0, 1, 2.0); + orig.put(1, 0, 3.0); orig.put(1, 1, 4.0); + + std::size_t region_size = orig.get_size(); + std::vector region(region_size, 0); + void* addr = region.data(); + orig.WriteMappedFileRegion(addr); + + matrix_2d attached; + attached.AttachMappedFileRegion(addr); + + // Copy constructor should create an owned copy + matrix_2d copy(attached); + REQUIRE(copy.owns_buffer()); + REQUIRE(copy.get(1, 1) == 4.0); + + // Modifying copy should not affect mmap + copy.put(1, 1, 99.0); + REQUIRE(attached.get(1, 1) == 4.0); + + attached.deallocate(); +} + +TEST_CASE("In-place mmap: operator= into owned buffer", "[matrix_2d][mmap]") { + matrix_2d orig(3, 2); + for (UINT32 r = 0; r < 3; ++r) + for (UINT32 c = 0; c < 2; ++c) + orig.put(r, c, r * 2.0 + c); + + std::size_t region_size = orig.get_size(); + std::vector region(region_size, 0); + void* addr = region.data(); + orig.WriteMappedFileRegion(addr); + + matrix_2d attached; + attached.AttachMappedFileRegion(addr); + + // Assign to a pre-allocated owned matrix of same size + matrix_2d dest(3, 2); + dest = attached; + REQUIRE(dest.owns_buffer()); + REQUIRE(dest.get(2, 1) == 5.0); + + attached.deallocate(); +} + +TEST_CASE("In-place mmap: region size alignment", "[matrix_2d][mmap]") { + // Verify that get_size() returns 8-byte aligned sizes for proper mmap alignment + matrix_2d full(7, 5); + REQUIRE(full.get_size() % 8 == 0); + + matrix_2d packed; + packed.redim_packed(11); + REQUIRE(packed.get_size() % 8 == 0); + + matrix_2d col_vec(100, 1); + REQUIRE(col_vec.get_size() % 8 == 0); +} + +TEST_CASE("operator<< binary size matches get_size for mmap regions", "[matrix_2d][mmap]") { + // Verify that operator<< writes exactly get_size() bytes in binary mode. + // A mismatch here causes mmap region offsets to be wrong for all blocks + // after the first, leading to corrupted header reads. + + auto check_stream_size = [](matrix_2d& m, const std::string& label) { + std::ostringstream oss; + oss.iword(0) = binary; + oss << m; + std::size_t stream_bytes = oss.str().size(); + std::size_t region_bytes = m.get_size(); + REQUIRE(stream_bytes == region_bytes); + }; + + // Full matrix (column vector — the case that crashed) + matrix_2d col_vec(50, 1); + for (UINT32 r = 0; r < 50; ++r) + col_vec.put(r, 0, static_cast(r)); + check_stream_size(col_vec, "column vector"); + + // Full matrix + matrix_2d full(7, 5); + for (UINT32 r = 0; r < 7; ++r) + for (UINT32 c = 0; c < 5; ++c) + full.put(r, c, static_cast(r * 10 + c)); + check_stream_size(full, "full 7x5"); + + // Packed lower-triangular + matrix_2d packed; + packed.redim_packed(6); + for (UINT32 r = 0; r < 6; ++r) + for (UINT32 c = 0; c <= r; ++c) + packed.put(r, c, static_cast(r * 10 + c)); + check_stream_size(packed, "packed 6x6"); + + // Full matrix with slack (redim + shrink, simulating staged adjustment) + matrix_2d with_slack; + with_slack.redim(100, 1); + with_slack.shrink(30, 0); // _rows=70, _mem_rows=100 + for (UINT32 r = 0; r < 70; ++r) + with_slack.put(r, 0, static_cast(r)); + check_stream_size(with_slack, "column vector with slack"); +}