#include #include #include #include #include #include #include #include "../Renderive/Axis/Axis.h" #include "../Renderive/Axis/Frequency_Axis.h" #include "../Renderive/Axis/Time_Axis.h" #include "../Renderive/Axis/Time_Axis_p.h" #include "../Renderive/architecture/Plot.h" #include "../Renderive/architecture/Plot_p.h" #include "../Renderive/base/Memory.h" #include "../Renderive/plottable/Audio_Frequency.h" #include "../Renderive/plottable/Audio_Frequency_p.h" #include "../Renderive/plottable/Planisphere.h" #include "../Renderive/plottable/Planisphere_p.h" #include "../Renderive/plottable/Waterfall.h" #include "../Renderive/plottable/Waterfall_p.h" namespace { void process_events(int milliseconds) { QElapsedTimer timer; timer.start(); while (timer.elapsed() < milliseconds) { QApplication::processEvents(QEventLoop::AllEvents, 10); QThread::msleep(1); } } std::vector make_frequency_data(int size, int frame) { std::vector data(size); for (int i = 0; i < size; ++i) data[i] = -90.0 + 30.0 * std::sin(static_cast(i + frame) * 0.017); return data; } bool wait_ready(const std::shared_ptr& waterfall, const std::shared_ptr& time_axis, const std::shared_ptr& audio, const std::shared_ptr& planisphere) { QElapsedTimer timer; timer.start(); while (timer.elapsed() < 3000) { QApplication::processEvents(QEventLoop::AllEvents, 10); if (waterfall->ok() && time_axis->ok() && audio->ok() && planisphere->ok()) return true; QThread::msleep(1); } return false; } bool wait_idle(renderive::Plot& plot) { QElapsedTimer timer; timer.start(); while (timer.elapsed() < 3000) { QApplication::processEvents(QEventLoop::AllEvents, 10); if (!plot.is_rendering() && !plot.d->active_render_tasks.load(std::memory_order_acquire)) return true; QThread::msleep(1); } return false; } int stress_data_paths() { renderive::Plot plot; plot.init(); plot.resize(720, 420); auto data_node = plot.create_renderable_node(plot.get_root_renderable(), "Data_Renderable"); auto axis_node = plot.create_renderable_node(plot.get_root_renderable(), "Axis_Renderable"); auto frequency_axis = renderive::Frequency_Axis::Builder(axis_node, Qt::Horizontal).set_pixel_size(700).set_coord_range({0.0, 100.0}).build(); auto time_axis = renderive::Time_Axis::Builder(axis_node, Qt::Vertical).set_pixel_size(400).set_time_point_size(32).build(); auto value_axis = renderive::Axis::Builder(axis_node, Qt::Vertical).set_pixel_size(400).set_coord_range({-120.0, 0.0}).build(); auto i_axis = renderive::Axis::Builder(axis_node, Qt::Horizontal).set_pixel_size(700).set_coord_range({-2.0, 2.0}).build(); auto q_axis = renderive::Axis::Builder(axis_node, Qt::Vertical).set_pixel_size(400).set_coord_range({-2.0, 2.0}).build(); auto waterfall = renderive::Waterfall::Builder(data_node, frequency_axis, time_axis).set_frequency_range({0.0, 100.0}).set_power_range({-120.0, 0.0}).set_frequency_point_size(64).build(); auto audio = renderive::Audio_Frequency::Builder(data_node, time_axis, value_axis).set_time_point_size(32).set_key_range({-120.0, 0.0}).build(); auto planisphere = renderive::Planisphere::Builder(data_node, i_axis, q_axis).set_i_range({-2.0, 2.0}).set_q_range({-2.0, 2.0}).set_continue_millisecond(500).build(); plot.show(); plot.start_render(240); if (!wait_ready(waterfall, time_axis, audio, planisphere)) return 1; const int frequency_sizes[] = {64, 512, 8192, 8193, 128}; const int time_sizes[] = {32, 96, 48, 64, 24}; const renderive::Waterfall_Update_Mode update_modes[] = { renderive::Waterfall_Update_Mode::Batch, renderive::Waterfall_Update_Mode::Single_Line, renderive::Waterfall_Update_Mode::Timed_Latest, renderive::Waterfall_Update_Mode::Single_Line, renderive::Waterfall_Update_Mode::Batch }; for (int stage = 0; stage < 5; ++stage) { waterfall->set_frequency_point_size(frequency_sizes[stage]); waterfall->set_update_mode(update_modes[stage]); waterfall->set_update_interval_ms(2); time_axis->set_time_point_size(time_sizes[stage]); audio->set_time_point_size(time_sizes[stage]); process_events(100); std::unordered_map expected_frequency; QTime expected_time; double expected_power{}; QPointF expected_position; for (int frame = 0; frame < time_sizes[stage] * 2; ++frame) { std::vector frequency_data = make_frequency_data(frequency_sizes[stage], frame); expected_time = QTime::fromMSecsSinceStartOfDay((stage * 10000 + frame * 4) % 86400000); int tick = time_axis->give_data(expected_time); waterfall->give_data(tick, frequency_data); expected_frequency[tick] = frequency_data.front(); expected_power = frequency_data[frame % frequency_data.size()]; audio->give_data(tick, expected_power); double phase = static_cast(frame) * 0.13; expected_position = {1.5 * std::cos(phase), 1.5 * std::sin(phase)}; planisphere->give_data(expected_position); if ((frame & 3) == 3) process_events(8); } process_events(60); plot.pause_render(); if (!wait_idle(plot)) return 2; auto* waterfall_data = waterfall->d(); if (waterfall_data->ring_buffer.col_count != frequency_sizes[stage] || waterfall_data->ring_buffer.row_count != time_sizes[stage]) return 3; if (waterfall_data->image.width() != frequency_sizes[stage] || waterfall_data->image.height() != time_sizes[stage]) return 4; int row_count = waterfall_data->ring_buffer.snapshot(); int previous_tick{}; for (int row = 0; row < row_count; ++row) { int tick = waterfall_data->ring_buffer.tick(row); auto expected = expected_frequency.find(tick); if (expected == expected_frequency.end()) return 5; if (std::abs(waterfall_data->ring_buffer.frequency_data(row, 0, 0)[0] - expected->second) > 0.000001) return 6; if (row && tick >= previous_tick) return 7; previous_tick = tick; } auto* time_data = time_axis->d(); if (time_data->time_ticker.time_point_size != time_sizes[stage] || time_data->time_snapshot.size() != static_cast(time_sizes[stage])) return 8; if (!time_data->time_data_count || time_data->time_snapshot[time_data->time_data_count - 1] != expected_time) return 9; int lower = qMin(time_data->time_ticker.lower, time_data->time_ticker.upper()); int upper = qMax(time_data->time_ticker.lower, time_data->time_ticker.upper()); for (const renderive::Time_Tick& tick : time_data->data) { if (tick.tick < lower || tick.tick > upper) return 10; } auto* audio_data = audio->d(); if (audio_data->point_count != time_sizes[stage] || audio_data->power_snapshot.size() != static_cast(time_sizes[stage])) return 11; if (!audio_data->data_count || std::abs(audio_data->power_snapshot[audio_data->data_count - 1] - expected_power) > 0.000001) return 12; auto* planisphere_data = planisphere->d(); if (planisphere_data->data_list.empty()) return 13; QPointF actual_position = planisphere_data->data_list.back().pos; if (std::abs(actual_position.x() - expected_position.x()) > 0.000001 || std::abs(actual_position.y() - expected_position.y()) > 0.000001) return 14; plot.start_render(240); } for (int frame = 0; frame < 180; ++frame) { planisphere->set_continue_millisecond(500 + (frame & 1)); process_events(8); } plot.pause_render(); if (!wait_idle(plot)) return 15; if (!planisphere->d()->data_list.empty()) return 16; renderive::Memory_Stats stats = renderive::memory_stats(); return stats.upstream_allocation_count && stats.upstream_peak_bytes ? 0 : 17; } } int main(int argc, char** argv) { QApplication app(argc, argv); renderive::start_render_scheduler(); return stress_data_paths(); }