mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 16:40:04 +00:00 
			
		
		
		
	Merge pull request #297 from dpaulat/feature/level2-sweeps
Intermediate Level 2 Sweeps
This commit is contained in:
		
						commit
						dbfacdfd28
					
				
					 10 changed files with 248 additions and 221 deletions
				
			
		|  | @ -241,7 +241,7 @@ public: | ||||||
| 
 | 
 | ||||||
|    std::vector<map::MapWidget*> maps_; |    std::vector<map::MapWidget*> maps_; | ||||||
| 
 | 
 | ||||||
|    std::chrono::system_clock::time_point volumeTime_ {}; |    std::chrono::system_clock::time_point selectedTime_ {}; | ||||||
| 
 | 
 | ||||||
| public slots: | public slots: | ||||||
|    void UpdateMapParameters(double latitude, |    void UpdateMapParameters(double latitude, | ||||||
|  | @ -997,22 +997,15 @@ void MainWindowImpl::ConnectAnimationSignals() | ||||||
| 
 | 
 | ||||||
|    connect(timelineManager_.get(), |    connect(timelineManager_.get(), | ||||||
|            &manager::TimelineManager::SelectedTimeUpdated, |            &manager::TimelineManager::SelectedTimeUpdated, | ||||||
|            [this]() |  | ||||||
|            { |  | ||||||
|               for (auto map : maps_) |  | ||||||
|               { |  | ||||||
|                  QMetaObject::invokeMethod( |  | ||||||
|                     map, static_cast<void (QWidget::*)()>(&QWidget::update)); |  | ||||||
|               } |  | ||||||
|            }); |  | ||||||
|    connect(timelineManager_.get(), |  | ||||||
|            &manager::TimelineManager::VolumeTimeUpdated, |  | ||||||
|            [this](std::chrono::system_clock::time_point dateTime) |            [this](std::chrono::system_clock::time_point dateTime) | ||||||
|            { |            { | ||||||
|               volumeTime_ = dateTime; |               selectedTime_ = dateTime; | ||||||
|  | 
 | ||||||
|               for (auto map : maps_) |               for (auto map : maps_) | ||||||
|               { |               { | ||||||
|                  map->SelectTime(dateTime); |                  map->SelectTime(dateTime); | ||||||
|  |                  QMetaObject::invokeMethod( | ||||||
|  |                     map, static_cast<void (QWidget::*)()>(&QWidget::update)); | ||||||
|               } |               } | ||||||
|            }); |            }); | ||||||
| 
 | 
 | ||||||
|  | @ -1400,7 +1393,8 @@ void MainWindowImpl::SelectRadarProduct(map::MapWidget*           mapWidget, | ||||||
|       UpdateRadarProductSettings(); |       UpdateRadarProductSettings(); | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    mapWidget->SelectRadarProduct(group, productName, productCode, volumeTime_); |    mapWidget->SelectRadarProduct( | ||||||
|  |       group, productName, productCode, selectedTime_); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MainWindowImpl::SetActiveMap(map::MapWidget* mapWidget) | void MainWindowImpl::SetActiveMap(map::MapWidget* mapWidget) | ||||||
|  |  | ||||||
|  | @ -91,13 +91,7 @@ public: | ||||||
|                             const std::string&        radarId, |                             const std::string&        radarId, | ||||||
|                             common::RadarProductGroup group, |                             common::RadarProductGroup group, | ||||||
|                             const std::string&        product) : |                             const std::string&        product) : | ||||||
|        radarId_ {radarId}, |        radarId_ {radarId}, group_ {group}, product_ {product} | ||||||
|        group_ {group}, |  | ||||||
|        product_ {product}, |  | ||||||
|        refreshEnabled_ {false}, |  | ||||||
|        refreshTimer_ {threadPool_}, |  | ||||||
|        refreshTimerMutex_ {}, |  | ||||||
|        provider_ {nullptr} |  | ||||||
|    { |    { | ||||||
|       connect(this, |       connect(this, | ||||||
|               &ProviderManager::NewDataAvailable, |               &ProviderManager::NewDataAvailable, | ||||||
|  | @ -115,10 +109,10 @@ public: | ||||||
|    const std::string                             radarId_; |    const std::string                             radarId_; | ||||||
|    const common::RadarProductGroup               group_; |    const common::RadarProductGroup               group_; | ||||||
|    const std::string                             product_; |    const std::string                             product_; | ||||||
|    bool                                          refreshEnabled_; |    bool                                          refreshEnabled_ {false}; | ||||||
|    boost::asio::steady_timer                     refreshTimer_; |    boost::asio::steady_timer                     refreshTimer_ {threadPool_}; | ||||||
|    std::mutex                                    refreshTimerMutex_; |    std::mutex                                    refreshTimerMutex_ {}; | ||||||
|    std::shared_ptr<provider::NexradDataProvider> provider_; |    std::shared_ptr<provider::NexradDataProvider> provider_ {nullptr}; | ||||||
| 
 | 
 | ||||||
| signals: | signals: | ||||||
|    void NewDataAvailable(common::RadarProductGroup             group, |    void NewDataAvailable(common::RadarProductGroup             group, | ||||||
|  | @ -136,24 +130,8 @@ public: | ||||||
|        initialized_ {false}, |        initialized_ {false}, | ||||||
|        level3ProductsInitialized_ {false}, |        level3ProductsInitialized_ {false}, | ||||||
|        radarSite_ {config::RadarSite::Get(radarId)}, |        radarSite_ {config::RadarSite::Get(radarId)}, | ||||||
|        coordinates0_5Degree_ {}, |  | ||||||
|        coordinates1Degree_ {}, |  | ||||||
|        level2ProductRecords_ {}, |  | ||||||
|        level2ProductRecentRecords_ {}, |  | ||||||
|        level3ProductRecordsMap_ {}, |  | ||||||
|        level3ProductRecentRecordsMap_ {}, |  | ||||||
|        level2ProductRecordMutex_ {}, |  | ||||||
|        level3ProductRecordMutex_ {}, |  | ||||||
|        level2ProviderManager_ {std::make_shared<ProviderManager>( |        level2ProviderManager_ {std::make_shared<ProviderManager>( | ||||||
|           self_, radarId_, common::RadarProductGroup::Level2)}, |           self_, radarId_, common::RadarProductGroup::Level2)} | ||||||
|        level3ProviderManagerMap_ {}, |  | ||||||
|        level3ProviderManagerMutex_ {}, |  | ||||||
|        initializeMutex_ {}, |  | ||||||
|        level3ProductsInitializeMutex_ {}, |  | ||||||
|        loadLevel2DataMutex_ {}, |  | ||||||
|        loadLevel3DataMutex_ {}, |  | ||||||
|        availableCategoryMap_ {}, |  | ||||||
|        availableCategoryMutex_ {} |  | ||||||
|    { |    { | ||||||
|       if (radarSite_ == nullptr) |       if (radarSite_ == nullptr) | ||||||
|       { |       { | ||||||
|  | @ -198,9 +176,9 @@ public: | ||||||
|    void RefreshData(std::shared_ptr<ProviderManager> providerManager); |    void RefreshData(std::shared_ptr<ProviderManager> providerManager); | ||||||
|    void RefreshDataSync(std::shared_ptr<ProviderManager> providerManager); |    void RefreshDataSync(std::shared_ptr<ProviderManager> providerManager); | ||||||
| 
 | 
 | ||||||
|    std::tuple<std::shared_ptr<types::RadarProductRecord>, |    std::map<std::chrono::system_clock::time_point, | ||||||
|               std::chrono::system_clock::time_point> |             std::shared_ptr<types::RadarProductRecord>> | ||||||
|    GetLevel2ProductRecord(std::chrono::system_clock::time_point time); |    GetLevel2ProductRecords(std::chrono::system_clock::time_point time); | ||||||
|    std::tuple<std::shared_ptr<types::RadarProductRecord>, |    std::tuple<std::shared_ptr<types::RadarProductRecord>, | ||||||
|               std::chrono::system_clock::time_point> |               std::chrono::system_clock::time_point> | ||||||
|    GetLevel3ProductRecord(const std::string&                    product, |    GetLevel3ProductRecord(const std::string&                    product, | ||||||
|  | @ -247,30 +225,30 @@ public: | ||||||
|    std::shared_ptr<config::RadarSite> radarSite_; |    std::shared_ptr<config::RadarSite> radarSite_; | ||||||
|    std::size_t                        cacheLimit_ {6u}; |    std::size_t                        cacheLimit_ {6u}; | ||||||
| 
 | 
 | ||||||
|    std::vector<float> coordinates0_5Degree_; |    std::vector<float> coordinates0_5Degree_ {}; | ||||||
|    std::vector<float> coordinates1Degree_; |    std::vector<float> coordinates1Degree_ {}; | ||||||
| 
 | 
 | ||||||
|    RadarProductRecordMap  level2ProductRecords_; |    RadarProductRecordMap   level2ProductRecords_ {}; | ||||||
|    RadarProductRecordList level2ProductRecentRecords_; |    RadarProductRecordList  level2ProductRecentRecords_ {}; | ||||||
|    std::unordered_map<std::string, RadarProductRecordMap> |    std::unordered_map<std::string, RadarProductRecordMap> | ||||||
|       level3ProductRecordsMap_; |       level3ProductRecordsMap_ {}; | ||||||
|    std::unordered_map<std::string, RadarProductRecordList> |    std::unordered_map<std::string, RadarProductRecordList> | ||||||
|                      level3ProductRecentRecordsMap_; |                      level3ProductRecentRecordsMap_ {}; | ||||||
|    std::shared_mutex level2ProductRecordMutex_; |    std::shared_mutex level2ProductRecordMutex_ {}; | ||||||
|    std::shared_mutex level3ProductRecordMutex_; |    std::shared_mutex level3ProductRecordMutex_ {}; | ||||||
| 
 | 
 | ||||||
|    std::shared_ptr<ProviderManager> level2ProviderManager_; |    std::shared_ptr<ProviderManager> level2ProviderManager_; | ||||||
|    std::unordered_map<std::string, std::shared_ptr<ProviderManager>> |    std::unordered_map<std::string, std::shared_ptr<ProviderManager>> | ||||||
|                      level3ProviderManagerMap_; |                      level3ProviderManagerMap_ {}; | ||||||
|    std::shared_mutex level3ProviderManagerMutex_; |    std::shared_mutex level3ProviderManagerMutex_ {}; | ||||||
| 
 | 
 | ||||||
|    std::mutex initializeMutex_; |    std::mutex initializeMutex_ {}; | ||||||
|    std::mutex level3ProductsInitializeMutex_; |    std::mutex level3ProductsInitializeMutex_ {}; | ||||||
|    std::mutex loadLevel2DataMutex_; |    std::mutex loadLevel2DataMutex_ {}; | ||||||
|    std::mutex loadLevel3DataMutex_; |    std::mutex loadLevel3DataMutex_ {}; | ||||||
| 
 | 
 | ||||||
|    common::Level3ProductCategoryMap availableCategoryMap_; |    common::Level3ProductCategoryMap availableCategoryMap_ {}; | ||||||
|    std::shared_mutex                availableCategoryMutex_; |    std::shared_mutex                availableCategoryMutex_ {}; | ||||||
| 
 | 
 | ||||||
|    std::unordered_map<boost::uuids::uuid, |    std::unordered_map<boost::uuids::uuid, | ||||||
|                       std::shared_ptr<ProviderManager>, |                       std::shared_ptr<ProviderManager>, | ||||||
|  | @ -1173,29 +1151,53 @@ void RadarProductManagerImpl::PopulateProductTimes( | ||||||
|                   }); |                   }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::tuple<std::shared_ptr<types::RadarProductRecord>, | std::map<std::chrono::system_clock::time_point, | ||||||
|            std::chrono::system_clock::time_point> |          std::shared_ptr<types::RadarProductRecord>> | ||||||
| RadarProductManagerImpl::GetLevel2ProductRecord( | RadarProductManagerImpl::GetLevel2ProductRecords( | ||||||
|    std::chrono::system_clock::time_point time) |    std::chrono::system_clock::time_point time) | ||||||
| { | { | ||||||
|    std::shared_ptr<types::RadarProductRecord> record {nullptr}; |    std::map<std::chrono::system_clock::time_point, | ||||||
|    RadarProductRecordMap::const_pointer       recordPtr {nullptr}; |             std::shared_ptr<types::RadarProductRecord>> | ||||||
|    std::chrono::system_clock::time_point      recordTime {time}; |                                                      records {}; | ||||||
|  |    std::vector<RadarProductRecordMap::const_pointer> recordPtrs {}; | ||||||
| 
 | 
 | ||||||
|    // Ensure Level 2 product records are updated
 |    // Ensure Level 2 product records are updated
 | ||||||
|    PopulateLevel2ProductTimes(time); |    PopulateLevel2ProductTimes(time); | ||||||
| 
 | 
 | ||||||
|  |    { | ||||||
|  |       std::shared_lock lock {level2ProductRecordMutex_}; | ||||||
|  | 
 | ||||||
|       if (!level2ProductRecords_.empty() && |       if (!level2ProductRecords_.empty() && | ||||||
|           time == std::chrono::system_clock::time_point {}) |           time == std::chrono::system_clock::time_point {}) | ||||||
|       { |       { | ||||||
|       // If a default-initialized time point is given, return the latest record
 |          // If a default-initialized time point is given, return the latest
 | ||||||
|       recordPtr = &(*level2ProductRecords_.rbegin()); |          // record
 | ||||||
|  |          recordPtrs.push_back(&(*level2ProductRecords_.rbegin())); | ||||||
|       } |       } | ||||||
|       else |       else | ||||||
|       { |       { | ||||||
|       recordPtr = |          // Get the requested record
 | ||||||
|          scwx::util::GetBoundedElementPointer(level2ProductRecords_, time); |          auto recordIt = | ||||||
|  |             scwx::util::GetBoundedElementIterator(level2ProductRecords_, time); | ||||||
|  | 
 | ||||||
|  |          if (recordIt != level2ProductRecords_.cend()) | ||||||
|  |          { | ||||||
|  |             recordPtrs.push_back(&(*(recordIt))); | ||||||
|  | 
 | ||||||
|  |             // The requested time may be in the previous record, so get that too
 | ||||||
|  |             if (recordIt != level2ProductRecords_.cbegin()) | ||||||
|  |             { | ||||||
|  |                recordPtrs.push_back(&(*(--recordIt))); | ||||||
|             } |             } | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // For each record pointer
 | ||||||
|  |    for (auto& recordPtr : recordPtrs) | ||||||
|  |    { | ||||||
|  |       std::shared_ptr<types::RadarProductRecord> record {nullptr}; | ||||||
|  |       std::chrono::system_clock::time_point      recordTime {time}; | ||||||
| 
 | 
 | ||||||
|       if (recordPtr != nullptr) |       if (recordPtr != nullptr) | ||||||
|       { |       { | ||||||
|  | @ -1226,7 +1228,14 @@ RadarProductManagerImpl::GetLevel2ProductRecord( | ||||||
|          self_->LoadLevel2Data(recordTime, request); |          self_->LoadLevel2Data(recordTime, request); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|    return {record, recordTime}; |       if (record != nullptr) | ||||||
|  |       { | ||||||
|  |          // Return valid records
 | ||||||
|  |          records.insert_or_assign(recordTime, record); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return records; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::tuple<std::shared_ptr<types::RadarProductRecord>, | std::tuple<std::shared_ptr<types::RadarProductRecord>, | ||||||
|  | @ -1399,19 +1408,46 @@ RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType, | ||||||
| { | { | ||||||
|    std::shared_ptr<wsr88d::rda::ElevationScan> radarData    = nullptr; |    std::shared_ptr<wsr88d::rda::ElevationScan> radarData    = nullptr; | ||||||
|    float                                       elevationCut = 0.0f; |    float                                       elevationCut = 0.0f; | ||||||
|    std::vector<float>                          elevationCuts; |    std::vector<float>                          elevationCuts {}; | ||||||
|  |    std::chrono::system_clock::time_point       foundTime {}; | ||||||
| 
 | 
 | ||||||
|    std::shared_ptr<types::RadarProductRecord> record; |    auto records = p->GetLevel2ProductRecords(time); | ||||||
|    std::tie(record, time) = p->GetLevel2ProductRecord(time); | 
 | ||||||
|  |    for (auto& recordPair : records) | ||||||
|  |    { | ||||||
|  |       auto& record = recordPair.second; | ||||||
| 
 | 
 | ||||||
|       if (record != nullptr) |       if (record != nullptr) | ||||||
|       { |       { | ||||||
|       std::tie(radarData, elevationCut, elevationCuts) = |          std::shared_ptr<wsr88d::rda::ElevationScan> recordRadarData = nullptr; | ||||||
|  |          float                                       recordElevationCut = 0.0f; | ||||||
|  |          std::vector<float>                          recordElevationCuts; | ||||||
|  | 
 | ||||||
|  |          std::tie(recordRadarData, recordElevationCut, recordElevationCuts) = | ||||||
|             record->level2_file()->GetElevationScan( |             record->level2_file()->GetElevationScan( | ||||||
|                dataBlockType, elevation, time); |                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<std::shared_ptr<wsr88d::rpg::Level3Message>, | std::tuple<std::shared_ptr<wsr88d::rpg::Level3Message>, | ||||||
|  | @ -1449,7 +1485,7 @@ std::vector<std::string> RadarProductManager::GetLevel3Products() | ||||||
| 
 | 
 | ||||||
| void RadarProductManager::SetCacheLimit(size_t cacheLimit) | void RadarProductManager::SetCacheLimit(size_t cacheLimit) | ||||||
| { | { | ||||||
|    p->cacheLimit_ = cacheLimit; |    p->cacheLimit_ = std::max<std::size_t>(cacheLimit, 6u); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RadarProductManager::UpdateAvailableProducts() | void RadarProductManager::UpdateAvailableProducts() | ||||||
|  |  | ||||||
|  | @ -132,6 +132,7 @@ public: | ||||||
| 
 | 
 | ||||||
|    /**
 |    /**
 | ||||||
|     * @brief Set the maximum number of products of each type that may be cached. |     * @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 |     * @param [in] cacheLimit The maximum number of products of each type | ||||||
|     */ |     */ | ||||||
|  |  | ||||||
|  | @ -336,10 +336,10 @@ void TimelineManager::ReceiveMapWidgetPainted(std::size_t mapIndex) | ||||||
|    std::unique_lock lock {p->radarSweepMonitorMutex_}; |    std::unique_lock lock {p->radarSweepMonitorMutex_}; | ||||||
| 
 | 
 | ||||||
|    // If the radar sweep has been updated
 |    // 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
 |       // Mark the radar sweep complete
 | ||||||
|       p->radarSweepsUpdated_.erase(mapIndex); |  | ||||||
|       p->radarSweepsComplete_.insert(mapIndex); |       p->radarSweepsComplete_.insert(mapIndex); | ||||||
| 
 | 
 | ||||||
|       // If all sweeps have completed rendering
 |       // If all sweeps have completed rendering
 | ||||||
|  | @ -466,20 +466,12 @@ void TimelineManager::Impl::PlaySync() | ||||||
| 
 | 
 | ||||||
|    // Select the time
 |    // Select the time
 | ||||||
|    auto selectTimeStart = std::chrono::steady_clock::now(); |    auto selectTimeStart = std::chrono::steady_clock::now(); | ||||||
|    auto [volumeTimeUpdated, selectedTimeUpdated] = SelectTime(newTime); |    SelectTime(newTime); | ||||||
|    auto selectTimeEnd = std::chrono::steady_clock::now(); |    auto selectTimeEnd = std::chrono::steady_clock::now(); | ||||||
|    auto elapsedTime   = selectTimeEnd - selectTimeStart; |    auto elapsedTime   = selectTimeEnd - selectTimeStart; | ||||||
| 
 | 
 | ||||||
|    if (volumeTimeUpdated) |  | ||||||
|    { |  | ||||||
|    // Wait for radar sweeps to update
 |    // Wait for radar sweeps to update
 | ||||||
|    RadarSweepMonitorWait(radarSweepMonitorLock); |    RadarSweepMonitorWait(radarSweepMonitorLock); | ||||||
|    } |  | ||||||
|    else |  | ||||||
|    { |  | ||||||
|       // Disable radar sweep monitor
 |  | ||||||
|       RadarSweepMonitorDisable(); |  | ||||||
|    } |  | ||||||
| 
 | 
 | ||||||
|    // Calculate the interval until the next update, prior to selecting
 |    // Calculate the interval until the next update, prior to selecting
 | ||||||
|    std::chrono::milliseconds interval; |    std::chrono::milliseconds interval; | ||||||
|  | @ -639,79 +631,63 @@ void TimelineManager::Impl::Step(Direction direction) | ||||||
|    // Take a lock for time selection
 |    // Take a lock for time selection
 | ||||||
|    std::unique_lock lock {selectTimeMutex_}; |    std::unique_lock lock {selectTimeMutex_}; | ||||||
| 
 | 
 | ||||||
|    // Determine time to get active volume times
 |    std::chrono::system_clock::time_point newTime = selectedTime_; | ||||||
|    std::chrono::system_clock::time_point queryTime = adjustedTime_; | 
 | ||||||
|    if (queryTime == std::chrono::system_clock::time_point {}) |    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<std::chrono::system_clock::time_point>::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) |       if (direction == Direction::Back) | ||||||
|       { |       { | ||||||
|       // Only if we aren't at the beginning of the volume times set
 |          newTime = std::chrono::floor<std::chrono::minutes>( | ||||||
|       if (it != volumeTimes.cbegin()) |             std::chrono::system_clock::now()); | ||||||
|       { |  | ||||||
|          // 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_); |  | ||||||
|       } |  | ||||||
|       } |       } | ||||||
|       else |       else | ||||||
|       { |       { | ||||||
|       // Only if we aren't at the end of the volume times set
 |          // Cannot step forward any further
 | ||||||
|       if (it != std::prev(volumeTimes.cend())) |          return; | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // 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) | ||||||
|    { |    { | ||||||
|          // Select the next time
 |       using namespace std::chrono_literals; | ||||||
|          adjustedTime_ = *(++it); |  | ||||||
|          selectedTime_ = adjustedTime_; |  | ||||||
| 
 | 
 | ||||||
|          logger_->debug("Volume time updated: {}", |       // Increment/decrement selected time by one minute
 | ||||||
|                         scwx::util::TimeString(adjustedTime_)); |       if (direction == Direction::Back) | ||||||
|  |       { | ||||||
|  |          newTime -= 1min; | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |       { | ||||||
|  |          newTime += 1min; | ||||||
| 
 | 
 | ||||||
|          Q_EMIT self_->LiveStateUpdated(false); |          // If the new time is more than 2 minutes in the future, stop stepping
 | ||||||
|          Q_EMIT self_->VolumeTimeUpdated(adjustedTime_); |          if (newTime > std::chrono::system_clock::now() + 2min) | ||||||
|          Q_EMIT self_->SelectedTimeUpdated(adjustedTime_); |          { | ||||||
|  |             break; | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // 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; | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -65,6 +65,8 @@ AnimationDockWidget::AnimationDockWidget(QWidget* parent) : | ||||||
|    QDateTime currentDateTime = QDateTime::currentDateTimeUtc(); |    QDateTime currentDateTime = QDateTime::currentDateTimeUtc(); | ||||||
|    QDate     currentDate     = currentDateTime.date(); |    QDate     currentDate     = currentDateTime.date(); | ||||||
|    QTime     currentTime     = currentDateTime.time(); |    QTime     currentTime     = currentDateTime.time(); | ||||||
|  |    currentTime               = currentTime.addSecs(-currentTime.second() + 59); | ||||||
|  | 
 | ||||||
|    ui->dateEdit->setDate(currentDate); |    ui->dateEdit->setDate(currentDate); | ||||||
|    ui->timeEdit->setTime(currentTime); |    ui->timeEdit->setTime(currentTime); | ||||||
|    ui->dateEdit->setMaximumDate(currentDateTime.date()); |    ui->dateEdit->setMaximumDate(currentDateTime.date()); | ||||||
|  |  | ||||||
|  | @ -182,12 +182,9 @@ void Level2ProductView::ConnectRadarProductManager() | ||||||
|            [this](std::shared_ptr<types::RadarProductRecord> record) |            [this](std::shared_ptr<types::RadarProductRecord> record) | ||||||
|            { |            { | ||||||
|               if (record->radar_product_group() == |               if (record->radar_product_group() == | ||||||
|                      common::RadarProductGroup::Level2 && |                   common::RadarProductGroup::Level2) | ||||||
|                   std::chrono::floor<std::chrono::seconds>(record->time()) == |  | ||||||
|                      selected_time()) |  | ||||||
|               { |               { | ||||||
|                  // If the data associated with the currently selected time is
 |                  // If level 2 data associated was reloaded, update the view
 | ||||||
|                  // reloaded, update the view
 |  | ||||||
|                  Update(); |                  Update(); | ||||||
|               } |               } | ||||||
|            }); |            }); | ||||||
|  | @ -500,7 +497,7 @@ void Level2ProductView::UpdateColorTableLut() | ||||||
| 
 | 
 | ||||||
| void Level2ProductView::ComputeSweep() | void Level2ProductView::ComputeSweep() | ||||||
| { | { | ||||||
|    logger_->debug("ComputeSweep()"); |    logger_->trace("ComputeSweep()"); | ||||||
| 
 | 
 | ||||||
|    boost::timer::cpu_timer timer; |    boost::timer::cpu_timer timer; | ||||||
| 
 | 
 | ||||||
|  | @ -517,17 +514,10 @@ void Level2ProductView::ComputeSweep() | ||||||
| 
 | 
 | ||||||
|    std::shared_ptr<wsr88d::rda::ElevationScan> radarData; |    std::shared_ptr<wsr88d::rda::ElevationScan> radarData; | ||||||
|    std::chrono::system_clock::time_point       requestedTime {selected_time()}; |    std::chrono::system_clock::time_point       requestedTime {selected_time()}; | ||||||
|    std::chrono::system_clock::time_point       foundTime; |    std::tie(radarData, p->elevationCut_, p->elevationCuts_, std::ignore) = | ||||||
|    std::tie(radarData, p->elevationCut_, p->elevationCuts_, foundTime) = |  | ||||||
|       radarProductManager->GetLevel2Data( |       radarProductManager->GetLevel2Data( | ||||||
|          p->dataBlockType_, p->selectedElevation_, requestedTime); |          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) |    if (radarData == nullptr) | ||||||
|    { |    { | ||||||
|       Q_EMIT SweepNotComputed(types::NoUpdateReason::NotLoaded); |       Q_EMIT SweepNotComputed(types::NoUpdateReason::NotLoaded); | ||||||
|  | @ -539,6 +529,8 @@ void Level2ProductView::ComputeSweep() | ||||||
|       return; |       return; | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|  |    logger_->debug("Computing Sweep"); | ||||||
|  | 
 | ||||||
|    std::size_t radials       = radarData->crbegin()->first + 1; |    std::size_t radials       = radarData->crbegin()->first + 1; | ||||||
|    std::size_t vertexRadials = radials; |    std::size_t vertexRadials = radials; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -117,7 +117,7 @@ std::tuple<const void*, size_t, size_t> Level3RadialView::GetMomentData() const | ||||||
| 
 | 
 | ||||||
| void Level3RadialView::ComputeSweep() | void Level3RadialView::ComputeSweep() | ||||||
| { | { | ||||||
|    logger_->debug("ComputeSweep()"); |    logger_->trace("ComputeSweep()"); | ||||||
| 
 | 
 | ||||||
|    boost::timer::cpu_timer timer; |    boost::timer::cpu_timer timer; | ||||||
| 
 | 
 | ||||||
|  | @ -185,6 +185,8 @@ void Level3RadialView::ComputeSweep() | ||||||
|       return; |       return; | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|  |    logger_->debug("Computing Sweep"); | ||||||
|  | 
 | ||||||
|    // A message with radial data should either have a Digital Radial Data
 |    // A message with radial data should either have a Digital Radial Data
 | ||||||
|    // Array Packet, or a Radial Data Array Packet
 |    // Array Packet, or a Radial Data Array Packet
 | ||||||
|    std::shared_ptr<wsr88d::rpg::DigitalRadialDataArrayPacket> |    std::shared_ptr<wsr88d::rpg::DigitalRadialDataArrayPacket> | ||||||
|  |  | ||||||
|  | @ -101,7 +101,7 @@ std::tuple<const void*, size_t, size_t> Level3RasterView::GetMomentData() const | ||||||
| 
 | 
 | ||||||
| void Level3RasterView::ComputeSweep() | void Level3RasterView::ComputeSweep() | ||||||
| { | { | ||||||
|    logger_->debug("ComputeSweep()"); |    logger_->trace("ComputeSweep()"); | ||||||
| 
 | 
 | ||||||
|    boost::timer::cpu_timer timer; |    boost::timer::cpu_timer timer; | ||||||
| 
 | 
 | ||||||
|  | @ -169,6 +169,8 @@ void Level3RasterView::ComputeSweep() | ||||||
|       return; |       return; | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|  |    logger_->debug("Computing Sweep"); | ||||||
|  | 
 | ||||||
|    // A message with raster data should have a Raster Data Packet
 |    // A message with raster data should have a Raster Data Packet
 | ||||||
|    std::shared_ptr<wsr88d::rpg::RasterDataPacket> rasterData = nullptr; |    std::shared_ptr<wsr88d::rpg::RasterDataPacket> rasterData = nullptr; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -183,6 +183,7 @@ AwsNexradDataProvider::GetTimePointsByDate( | ||||||
|    std::shared_lock lock(p->objectsMutex_); |    std::shared_lock lock(p->objectsMutex_); | ||||||
| 
 | 
 | ||||||
|    // Is the date present in the date list?
 |    // Is the date present in the date list?
 | ||||||
|  |    bool currentDatePresent; | ||||||
|    auto currentDateIterator = |    auto currentDateIterator = | ||||||
|       std::find(p->objectDates_.cbegin(), p->objectDates_.cend(), day); |       std::find(p->objectDates_.cbegin(), p->objectDates_.cend(), day); | ||||||
|    if (currentDateIterator == p->objectDates_.cend()) |    if (currentDateIterator == p->objectDates_.cend()) | ||||||
|  | @ -199,6 +200,12 @@ AwsNexradDataProvider::GetTimePointsByDate( | ||||||
| 
 | 
 | ||||||
|       // Re-lock mutex
 |       // Re-lock mutex
 | ||||||
|       lock.lock(); |       lock.lock(); | ||||||
|  | 
 | ||||||
|  |       currentDatePresent = false; | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       currentDatePresent = true; | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    // Determine objects to retrieve
 |    // Determine objects to retrieve
 | ||||||
|  | @ -216,7 +223,7 @@ AwsNexradDataProvider::GetTimePointsByDate( | ||||||
| 
 | 
 | ||||||
|    // If we haven't updated the most recently queried dates yet, because the
 |    // If we haven't updated the most recently queried dates yet, because the
 | ||||||
|    // date was already cached, update
 |    // date was already cached, update
 | ||||||
|    if (currentDateIterator != p->objectDates_.cend()) |    if (currentDatePresent) | ||||||
|    { |    { | ||||||
|       p->UpdateObjectDates(date); |       p->UpdateObjectDates(date); | ||||||
|    } |    } | ||||||
|  |  | ||||||
|  | @ -65,7 +65,9 @@ public: | ||||||
|    std::map<std::uint16_t, std::shared_ptr<rda::ElevationScan>> radarData_ {}; |    std::map<std::uint16_t, std::shared_ptr<rda::ElevationScan>> radarData_ {}; | ||||||
| 
 | 
 | ||||||
|    std::map<rda::DataBlockType, |    std::map<rda::DataBlockType, | ||||||
|             std::map<std::uint16_t, std::shared_ptr<rda::ElevationScan>>> |             std::map<std::uint16_t, | ||||||
|  |                      std::map<std::chrono::system_clock::time_point, | ||||||
|  |                               std::shared_ptr<rda::ElevationScan>>>> | ||||||
|       index_ {}; |       index_ {}; | ||||||
| 
 | 
 | ||||||
|    std::list<std::stringstream> rawRecords_ {}; |    std::list<std::stringstream> rawRecords_ {}; | ||||||
|  | @ -132,7 +134,7 @@ std::shared_ptr<const rda::VolumeCoveragePatternData> Ar2vFile::vcp_data() const | ||||||
| std::tuple<std::shared_ptr<rda::ElevationScan>, float, std::vector<float>> | std::tuple<std::shared_ptr<rda::ElevationScan>, float, std::vector<float>> | ||||||
| Ar2vFile::GetElevationScan(rda::DataBlockType                    dataBlockType, | Ar2vFile::GetElevationScan(rda::DataBlockType                    dataBlockType, | ||||||
|                            float                                 elevation, |                            float                                 elevation, | ||||||
|                            std::chrono::system_clock::time_point /*time*/) const |                            std::chrono::system_clock::time_point time) const | ||||||
| { | { | ||||||
|    logger_->debug("GetElevationScan: {} degrees", elevation); |    logger_->debug("GetElevationScan: {} degrees", elevation); | ||||||
| 
 | 
 | ||||||
|  | @ -152,6 +154,7 @@ Ar2vFile::GetElevationScan(rda::DataBlockType dataBlockType, | ||||||
|       std::uint16_t lowerBound = scans.cbegin()->first; |       std::uint16_t lowerBound = scans.cbegin()->first; | ||||||
|       std::uint16_t upperBound = scans.crbegin()->first; |       std::uint16_t upperBound = scans.crbegin()->first; | ||||||
| 
 | 
 | ||||||
|  |       // Find closest elevation match
 | ||||||
|       for (auto& scan : scans) |       for (auto& scan : scans) | ||||||
|       { |       { | ||||||
|          if (scan.first > lowerBound && scan.first <= codedElevation) |          if (scan.first > lowerBound && scan.first <= codedElevation) | ||||||
|  | @ -173,15 +176,25 @@ Ar2vFile::GetElevationScan(rda::DataBlockType dataBlockType, | ||||||
|          std::abs(static_cast<std::int32_t>(codedElevation) - |          std::abs(static_cast<std::int32_t>(codedElevation) - | ||||||
|                   static_cast<std::int32_t>(upperBound)); |                   static_cast<std::int32_t>(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); |          auto& scanTime = scan.first; | ||||||
|          elevationCut  = lowerBound / scaleFactor; | 
 | ||||||
|  |          if (elevationScan == nullptr || | ||||||
|  |              (scanTime <= time && scanTime > foundTime)) | ||||||
|  |          { | ||||||
|  |             elevationScan = scan.second; | ||||||
|  |             foundTime     = scanTime; | ||||||
|          } |          } | ||||||
|       else |  | ||||||
|       { |  | ||||||
|          elevationScan = scans.at(upperBound); |  | ||||||
|          elevationCut  = upperBound / scaleFactor; |  | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|  | @ -460,8 +473,8 @@ void Ar2vFileImpl::IndexFile() | ||||||
|          waveformType   = vcpData_->waveform_type(elevationCut.first); |          waveformType   = vcpData_->waveform_type(elevationCut.first); | ||||||
|       } |       } | ||||||
|       else if ((digitalRadarData0 = |       else if ((digitalRadarData0 = | ||||||
|                    std::dynamic_pointer_cast<rda::DigitalRadarData>( |                    std::dynamic_pointer_cast<rda::DigitalRadarData>(radial0)) != | ||||||
|                       (*elevationCut.second)[0])) != nullptr) |                nullptr) | ||||||
|       { |       { | ||||||
|          elevationAngle = digitalRadarData0->elevation_angle_raw(); |          elevationAngle = digitalRadarData0->elevation_angle_raw(); | ||||||
|       } |       } | ||||||
|  | @ -488,8 +501,10 @@ void Ar2vFileImpl::IndexFile() | ||||||
| 
 | 
 | ||||||
|          if (momentData != nullptr) |          if (momentData != nullptr) | ||||||
|          { |          { | ||||||
|             // TODO: Handle multiple elevation scans
 |             auto time = util::TimePoint(radial0->modified_julian_date(), | ||||||
|             index_[dataBlockType][elevationAngle] = elevationCut.second; |                                         radial0->collection_time()); | ||||||
|  | 
 | ||||||
|  |             index_[dataBlockType][elevationAngle][time] = elevationCut.second; | ||||||
|          } |          } | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat