44
55
66#include " down_sampling_thread.h"
7-
8- #include < fstream>
9-
10- #include " signal_processing/SPUC/butterworth.h"
117#include " base/fixed_data_block.h"
128#include " gui/background_processes.h"
139
1410#include < vector>
15- #include < QMessageBox>
11+ #include < limits>
12+ #include < cmath>
1613#include < QDebug>
1714
15+ // SPUC headers define macros (E, PI, ...) that must come after Qt/STL headers
16+ // to avoid corrupting Qt6's C++20 constructs in C++17 mode.
17+ #include < fstream>
18+ #include " signal_processing/SPUC/butterworth.h"
19+
1820namespace SigViewer_
1921{
2022
23+ using namespace sigviewer ;
24+
2125QString DownSamplingThread::PROCESS_NAME_ (" Downsampling..." );
2226
2327// -----------------------------------------------------------------------------
@@ -63,10 +67,102 @@ void DownSamplingThread::run ()
6367 running_ = false ;
6468}
6569
70+ // -------------------------------------------------------------------------
71+ void DownSamplingThread::runSynchronously ()
72+ {
73+ minMaxDownsampling ();
74+ }
75+
6676// -------------------------------------------------------------------------
6777void DownSamplingThread::minMaxDownsampling ()
6878{
79+ std::set<ChannelID> channels = channel_manager_->getChannels ();
80+ size_t num_samples = channel_manager_->getNumberSamples ();
81+ double sample_rate = channel_manager_->getSampleRate ();
6982
83+ for (ChannelID id : channels)
84+ {
85+ QSharedPointer<DataBlock const > full_data =
86+ channel_manager_->getData (id, 0 , static_cast <unsigned >(num_samples));
87+ if (full_data.isNull () || full_data->size () == 0 )
88+ continue ;
89+
90+ size_t cur_size = full_data->size ();
91+
92+ // Seed working buffers from the raw samples.
93+ // NaN values propagate: a downsampled window is NaN only when every
94+ // raw sample inside it is NaN.
95+ QVector<float32> cur_min (static_cast <int >(cur_size));
96+ QVector<float32> cur_max (static_cast <int >(cur_size));
97+ for (size_t i = 0 ; i < cur_size; i++)
98+ {
99+ float32 v = (*full_data)[i];
100+ cur_min[static_cast <int >(i)] = v;
101+ cur_max[static_cast <int >(i)] = v;
102+ }
103+
104+ // Build the min/max pyramid hierarchically: each level aggregates the
105+ // previous one by downsampling_step_, so total work is O(N) per channel
106+ // (geometric series: N + N/step + N/step^2 + ...).
107+ for (unsigned factor = downsampling_step_;
108+ factor < downsampling_max_ && cur_size > 1 ;
109+ factor *= downsampling_step_)
110+ {
111+ size_t next_size = (cur_size + downsampling_step_ - 1 ) / downsampling_step_;
112+ QVector<float32> next_min (static_cast <int >(next_size));
113+ QVector<float32> next_max (static_cast <int >(next_size));
114+
115+ for (size_t i = 0 ; i < next_size; i++)
116+ {
117+ size_t j0 = i * downsampling_step_;
118+ size_t j1 = std::min (j0 + static_cast <size_t >(downsampling_step_), cur_size);
119+
120+ float32 vmin = std::numeric_limits<float32>::quiet_NaN ();
121+ float32 vmax = std::numeric_limits<float32>::quiet_NaN ();
122+
123+ for (size_t j = j0; j < j1; j++)
124+ {
125+ float32 lo = cur_min[static_cast <int >(j)];
126+ float32 hi = cur_max[static_cast <int >(j)];
127+ if (!std::isnan (lo))
128+ {
129+ if (std::isnan (vmin))
130+ {
131+ vmin = lo;
132+ vmax = hi;
133+ }
134+ else
135+ {
136+ if (lo < vmin) vmin = lo;
137+ if (hi > vmax) vmax = hi;
138+ }
139+ }
140+ }
141+
142+ next_min[static_cast <int >(i)] = vmin;
143+ next_max[static_cast <int >(i)] = vmax;
144+ }
145+
146+ // Persist this level – copy into fresh QVectors so the working
147+ // buffers below can be moved cheaply on the next iteration.
148+ auto stored_min = QSharedPointer<QVector<float32>> (
149+ new QVector<float32> (next_min));
150+ auto stored_max = QSharedPointer<QVector<float32>> (
151+ new QVector<float32> (next_max));
152+
153+ double ds_rate = sample_rate / factor;
154+ channel_manager_->addDownsampledMinMaxVersion (
155+ id,
156+ QSharedPointer<DataBlock const > (new FixedDataBlock (stored_min, ds_rate)),
157+ QSharedPointer<DataBlock const > (new FixedDataBlock (stored_max, ds_rate)),
158+ factor);
159+
160+ // Advance working buffers to this level for the next iteration.
161+ cur_min = std::move (next_min);
162+ cur_max = std::move (next_max);
163+ cur_size = next_size;
164+ }
165+ }
70166}
71167
72168// -------------------------------------------------------------------------
@@ -83,14 +179,11 @@ void DownSamplingThread::downsampleAllOnBasisData ()
83179 {
84180 for (int channel = 0 ; channel < data_.size (); channel++)
85181 {
86- max_channel_length = std::max (max_channel_length, basis_data_[channel]->size ());
182+ max_channel_length = std::max (max_channel_length, static_cast < unsigned >( basis_data_[channel]->size () ));
87183 sample_rates[downsampling_factor].append (basis_data_[channel]->getSampleRatePerUnit () / downsampling_factor);
88184
89185 QSharedPointer<QVector<float32> > raw_data_vector (new QVector<float32> (static_cast <int >(basis_data_[channel]->size () / downsampling_factor) + 1 ));
90186
91- QSharedPointer<DataBlock> downsampled_data (new FixedDataBlock (raw_data_vector, sample_rates[downsampling_factor][channel]));
92- basis_data_[channel]->addDownSampledVersion (downsampled_data, downsampling_factor);
93-
94187 raw_downsampled_data[downsampling_factor].append (raw_data_vector);
95188 low_pass_filters[downsampling_factor].append (QSharedPointer<SPUC ::butterworth<float32> > (new SPUC ::butterworth<float32> (0.5 / downsampling_factor, 4 , 3 )));
96189 }
@@ -132,7 +225,7 @@ void DownSamplingThread::downsampleOnDownsampledData ()
132225
133226 for (int channel = 0 ; channel < data_.size (); channel++)
134227 {
135- max_channel_length = std::max (max_channel_length, data_[channel]->size ());
228+ max_channel_length = std::max (max_channel_length, static_cast < unsigned >( data_[channel]->size () ));
136229 raw_downsampled_data.append (QSharedPointer<QVector<float32> > (new QVector<float32> (static_cast <int >(data_[channel]->size () / downsampling_step_) + 1 )));
137230 low_pass_filters.append (QSharedPointer<SPUC ::butterworth<float32> > (new SPUC ::butterworth<float32> (0.9 / downsampling_step_, 4 , 3 )));
138231 sample_rates_.append (data_[channel]->getSampleRatePerUnit () / downsampling_step_);
@@ -156,7 +249,6 @@ void DownSamplingThread::downsampleOnDownsampledData ()
156249 for (int channel = 0 ; channel < raw_downsampled_data.size (); channel++)
157250 {
158251 QSharedPointer<DataBlock> downsampled_data (new FixedDataBlock (raw_downsampled_data[channel], sample_rates_[channel]));
159- basis_data_[channel]->addDownSampledVersion (downsampled_data, downsampling_factor);
160252 data_.append (downsampled_data);
161253 }
162254 }
0 commit comments