diff --git a/Kernel/src/renderive/real_time_data/History_Real_Time_Data.hpp b/Kernel/src/renderive/real_time_data/History_Real_Time_Data.hpp index 99867f3..6f28d1a 100644 --- a/Kernel/src/renderive/real_time_data/History_Real_Time_Data.hpp +++ b/Kernel/src/renderive/real_time_data/History_Real_Time_Data.hpp @@ -31,7 +31,12 @@ public: void update(Value value, std::size_t retain_latest_count); void clear(); std::size_t retain_latest(std::size_t count); + struct Snapshot { + Container values; + Real_Time_Data_Update_State update_state; + }; Container snapshot() const; + Snapshot snapshot_with_update_state() const; std::size_t size() const; std::uint64_t revision() const; Real_Time_Data_Retention retention() const noexcept override; @@ -49,6 +54,7 @@ private: void reserve_update_times(std::size_t capacity); void publish_render_state() override; const Container& render_state_value() const noexcept; + const Real_Time_Data_Update_State& render_update_state_value() const noexcept; mutable Mutex mutex_; std::recursive_mutex mutation_mutex_; Observer observer_; @@ -61,7 +67,7 @@ private: std::size_t update_times_size_{}; std::size_t update_times_capacity_{}; std::uint64_t revision_{}; - std::uint64_t render_revision_{}; + Real_Time_Data_Update_State render_update_state_{}; std::uint64_t total_update_count_{}; std::uint64_t last_update_time_ns_{}; }; diff --git a/Kernel/src/renderive/real_time_data/History_Real_Time_Data.inl b/Kernel/src/renderive/real_time_data/History_Real_Time_Data.inl index 179b2b0..7936238 100644 --- a/Kernel/src/renderive/real_time_data/History_Real_Time_Data.inl +++ b/Kernel/src/renderive/real_time_data/History_Real_Time_Data.inl @@ -101,6 +101,15 @@ auto History_Real_Time_Data::snapshot() return *cache_state_; } template +auto History_Real_Time_Data::snapshot_with_update_state() const -> Snapshot { + std::lock_guard < Mutex > lock(mutex_); + const Real_Time_Data_Update_State update_state{ + this, Real_Time_Data_Retention::history, revision_, last_update_time_ns_, + total_update_count_, cache_state_->size() + }; + return {*cache_state_, update_state}; +} +template std::size_t History_Real_Time_Data::size() const { std::lock_guard < Mutex > lock(mutex_); return cache_state_->size(); @@ -144,12 +153,20 @@ std::size_t History_Real_Time_Data::disc template void History_Real_Time_Data::publish_render_state() { std::lock_guard < Mutex > lock(mutex_); - if (render_revision_ == revision_) return; + if (render_update_state_.revision == revision_) return; *scratch_state_ = *cache_state_; std::swap(render_state_, scratch_state_); - render_revision_ = revision_; + render_update_state_ = { + this, Real_Time_Data_Retention::history, revision_, last_update_time_ns_, + total_update_count_, render_state_->size() + }; } template auto History_Real_Time_Data::render_state_value() const noexcept -> const Container& { return *render_state_; } +template +auto History_Real_Time_Data::render_update_state_value() const noexcept + -> const Real_Time_Data_Update_State& { + return render_update_state_; +} diff --git a/Kernel/src/renderive/state/Render_State_View.hpp b/Kernel/src/renderive/state/Render_State_View.hpp index f01a54e..a35324c 100644 --- a/Kernel/src/renderive/state/Render_State_View.hpp +++ b/Kernel/src/renderive/state/Render_State_View.hpp @@ -7,6 +7,10 @@ public: [[nodiscard]] const auto& get(const Source& source) const noexcept { return source.render_state_value(); } + template + [[nodiscard]] const auto& update_state(const Source& source) const noexcept { + return source.render_update_state_value(); + } private: friend class Renderable_Base; friend class Frame_Render_Snapshot; diff --git a/render_2D/render_2D/plottable/Sweep_Spectrum.cpp b/render_2D/render_2D/plottable/Sweep_Spectrum.cpp index 3c4309d..a3632f8 100644 --- a/render_2D/render_2D/plottable/Sweep_Spectrum.cpp +++ b/render_2D/render_2D/plottable/Sweep_Spectrum.cpp @@ -3,18 +3,37 @@ #include "Plottable_Real_Time_Data.h" #include "../render/Blend2D_Cache.h" #include "../renderable/Renderable_p.h" -#include -#include +#include #include #include namespace renderive::detail { namespace { using Sweep_Spectrum_History = Plottable_History_Real_Time_Data, std::deque>>; -std::vector flatten(const std::deque>& blocks) { +std::size_t current_sweep_block_count(std::uint64_t total_update_count, + std::size_t block_count) { + if (total_update_count == 0) return 0; + return static_cast((total_update_count - 1) % block_count) + 1; +} +std::vector flatten_latest(const std::deque>& blocks, + std::size_t block_count) { std::vector values; - for (const auto& block : blocks) values.insert(values.end(), block.begin(), block.end()); + const std::size_t first = blocks.size() - block_count; + std::size_t value_count{}; + for (std::size_t index = first; index < blocks.size(); ++index) { + value_count += blocks[index].size(); + } + values.reserve(value_count); + for (std::size_t index = first; index < blocks.size(); ++index) { + values.insert(values.end(), blocks[index].begin(), blocks[index].end()); + } return values; } +Range completed_frequency_range(Range frequency_range, + std::size_t completed_block_count, + std::size_t block_count) { + const double completed = static_cast(completed_block_count) / static_cast(block_count); + return {frequency_range.origin, frequency_range.origin + frequency_range.length() * completed}; +} } struct Sweep_Spectrum::Impl : Base::next_Impl { @@ -71,8 +90,17 @@ std::size_t Sweep_Spectrum::stored_point_count() const { } std::size_t Sweep_Spectrum::rendered_point_count() const { const auto state = Renderable::state(); - const auto values = flatten(d_func().blocks->snapshot()); - return curve_points(values, state.frequency_range, d_func().frequency_axis->transform(), d_func().power_axis->transform(), state.visible_range_only, state.interpolation_mode).size(); + const auto snapshot = d_func().blocks->snapshot_with_update_state(); + const std::size_t block_count = static_cast(state.block_count.get()); + const std::size_t completed_block_count = + current_sweep_block_count(snapshot.update_state.total_update_count, block_count); + const auto values = flatten_latest(snapshot.values, completed_block_count); + const Range frequency_range = completed_frequency_range( + state.frequency_range, completed_block_count, block_count); + return curve_points(values, frequency_range, + d_func().frequency_axis->transform(), + d_func().power_axis->transform(), + state.visible_range_only, state.interpolation_mode).size(); } Sweep_Spectrum::Observer Sweep_Spectrum::capture_observation() const { const auto value = Renderable::published_state(); @@ -88,16 +116,21 @@ void Sweep_Spectrum::Impl::prepare_frame(const Prepare_Render_Context& context) const auto& view = context.frame.render_state; const auto& state = control.render_state(view); const auto& published_blocks = view.get(*blocks); - const auto values = flatten(published_blocks); + const auto& published_update_state = view.update_state(*blocks); + const std::size_t block_count = static_cast(state.block_count.get()); + const std::size_t completed_block_count = current_sweep_block_count( + published_update_state.total_update_count, block_count); + const auto values = flatten_latest(published_blocks, completed_block_count); auto& output = prepare_buffer; output = {}; if (values.size() < 2) return; const Axis_Transform x = frequency_axis->transform(view); const Axis_Transform y = power_axis->transform(view); - output.points = curve_points(values, state.frequency_range, x, y, + const Range frequency_range = completed_frequency_range( + state.frequency_range, completed_block_count, block_count); + output.points = curve_points(values, frequency_range, x, y, state.visible_range_only, state.interpolation_mode); - const double completed = std::min(1.0, static_cast(published_blocks.size()) / state.block_count.get()); - const double frequency = state.frequency_range.origin + state.frequency_range.length() * completed; + const double frequency = frequency_range.target; output.current_first = mapped_point(x, frequency, y, y.coordinate_range.origin); output.current_second = mapped_point(x, frequency, y, y.coordinate_range.target); output.valid = true; diff --git a/web_server/app/render_2D/Gallery_Scene2D.cpp b/web_server/app/render_2D/Gallery_Scene2D.cpp index 8a6a30f..67f7e3a 100644 --- a/web_server/app/render_2D/Gallery_Scene2D.cpp +++ b/web_server/app/render_2D/Gallery_Scene2D.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,8 @@ struct Observer_Pfr_Adapter { Render_Scene_2D::Observer, Render_Scene_2D::Observer_Fields>; }; namespace { +constexpr std::size_t sweep_bins_per_block = 64; +constexpr std::size_t sweep_block_count = 8; std::shared_ptr make_strategy( Gallery_Frame_Mode mode) { switch (mode) { @@ -349,8 +352,8 @@ private: } else if (case_id_ == "sweep_spectrum") { Sweep_Spectrum::State state; state.frequency_range = {0.0, 300.0}; - state.bins_per_block = 64; - state.block_count = 8; + state.bins_per_block = static_cast(sweep_bins_per_block); + state.block_count = static_cast(sweep_block_count); Sweep_Spectrum::Builder builder{state, &attach}; sweep_ = build( builder, root, domain_axis_, value_axis_); @@ -382,9 +385,16 @@ private: } void update_samples() { - const std::size_t count = waterfall_ ? 256U : afterglow_ ? 192U : 512U; + const std::size_t count = + waterfall_ ? 256U : + afterglow_ ? 192U : + sweep_ ? sweep_bins_per_block * sweep_block_count : 512U; std::vector values(count); - const double time = static_cast(sample_sequence_++) * 0.075; + const std::uint64_t sample_sequence = sample_sequence_++; + const std::uint64_t time_sequence = sweep_ + ? sample_sequence / sweep_block_count * sweep_block_count + : sample_sequence; + const double time = static_cast(time_sequence) * 0.075; for (std::size_t index = 0; index < count; ++index) { const double x = static_cast(index) / (count - 1); const auto gaussian = [](double value, double center, double width) { @@ -402,15 +412,26 @@ private: if (waterfall_) waterfall_->append_row(current_time_of_day(), values); if (afterglow_) afterglow_->append_spectrum(values); if (sweep_) { - values.resize(64); - sweep_->append_block(values); + const std::size_t block_index = static_cast( + sample_sequence % sweep_block_count); + const std::size_t first = block_index * sweep_bins_per_block; + sweep_->append_block(std::span( + values.data() + first, sweep_bins_per_block)); } if (trace_) trace_->append_sample(current_time_of_day(), std::sin(time)); if (constellation_) { - const double phase = time * 1.7; + constexpr std::size_t symbol_count = + static_cast(Constellation_Diagram_Type::Psk8); + const std::size_t symbol_index = sample_sequence % symbol_count; + const double phase = 2.0 * std::numbers::pi * + static_cast(symbol_index) / + static_cast(symbol_count); + const double i_noise = + std::sin(time * 17.0 + static_cast(symbol_index) * 0.73) * 0.04; + const double q_noise = + std::cos(time * 19.0 + static_cast(symbol_index) * 1.11) * 0.04; constellation_->append_point( - {std::cos(phase) + std::sin(time * 9.0) * 0.08, - std::sin(phase) + std::cos(time * 7.0) * 0.08}); + {std::cos(phase) + i_noise, std::sin(phase) + q_noise}); } }