mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 16:00:08 +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