mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 04:00: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::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<void (QWidget::*)()>(&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<void (QWidget::*)()>(&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) | ||||
|  |  | |||
|  | @ -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::NexradDataProvider> provider_; | ||||
|    bool                                          refreshEnabled_ {false}; | ||||
|    boost::asio::steady_timer                     refreshTimer_ {threadPool_}; | ||||
|    std::mutex                                    refreshTimerMutex_ {}; | ||||
|    std::shared_ptr<provider::NexradDataProvider> 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<ProviderManager>( | ||||
|           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> providerManager); | ||||
|    void RefreshDataSync(std::shared_ptr<ProviderManager> providerManager); | ||||
| 
 | ||||
|    std::tuple<std::shared_ptr<types::RadarProductRecord>, | ||||
|               std::chrono::system_clock::time_point> | ||||
|    GetLevel2ProductRecord(std::chrono::system_clock::time_point time); | ||||
|    std::map<std::chrono::system_clock::time_point, | ||||
|             std::shared_ptr<types::RadarProductRecord>> | ||||
|    GetLevel2ProductRecords(std::chrono::system_clock::time_point time); | ||||
|    std::tuple<std::shared_ptr<types::RadarProductRecord>, | ||||
|               std::chrono::system_clock::time_point> | ||||
|    GetLevel3ProductRecord(const std::string&                    product, | ||||
|  | @ -247,30 +225,30 @@ public: | |||
|    std::shared_ptr<config::RadarSite> radarSite_; | ||||
|    std::size_t                        cacheLimit_ {6u}; | ||||
| 
 | ||||
|    std::vector<float> coordinates0_5Degree_; | ||||
|    std::vector<float> coordinates1Degree_; | ||||
|    std::vector<float> coordinates0_5Degree_ {}; | ||||
|    std::vector<float> coordinates1Degree_ {}; | ||||
| 
 | ||||
|    RadarProductRecordMap  level2ProductRecords_; | ||||
|    RadarProductRecordList level2ProductRecentRecords_; | ||||
|    RadarProductRecordMap   level2ProductRecords_ {}; | ||||
|    RadarProductRecordList  level2ProductRecentRecords_ {}; | ||||
|    std::unordered_map<std::string, RadarProductRecordMap> | ||||
|       level3ProductRecordsMap_; | ||||
|       level3ProductRecordsMap_ {}; | ||||
|    std::unordered_map<std::string, RadarProductRecordList> | ||||
|                      level3ProductRecentRecordsMap_; | ||||
|    std::shared_mutex level2ProductRecordMutex_; | ||||
|    std::shared_mutex level3ProductRecordMutex_; | ||||
|                      level3ProductRecentRecordsMap_ {}; | ||||
|    std::shared_mutex level2ProductRecordMutex_ {}; | ||||
|    std::shared_mutex level3ProductRecordMutex_ {}; | ||||
| 
 | ||||
|    std::shared_ptr<ProviderManager> level2ProviderManager_; | ||||
|    std::unordered_map<std::string, std::shared_ptr<ProviderManager>> | ||||
|                      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<boost::uuids::uuid, | ||||
|                       std::shared_ptr<ProviderManager>, | ||||
|  | @ -1173,60 +1151,91 @@ void RadarProductManagerImpl::PopulateProductTimes( | |||
|                   }); | ||||
| } | ||||
| 
 | ||||
| std::tuple<std::shared_ptr<types::RadarProductRecord>, | ||||
|            std::chrono::system_clock::time_point> | ||||
| RadarProductManagerImpl::GetLevel2ProductRecord( | ||||
| std::map<std::chrono::system_clock::time_point, | ||||
|          std::shared_ptr<types::RadarProductRecord>> | ||||
| RadarProductManagerImpl::GetLevel2ProductRecords( | ||||
|    std::chrono::system_clock::time_point time) | ||||
| { | ||||
|    std::shared_ptr<types::RadarProductRecord> record {nullptr}; | ||||
|    RadarProductRecordMap::const_pointer       recordPtr {nullptr}; | ||||
|    std::chrono::system_clock::time_point      recordTime {time}; | ||||
|    std::map<std::chrono::system_clock::time_point, | ||||
|             std::shared_ptr<types::RadarProductRecord>> | ||||
|                                                      records {}; | ||||
|    std::vector<RadarProductRecordMap::const_pointer> 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::NexradFileRequest> request = | ||||
|          std::make_shared<request::NexradFileRequest>(radarId_); | ||||
| 
 | ||||
|       QObject::connect( | ||||
|          request.get(), | ||||
|          &request::NexradFileRequest::RequestComplete, | ||||
|          self_, | ||||
|          [this](std::shared_ptr<request::NexradFileRequest> 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<types::RadarProductRecord> 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::NexradFileRequest> request = | ||||
|             std::make_shared<request::NexradFileRequest>(radarId_); | ||||
| 
 | ||||
|          QObject::connect( | ||||
|             request.get(), | ||||
|             &request::NexradFileRequest::RequestComplete, | ||||
|             self_, | ||||
|             [this](std::shared_ptr<request::NexradFileRequest> 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<std::shared_ptr<types::RadarProductRecord>, | ||||
|  | @ -1399,19 +1408,46 @@ RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType, | |||
| { | ||||
|    std::shared_ptr<wsr88d::rda::ElevationScan> radarData    = nullptr; | ||||
|    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; | ||||
|    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<wsr88d::rda::ElevationScan> recordRadarData = nullptr; | ||||
|          float                                       recordElevationCut = 0.0f; | ||||
|          std::vector<float>                          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<std::shared_ptr<wsr88d::rpg::Level3Message>, | ||||
|  | @ -1449,7 +1485,7 @@ std::vector<std::string> RadarProductManager::GetLevel3Products() | |||
| 
 | ||||
| void RadarProductManager::SetCacheLimit(size_t cacheLimit) | ||||
| { | ||||
|    p->cacheLimit_ = cacheLimit; | ||||
|    p->cacheLimit_ = std::max<std::size_t>(cacheLimit, 6u); | ||||
| } | ||||
| 
 | ||||
| void RadarProductManager::UpdateAvailableProducts() | ||||
|  |  | |||
|  | @ -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 | ||||
|     */ | ||||
|  |  | |||
|  | @ -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<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) | ||||
|    { | ||||
|       // 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::minutes>( | ||||
|             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; | ||||
|       } | ||||
|    } | ||||
| } | ||||
|  |  | |||
|  | @ -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()); | ||||
|  |  | |||
|  | @ -182,12 +182,9 @@ void Level2ProductView::ConnectRadarProductManager() | |||
|            [this](std::shared_ptr<types::RadarProductRecord> record) | ||||
|            { | ||||
|               if (record->radar_product_group() == | ||||
|                      common::RadarProductGroup::Level2 && | ||||
|                   std::chrono::floor<std::chrono::seconds>(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<wsr88d::rda::ElevationScan> 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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -117,7 +117,7 @@ std::tuple<const void*, size_t, size_t> 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<wsr88d::rpg::DigitalRadialDataArrayPacket> | ||||
|  |  | |||
|  | @ -101,7 +101,7 @@ std::tuple<const void*, size_t, size_t> 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<wsr88d::rpg::RasterDataPacket> rasterData = nullptr; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); | ||||
|    } | ||||
|  |  | |||
|  | @ -65,7 +65,9 @@ public: | |||
|    std::map<std::uint16_t, std::shared_ptr<rda::ElevationScan>> radarData_ {}; | ||||
| 
 | ||||
|    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_ {}; | ||||
| 
 | ||||
|    std::list<std::stringstream> rawRecords_ {}; | ||||
|  | @ -130,9 +132,9 @@ std::shared_ptr<const rda::VolumeCoveragePatternData> Ar2vFile::vcp_data() const | |||
| } | ||||
| 
 | ||||
| std::tuple<std::shared_ptr<rda::ElevationScan>, float, std::vector<float>> | ||||
| 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<std::int32_t>(codedElevation) - | ||||
|                   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); | ||||
|          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<rda::DigitalRadarData>( | ||||
|                       (*elevationCut.second)[0])) != nullptr) | ||||
|                    std::dynamic_pointer_cast<rda::DigitalRadarData>(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; | ||||
|          } | ||||
|       } | ||||
|    } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat