diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index cebdab48..37b8b268 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -241,7 +241,7 @@ public: std::vector maps_; - std::chrono::system_clock::time_point volumeTime_ {}; + std::chrono::system_clock::time_point selectedTime_ {}; public slots: void UpdateMapParameters(double latitude, @@ -997,22 +997,15 @@ void MainWindowImpl::ConnectAnimationSignals() connect(timelineManager_.get(), &manager::TimelineManager::SelectedTimeUpdated, - [this]() - { - for (auto map : maps_) - { - QMetaObject::invokeMethod( - map, static_cast(&QWidget::update)); - } - }); - connect(timelineManager_.get(), - &manager::TimelineManager::VolumeTimeUpdated, [this](std::chrono::system_clock::time_point dateTime) { - volumeTime_ = dateTime; + selectedTime_ = dateTime; + for (auto map : maps_) { map->SelectTime(dateTime); + QMetaObject::invokeMethod( + map, static_cast(&QWidget::update)); } }); @@ -1400,7 +1393,8 @@ void MainWindowImpl::SelectRadarProduct(map::MapWidget* mapWidget, UpdateRadarProductSettings(); } - mapWidget->SelectRadarProduct(group, productName, productCode, volumeTime_); + mapWidget->SelectRadarProduct( + group, productName, productCode, selectedTime_); } void MainWindowImpl::SetActiveMap(map::MapWidget* mapWidget) diff --git a/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp b/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp index 1c093e9b..4659f079 100644 --- a/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp @@ -91,13 +91,7 @@ public: const std::string& radarId, common::RadarProductGroup group, const std::string& product) : - radarId_ {radarId}, - group_ {group}, - product_ {product}, - refreshEnabled_ {false}, - refreshTimer_ {threadPool_}, - refreshTimerMutex_ {}, - provider_ {nullptr} + radarId_ {radarId}, group_ {group}, product_ {product} { connect(this, &ProviderManager::NewDataAvailable, @@ -115,10 +109,10 @@ public: const std::string radarId_; const common::RadarProductGroup group_; const std::string product_; - bool refreshEnabled_; - boost::asio::steady_timer refreshTimer_; - std::mutex refreshTimerMutex_; - std::shared_ptr provider_; + bool refreshEnabled_ {false}; + boost::asio::steady_timer refreshTimer_ {threadPool_}; + std::mutex refreshTimerMutex_ {}; + std::shared_ptr provider_ {nullptr}; signals: void NewDataAvailable(common::RadarProductGroup group, @@ -136,24 +130,8 @@ public: initialized_ {false}, level3ProductsInitialized_ {false}, radarSite_ {config::RadarSite::Get(radarId)}, - coordinates0_5Degree_ {}, - coordinates1Degree_ {}, - level2ProductRecords_ {}, - level2ProductRecentRecords_ {}, - level3ProductRecordsMap_ {}, - level3ProductRecentRecordsMap_ {}, - level2ProductRecordMutex_ {}, - level3ProductRecordMutex_ {}, level2ProviderManager_ {std::make_shared( - self_, radarId_, common::RadarProductGroup::Level2)}, - level3ProviderManagerMap_ {}, - level3ProviderManagerMutex_ {}, - initializeMutex_ {}, - level3ProductsInitializeMutex_ {}, - loadLevel2DataMutex_ {}, - loadLevel3DataMutex_ {}, - availableCategoryMap_ {}, - availableCategoryMutex_ {} + self_, radarId_, common::RadarProductGroup::Level2)} { if (radarSite_ == nullptr) { @@ -198,9 +176,9 @@ public: void RefreshData(std::shared_ptr providerManager); void RefreshDataSync(std::shared_ptr providerManager); - std::tuple, - std::chrono::system_clock::time_point> - GetLevel2ProductRecord(std::chrono::system_clock::time_point time); + std::map> + GetLevel2ProductRecords(std::chrono::system_clock::time_point time); std::tuple, std::chrono::system_clock::time_point> GetLevel3ProductRecord(const std::string& product, @@ -247,30 +225,30 @@ public: std::shared_ptr radarSite_; std::size_t cacheLimit_ {6u}; - std::vector coordinates0_5Degree_; - std::vector coordinates1Degree_; + std::vector coordinates0_5Degree_ {}; + std::vector coordinates1Degree_ {}; - RadarProductRecordMap level2ProductRecords_; - RadarProductRecordList level2ProductRecentRecords_; + RadarProductRecordMap level2ProductRecords_ {}; + RadarProductRecordList level2ProductRecentRecords_ {}; std::unordered_map - level3ProductRecordsMap_; + level3ProductRecordsMap_ {}; std::unordered_map - level3ProductRecentRecordsMap_; - std::shared_mutex level2ProductRecordMutex_; - std::shared_mutex level3ProductRecordMutex_; + level3ProductRecentRecordsMap_ {}; + std::shared_mutex level2ProductRecordMutex_ {}; + std::shared_mutex level3ProductRecordMutex_ {}; std::shared_ptr level2ProviderManager_; std::unordered_map> - level3ProviderManagerMap_; - std::shared_mutex level3ProviderManagerMutex_; + level3ProviderManagerMap_ {}; + std::shared_mutex level3ProviderManagerMutex_ {}; - std::mutex initializeMutex_; - std::mutex level3ProductsInitializeMutex_; - std::mutex loadLevel2DataMutex_; - std::mutex loadLevel3DataMutex_; + std::mutex initializeMutex_ {}; + std::mutex level3ProductsInitializeMutex_ {}; + std::mutex loadLevel2DataMutex_ {}; + std::mutex loadLevel3DataMutex_ {}; - common::Level3ProductCategoryMap availableCategoryMap_; - std::shared_mutex availableCategoryMutex_; + common::Level3ProductCategoryMap availableCategoryMap_ {}; + std::shared_mutex availableCategoryMutex_ {}; std::unordered_map, @@ -1173,60 +1151,91 @@ void RadarProductManagerImpl::PopulateProductTimes( }); } -std::tuple, - std::chrono::system_clock::time_point> -RadarProductManagerImpl::GetLevel2ProductRecord( +std::map> +RadarProductManagerImpl::GetLevel2ProductRecords( std::chrono::system_clock::time_point time) { - std::shared_ptr record {nullptr}; - RadarProductRecordMap::const_pointer recordPtr {nullptr}; - std::chrono::system_clock::time_point recordTime {time}; + std::map> + records {}; + std::vector recordPtrs {}; // Ensure Level 2 product records are updated PopulateLevel2ProductTimes(time); - if (!level2ProductRecords_.empty() && - time == std::chrono::system_clock::time_point {}) { - // If a default-initialized time point is given, return the latest record - recordPtr = &(*level2ProductRecords_.rbegin()); - } - else - { - recordPtr = - scwx::util::GetBoundedElementPointer(level2ProductRecords_, time); - } + std::shared_lock lock {level2ProductRecordMutex_}; - if (recordPtr != nullptr) - { - // Don't check for an exact time match for level 2 products - recordTime = recordPtr->first; - record = recordPtr->second.lock(); - } + if (!level2ProductRecords_.empty() && + time == std::chrono::system_clock::time_point {}) + { + // If a default-initialized time point is given, return the latest + // record + recordPtrs.push_back(&(*level2ProductRecords_.rbegin())); + } + else + { + // Get the requested record + auto recordIt = + scwx::util::GetBoundedElementIterator(level2ProductRecords_, time); - if (recordPtr != nullptr && record == nullptr && - recordTime != std::chrono::system_clock::time_point {}) - { - // Product is expired, reload it - std::shared_ptr request = - std::make_shared(radarId_); - - QObject::connect( - request.get(), - &request::NexradFileRequest::RequestComplete, - self_, - [this](std::shared_ptr request) + if (recordIt != level2ProductRecords_.cend()) { - if (request->radar_product_record() != nullptr) - { - Q_EMIT self_->DataReloaded(request->radar_product_record()); - } - }); + recordPtrs.push_back(&(*(recordIt))); - self_->LoadLevel2Data(recordTime, request); + // The requested time may be in the previous record, so get that too + if (recordIt != level2ProductRecords_.cbegin()) + { + recordPtrs.push_back(&(*(--recordIt))); + } + } + } } - return {record, recordTime}; + // For each record pointer + for (auto& recordPtr : recordPtrs) + { + std::shared_ptr record {nullptr}; + std::chrono::system_clock::time_point recordTime {time}; + + if (recordPtr != nullptr) + { + // Don't check for an exact time match for level 2 products + recordTime = recordPtr->first; + record = recordPtr->second.lock(); + } + + if (recordPtr != nullptr && record == nullptr && + recordTime != std::chrono::system_clock::time_point {}) + { + // Product is expired, reload it + std::shared_ptr request = + std::make_shared(radarId_); + + QObject::connect( + request.get(), + &request::NexradFileRequest::RequestComplete, + self_, + [this](std::shared_ptr request) + { + if (request->radar_product_record() != nullptr) + { + Q_EMIT self_->DataReloaded(request->radar_product_record()); + } + }); + + self_->LoadLevel2Data(recordTime, request); + } + + if (record != nullptr) + { + // Return valid records + records.insert_or_assign(recordTime, record); + } + } + + return records; } std::tuple, @@ -1399,19 +1408,46 @@ RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType, { std::shared_ptr radarData = nullptr; float elevationCut = 0.0f; - std::vector elevationCuts; + std::vector elevationCuts {}; + std::chrono::system_clock::time_point foundTime {}; - std::shared_ptr record; - std::tie(record, time) = p->GetLevel2ProductRecord(time); + auto records = p->GetLevel2ProductRecords(time); - if (record != nullptr) + for (auto& recordPair : records) { - std::tie(radarData, elevationCut, elevationCuts) = - record->level2_file()->GetElevationScan( - dataBlockType, elevation, time); + auto& record = recordPair.second; + + if (record != nullptr) + { + std::shared_ptr recordRadarData = nullptr; + float recordElevationCut = 0.0f; + std::vector recordElevationCuts; + + std::tie(recordRadarData, recordElevationCut, recordElevationCuts) = + record->level2_file()->GetElevationScan( + dataBlockType, elevation, time); + + if (recordRadarData != nullptr) + { + auto& radarData0 = (*recordRadarData)[0]; + auto collectionTime = + scwx::util::TimePoint(radarData0->modified_julian_date(), + radarData0->collection_time()); + + // Find the newest radar data, not newer than the selected time + if (radarData == nullptr || + (collectionTime <= time && foundTime < collectionTime)) + { + radarData = recordRadarData; + elevationCut = recordElevationCut; + elevationCuts = std::move(recordElevationCuts); + foundTime = collectionTime; + } + } + } } - return {radarData, elevationCut, elevationCuts, time}; + return {radarData, elevationCut, elevationCuts, foundTime}; } std::tuple, @@ -1449,7 +1485,7 @@ std::vector RadarProductManager::GetLevel3Products() void RadarProductManager::SetCacheLimit(size_t cacheLimit) { - p->cacheLimit_ = cacheLimit; + p->cacheLimit_ = std::max(cacheLimit, 6u); } void RadarProductManager::UpdateAvailableProducts() diff --git a/scwx-qt/source/scwx/qt/manager/radar_product_manager.hpp b/scwx-qt/source/scwx/qt/manager/radar_product_manager.hpp index 4a3edabf..17dfa551 100644 --- a/scwx-qt/source/scwx/qt/manager/radar_product_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/radar_product_manager.hpp @@ -132,6 +132,7 @@ public: /** * @brief Set the maximum number of products of each type that may be cached. + * The cache limit cannot be set lower than 6. * * @param [in] cacheLimit The maximum number of products of each type */ diff --git a/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp b/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp index 1d48ac9c..f0870f93 100644 --- a/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp @@ -336,10 +336,10 @@ void TimelineManager::ReceiveMapWidgetPainted(std::size_t mapIndex) std::unique_lock lock {p->radarSweepMonitorMutex_}; // If the radar sweep has been updated - if (p->radarSweepsUpdated_.contains(mapIndex)) + if (p->radarSweepsUpdated_.contains(mapIndex) && + !p->radarSweepsComplete_.contains(mapIndex)) { // Mark the radar sweep complete - p->radarSweepsUpdated_.erase(mapIndex); p->radarSweepsComplete_.insert(mapIndex); // If all sweeps have completed rendering @@ -466,20 +466,12 @@ void TimelineManager::Impl::PlaySync() // Select the time auto selectTimeStart = std::chrono::steady_clock::now(); - auto [volumeTimeUpdated, selectedTimeUpdated] = SelectTime(newTime); + SelectTime(newTime); auto selectTimeEnd = std::chrono::steady_clock::now(); auto elapsedTime = selectTimeEnd - selectTimeStart; - if (volumeTimeUpdated) - { - // Wait for radar sweeps to update - RadarSweepMonitorWait(radarSweepMonitorLock); - } - else - { - // Disable radar sweep monitor - RadarSweepMonitorDisable(); - } + // Wait for radar sweeps to update + RadarSweepMonitorWait(radarSweepMonitorLock); // Calculate the interval until the next update, prior to selecting std::chrono::milliseconds interval; @@ -639,79 +631,63 @@ void TimelineManager::Impl::Step(Direction direction) // Take a lock for time selection std::unique_lock lock {selectTimeMutex_}; - // Determine time to get active volume times - std::chrono::system_clock::time_point queryTime = adjustedTime_; - if (queryTime == std::chrono::system_clock::time_point {}) + std::chrono::system_clock::time_point newTime = selectedTime_; + + if (newTime == std::chrono::system_clock::time_point {}) { - queryTime = std::chrono::system_clock::now(); - } - - // Request active volume times - auto radarProductManager = - manager::RadarProductManager::Instance(radarSite_); - auto volumeTimes = radarProductManager->GetActiveVolumeTimes(queryTime); - - if (volumeTimes.empty()) - { - logger_->debug("No products to step through"); - return; - } - - // Dynamically update maximum cached volume scans - UpdateCacheLimit(radarProductManager, volumeTimes); - - std::set::const_iterator it; - - if (adjustedTime_ == std::chrono::system_clock::time_point {}) - { - // If the adjusted time is live, get the last element in the set - it = std::prev(volumeTimes.cend()); - } - else - { - // Get the current element in the set - it = scwx::util::GetBoundedElementIterator(volumeTimes, adjustedTime_); - } - - if (it == volumeTimes.cend()) - { - // Should not get here, but protect against an error - logger_->error("No suitable volume time found"); - return; - } - - if (direction == Direction::Back) - { - // Only if we aren't at the beginning of the volume times set - if (it != volumeTimes.cbegin()) + if (direction == Direction::Back) { - // Select the previous time - adjustedTime_ = *(--it); - selectedTime_ = adjustedTime_; - - logger_->debug("Volume time updated: {}", - scwx::util::TimeString(adjustedTime_)); - - Q_EMIT self_->LiveStateUpdated(false); - Q_EMIT self_->VolumeTimeUpdated(adjustedTime_); - Q_EMIT self_->SelectedTimeUpdated(adjustedTime_); + newTime = std::chrono::floor( + std::chrono::system_clock::now()); + } + else + { + // Cannot step forward any further + return; } } - else + + // Unlock prior to selecting time + lock.unlock(); + + // Lock radar sweep monitor + std::unique_lock radarSweepMonitorLock {radarSweepMonitorMutex_}; + + // Attempt to step forward or backward up to 30 minutes until an update is + // received on at least one map + for (std::size_t i = 0; i < 30; ++i) { - // Only if we aren't at the end of the volume times set - if (it != std::prev(volumeTimes.cend())) + using namespace std::chrono_literals; + + // Increment/decrement selected time by one minute + if (direction == Direction::Back) { - // Select the next time - adjustedTime_ = *(++it); - selectedTime_ = adjustedTime_; + newTime -= 1min; + } + else + { + newTime += 1min; - logger_->debug("Volume time updated: {}", - scwx::util::TimeString(adjustedTime_)); + // If the new time is more than 2 minutes in the future, stop stepping + if (newTime > std::chrono::system_clock::now() + 2min) + { + break; + } + } - Q_EMIT self_->LiveStateUpdated(false); - Q_EMIT self_->VolumeTimeUpdated(adjustedTime_); - Q_EMIT self_->SelectedTimeUpdated(adjustedTime_); + // Reset radar sweep monitor in preparation for update + RadarSweepMonitorReset(); + + // Select the time + SelectTime(newTime); + + // Wait for radar sweeps to update + RadarSweepMonitorWait(radarSweepMonitorLock); + + // Check for updates + if (!radarSweepsUpdated_.empty()) + { + break; } } } diff --git a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp index 12d94d27..e8519c47 100644 --- a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp +++ b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp @@ -65,6 +65,8 @@ AnimationDockWidget::AnimationDockWidget(QWidget* parent) : QDateTime currentDateTime = QDateTime::currentDateTimeUtc(); QDate currentDate = currentDateTime.date(); QTime currentTime = currentDateTime.time(); + currentTime = currentTime.addSecs(-currentTime.second() + 59); + ui->dateEdit->setDate(currentDate); ui->timeEdit->setTime(currentTime); ui->dateEdit->setMaximumDate(currentDateTime.date()); diff --git a/scwx-qt/source/scwx/qt/view/level2_product_view.cpp b/scwx-qt/source/scwx/qt/view/level2_product_view.cpp index 0938f614..a5a26157 100644 --- a/scwx-qt/source/scwx/qt/view/level2_product_view.cpp +++ b/scwx-qt/source/scwx/qt/view/level2_product_view.cpp @@ -182,12 +182,9 @@ void Level2ProductView::ConnectRadarProductManager() [this](std::shared_ptr record) { if (record->radar_product_group() == - common::RadarProductGroup::Level2 && - std::chrono::floor(record->time()) == - selected_time()) + common::RadarProductGroup::Level2) { - // If the data associated with the currently selected time is - // reloaded, update the view + // If level 2 data associated was reloaded, update the view Update(); } }); @@ -500,7 +497,7 @@ void Level2ProductView::UpdateColorTableLut() void Level2ProductView::ComputeSweep() { - logger_->debug("ComputeSweep()"); + logger_->trace("ComputeSweep()"); boost::timer::cpu_timer timer; @@ -517,17 +514,10 @@ void Level2ProductView::ComputeSweep() std::shared_ptr radarData; std::chrono::system_clock::time_point requestedTime {selected_time()}; - std::chrono::system_clock::time_point foundTime; - std::tie(radarData, p->elevationCut_, p->elevationCuts_, foundTime) = + std::tie(radarData, p->elevationCut_, p->elevationCuts_, std::ignore) = radarProductManager->GetLevel2Data( p->dataBlockType_, p->selectedElevation_, requestedTime); - // If a different time was found than what was requested, update it - if (requestedTime != foundTime) - { - SelectTime(foundTime); - } - if (radarData == nullptr) { Q_EMIT SweepNotComputed(types::NoUpdateReason::NotLoaded); @@ -539,6 +529,8 @@ void Level2ProductView::ComputeSweep() return; } + logger_->debug("Computing Sweep"); + std::size_t radials = radarData->crbegin()->first + 1; std::size_t vertexRadials = radials; diff --git a/scwx-qt/source/scwx/qt/view/level3_radial_view.cpp b/scwx-qt/source/scwx/qt/view/level3_radial_view.cpp index 5611fdf5..5fa3531f 100644 --- a/scwx-qt/source/scwx/qt/view/level3_radial_view.cpp +++ b/scwx-qt/source/scwx/qt/view/level3_radial_view.cpp @@ -117,7 +117,7 @@ std::tuple Level3RadialView::GetMomentData() const void Level3RadialView::ComputeSweep() { - logger_->debug("ComputeSweep()"); + logger_->trace("ComputeSweep()"); boost::timer::cpu_timer timer; @@ -185,6 +185,8 @@ void Level3RadialView::ComputeSweep() return; } + logger_->debug("Computing Sweep"); + // A message with radial data should either have a Digital Radial Data // Array Packet, or a Radial Data Array Packet std::shared_ptr diff --git a/scwx-qt/source/scwx/qt/view/level3_raster_view.cpp b/scwx-qt/source/scwx/qt/view/level3_raster_view.cpp index fefeb587..b51c2cd0 100644 --- a/scwx-qt/source/scwx/qt/view/level3_raster_view.cpp +++ b/scwx-qt/source/scwx/qt/view/level3_raster_view.cpp @@ -101,7 +101,7 @@ std::tuple Level3RasterView::GetMomentData() const void Level3RasterView::ComputeSweep() { - logger_->debug("ComputeSweep()"); + logger_->trace("ComputeSweep()"); boost::timer::cpu_timer timer; @@ -169,6 +169,8 @@ void Level3RasterView::ComputeSweep() return; } + logger_->debug("Computing Sweep"); + // A message with raster data should have a Raster Data Packet std::shared_ptr rasterData = nullptr; diff --git a/wxdata/source/scwx/provider/aws_nexrad_data_provider.cpp b/wxdata/source/scwx/provider/aws_nexrad_data_provider.cpp index 4e139bac..c4ac523b 100644 --- a/wxdata/source/scwx/provider/aws_nexrad_data_provider.cpp +++ b/wxdata/source/scwx/provider/aws_nexrad_data_provider.cpp @@ -183,6 +183,7 @@ AwsNexradDataProvider::GetTimePointsByDate( std::shared_lock lock(p->objectsMutex_); // Is the date present in the date list? + bool currentDatePresent; auto currentDateIterator = std::find(p->objectDates_.cbegin(), p->objectDates_.cend(), day); if (currentDateIterator == p->objectDates_.cend()) @@ -199,6 +200,12 @@ AwsNexradDataProvider::GetTimePointsByDate( // Re-lock mutex lock.lock(); + + currentDatePresent = false; + } + else + { + currentDatePresent = true; } // Determine objects to retrieve @@ -216,7 +223,7 @@ AwsNexradDataProvider::GetTimePointsByDate( // If we haven't updated the most recently queried dates yet, because the // date was already cached, update - if (currentDateIterator != p->objectDates_.cend()) + if (currentDatePresent) { p->UpdateObjectDates(date); } diff --git a/wxdata/source/scwx/wsr88d/ar2v_file.cpp b/wxdata/source/scwx/wsr88d/ar2v_file.cpp index 6d951d6c..bdd1bcfc 100644 --- a/wxdata/source/scwx/wsr88d/ar2v_file.cpp +++ b/wxdata/source/scwx/wsr88d/ar2v_file.cpp @@ -65,7 +65,9 @@ public: std::map> radarData_ {}; std::map>> + std::map>>> index_ {}; std::list rawRecords_ {}; @@ -130,9 +132,9 @@ std::shared_ptr Ar2vFile::vcp_data() const } std::tuple, float, std::vector> -Ar2vFile::GetElevationScan(rda::DataBlockType dataBlockType, - float elevation, - std::chrono::system_clock::time_point /*time*/) const +Ar2vFile::GetElevationScan(rda::DataBlockType dataBlockType, + float elevation, + std::chrono::system_clock::time_point time) const { logger_->debug("GetElevationScan: {} degrees", elevation); @@ -152,6 +154,7 @@ Ar2vFile::GetElevationScan(rda::DataBlockType dataBlockType, std::uint16_t lowerBound = scans.cbegin()->first; std::uint16_t upperBound = scans.crbegin()->first; + // Find closest elevation match for (auto& scan : scans) { if (scan.first > lowerBound && scan.first <= codedElevation) @@ -173,15 +176,25 @@ Ar2vFile::GetElevationScan(rda::DataBlockType dataBlockType, std::abs(static_cast(codedElevation) - static_cast(upperBound)); - if (lowerDelta < upperDelta) + // Select closest elevation match + std::uint16_t elevationIndex = + (lowerDelta < upperDelta) ? lowerBound : upperBound; + elevationCut = elevationIndex / scaleFactor; + + // Select closest time match, not newer than the selected time + std::chrono::system_clock::time_point foundTime {}; + auto& elevationScans = scans.at(elevationIndex); + + for (auto& scan : elevationScans) { - elevationScan = scans.at(lowerBound); - elevationCut = lowerBound / scaleFactor; - } - else - { - elevationScan = scans.at(upperBound); - elevationCut = upperBound / scaleFactor; + auto& scanTime = scan.first; + + if (elevationScan == nullptr || + (scanTime <= time && scanTime > foundTime)) + { + elevationScan = scan.second; + foundTime = scanTime; + } } } @@ -460,8 +473,8 @@ void Ar2vFileImpl::IndexFile() waveformType = vcpData_->waveform_type(elevationCut.first); } else if ((digitalRadarData0 = - std::dynamic_pointer_cast( - (*elevationCut.second)[0])) != nullptr) + std::dynamic_pointer_cast(radial0)) != + nullptr) { elevationAngle = digitalRadarData0->elevation_angle_raw(); } @@ -488,8 +501,10 @@ void Ar2vFileImpl::IndexFile() if (momentData != nullptr) { - // TODO: Handle multiple elevation scans - index_[dataBlockType][elevationAngle] = elevationCut.second; + auto time = util::TimePoint(radial0->modified_julian_date(), + radial0->collection_time()); + + index_[dataBlockType][elevationAngle][time] = elevationCut.second; } } }