Commit 812e83f5f19ffbfd6ded73181425f61cd68084b3
1 parent
76118202
Finish refactoring of data flow from USBDevice->HantekDSOControl->DataAnalyser->…
…DSOWidget->GLGenerator. Less thread locks, no deadlocks by design
Showing
20 changed files
with
2084 additions
and
2353 deletions
openhantek/CMakeLists.txt
| ... | ... | @@ -10,7 +10,7 @@ set(CMAKE_AUTORCC ON) |
| 10 | 10 | |
| 11 | 11 | # include directories |
| 12 | 12 | set(CMAKE_INCLUDE_CURRENT_DIR ON) |
| 13 | -include_directories(src/ src/dso src/hantek) | |
| 13 | +include_directories(src/ src/hantek src/analyse src/widgets src/docks src/configdialog) | |
| 14 | 14 | |
| 15 | 15 | # collect sources and other files |
| 16 | 16 | file(GLOB_RECURSE SRC "src/*.cpp") | ... | ... |
openhantek/src/analyse/dataanalyzer.cpp
0 → 100644
| 1 | +//////////////////////////////////////////////////////////////////////////////// | |
| 2 | +// | |
| 3 | +// OpenHantek | |
| 4 | +// dataanalyzer.cpp | |
| 5 | +// | |
| 6 | +// Copyright (C) 2010 Oliver Haag | |
| 7 | +// oliver.haag@gmail.com | |
| 8 | +// | |
| 9 | +// This program is free software: you can redistribute it and/or modify it | |
| 10 | +// under the terms of the GNU General Public License as published by the Free | |
| 11 | +// Software Foundation, either version 3 of the License, or (at your option) | |
| 12 | +// any later version. | |
| 13 | +// | |
| 14 | +// This program is distributed in the hope that it will be useful, but WITHOUT | |
| 15 | +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 16 | +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| 17 | +// more details. | |
| 18 | +// | |
| 19 | +// You should have received a copy of the GNU General Public License along with | |
| 20 | +// this program. If not, see <http://www.gnu.org/licenses/>. | |
| 21 | +// | |
| 22 | +//////////////////////////////////////////////////////////////////////////////// | |
| 23 | + | |
| 24 | +#define _USE_MATH_DEFINES | |
| 25 | +#include <cmath> | |
| 26 | + | |
| 27 | +#include <QColor> | |
| 28 | +#include <QMutex> | |
| 29 | +#include <QTimer> | |
| 30 | + | |
| 31 | +#include <fftw3.h> | |
| 32 | + | |
| 33 | +#include "dataanalyzer.h" | |
| 34 | + | |
| 35 | +#include "glscope.h" | |
| 36 | +#include "utils/printutils.h" | |
| 37 | +#include "settings.h" | |
| 38 | + | |
| 39 | +std::unique_ptr<DataAnalyzerResult> DataAnalyzer::convertData(const DSOsamples* data, const DsoSettingsScope *scope) { | |
| 40 | + QReadLocker locker(&data->lock); | |
| 41 | + | |
| 42 | + unsigned int channelCount = (unsigned int)scope->voltage.size(); | |
| 43 | + | |
| 44 | + std::unique_ptr<DataAnalyzerResult> result = std::unique_ptr<DataAnalyzerResult>(new DataAnalyzerResult(channelCount)); | |
| 45 | + | |
| 46 | + for (unsigned int channel = 0; channel < channelCount; ++channel) { | |
| 47 | + DataChannel *const channelData = result->modifyData(channel); | |
| 48 | + | |
| 49 | + bool gotDataForChannel = channel < scope->physicalChannels && | |
| 50 | + channel < (unsigned int)data->data.size() && | |
| 51 | + !data->data.at(channel).empty(); | |
| 52 | + bool isMathChannel = channel >= scope->physicalChannels && | |
| 53 | + (scope->voltage[channel].used || | |
| 54 | + scope->spectrum[channel].used) && | |
| 55 | + result->channelCount() >= 2 && | |
| 56 | + !result->data(0)->voltage.sample.empty() && | |
| 57 | + !result->data(1)->voltage.sample.empty(); | |
| 58 | + | |
| 59 | + if ( !gotDataForChannel && !isMathChannel) { | |
| 60 | + // Clear unused channels | |
| 61 | + channelData->voltage.sample.clear(); | |
| 62 | + result->modifyData(scope->physicalChannels)->voltage.interval = 0; | |
| 63 | + continue; | |
| 64 | + } | |
| 65 | + | |
| 66 | + // Set sampling interval | |
| 67 | + const double interval = 1.0 / data->samplerate; | |
| 68 | + if (interval != channelData->voltage.interval) { | |
| 69 | + channelData->voltage.interval = interval; | |
| 70 | + if (data->append) // Clear roll buffer if the samplerate changed | |
| 71 | + channelData->voltage.sample.clear(); | |
| 72 | + } | |
| 73 | + | |
| 74 | + unsigned int size; | |
| 75 | + if (channel < scope->physicalChannels) { | |
| 76 | + size = data->data.at(channel).size(); | |
| 77 | + if (data->append) | |
| 78 | + size += channelData->voltage.sample.size(); | |
| 79 | + result->challengeMaxSamples(size); | |
| 80 | + } else | |
| 81 | + size = result->getMaxSamples(); | |
| 82 | + | |
| 83 | + // Physical channels | |
| 84 | + if (channel < scope->physicalChannels) { | |
| 85 | + // Copy the buffer of the oscilloscope into the sample buffer | |
| 86 | + if (data->append) | |
| 87 | + channelData->voltage.sample.insert( | |
| 88 | + channelData->voltage.sample.end(), | |
| 89 | + data->data.at(channel).begin(), | |
| 90 | + data->data.at(channel).end()); | |
| 91 | + else | |
| 92 | + channelData->voltage.sample = data->data.at(channel); | |
| 93 | + } else { // Math channel | |
| 94 | + // Resize the sample vector | |
| 95 | + channelData->voltage.sample.resize(size); | |
| 96 | + // Set sampling interval | |
| 97 | + result->modifyData(scope->physicalChannels)->voltage.interval = | |
| 98 | + result->data(0)->voltage.interval; | |
| 99 | + | |
| 100 | + // Resize the sample vector | |
| 101 | + result->modifyData(scope->physicalChannels)-> | |
| 102 | + voltage.sample.resize(qMin(result->data(0)->voltage.sample.size(), | |
| 103 | + result->data(1)->voltage.sample.size())); | |
| 104 | + | |
| 105 | + // Calculate values and write them into the sample buffer | |
| 106 | + std::vector<double>::const_iterator ch1Iterator = | |
| 107 | + result->data(0)->voltage.sample.begin(); | |
| 108 | + std::vector<double>::const_iterator ch2Iterator = | |
| 109 | + result->data(1)->voltage.sample.begin(); | |
| 110 | + std::vector<double> &resultData = | |
| 111 | + result->modifyData(scope->physicalChannels)->voltage.sample; | |
| 112 | + for (std::vector<double>::iterator resultIterator = resultData.begin(); | |
| 113 | + resultIterator != resultData.end(); ++resultIterator) { | |
| 114 | + switch (scope->voltage[scope->physicalChannels].misc) { | |
| 115 | + case Dso::MATHMODE_1ADD2: | |
| 116 | + *resultIterator = *ch1Iterator + *ch2Iterator; | |
| 117 | + break; | |
| 118 | + case Dso::MATHMODE_1SUB2: | |
| 119 | + *resultIterator = *ch1Iterator - *ch2Iterator; | |
| 120 | + break; | |
| 121 | + case Dso::MATHMODE_2SUB1: | |
| 122 | + *resultIterator = *ch2Iterator - *ch1Iterator; | |
| 123 | + break; | |
| 124 | + } | |
| 125 | + ++ch1Iterator; | |
| 126 | + ++ch2Iterator; | |
| 127 | + } | |
| 128 | + } | |
| 129 | + } | |
| 130 | + return result; | |
| 131 | +} | |
| 132 | + | |
| 133 | +/// \brief Analyzes the data from the dso. | |
| 134 | +void DataAnalyzer::applySettings(DsoSettingsScope *scope) | |
| 135 | +{ | |
| 136 | + this->scope=scope; | |
| 137 | +} | |
| 138 | + | |
| 139 | +void DataAnalyzer::setSourceData(const DSOsamples *data) | |
| 140 | +{ | |
| 141 | + sourceData = data; | |
| 142 | +} | |
| 143 | + | |
| 144 | +std::unique_ptr<DataAnalyzerResult> DataAnalyzer::getNextResult() | |
| 145 | +{ | |
| 146 | + return std::move(lastResult); | |
| 147 | +} | |
| 148 | + | |
| 149 | +void DataAnalyzer::samplesAvailable() | |
| 150 | +{ | |
| 151 | + if (sourceData == nullptr) return; | |
| 152 | + std::unique_ptr<DataAnalyzerResult> result = convertData(sourceData, scope); | |
| 153 | + spectrumAnalysis(result.get(), lastWindow, lastRecordLength, scope); | |
| 154 | + lastResult = std::move(result); | |
| 155 | + emit analyzed(); | |
| 156 | + | |
| 157 | +} | |
| 158 | + | |
| 159 | +void DataAnalyzer::spectrumAnalysis(DataAnalyzerResult* result, | |
| 160 | + Dso::WindowFunction& lastWindow, unsigned int lastRecordLength, | |
| 161 | + const DsoSettingsScope *scope) { | |
| 162 | + // Calculate frequencies, peak-to-peak voltages and spectrums | |
| 163 | + for (unsigned int channel = 0; channel < result->channelCount(); ++channel) { | |
| 164 | + DataChannel *const channelData = result->modifyData(channel); | |
| 165 | + | |
| 166 | + if (!channelData->voltage.sample.empty()) { | |
| 167 | + // Calculate new window | |
| 168 | + unsigned int sampleCount = channelData->voltage.sample.size(); | |
| 169 | + if (lastWindow != scope->spectrumWindow || | |
| 170 | + lastRecordLength != sampleCount) { | |
| 171 | + if (lastRecordLength != sampleCount) { | |
| 172 | + lastRecordLength = sampleCount; | |
| 173 | + | |
| 174 | + if (result->window) | |
| 175 | + fftw_free(result->window); | |
| 176 | + result->window = | |
| 177 | + (double *)fftw_malloc(sizeof(double) * lastRecordLength); | |
| 178 | + } | |
| 179 | + | |
| 180 | + unsigned int windowEnd = lastRecordLength - 1; | |
| 181 | + lastWindow = scope->spectrumWindow; | |
| 182 | + | |
| 183 | + switch (scope->spectrumWindow) { | |
| 184 | + case Dso::WINDOW_HAMMING: | |
| 185 | + for (unsigned int windowPosition = 0; | |
| 186 | + windowPosition < lastRecordLength; ++windowPosition) | |
| 187 | + *(result->window + windowPosition) = | |
| 188 | + 0.54 - 0.46 * cos(2.0 * M_PI * windowPosition / windowEnd); | |
| 189 | + break; | |
| 190 | + case Dso::WINDOW_HANN: | |
| 191 | + for (unsigned int windowPosition = 0; | |
| 192 | + windowPosition < lastRecordLength; ++windowPosition) | |
| 193 | + *(result->window + windowPosition) = | |
| 194 | + 0.5 * (1.0 - cos(2.0 * M_PI * windowPosition / windowEnd)); | |
| 195 | + break; | |
| 196 | + case Dso::WINDOW_COSINE: | |
| 197 | + for (unsigned int windowPosition = 0; | |
| 198 | + windowPosition < lastRecordLength; ++windowPosition) | |
| 199 | + *(result->window + windowPosition) = | |
| 200 | + sin(M_PI * windowPosition / windowEnd); | |
| 201 | + break; | |
| 202 | + case Dso::WINDOW_LANCZOS: | |
| 203 | + for (unsigned int windowPosition = 0; | |
| 204 | + windowPosition < lastRecordLength; ++windowPosition) { | |
| 205 | + double sincParameter = | |
| 206 | + (2.0 * windowPosition / windowEnd - 1.0) * M_PI; | |
| 207 | + if (sincParameter == 0) | |
| 208 | + *(result->window + windowPosition) = 1; | |
| 209 | + else | |
| 210 | + *(result->window + windowPosition) = | |
| 211 | + sin(sincParameter) / sincParameter; | |
| 212 | + } | |
| 213 | + break; | |
| 214 | + case Dso::WINDOW_BARTLETT: | |
| 215 | + for (unsigned int windowPosition = 0; | |
| 216 | + windowPosition < lastRecordLength; ++windowPosition) | |
| 217 | + *(result->window + windowPosition) = | |
| 218 | + 2.0 / windowEnd * | |
| 219 | + (windowEnd / 2 - | |
| 220 | + std::abs((double)(windowPosition - windowEnd / 2.0))); | |
| 221 | + break; | |
| 222 | + case Dso::WINDOW_TRIANGULAR: | |
| 223 | + for (unsigned int windowPosition = 0; | |
| 224 | + windowPosition < lastRecordLength; ++windowPosition) | |
| 225 | + *(result->window + windowPosition) = | |
| 226 | + 2.0 / lastRecordLength * | |
| 227 | + (lastRecordLength / 2 - | |
| 228 | + std::abs((double)(windowPosition - windowEnd / 2.0))); | |
| 229 | + break; | |
| 230 | + case Dso::WINDOW_GAUSS: { | |
| 231 | + double sigma = 0.4; | |
| 232 | + for (unsigned int windowPosition = 0; | |
| 233 | + windowPosition < lastRecordLength; ++windowPosition) | |
| 234 | + *(result->window + windowPosition) = | |
| 235 | + exp(-0.5 * pow(((windowPosition - windowEnd / 2) / | |
| 236 | + (sigma * windowEnd / 2)), | |
| 237 | + 2)); | |
| 238 | + } break; | |
| 239 | + case Dso::WINDOW_BARTLETTHANN: | |
| 240 | + for (unsigned int windowPosition = 0; | |
| 241 | + windowPosition < lastRecordLength; ++windowPosition) | |
| 242 | + *(result->window + windowPosition) = | |
| 243 | + 0.62 - | |
| 244 | + 0.48 * std::abs((double)(windowPosition / windowEnd - 0.5)) - | |
| 245 | + 0.38 * cos(2.0 * M_PI * windowPosition / windowEnd); | |
| 246 | + break; | |
| 247 | + case Dso::WINDOW_BLACKMAN: { | |
| 248 | + double alpha = 0.16; | |
| 249 | + for (unsigned int windowPosition = 0; | |
| 250 | + windowPosition < lastRecordLength; ++windowPosition) | |
| 251 | + *(result->window + windowPosition) = | |
| 252 | + (1 - alpha) / 2 - | |
| 253 | + 0.5 * cos(2.0 * M_PI * windowPosition / windowEnd) + | |
| 254 | + alpha / 2 * cos(4.0 * M_PI * windowPosition / windowEnd); | |
| 255 | + } break; | |
| 256 | + // case WINDOW_KAISER: | |
| 257 | + // TODO | |
| 258 | + // double alpha = 3.0; | |
| 259 | + // for(unsigned int windowPosition = 0; windowPosition < | |
| 260 | + // lastRecordLength; ++windowPosition) | |
| 261 | + //*(result->window + windowPosition) = ; | |
| 262 | + // break; | |
| 263 | + case Dso::WINDOW_NUTTALL: | |
| 264 | + for (unsigned int windowPosition = 0; | |
| 265 | + windowPosition < lastRecordLength; ++windowPosition) | |
| 266 | + *(result->window + windowPosition) = | |
| 267 | + 0.355768 - | |
| 268 | + 0.487396 * cos(2 * M_PI * windowPosition / windowEnd) + | |
| 269 | + 0.144232 * cos(4 * M_PI * windowPosition / windowEnd) - | |
| 270 | + 0.012604 * cos(6 * M_PI * windowPosition / windowEnd); | |
| 271 | + break; | |
| 272 | + case Dso::WINDOW_BLACKMANHARRIS: | |
| 273 | + for (unsigned int windowPosition = 0; | |
| 274 | + windowPosition < lastRecordLength; ++windowPosition) | |
| 275 | + *(result->window + windowPosition) = | |
| 276 | + 0.35875 - 0.48829 * cos(2 * M_PI * windowPosition / windowEnd) + | |
| 277 | + 0.14128 * cos(4 * M_PI * windowPosition / windowEnd) - | |
| 278 | + 0.01168 * cos(6 * M_PI * windowPosition / windowEnd); | |
| 279 | + break; | |
| 280 | + case Dso::WINDOW_BLACKMANNUTTALL: | |
| 281 | + for (unsigned int windowPosition = 0; | |
| 282 | + windowPosition < lastRecordLength; ++windowPosition) | |
| 283 | + *(result->window + windowPosition) = | |
| 284 | + 0.3635819 - | |
| 285 | + 0.4891775 * cos(2 * M_PI * windowPosition / windowEnd) + | |
| 286 | + 0.1365995 * cos(4 * M_PI * windowPosition / windowEnd) - | |
| 287 | + 0.0106411 * cos(6 * M_PI * windowPosition / windowEnd); | |
| 288 | + break; | |
| 289 | + case Dso::WINDOW_FLATTOP: | |
| 290 | + for (unsigned int windowPosition = 0; | |
| 291 | + windowPosition < lastRecordLength; ++windowPosition) | |
| 292 | + *(result->window + windowPosition) = | |
| 293 | + 1.0 - 1.93 * cos(2 * M_PI * windowPosition / windowEnd) + | |
| 294 | + 1.29 * cos(4 * M_PI * windowPosition / windowEnd) - | |
| 295 | + 0.388 * cos(6 * M_PI * windowPosition / windowEnd) + | |
| 296 | + 0.032 * cos(8 * M_PI * windowPosition / windowEnd); | |
| 297 | + break; | |
| 298 | + default: // Dso::WINDOW_RECTANGULAR | |
| 299 | + for (unsigned int windowPosition = 0; | |
| 300 | + windowPosition < lastRecordLength; ++windowPosition) | |
| 301 | + *(result->window + windowPosition) = 1.0; | |
| 302 | + } | |
| 303 | + } | |
| 304 | + | |
| 305 | + // Set sampling interval | |
| 306 | + channelData->spectrum.interval = | |
| 307 | + 1.0 / channelData->voltage.interval / sampleCount; | |
| 308 | + | |
| 309 | + // Number of real/complex samples | |
| 310 | + unsigned int dftLength = sampleCount / 2; | |
| 311 | + | |
| 312 | + // Reallocate memory for samples if the sample count has changed | |
| 313 | + channelData->spectrum.sample.resize(sampleCount); | |
| 314 | + | |
| 315 | + // Create sample buffer and apply window | |
| 316 | + double *windowedValues = new double[sampleCount]; | |
| 317 | + for (unsigned int position = 0; position < sampleCount; ++position) | |
| 318 | + windowedValues[position] = | |
| 319 | + result->window[position] * | |
| 320 | + channelData->voltage.sample[position]; | |
| 321 | + | |
| 322 | + // Do discrete real to half-complex transformation | |
| 323 | + /// \todo Check if record length is multiple of 2 | |
| 324 | + /// \todo Reuse plan and use FFTW_MEASURE to get fastest algorithm | |
| 325 | + fftw_plan fftPlan = | |
| 326 | + fftw_plan_r2r_1d(sampleCount, windowedValues, | |
| 327 | + &channelData->spectrum.sample.front(), | |
| 328 | + FFTW_R2HC, FFTW_ESTIMATE); | |
| 329 | + fftw_execute(fftPlan); | |
| 330 | + fftw_destroy_plan(fftPlan); | |
| 331 | + | |
| 332 | + // Do an autocorrelation to get the frequency of the signal | |
| 333 | + double *conjugateComplex = | |
| 334 | + windowedValues; // Reuse the windowedValues buffer | |
| 335 | + | |
| 336 | + // Real values | |
| 337 | + unsigned int position; | |
| 338 | + double correctionFactor = 1.0 / dftLength / dftLength; | |
| 339 | + conjugateComplex[0] = (channelData->spectrum.sample[0] * | |
| 340 | + channelData->spectrum.sample[0]) * | |
| 341 | + correctionFactor; | |
| 342 | + for (position = 1; position < dftLength; ++position) | |
| 343 | + conjugateComplex[position] = | |
| 344 | + (channelData->spectrum.sample[position] * | |
| 345 | + channelData->spectrum.sample[position] + | |
| 346 | + channelData->spectrum.sample[sampleCount - position] * | |
| 347 | + channelData->spectrum.sample[sampleCount - position]) * | |
| 348 | + correctionFactor; | |
| 349 | + // Complex values, all zero for autocorrelation | |
| 350 | + conjugateComplex[dftLength] = | |
| 351 | + (channelData->spectrum.sample[dftLength] * | |
| 352 | + channelData->spectrum.sample[dftLength]) * | |
| 353 | + correctionFactor; | |
| 354 | + for (++position; position < sampleCount; ++position) | |
| 355 | + conjugateComplex[position] = 0; | |
| 356 | + | |
| 357 | + // Do half-complex to real inverse transformation | |
| 358 | + double *correlation = new double[sampleCount]; | |
| 359 | + fftPlan = fftw_plan_r2r_1d(sampleCount, conjugateComplex, correlation, | |
| 360 | + FFTW_HC2R, FFTW_ESTIMATE); | |
| 361 | + fftw_execute(fftPlan); | |
| 362 | + fftw_destroy_plan(fftPlan); | |
| 363 | + delete[] conjugateComplex; | |
| 364 | + | |
| 365 | + // Calculate peak-to-peak voltage | |
| 366 | + double minimalVoltage, maximalVoltage; | |
| 367 | + minimalVoltage = maximalVoltage = channelData->voltage.sample[0]; | |
| 368 | + | |
| 369 | + for (unsigned int position = 1; position < sampleCount; ++position) { | |
| 370 | + if (channelData->voltage.sample[position] < minimalVoltage) | |
| 371 | + minimalVoltage = channelData->voltage.sample[position]; | |
| 372 | + else if (channelData->voltage.sample[position] > maximalVoltage) | |
| 373 | + maximalVoltage = channelData->voltage.sample[position]; | |
| 374 | + } | |
| 375 | + | |
| 376 | + channelData->amplitude = maximalVoltage - minimalVoltage; | |
| 377 | + | |
| 378 | + // Get the frequency from the correlation results | |
| 379 | + double minimumCorrelation = correlation[0]; | |
| 380 | + double peakCorrelation = 0; | |
| 381 | + unsigned int peakPosition = 0; | |
| 382 | + | |
| 383 | + for (unsigned int position = 1; position < sampleCount / 2; ++position) { | |
| 384 | + if (correlation[position] > peakCorrelation && | |
| 385 | + correlation[position] > minimumCorrelation * 2) { | |
| 386 | + peakCorrelation = correlation[position]; | |
| 387 | + peakPosition = position; | |
| 388 | + } else if (correlation[position] < minimumCorrelation) | |
| 389 | + minimumCorrelation = correlation[position]; | |
| 390 | + } | |
| 391 | + delete[] correlation; | |
| 392 | + | |
| 393 | + // Calculate the frequency in Hz | |
| 394 | + if (peakPosition) | |
| 395 | + channelData->frequency = | |
| 396 | + 1.0 / (channelData->voltage.interval * peakPosition); | |
| 397 | + else | |
| 398 | + channelData->frequency = 0; | |
| 399 | + | |
| 400 | + // Finally calculate the real spectrum if we want it | |
| 401 | + if (scope->spectrum[channel].used) { | |
| 402 | + // Convert values into dB (Relative to the reference level) | |
| 403 | + double offset = 60 - scope->spectrumReference - | |
| 404 | + 20 * log10(dftLength); | |
| 405 | + double offsetLimit = scope->spectrumLimit - | |
| 406 | + scope->spectrumReference; | |
| 407 | + for (std::vector<double>::iterator spectrumIterator = | |
| 408 | + channelData->spectrum.sample.begin(); | |
| 409 | + spectrumIterator != channelData->spectrum.sample.end(); | |
| 410 | + ++spectrumIterator) { | |
| 411 | + double value = 20 * log10(fabs(*spectrumIterator)) + offset; | |
| 412 | + | |
| 413 | + // Check if this value has to be limited | |
| 414 | + if (offsetLimit > value) | |
| 415 | + value = offsetLimit; | |
| 416 | + | |
| 417 | + *spectrumIterator = value; | |
| 418 | + } | |
| 419 | + } | |
| 420 | + } else if (!channelData->spectrum.sample.empty()) { | |
| 421 | + // Clear unused channels | |
| 422 | + channelData->spectrum.interval = 0; | |
| 423 | + channelData->spectrum.sample.clear(); | |
| 424 | + } | |
| 425 | + } | |
| 426 | +} | ... | ... |
openhantek/src/analyse/dataanalyzer.h
0 → 100644
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 2 | + | |
| 3 | +#pragma once | |
| 4 | + | |
| 5 | +#include <vector> | |
| 6 | + | |
| 7 | +#include <QThread> | |
| 8 | +#include <QMutex> | |
| 9 | +#include <memory> | |
| 10 | + | |
| 11 | +#include "definitions.h" | |
| 12 | +#include "utils/printutils.h" | |
| 13 | +#include "dataanalyzerresult.h" | |
| 14 | +#include "dsosamples.h" | |
| 15 | + | |
| 16 | +struct DsoSettingsScope; | |
| 17 | + | |
| 18 | +//////////////////////////////////////////////////////////////////////////////// | |
| 19 | +/// \class DataAnalyzer dataanalyzer.h | |
| 20 | +/// \brief Analyzes the data from the dso. | |
| 21 | +/// Calculates the spectrum and various data about the signal and saves the | |
| 22 | +/// time-/frequencysteps between two values. | |
| 23 | +class DataAnalyzer : public QObject { | |
| 24 | + Q_OBJECT | |
| 25 | + | |
| 26 | +public: | |
| 27 | + void applySettings(DsoSettingsScope* scope); | |
| 28 | + void setSourceData(const DSOsamples* data); | |
| 29 | + std::unique_ptr<DataAnalyzerResult> getNextResult(); | |
| 30 | + /** | |
| 31 | + * Call this if the source data changed. | |
| 32 | + */ | |
| 33 | + void samplesAvailable(); | |
| 34 | +private: | |
| 35 | + static std::unique_ptr<DataAnalyzerResult> convertData(const DSOsamples *data, const DsoSettingsScope *scope); | |
| 36 | + static void spectrumAnalysis(DataAnalyzerResult* result, | |
| 37 | + Dso::WindowFunction &lastWindow, | |
| 38 | + unsigned int lastRecordLength, | |
| 39 | + const DsoSettingsScope* scope); | |
| 40 | +private: | |
| 41 | + DsoSettingsScope* scope; | |
| 42 | + unsigned int lastRecordLength=0; ///< The record length of the previously analyzed data | |
| 43 | + Dso::WindowFunction lastWindow=(Dso::WindowFunction)-1; ///< The previously used dft window function | |
| 44 | + const DSOsamples *sourceData=nullptr; | |
| 45 | + std::unique_ptr<DataAnalyzerResult> lastResult; | |
| 46 | +signals: | |
| 47 | + void analyzed(); | |
| 48 | +}; | ... | ... |
openhantek/src/analyse/dataanalyzerresult.cpp
0 → 100644
| 1 | +#include "dataanalyzerresult.h" | |
| 2 | +#include <stdexcept> | |
| 3 | + | |
| 4 | +DataAnalyzerResult::DataAnalyzerResult(unsigned int channelCount) | |
| 5 | +{ | |
| 6 | + analyzedData.resize(channelCount); | |
| 7 | +} | |
| 8 | + | |
| 9 | +/// \brief Returns the analyzed data. | |
| 10 | +/// \param channel Channel, whose data should be returned. | |
| 11 | +/// \return Analyzed data as AnalyzedData struct. | |
| 12 | +const DataChannel *DataAnalyzerResult::data(int channel) const { | |
| 13 | + if (channel >= (int)this->analyzedData.size()) | |
| 14 | + return 0; | |
| 15 | + | |
| 16 | + return &this->analyzedData[(size_t)channel]; | |
| 17 | +} | |
| 18 | + | |
| 19 | +DataChannel *DataAnalyzerResult::modifyData(int channel) { | |
| 20 | + if (channel >= (int)this->analyzedData.size()) | |
| 21 | + throw new std::runtime_error("If you modfiy the DataAnalyzerResult, you need to set the channels first!"); | |
| 22 | + | |
| 23 | + return &this->analyzedData[(size_t)channel]; | |
| 24 | +} | |
| 25 | + | |
| 26 | +/// \brief Returns the sample count of the analyzed data. | |
| 27 | +/// \return The maximum sample count of the last analyzed data. | |
| 28 | +unsigned int DataAnalyzerResult::sampleCount() const { return this->maxSamples; } | |
| 29 | + | |
| 30 | +unsigned int DataAnalyzerResult::channelCount() const | |
| 31 | +{ | |
| 32 | + return analyzedData.size(); | |
| 33 | +} | |
| 34 | + | |
| 35 | +void DataAnalyzerResult::challengeMaxSamples(unsigned int newMaxSamples) | |
| 36 | +{ | |
| 37 | + if (newMaxSamples > this->maxSamples) | |
| 38 | + this->maxSamples = newMaxSamples; | |
| 39 | +} | |
| 40 | + | |
| 41 | +unsigned int DataAnalyzerResult::getMaxSamples() const | |
| 42 | +{ | |
| 43 | + return maxSamples; | |
| 44 | +} | ... | ... |
openhantek/src/analyse/dataanalyzerresult.h
0 → 100644
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 2 | + | |
| 3 | +#pragma once | |
| 4 | + | |
| 5 | +#include <vector> | |
| 6 | + | |
| 7 | +//////////////////////////////////////////////////////////////////////////////// | |
| 8 | +/// \struct SampleValues dataanalyzer.h | |
| 9 | +/// \brief Struct for a array of sample values. | |
| 10 | +struct SampleValues { | |
| 11 | + std::vector<double> sample; ///< Vector holding the sampling data | |
| 12 | + double interval=0.0; ///< The interval between two sample values | |
| 13 | +}; | |
| 14 | + | |
| 15 | +//////////////////////////////////////////////////////////////////////////////// | |
| 16 | +/// \struct AnalyzedData dataanalyzer.h | |
| 17 | +/// \brief Struct for the analyzed data. | |
| 18 | +struct DataChannel { | |
| 19 | + SampleValues voltage; ///< The time-domain voltage levels (V) | |
| 20 | + SampleValues spectrum; ///< The frequency-domain power levels (dB) | |
| 21 | + double amplitude = 0.0; ///< The amplitude of the signal | |
| 22 | + double frequency = 0.0; ///< The frequency of the signal | |
| 23 | +}; | |
| 24 | + | |
| 25 | +class DataAnalyzerResult | |
| 26 | +{ | |
| 27 | +public: | |
| 28 | + DataAnalyzerResult(unsigned int channelCount); | |
| 29 | + const DataChannel *data(int channel) const; | |
| 30 | + DataChannel *modifyData(int channel); | |
| 31 | + unsigned int sampleCount() const; | |
| 32 | + unsigned int channelCount() const; | |
| 33 | + double *window = nullptr; ///< The array for the dft window factors | |
| 34 | + | |
| 35 | + /** | |
| 36 | + * Applies a new maximum samples value, if the given value is higher than the already stored one | |
| 37 | + * @param newMaxSamples Maximum samples value | |
| 38 | + */ | |
| 39 | + void challengeMaxSamples(unsigned int newMaxSamples); | |
| 40 | + unsigned int getMaxSamples() const; | |
| 41 | +private: | |
| 42 | + std::vector<DataChannel> analyzedData; ///< The analyzed data for each channel | |
| 43 | + unsigned int maxSamples = 0; ///< The maximum record length of the analyzed data | |
| 44 | +}; | ... | ... |
openhantek/src/dataanalyzer.cpp deleted
| 1 | -//////////////////////////////////////////////////////////////////////////////// | |
| 2 | -// | |
| 3 | -// OpenHantek | |
| 4 | -// dataanalyzer.cpp | |
| 5 | -// | |
| 6 | -// Copyright (C) 2010 Oliver Haag | |
| 7 | -// oliver.haag@gmail.com | |
| 8 | -// | |
| 9 | -// This program is free software: you can redistribute it and/or modify it | |
| 10 | -// under the terms of the GNU General Public License as published by the Free | |
| 11 | -// Software Foundation, either version 3 of the License, or (at your option) | |
| 12 | -// any later version. | |
| 13 | -// | |
| 14 | -// This program is distributed in the hope that it will be useful, but WITHOUT | |
| 15 | -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 16 | -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| 17 | -// more details. | |
| 18 | -// | |
| 19 | -// You should have received a copy of the GNU General Public License along with | |
| 20 | -// this program. If not, see <http://www.gnu.org/licenses/>. | |
| 21 | -// | |
| 22 | -//////////////////////////////////////////////////////////////////////////////// | |
| 23 | - | |
| 24 | -#define _USE_MATH_DEFINES | |
| 25 | -#include <cmath> | |
| 26 | - | |
| 27 | -#include <QColor> | |
| 28 | -#include <QMutex> | |
| 29 | - | |
| 30 | -#include <fftw3.h> | |
| 31 | - | |
| 32 | -#include "dataanalyzer.h" | |
| 33 | - | |
| 34 | -#include "glscope.h" | |
| 35 | -#include "utils/printutils.h" | |
| 36 | -#include "settings.h" | |
| 37 | - | |
| 38 | -//////////////////////////////////////////////////////////////////////////////// | |
| 39 | -// struct SampleValues | |
| 40 | -/// \brief Initializes the members to their default values. | |
| 41 | -SampleValues::SampleValues() { this->interval = 0.0; } | |
| 42 | - | |
| 43 | -//////////////////////////////////////////////////////////////////////////////// | |
| 44 | -// struct AnalyzedData | |
| 45 | -/// \brief Initializes the members to their default values. | |
| 46 | -AnalyzedData::AnalyzedData() { | |
| 47 | - this->amplitude = 0.0; | |
| 48 | - this->frequency = 0.0; | |
| 49 | -} | |
| 50 | - | |
| 51 | -/// \brief Returns the analyzed data. | |
| 52 | -/// \param channel Channel, whose data should be returned. | |
| 53 | -/// \return Analyzed data as AnalyzedData struct. | |
| 54 | -AnalyzedData const *DataAnalyzer::data(unsigned int channel) const { | |
| 55 | - if (channel >= this->analyzedData.size()) | |
| 56 | - return 0; | |
| 57 | - | |
| 58 | - return &this->analyzedData[channel]; | |
| 59 | -} | |
| 60 | - | |
| 61 | -/// \brief Returns the sample count of the analyzed data. | |
| 62 | -/// \return The maximum sample count of the last analyzed data. | |
| 63 | -unsigned int DataAnalyzer::sampleCount() { return this->maxSamples; } | |
| 64 | - | |
| 65 | -QMutex *DataAnalyzer::mutex() const { return this->analyzedDataMutex; } | |
| 66 | - | |
| 67 | -void DataAnalyzer::transferData() { | |
| 68 | - QMutexLocker locker(waitingDataMutex); | |
| 69 | - QMutexLocker locker2(analyzedDataMutex); | |
| 70 | - | |
| 71 | - unsigned int maxSamples = 0; | |
| 72 | - unsigned int channelCount = | |
| 73 | - (unsigned int)scope->voltage.size(); | |
| 74 | - | |
| 75 | - // Adapt the number of channels for analyzed data | |
| 76 | - this->analyzedData.resize(channelCount); | |
| 77 | - | |
| 78 | - for (unsigned int channel = 0; channel < channelCount; ++channel) { | |
| 79 | - AnalyzedData *const channelData = &this->analyzedData[channel]; | |
| 80 | - | |
| 81 | - if ( // Check... | |
| 82 | - ( // ...if we got data for this channel... | |
| 83 | - channel < scope->physicalChannels && | |
| 84 | - channel < (unsigned int)this->waitingData->size() && | |
| 85 | - !this->waitingData->at(channel).empty()) || | |
| 86 | - ( // ...or if it's a math channel that can be calculated | |
| 87 | - channel >= scope->physicalChannels && | |
| 88 | - (scope->voltage[channel].used || | |
| 89 | - scope->spectrum[channel].used) && | |
| 90 | - this->analyzedData.size() >= 2 && | |
| 91 | - !this->analyzedData[0].samples.voltage.sample.empty() && | |
| 92 | - !this->analyzedData[1].samples.voltage.sample.empty())) { | |
| 93 | - // Set sampling interval | |
| 94 | - const double interval = 1.0 / this->waitingDataSamplerate; | |
| 95 | - if (interval != channelData->samples.voltage.interval) { | |
| 96 | - channelData->samples.voltage.interval = interval; | |
| 97 | - if (this->waitingDataAppend) // Clear roll buffer if the samplerate | |
| 98 | - // changed | |
| 99 | - channelData->samples.voltage.sample.clear(); | |
| 100 | - } | |
| 101 | - | |
| 102 | - unsigned int size; | |
| 103 | - if (channel < scope->physicalChannels) { | |
| 104 | - size = this->waitingData->at(channel).size(); | |
| 105 | - if (this->waitingDataAppend) | |
| 106 | - size += channelData->samples.voltage.sample.size(); | |
| 107 | - if (size > maxSamples) | |
| 108 | - maxSamples = size; | |
| 109 | - } else | |
| 110 | - size = maxSamples; | |
| 111 | - | |
| 112 | - // Physical channels | |
| 113 | - if (channel < scope->physicalChannels) { | |
| 114 | - // Copy the buffer of the oscilloscope into the sample buffer | |
| 115 | - if (this->waitingDataAppend) | |
| 116 | - channelData->samples.voltage.sample.insert( | |
| 117 | - channelData->samples.voltage.sample.end(), | |
| 118 | - this->waitingData->at(channel).begin(), | |
| 119 | - this->waitingData->at(channel).end()); | |
| 120 | - else | |
| 121 | - channelData->samples.voltage.sample = this->waitingData->at(channel); | |
| 122 | - } | |
| 123 | - // Math channel | |
| 124 | - else { | |
| 125 | - // Resize the sample vector | |
| 126 | - channelData->samples.voltage.sample.resize(size); | |
| 127 | - // Set sampling interval | |
| 128 | - this->analyzedData[scope->physicalChannels] | |
| 129 | - .samples.voltage.interval = | |
| 130 | - this->analyzedData[0].samples.voltage.interval; | |
| 131 | - | |
| 132 | - // Resize the sample vector | |
| 133 | - this->analyzedData[scope->physicalChannels] | |
| 134 | - .samples.voltage.sample.resize( | |
| 135 | - qMin(this->analyzedData[0].samples.voltage.sample.size(), | |
| 136 | - this->analyzedData[1].samples.voltage.sample.size())); | |
| 137 | - | |
| 138 | - // Calculate values and write them into the sample buffer | |
| 139 | - std::vector<double>::const_iterator ch1Iterator = | |
| 140 | - this->analyzedData[0].samples.voltage.sample.begin(); | |
| 141 | - std::vector<double>::const_iterator ch2Iterator = | |
| 142 | - this->analyzedData[1].samples.voltage.sample.begin(); | |
| 143 | - std::vector<double> &resultData = | |
| 144 | - this->analyzedData[scope->physicalChannels] | |
| 145 | - .samples.voltage.sample; | |
| 146 | - for (std::vector<double>::iterator resultIterator = resultData.begin(); | |
| 147 | - resultIterator != resultData.end(); ++resultIterator) { | |
| 148 | - switch (scope->voltage[scope->physicalChannels].misc) { | |
| 149 | - case Dso::MATHMODE_1ADD2: | |
| 150 | - *resultIterator = *ch1Iterator + *ch2Iterator; | |
| 151 | - break; | |
| 152 | - case Dso::MATHMODE_1SUB2: | |
| 153 | - *resultIterator = *ch1Iterator - *ch2Iterator; | |
| 154 | - break; | |
| 155 | - case Dso::MATHMODE_2SUB1: | |
| 156 | - *resultIterator = *ch2Iterator - *ch1Iterator; | |
| 157 | - break; | |
| 158 | - } | |
| 159 | - ++ch1Iterator; | |
| 160 | - ++ch2Iterator; | |
| 161 | - } | |
| 162 | - } | |
| 163 | - } else { | |
| 164 | - // Clear unused channels | |
| 165 | - channelData->samples.voltage.sample.clear(); | |
| 166 | - this->analyzedData[scope->physicalChannels] | |
| 167 | - .samples.voltage.interval = 0; | |
| 168 | - } | |
| 169 | - } | |
| 170 | -} | |
| 171 | - | |
| 172 | -/// \brief Analyzes the data from the dso. | |
| 173 | -void DataAnalyzer::run(DsoSettingsOptions* options,DsoSettingsScope* scope,DsoSettingsView* view) { | |
| 174 | - this->options=options; | |
| 175 | - this->scope=scope; | |
| 176 | - this->view=view; | |
| 177 | - | |
| 178 | - transferData(); | |
| 179 | - | |
| 180 | - // Lower priority for spectrum calculation | |
| 181 | - this->setPriority(QThread::LowPriority); | |
| 182 | - | |
| 183 | - QMutexLocker locker(analyzedDataMutex); | |
| 184 | - | |
| 185 | - // Calculate frequencies, peak-to-peak voltages and spectrums | |
| 186 | - for (unsigned int channel = 0; channel < this->analyzedData.size(); | |
| 187 | - ++channel) { | |
| 188 | - AnalyzedData *const channelData = &this->analyzedData[channel]; | |
| 189 | - | |
| 190 | - if (!channelData->samples.voltage.sample.empty()) { | |
| 191 | - // Calculate new window | |
| 192 | - unsigned int sampleCount = channelData->samples.voltage.sample.size(); | |
| 193 | - if (this->lastWindow != scope->spectrumWindow || | |
| 194 | - this->lastRecordLength != sampleCount) { | |
| 195 | - if (this->lastRecordLength != sampleCount) { | |
| 196 | - this->lastRecordLength = sampleCount; | |
| 197 | - | |
| 198 | - if (this->window) | |
| 199 | - fftw_free(this->window); | |
| 200 | - this->window = | |
| 201 | - (double *)fftw_malloc(sizeof(double) * this->lastRecordLength); | |
| 202 | - } | |
| 203 | - | |
| 204 | - unsigned int windowEnd = this->lastRecordLength - 1; | |
| 205 | - this->lastWindow = scope->spectrumWindow; | |
| 206 | - | |
| 207 | - switch (scope->spectrumWindow) { | |
| 208 | - case Dso::WINDOW_HAMMING: | |
| 209 | - for (unsigned int windowPosition = 0; | |
| 210 | - windowPosition < this->lastRecordLength; ++windowPosition) | |
| 211 | - *(this->window + windowPosition) = | |
| 212 | - 0.54 - 0.46 * cos(2.0 * M_PI * windowPosition / windowEnd); | |
| 213 | - break; | |
| 214 | - case Dso::WINDOW_HANN: | |
| 215 | - for (unsigned int windowPosition = 0; | |
| 216 | - windowPosition < this->lastRecordLength; ++windowPosition) | |
| 217 | - *(this->window + windowPosition) = | |
| 218 | - 0.5 * (1.0 - cos(2.0 * M_PI * windowPosition / windowEnd)); | |
| 219 | - break; | |
| 220 | - case Dso::WINDOW_COSINE: | |
| 221 | - for (unsigned int windowPosition = 0; | |
| 222 | - windowPosition < this->lastRecordLength; ++windowPosition) | |
| 223 | - *(this->window + windowPosition) = | |
| 224 | - sin(M_PI * windowPosition / windowEnd); | |
| 225 | - break; | |
| 226 | - case Dso::WINDOW_LANCZOS: | |
| 227 | - for (unsigned int windowPosition = 0; | |
| 228 | - windowPosition < this->lastRecordLength; ++windowPosition) { | |
| 229 | - double sincParameter = | |
| 230 | - (2.0 * windowPosition / windowEnd - 1.0) * M_PI; | |
| 231 | - if (sincParameter == 0) | |
| 232 | - *(this->window + windowPosition) = 1; | |
| 233 | - else | |
| 234 | - *(this->window + windowPosition) = | |
| 235 | - sin(sincParameter) / sincParameter; | |
| 236 | - } | |
| 237 | - break; | |
| 238 | - case Dso::WINDOW_BARTLETT: | |
| 239 | - for (unsigned int windowPosition = 0; | |
| 240 | - windowPosition < this->lastRecordLength; ++windowPosition) | |
| 241 | - *(this->window + windowPosition) = | |
| 242 | - 2.0 / windowEnd * | |
| 243 | - (windowEnd / 2 - | |
| 244 | - std::abs((double)(windowPosition - windowEnd / 2.0))); | |
| 245 | - break; | |
| 246 | - case Dso::WINDOW_TRIANGULAR: | |
| 247 | - for (unsigned int windowPosition = 0; | |
| 248 | - windowPosition < this->lastRecordLength; ++windowPosition) | |
| 249 | - *(this->window + windowPosition) = | |
| 250 | - 2.0 / this->lastRecordLength * | |
| 251 | - (this->lastRecordLength / 2 - | |
| 252 | - std::abs((double)(windowPosition - windowEnd / 2.0))); | |
| 253 | - break; | |
| 254 | - case Dso::WINDOW_GAUSS: { | |
| 255 | - double sigma = 0.4; | |
| 256 | - for (unsigned int windowPosition = 0; | |
| 257 | - windowPosition < this->lastRecordLength; ++windowPosition) | |
| 258 | - *(this->window + windowPosition) = | |
| 259 | - exp(-0.5 * pow(((windowPosition - windowEnd / 2) / | |
| 260 | - (sigma * windowEnd / 2)), | |
| 261 | - 2)); | |
| 262 | - } break; | |
| 263 | - case Dso::WINDOW_BARTLETTHANN: | |
| 264 | - for (unsigned int windowPosition = 0; | |
| 265 | - windowPosition < this->lastRecordLength; ++windowPosition) | |
| 266 | - *(this->window + windowPosition) = | |
| 267 | - 0.62 - | |
| 268 | - 0.48 * std::abs((double)(windowPosition / windowEnd - 0.5)) - | |
| 269 | - 0.38 * cos(2.0 * M_PI * windowPosition / windowEnd); | |
| 270 | - break; | |
| 271 | - case Dso::WINDOW_BLACKMAN: { | |
| 272 | - double alpha = 0.16; | |
| 273 | - for (unsigned int windowPosition = 0; | |
| 274 | - windowPosition < this->lastRecordLength; ++windowPosition) | |
| 275 | - *(this->window + windowPosition) = | |
| 276 | - (1 - alpha) / 2 - | |
| 277 | - 0.5 * cos(2.0 * M_PI * windowPosition / windowEnd) + | |
| 278 | - alpha / 2 * cos(4.0 * M_PI * windowPosition / windowEnd); | |
| 279 | - } break; | |
| 280 | - // case WINDOW_KAISER: | |
| 281 | - // TODO | |
| 282 | - // double alpha = 3.0; | |
| 283 | - // for(unsigned int windowPosition = 0; windowPosition < | |
| 284 | - // this->lastRecordLength; ++windowPosition) | |
| 285 | - //*(this->window + windowPosition) = ; | |
| 286 | - // break; | |
| 287 | - case Dso::WINDOW_NUTTALL: | |
| 288 | - for (unsigned int windowPosition = 0; | |
| 289 | - windowPosition < this->lastRecordLength; ++windowPosition) | |
| 290 | - *(this->window + windowPosition) = | |
| 291 | - 0.355768 - | |
| 292 | - 0.487396 * cos(2 * M_PI * windowPosition / windowEnd) + | |
| 293 | - 0.144232 * cos(4 * M_PI * windowPosition / windowEnd) - | |
| 294 | - 0.012604 * cos(6 * M_PI * windowPosition / windowEnd); | |
| 295 | - break; | |
| 296 | - case Dso::WINDOW_BLACKMANHARRIS: | |
| 297 | - for (unsigned int windowPosition = 0; | |
| 298 | - windowPosition < this->lastRecordLength; ++windowPosition) | |
| 299 | - *(this->window + windowPosition) = | |
| 300 | - 0.35875 - 0.48829 * cos(2 * M_PI * windowPosition / windowEnd) + | |
| 301 | - 0.14128 * cos(4 * M_PI * windowPosition / windowEnd) - | |
| 302 | - 0.01168 * cos(6 * M_PI * windowPosition / windowEnd); | |
| 303 | - break; | |
| 304 | - case Dso::WINDOW_BLACKMANNUTTALL: | |
| 305 | - for (unsigned int windowPosition = 0; | |
| 306 | - windowPosition < this->lastRecordLength; ++windowPosition) | |
| 307 | - *(this->window + windowPosition) = | |
| 308 | - 0.3635819 - | |
| 309 | - 0.4891775 * cos(2 * M_PI * windowPosition / windowEnd) + | |
| 310 | - 0.1365995 * cos(4 * M_PI * windowPosition / windowEnd) - | |
| 311 | - 0.0106411 * cos(6 * M_PI * windowPosition / windowEnd); | |
| 312 | - break; | |
| 313 | - case Dso::WINDOW_FLATTOP: | |
| 314 | - for (unsigned int windowPosition = 0; | |
| 315 | - windowPosition < this->lastRecordLength; ++windowPosition) | |
| 316 | - *(this->window + windowPosition) = | |
| 317 | - 1.0 - 1.93 * cos(2 * M_PI * windowPosition / windowEnd) + | |
| 318 | - 1.29 * cos(4 * M_PI * windowPosition / windowEnd) - | |
| 319 | - 0.388 * cos(6 * M_PI * windowPosition / windowEnd) + | |
| 320 | - 0.032 * cos(8 * M_PI * windowPosition / windowEnd); | |
| 321 | - break; | |
| 322 | - default: // Dso::WINDOW_RECTANGULAR | |
| 323 | - for (unsigned int windowPosition = 0; | |
| 324 | - windowPosition < this->lastRecordLength; ++windowPosition) | |
| 325 | - *(this->window + windowPosition) = 1.0; | |
| 326 | - } | |
| 327 | - } | |
| 328 | - | |
| 329 | - // Set sampling interval | |
| 330 | - channelData->samples.spectrum.interval = | |
| 331 | - 1.0 / channelData->samples.voltage.interval / sampleCount; | |
| 332 | - | |
| 333 | - // Number of real/complex samples | |
| 334 | - unsigned int dftLength = sampleCount / 2; | |
| 335 | - | |
| 336 | - // Reallocate memory for samples if the sample count has changed | |
| 337 | - channelData->samples.spectrum.sample.resize(sampleCount); | |
| 338 | - | |
| 339 | - // Create sample buffer and apply window | |
| 340 | - double *windowedValues = new double[sampleCount]; | |
| 341 | - for (unsigned int position = 0; position < sampleCount; ++position) | |
| 342 | - windowedValues[position] = | |
| 343 | - this->window[position] * | |
| 344 | - channelData->samples.voltage.sample[position]; | |
| 345 | - | |
| 346 | - // Do discrete real to half-complex transformation | |
| 347 | - /// \todo Check if record length is multiple of 2 | |
| 348 | - /// \todo Reuse plan and use FFTW_MEASURE to get fastest algorithm | |
| 349 | - fftw_plan fftPlan = | |
| 350 | - fftw_plan_r2r_1d(sampleCount, windowedValues, | |
| 351 | - &channelData->samples.spectrum.sample.front(), | |
| 352 | - FFTW_R2HC, FFTW_ESTIMATE); | |
| 353 | - fftw_execute(fftPlan); | |
| 354 | - fftw_destroy_plan(fftPlan); | |
| 355 | - | |
| 356 | - // Do an autocorrelation to get the frequency of the signal | |
| 357 | - double *conjugateComplex = | |
| 358 | - windowedValues; // Reuse the windowedValues buffer | |
| 359 | - | |
| 360 | - // Real values | |
| 361 | - unsigned int position; | |
| 362 | - double correctionFactor = 1.0 / dftLength / dftLength; | |
| 363 | - conjugateComplex[0] = (channelData->samples.spectrum.sample[0] * | |
| 364 | - channelData->samples.spectrum.sample[0]) * | |
| 365 | - correctionFactor; | |
| 366 | - for (position = 1; position < dftLength; ++position) | |
| 367 | - conjugateComplex[position] = | |
| 368 | - (channelData->samples.spectrum.sample[position] * | |
| 369 | - channelData->samples.spectrum.sample[position] + | |
| 370 | - channelData->samples.spectrum.sample[sampleCount - position] * | |
| 371 | - channelData->samples.spectrum.sample[sampleCount - position]) * | |
| 372 | - correctionFactor; | |
| 373 | - // Complex values, all zero for autocorrelation | |
| 374 | - conjugateComplex[dftLength] = | |
| 375 | - (channelData->samples.spectrum.sample[dftLength] * | |
| 376 | - channelData->samples.spectrum.sample[dftLength]) * | |
| 377 | - correctionFactor; | |
| 378 | - for (++position; position < sampleCount; ++position) | |
| 379 | - conjugateComplex[position] = 0; | |
| 380 | - | |
| 381 | - // Do half-complex to real inverse transformation | |
| 382 | - double *correlation = new double[sampleCount]; | |
| 383 | - fftPlan = fftw_plan_r2r_1d(sampleCount, conjugateComplex, correlation, | |
| 384 | - FFTW_HC2R, FFTW_ESTIMATE); | |
| 385 | - fftw_execute(fftPlan); | |
| 386 | - fftw_destroy_plan(fftPlan); | |
| 387 | - delete[] conjugateComplex; | |
| 388 | - | |
| 389 | - // Calculate peak-to-peak voltage | |
| 390 | - double minimalVoltage, maximalVoltage; | |
| 391 | - minimalVoltage = maximalVoltage = channelData->samples.voltage.sample[0]; | |
| 392 | - | |
| 393 | - for (unsigned int position = 1; position < sampleCount; ++position) { | |
| 394 | - if (channelData->samples.voltage.sample[position] < minimalVoltage) | |
| 395 | - minimalVoltage = channelData->samples.voltage.sample[position]; | |
| 396 | - else if (channelData->samples.voltage.sample[position] > maximalVoltage) | |
| 397 | - maximalVoltage = channelData->samples.voltage.sample[position]; | |
| 398 | - } | |
| 399 | - | |
| 400 | - channelData->amplitude = maximalVoltage - minimalVoltage; | |
| 401 | - | |
| 402 | - // Get the frequency from the correlation results | |
| 403 | - double minimumCorrelation = correlation[0]; | |
| 404 | - double peakCorrelation = 0; | |
| 405 | - unsigned int peakPosition = 0; | |
| 406 | - | |
| 407 | - for (unsigned int position = 1; position < sampleCount / 2; ++position) { | |
| 408 | - if (correlation[position] > peakCorrelation && | |
| 409 | - correlation[position] > minimumCorrelation * 2) { | |
| 410 | - peakCorrelation = correlation[position]; | |
| 411 | - peakPosition = position; | |
| 412 | - } else if (correlation[position] < minimumCorrelation) | |
| 413 | - minimumCorrelation = correlation[position]; | |
| 414 | - } | |
| 415 | - delete[] correlation; | |
| 416 | - | |
| 417 | - // Calculate the frequency in Hz | |
| 418 | - if (peakPosition) | |
| 419 | - channelData->frequency = | |
| 420 | - 1.0 / (channelData->samples.voltage.interval * peakPosition); | |
| 421 | - else | |
| 422 | - channelData->frequency = 0; | |
| 423 | - | |
| 424 | - // Finally calculate the real spectrum if we want it | |
| 425 | - if (scope->spectrum[channel].used) { | |
| 426 | - // Convert values into dB (Relative to the reference level) | |
| 427 | - double offset = 60 - scope->spectrumReference - | |
| 428 | - 20 * log10(dftLength); | |
| 429 | - double offsetLimit = scope->spectrumLimit - | |
| 430 | - scope->spectrumReference; | |
| 431 | - for (std::vector<double>::iterator spectrumIterator = | |
| 432 | - channelData->samples.spectrum.sample.begin(); | |
| 433 | - spectrumIterator != channelData->samples.spectrum.sample.end(); | |
| 434 | - ++spectrumIterator) { | |
| 435 | - double value = 20 * log10(fabs(*spectrumIterator)) + offset; | |
| 436 | - | |
| 437 | - // Check if this value has to be limited | |
| 438 | - if (offsetLimit > value) | |
| 439 | - value = offsetLimit; | |
| 440 | - | |
| 441 | - *spectrumIterator = value; | |
| 442 | - } | |
| 443 | - } | |
| 444 | - } else if (!channelData->samples.spectrum.sample.empty()) { | |
| 445 | - // Clear unused channels | |
| 446 | - channelData->samples.spectrum.interval = 0; | |
| 447 | - channelData->samples.spectrum.sample.clear(); | |
| 448 | - } | |
| 449 | - } | |
| 450 | - | |
| 451 | - this->maxSamples = maxSamples; | |
| 452 | - emit analyzed(maxSamples); | |
| 453 | -} | |
| 454 | - | |
| 455 | -/// \brief Starts the analyzing of new input data. | |
| 456 | -/// \param data The data arrays with the input data. | |
| 457 | -/// \param size The sizes of the data arrays. | |
| 458 | -/// \param samplerate The samplerate for all input data. | |
| 459 | -/// \param append The data will be appended to the previously analyzed data | |
| 460 | -/// (Roll mode). | |
| 461 | -/// \param mutex The mutex for all input data. | |
| 462 | -void DataAnalyzer::analyze(const std::vector<std::vector<double>> *data, | |
| 463 | - double samplerate, bool append, QMutex *mutex) { | |
| 464 | - // Previous analysis still running, drop the new data | |
| 465 | - if (this->isRunning()) { | |
| 466 | -#ifdef DEBUG | |
| 467 | - timestampDebug("Analyzer overload, dropping packets!"); | |
| 468 | -#endif | |
| 469 | - return; | |
| 470 | - } | |
| 471 | - | |
| 472 | - // The thread will analyze it, just save the pointers | |
| 473 | - this->waitingData = data; | |
| 474 | - this->waitingDataAppend = append; | |
| 475 | - this->waitingDataMutex = mutex; | |
| 476 | - this->waitingDataSamplerate = samplerate; | |
| 477 | - this->start(); | |
| 478 | -#ifdef DEBUG | |
| 479 | - static unsigned long id = 0; | |
| 480 | - ++id; | |
| 481 | - timestampDebug(QString("Analyzed packet %1").arg(id)); | |
| 482 | -#endif | |
| 483 | -} |
openhantek/src/dataanalyzer.h deleted
| 1 | -//////////////////////////////////////////////////////////////////////////////// | |
| 2 | -// | |
| 3 | -// OpenHantek | |
| 4 | -/// \file dataanalyzer.h | |
| 5 | -/// \brief Declares the DataAnalyzer class. | |
| 6 | -// | |
| 7 | -// Copyright (C) 2010 Oliver Haag | |
| 8 | -// oliver.haag@gmail.com | |
| 9 | -// | |
| 10 | -// This program is free software: you can redistribute it and/or modify it | |
| 11 | -// under the terms of the GNU General Public License as published by the Free | |
| 12 | -// Software Foundation, either version 3 of the License, or (at your option) | |
| 13 | -// any later version. | |
| 14 | -// | |
| 15 | -// This program is distributed in the hope that it will be useful, but WITHOUT | |
| 16 | -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 17 | -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| 18 | -// more details. | |
| 19 | -// | |
| 20 | -// You should have received a copy of the GNU General Public License along with | |
| 21 | -// this program. If not, see <http://www.gnu.org/licenses/>. | |
| 22 | -// | |
| 23 | -//////////////////////////////////////////////////////////////////////////////// | |
| 24 | - | |
| 25 | -#ifndef DATAANALYZER_H | |
| 26 | -#define DATAANALYZER_H | |
| 27 | - | |
| 28 | -#include <vector> | |
| 29 | - | |
| 30 | -#include <QThread> | |
| 31 | -#include <QMutex> | |
| 32 | - | |
| 33 | -#include "definitions.h" | |
| 34 | -#include "utils/printutils.h" | |
| 35 | - | |
| 36 | -struct DsoSettingsOptions; | |
| 37 | -struct DsoSettingsScope; | |
| 38 | -struct DsoSettingsView; | |
| 39 | -//////////////////////////////////////////////////////////////////////////////// | |
| 40 | -/// \struct SampleValues dataanalyzer.h | |
| 41 | -/// \brief Struct for a array of sample values. | |
| 42 | -struct SampleValues { | |
| 43 | - std::vector<double> sample; ///< Vector holding the sampling data | |
| 44 | - double interval; ///< The interval between two sample values | |
| 45 | - | |
| 46 | - SampleValues(); | |
| 47 | -}; | |
| 48 | - | |
| 49 | -//////////////////////////////////////////////////////////////////////////////// | |
| 50 | -/// \struct SampleData dataanalyzer.h | |
| 51 | -/// \brief Struct for the sample value arrays. | |
| 52 | -struct SampleData { | |
| 53 | - SampleValues voltage; ///< The time-domain voltage levels (V) | |
| 54 | - SampleValues spectrum; ///< The frequency-domain power levels (dB) | |
| 55 | -}; | |
| 56 | - | |
| 57 | -//////////////////////////////////////////////////////////////////////////////// | |
| 58 | -/// \struct AnalyzedData dataanalyzer.h | |
| 59 | -/// \brief Struct for the analyzed data. | |
| 60 | -struct AnalyzedData { | |
| 61 | - SampleData samples; ///< Voltage and spectrum values | |
| 62 | - double amplitude; ///< The amplitude of the signal | |
| 63 | - double frequency; ///< The frequency of the signal | |
| 64 | - | |
| 65 | - AnalyzedData(); | |
| 66 | -}; | |
| 67 | - | |
| 68 | -//////////////////////////////////////////////////////////////////////////////// | |
| 69 | -/// \class DataAnalyzer dataanalyzer.h | |
| 70 | -/// \brief Analyzes the data from the dso. | |
| 71 | -/// Calculates the spectrum and various data about the signal and saves the | |
| 72 | -/// time-/frequencysteps between two values. | |
| 73 | -class DataAnalyzer : public QThread { | |
| 74 | - Q_OBJECT | |
| 75 | - | |
| 76 | -public: | |
| 77 | - const AnalyzedData *data(unsigned int channel) const; | |
| 78 | - unsigned int sampleCount(); | |
| 79 | - /// \brief Returns the mutex for the data. | |
| 80 | - /// \return Mutex for the analyzed data. | |
| 81 | - QMutex *mutex() const; | |
| 82 | - | |
| 83 | -protected: | |
| 84 | - void run(DsoSettingsOptions* options,DsoSettingsScope* scope,DsoSettingsView* view); | |
| 85 | - void transferData(); | |
| 86 | - | |
| 87 | - DsoSettingsOptions* options; ///< General options of the program | |
| 88 | - DsoSettingsScope* scope; ///< All oscilloscope related settings | |
| 89 | - DsoSettingsView* view; ///< All view related settings | |
| 90 | - | |
| 91 | - std::vector<AnalyzedData> analyzedData; ///< The analyzed data for each channel | |
| 92 | - QMutex *analyzedDataMutex= new QMutex(); ///< A mutex for the analyzed data of all channels | |
| 93 | - | |
| 94 | - unsigned int lastRecordLength=0; ///< The record length of the previously analyzed data | |
| 95 | - unsigned int maxSamples=0; ///< The maximum record length of the analyzed data | |
| 96 | - Dso::WindowFunction lastWindow=(Dso::WindowFunction)-1; ///< The previously used dft window function | |
| 97 | - double *window=nullptr; ///< The array for the dft window factors | |
| 98 | - | |
| 99 | - const std::vector<std::vector<double>> *waitingData; ///< Pointer to input data from device | |
| 100 | - double waitingDataSamplerate=0.0; ///< The samplerate of the input data | |
| 101 | - bool waitingDataAppend; ///< true, if waiting data should be appended | |
| 102 | - QMutex *waitingDataMutex=nullptr; ///< A mutex for the input data | |
| 103 | - | |
| 104 | -public slots: | |
| 105 | - void analyze(const std::vector<std::vector<double>> *data, double samplerate, | |
| 106 | - bool append, QMutex *mutex); | |
| 107 | - | |
| 108 | -signals: | |
| 109 | - void analyzed(unsigned long samples); ///< The data with that much samples has | |
| 110 | - ///been analyzed | |
| 111 | -}; | |
| 112 | - | |
| 113 | -#endif |
openhantek/src/dsowidget.cpp
| 1 | -//////////////////////////////////////////////////////////////////////////////// | |
| 2 | -// | |
| 3 | -// OpenHantek | |
| 4 | -// dsowidget.cpp | |
| 5 | -// | |
| 6 | -// Copyright (C) 2010 Oliver Haag | |
| 7 | -// oliver.haag@gmail.com | |
| 8 | -// | |
| 9 | -// This program is free software: you can redistribute it and/or modify it | |
| 10 | -// under the terms of the GNU General Public License as published by the Free | |
| 11 | -// Software Foundation, either version 3 of the License, or (at your option) | |
| 12 | -// any later version. | |
| 13 | -// | |
| 14 | -// This program is distributed in the hope that it will be useful, but WITHOUT | |
| 15 | -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 16 | -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| 17 | -// more details. | |
| 18 | -// | |
| 19 | -// You should have received a copy of the GNU General Public License along with | |
| 20 | -// this program. If not, see <http://www.gnu.org/licenses/>. | |
| 21 | -// | |
| 22 | -//////////////////////////////////////////////////////////////////////////////// | |
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 23 | 2 | |
| 24 | 3 | #include <cmath> |
| 25 | 4 | |
| ... | ... | @@ -33,437 +12,413 @@ |
| 33 | 12 | #include "hantek/definitions.h" |
| 34 | 13 | #include "utils/printutils.h" |
| 35 | 14 | #include "utils/dsoStrings.h" |
| 36 | -#include "dataanalyzer.h" | |
| 37 | 15 | #include "exporter.h" |
| 38 | 16 | #include "glscope.h" |
| 39 | -#include "levelslider.h" | |
| 17 | +#include "widgets/levelslider.h" | |
| 40 | 18 | #include "settings.h" |
| 41 | 19 | |
| 42 | -//////////////////////////////////////////////////////////////////////////////// | |
| 43 | -// class DsoWidget | |
| 44 | -/// \brief Initializes the components of the oszilloscope-screen. | |
| 45 | -/// \param settings The settings object containing the oscilloscope settings. | |
| 46 | -/// \param dataAnalyzer The data analyzer that should be used as data source. | |
| 47 | -/// \param parent The parent widget. | |
| 48 | -/// \param flags Flags for the window manager. | |
| 49 | -DsoWidget::DsoWidget(DsoSettings *settings, DataAnalyzer *dataAnalyzer, | |
| 50 | - QWidget *parent, Qt::WindowFlags flags) | |
| 51 | - : QWidget(parent, flags) { | |
| 52 | - this->settings = settings; | |
| 53 | - this->dataAnalyzer = dataAnalyzer; | |
| 54 | - | |
| 55 | - // Palette for this widget | |
| 56 | - QPalette palette; | |
| 57 | - palette.setColor(QPalette::Background, | |
| 58 | - this->settings->view.color.screen.background); | |
| 59 | - palette.setColor(QPalette::WindowText, | |
| 60 | - this->settings->view.color.screen.text); | |
| 61 | - | |
| 62 | - // The OpenGL accelerated scope widgets | |
| 63 | - this->generator = new GlGenerator(this->settings, this); | |
| 64 | - this->generator->setDataAnalyzer(this->dataAnalyzer); | |
| 65 | - this->mainScope = new GlScope(this->settings); | |
| 66 | - this->mainScope->setGenerator(this->generator); | |
| 67 | - this->zoomScope = new GlScope(this->settings); | |
| 68 | - this->zoomScope->setGenerator(this->generator); | |
| 69 | - this->zoomScope->setZoomMode(true); | |
| 70 | - | |
| 71 | -#ifdef OS_DARWIN | |
| 72 | - // Workaround for https://bugreports.qt-project.org/browse/QTBUG-8580 | |
| 73 | - this->mainScope->hide(); | |
| 74 | - this->mainScope->show(); | |
| 75 | -#endif | |
| 76 | - | |
| 77 | - // The offset sliders for all possible channels | |
| 78 | - this->offsetSlider = new LevelSlider(Qt::RightArrow); | |
| 79 | - for (int channel = 0; channel < this->settings->scope.voltage.count(); | |
| 80 | - ++channel) { | |
| 81 | - this->offsetSlider->addSlider(this->settings->scope.voltage[channel].name, | |
| 82 | - channel); | |
| 83 | - this->offsetSlider->setColor( | |
| 84 | - channel, this->settings->view.color.screen.voltage[channel]); | |
| 85 | - this->offsetSlider->setLimits(channel, -DIVS_VOLTAGE / 2, DIVS_VOLTAGE / 2); | |
| 86 | - this->offsetSlider->setStep(channel, 0.2); | |
| 87 | - this->offsetSlider->setValue(channel, | |
| 88 | - this->settings->scope.voltage[channel].offset); | |
| 89 | - this->offsetSlider->setVisible(channel, | |
| 90 | - this->settings->scope.voltage[channel].used); | |
| 91 | - } | |
| 92 | - for (int channel = 0; channel < this->settings->scope.voltage.count(); | |
| 93 | - ++channel) { | |
| 94 | - this->offsetSlider->addSlider(this->settings->scope.spectrum[channel].name, | |
| 95 | - this->settings->scope.voltage.count() + | |
| 20 | +DsoWidget::DsoWidget(DsoSettings *settings, QWidget *parent, Qt::WindowFlags flags) | |
| 21 | + : QWidget(parent, flags), settings(settings), | |
| 22 | + generator(new GlGenerator(&settings->scope, &settings->view)), | |
| 23 | + mainScope(new GlScope(settings, generator)), | |
| 24 | + zoomScope(new GlScope(settings, generator)) { | |
| 25 | + | |
| 26 | + // Palette for this widget | |
| 27 | + QPalette palette; | |
| 28 | + palette.setColor(QPalette::Background, this->settings->view.color.screen.background); | |
| 29 | + palette.setColor(QPalette::WindowText, this->settings->view.color.screen.text); | |
| 30 | + | |
| 31 | + // The OpenGL accelerated scope widgets | |
| 32 | + this->zoomScope->setZoomMode(true); | |
| 33 | + | |
| 34 | + // The offset sliders for all possible channels | |
| 35 | + this->offsetSlider = new LevelSlider(Qt::RightArrow); | |
| 36 | + for (int channel = 0; channel < this->settings->scope.voltage.count(); | |
| 37 | + ++channel) { | |
| 38 | + this->offsetSlider->addSlider(this->settings->scope.voltage[channel].name, | |
| 96 | 39 | channel); |
| 97 | - this->offsetSlider->setColor( | |
| 98 | - this->settings->scope.voltage.count() + channel, | |
| 99 | - this->settings->view.color.screen.spectrum[channel]); | |
| 100 | - this->offsetSlider->setLimits(this->settings->scope.voltage.count() + | |
| 40 | + this->offsetSlider->setColor( | |
| 41 | + channel, this->settings->view.color.screen.voltage[channel]); | |
| 42 | + this->offsetSlider->setLimits(channel, -DIVS_VOLTAGE / 2, DIVS_VOLTAGE / 2); | |
| 43 | + this->offsetSlider->setStep(channel, 0.2); | |
| 44 | + this->offsetSlider->setValue(channel, | |
| 45 | + this->settings->scope.voltage[channel].offset); | |
| 46 | + this->offsetSlider->setVisible(channel, | |
| 47 | + this->settings->scope.voltage[channel].used); | |
| 48 | + } | |
| 49 | + for (int channel = 0; channel < this->settings->scope.voltage.count(); | |
| 50 | + ++channel) { | |
| 51 | + this->offsetSlider->addSlider(this->settings->scope.spectrum[channel].name, | |
| 52 | + this->settings->scope.voltage.count() + | |
| 53 | + channel); | |
| 54 | + this->offsetSlider->setColor( | |
| 55 | + this->settings->scope.voltage.count() + channel, | |
| 56 | + this->settings->view.color.screen.spectrum[channel]); | |
| 57 | + this->offsetSlider->setLimits(this->settings->scope.voltage.count() + | |
| 101 | 58 | channel, |
| 102 | - -DIVS_VOLTAGE / 2, DIVS_VOLTAGE / 2); | |
| 103 | - this->offsetSlider->setStep(this->settings->scope.voltage.count() + channel, | |
| 104 | - 0.2); | |
| 105 | - this->offsetSlider->setValue( | |
| 106 | - this->settings->scope.voltage.count() + channel, | |
| 107 | - this->settings->scope.spectrum[channel].offset); | |
| 108 | - this->offsetSlider->setVisible( | |
| 109 | - this->settings->scope.voltage.count() + channel, | |
| 110 | - this->settings->scope.spectrum[channel].used); | |
| 111 | - } | |
| 112 | - | |
| 113 | - // The triggerPosition slider | |
| 114 | - this->triggerPositionSlider = new LevelSlider(Qt::DownArrow); | |
| 115 | - this->triggerPositionSlider->addSlider(); | |
| 116 | - this->triggerPositionSlider->setLimits(0, 0.0, 1.0); | |
| 117 | - this->triggerPositionSlider->setStep(0, 0.2 / DIVS_TIME); | |
| 118 | - this->triggerPositionSlider->setValue(0, | |
| 119 | - this->settings->scope.trigger.position); | |
| 120 | - this->triggerPositionSlider->setVisible(0, true); | |
| 121 | - | |
| 122 | - // The sliders for the trigger levels | |
| 123 | - this->triggerLevelSlider = new LevelSlider(Qt::LeftArrow); | |
| 124 | - for (int channel = 0; channel < (int)this->settings->scope.physicalChannels; | |
| 125 | - ++channel) { | |
| 126 | - this->triggerLevelSlider->addSlider(channel); | |
| 127 | - this->triggerLevelSlider->setColor( | |
| 128 | - channel, | |
| 129 | - (!this->settings->scope.trigger.special && | |
| 130 | - channel == (int)this->settings->scope.trigger.source) | |
| 131 | - ? this->settings->view.color.screen.voltage[channel] | |
| 132 | - : this->settings->view.color.screen.voltage[channel].darker()); | |
| 133 | - this->adaptTriggerLevelSlider(channel); | |
| 134 | - this->triggerLevelSlider->setValue( | |
| 135 | - channel, this->settings->scope.voltage[channel].trigger); | |
| 136 | - this->triggerLevelSlider->setVisible( | |
| 137 | - channel, this->settings->scope.voltage[channel].used); | |
| 138 | - } | |
| 139 | - | |
| 140 | - // The marker slider | |
| 141 | - this->markerSlider = new LevelSlider(Qt::UpArrow); | |
| 142 | - for (int marker = 0; marker < MARKER_COUNT; ++marker) { | |
| 143 | - this->markerSlider->addSlider(QString::number(marker + 1), marker); | |
| 144 | - this->markerSlider->setLimits(marker, -DIVS_TIME / 2, DIVS_TIME / 2); | |
| 145 | - this->markerSlider->setStep(marker, 0.2); | |
| 146 | - this->markerSlider->setValue( | |
| 147 | - marker, this->settings->scope.horizontal.marker[marker]); | |
| 148 | - this->markerSlider->setVisible(marker, true); | |
| 149 | - this->settings->scope.horizontal.marker_visible[marker] = true; | |
| 150 | - } | |
| 151 | - | |
| 152 | - // The table for the settings | |
| 153 | - this->settingsTriggerLabel = new QLabel(); | |
| 154 | - this->settingsTriggerLabel->setMinimumWidth(160); | |
| 155 | - this->settingsRecordLengthLabel = new QLabel(); | |
| 156 | - this->settingsRecordLengthLabel->setAlignment(Qt::AlignRight); | |
| 157 | - this->settingsRecordLengthLabel->setPalette(palette); | |
| 158 | - this->settingsSamplerateLabel = new QLabel(); | |
| 159 | - this->settingsSamplerateLabel->setAlignment(Qt::AlignRight); | |
| 160 | - this->settingsSamplerateLabel->setPalette(palette); | |
| 161 | - this->settingsTimebaseLabel = new QLabel(); | |
| 162 | - this->settingsTimebaseLabel->setAlignment(Qt::AlignRight); | |
| 163 | - this->settingsTimebaseLabel->setPalette(palette); | |
| 164 | - this->settingsFrequencybaseLabel = new QLabel(); | |
| 165 | - this->settingsFrequencybaseLabel->setAlignment(Qt::AlignRight); | |
| 166 | - this->settingsFrequencybaseLabel->setPalette(palette); | |
| 167 | - this->settingsLayout = new QHBoxLayout(); | |
| 168 | - this->settingsLayout->addWidget(this->settingsTriggerLabel); | |
| 169 | - this->settingsLayout->addWidget(this->settingsRecordLengthLabel, 1); | |
| 170 | - this->settingsLayout->addWidget(this->settingsSamplerateLabel, 1); | |
| 171 | - this->settingsLayout->addWidget(this->settingsTimebaseLabel, 1); | |
| 172 | - this->settingsLayout->addWidget(this->settingsFrequencybaseLabel, 1); | |
| 173 | - | |
| 174 | - // The table for the marker details | |
| 175 | - this->markerInfoLabel = new QLabel(); | |
| 176 | - this->markerInfoLabel->setMinimumWidth(160); | |
| 177 | - this->markerInfoLabel->setPalette(palette); | |
| 178 | - this->markerTimeLabel = new QLabel(); | |
| 179 | - this->markerTimeLabel->setAlignment(Qt::AlignRight); | |
| 180 | - this->markerTimeLabel->setPalette(palette); | |
| 181 | - this->markerFrequencyLabel = new QLabel(); | |
| 182 | - this->markerFrequencyLabel->setAlignment(Qt::AlignRight); | |
| 183 | - this->markerFrequencyLabel->setPalette(palette); | |
| 184 | - this->markerTimebaseLabel = new QLabel(); | |
| 185 | - this->markerTimebaseLabel->setAlignment(Qt::AlignRight); | |
| 186 | - this->markerTimebaseLabel->setPalette(palette); | |
| 187 | - this->markerFrequencybaseLabel = new QLabel(); | |
| 188 | - this->markerFrequencybaseLabel->setAlignment(Qt::AlignRight); | |
| 189 | - this->markerFrequencybaseLabel->setPalette(palette); | |
| 190 | - this->markerLayout = new QHBoxLayout(); | |
| 191 | - this->markerLayout->addWidget(this->markerInfoLabel); | |
| 192 | - this->markerLayout->addWidget(this->markerTimeLabel, 1); | |
| 193 | - this->markerLayout->addWidget(this->markerFrequencyLabel, 1); | |
| 194 | - this->markerLayout->addWidget(this->markerTimebaseLabel, 1); | |
| 195 | - this->markerLayout->addWidget(this->markerFrequencybaseLabel, 1); | |
| 196 | - | |
| 197 | - // The table for the measurements | |
| 198 | - QPalette tablePalette = palette; | |
| 199 | - this->measurementLayout = new QGridLayout(); | |
| 200 | - this->measurementLayout->setColumnMinimumWidth(0, 64); | |
| 201 | - this->measurementLayout->setColumnMinimumWidth(1, 32); | |
| 202 | - this->measurementLayout->setColumnStretch(2, 2); | |
| 203 | - this->measurementLayout->setColumnStretch(3, 2); | |
| 204 | - this->measurementLayout->setColumnStretch(4, 3); | |
| 205 | - this->measurementLayout->setColumnStretch(5, 3); | |
| 206 | - for (int channel = 0; channel < this->settings->scope.voltage.count(); | |
| 207 | - ++channel) { | |
| 208 | - tablePalette.setColor(QPalette::WindowText, | |
| 209 | - this->settings->view.color.screen.voltage[channel]); | |
| 210 | - this->measurementNameLabel.append( | |
| 211 | - new QLabel(this->settings->scope.voltage[channel].name)); | |
| 212 | - this->measurementNameLabel[channel]->setPalette(tablePalette); | |
| 213 | - this->measurementMiscLabel.append(new QLabel()); | |
| 214 | - this->measurementMiscLabel[channel]->setPalette(tablePalette); | |
| 215 | - this->measurementGainLabel.append(new QLabel()); | |
| 216 | - this->measurementGainLabel[channel]->setAlignment(Qt::AlignRight); | |
| 217 | - this->measurementGainLabel[channel]->setPalette(tablePalette); | |
| 218 | - tablePalette.setColor(QPalette::WindowText, | |
| 219 | - this->settings->view.color.screen.spectrum[channel]); | |
| 220 | - this->measurementMagnitudeLabel.append(new QLabel()); | |
| 221 | - this->measurementMagnitudeLabel[channel]->setAlignment(Qt::AlignRight); | |
| 222 | - this->measurementMagnitudeLabel[channel]->setPalette(tablePalette); | |
| 223 | - this->measurementAmplitudeLabel.append(new QLabel()); | |
| 224 | - this->measurementAmplitudeLabel[channel]->setAlignment(Qt::AlignRight); | |
| 225 | - this->measurementAmplitudeLabel[channel]->setPalette(palette); | |
| 226 | - this->measurementFrequencyLabel.append(new QLabel()); | |
| 227 | - this->measurementFrequencyLabel[channel]->setAlignment(Qt::AlignRight); | |
| 228 | - this->measurementFrequencyLabel[channel]->setPalette(palette); | |
| 229 | - this->setMeasurementVisible(channel, | |
| 230 | - this->settings->scope.voltage[channel].used); | |
| 231 | - this->measurementLayout->addWidget(this->measurementNameLabel[channel], | |
| 232 | - channel, 0); | |
| 233 | - this->measurementLayout->addWidget(this->measurementMiscLabel[channel], | |
| 234 | - channel, 1); | |
| 235 | - this->measurementLayout->addWidget(this->measurementGainLabel[channel], | |
| 236 | - channel, 2); | |
| 237 | - this->measurementLayout->addWidget(this->measurementMagnitudeLabel[channel], | |
| 238 | - channel, 3); | |
| 239 | - this->measurementLayout->addWidget(this->measurementAmplitudeLabel[channel], | |
| 240 | - channel, 4); | |
| 241 | - this->measurementLayout->addWidget(this->measurementFrequencyLabel[channel], | |
| 242 | - channel, 5); | |
| 243 | - if ((unsigned int)channel < this->settings->scope.physicalChannels) | |
| 244 | - this->updateVoltageCoupling(channel); | |
| 245 | - else | |
| 246 | - this->updateMathMode(); | |
| 247 | - this->updateVoltageDetails(channel); | |
| 248 | - this->updateSpectrumDetails(channel); | |
| 249 | - } | |
| 250 | - | |
| 251 | - // The layout for the widgets | |
| 252 | - this->mainLayout = new QGridLayout(); | |
| 253 | - this->mainLayout->setColumnStretch(2, 1); // Scopes increase their size | |
| 254 | - this->mainLayout->setRowStretch(3, 1); | |
| 255 | - // Bars around the scope, needed because the slider-drawing-area is outside | |
| 256 | - // the scope at min/max | |
| 257 | - this->mainLayout->setColumnMinimumWidth( | |
| 258 | - 1, this->triggerPositionSlider->preMargin()); | |
| 259 | - this->mainLayout->setColumnMinimumWidth( | |
| 260 | - 3, this->triggerPositionSlider->postMargin()); | |
| 261 | - this->mainLayout->setRowMinimumHeight(2, this->offsetSlider->preMargin()); | |
| 262 | - this->mainLayout->setRowMinimumHeight(4, this->offsetSlider->postMargin()); | |
| 263 | - this->mainLayout->setRowMinimumHeight(6, 4); | |
| 264 | - this->mainLayout->setRowMinimumHeight(8, 4); | |
| 265 | - this->mainLayout->setRowMinimumHeight(10, 8); | |
| 266 | - this->mainLayout->setSpacing(0); | |
| 267 | - this->mainLayout->addLayout(this->settingsLayout, 0, 0, 1, 5); | |
| 268 | - this->mainLayout->addWidget(this->mainScope, 3, 2); | |
| 269 | - this->mainLayout->addWidget(this->offsetSlider, 2, 0, 3, 2, Qt::AlignRight); | |
| 270 | - this->mainLayout->addWidget(this->triggerPositionSlider, 1, 1, 2, 3, | |
| 271 | - Qt::AlignBottom); | |
| 272 | - this->mainLayout->addWidget(this->triggerLevelSlider, 2, 3, 3, 2, | |
| 273 | - Qt::AlignLeft); | |
| 274 | - this->mainLayout->addWidget(this->markerSlider, 4, 1, 2, 3, Qt::AlignTop); | |
| 275 | - this->mainLayout->addLayout(this->markerLayout, 7, 0, 1, 5); | |
| 276 | - this->mainLayout->addWidget(this->zoomScope, 9, 2); | |
| 277 | - this->mainLayout->addLayout(this->measurementLayout, 11, 0, 1, 5); | |
| 278 | - | |
| 279 | - // Apply settings and update measured values | |
| 280 | - this->updateTriggerDetails(); | |
| 281 | - this->updateRecordLength(this->settings->scope.horizontal.recordLength); | |
| 282 | - this->updateFrequencybase(this->settings->scope.horizontal.frequencybase); | |
| 283 | - this->updateSamplerate(this->settings->scope.horizontal.samplerate); | |
| 284 | - this->updateTimebase(this->settings->scope.horizontal.timebase); | |
| 285 | - this->updateZoom(this->settings->view.zoom); | |
| 286 | - | |
| 287 | - // The widget itself | |
| 288 | - this->setPalette(palette); | |
| 289 | - this->setBackgroundRole(QPalette::Background); | |
| 290 | - this->setAutoFillBackground(true); | |
| 291 | - this->setLayout(this->mainLayout); | |
| 292 | - | |
| 293 | - // Connect change-signals of sliders | |
| 294 | - this->connect(this->offsetSlider, SIGNAL(valueChanged(int, double)), this, | |
| 295 | - SLOT(updateOffset(int, double))); | |
| 296 | - this->connect(this->triggerPositionSlider, SIGNAL(valueChanged(int, double)), | |
| 297 | - this, SLOT(updateTriggerPosition(int, double))); | |
| 298 | - this->connect(this->triggerLevelSlider, SIGNAL(valueChanged(int, double)), | |
| 299 | - this, SLOT(updateTriggerLevel(int, double))); | |
| 300 | - this->connect(this->markerSlider, SIGNAL(valueChanged(int, double)), this, | |
| 301 | - SLOT(updateMarker(int, double))); | |
| 302 | - this->connect(this->markerSlider, SIGNAL(valueChanged(int, double)), | |
| 303 | - this->mainScope, SLOT(update())); | |
| 304 | - this->connect(this->markerSlider, SIGNAL(valueChanged(int, double)), | |
| 305 | - this->zoomScope, SLOT(update())); | |
| 306 | - | |
| 307 | - // Connect other signals | |
| 308 | - this->connect(this->dataAnalyzer, SIGNAL(analyzed(unsigned long)), this, | |
| 309 | - SLOT(dataAnalyzed())); | |
| 310 | - this->connect(this->dataAnalyzer, SIGNAL(analyzed(unsigned long)), this, | |
| 311 | - SLOT(updateRecordLength(unsigned long))); | |
| 59 | + -DIVS_VOLTAGE / 2, DIVS_VOLTAGE / 2); | |
| 60 | + this->offsetSlider->setStep(this->settings->scope.voltage.count() + channel, | |
| 61 | + 0.2); | |
| 62 | + this->offsetSlider->setValue( | |
| 63 | + this->settings->scope.voltage.count() + channel, | |
| 64 | + this->settings->scope.spectrum[channel].offset); | |
| 65 | + this->offsetSlider->setVisible( | |
| 66 | + this->settings->scope.voltage.count() + channel, | |
| 67 | + this->settings->scope.spectrum[channel].used); | |
| 68 | + } | |
| 69 | + | |
| 70 | + // The triggerPosition slider | |
| 71 | + this->triggerPositionSlider = new LevelSlider(Qt::DownArrow); | |
| 72 | + this->triggerPositionSlider->addSlider(); | |
| 73 | + this->triggerPositionSlider->setLimits(0, 0.0, 1.0); | |
| 74 | + this->triggerPositionSlider->setStep(0, 0.2 / DIVS_TIME); | |
| 75 | + this->triggerPositionSlider->setValue(0, | |
| 76 | + this->settings->scope.trigger.position); | |
| 77 | + this->triggerPositionSlider->setVisible(0, true); | |
| 78 | + | |
| 79 | + // The sliders for the trigger levels | |
| 80 | + this->triggerLevelSlider = new LevelSlider(Qt::LeftArrow); | |
| 81 | + for (int channel = 0; channel < (int)this->settings->scope.physicalChannels; | |
| 82 | + ++channel) { | |
| 83 | + this->triggerLevelSlider->addSlider(channel); | |
| 84 | + this->triggerLevelSlider->setColor( | |
| 85 | + channel, | |
| 86 | + (!this->settings->scope.trigger.special && | |
| 87 | + channel == (int)this->settings->scope.trigger.source) | |
| 88 | + ? this->settings->view.color.screen.voltage[channel] | |
| 89 | + : this->settings->view.color.screen.voltage[channel].darker()); | |
| 90 | + this->adaptTriggerLevelSlider(channel); | |
| 91 | + this->triggerLevelSlider->setValue( | |
| 92 | + channel, this->settings->scope.voltage[channel].trigger); | |
| 93 | + this->triggerLevelSlider->setVisible( | |
| 94 | + channel, this->settings->scope.voltage[channel].used); | |
| 95 | + } | |
| 96 | + | |
| 97 | + // The marker slider | |
| 98 | + this->markerSlider = new LevelSlider(Qt::UpArrow); | |
| 99 | + for (int marker = 0; marker < MARKER_COUNT; ++marker) { | |
| 100 | + this->markerSlider->addSlider(QString::number(marker + 1), marker); | |
| 101 | + this->markerSlider->setLimits(marker, -DIVS_TIME / 2, DIVS_TIME / 2); | |
| 102 | + this->markerSlider->setStep(marker, 0.2); | |
| 103 | + this->markerSlider->setValue( | |
| 104 | + marker, this->settings->scope.horizontal.marker[marker]); | |
| 105 | + this->markerSlider->setVisible(marker, true); | |
| 106 | + this->settings->scope.horizontal.marker_visible[marker] = true; | |
| 107 | + } | |
| 108 | + | |
| 109 | + // The table for the settings | |
| 110 | + this->settingsTriggerLabel = new QLabel(); | |
| 111 | + this->settingsTriggerLabel->setMinimumWidth(160); | |
| 112 | + this->settingsRecordLengthLabel = new QLabel(); | |
| 113 | + this->settingsRecordLengthLabel->setAlignment(Qt::AlignRight); | |
| 114 | + this->settingsRecordLengthLabel->setPalette(palette); | |
| 115 | + this->settingsSamplerateLabel = new QLabel(); | |
| 116 | + this->settingsSamplerateLabel->setAlignment(Qt::AlignRight); | |
| 117 | + this->settingsSamplerateLabel->setPalette(palette); | |
| 118 | + this->settingsTimebaseLabel = new QLabel(); | |
| 119 | + this->settingsTimebaseLabel->setAlignment(Qt::AlignRight); | |
| 120 | + this->settingsTimebaseLabel->setPalette(palette); | |
| 121 | + this->settingsFrequencybaseLabel = new QLabel(); | |
| 122 | + this->settingsFrequencybaseLabel->setAlignment(Qt::AlignRight); | |
| 123 | + this->settingsFrequencybaseLabel->setPalette(palette); | |
| 124 | + this->settingsLayout = new QHBoxLayout(); | |
| 125 | + this->settingsLayout->addWidget(this->settingsTriggerLabel); | |
| 126 | + this->settingsLayout->addWidget(this->settingsRecordLengthLabel, 1); | |
| 127 | + this->settingsLayout->addWidget(this->settingsSamplerateLabel, 1); | |
| 128 | + this->settingsLayout->addWidget(this->settingsTimebaseLabel, 1); | |
| 129 | + this->settingsLayout->addWidget(this->settingsFrequencybaseLabel, 1); | |
| 130 | + | |
| 131 | + // The table for the marker details | |
| 132 | + this->markerInfoLabel = new QLabel(); | |
| 133 | + this->markerInfoLabel->setMinimumWidth(160); | |
| 134 | + this->markerInfoLabel->setPalette(palette); | |
| 135 | + this->markerTimeLabel = new QLabel(); | |
| 136 | + this->markerTimeLabel->setAlignment(Qt::AlignRight); | |
| 137 | + this->markerTimeLabel->setPalette(palette); | |
| 138 | + this->markerFrequencyLabel = new QLabel(); | |
| 139 | + this->markerFrequencyLabel->setAlignment(Qt::AlignRight); | |
| 140 | + this->markerFrequencyLabel->setPalette(palette); | |
| 141 | + this->markerTimebaseLabel = new QLabel(); | |
| 142 | + this->markerTimebaseLabel->setAlignment(Qt::AlignRight); | |
| 143 | + this->markerTimebaseLabel->setPalette(palette); | |
| 144 | + this->markerFrequencybaseLabel = new QLabel(); | |
| 145 | + this->markerFrequencybaseLabel->setAlignment(Qt::AlignRight); | |
| 146 | + this->markerFrequencybaseLabel->setPalette(palette); | |
| 147 | + this->markerLayout = new QHBoxLayout(); | |
| 148 | + this->markerLayout->addWidget(this->markerInfoLabel); | |
| 149 | + this->markerLayout->addWidget(this->markerTimeLabel, 1); | |
| 150 | + this->markerLayout->addWidget(this->markerFrequencyLabel, 1); | |
| 151 | + this->markerLayout->addWidget(this->markerTimebaseLabel, 1); | |
| 152 | + this->markerLayout->addWidget(this->markerFrequencybaseLabel, 1); | |
| 153 | + | |
| 154 | + // The table for the measurements | |
| 155 | + QPalette tablePalette = palette; | |
| 156 | + this->measurementLayout = new QGridLayout(); | |
| 157 | + this->measurementLayout->setColumnMinimumWidth(0, 64); | |
| 158 | + this->measurementLayout->setColumnMinimumWidth(1, 32); | |
| 159 | + this->measurementLayout->setColumnStretch(2, 2); | |
| 160 | + this->measurementLayout->setColumnStretch(3, 2); | |
| 161 | + this->measurementLayout->setColumnStretch(4, 3); | |
| 162 | + this->measurementLayout->setColumnStretch(5, 3); | |
| 163 | + for (int channel = 0; channel < this->settings->scope.voltage.count(); | |
| 164 | + ++channel) { | |
| 165 | + tablePalette.setColor(QPalette::WindowText, | |
| 166 | + this->settings->view.color.screen.voltage[channel]); | |
| 167 | + this->measurementNameLabel.append( | |
| 168 | + new QLabel(this->settings->scope.voltage[channel].name)); | |
| 169 | + this->measurementNameLabel[channel]->setPalette(tablePalette); | |
| 170 | + this->measurementMiscLabel.append(new QLabel()); | |
| 171 | + this->measurementMiscLabel[channel]->setPalette(tablePalette); | |
| 172 | + this->measurementGainLabel.append(new QLabel()); | |
| 173 | + this->measurementGainLabel[channel]->setAlignment(Qt::AlignRight); | |
| 174 | + this->measurementGainLabel[channel]->setPalette(tablePalette); | |
| 175 | + tablePalette.setColor(QPalette::WindowText, | |
| 176 | + this->settings->view.color.screen.spectrum[channel]); | |
| 177 | + this->measurementMagnitudeLabel.append(new QLabel()); | |
| 178 | + this->measurementMagnitudeLabel[channel]->setAlignment(Qt::AlignRight); | |
| 179 | + this->measurementMagnitudeLabel[channel]->setPalette(tablePalette); | |
| 180 | + this->measurementAmplitudeLabel.append(new QLabel()); | |
| 181 | + this->measurementAmplitudeLabel[channel]->setAlignment(Qt::AlignRight); | |
| 182 | + this->measurementAmplitudeLabel[channel]->setPalette(palette); | |
| 183 | + this->measurementFrequencyLabel.append(new QLabel()); | |
| 184 | + this->measurementFrequencyLabel[channel]->setAlignment(Qt::AlignRight); | |
| 185 | + this->measurementFrequencyLabel[channel]->setPalette(palette); | |
| 186 | + this->setMeasurementVisible(channel, | |
| 187 | + this->settings->scope.voltage[channel].used); | |
| 188 | + this->measurementLayout->addWidget(this->measurementNameLabel[channel], | |
| 189 | + channel, 0); | |
| 190 | + this->measurementLayout->addWidget(this->measurementMiscLabel[channel], | |
| 191 | + channel, 1); | |
| 192 | + this->measurementLayout->addWidget(this->measurementGainLabel[channel], | |
| 193 | + channel, 2); | |
| 194 | + this->measurementLayout->addWidget(this->measurementMagnitudeLabel[channel], | |
| 195 | + channel, 3); | |
| 196 | + this->measurementLayout->addWidget(this->measurementAmplitudeLabel[channel], | |
| 197 | + channel, 4); | |
| 198 | + this->measurementLayout->addWidget(this->measurementFrequencyLabel[channel], | |
| 199 | + channel, 5); | |
| 200 | + if ((unsigned int)channel < this->settings->scope.physicalChannels) | |
| 201 | + this->updateVoltageCoupling(channel); | |
| 202 | + else | |
| 203 | + this->updateMathMode(); | |
| 204 | + this->updateVoltageDetails(channel); | |
| 205 | + this->updateSpectrumDetails(channel); | |
| 206 | + } | |
| 207 | + | |
| 208 | + // The layout for the widgets | |
| 209 | + this->mainLayout = new QGridLayout(); | |
| 210 | + this->mainLayout->setColumnStretch(2, 1); // Scopes increase their size | |
| 211 | + this->mainLayout->setRowStretch(3, 1); | |
| 212 | + // Bars around the scope, needed because the slider-drawing-area is outside | |
| 213 | + // the scope at min/max | |
| 214 | + this->mainLayout->setColumnMinimumWidth( | |
| 215 | + 1, this->triggerPositionSlider->preMargin()); | |
| 216 | + this->mainLayout->setColumnMinimumWidth( | |
| 217 | + 3, this->triggerPositionSlider->postMargin()); | |
| 218 | + this->mainLayout->setRowMinimumHeight(2, this->offsetSlider->preMargin()); | |
| 219 | + this->mainLayout->setRowMinimumHeight(4, this->offsetSlider->postMargin()); | |
| 220 | + this->mainLayout->setRowMinimumHeight(6, 4); | |
| 221 | + this->mainLayout->setRowMinimumHeight(8, 4); | |
| 222 | + this->mainLayout->setRowMinimumHeight(10, 8); | |
| 223 | + this->mainLayout->setSpacing(0); | |
| 224 | + this->mainLayout->addLayout(this->settingsLayout, 0, 0, 1, 5); | |
| 225 | + this->mainLayout->addWidget(this->mainScope, 3, 2); | |
| 226 | + this->mainLayout->addWidget(this->offsetSlider, 2, 0, 3, 2, Qt::AlignRight); | |
| 227 | + this->mainLayout->addWidget(this->triggerPositionSlider, 1, 1, 2, 3, | |
| 228 | + Qt::AlignBottom); | |
| 229 | + this->mainLayout->addWidget(this->triggerLevelSlider, 2, 3, 3, 2, | |
| 230 | + Qt::AlignLeft); | |
| 231 | + this->mainLayout->addWidget(this->markerSlider, 4, 1, 2, 3, Qt::AlignTop); | |
| 232 | + this->mainLayout->addLayout(this->markerLayout, 7, 0, 1, 5); | |
| 233 | + this->mainLayout->addWidget(this->zoomScope, 9, 2); | |
| 234 | + this->mainLayout->addLayout(this->measurementLayout, 11, 0, 1, 5); | |
| 235 | + | |
| 236 | + // Apply settings and update measured values | |
| 237 | + this->updateTriggerDetails(); | |
| 238 | + this->updateRecordLength(this->settings->scope.horizontal.recordLength); | |
| 239 | + this->updateFrequencybase(this->settings->scope.horizontal.frequencybase); | |
| 240 | + this->updateSamplerate(this->settings->scope.horizontal.samplerate); | |
| 241 | + this->updateTimebase(this->settings->scope.horizontal.timebase); | |
| 242 | + this->updateZoom(this->settings->view.zoom); | |
| 243 | + | |
| 244 | + // The widget itself | |
| 245 | + this->setPalette(palette); | |
| 246 | + this->setBackgroundRole(QPalette::Background); | |
| 247 | + this->setAutoFillBackground(true); | |
| 248 | + this->setLayout(this->mainLayout); | |
| 249 | + | |
| 250 | + // Connect change-signals of sliders | |
| 251 | + this->connect(this->offsetSlider, SIGNAL(valueChanged(int, double)), this, | |
| 252 | + SLOT(updateOffset(int, double))); | |
| 253 | + this->connect(this->triggerPositionSlider, SIGNAL(valueChanged(int, double)), | |
| 254 | + this, SLOT(updateTriggerPosition(int, double))); | |
| 255 | + this->connect(this->triggerLevelSlider, SIGNAL(valueChanged(int, double)), | |
| 256 | + this, SLOT(updateTriggerLevel(int, double))); | |
| 257 | + this->connect(this->markerSlider, SIGNAL(valueChanged(int, double)), this, | |
| 258 | + SLOT(updateMarker(int, double))); | |
| 259 | + this->connect(this->markerSlider, SIGNAL(valueChanged(int, double)), | |
| 260 | + this->mainScope, SLOT(update())); | |
| 261 | + this->connect(this->markerSlider, SIGNAL(valueChanged(int, double)), | |
| 262 | + this->zoomScope, SLOT(update())); | |
| 312 | 263 | } |
| 313 | 264 | |
| 314 | -/// \brief Stops the oscilloscope thread and the timer. | |
| 315 | -DsoWidget::~DsoWidget() {} | |
| 265 | +void DsoWidget::showNewData(std::unique_ptr<DataAnalyzerResult> data) | |
| 266 | +{ | |
| 267 | + if (!data) return; | |
| 268 | + this->data = std::move(data); | |
| 269 | + emit doShowNewData(); | |
| 270 | +} | |
| 316 | 271 | |
| 317 | 272 | /// \brief Set the trigger level sliders minimum and maximum to the new values. |
| 318 | 273 | void DsoWidget::adaptTriggerLevelSlider(unsigned int channel) { |
| 319 | - this->triggerLevelSlider->setLimits( | |
| 320 | - channel, | |
| 321 | - (-DIVS_VOLTAGE / 2 - this->settings->scope.voltage[channel].offset) * | |
| 322 | - this->settings->scope.voltage[channel].gain, | |
| 323 | - (DIVS_VOLTAGE / 2 - this->settings->scope.voltage[channel].offset) * | |
| 324 | - this->settings->scope.voltage[channel].gain); | |
| 325 | - this->triggerLevelSlider->setStep( | |
| 326 | - channel, this->settings->scope.voltage[channel].gain * 0.2); | |
| 274 | + this->triggerLevelSlider->setLimits( | |
| 275 | + channel, | |
| 276 | + (-DIVS_VOLTAGE / 2 - this->settings->scope.voltage[channel].offset) * | |
| 277 | + this->settings->scope.voltage[channel].gain, | |
| 278 | + (DIVS_VOLTAGE / 2 - this->settings->scope.voltage[channel].offset) * | |
| 279 | + this->settings->scope.voltage[channel].gain); | |
| 280 | + this->triggerLevelSlider->setStep( | |
| 281 | + channel, this->settings->scope.voltage[channel].gain * 0.2); | |
| 327 | 282 | } |
| 328 | 283 | |
| 329 | 284 | /// \brief Show/Hide a line of the measurement table. |
| 330 | 285 | void DsoWidget::setMeasurementVisible(unsigned int channel, bool visible) { |
| 331 | - this->measurementNameLabel[channel]->setVisible(visible); | |
| 332 | - this->measurementMiscLabel[channel]->setVisible(visible); | |
| 333 | - this->measurementGainLabel[channel]->setVisible(visible); | |
| 334 | - this->measurementMagnitudeLabel[channel]->setVisible(visible); | |
| 335 | - this->measurementAmplitudeLabel[channel]->setVisible(visible); | |
| 336 | - this->measurementFrequencyLabel[channel]->setVisible(visible); | |
| 337 | - if (!visible) { | |
| 338 | - this->measurementGainLabel[channel]->setText(QString()); | |
| 339 | - this->measurementMagnitudeLabel[channel]->setText(QString()); | |
| 340 | - this->measurementAmplitudeLabel[channel]->setText(QString()); | |
| 341 | - this->measurementFrequencyLabel[channel]->setText(QString()); | |
| 342 | - } | |
| 286 | + this->measurementNameLabel[channel]->setVisible(visible); | |
| 287 | + this->measurementMiscLabel[channel]->setVisible(visible); | |
| 288 | + this->measurementGainLabel[channel]->setVisible(visible); | |
| 289 | + this->measurementMagnitudeLabel[channel]->setVisible(visible); | |
| 290 | + this->measurementAmplitudeLabel[channel]->setVisible(visible); | |
| 291 | + this->measurementFrequencyLabel[channel]->setVisible(visible); | |
| 292 | + if (!visible) { | |
| 293 | + this->measurementGainLabel[channel]->setText(QString()); | |
| 294 | + this->measurementMagnitudeLabel[channel]->setText(QString()); | |
| 295 | + this->measurementAmplitudeLabel[channel]->setText(QString()); | |
| 296 | + this->measurementFrequencyLabel[channel]->setText(QString()); | |
| 297 | + } | |
| 343 | 298 | } |
| 344 | 299 | |
| 345 | 300 | /// \brief Update the label about the marker measurements |
| 346 | 301 | void DsoWidget::updateMarkerDetails() { |
| 347 | - double divs = fabs(this->settings->scope.horizontal.marker[1] - | |
| 348 | - this->settings->scope.horizontal.marker[0]); | |
| 349 | - double time = divs * this->settings->scope.horizontal.timebase; | |
| 350 | - | |
| 351 | - if (this->settings->view.zoom) { | |
| 352 | - this->markerInfoLabel->setText( | |
| 353 | - tr("Zoom x%L1").arg(DIVS_TIME / divs, -1, 'g', 3)); | |
| 354 | - this->markerTimebaseLabel->setText( | |
| 355 | - valueToString(time / DIVS_TIME, UNIT_SECONDS, 3) + | |
| 356 | - tr("/div")); | |
| 357 | - this->markerFrequencybaseLabel->setText( | |
| 358 | - valueToString( | |
| 359 | - divs * this->settings->scope.horizontal.frequencybase / DIVS_TIME, | |
| 360 | - UNIT_HERTZ, 4) + | |
| 361 | - tr("/div")); | |
| 362 | - } | |
| 363 | - this->markerTimeLabel->setText( | |
| 364 | - valueToString(time, UNIT_SECONDS, 4)); | |
| 365 | - this->markerFrequencyLabel->setText( | |
| 366 | - valueToString(1.0 / time, UNIT_HERTZ, 4)); | |
| 302 | + double divs = fabs(this->settings->scope.horizontal.marker[1] - | |
| 303 | + this->settings->scope.horizontal.marker[0]); | |
| 304 | + double time = divs * this->settings->scope.horizontal.timebase; | |
| 305 | + | |
| 306 | + if (this->settings->view.zoom) { | |
| 307 | + this->markerInfoLabel->setText( | |
| 308 | + tr("Zoom x%L1").arg(DIVS_TIME / divs, -1, 'g', 3)); | |
| 309 | + this->markerTimebaseLabel->setText( | |
| 310 | + valueToString(time / DIVS_TIME, UNIT_SECONDS, 3) + | |
| 311 | + tr("/div")); | |
| 312 | + this->markerFrequencybaseLabel->setText( | |
| 313 | + valueToString( | |
| 314 | + divs * this->settings->scope.horizontal.frequencybase / DIVS_TIME, | |
| 315 | + UNIT_HERTZ, 4) + | |
| 316 | + tr("/div")); | |
| 317 | + } | |
| 318 | + this->markerTimeLabel->setText( | |
| 319 | + valueToString(time, UNIT_SECONDS, 4)); | |
| 320 | + this->markerFrequencyLabel->setText( | |
| 321 | + valueToString(1.0 / time, UNIT_HERTZ, 4)); | |
| 367 | 322 | } |
| 368 | 323 | |
| 369 | 324 | /// \brief Update the label about the trigger settings |
| 370 | 325 | void DsoWidget::updateSpectrumDetails(unsigned int channel) { |
| 371 | - this->setMeasurementVisible(channel, | |
| 372 | - this->settings->scope.voltage[channel].used || | |
| 373 | - this->settings->scope.spectrum[channel].used); | |
| 326 | + this->setMeasurementVisible(channel, | |
| 327 | + this->settings->scope.voltage[channel].used || | |
| 328 | + this->settings->scope.spectrum[channel].used); | |
| 374 | 329 | |
| 375 | - if (this->settings->scope.spectrum[channel].used) | |
| 376 | - this->measurementMagnitudeLabel[channel]->setText( | |
| 377 | - valueToString(this->settings->scope.spectrum[channel].magnitude, | |
| 330 | + if (this->settings->scope.spectrum[channel].used) | |
| 331 | + this->measurementMagnitudeLabel[channel]->setText( | |
| 332 | + valueToString(this->settings->scope.spectrum[channel].magnitude, | |
| 378 | 333 | UNIT_DECIBEL, 3) + |
| 379 | - tr("/div")); | |
| 380 | - else | |
| 381 | - this->measurementMagnitudeLabel[channel]->setText(QString()); | |
| 334 | + tr("/div")); | |
| 335 | + else | |
| 336 | + this->measurementMagnitudeLabel[channel]->setText(QString()); | |
| 382 | 337 | } |
| 383 | 338 | |
| 384 | 339 | /// \brief Update the label about the trigger settings |
| 385 | 340 | void DsoWidget::updateTriggerDetails() { |
| 386 | - // Update the trigger details | |
| 387 | - QPalette tablePalette = this->palette(); | |
| 388 | - tablePalette.setColor(QPalette::WindowText, | |
| 389 | - this->settings->view.color.screen | |
| 390 | - .voltage[this->settings->scope.trigger.source]); | |
| 391 | - this->settingsTriggerLabel->setPalette(tablePalette); | |
| 392 | - QString levelString = valueToString( | |
| 393 | - this->settings->scope.voltage[this->settings->scope.trigger.source] | |
| 394 | - .trigger, | |
| 395 | - UNIT_VOLTS, 3); | |
| 396 | - QString pretriggerString = | |
| 397 | - tr("%L1%").arg((int)(this->settings->scope.trigger.position * 100 + 0.5)); | |
| 398 | - this->settingsTriggerLabel->setText( | |
| 399 | - tr("%1 %2 %3 %4") | |
| 400 | - .arg(this->settings->scope | |
| 401 | - .voltage[this->settings->scope.trigger.source] | |
| 402 | - .name, | |
| 403 | - Dso::slopeString(this->settings->scope.trigger.slope), | |
| 404 | - levelString, pretriggerString)); | |
| 405 | - | |
| 406 | - /// \todo This won't work for special trigger sources | |
| 341 | + // Update the trigger details | |
| 342 | + QPalette tablePalette = this->palette(); | |
| 343 | + tablePalette.setColor(QPalette::WindowText, | |
| 344 | + this->settings->view.color.screen | |
| 345 | + .voltage[this->settings->scope.trigger.source]); | |
| 346 | + this->settingsTriggerLabel->setPalette(tablePalette); | |
| 347 | + QString levelString = valueToString( | |
| 348 | + this->settings->scope.voltage[this->settings->scope.trigger.source] | |
| 349 | + .trigger, | |
| 350 | + UNIT_VOLTS, 3); | |
| 351 | + QString pretriggerString = | |
| 352 | + tr("%L1%").arg((int)(this->settings->scope.trigger.position * 100 + 0.5)); | |
| 353 | + this->settingsTriggerLabel->setText( | |
| 354 | + tr("%1 %2 %3 %4") | |
| 355 | + .arg(this->settings->scope | |
| 356 | + .voltage[this->settings->scope.trigger.source] | |
| 357 | + .name, | |
| 358 | + Dso::slopeString(this->settings->scope.trigger.slope), | |
| 359 | + levelString, pretriggerString)); | |
| 360 | + | |
| 361 | + /// \todo This won't work for special trigger sources | |
| 407 | 362 | } |
| 408 | 363 | |
| 409 | 364 | /// \brief Update the label about the trigger settings |
| 410 | 365 | void DsoWidget::updateVoltageDetails(unsigned int channel) { |
| 411 | - if (channel >= (unsigned int)this->settings->scope.voltage.count()) | |
| 412 | - return; | |
| 366 | + if (channel >= (unsigned int)this->settings->scope.voltage.count()) | |
| 367 | + return; | |
| 413 | 368 | |
| 414 | - this->setMeasurementVisible(channel, | |
| 415 | - this->settings->scope.voltage[channel].used || | |
| 416 | - this->settings->scope.spectrum[channel].used); | |
| 369 | + this->setMeasurementVisible(channel, | |
| 370 | + this->settings->scope.voltage[channel].used || | |
| 371 | + this->settings->scope.spectrum[channel].used); | |
| 417 | 372 | |
| 418 | - if (this->settings->scope.voltage[channel].used) | |
| 419 | - this->measurementGainLabel[channel]->setText( | |
| 420 | - valueToString(this->settings->scope.voltage[channel].gain, | |
| 373 | + if (this->settings->scope.voltage[channel].used) | |
| 374 | + this->measurementGainLabel[channel]->setText( | |
| 375 | + valueToString(this->settings->scope.voltage[channel].gain, | |
| 421 | 376 | UNIT_VOLTS, 3) + |
| 422 | - tr("/div")); | |
| 423 | - else | |
| 424 | - this->measurementGainLabel[channel]->setText(QString()); | |
| 377 | + tr("/div")); | |
| 378 | + else | |
| 379 | + this->measurementGainLabel[channel]->setText(QString()); | |
| 425 | 380 | } |
| 426 | 381 | |
| 427 | 382 | /// \brief Handles frequencybaseChanged signal from the horizontal dock. |
| 428 | 383 | /// \param frequencybase The frequencybase used for displaying the trace. |
| 429 | 384 | void DsoWidget::updateFrequencybase(double frequencybase) { |
| 430 | - this->settingsFrequencybaseLabel->setText( | |
| 431 | - valueToString(frequencybase, UNIT_HERTZ, 4) + tr("/div")); | |
| 385 | + this->settingsFrequencybaseLabel->setText( | |
| 386 | + valueToString(frequencybase, UNIT_HERTZ, 4) + tr("/div")); | |
| 432 | 387 | } |
| 433 | 388 | |
| 434 | 389 | /// \brief Updates the samplerate field after changing the samplerate. |
| 435 | 390 | /// \param samplerate The samplerate set in the oscilloscope. |
| 436 | 391 | void DsoWidget::updateSamplerate(double samplerate) { |
| 437 | - this->settingsSamplerateLabel->setText( | |
| 438 | - valueToString(samplerate, UNIT_SAMPLES, 4) + tr("/s")); | |
| 392 | + this->settingsSamplerateLabel->setText( | |
| 393 | + valueToString(samplerate, UNIT_SAMPLES, 4) + tr("/s")); | |
| 439 | 394 | } |
| 440 | 395 | |
| 441 | 396 | /// \brief Handles timebaseChanged signal from the horizontal dock. |
| 442 | 397 | /// \param timebase The timebase used for displaying the trace. |
| 443 | 398 | void DsoWidget::updateTimebase(double timebase) { |
| 444 | - this->settingsTimebaseLabel->setText( | |
| 445 | - valueToString(timebase, UNIT_SECONDS, 4) + tr("/div")); | |
| 399 | + this->settingsTimebaseLabel->setText( | |
| 400 | + valueToString(timebase, UNIT_SECONDS, 4) + tr("/div")); | |
| 446 | 401 | |
| 447 | - this->updateMarkerDetails(); | |
| 402 | + this->updateMarkerDetails(); | |
| 448 | 403 | } |
| 449 | 404 | |
| 450 | 405 | /// \brief Handles magnitudeChanged signal from the spectrum dock. |
| 451 | 406 | /// \param channel The channel whose magnitude was changed. |
| 452 | 407 | void DsoWidget::updateSpectrumMagnitude(unsigned int channel) { |
| 453 | - this->updateSpectrumDetails(channel); | |
| 408 | + this->updateSpectrumDetails(channel); | |
| 454 | 409 | } |
| 455 | 410 | |
| 456 | 411 | /// \brief Handles usedChanged signal from the spectrum dock. |
| 457 | 412 | /// \param channel The channel whose used-state was changed. |
| 458 | 413 | /// \param used The new used-state for the channel. |
| 459 | 414 | void DsoWidget::updateSpectrumUsed(unsigned int channel, bool used) { |
| 460 | - if (channel >= (unsigned int)this->settings->scope.voltage.count()) | |
| 461 | - return; | |
| 415 | + if (channel >= (unsigned int)this->settings->scope.voltage.count()) | |
| 416 | + return; | |
| 462 | 417 | |
| 463 | - this->offsetSlider->setVisible( | |
| 464 | - this->settings->scope.voltage.count() + channel, used); | |
| 418 | + this->offsetSlider->setVisible( | |
| 419 | + this->settings->scope.voltage.count() + channel, used); | |
| 465 | 420 | |
| 466 | - this->updateSpectrumDetails(channel); | |
| 421 | + this->updateSpectrumDetails(channel); | |
| 467 | 422 | } |
| 468 | 423 | |
| 469 | 424 | /// \brief Handles modeChanged signal from the trigger dock. |
| ... | ... | @@ -474,163 +429,150 @@ void DsoWidget::updateTriggerSlope() { this->updateTriggerDetails(); } |
| 474 | 429 | |
| 475 | 430 | /// \brief Handles sourceChanged signal from the trigger dock. |
| 476 | 431 | void DsoWidget::updateTriggerSource() { |
| 477 | - // Change the colors of the trigger sliders | |
| 478 | - if (this->settings->scope.trigger.special || | |
| 479 | - this->settings->scope.trigger.source >= | |
| 480 | - this->settings->scope.physicalChannels) | |
| 481 | - this->triggerPositionSlider->setColor( | |
| 482 | - 0, this->settings->view.color.screen.border); | |
| 483 | - else | |
| 484 | - this->triggerPositionSlider->setColor( | |
| 485 | - 0, this->settings->view.color.screen | |
| 486 | - .voltage[this->settings->scope.trigger.source]); | |
| 487 | - | |
| 488 | - for (int channel = 0; channel < (int)this->settings->scope.physicalChannels; | |
| 489 | - ++channel) | |
| 490 | - this->triggerLevelSlider->setColor( | |
| 491 | - channel, | |
| 492 | - (!this->settings->scope.trigger.special && | |
| 493 | - channel == (int)this->settings->scope.trigger.source) | |
| 494 | - ? this->settings->view.color.screen.voltage[channel] | |
| 495 | - : this->settings->view.color.screen.voltage[channel].darker()); | |
| 496 | - | |
| 497 | - this->updateTriggerDetails(); | |
| 432 | + // Change the colors of the trigger sliders | |
| 433 | + if (this->settings->scope.trigger.special || | |
| 434 | + this->settings->scope.trigger.source >= | |
| 435 | + this->settings->scope.physicalChannels) | |
| 436 | + this->triggerPositionSlider->setColor( | |
| 437 | + 0, this->settings->view.color.screen.border); | |
| 438 | + else | |
| 439 | + this->triggerPositionSlider->setColor( | |
| 440 | + 0, this->settings->view.color.screen | |
| 441 | + .voltage[this->settings->scope.trigger.source]); | |
| 442 | + | |
| 443 | + for (int channel = 0; channel < (int)this->settings->scope.physicalChannels; | |
| 444 | + ++channel) | |
| 445 | + this->triggerLevelSlider->setColor( | |
| 446 | + channel, | |
| 447 | + (!this->settings->scope.trigger.special && | |
| 448 | + channel == (int)this->settings->scope.trigger.source) | |
| 449 | + ? this->settings->view.color.screen.voltage[channel] | |
| 450 | + : this->settings->view.color.screen.voltage[channel].darker()); | |
| 451 | + | |
| 452 | + this->updateTriggerDetails(); | |
| 498 | 453 | } |
| 499 | 454 | |
| 500 | 455 | /// \brief Handles couplingChanged signal from the voltage dock. |
| 501 | 456 | /// \param channel The channel whose coupling was changed. |
| 502 | 457 | void DsoWidget::updateVoltageCoupling(unsigned int channel) { |
| 503 | - if (channel >= (unsigned int)this->settings->scope.voltage.count()) | |
| 504 | - return; | |
| 458 | + if (channel >= (unsigned int)this->settings->scope.voltage.count()) | |
| 459 | + return; | |
| 505 | 460 | |
| 506 | - this->measurementMiscLabel[channel]->setText(Dso::couplingString( | |
| 507 | - (Dso::Coupling)this->settings->scope.voltage[channel].misc)); | |
| 461 | + this->measurementMiscLabel[channel]->setText(Dso::couplingString( | |
| 462 | + (Dso::Coupling)this->settings->scope.voltage[channel].misc)); | |
| 508 | 463 | } |
| 509 | 464 | |
| 510 | 465 | /// \brief Handles modeChanged signal from the voltage dock. |
| 511 | 466 | void DsoWidget::updateMathMode() { |
| 512 | - this->measurementMiscLabel[this->settings->scope.physicalChannels]->setText( | |
| 513 | - Dso::mathModeString((Dso::MathMode)this->settings->scope | |
| 514 | - .voltage[this->settings->scope.physicalChannels] | |
| 515 | - .misc)); | |
| 467 | + this->measurementMiscLabel[this->settings->scope.physicalChannels]->setText( | |
| 468 | + Dso::mathModeString((Dso::MathMode)this->settings->scope | |
| 469 | + .voltage[this->settings->scope.physicalChannels] | |
| 470 | + .misc)); | |
| 516 | 471 | } |
| 517 | 472 | |
| 518 | 473 | /// \brief Handles gainChanged signal from the voltage dock. |
| 519 | 474 | /// \param channel The channel whose gain was changed. |
| 520 | 475 | void DsoWidget::updateVoltageGain(unsigned int channel) { |
| 521 | - if (channel >= (unsigned int)this->settings->scope.voltage.count()) | |
| 522 | - return; | |
| 476 | + if (channel >= (unsigned int)this->settings->scope.voltage.count()) | |
| 477 | + return; | |
| 523 | 478 | |
| 524 | - if (channel < this->settings->scope.physicalChannels) | |
| 525 | - this->adaptTriggerLevelSlider(channel); | |
| 479 | + if (channel < this->settings->scope.physicalChannels) | |
| 480 | + this->adaptTriggerLevelSlider(channel); | |
| 526 | 481 | |
| 527 | - this->updateVoltageDetails(channel); | |
| 482 | + this->updateVoltageDetails(channel); | |
| 528 | 483 | } |
| 529 | 484 | |
| 530 | 485 | /// \brief Handles usedChanged signal from the voltage dock. |
| 531 | 486 | /// \param channel The channel whose used-state was changed. |
| 532 | 487 | /// \param used The new used-state for the channel. |
| 533 | 488 | void DsoWidget::updateVoltageUsed(unsigned int channel, bool used) { |
| 534 | - if (channel >= (unsigned int)this->settings->scope.voltage.count()) | |
| 535 | - return; | |
| 489 | + if (channel >= (unsigned int)this->settings->scope.voltage.count()) | |
| 490 | + return; | |
| 536 | 491 | |
| 537 | - this->offsetSlider->setVisible(channel, used); | |
| 538 | - this->triggerLevelSlider->setVisible(channel, used); | |
| 539 | - this->setMeasurementVisible(channel, | |
| 540 | - this->settings->scope.voltage[channel].used); | |
| 492 | + this->offsetSlider->setVisible(channel, used); | |
| 493 | + this->triggerLevelSlider->setVisible(channel, used); | |
| 494 | + this->setMeasurementVisible(channel, | |
| 495 | + this->settings->scope.voltage[channel].used); | |
| 541 | 496 | |
| 542 | - this->updateVoltageDetails(channel); | |
| 497 | + this->updateVoltageDetails(channel); | |
| 543 | 498 | } |
| 544 | 499 | |
| 545 | 500 | /// \brief Change the record length. |
| 546 | 501 | void DsoWidget::updateRecordLength(unsigned long size) { |
| 547 | - this->settingsRecordLengthLabel->setText( | |
| 548 | - valueToString(size, UNIT_SAMPLES, 4)); | |
| 502 | + this->settingsRecordLengthLabel->setText( | |
| 503 | + valueToString(size, UNIT_SAMPLES, 4)); | |
| 549 | 504 | } |
| 550 | 505 | |
| 551 | 506 | /// \brief Export the oscilloscope screen to a file. |
| 552 | 507 | /// \return true if the document was exported successfully. |
| 553 | -bool DsoWidget::exportAs() { | |
| 554 | - QStringList filters; | |
| 555 | - filters << tr("Portable Document Format (*.pdf)") | |
| 556 | - << tr("Image (*.png *.xpm *.jpg)") | |
| 557 | - << tr("Comma-Separated Values (*.csv)"); | |
| 558 | - | |
| 559 | - QFileDialog fileDialog(static_cast<QWidget *>(this->parent()), | |
| 560 | - tr("Export file..."), QString(), filters.join(";;")); | |
| 561 | - fileDialog.setFileMode(QFileDialog::AnyFile); | |
| 562 | - fileDialog.setAcceptMode(QFileDialog::AcceptSave); | |
| 563 | - if (fileDialog.exec() != QDialog::Accepted) | |
| 564 | - return false; | |
| 565 | - | |
| 566 | - Exporter exporter(this->settings, this->dataAnalyzer, | |
| 567 | - static_cast<QWidget *>(this->parent())); | |
| 568 | - exporter.setFilename(fileDialog.selectedFiles().first()); | |
| 569 | - exporter.setFormat((ExportFormat)( | |
| 570 | - EXPORT_FORMAT_PDF + filters.indexOf(fileDialog.selectedNameFilter()))); | |
| 571 | - | |
| 572 | - return exporter.doExport(); | |
| 508 | +void DsoWidget::exportAs() { | |
| 509 | + exportNextFrame.reset(Exporter::createSaveToFileExporter(settings)); | |
| 573 | 510 | } |
| 574 | 511 | |
| 575 | 512 | /// \brief Print the oscilloscope screen. |
| 576 | 513 | /// \return true if the document was sent to the printer successfully. |
| 577 | -bool DsoWidget::print() { | |
| 578 | - Exporter exporter(this->settings, this->dataAnalyzer, | |
| 579 | - static_cast<QWidget *>(this->parent())); | |
| 580 | - exporter.setFormat(EXPORT_FORMAT_PRINTER); | |
| 581 | - | |
| 582 | - return exporter.doExport(); | |
| 514 | +void DsoWidget::print() { | |
| 515 | + exportNextFrame.reset(Exporter::createPrintExporter(settings)); | |
| 583 | 516 | } |
| 584 | 517 | |
| 585 | 518 | /// \brief Stop the oscilloscope. |
| 586 | 519 | void DsoWidget::updateZoom(bool enabled) { |
| 587 | - this->mainLayout->setRowStretch(9, enabled ? 1 : 0); | |
| 588 | - this->zoomScope->setVisible(enabled); | |
| 589 | - | |
| 590 | - // Show time-/frequencybase and zoom factor if the magnified scope is shown | |
| 591 | - this->markerLayout->setStretch(3, enabled ? 1 : 0); | |
| 592 | - this->markerTimebaseLabel->setVisible(enabled); | |
| 593 | - this->markerLayout->setStretch(4, enabled ? 1 : 0); | |
| 594 | - this->markerFrequencybaseLabel->setVisible(enabled); | |
| 595 | - if (enabled) | |
| 596 | - this->updateMarkerDetails(); | |
| 597 | - else | |
| 598 | - this->markerInfoLabel->setText(tr("Marker 1/2")); | |
| 520 | + this->mainLayout->setRowStretch(9, enabled ? 1 : 0); | |
| 521 | + this->zoomScope->setVisible(enabled); | |
| 522 | + | |
| 523 | + // Show time-/frequencybase and zoom factor if the magnified scope is shown | |
| 524 | + this->markerLayout->setStretch(3, enabled ? 1 : 0); | |
| 525 | + this->markerTimebaseLabel->setVisible(enabled); | |
| 526 | + this->markerLayout->setStretch(4, enabled ? 1 : 0); | |
| 527 | + this->markerFrequencybaseLabel->setVisible(enabled); | |
| 528 | + if (enabled) | |
| 529 | + this->updateMarkerDetails(); | |
| 530 | + else | |
| 531 | + this->markerInfoLabel->setText(tr("Marker 1/2")); | |
| 599 | 532 | |
| 600 | - this->repaint(); | |
| 533 | + this->repaint(); | |
| 601 | 534 | } |
| 602 | 535 | |
| 603 | 536 | /// \brief Prints analyzed data. |
| 604 | -void DsoWidget::dataAnalyzed() { | |
| 605 | - for (int channel = 0; channel < this->settings->scope.voltage.count(); | |
| 606 | - ++channel) { | |
| 607 | - if (this->settings->scope.voltage[channel].used && | |
| 608 | - this->dataAnalyzer->data(channel)) { | |
| 609 | - // Amplitude string representation (4 significant digits) | |
| 610 | - this->measurementAmplitudeLabel[channel]->setText(valueToString( | |
| 611 | - this->dataAnalyzer->data(channel)->amplitude, UNIT_VOLTS, 4)); | |
| 612 | - // Frequency string representation (5 significant digits) | |
| 613 | - this->measurementFrequencyLabel[channel]->setText(valueToString( | |
| 614 | - this->dataAnalyzer->data(channel)->frequency, UNIT_HERTZ, 5)); | |
| 537 | +void DsoWidget::doShowNewData() { | |
| 538 | + if (exportNextFrame){ | |
| 539 | + exportNextFrame->exportSamples(data.get()); | |
| 540 | + exportNextFrame.reset(nullptr); | |
| 541 | + } | |
| 542 | + | |
| 543 | + generator->generateGraphs(data.get()); | |
| 544 | + | |
| 545 | + updateRecordLength(data.get()->getMaxSamples()); | |
| 546 | + | |
| 547 | + for (int channel = 0; channel < this->settings->scope.voltage.count(); | |
| 548 | + ++channel) { | |
| 549 | + if (this->settings->scope.voltage[channel].used && | |
| 550 | + data.get()->data(channel)) { | |
| 551 | + // Amplitude string representation (4 significant digits) | |
| 552 | + this->measurementAmplitudeLabel[channel]->setText(valueToString( | |
| 553 | + data.get()->data(channel)->amplitude, UNIT_VOLTS, 4)); | |
| 554 | + // Frequency string representation (5 significant digits) | |
| 555 | + this->measurementFrequencyLabel[channel]->setText(valueToString( | |
| 556 | + data.get()->data(channel)->frequency, UNIT_HERTZ, 5)); | |
| 557 | + } | |
| 615 | 558 | } |
| 616 | - } | |
| 617 | 559 | } |
| 618 | 560 | |
| 619 | 561 | /// \brief Handles valueChanged signal from the offset sliders. |
| 620 | 562 | /// \param channel The channel whose offset was changed. |
| 621 | 563 | /// \param value The new offset for the channel. |
| 622 | 564 | void DsoWidget::updateOffset(int channel, double value) { |
| 623 | - if (channel < this->settings->scope.voltage.count()) { | |
| 624 | - this->settings->scope.voltage[channel].offset = value; | |
| 565 | + if (channel < this->settings->scope.voltage.count()) { | |
| 566 | + this->settings->scope.voltage[channel].offset = value; | |
| 625 | 567 | |
| 626 | - if (channel < (int)this->settings->scope.physicalChannels) | |
| 627 | - this->adaptTriggerLevelSlider(channel); | |
| 628 | - } else if (channel < this->settings->scope.voltage.count() * 2) | |
| 629 | - this->settings->scope | |
| 630 | - .spectrum[channel - this->settings->scope.voltage.count()] | |
| 631 | - .offset = value; | |
| 568 | + if (channel < (int)this->settings->scope.physicalChannels) | |
| 569 | + this->adaptTriggerLevelSlider(channel); | |
| 570 | + } else if (channel < this->settings->scope.voltage.count() * 2) | |
| 571 | + this->settings->scope | |
| 572 | + .spectrum[channel - this->settings->scope.voltage.count()] | |
| 573 | + .offset = value; | |
| 632 | 574 | |
| 633 | - emit offsetChanged(channel, value); | |
| 575 | + emit offsetChanged(channel, value); | |
| 634 | 576 | } |
| 635 | 577 | |
| 636 | 578 | /// \brief Handles valueChanged signal from the triggerPosition slider. |
| ... | ... | @@ -638,35 +580,35 @@ void DsoWidget::updateOffset(int channel, double value) { |
| 638 | 580 | /// \param value The new triggerPosition in seconds relative to the first |
| 639 | 581 | /// sample. |
| 640 | 582 | void DsoWidget::updateTriggerPosition(int index, double value) { |
| 641 | - if (index != 0) | |
| 642 | - return; | |
| 583 | + if (index != 0) | |
| 584 | + return; | |
| 643 | 585 | |
| 644 | - this->settings->scope.trigger.position = value; | |
| 586 | + this->settings->scope.trigger.position = value; | |
| 645 | 587 | |
| 646 | - this->updateTriggerDetails(); | |
| 588 | + this->updateTriggerDetails(); | |
| 647 | 589 | |
| 648 | - emit triggerPositionChanged( | |
| 649 | - value * this->settings->scope.horizontal.timebase * DIVS_TIME); | |
| 590 | + emit triggerPositionChanged( | |
| 591 | + value * this->settings->scope.horizontal.timebase * DIVS_TIME); | |
| 650 | 592 | } |
| 651 | 593 | |
| 652 | 594 | /// \brief Handles valueChanged signal from the trigger level slider. |
| 653 | 595 | /// \param channel The index of the slider. |
| 654 | 596 | /// \param value The new trigger level. |
| 655 | 597 | void DsoWidget::updateTriggerLevel(int channel, double value) { |
| 656 | - this->settings->scope.voltage[channel].trigger = value; | |
| 598 | + this->settings->scope.voltage[channel].trigger = value; | |
| 657 | 599 | |
| 658 | - this->updateTriggerDetails(); | |
| 600 | + this->updateTriggerDetails(); | |
| 659 | 601 | |
| 660 | - emit triggerLevelChanged(channel, value); | |
| 602 | + emit triggerLevelChanged(channel, value); | |
| 661 | 603 | } |
| 662 | 604 | |
| 663 | 605 | /// \brief Handles valueChanged signal from the marker slider. |
| 664 | 606 | /// \param marker The index of the slider. |
| 665 | 607 | /// \param value The new marker position. |
| 666 | 608 | void DsoWidget::updateMarker(int marker, double value) { |
| 667 | - this->settings->scope.horizontal.marker[marker] = value; | |
| 609 | + this->settings->scope.horizontal.marker[marker] = value; | |
| 668 | 610 | |
| 669 | - this->updateMarkerDetails(); | |
| 611 | + this->updateMarkerDetails(); | |
| 670 | 612 | |
| 671 | - emit markerChanged(marker, value); | |
| 613 | + emit markerChanged(marker, value); | |
| 672 | 614 | } | ... | ... |
openhantek/src/dsowidget.h
| 1 | -//////////////////////////////////////////////////////////////////////////////// | |
| 2 | -// | |
| 3 | -// OpenHantek | |
| 4 | -/// \file dsowidget.h | |
| 5 | -/// \brief Declares the DsoWidget class. | |
| 6 | -// | |
| 7 | -// Copyright (C) 2010 Oliver Haag | |
| 8 | -// oliver.haag@gmail.com | |
| 9 | -// | |
| 10 | -// This program is free software: you can redistribute it and/or modify it | |
| 11 | -// under the terms of the GNU General Public License as published by the Free | |
| 12 | -// Software Foundation, either version 3 of the License, or (at your option) | |
| 13 | -// any later version. | |
| 14 | -// | |
| 15 | -// This program is distributed in the hope that it will be useful, but WITHOUT | |
| 16 | -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 17 | -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| 18 | -// more details. | |
| 19 | -// | |
| 20 | -// You should have received a copy of the GNU General Public License along with | |
| 21 | -// this program. If not, see <http://www.gnu.org/licenses/>. | |
| 22 | -// | |
| 23 | -//////////////////////////////////////////////////////////////////////////////// | |
| 24 | - | |
| 25 | -#ifndef DSOWIDGET_H | |
| 26 | -#define DSOWIDGET_H | |
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 2 | + | |
| 3 | +#pragma once | |
| 27 | 4 | |
| 28 | 5 | #include <QWidget> |
| 6 | +#include <memory> | |
| 29 | 7 | |
| 30 | 8 | #include "dockwindows.h" |
| 31 | 9 | #include "glscope.h" |
| 32 | 10 | #include "levelslider.h" |
| 11 | +#include "exporter.h" | |
| 33 | 12 | |
| 34 | 13 | class DataAnalyzer; |
| 35 | 14 | class DsoSettings; |
| 36 | 15 | class QGridLayout; |
| 37 | 16 | |
| 38 | -//////////////////////////////////////////////////////////////////////////////// | |
| 39 | -/// \class DsoWidget dsowidget.h | |
| 17 | +/// \class DsoWidget | |
| 40 | 18 | /// \brief The widget for the oszilloscope-screen |
| 41 | 19 | /// This widget contains the scopes and all level sliders. |
| 42 | 20 | class DsoWidget : public QWidget { |
| 43 | - Q_OBJECT | |
| 21 | + Q_OBJECT | |
| 44 | 22 | |
| 45 | 23 | public: |
| 46 | - DsoWidget(DsoSettings *settings, DataAnalyzer *dataAnalyzer, | |
| 47 | - QWidget *parent = 0, Qt::WindowFlags flags = 0); | |
| 48 | - ~DsoWidget(); | |
| 49 | - | |
| 24 | + /// \brief Initializes the components of the oszilloscope-screen. | |
| 25 | + /// \param settings The settings object containing the oscilloscope settings. | |
| 26 | + /// \param dataAnalyzer The data analyzer that should be used as data source. | |
| 27 | + /// \param parent The parent widget. | |
| 28 | + /// \param flags Flags for the window manager. | |
| 29 | + DsoWidget(DsoSettings *settings, QWidget *parent = 0, Qt::WindowFlags flags = 0); | |
| 30 | + void showNewData(std::unique_ptr<DataAnalyzerResult> data); | |
| 50 | 31 | protected: |
| 51 | - void adaptTriggerLevelSlider(unsigned int channel); | |
| 52 | - void setMeasurementVisible(unsigned int channel, bool visible); | |
| 53 | - void updateMarkerDetails(); | |
| 54 | - void updateSpectrumDetails(unsigned int channel); | |
| 55 | - void updateTriggerDetails(); | |
| 56 | - void updateVoltageDetails(unsigned int channel); | |
| 57 | - | |
| 58 | - QGridLayout *mainLayout; ///< The main layout for this widget | |
| 59 | - GlGenerator *generator; ///< The generator for the OpenGL vertex arrays | |
| 60 | - GlScope *mainScope; ///< The main scope screen | |
| 61 | - GlScope *zoomScope; ///< The optional magnified scope screen | |
| 62 | - LevelSlider *offsetSlider; ///< The sliders for the graph offsets | |
| 63 | - LevelSlider *triggerPositionSlider; ///< The slider for the pretrigger | |
| 64 | - LevelSlider *triggerLevelSlider; ///< The sliders for the trigger level | |
| 65 | - LevelSlider *markerSlider; ///< The sliders for the markers | |
| 66 | - | |
| 67 | - QHBoxLayout *settingsLayout; ///< The table for the settings info | |
| 68 | - QLabel *settingsTriggerLabel; ///< The trigger details | |
| 69 | - QLabel *settingsRecordLengthLabel; ///< The record length | |
| 70 | - QLabel *settingsSamplerateLabel; ///< The samplerate | |
| 71 | - QLabel *settingsTimebaseLabel; ///< The timebase of the main scope | |
| 72 | - QLabel *settingsFrequencybaseLabel; ///< The frequencybase of the main scope | |
| 73 | - | |
| 74 | - QHBoxLayout *markerLayout; ///< The table for the marker details | |
| 75 | - QLabel *markerInfoLabel; ///< The info about the zoom factor | |
| 76 | - QLabel *markerTimeLabel; ///< The time period between the markers | |
| 77 | - QLabel *markerFrequencyLabel; ///< The frequency for the time period | |
| 78 | - QLabel *markerTimebaseLabel; ///< The timebase for the zoomed scope | |
| 79 | - QLabel *markerFrequencybaseLabel; ///< The frequencybase for the zoomed scope | |
| 80 | - | |
| 81 | - QGridLayout *measurementLayout; ///< The table for the signal details | |
| 82 | - QList<QLabel *> measurementNameLabel; ///< The name of the channel | |
| 83 | - QList<QLabel *> measurementGainLabel; ///< The gain for the voltage (V/div) | |
| 84 | - QList<QLabel *> | |
| 85 | - measurementMagnitudeLabel; ///< The magnitude for the spectrum (dB/div) | |
| 86 | - QList<QLabel *> measurementMiscLabel; ///< Coupling or math mode | |
| 87 | - QList<QLabel *> measurementAmplitudeLabel; ///< Amplitude of the signal (V) | |
| 88 | - QList<QLabel *> measurementFrequencyLabel; ///< Frequency of the signal (Hz) | |
| 89 | - | |
| 90 | - DsoSettings *settings; ///< The settings provided by the main window | |
| 91 | - | |
| 92 | - DataAnalyzer *dataAnalyzer; ///< The data source provided by the main window | |
| 93 | - | |
| 32 | + void adaptTriggerLevelSlider(unsigned int channel); | |
| 33 | + void setMeasurementVisible(unsigned int channel, bool visible); | |
| 34 | + void updateMarkerDetails(); | |
| 35 | + void updateSpectrumDetails(unsigned int channel); | |
| 36 | + void updateTriggerDetails(); | |
| 37 | + void updateVoltageDetails(unsigned int channel); | |
| 38 | + | |
| 39 | + QGridLayout *mainLayout; ///< The main layout for this widget | |
| 40 | + LevelSlider *offsetSlider; ///< The sliders for the graph offsets | |
| 41 | + LevelSlider *triggerPositionSlider; ///< The slider for the pretrigger | |
| 42 | + LevelSlider *triggerLevelSlider; ///< The sliders for the trigger level | |
| 43 | + LevelSlider *markerSlider; ///< The sliders for the markers | |
| 44 | + | |
| 45 | + QHBoxLayout *settingsLayout; ///< The table for the settings info | |
| 46 | + QLabel *settingsTriggerLabel; ///< The trigger details | |
| 47 | + QLabel *settingsRecordLengthLabel; ///< The record length | |
| 48 | + QLabel *settingsSamplerateLabel; ///< The samplerate | |
| 49 | + QLabel *settingsTimebaseLabel; ///< The timebase of the main scope | |
| 50 | + QLabel *settingsFrequencybaseLabel; ///< The frequencybase of the main scope | |
| 51 | + | |
| 52 | + QHBoxLayout *markerLayout; ///< The table for the marker details | |
| 53 | + QLabel *markerInfoLabel; ///< The info about the zoom factor | |
| 54 | + QLabel *markerTimeLabel; ///< The time period between the markers | |
| 55 | + QLabel *markerFrequencyLabel; ///< The frequency for the time period | |
| 56 | + QLabel *markerTimebaseLabel; ///< The timebase for the zoomed scope | |
| 57 | + QLabel *markerFrequencybaseLabel; ///< The frequencybase for the zoomed scope | |
| 58 | + | |
| 59 | + QGridLayout *measurementLayout; ///< The table for the signal details | |
| 60 | + QList<QLabel *> measurementNameLabel; ///< The name of the channel | |
| 61 | + QList<QLabel *> measurementGainLabel; ///< The gain for the voltage (V/div) | |
| 62 | + QList<QLabel *> | |
| 63 | + measurementMagnitudeLabel; ///< The magnitude for the spectrum (dB/div) | |
| 64 | + QList<QLabel *> measurementMiscLabel; ///< Coupling or math mode | |
| 65 | + QList<QLabel *> measurementAmplitudeLabel; ///< Amplitude of the signal (V) | |
| 66 | + QList<QLabel *> measurementFrequencyLabel; ///< Frequency of the signal (Hz) | |
| 67 | + | |
| 68 | + DsoSettings *settings; ///< The settings provided by the main window | |
| 69 | + GlGenerator *generator; ///< The generator for the OpenGL vertex arrays | |
| 70 | + GlScope *mainScope; ///< The main scope screen | |
| 71 | + GlScope *zoomScope; ///< The optional magnified scope screen | |
| 72 | + std::unique_ptr<Exporter> exportNextFrame; | |
| 73 | + std::unique_ptr<DataAnalyzerResult> data; | |
| 94 | 74 | public slots: |
| 95 | - // Horizontal axis | |
| 96 | - // void horizontalFormatChanged(HorizontalFormat format); | |
| 97 | - void updateFrequencybase(double frequencybase); | |
| 98 | - void updateSamplerate(double samplerate); | |
| 99 | - void updateTimebase(double timebase); | |
| 100 | - | |
| 101 | - // Trigger | |
| 102 | - void updateTriggerMode(); | |
| 103 | - void updateTriggerSlope(); | |
| 104 | - void updateTriggerSource(); | |
| 105 | - | |
| 106 | - // Spectrum | |
| 107 | - void updateSpectrumMagnitude(unsigned int channel); | |
| 108 | - void updateSpectrumUsed(unsigned int channel, bool used); | |
| 109 | - | |
| 110 | - // Vertical axis | |
| 111 | - void updateVoltageCoupling(unsigned int channel); | |
| 112 | - void updateMathMode(); | |
| 113 | - void updateVoltageGain(unsigned int channel); | |
| 114 | - void updateVoltageUsed(unsigned int channel, bool used); | |
| 115 | - | |
| 116 | - // Menus | |
| 117 | - void updateRecordLength(unsigned long size); | |
| 118 | - | |
| 119 | - // Export | |
| 120 | - bool exportAs(); | |
| 121 | - bool print(); | |
| 122 | - | |
| 123 | - // Scope control | |
| 124 | - void updateZoom(bool enabled); | |
| 125 | - | |
| 126 | - // Data analyzer | |
| 127 | - void dataAnalyzed(); | |
| 128 | - | |
| 129 | -protected slots: | |
| 130 | - // Sliders | |
| 131 | - void updateOffset(int channel, double value); | |
| 132 | - void updateTriggerPosition(int index, double value); | |
| 133 | - void updateTriggerLevel(int channel, double value); | |
| 134 | - void updateMarker(int marker, double value); | |
| 75 | + // Horizontal axis | |
| 76 | + // void horizontalFormatChanged(HorizontalFormat format); | |
| 77 | + void updateFrequencybase(double frequencybase); | |
| 78 | + void updateSamplerate(double samplerate); | |
| 79 | + void updateTimebase(double timebase); | |
| 80 | + | |
| 81 | + // Trigger | |
| 82 | + void updateTriggerMode(); | |
| 83 | + void updateTriggerSlope(); | |
| 84 | + void updateTriggerSource(); | |
| 85 | + | |
| 86 | + // Spectrum | |
| 87 | + void updateSpectrumMagnitude(unsigned int channel); | |
| 88 | + void updateSpectrumUsed(unsigned int channel, bool used); | |
| 89 | + | |
| 90 | + // Vertical axis | |
| 91 | + void updateVoltageCoupling(unsigned int channel); | |
| 92 | + void updateMathMode(); | |
| 93 | + void updateVoltageGain(unsigned int channel); | |
| 94 | + void updateVoltageUsed(unsigned int channel, bool used); | |
| 95 | + | |
| 96 | + // Menus | |
| 97 | + void updateRecordLength(unsigned long size); | |
| 98 | + | |
| 99 | + // Export | |
| 100 | + void exportAs(); | |
| 101 | + void print(); | |
| 102 | + | |
| 103 | + // Scope control | |
| 104 | + void updateZoom(bool enabled); | |
| 105 | + | |
| 106 | + // Data analyzer | |
| 107 | + void doShowNewData(); | |
| 108 | + | |
| 109 | +private slots: | |
| 110 | + // Sliders | |
| 111 | + void updateOffset(int channel, double value); | |
| 112 | + void updateTriggerPosition(int index, double value); | |
| 113 | + void updateTriggerLevel(int channel, double value); | |
| 114 | + void updateMarker(int marker, double value); | |
| 135 | 115 | |
| 136 | 116 | signals: |
| 137 | - // Sliders | |
| 138 | - void offsetChanged(unsigned int channel, | |
| 139 | - double value); ///< A graph offset has been changed | |
| 140 | - void | |
| 141 | - triggerPositionChanged(double value); ///< The pretrigger has been changed | |
| 142 | - void triggerLevelChanged(unsigned int channel, | |
| 143 | - double value); ///< A trigger level has been changed | |
| 144 | - void markerChanged(unsigned int marker, | |
| 145 | - double value); ///< A marker position has been changed | |
| 117 | + // Sliders | |
| 118 | + void offsetChanged(unsigned int channel, | |
| 119 | + double value); ///< A graph offset has been changed | |
| 120 | + void | |
| 121 | + triggerPositionChanged(double value); ///< The pretrigger has been changed | |
| 122 | + void triggerLevelChanged(unsigned int channel, | |
| 123 | + double value); ///< A trigger level has been changed | |
| 124 | + void markerChanged(unsigned int marker, | |
| 125 | + double value); ///< A marker position has been changed | |
| 146 | 126 | }; |
| 147 | - | |
| 148 | -#endif | ... | ... |
openhantek/src/glgenerator.cpp
| 1 | -//////////////////////////////////////////////////////////////////////////////// | |
| 2 | -// | |
| 3 | -// OpenHantek | |
| 4 | -// glgenerator.cpp | |
| 5 | -// | |
| 6 | -// Copyright (C) 2010 Oliver Haag | |
| 7 | -// oliver.haag@gmail.com | |
| 8 | -// | |
| 9 | -// This program is free software: you can redistribute it and/or modify it | |
| 10 | -// under the terms of the GNU General Public License as published by the Free | |
| 11 | -// Software Foundation, either version 3 of the License, or (at your option) | |
| 12 | -// any later version. | |
| 13 | -// | |
| 14 | -// This program is distributed in the hope that it will be useful, but WITHOUT | |
| 15 | -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 16 | -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| 17 | -// more details. | |
| 18 | -// | |
| 19 | -// You should have received a copy of the GNU General Public License along with | |
| 20 | -// this program. If not, see <http://www.gnu.org/licenses/>. | |
| 21 | -// | |
| 22 | -//////////////////////////////////////////////////////////////////////////////// | |
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 23 | 2 | |
| 24 | 3 | #include <QMutex> |
| 25 | 4 | |
| ... | ... | @@ -28,400 +7,353 @@ |
| 28 | 7 | #include "dataanalyzer.h" |
| 29 | 8 | #include "settings.h" |
| 30 | 9 | |
| 31 | -//////////////////////////////////////////////////////////////////////////////// | |
| 32 | -// class GlGenerator | |
| 33 | -/// \brief Initializes the scope widget. | |
| 34 | -/// \param settings The target settings object. | |
| 35 | -/// \param parent The parent widget. | |
| 36 | -GlGenerator::GlGenerator(DsoSettings *settings, QObject *parent) | |
| 37 | - : QObject(parent) { | |
| 38 | - this->settings = settings; | |
| 10 | +GlGenerator::GlGenerator(DsoSettingsScope *scope, DsoSettingsView *view) : settings(scope), view(view) { | |
| 11 | + // Grid | |
| 12 | + vaGrid[0].resize(((DIVS_TIME * DIVS_SUB - 2) * (DIVS_VOLTAGE - 2) + | |
| 13 | + (DIVS_VOLTAGE * DIVS_SUB - 2) * (DIVS_TIME - 2) - | |
| 14 | + ((DIVS_TIME - 2) * (DIVS_VOLTAGE - 2))) * | |
| 15 | + 2); | |
| 16 | + std::vector<GLfloat>::iterator glIterator = vaGrid[0].begin(); | |
| 17 | + // Draw vertical lines | |
| 18 | + for (int div = 1; div < DIVS_TIME / 2; ++div) { | |
| 19 | + for (int dot = 1; dot < DIVS_VOLTAGE / 2 * DIVS_SUB; ++dot) { | |
| 20 | + float dotPosition = (float)dot / DIVS_SUB; | |
| 21 | + *(glIterator++) = -div; | |
| 22 | + *(glIterator++) = -dotPosition; | |
| 23 | + *(glIterator++) = -div; | |
| 24 | + *(glIterator++) = dotPosition; | |
| 25 | + *(glIterator++) = div; | |
| 26 | + *(glIterator++) = -dotPosition; | |
| 27 | + *(glIterator++) = div; | |
| 28 | + *(glIterator++) = dotPosition; | |
| 29 | + } | |
| 30 | + } | |
| 31 | + // Draw horizontal lines | |
| 32 | + for (int div = 1; div < DIVS_VOLTAGE / 2; ++div) { | |
| 33 | + for (int dot = 1; dot < DIVS_TIME / 2 * DIVS_SUB; ++dot) { | |
| 34 | + if (dot % DIVS_SUB == 0) | |
| 35 | + continue; // Already done by vertical lines | |
| 36 | + float dotPosition = (float)dot / DIVS_SUB; | |
| 37 | + *(glIterator++) = -dotPosition; | |
| 38 | + *(glIterator++) = -div; | |
| 39 | + *(glIterator++) = dotPosition; | |
| 40 | + *(glIterator++) = -div; | |
| 41 | + *(glIterator++) = -dotPosition; | |
| 42 | + *(glIterator++) = div; | |
| 43 | + *(glIterator++) = dotPosition; | |
| 44 | + *(glIterator++) = div; | |
| 45 | + } | |
| 46 | + } | |
| 39 | 47 | |
| 40 | - this->dataAnalyzer = 0; | |
| 41 | - this->digitalPhosphorDepth = 0; | |
| 48 | + // Axes | |
| 49 | + vaGrid[1].resize( | |
| 50 | + (2 + (DIVS_TIME * DIVS_SUB - 2) + (DIVS_VOLTAGE * DIVS_SUB - 2)) * 4); | |
| 51 | + glIterator = vaGrid[1].begin(); | |
| 52 | + // Horizontal axis | |
| 53 | + *(glIterator++) = -DIVS_TIME / 2; | |
| 54 | + *(glIterator++) = 0; | |
| 55 | + *(glIterator++) = DIVS_TIME / 2; | |
| 56 | + *(glIterator++) = 0; | |
| 57 | + // Vertical axis | |
| 58 | + *(glIterator++) = 0; | |
| 59 | + *(glIterator++) = -DIVS_VOLTAGE / 2; | |
| 60 | + *(glIterator++) = 0; | |
| 61 | + *(glIterator++) = DIVS_VOLTAGE / 2; | |
| 62 | + // Subdiv lines on horizontal axis | |
| 63 | + for (int line = 1; line < DIVS_TIME / 2 * DIVS_SUB; ++line) { | |
| 64 | + float linePosition = (float)line / DIVS_SUB; | |
| 65 | + *(glIterator++) = linePosition; | |
| 66 | + *(glIterator++) = -0.05; | |
| 67 | + *(glIterator++) = linePosition; | |
| 68 | + *(glIterator++) = 0.05; | |
| 69 | + *(glIterator++) = -linePosition; | |
| 70 | + *(glIterator++) = -0.05; | |
| 71 | + *(glIterator++) = -linePosition; | |
| 72 | + *(glIterator++) = 0.05; | |
| 73 | + } | |
| 74 | + // Subdiv lines on vertical axis | |
| 75 | + for (int line = 1; line < DIVS_VOLTAGE / 2 * DIVS_SUB; ++line) { | |
| 76 | + float linePosition = (float)line / DIVS_SUB; | |
| 77 | + *(glIterator++) = -0.05; | |
| 78 | + *(glIterator++) = linePosition; | |
| 79 | + *(glIterator++) = 0.05; | |
| 80 | + *(glIterator++) = linePosition; | |
| 81 | + *(glIterator++) = -0.05; | |
| 82 | + *(glIterator++) = -linePosition; | |
| 83 | + *(glIterator++) = 0.05; | |
| 84 | + *(glIterator++) = -linePosition; | |
| 85 | + } | |
| 42 | 86 | |
| 43 | - this->generateGrid(); | |
| 87 | + // Border | |
| 88 | + vaGrid[2].resize(4 * 2); | |
| 89 | + glIterator = vaGrid[2].begin(); | |
| 90 | + *(glIterator++) = -DIVS_TIME / 2; | |
| 91 | + *(glIterator++) = -DIVS_VOLTAGE / 2; | |
| 92 | + *(glIterator++) = DIVS_TIME / 2; | |
| 93 | + *(glIterator++) = -DIVS_VOLTAGE / 2; | |
| 94 | + *(glIterator++) = DIVS_TIME / 2; | |
| 95 | + *(glIterator++) = DIVS_VOLTAGE / 2; | |
| 96 | + *(glIterator++) = -DIVS_TIME / 2; | |
| 97 | + *(glIterator++) = DIVS_VOLTAGE / 2; | |
| 44 | 98 | } |
| 45 | 99 | |
| 46 | -/// \brief Deletes OpenGL objects. | |
| 47 | -GlGenerator::~GlGenerator() { | |
| 48 | - /// \todo Clean up vaChannel | |
| 100 | +const std::vector<GLfloat> &GlGenerator::channel(int mode, int channel, int index) const { | |
| 101 | + return vaChannel[mode][channel][index]; | |
| 49 | 102 | } |
| 50 | 103 | |
| 51 | -/// \brief Set the data analyzer whose data will be drawn. | |
| 52 | -/// \param dataAnalyzer Pointer to the DataAnalyzer class. | |
| 53 | -void GlGenerator::setDataAnalyzer(DataAnalyzer *dataAnalyzer) { | |
| 54 | - if (this->dataAnalyzer) | |
| 55 | - disconnect(this->dataAnalyzer, SIGNAL(finished()), this, | |
| 56 | - SLOT(generateGraphs())); | |
| 57 | - this->dataAnalyzer = dataAnalyzer; | |
| 58 | - connect(this->dataAnalyzer, SIGNAL(finished()), this, SLOT(generateGraphs())); | |
| 104 | +const std::vector<GLfloat> &GlGenerator::grid(int a) const { | |
| 105 | + return vaGrid[a]; | |
| 59 | 106 | } |
| 60 | 107 | |
| 61 | -/// \brief Prepare arrays for drawing the data we get from the data analyzer. | |
| 62 | -void GlGenerator::generateGraphs() { | |
| 63 | - if (!this->dataAnalyzer) | |
| 64 | - return; | |
| 65 | - | |
| 66 | - // Adapt the number of graphs | |
| 67 | - for (int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT; | |
| 68 | - ++mode) | |
| 69 | - this->vaChannel[mode].resize(this->settings->scope.voltage.count()); | |
| 70 | - | |
| 71 | - // Set digital phosphor depth to one if we don't use it | |
| 72 | - if (this->settings->view.digitalPhosphor) | |
| 73 | - this->digitalPhosphorDepth = this->settings->view.digitalPhosphorDepth; | |
| 74 | - else | |
| 75 | - this->digitalPhosphorDepth = 1; | |
| 76 | - | |
| 77 | - // Handle all digital phosphor related list manipulations | |
| 78 | - for (int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT; | |
| 79 | - ++mode) { | |
| 80 | - for (unsigned int channel = 0; channel < this->vaChannel[mode].size(); | |
| 81 | - ++channel) { | |
| 82 | - // Move the last list element to the front | |
| 83 | - this->vaChannel[mode][channel].push_front(std::vector<GLfloat>()); | |
| 84 | - | |
| 85 | - // Resize lists for vector array to fit the digital phosphor depth | |
| 86 | - this->vaChannel[mode][channel].resize(this->digitalPhosphorDepth); | |
| 87 | - } | |
| 88 | - } | |
| 89 | - | |
| 90 | - QMutexLocker locker(this->dataAnalyzer->mutex()); | |
| 91 | - | |
| 92 | - unsigned int preTrigSamples = 0; | |
| 93 | - unsigned int postTrigSamples = 0; | |
| 94 | - switch (this->settings->scope.horizontal.format) { | |
| 95 | - case Dso::GRAPHFORMAT_TY: { | |
| 96 | - unsigned int swTriggerStart = 0; | |
| 97 | - // check trigger point for software trigger | |
| 98 | - if (this->settings->scope.trigger.mode == Dso::TRIGGERMODE_SOFTWARE && | |
| 99 | - this->settings->scope.trigger.source <= 1) { | |
| 100 | - int channel = this->settings->scope.trigger.source; | |
| 101 | - if (this->settings->scope.voltage[channel].used && | |
| 102 | - this->dataAnalyzer->data(channel) && | |
| 103 | - !this->dataAnalyzer->data(channel)->samples.voltage.sample.empty()) { | |
| 104 | - double value; | |
| 105 | - double level = this->settings->scope.voltage[channel].trigger; | |
| 106 | - unsigned int sampleCount = | |
| 107 | - this->dataAnalyzer->data(channel)->samples.voltage.sample.size(); | |
| 108 | - double timeDisplay = this->settings->scope.horizontal.timebase * 10; | |
| 109 | - double samplesDisplay = | |
| 110 | - timeDisplay * this->settings->scope.horizontal.samplerate; | |
| 111 | - if (samplesDisplay >= sampleCount) { | |
| 112 | -// For sure not enough samples to adjust for jitter. | |
| 113 | -// Following options exist: | |
| 114 | -// 1: Decrease sample rate | |
| 115 | -// 2: Change trigger mode to auto | |
| 116 | -// 3: Ignore samples | |
| 117 | -// For now #3 is chosen | |
| 118 | -#ifdef DEBUG | |
| 119 | - timestampDebug(QString("Too few samples to make a steady " | |
| 120 | - "picture. Decrease sample rate")); | |
| 121 | -#endif | |
| 122 | - return; | |
| 108 | +bool GlGenerator::isReady() const | |
| 109 | +{ | |
| 110 | + return ready; | |
| 111 | +} | |
| 112 | + | |
| 113 | + | |
| 114 | +void GlGenerator::generateGraphs(const DataAnalyzerResult* result) { | |
| 115 | + | |
| 116 | + int digitalPhosphorDepth = view->digitalPhosphorDepth; | |
| 117 | + | |
| 118 | + // Handle all digital phosphor related list manipulations | |
| 119 | + for (int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT; ++mode) { | |
| 120 | + // Adapt the number of graphs | |
| 121 | + vaChannel[mode].resize(settings->voltage.count()); | |
| 122 | + | |
| 123 | + for (unsigned int channel = 0; channel < vaChannel[mode].size(); ++channel) { | |
| 124 | + // Move the last list element to the front | |
| 125 | + vaChannel[mode][channel].push_front(std::vector<GLfloat>()); | |
| 126 | + | |
| 127 | + // Resize lists for vector array to fit the digital phosphor depth | |
| 128 | + vaChannel[mode][channel].resize(digitalPhosphorDepth); | |
| 123 | 129 | } |
| 124 | - preTrigSamples = | |
| 125 | - (this->settings->scope.trigger.position * samplesDisplay); | |
| 126 | - postTrigSamples = sampleCount - (samplesDisplay - preTrigSamples); | |
| 127 | - // std::vector<double>::const_iterator dataIterator = | |
| 128 | - // this->dataAnalyzer->data(channel)->samples.voltage.sample.begin(); | |
| 129 | - | |
| 130 | - if (this->settings->scope.trigger.slope == Dso::SLOPE_POSITIVE) { | |
| 131 | - double prev = INT_MAX; | |
| 132 | - for (unsigned int i = preTrigSamples; i < postTrigSamples; i++) { | |
| 133 | - value = | |
| 134 | - this->dataAnalyzer->data(channel)->samples.voltage.sample[i]; | |
| 135 | - if (value > level && prev <= level) { | |
| 136 | - int rising = 0; | |
| 137 | - for (unsigned int k = i + 1; k < i + 11 && k < sampleCount; k++) { | |
| 138 | - if (this->dataAnalyzer->data(channel) | |
| 139 | - ->samples.voltage.sample[k] >= value) { | |
| 140 | - rising++; | |
| 130 | + } | |
| 131 | + | |
| 132 | + ready = true; | |
| 133 | + | |
| 134 | + unsigned int preTrigSamples = 0; | |
| 135 | + unsigned int postTrigSamples = 0; | |
| 136 | + switch (settings->horizontal.format) { | |
| 137 | + case Dso::GRAPHFORMAT_TY: { | |
| 138 | + unsigned int swTriggerStart = 0; | |
| 139 | + // check trigger point for software trigger | |
| 140 | + if (settings->trigger.mode == Dso::TRIGGERMODE_SOFTWARE && | |
| 141 | + settings->trigger.source <= 1) { | |
| 142 | + unsigned int channel = settings->trigger.source; | |
| 143 | + if (settings->voltage[channel].used && | |
| 144 | + result->data(channel) && | |
| 145 | + !result->data(channel)->voltage.sample.empty()) { | |
| 146 | + double value; | |
| 147 | + double level = settings->voltage[channel].trigger; | |
| 148 | + unsigned int sampleCount = | |
| 149 | + result->data(channel)->voltage.sample.size(); | |
| 150 | + double timeDisplay = settings->horizontal.timebase * 10; | |
| 151 | + double samplesDisplay = | |
| 152 | + timeDisplay * settings->horizontal.samplerate; | |
| 153 | + if (samplesDisplay >= sampleCount) { | |
| 154 | + // For sure not enough samples to adjust for jitter. | |
| 155 | + // Following options exist: | |
| 156 | + // 1: Decrease sample rate | |
| 157 | + // 2: Change trigger mode to auto | |
| 158 | + // 3: Ignore samples | |
| 159 | + // For now #3 is chosen | |
| 160 | + timestampDebug(QString("Too few samples to make a steady " | |
| 161 | + "picture. Decrease sample rate")); | |
| 162 | + return; | |
| 141 | 163 | } |
| 142 | - } | |
| 143 | - if (rising > 7) { | |
| 144 | - swTriggerStart = i; | |
| 145 | - break; | |
| 146 | - } | |
| 147 | - } | |
| 148 | - prev = value; | |
| 149 | - } | |
| 150 | - } else if (this->settings->scope.trigger.slope == Dso::SLOPE_NEGATIVE) { | |
| 151 | - double prev = INT_MIN; | |
| 152 | - for (unsigned int i = preTrigSamples; i < postTrigSamples; i++) { | |
| 153 | - value = | |
| 154 | - this->dataAnalyzer->data(channel)->samples.voltage.sample[i]; | |
| 155 | - if (value < level && prev >= level) { | |
| 156 | - int falling = 0; | |
| 157 | - for (unsigned int k = i + 1; k < i + 11 && k < sampleCount; k++) { | |
| 158 | - if (this->dataAnalyzer->data(channel) | |
| 159 | - ->samples.voltage.sample[k] < value) { | |
| 160 | - falling++; | |
| 164 | + preTrigSamples = (settings->trigger.position * samplesDisplay); | |
| 165 | + postTrigSamples = sampleCount - (samplesDisplay - preTrigSamples); | |
| 166 | + | |
| 167 | + if (settings->trigger.slope == Dso::SLOPE_POSITIVE) { | |
| 168 | + double prev = INT_MAX; | |
| 169 | + for (unsigned int i = preTrigSamples; i < postTrigSamples; i++) { | |
| 170 | + value = result->data(channel)->voltage.sample[i]; | |
| 171 | + if (value > level && prev <= level) { | |
| 172 | + int rising = 0; | |
| 173 | + for (unsigned int k = i + 1; k < i + 11 && k < sampleCount; k++) { | |
| 174 | + if (result->data(channel) | |
| 175 | + ->voltage.sample[k] >= value) { | |
| 176 | + rising++; | |
| 177 | + } | |
| 178 | + } | |
| 179 | + if (rising > 7) { | |
| 180 | + swTriggerStart = i; | |
| 181 | + break; | |
| 182 | + } | |
| 183 | + } | |
| 184 | + prev = value; | |
| 185 | + } | |
| 186 | + } else if (settings->trigger.slope == Dso::SLOPE_NEGATIVE) { | |
| 187 | + double prev = INT_MIN; | |
| 188 | + for (unsigned int i = preTrigSamples; i < postTrigSamples; i++) { | |
| 189 | + value = result->data(channel)->voltage.sample[i]; | |
| 190 | + if (value < level && prev >= level) { | |
| 191 | + int falling = 0; | |
| 192 | + for (unsigned int k = i + 1; k < i + 11 && k < sampleCount; k++) { | |
| 193 | + if (result->data(channel)->voltage.sample[k] < value) { | |
| 194 | + falling++; | |
| 195 | + } | |
| 196 | + } | |
| 197 | + if (falling > 7) { | |
| 198 | + swTriggerStart = i; | |
| 199 | + break; | |
| 200 | + } | |
| 201 | + } | |
| 202 | + prev = value; | |
| 203 | + } | |
| 161 | 204 | } |
| 162 | - } | |
| 163 | - if (falling > 7) { | |
| 164 | - swTriggerStart = i; | |
| 165 | - break; | |
| 166 | - } | |
| 167 | 205 | } |
| 168 | - prev = value; | |
| 169 | - } | |
| 206 | + if (swTriggerStart == 0) { | |
| 207 | + timestampDebug(QString("Trigger not asserted. Data ignored")); | |
| 208 | + return; | |
| 209 | + } | |
| 170 | 210 | } |
| 171 | - } | |
| 172 | - if (swTriggerStart == 0) { | |
| 173 | -#ifdef DEBUG | |
| 174 | - timestampDebug(QString("Trigger not asserted. Data ignored")); | |
| 175 | -#endif | |
| 176 | - return; | |
| 177 | - } | |
| 178 | - } | |
| 179 | 211 | |
| 180 | - // Add graphs for channels | |
| 181 | - for (int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT; | |
| 182 | - ++mode) { | |
| 183 | - for (int channel = 0; channel < this->settings->scope.voltage.size(); | |
| 184 | - ++channel) { | |
| 185 | - // Check if this channel is used and available at the data analyzer | |
| 186 | - if (((mode == Dso::CHANNELMODE_VOLTAGE) | |
| 187 | - ? this->settings->scope.voltage[channel].used | |
| 188 | - : this->settings->scope.spectrum[channel].used) && | |
| 189 | - this->dataAnalyzer->data(channel) && | |
| 190 | - !this->dataAnalyzer->data(channel) | |
| 191 | - ->samples.voltage.sample.empty()) { | |
| 192 | - // Check if the sample count has changed | |
| 193 | - unsigned int sampleCount = (mode == Dso::CHANNELMODE_VOLTAGE) | |
| 194 | - ? this->dataAnalyzer->data(channel) | |
| 195 | - ->samples.voltage.sample.size() | |
| 196 | - : this->dataAnalyzer->data(channel) | |
| 197 | - ->samples.spectrum.sample.size(); | |
| 198 | - if (mode == Dso::CHANNELMODE_VOLTAGE) | |
| 199 | - sampleCount -= (swTriggerStart - preTrigSamples); | |
| 200 | - unsigned int neededSize = sampleCount * 2; | |
| 212 | + // Add graphs for channels | |
| 213 | + for (int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT; ++mode) { | |
| 214 | + for (int channel = 0; channel < (int)settings->voltage.size(); ++channel) { | |
| 215 | + // Check if this channel is used and available at the data analyzer | |
| 216 | + if (((mode == Dso::CHANNELMODE_VOLTAGE) | |
| 217 | + ? settings->voltage[channel].used | |
| 218 | + : settings->spectrum[channel].used) && | |
| 219 | + result->data(channel) && | |
| 220 | + !result->data(channel) | |
| 221 | + ->voltage.sample.empty()) { | |
| 222 | + // Check if the sample count has changed | |
| 223 | + size_t sampleCount = (mode == Dso::CHANNELMODE_VOLTAGE) | |
| 224 | + ? result->data(channel)->voltage.sample.size() | |
| 225 | + : result->data(channel)->spectrum.sample.size(); | |
| 226 | + if (mode == Dso::CHANNELMODE_VOLTAGE) | |
| 227 | + sampleCount -= (swTriggerStart - preTrigSamples); | |
| 228 | + size_t neededSize = sampleCount * 2; | |
| 201 | 229 | |
| 202 | 230 | #if 0 |
| 203 | - for(unsigned int index = 0; index < this->digitalPhosphorDepth; ++index) { | |
| 204 | - if(this->vaChannel[mode][channel][index].size() != neededSize) | |
| 205 | - this->vaChannel[mode][channel][index].clear(); // Something was changed, drop old traces | |
| 206 | - } | |
| 231 | + for(unsigned int index = 0; index < digitalPhosphorDepth; ++index) { | |
| 232 | + if(vaChannel[mode][channel][index].size() != neededSize) | |
| 233 | + vaChannel[mode][channel][index].clear(); // Something was changed, drop old traces | |
| 234 | + } | |
| 207 | 235 | #endif |
| 208 | 236 | |
| 209 | - // Set size directly to avoid reallocations | |
| 210 | - this->vaChannel[mode][channel].front().resize(neededSize); | |
| 211 | - | |
| 212 | - // Iterator to data for direct access | |
| 213 | - std::vector<GLfloat>::iterator glIterator = | |
| 214 | - this->vaChannel[mode][channel].front().begin(); | |
| 215 | - | |
| 216 | - // What's the horizontal distance between sampling points? | |
| 217 | - double horizontalFactor; | |
| 218 | - if (mode == Dso::CHANNELMODE_VOLTAGE) | |
| 219 | - horizontalFactor = | |
| 220 | - this->dataAnalyzer->data(channel)->samples.voltage.interval / | |
| 221 | - this->settings->scope.horizontal.timebase; | |
| 222 | - else | |
| 223 | - horizontalFactor = | |
| 224 | - this->dataAnalyzer->data(channel)->samples.spectrum.interval / | |
| 225 | - this->settings->scope.horizontal.frequencybase; | |
| 226 | - | |
| 227 | - // Fill vector array | |
| 228 | - if (mode == Dso::CHANNELMODE_VOLTAGE) { | |
| 229 | - std::vector<double>::const_iterator dataIterator = | |
| 230 | - this->dataAnalyzer->data(channel) | |
| 231 | - ->samples.voltage.sample.begin(); | |
| 232 | - const double gain = this->settings->scope.voltage[channel].gain; | |
| 233 | - const double offset = this->settings->scope.voltage[channel].offset; | |
| 234 | - | |
| 235 | - std::advance(dataIterator, swTriggerStart - preTrigSamples); | |
| 236 | - | |
| 237 | - for (unsigned int position = 0; position < sampleCount; | |
| 238 | - ++position) { | |
| 239 | - *(glIterator++) = position * horizontalFactor - DIVS_TIME / 2; | |
| 240 | - *(glIterator++) = *(dataIterator++) / gain + offset; | |
| 241 | - } | |
| 242 | - } else { | |
| 243 | - std::vector<double>::const_iterator dataIterator = | |
| 244 | - this->dataAnalyzer->data(channel) | |
| 245 | - ->samples.spectrum.sample.begin(); | |
| 246 | - const double magnitude = | |
| 247 | - this->settings->scope.spectrum[channel].magnitude; | |
| 248 | - const double offset = | |
| 249 | - this->settings->scope.spectrum[channel].offset; | |
| 250 | - | |
| 251 | - for (unsigned int position = 0; position < sampleCount; | |
| 252 | - ++position) { | |
| 253 | - *(glIterator++) = position * horizontalFactor - DIVS_TIME / 2; | |
| 254 | - *(glIterator++) = *(dataIterator++) / magnitude + offset; | |
| 237 | + // Set size directly to avoid reallocations | |
| 238 | + vaChannel[mode][(size_t)channel].front().resize(neededSize); | |
| 239 | + | |
| 240 | + // Iterator to data for direct access | |
| 241 | + std::vector<GLfloat>::iterator glIterator = | |
| 242 | + vaChannel[mode][(size_t)channel].front().begin(); | |
| 243 | + | |
| 244 | + // What's the horizontal distance between sampling points? | |
| 245 | + double horizontalFactor; | |
| 246 | + if (mode == Dso::CHANNELMODE_VOLTAGE) | |
| 247 | + horizontalFactor = | |
| 248 | + result->data(channel)->voltage.interval / | |
| 249 | + settings->horizontal.timebase; | |
| 250 | + else | |
| 251 | + horizontalFactor = | |
| 252 | + result->data(channel)->spectrum.interval / | |
| 253 | + settings->horizontal.frequencybase; | |
| 254 | + | |
| 255 | + // Fill vector array | |
| 256 | + if (mode == Dso::CHANNELMODE_VOLTAGE) { | |
| 257 | + std::vector<double>::const_iterator dataIterator = | |
| 258 | + result->data(channel) | |
| 259 | + ->voltage.sample.begin(); | |
| 260 | + const double gain = settings->voltage[channel].gain; | |
| 261 | + const double offset = settings->voltage[channel].offset; | |
| 262 | + | |
| 263 | + std::advance(dataIterator, swTriggerStart - preTrigSamples); | |
| 264 | + | |
| 265 | + for (unsigned int position = 0; position < sampleCount; | |
| 266 | + ++position) { | |
| 267 | + *(glIterator++) = position * horizontalFactor - DIVS_TIME / 2; | |
| 268 | + *(glIterator++) = *(dataIterator++) / gain + offset; | |
| 269 | + } | |
| 270 | + } else { | |
| 271 | + std::vector<double>::const_iterator dataIterator = | |
| 272 | + result->data(channel) | |
| 273 | + ->spectrum.sample.begin(); | |
| 274 | + const double magnitude = | |
| 275 | + settings->spectrum[channel].magnitude; | |
| 276 | + const double offset = | |
| 277 | + settings->spectrum[channel].offset; | |
| 278 | + | |
| 279 | + for (unsigned int position = 0; position < sampleCount; | |
| 280 | + ++position) { | |
| 281 | + *(glIterator++) = position * horizontalFactor - DIVS_TIME / 2; | |
| 282 | + *(glIterator++) = *(dataIterator++) / magnitude + offset; | |
| 283 | + } | |
| 284 | + } | |
| 285 | + } else { | |
| 286 | + // Delete all vector arrays | |
| 287 | + for (unsigned index = 0; index < (unsigned)digitalPhosphorDepth; ++index) | |
| 288 | + vaChannel[mode][channel][index].clear(); | |
| 289 | + } | |
| 255 | 290 | } |
| 256 | - } | |
| 257 | - } else { | |
| 258 | - // Delete all vector arrays | |
| 259 | - for (unsigned int index = 0; index < this->digitalPhosphorDepth; | |
| 260 | - ++index) | |
| 261 | - this->vaChannel[mode][channel][index].clear(); | |
| 262 | - } | |
| 263 | - } | |
| 264 | - } | |
| 265 | - } break; | |
| 266 | - | |
| 267 | - case Dso::GRAPHFORMAT_XY: | |
| 268 | - for (int channel = 0; channel < this->settings->scope.voltage.size(); | |
| 269 | - ++channel) { | |
| 270 | - // For even channel numbers check if this channel is used and this and the | |
| 271 | - // following channel are available at the data analyzer | |
| 272 | - if (channel % 2 == 0 && | |
| 273 | - channel + 1 < this->settings->scope.voltage.size() && | |
| 274 | - this->settings->scope.voltage[channel].used && | |
| 275 | - this->dataAnalyzer->data(channel) && | |
| 276 | - !this->dataAnalyzer->data(channel)->samples.voltage.sample.empty() && | |
| 277 | - this->dataAnalyzer->data(channel + 1) && | |
| 278 | - !this->dataAnalyzer->data(channel + 1) | |
| 279 | - ->samples.voltage.sample.empty()) { | |
| 280 | - // Check if the sample count has changed | |
| 281 | - const unsigned int sampleCount = qMin( | |
| 282 | - this->dataAnalyzer->data(channel)->samples.voltage.sample.size(), | |
| 283 | - this->dataAnalyzer->data(channel + 1) | |
| 284 | - ->samples.voltage.sample.size()); | |
| 285 | - const unsigned int neededSize = sampleCount * 2; | |
| 286 | - for (unsigned int index = 0; index < this->digitalPhosphorDepth; | |
| 287 | - ++index) { | |
| 288 | - if (this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index] | |
| 289 | - .size() != neededSize) | |
| 290 | - this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index] | |
| 291 | - .clear(); // Something was changed, drop old traces | |
| 292 | - } | |
| 293 | - | |
| 294 | - // Set size directly to avoid reallocations | |
| 295 | - this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel].front().resize( | |
| 296 | - neededSize); | |
| 297 | - | |
| 298 | - // Iterator to data for direct access | |
| 299 | - std::vector<GLfloat>::iterator glIterator = | |
| 300 | - this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel].front().begin(); | |
| 301 | - | |
| 302 | - // Fill vector array | |
| 303 | - unsigned int xChannel = channel; | |
| 304 | - unsigned int yChannel = channel + 1; | |
| 305 | - std::vector<double>::const_iterator xIterator = | |
| 306 | - this->dataAnalyzer->data(xChannel)->samples.voltage.sample.begin(); | |
| 307 | - std::vector<double>::const_iterator yIterator = | |
| 308 | - this->dataAnalyzer->data(yChannel)->samples.voltage.sample.begin(); | |
| 309 | - const double xGain = this->settings->scope.voltage[xChannel].gain; | |
| 310 | - const double yGain = this->settings->scope.voltage[yChannel].gain; | |
| 311 | - const double xOffset = this->settings->scope.voltage[xChannel].offset; | |
| 312 | - const double yOffset = this->settings->scope.voltage[yChannel].offset; | |
| 313 | - | |
| 314 | - for (unsigned int position = 0; position < sampleCount; ++position) { | |
| 315 | - *(glIterator++) = *(xIterator++) / xGain + xOffset; | |
| 316 | - *(glIterator++) = *(yIterator++) / yGain + yOffset; | |
| 317 | 291 | } |
| 318 | - } else { | |
| 319 | - // Delete all vector arrays | |
| 320 | - for (unsigned int index = 0; index < this->digitalPhosphorDepth; | |
| 321 | - ++index) | |
| 322 | - this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index].clear(); | |
| 323 | - } | |
| 324 | - | |
| 325 | - // Delete all spectrum graphs | |
| 326 | - for (unsigned int index = 0; index < this->digitalPhosphorDepth; ++index) | |
| 327 | - this->vaChannel[Dso::CHANNELMODE_SPECTRUM][channel][index].clear(); | |
| 328 | - } | |
| 329 | - break; | |
| 292 | + } break; | |
| 293 | + | |
| 294 | + case Dso::GRAPHFORMAT_XY: | |
| 295 | + for (int channel = 0; channel < settings->voltage.size(); | |
| 296 | + ++channel) { | |
| 297 | + // For even channel numbers check if this channel is used and this and the | |
| 298 | + // following channel are available at the data analyzer | |
| 299 | + if (channel % 2 == 0 && | |
| 300 | + channel + 1 < settings->voltage.size() && | |
| 301 | + settings->voltage[channel].used && | |
| 302 | + result->data(channel) && | |
| 303 | + !result->data(channel)->voltage.sample.empty() && | |
| 304 | + result->data(channel + 1) && | |
| 305 | + !result->data(channel + 1) | |
| 306 | + ->voltage.sample.empty()) { | |
| 307 | + // Check if the sample count has changed | |
| 308 | + const unsigned sampleCount = qMin( | |
| 309 | + result->data(channel)->voltage.sample.size(), | |
| 310 | + result->data(channel + 1) | |
| 311 | + ->voltage.sample.size()); | |
| 312 | + const unsigned neededSize = sampleCount * 2; | |
| 313 | + for (unsigned index = 0; index < (unsigned)digitalPhosphorDepth; ++index) { | |
| 314 | + if (vaChannel[Dso::CHANNELMODE_VOLTAGE][(size_t)channel][index].size() != neededSize) | |
| 315 | + vaChannel[Dso::CHANNELMODE_VOLTAGE][(size_t)channel][index].clear(); // Something was changed, drop old traces | |
| 316 | + } | |
| 330 | 317 | |
| 331 | - default: | |
| 332 | - break; | |
| 333 | - } | |
| 318 | + // Set size directly to avoid reallocations | |
| 319 | + vaChannel[Dso::CHANNELMODE_VOLTAGE][(size_t)channel].front().resize( | |
| 320 | + neededSize); | |
| 321 | + | |
| 322 | + // Iterator to data for direct access | |
| 323 | + std::vector<GLfloat>::iterator glIterator = | |
| 324 | + vaChannel[Dso::CHANNELMODE_VOLTAGE][channel].front().begin(); | |
| 325 | + | |
| 326 | + // Fill vector array | |
| 327 | + unsigned int xChannel = channel; | |
| 328 | + unsigned int yChannel = channel + 1; | |
| 329 | + std::vector<double>::const_iterator xIterator = | |
| 330 | + result->data(xChannel)->voltage.sample.begin(); | |
| 331 | + std::vector<double>::const_iterator yIterator = | |
| 332 | + result->data(yChannel)->voltage.sample.begin(); | |
| 333 | + const double xGain = settings->voltage[xChannel].gain; | |
| 334 | + const double yGain = settings->voltage[yChannel].gain; | |
| 335 | + const double xOffset = settings->voltage[xChannel].offset; | |
| 336 | + const double yOffset = settings->voltage[yChannel].offset; | |
| 337 | + | |
| 338 | + for (unsigned int position = 0; position < sampleCount; ++position) { | |
| 339 | + *(glIterator++) = *(xIterator++) / xGain + xOffset; | |
| 340 | + *(glIterator++) = *(yIterator++) / yGain + yOffset; | |
| 341 | + } | |
| 342 | + } else { | |
| 343 | + // Delete all vector arrays | |
| 344 | + for (unsigned int index = 0; index < (unsigned)digitalPhosphorDepth; ++index) | |
| 345 | + vaChannel[Dso::CHANNELMODE_VOLTAGE][(size_t)channel][index].clear(); | |
| 346 | + } | |
| 334 | 347 | |
| 335 | - emit graphsGenerated(); | |
| 336 | -} | |
| 348 | + // Delete all spectrum graphs | |
| 349 | + for (unsigned int index = 0; index < (unsigned)digitalPhosphorDepth; ++index) | |
| 350 | + vaChannel[Dso::CHANNELMODE_SPECTRUM][(size_t)channel][index].clear(); | |
| 351 | + } | |
| 352 | + break; | |
| 337 | 353 | |
| 338 | -/// \brief Create the needed OpenGL vertex arrays for the grid. | |
| 339 | -void GlGenerator::generateGrid() { | |
| 340 | - // Grid | |
| 341 | - this->vaGrid[0].resize(((DIVS_TIME * DIVS_SUB - 2) * (DIVS_VOLTAGE - 2) + | |
| 342 | - (DIVS_VOLTAGE * DIVS_SUB - 2) * (DIVS_TIME - 2) - | |
| 343 | - ((DIVS_TIME - 2) * (DIVS_VOLTAGE - 2))) * | |
| 344 | - 2); | |
| 345 | - std::vector<GLfloat>::iterator glIterator = this->vaGrid[0].begin(); | |
| 346 | - // Draw vertical lines | |
| 347 | - for (int div = 1; div < DIVS_TIME / 2; ++div) { | |
| 348 | - for (int dot = 1; dot < DIVS_VOLTAGE / 2 * DIVS_SUB; ++dot) { | |
| 349 | - float dotPosition = (float)dot / DIVS_SUB; | |
| 350 | - *(glIterator++) = -div; | |
| 351 | - *(glIterator++) = -dotPosition; | |
| 352 | - *(glIterator++) = -div; | |
| 353 | - *(glIterator++) = dotPosition; | |
| 354 | - *(glIterator++) = div; | |
| 355 | - *(glIterator++) = -dotPosition; | |
| 356 | - *(glIterator++) = div; | |
| 357 | - *(glIterator++) = dotPosition; | |
| 354 | + default: | |
| 355 | + break; | |
| 358 | 356 | } |
| 359 | - } | |
| 360 | - // Draw horizontal lines | |
| 361 | - for (int div = 1; div < DIVS_VOLTAGE / 2; ++div) { | |
| 362 | - for (int dot = 1; dot < DIVS_TIME / 2 * DIVS_SUB; ++dot) { | |
| 363 | - if (dot % DIVS_SUB == 0) | |
| 364 | - continue; // Already done by vertical lines | |
| 365 | - float dotPosition = (float)dot / DIVS_SUB; | |
| 366 | - *(glIterator++) = -dotPosition; | |
| 367 | - *(glIterator++) = -div; | |
| 368 | - *(glIterator++) = dotPosition; | |
| 369 | - *(glIterator++) = -div; | |
| 370 | - *(glIterator++) = -dotPosition; | |
| 371 | - *(glIterator++) = div; | |
| 372 | - *(glIterator++) = dotPosition; | |
| 373 | - *(glIterator++) = div; | |
| 374 | - } | |
| 375 | - } | |
| 376 | - | |
| 377 | - // Axes | |
| 378 | - this->vaGrid[1].resize( | |
| 379 | - (2 + (DIVS_TIME * DIVS_SUB - 2) + (DIVS_VOLTAGE * DIVS_SUB - 2)) * 4); | |
| 380 | - glIterator = this->vaGrid[1].begin(); | |
| 381 | - // Horizontal axis | |
| 382 | - *(glIterator++) = -DIVS_TIME / 2; | |
| 383 | - *(glIterator++) = 0; | |
| 384 | - *(glIterator++) = DIVS_TIME / 2; | |
| 385 | - *(glIterator++) = 0; | |
| 386 | - // Vertical axis | |
| 387 | - *(glIterator++) = 0; | |
| 388 | - *(glIterator++) = -DIVS_VOLTAGE / 2; | |
| 389 | - *(glIterator++) = 0; | |
| 390 | - *(glIterator++) = DIVS_VOLTAGE / 2; | |
| 391 | - // Subdiv lines on horizontal axis | |
| 392 | - for (int line = 1; line < DIVS_TIME / 2 * DIVS_SUB; ++line) { | |
| 393 | - float linePosition = (float)line / DIVS_SUB; | |
| 394 | - *(glIterator++) = linePosition; | |
| 395 | - *(glIterator++) = -0.05; | |
| 396 | - *(glIterator++) = linePosition; | |
| 397 | - *(glIterator++) = 0.05; | |
| 398 | - *(glIterator++) = -linePosition; | |
| 399 | - *(glIterator++) = -0.05; | |
| 400 | - *(glIterator++) = -linePosition; | |
| 401 | - *(glIterator++) = 0.05; | |
| 402 | - } | |
| 403 | - // Subdiv lines on vertical axis | |
| 404 | - for (int line = 1; line < DIVS_VOLTAGE / 2 * DIVS_SUB; ++line) { | |
| 405 | - float linePosition = (float)line / DIVS_SUB; | |
| 406 | - *(glIterator++) = -0.05; | |
| 407 | - *(glIterator++) = linePosition; | |
| 408 | - *(glIterator++) = 0.05; | |
| 409 | - *(glIterator++) = linePosition; | |
| 410 | - *(glIterator++) = -0.05; | |
| 411 | - *(glIterator++) = -linePosition; | |
| 412 | - *(glIterator++) = 0.05; | |
| 413 | - *(glIterator++) = -linePosition; | |
| 414 | - } | |
| 415 | - | |
| 416 | - // Border | |
| 417 | - this->vaGrid[2].resize(4 * 2); | |
| 418 | - glIterator = this->vaGrid[2].begin(); | |
| 419 | - *(glIterator++) = -DIVS_TIME / 2; | |
| 420 | - *(glIterator++) = -DIVS_VOLTAGE / 2; | |
| 421 | - *(glIterator++) = DIVS_TIME / 2; | |
| 422 | - *(glIterator++) = -DIVS_VOLTAGE / 2; | |
| 423 | - *(glIterator++) = DIVS_TIME / 2; | |
| 424 | - *(glIterator++) = DIVS_VOLTAGE / 2; | |
| 425 | - *(glIterator++) = -DIVS_TIME / 2; | |
| 426 | - *(glIterator++) = DIVS_VOLTAGE / 2; | |
| 357 | + | |
| 358 | + emit graphsGenerated(); | |
| 427 | 359 | } | ... | ... |
openhantek/src/glgenerator.h
| 1 | -//////////////////////////////////////////////////////////////////////////////// | |
| 2 | -// | |
| 3 | -// OpenHantek | |
| 4 | -/// \file glgenerator.h | |
| 5 | -/// \brief Declares the GlScope class. | |
| 6 | -// | |
| 7 | -// Copyright (C) 2008, 2009 Oleg Khudyakov | |
| 8 | -// prcoder@potrebitel.ru | |
| 9 | -// Copyright (C) 2010 Oliver Haag | |
| 10 | -// oliver.haag@gmail.com | |
| 11 | -// | |
| 12 | -// This program is free software: you can redistribute it and/or modify it | |
| 13 | -// under the terms of the GNU General Public License as published by the Free | |
| 14 | -// Software Foundation, either version 3 of the License, or (at your option) | |
| 15 | -// any later version. | |
| 16 | -// | |
| 17 | -// This program is distributed in the hope that it will be useful, but WITHOUT | |
| 18 | -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 19 | -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| 20 | -// more details. | |
| 21 | -// | |
| 22 | -// You should have received a copy of the GNU General Public License along with | |
| 23 | -// this program. If not, see <http://www.gnu.org/licenses/>. | |
| 24 | -// | |
| 25 | -//////////////////////////////////////////////////////////////////////////////// | |
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 26 | 2 | |
| 27 | -#ifndef GLGENERATOR_H | |
| 28 | -#define GLGENERATOR_H | |
| 3 | +#pragma once | |
| 29 | 4 | |
| 30 | 5 | #include <deque> |
| 31 | 6 | |
| 32 | 7 | #include <QGLFunctions> |
| 33 | 8 | #include <QObject> |
| 34 | 9 | |
| 35 | -#include "definitions.h" | |
| 10 | +#include "scopesettings.h" | |
| 11 | +#include "viewsettings.h" | |
| 12 | +#include "dataanalyzerresult.h" | |
| 36 | 13 | |
| 37 | 14 | #define DIVS_TIME 10.0 ///< Number of horizontal screen divs |
| 38 | 15 | #define DIVS_VOLTAGE 8.0 ///< Number of vertical screen divs |
| 39 | 16 | #define DIVS_SUB 5 ///< Number of sub-divisions per div |
| 40 | 17 | |
| 41 | -class DataAnalyzer; | |
| 42 | -class DsoSettings; | |
| 43 | 18 | class GlScope; |
| 44 | 19 | |
| 45 | 20 | //////////////////////////////////////////////////////////////////////////////// |
| 46 | -/// \class GlGenerator glgenerator.h | |
| 21 | +/// \class GlGenerator | |
| 47 | 22 | /// \brief Generates the vertex arrays for the GlScope classes. |
| 48 | 23 | class GlGenerator : public QObject { |
| 49 | - Q_OBJECT | |
| 50 | - | |
| 51 | - friend class GlScope; | |
| 24 | + Q_OBJECT | |
| 52 | 25 | |
| 53 | 26 | public: |
| 54 | - GlGenerator(DsoSettings *settings, QObject *parent = 0); | |
| 55 | - ~GlGenerator(); | |
| 56 | - | |
| 57 | - void setDataAnalyzer(DataAnalyzer *dataAnalyzer); | |
| 58 | - | |
| 59 | -protected: | |
| 60 | - void generateGrid(); | |
| 61 | - | |
| 27 | + /// \brief Initializes the scope widget. | |
| 28 | + /// \param settings The target settings object. | |
| 29 | + /// \param parent The parent widget. | |
| 30 | + GlGenerator(DsoSettingsScope *scope, DsoSettingsView* view); | |
| 31 | + void generateGraphs(const DataAnalyzerResult *result); | |
| 32 | + const std::vector<GLfloat>& channel(int mode, int channel, int index) const; | |
| 33 | + const std::vector<GLfloat>& grid(int a) const; | |
| 34 | + bool isReady() const; | |
| 62 | 35 | private: |
| 63 | - DataAnalyzer *dataAnalyzer; | |
| 64 | - DsoSettings *settings; | |
| 65 | - | |
| 66 | - std::vector<std::deque<std::vector<GLfloat>>> | |
| 67 | - vaChannel[Dso::CHANNELMODE_COUNT]; | |
| 68 | - std::vector<GLfloat> vaGrid[3]; | |
| 69 | - | |
| 70 | - unsigned int digitalPhosphorDepth; | |
| 71 | - | |
| 72 | -public slots: | |
| 73 | - void generateGraphs(); | |
| 74 | - | |
| 36 | + DsoSettingsScope *settings; | |
| 37 | + DsoSettingsView *view; | |
| 38 | + std::vector<std::deque<std::vector<GLfloat>>> vaChannel[Dso::CHANNELMODE_COUNT]; | |
| 39 | + std::vector<GLfloat> vaGrid[3]; | |
| 40 | + bool ready = false; | |
| 75 | 41 | signals: |
| 76 | - void graphsGenerated(); ///< The graphs are ready to be drawn | |
| 42 | + void graphsGenerated(); ///< The graphs are ready to be drawn | |
| 77 | 43 | }; |
| 78 | 44 | |
| 79 | -#endif | |
| 45 | + | ... | ... |
openhantek/src/glscope.cpp
| 1 | -//////////////////////////////////////////////////////////////////////////////// | |
| 2 | -// | |
| 3 | -// OpenHantek | |
| 4 | -// glscope.cpp | |
| 5 | -// | |
| 6 | -// Copyright (C) 2008, 2009 Oleg Khudyakov | |
| 7 | -// prcoder@potrebitel.ru | |
| 8 | -// Copyright (C) 2010 Oliver Haag | |
| 9 | -// oliver.haag@gmail.com | |
| 10 | -// | |
| 11 | -// This program is free software: you can redistribute it and/or modify it | |
| 12 | -// under the terms of the GNU General Public License as published by the Free | |
| 13 | -// Software Foundation, either version 3 of the License, or (at your option) | |
| 14 | -// any later version. | |
| 15 | -// | |
| 16 | -// This program is distributed in the hope that it will be useful, but WITHOUT | |
| 17 | -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 18 | -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| 19 | -// more details. | |
| 20 | -// | |
| 21 | -// You should have received a copy of the GNU General Public License along with | |
| 22 | -// this program. If not, see <http://www.gnu.org/licenses/>. | |
| 23 | -// | |
| 24 | -//////////////////////////////////////////////////////////////////////////////// | |
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 25 | 2 | |
| 26 | 3 | #include <cmath> |
| 27 | 4 | |
| ... | ... | @@ -33,250 +10,212 @@ |
| 33 | 10 | #include "glgenerator.h" |
| 34 | 11 | #include "settings.h" |
| 35 | 12 | |
| 36 | -//////////////////////////////////////////////////////////////////////////////// | |
| 37 | -// class GlScope | |
| 38 | -/// \brief Initializes the scope widget. | |
| 39 | -/// \param settings The settings that should be used. | |
| 40 | -/// \param parent The parent widget. | |
| 41 | -GlScope::GlScope(DsoSettings *settings, QWidget *parent) : GL_WIDGET_CLASS(parent) { | |
| 42 | - this->settings = settings; | |
| 43 | - | |
| 44 | - this->generator = 0; | |
| 45 | - this->zoomed = false; | |
| 13 | +GlScope::GlScope(DsoSettings *settings, const GlGenerator *generator, QWidget *parent) : GL_WIDGET_CLASS(parent), | |
| 14 | + settings(settings), generator(generator) { | |
| 15 | + connect(generator, &GlGenerator::graphsGenerated, [this]() {update();}); | |
| 46 | 16 | } |
| 47 | 17 | |
| 48 | -/// \brief Deletes OpenGL objects. | |
| 49 | -GlScope::~GlScope() {} | |
| 50 | - | |
| 51 | 18 | /// \brief Initializes OpenGL output. |
| 52 | 19 | void GlScope::initializeGL() { |
| 53 | - glDisable(GL_DEPTH_TEST); | |
| 54 | - glEnable(GL_BLEND); | |
| 55 | - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
| 20 | + glDisable(GL_DEPTH_TEST); | |
| 21 | + glEnable(GL_BLEND); | |
| 22 | + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
| 56 | 23 | |
| 57 | - glPointSize(1); | |
| 24 | + glPointSize(1); | |
| 58 | 25 | |
| 59 | - QColor bg = settings->view.color.screen.background; | |
| 60 | - glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF()); | |
| 26 | + QColor bg = settings->view.color.screen.background; | |
| 27 | + glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF()); | |
| 61 | 28 | |
| 62 | - glShadeModel(GL_SMOOTH /*GL_FLAT*/); | |
| 63 | - glLineStipple(1, 0x3333); | |
| 29 | + glShadeModel(GL_SMOOTH /*GL_FLAT*/); | |
| 30 | + glLineStipple(1, 0x3333); | |
| 64 | 31 | |
| 65 | - glEnableClientState(GL_VERTEX_ARRAY); | |
| 32 | + glEnableClientState(GL_VERTEX_ARRAY); | |
| 66 | 33 | } |
| 67 | 34 | |
| 68 | 35 | /// \brief Draw the graphs and the grid. |
| 69 | 36 | void GlScope::paintGL() { |
| 70 | - if (!this->isVisible()) | |
| 71 | - return; | |
| 37 | + if (!this->isVisible() || !this->generator->isReady()) | |
| 38 | + return; | |
| 39 | + | |
| 40 | + // Clear OpenGL buffer and configure settings | |
| 41 | + glClear(GL_COLOR_BUFFER_BIT); | |
| 42 | + glLineWidth(1); | |
| 43 | + | |
| 44 | + if (this->settings->view.digitalPhosphorDepth > 0) { | |
| 45 | + drawGraph(); | |
| 46 | + } | |
| 47 | + | |
| 48 | + if (!this->zoomed) { | |
| 49 | + // Draw vertical lines at marker positions | |
| 50 | + glEnable(GL_LINE_STIPPLE); | |
| 51 | + QColor trColor = settings->view.color.screen.markers; | |
| 52 | + glColor4f(trColor.redF(), trColor.greenF(), trColor.blueF(), trColor.alphaF()); | |
| 53 | + | |
| 54 | + for (int marker = 0; marker < MARKER_COUNT; ++marker) { | |
| 55 | + if (!this->settings->scope.horizontal.marker_visible[marker]) | |
| 56 | + continue; | |
| 57 | + if (this->vaMarker[marker].size() != 4) { | |
| 58 | + this->vaMarker[marker].resize(2 * 2); | |
| 59 | + this->vaMarker[marker][1] = -DIVS_VOLTAGE; | |
| 60 | + this->vaMarker[marker][3] = DIVS_VOLTAGE; | |
| 61 | + } | |
| 72 | 62 | |
| 73 | - // Clear OpenGL buffer and configure settings | |
| 74 | - glClear(GL_COLOR_BUFFER_BIT); | |
| 75 | - glLineWidth(1); | |
| 63 | + this->vaMarker[marker][0] = | |
| 64 | + this->settings->scope.horizontal.marker[marker]; | |
| 65 | + this->vaMarker[marker][2] = | |
| 66 | + this->settings->scope.horizontal.marker[marker]; | |
| 76 | 67 | |
| 77 | - // Draw the graphs | |
| 78 | - if (this->generator && this->generator->digitalPhosphorDepth > 0) { | |
| 68 | + glVertexPointer(2, GL_FLOAT, 0, &this->vaMarker[marker].front()); | |
| 69 | + glDrawArrays(GL_LINES, 0, this->vaMarker[marker].size() / 2); | |
| 70 | + } | |
| 71 | + | |
| 72 | + glDisable(GL_LINE_STIPPLE); | |
| 73 | + } | |
| 74 | + | |
| 75 | + // Draw grid | |
| 76 | + this->drawGrid(); | |
| 77 | +} | |
| 78 | + | |
| 79 | +/// \brief Resize the widget. | |
| 80 | +/// \param width The new width of the widget. | |
| 81 | +/// \param height The new height of the widget. | |
| 82 | +void GlScope::resizeGL(int width, int height) { | |
| 83 | + glViewport(0, 0, (GLint)width, (GLint)height); | |
| 84 | + | |
| 85 | + glMatrixMode(GL_PROJECTION); | |
| 86 | + | |
| 87 | + // Set axes to div-scale and apply correction for exact pixelization | |
| 88 | + glLoadIdentity(); | |
| 89 | + GLdouble pixelizationWidthCorrection = | |
| 90 | + (width > 0) ? (GLdouble)width / (width - 1) : 1; | |
| 91 | + GLdouble pixelizationHeightCorrection = | |
| 92 | + (height > 0) ? (GLdouble)height / (height - 1) : 1; | |
| 93 | + glOrtho(-(DIVS_TIME / 2) * pixelizationWidthCorrection, | |
| 94 | + (DIVS_TIME / 2) * pixelizationWidthCorrection, | |
| 95 | + -(DIVS_VOLTAGE / 2) * pixelizationHeightCorrection, | |
| 96 | + (DIVS_VOLTAGE / 2) * pixelizationHeightCorrection, -1.0, 1.0); | |
| 97 | + | |
| 98 | + glMatrixMode(GL_MODELVIEW); | |
| 99 | +} | |
| 100 | + | |
| 101 | +/// \brief Set the zoom mode for this GlScope. | |
| 102 | +/// \param zoomed true magnifies the area between the markers. | |
| 103 | +void GlScope::setZoomMode(bool zoomed) { this->zoomed = zoomed; } | |
| 104 | + | |
| 105 | +/// \brief Draw the grid. | |
| 106 | +void GlScope::drawGrid() { | |
| 107 | + glDisable(GL_POINT_SMOOTH); | |
| 108 | + glDisable(GL_LINE_SMOOTH); | |
| 109 | + | |
| 110 | + QColor color; | |
| 111 | + // Grid | |
| 112 | + color = this->settings->view.color.screen.grid; | |
| 113 | + glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); | |
| 114 | + glVertexPointer(2, GL_FLOAT, 0, &generator->grid(0).front()); | |
| 115 | + glDrawArrays(GL_POINTS, 0, generator->grid(0).size() / 2); | |
| 116 | + // Axes | |
| 117 | + color = settings->view.color.screen.axes; | |
| 118 | + glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); | |
| 119 | + glVertexPointer(2, GL_FLOAT, 0, &generator->grid(1).front()); | |
| 120 | + glDrawArrays(GL_LINES, 0, generator->grid(1).size() / 2); | |
| 121 | + // Border | |
| 122 | + color = settings->view.color.screen.border; | |
| 123 | + glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); | |
| 124 | + glVertexPointer(2, GL_FLOAT, 0, &generator->grid(2).front()); | |
| 125 | + glDrawArrays(GL_LINE_LOOP, 0, generator->grid(2).size() / 2); | |
| 126 | +} | |
| 127 | + | |
| 128 | +void GlScope::drawGraphDepth(int mode, int channel, int index) | |
| 129 | +{ | |
| 130 | + if (generator->channel(mode, channel, index).empty()) return; | |
| 131 | + QColor trColor; | |
| 132 | + if (mode == Dso::CHANNELMODE_VOLTAGE) | |
| 133 | + trColor = settings->view.color.screen.voltage[channel] | |
| 134 | + .darker(fadingFactor[index]); | |
| 135 | + else | |
| 136 | + trColor = settings->view.color.screen.spectrum[channel] | |
| 137 | + .darker(fadingFactor[index]); | |
| 138 | + glColor4f(trColor.redF(), trColor.greenF(), trColor.blueF(), | |
| 139 | + trColor.alphaF()); | |
| 140 | + glVertexPointer( | |
| 141 | + 2, GL_FLOAT, 0, | |
| 142 | + &generator->channel(mode, channel, index).front()); | |
| 143 | + glDrawArrays( | |
| 144 | + (this->settings->view.interpolation == Dso::INTERPOLATION_OFF) ? GL_POINTS : GL_LINE_STRIP, | |
| 145 | + 0, generator->channel(mode, channel, index).size() / | |
| 146 | + 2); | |
| 147 | +} | |
| 148 | + | |
| 149 | +void GlScope::drawGraph() | |
| 150 | +{ | |
| 79 | 151 | if (this->settings->view.antialiasing) { |
| 80 | - glEnable(GL_POINT_SMOOTH); | |
| 81 | - glEnable(GL_LINE_SMOOTH); | |
| 82 | - glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); | |
| 152 | + glEnable(GL_POINT_SMOOTH); | |
| 153 | + glEnable(GL_LINE_SMOOTH); | |
| 154 | + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); | |
| 83 | 155 | } |
| 84 | 156 | |
| 85 | 157 | // Apply zoom settings via matrix transformation |
| 86 | 158 | if (this->zoomed) { |
| 87 | - glPushMatrix(); | |
| 88 | - glScalef(DIVS_TIME / fabs(this->settings->scope.horizontal.marker[1] - | |
| 89 | - this->settings->scope.horizontal.marker[0]), | |
| 90 | - 1.0, 1.0); | |
| 91 | - glTranslatef(-(this->settings->scope.horizontal.marker[0] + | |
| 159 | + glPushMatrix(); | |
| 160 | + glScalef(DIVS_TIME / fabs(this->settings->scope.horizontal.marker[1] - | |
| 161 | + this->settings->scope.horizontal.marker[0]), | |
| 162 | + 1.0, 1.0); | |
| 163 | + glTranslatef(-(this->settings->scope.horizontal.marker[0] + | |
| 92 | 164 | this->settings->scope.horizontal.marker[1]) / |
| 93 | - 2, | |
| 94 | - 0.0, 0.0); | |
| 165 | + 2, | |
| 166 | + 0.0, 0.0); | |
| 95 | 167 | } |
| 96 | 168 | |
| 97 | 169 | // Values we need for the fading of the digital phosphor |
| 98 | - double *fadingFactor = new double[this->generator->digitalPhosphorDepth]; | |
| 99 | - fadingFactor[0] = 100; | |
| 100 | - double fadingRatio = pow(10.0, 2.0 / this->generator->digitalPhosphorDepth); | |
| 101 | - for (unsigned int index = 1; index < this->generator->digitalPhosphorDepth; | |
| 102 | - ++index) | |
| 103 | - fadingFactor[index] = fadingFactor[index - 1] * fadingRatio; | |
| 170 | + if ((int)fadingFactor.size() != this->settings->view.digitalPhosphorDepth) { | |
| 171 | + fadingFactor.resize((size_t)this->settings->view.digitalPhosphorDepth); | |
| 172 | + fadingFactor[0] = 100; | |
| 173 | + double fadingRatio = pow(10.0, 2.0 / this->settings->view.digitalPhosphorDepth); | |
| 174 | + for (unsigned index = 1; index < this->settings->view.digitalPhosphorDepth; ++index) | |
| 175 | + fadingFactor[index] = fadingFactor[index - 1] * fadingRatio; | |
| 176 | + } | |
| 104 | 177 | |
| 105 | 178 | switch (this->settings->scope.horizontal.format) { |
| 106 | 179 | case Dso::GRAPHFORMAT_TY: |
| 107 | - // Real and virtual channels | |
| 108 | - for (int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT; | |
| 109 | - ++mode) { | |
| 110 | - for (int channel = 0; channel < this->settings->scope.voltage.count(); | |
| 111 | - ++channel) { | |
| 112 | - if ((mode == Dso::CHANNELMODE_VOLTAGE) | |
| 113 | - ? this->settings->scope.voltage[channel].used | |
| 114 | - : this->settings->scope.spectrum[channel].used) { | |
| 115 | - // Draw graph for all available depths | |
| 116 | - for (int index = this->generator->digitalPhosphorDepth - 1; | |
| 117 | - index >= 0; index--) { | |
| 118 | - if (!this->generator->vaChannel[mode][channel][index].empty()) { | |
| 119 | - QColor trColor; | |
| 120 | - if (mode == Dso::CHANNELMODE_VOLTAGE) | |
| 121 | - trColor = settings->view.color.screen.voltage[channel] | |
| 122 | - .darker(fadingFactor[index]); | |
| 123 | - else | |
| 124 | - trColor = settings->view.color.screen.spectrum[channel] | |
| 125 | - .darker(fadingFactor[index]); | |
| 126 | - glColor4f(trColor.redF(), trColor.greenF(), trColor.blueF(), | |
| 127 | - trColor.alphaF()); | |
| 128 | - glVertexPointer( | |
| 129 | - 2, GL_FLOAT, 0, | |
| 130 | - &this->generator->vaChannel[mode][channel][index].front()); | |
| 131 | - glDrawArrays( | |
| 132 | - (this->settings->view.interpolation == | |
| 133 | - Dso::INTERPOLATION_OFF) | |
| 134 | - ? GL_POINTS | |
| 135 | - : GL_LINE_STRIP, | |
| 136 | - 0, this->generator->vaChannel[mode][channel][index].size() / | |
| 137 | - 2); | |
| 138 | - } | |
| 180 | + // Real and virtual channels | |
| 181 | + for (int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT; ++mode) { | |
| 182 | + for (int channel = 0; channel < this->settings->scope.voltage.count(); ++channel) { | |
| 183 | + if (!channelUsed(mode, channel)) | |
| 184 | + continue; | |
| 185 | + | |
| 186 | + // Draw graph for all available depths | |
| 187 | + for (int index = this->settings->view.digitalPhosphorDepth - 1; index >= 0; index--) { | |
| 188 | + drawGraphDepth(mode, channel, index); | |
| 189 | + } | |
| 139 | 190 | } |
| 140 | - } | |
| 141 | 191 | } |
| 142 | - } | |
| 143 | - break; | |
| 192 | + break; | |
| 144 | 193 | |
| 145 | 194 | case Dso::GRAPHFORMAT_XY: |
| 146 | - // Real and virtual channels | |
| 147 | - for (int channel = 0; channel < this->settings->scope.voltage.count() - 1; | |
| 148 | - channel += 2) { | |
| 149 | - if (this->settings->scope.voltage[channel].used) { | |
| 150 | - // Draw graph for all available depths | |
| 151 | - for (int index = this->generator->digitalPhosphorDepth - 1; | |
| 152 | - index >= 0; index--) { | |
| 153 | - if (!this->generator | |
| 154 | - ->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index] | |
| 155 | - .empty()) { | |
| 156 | - QColor trColor = settings->view.color.screen.voltage[channel] | |
| 157 | - .darker(fadingFactor[index]); | |
| 158 | - glColor4f(trColor.redF(), trColor.greenF(), trColor.blueF(), | |
| 159 | - trColor.alphaF()); | |
| 160 | - glVertexPointer( | |
| 161 | - 2, GL_FLOAT, 0, | |
| 162 | - &this->generator | |
| 163 | - ->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index] | |
| 164 | - .front()); | |
| 165 | - glDrawArrays( | |
| 166 | - (this->settings->view.interpolation == Dso::INTERPOLATION_OFF) | |
| 167 | - ? GL_POINTS | |
| 168 | - : GL_LINE_STRIP, | |
| 169 | - 0, | |
| 170 | - this->generator | |
| 171 | - ->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index] | |
| 172 | - .size() / | |
| 173 | - 2); | |
| 195 | + // Real and virtual channels | |
| 196 | + for (int channel = 0; channel < this->settings->scope.voltage.count() - 1; channel += 2) { | |
| 197 | + if (this->settings->scope.voltage[channel].used) { | |
| 198 | + for (int index = this->settings->view.digitalPhosphorDepth - 1; index >= 0; index--) { | |
| 199 | + drawGraphDepth(Dso::CHANNELMODE_VOLTAGE, channel, index); | |
| 200 | + } | |
| 174 | 201 | } |
| 175 | - } | |
| 176 | 202 | } |
| 177 | - } | |
| 178 | - break; | |
| 203 | + break; | |
| 179 | 204 | |
| 180 | 205 | default: |
| 181 | - break; | |
| 206 | + break; | |
| 182 | 207 | } |
| 183 | 208 | |
| 184 | - delete[] fadingFactor; | |
| 185 | - | |
| 186 | 209 | glDisable(GL_POINT_SMOOTH); |
| 187 | 210 | glDisable(GL_LINE_SMOOTH); |
| 188 | 211 | |
| 189 | 212 | if (this->zoomed) |
| 190 | - glPopMatrix(); | |
| 191 | - } | |
| 192 | - | |
| 193 | - if (!this->zoomed) { | |
| 194 | - // Draw vertical lines at marker positions | |
| 195 | - glEnable(GL_LINE_STIPPLE); | |
| 196 | - QColor trColor = settings->view.color.screen.markers; | |
| 197 | - glColor4f(trColor.redF(), trColor.greenF(), trColor.blueF(), | |
| 198 | - trColor.alphaF()); | |
| 199 | - | |
| 200 | - for (int marker = 0; marker < MARKER_COUNT; ++marker) { | |
| 201 | - if (!this->settings->scope.horizontal.marker_visible[marker]) | |
| 202 | - continue; | |
| 203 | - if (this->vaMarker[marker].size() != 4) { | |
| 204 | - this->vaMarker[marker].resize(2 * 2); | |
| 205 | - this->vaMarker[marker][1] = -DIVS_VOLTAGE; | |
| 206 | - this->vaMarker[marker][3] = DIVS_VOLTAGE; | |
| 207 | - } | |
| 208 | - | |
| 209 | - this->vaMarker[marker][0] = | |
| 210 | - this->settings->scope.horizontal.marker[marker]; | |
| 211 | - this->vaMarker[marker][2] = | |
| 212 | - this->settings->scope.horizontal.marker[marker]; | |
| 213 | - | |
| 214 | - glVertexPointer(2, GL_FLOAT, 0, &this->vaMarker[marker].front()); | |
| 215 | - glDrawArrays(GL_LINES, 0, this->vaMarker[marker].size() / 2); | |
| 216 | - } | |
| 217 | - | |
| 218 | - glDisable(GL_LINE_STIPPLE); | |
| 219 | - } | |
| 220 | - | |
| 221 | - // Draw grid | |
| 222 | - this->drawGrid(); | |
| 223 | -} | |
| 224 | - | |
| 225 | -/// \brief Resize the widget. | |
| 226 | -/// \param width The new width of the widget. | |
| 227 | -/// \param height The new height of the widget. | |
| 228 | -void GlScope::resizeGL(int width, int height) { | |
| 229 | - glViewport(0, 0, (GLint)width, (GLint)height); | |
| 230 | - | |
| 231 | - glMatrixMode(GL_PROJECTION); | |
| 232 | - | |
| 233 | - // Set axes to div-scale and apply correction for exact pixelization | |
| 234 | - glLoadIdentity(); | |
| 235 | - GLdouble pixelizationWidthCorrection = | |
| 236 | - (width > 0) ? (GLdouble)width / (width - 1) : 1; | |
| 237 | - GLdouble pixelizationHeightCorrection = | |
| 238 | - (height > 0) ? (GLdouble)height / (height - 1) : 1; | |
| 239 | - glOrtho(-(DIVS_TIME / 2) * pixelizationWidthCorrection, | |
| 240 | - (DIVS_TIME / 2) * pixelizationWidthCorrection, | |
| 241 | - -(DIVS_VOLTAGE / 2) * pixelizationHeightCorrection, | |
| 242 | - (DIVS_VOLTAGE / 2) * pixelizationHeightCorrection, -1.0, 1.0); | |
| 243 | - | |
| 244 | - glMatrixMode(GL_MODELVIEW); | |
| 245 | -} | |
| 246 | - | |
| 247 | -/// \brief Set the generator that provides the vertex arrays. | |
| 248 | -/// \param generator Pointer to the GlGenerator class. | |
| 249 | -void GlScope::setGenerator(GlGenerator *generator) { | |
| 250 | - if (this->generator) | |
| 251 | - disconnect(this->generator, SIGNAL(graphsGenerated()), this, | |
| 252 | - SLOT(update())); | |
| 253 | - this->generator = generator; | |
| 254 | - connect(this->generator, SIGNAL(graphsGenerated()), this, SLOT(update())); | |
| 213 | + glPopMatrix(); | |
| 255 | 214 | } |
| 256 | 215 | |
| 257 | -/// \brief Set the zoom mode for this GlScope. | |
| 258 | -/// \param zoomed true magnifies the area between the markers. | |
| 259 | -void GlScope::setZoomMode(bool zoomed) { this->zoomed = zoomed; } | |
| 260 | - | |
| 261 | -/// \brief Draw the grid. | |
| 262 | -void GlScope::drawGrid() { | |
| 263 | - glDisable(GL_POINT_SMOOTH); | |
| 264 | - glDisable(GL_LINE_SMOOTH); | |
| 265 | - | |
| 266 | - QColor color; | |
| 267 | - // Grid | |
| 268 | - color = this->settings->view.color.screen.grid; | |
| 269 | - glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); | |
| 270 | - glVertexPointer(2, GL_FLOAT, 0, &this->generator->vaGrid[0].front()); | |
| 271 | - glDrawArrays(GL_POINTS, 0, this->generator->vaGrid[0].size() / 2); | |
| 272 | - // Axes | |
| 273 | - color = settings->view.color.screen.axes; | |
| 274 | - glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); | |
| 275 | - glVertexPointer(2, GL_FLOAT, 0, &this->generator->vaGrid[1].front()); | |
| 276 | - glDrawArrays(GL_LINES, 0, this->generator->vaGrid[1].size() / 2); | |
| 277 | - // Border | |
| 278 | - color = settings->view.color.screen.border; | |
| 279 | - glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); | |
| 280 | - glVertexPointer(2, GL_FLOAT, 0, &this->generator->vaGrid[2].front()); | |
| 281 | - glDrawArrays(GL_LINE_LOOP, 0, this->generator->vaGrid[2].size() / 2); | |
| 216 | +bool GlScope::channelUsed(int mode, int channel) | |
| 217 | +{ | |
| 218 | + return (mode == Dso::CHANNELMODE_VOLTAGE) | |
| 219 | + ? settings->scope.voltage[channel].used | |
| 220 | + : settings->scope.spectrum[channel].used; | |
| 282 | 221 | } | ... | ... |
openhantek/src/glscope.h
| 1 | -//////////////////////////////////////////////////////////////////////////////// | |
| 2 | -// | |
| 3 | -// OpenHantek | |
| 4 | -/// \file glscope.h | |
| 5 | -/// \brief Declares the GlScope class. | |
| 6 | -// | |
| 7 | -// Copyright (C) 2008, 2009 Oleg Khudyakov | |
| 8 | -// prcoder@potrebitel.ru | |
| 9 | -// Copyright (C) 2010 Oliver Haag | |
| 10 | -// oliver.haag@gmail.com | |
| 11 | -// | |
| 12 | -// This program is free software: you can redistribute it and/or modify it | |
| 13 | -// under the terms of the GNU General Public License as published by the Free | |
| 14 | -// Software Foundation, either version 3 of the License, or (at your option) | |
| 15 | -// any later version. | |
| 16 | -// | |
| 17 | -// This program is distributed in the hope that it will be useful, but WITHOUT | |
| 18 | -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 19 | -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| 20 | -// more details. | |
| 21 | -// | |
| 22 | -// You should have received a copy of the GNU General Public License along with | |
| 23 | -// this program. If not, see <http://www.gnu.org/licenses/>. | |
| 24 | -// | |
| 25 | -//////////////////////////////////////////////////////////////////////////////// | |
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 26 | 2 | |
| 27 | -#ifndef GLSCOPE_H | |
| 28 | -#define GLSCOPE_H | |
| 3 | +#pragma once | |
| 29 | 4 | |
| 5 | +#include <array> | |
| 30 | 6 | #include <QtGlobal> |
| 7 | +#include <QMetaObject> | |
| 31 | 8 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) |
| 32 | 9 | #include <QOpenGLWidget> |
| 33 | 10 | using GL_WIDGET_CLASS = QOpenGLWidget; |
| ... | ... | @@ -46,28 +23,30 @@ class DsoSettings; |
| 46 | 23 | /// \class GlScope glscope.h |
| 47 | 24 | /// \brief OpenGL accelerated widget that displays the oscilloscope screen. |
| 48 | 25 | class GlScope : public GL_WIDGET_CLASS { |
| 49 | - Q_OBJECT | |
| 26 | + Q_OBJECT | |
| 50 | 27 | |
| 51 | 28 | public: |
| 52 | - GlScope(DsoSettings *settings, QWidget *parent = 0); | |
| 53 | - ~GlScope(); | |
| 29 | + /// \brief Initializes the scope widget. | |
| 30 | + /// \param settings The settings that should be used. | |
| 31 | + /// \param parent The parent widget. | |
| 32 | + GlScope(DsoSettings *settings, const GlGenerator *generator, QWidget *parent = 0); | |
| 54 | 33 | |
| 55 | - void setGenerator(GlGenerator *generator); | |
| 56 | - void setZoomMode(bool zoomed); | |
| 34 | + void setZoomMode(bool zoomed); | |
| 57 | 35 | |
| 58 | 36 | protected: |
| 59 | - void initializeGL(); | |
| 60 | - void paintGL(); | |
| 61 | - void resizeGL(int width, int height); | |
| 62 | - | |
| 63 | - void drawGrid(); | |
| 64 | - | |
| 37 | + void initializeGL() override; | |
| 38 | + void paintGL() override; | |
| 39 | + void resizeGL(int width, int height) override; | |
| 40 | + | |
| 41 | + void drawGrid(); | |
| 42 | + void drawGraphDepth(int mode, int channel, int index); | |
| 43 | + void drawGraph(); | |
| 44 | + bool channelUsed(int mode, int channel); | |
| 65 | 45 | private: |
| 66 | - GlGenerator *generator; | |
| 67 | - DsoSettings *settings; | |
| 46 | + DsoSettings *settings; | |
| 47 | + const GlGenerator *generator; | |
| 48 | + std::vector<double> fadingFactor; | |
| 68 | 49 | |
| 69 | - std::vector<GLfloat> vaMarker[2]; | |
| 70 | - bool zoomed; | |
| 50 | + std::vector<GLfloat> vaMarker[2]; | |
| 51 | + bool zoomed = false; | |
| 71 | 52 | }; |
| 72 | - | |
| 73 | -#endif | ... | ... |
openhantek/src/hantek/dsosamples.h
0 → 100644
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 2 | + | |
| 3 | +#pragma once | |
| 4 | + | |
| 5 | +#include <vector> | |
| 6 | +#include <QReadWriteLock> | |
| 7 | +#include <QReadLocker> | |
| 8 | +#include <QWriteLocker> | |
| 9 | + | |
| 10 | +struct DSOsamples { | |
| 11 | + std::vector<std::vector<double>> data; ///< Pointer to input data from device | |
| 12 | + double samplerate = 0.0; ///< The samplerate of the input data | |
| 13 | + bool append = false; ///< true, if waiting data should be appended | |
| 14 | + mutable QReadWriteLock lock; | |
| 15 | +}; | ... | ... |
openhantek/src/hantek/hantekdsocontrol.cpp
| ... | ... | @@ -40,6 +40,11 @@ const USBDevice *HantekDsoControl::getDevice() const |
| 40 | 40 | return device; |
| 41 | 41 | } |
| 42 | 42 | |
| 43 | +const DSOsamples &HantekDsoControl::getLastSamples() | |
| 44 | +{ | |
| 45 | + return result; | |
| 46 | +} | |
| 47 | + | |
| 43 | 48 | /// \brief Initializes the command buffers and lists. |
| 44 | 49 | /// \param parent The parent widget. |
| 45 | 50 | HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) { |
| ... | ... | @@ -73,24 +78,24 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) { |
| 73 | 78 | } |
| 74 | 79 | |
| 75 | 80 | // Set settings to default values |
| 76 | - this->settings.samplerate.limits = &(this->specification.samplerate.single); | |
| 77 | - this->settings.samplerate.downsampler = 1; | |
| 78 | - this->settings.samplerate.current = 1e8; | |
| 79 | - this->settings.trigger.position = 0; | |
| 80 | - this->settings.trigger.point = 0; | |
| 81 | - this->settings.trigger.mode = Dso::TRIGGERMODE_NORMAL; | |
| 82 | - this->settings.trigger.slope = Dso::SLOPE_POSITIVE; | |
| 83 | - this->settings.trigger.special = false; | |
| 84 | - this->settings.trigger.source = 0; | |
| 81 | + settings.samplerate.limits = &(this->specification.samplerate.single); | |
| 82 | + settings.samplerate.downsampler = 1; | |
| 83 | + settings.samplerate.current = 1e8; | |
| 84 | + settings.trigger.position = 0; | |
| 85 | + settings.trigger.point = 0; | |
| 86 | + settings.trigger.mode = Dso::TRIGGERMODE_NORMAL; | |
| 87 | + settings.trigger.slope = Dso::SLOPE_POSITIVE; | |
| 88 | + settings.trigger.special = false; | |
| 89 | + settings.trigger.source = 0; | |
| 85 | 90 | for (unsigned int channel = 0; channel < HANTEK_CHANNELS; ++channel) { |
| 86 | - this->settings.trigger.level[channel] = 0.0; | |
| 87 | - this->settings.voltage[channel].gain = 0; | |
| 88 | - this->settings.voltage[channel].offset = 0.0; | |
| 89 | - this->settings.voltage[channel].offsetReal = 0.0; | |
| 90 | - this->settings.voltage[channel].used = false; | |
| 91 | + settings.trigger.level[channel] = 0.0; | |
| 92 | + settings.voltage[channel].gain = 0; | |
| 93 | + settings.voltage[channel].offset = 0.0; | |
| 94 | + settings.voltage[channel].offsetReal = 0.0; | |
| 95 | + settings.voltage[channel].used = false; | |
| 91 | 96 | } |
| 92 | - this->settings.recordLengthId = 1; | |
| 93 | - this->settings.usedChannels = 0; | |
| 97 | + settings.recordLengthId = 1; | |
| 98 | + settings.usedChannels = 0; | |
| 94 | 99 | |
| 95 | 100 | // Special trigger sources |
| 96 | 101 | this->specialTriggerSources << tr("EXT") << tr("EXT/10"); |
| ... | ... | @@ -117,7 +122,7 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) { |
| 117 | 122 | this->lastTriggerMode = (Dso::TriggerMode)-1; |
| 118 | 123 | |
| 119 | 124 | // Sample buffers |
| 120 | - this->samples.resize(HANTEK_CHANNELS); | |
| 125 | + result.data.resize(HANTEK_CHANNELS); | |
| 121 | 126 | |
| 122 | 127 | this->previousSampleCount = 0; |
| 123 | 128 | |
| ... | ... | @@ -390,9 +395,9 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) { |
| 390 | 395 | this->specification.sampleSize = 8; |
| 391 | 396 | break; |
| 392 | 397 | } |
| 393 | - this->settings.recordLengthId = 1; | |
| 394 | - this->settings.samplerate.limits = &(this->specification.samplerate.single); | |
| 395 | - this->settings.samplerate.downsampler = 1; | |
| 398 | + settings.recordLengthId = 1; | |
| 399 | + settings.samplerate.limits = &(this->specification.samplerate.single); | |
| 400 | + settings.samplerate.downsampler = 1; | |
| 396 | 401 | this->previousSampleCount = 0; |
| 397 | 402 | |
| 398 | 403 | // Get channel level data |
| ... | ... | @@ -408,16 +413,16 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) { |
| 408 | 413 | |
| 409 | 414 | // Emit signals for initial settings |
| 410 | 415 | emit availableRecordLengthsChanged( |
| 411 | - this->settings.samplerate.limits->recordLengths); | |
| 416 | + settings.samplerate.limits->recordLengths); | |
| 412 | 417 | updateSamplerateLimits(); |
| 413 | - emit recordLengthChanged(this->settings.samplerate.limits | |
| 414 | - ->recordLengths[this->settings.recordLengthId]); | |
| 415 | - if (this->settings.samplerate.limits | |
| 416 | - ->recordLengths[this->settings.recordLengthId] != UINT_MAX) | |
| 417 | - emit recordTimeChanged((double)this->settings.samplerate.limits | |
| 418 | - ->recordLengths[this->settings.recordLengthId] / | |
| 419 | - this->settings.samplerate.current); | |
| 420 | - emit samplerateChanged(this->settings.samplerate.current); | |
| 418 | + emit recordLengthChanged(settings.samplerate.limits | |
| 419 | + ->recordLengths[settings.recordLengthId]); | |
| 420 | + if (settings.samplerate.limits | |
| 421 | + ->recordLengths[settings.recordLengthId] != UINT_MAX) | |
| 422 | + emit recordTimeChanged((double)settings.samplerate.limits | |
| 423 | + ->recordLengths[settings.recordLengthId] / | |
| 424 | + settings.samplerate.current); | |
| 425 | + emit samplerateChanged(settings.samplerate.current); | |
| 421 | 426 | |
| 422 | 427 | if (this->device->getUniqueModelID() == MODEL_DSO6022BE) { |
| 423 | 428 | QList<double> sampleSteps; |
| ... | ... | @@ -445,7 +450,7 @@ unsigned int HantekDsoControl::getChannelCount() { return HANTEK_CHANNELS; } |
| 445 | 450 | /// \brief Get available record lengths for this oscilloscope. |
| 446 | 451 | /// \return The number of physical channels, empty list for continuous. |
| 447 | 452 | QList<unsigned int> *HantekDsoControl::getAvailableRecordLengths() { |
| 448 | - return &this->settings.samplerate.limits->recordLengths; | |
| 453 | + return &settings.samplerate.limits->recordLengths; | |
| 449 | 454 | } |
| 450 | 455 | |
| 451 | 456 | /// \brief Get minimum samplerate for this oscilloscope. |
| ... | ... | @@ -459,7 +464,7 @@ double HantekDsoControl::getMinSamplerate() { |
| 459 | 464 | /// \return The maximum samplerate for the current configuration in S/s. |
| 460 | 465 | double HantekDsoControl::getMaxSamplerate() { |
| 461 | 466 | ControlSamplerateLimits *limits = |
| 462 | - (this->settings.usedChannels <= 1) | |
| 467 | + (settings.usedChannels <= 1) | |
| 463 | 468 | ? &this->specification.samplerate.multi |
| 464 | 469 | : &this->specification.samplerate.single; |
| 465 | 470 | return limits->max; |
| ... | ... | @@ -469,18 +474,18 @@ double HantekDsoControl::getMaxSamplerate() { |
| 469 | 474 | void HantekDsoControl::updateInterval() { |
| 470 | 475 | // Check the current oscilloscope state everytime 25% of the time the buffer |
| 471 | 476 | // should be refilled |
| 472 | - if (this->settings.samplerate.limits | |
| 473 | - ->recordLengths[this->settings.recordLengthId] == UINT_MAX) | |
| 477 | + if (settings.samplerate.limits | |
| 478 | + ->recordLengths[settings.recordLengthId] == UINT_MAX) | |
| 474 | 479 | cycleTime = (int)((double)this->device->getPacketSize() / |
| 475 | - ((this->settings.samplerate.limits == | |
| 480 | + ((settings.samplerate.limits == | |
| 476 | 481 | &this->specification.samplerate.multi) |
| 477 | 482 | ? 1 |
| 478 | 483 | : HANTEK_CHANNELS) / |
| 479 | - this->settings.samplerate.current * 250); | |
| 484 | + settings.samplerate.current * 250); | |
| 480 | 485 | else |
| 481 | - cycleTime = (int)((double)this->settings.samplerate.limits | |
| 482 | - ->recordLengths[this->settings.recordLengthId] / | |
| 483 | - this->settings.samplerate.current * 250); | |
| 486 | + cycleTime = (int)((double)settings.samplerate.limits | |
| 487 | + ->recordLengths[settings.recordLengthId] / | |
| 488 | + settings.samplerate.current * 250); | |
| 484 | 489 | |
| 485 | 490 | // Not more often than every 10 ms though but at least once every second |
| 486 | 491 | cycleTime = qBound(10, cycleTime, 1000); |
| ... | ... | @@ -519,7 +524,7 @@ int HantekDsoControl::getCaptureState() { |
| 519 | 524 | if (errorCode < 0) |
| 520 | 525 | return errorCode; |
| 521 | 526 | |
| 522 | - this->settings.trigger.point = | |
| 527 | + settings.trigger.point = | |
| 523 | 528 | this->calculateTriggerPoint(response.getTriggerPoint()); |
| 524 | 529 | |
| 525 | 530 | return (int)response.getCaptureState(); |
| ... | ... | @@ -571,192 +576,194 @@ int HantekDsoControl::getSamples(bool process) { |
| 571 | 576 | return errorCode; |
| 572 | 577 | |
| 573 | 578 | // Process the data only if we want it |
| 574 | - if (process) { | |
| 575 | - // How much data did we really receive? | |
| 576 | - dataLength = errorCode; | |
| 577 | - if (this->specification.sampleSize > 8) | |
| 578 | - totalSampleCount = dataLength / 2; | |
| 579 | - else | |
| 580 | - totalSampleCount = dataLength; | |
| 579 | + if (!process) | |
| 580 | + return LIBUSB_SUCCESS; | |
| 581 | 581 | |
| 582 | - QMutexLocker locker(&samplesMutex); | |
| 582 | + // How much data did we really receive? | |
| 583 | + dataLength = errorCode; | |
| 584 | + if (this->specification.sampleSize > 8) | |
| 585 | + totalSampleCount = dataLength / 2; | |
| 586 | + else | |
| 587 | + totalSampleCount = dataLength; | |
| 588 | + | |
| 589 | + // Convert channel data | |
| 590 | + if (fastRate) { | |
| 591 | + QWriteLocker locker(&result.lock); | |
| 592 | + result.samplerate = settings.samplerate.current; | |
| 593 | + result.append = settings.samplerate.limits->recordLengths[settings.recordLengthId] == UINT_MAX; | |
| 594 | + | |
| 595 | + // Fast rate mode, one channel is using all buffers | |
| 596 | + sampleCount = totalSampleCount; | |
| 597 | + int channel = 0; | |
| 598 | + for (; channel < HANTEK_CHANNELS; ++channel) { | |
| 599 | + if (settings.voltage[channel].used) | |
| 600 | + break; | |
| 601 | + } | |
| 583 | 602 | |
| 584 | - // Convert channel data | |
| 585 | - if (fastRate) { | |
| 586 | - // Fast rate mode, one channel is using all buffers | |
| 587 | - sampleCount = totalSampleCount; | |
| 588 | - int channel = 0; | |
| 589 | - for (; channel < HANTEK_CHANNELS; ++channel) { | |
| 590 | - if (this->settings.voltage[channel].used) | |
| 591 | - break; | |
| 592 | - } | |
| 603 | + // Clear unused channels | |
| 604 | + for (int channelCounter = 0; channelCounter < HANTEK_CHANNELS; | |
| 605 | + ++channelCounter) | |
| 606 | + if (channelCounter != channel) { | |
| 593 | 607 | |
| 594 | - // Clear unused channels | |
| 595 | - for (int channelCounter = 0; channelCounter < HANTEK_CHANNELS; | |
| 596 | - ++channelCounter) | |
| 597 | - if (channelCounter != channel) { | |
| 608 | + result.data[channelCounter].clear(); | |
| 609 | + } | |
| 598 | 610 | |
| 599 | - this->samples[channelCounter].clear(); | |
| 611 | + if (channel < HANTEK_CHANNELS) { | |
| 612 | + // Resize sample vector | |
| 613 | + result.data[channel].resize(sampleCount); | |
| 614 | + | |
| 615 | + // Convert data from the oscilloscope and write it into the sample | |
| 616 | + // buffer | |
| 617 | + unsigned int bufferPosition = settings.trigger.point * 2; | |
| 618 | + if (this->specification.sampleSize > 8) { | |
| 619 | + // Additional most significant bits after the normal data | |
| 620 | + unsigned int extraBitsPosition; // Track the position of the extra | |
| 621 | + // bits in the additional byte | |
| 622 | + unsigned int extraBitsSize = | |
| 623 | + this->specification.sampleSize - 8; // Number of extra bits | |
| 624 | + unsigned short int extraBitsMask = | |
| 625 | + (0x00ff << extraBitsSize) & | |
| 626 | + 0xff00; // Mask for extra bits extraction | |
| 627 | + | |
| 628 | + for (unsigned int realPosition = 0; realPosition < sampleCount; | |
| 629 | + ++realPosition, ++bufferPosition) { | |
| 630 | + if (bufferPosition >= sampleCount) | |
| 631 | + bufferPosition %= sampleCount; | |
| 632 | + | |
| 633 | + extraBitsPosition = bufferPosition % HANTEK_CHANNELS; | |
| 634 | + | |
| 635 | + result.data[channel][realPosition] = | |
| 636 | + ((double)((unsigned short int)data[bufferPosition] + | |
| 637 | + (((unsigned short int) | |
| 638 | + data[sampleCount + bufferPosition - | |
| 639 | + extraBitsPosition] | |
| 640 | + << (8 - | |
| 641 | + (HANTEK_CHANNELS - 1 - extraBitsPosition) * | |
| 642 | + extraBitsSize)) & | |
| 643 | + extraBitsMask)) / | |
| 644 | + this->specification | |
| 645 | + .voltageLimit[channel] | |
| 646 | + [settings.voltage[channel].gain] - | |
| 647 | + settings.voltage[channel].offsetReal) * | |
| 648 | + this->specification | |
| 649 | + .gainSteps[settings.voltage[channel].gain]; | |
| 600 | 650 | } |
| 601 | - | |
| 602 | - if (channel < HANTEK_CHANNELS) { | |
| 651 | + } else { | |
| 652 | + for (unsigned int realPosition = 0; realPosition < sampleCount; | |
| 653 | + ++realPosition, ++bufferPosition) { | |
| 654 | + if (bufferPosition >= sampleCount) | |
| 655 | + bufferPosition %= sampleCount; | |
| 656 | + | |
| 657 | + double dataBuf = (double)((int)data[bufferPosition]); | |
| 658 | + result.data[channel][realPosition] = | |
| 659 | + (dataBuf / | |
| 660 | + this->specification | |
| 661 | + .voltageLimit[channel] | |
| 662 | + [settings.voltage[channel].gain] - | |
| 663 | + settings.voltage[channel].offsetReal) * | |
| 664 | + this->specification | |
| 665 | + .gainSteps[settings.voltage[channel].gain]; | |
| 666 | + } | |
| 667 | + } | |
| 668 | + } | |
| 669 | + } else { | |
| 670 | + QWriteLocker locker(&result.lock); | |
| 671 | + result.samplerate = settings.samplerate.current; | |
| 672 | + result.append = settings.samplerate.limits->recordLengths[settings.recordLengthId] == UINT_MAX; | |
| 673 | + | |
| 674 | + // Normal mode, channels are using their separate buffers | |
| 675 | + sampleCount = totalSampleCount / HANTEK_CHANNELS; | |
| 676 | + // if device is 6022BE, drop heading & trailing samples | |
| 677 | + if (this->device->getUniqueModelID() == MODEL_DSO6022BE) | |
| 678 | + sampleCount -= (DROP_DSO6022_HEAD + DROP_DSO6022_TAIL); | |
| 679 | + for (int channel = 0; channel < HANTEK_CHANNELS; ++channel) { | |
| 680 | + if (settings.voltage[channel].used) { | |
| 603 | 681 | // Resize sample vector |
| 604 | - this->samples[channel].resize(sampleCount); | |
| 682 | + if (result.data[channel].size() < sampleCount) { | |
| 683 | + result.data[channel].resize(sampleCount); | |
| 684 | + } | |
| 605 | 685 | |
| 606 | 686 | // Convert data from the oscilloscope and write it into the sample |
| 607 | 687 | // buffer |
| 608 | - unsigned int bufferPosition = this->settings.trigger.point * 2; | |
| 688 | + unsigned int bufferPosition = settings.trigger.point * 2; | |
| 609 | 689 | if (this->specification.sampleSize > 8) { |
| 610 | 690 | // Additional most significant bits after the normal data |
| 611 | - unsigned int extraBitsPosition; // Track the position of the extra | |
| 612 | - // bits in the additional byte | |
| 613 | 691 | unsigned int extraBitsSize = |
| 614 | 692 | this->specification.sampleSize - 8; // Number of extra bits |
| 615 | 693 | unsigned short int extraBitsMask = |
| 616 | 694 | (0x00ff << extraBitsSize) & |
| 617 | 695 | 0xff00; // Mask for extra bits extraction |
| 696 | + unsigned int extraBitsIndex = | |
| 697 | + 8 - | |
| 698 | + channel * 2; // Bit position offset for extra bits extraction | |
| 618 | 699 | |
| 619 | 700 | for (unsigned int realPosition = 0; realPosition < sampleCount; |
| 620 | - ++realPosition, ++bufferPosition) { | |
| 621 | - if (bufferPosition >= sampleCount) | |
| 622 | - bufferPosition %= sampleCount; | |
| 623 | - | |
| 624 | - extraBitsPosition = bufferPosition % HANTEK_CHANNELS; | |
| 625 | - | |
| 626 | - this->samples[channel][realPosition] = | |
| 627 | - ((double)((unsigned short int)data[bufferPosition] + | |
| 628 | - (((unsigned short int) | |
| 629 | - data[sampleCount + bufferPosition - | |
| 630 | - extraBitsPosition] | |
| 631 | - << (8 - | |
| 632 | - (HANTEK_CHANNELS - 1 - extraBitsPosition) * | |
| 633 | - extraBitsSize)) & | |
| 634 | - extraBitsMask)) / | |
| 701 | + ++realPosition, bufferPosition += HANTEK_CHANNELS) { | |
| 702 | + if (bufferPosition >= totalSampleCount) | |
| 703 | + bufferPosition %= totalSampleCount; | |
| 704 | + | |
| 705 | + result.data[channel][realPosition] = | |
| 706 | + ((double)((unsigned short int) | |
| 707 | + data[bufferPosition + HANTEK_CHANNELS - 1 - | |
| 708 | + channel] + | |
| 709 | + (((unsigned short int) | |
| 710 | + data[totalSampleCount + bufferPosition] | |
| 711 | + << extraBitsIndex) & | |
| 712 | + extraBitsMask)) / | |
| 635 | 713 | this->specification |
| 636 | 714 | .voltageLimit[channel] |
| 637 | - [this->settings.voltage[channel].gain] - | |
| 638 | - this->settings.voltage[channel].offsetReal) * | |
| 715 | + [settings.voltage[channel].gain] - | |
| 716 | + settings.voltage[channel].offsetReal) * | |
| 639 | 717 | this->specification |
| 640 | - .gainSteps[this->settings.voltage[channel].gain]; | |
| 718 | + .gainSteps[settings.voltage[channel].gain]; | |
| 641 | 719 | } |
| 642 | 720 | } else { |
| 721 | + if (this->device->getUniqueModelID() == MODEL_DSO6022BE) { | |
| 722 | + bufferPosition += channel; | |
| 723 | + // if device is 6022BE, offset DROP_DSO6022_HEAD incrementally | |
| 724 | + bufferPosition += DROP_DSO6022_HEAD * 2; | |
| 725 | + } else | |
| 726 | + bufferPosition += HANTEK_CHANNELS - 1 - channel; | |
| 727 | + | |
| 643 | 728 | for (unsigned int realPosition = 0; realPosition < sampleCount; |
| 644 | - ++realPosition, ++bufferPosition) { | |
| 645 | - if (bufferPosition >= sampleCount) | |
| 646 | - bufferPosition %= sampleCount; | |
| 647 | - | |
| 648 | - double dataBuf = (double)((int)data[bufferPosition]); | |
| 649 | - this->samples[channel][realPosition] = | |
| 650 | - (dataBuf / | |
| 651 | - this->specification | |
| 652 | - .voltageLimit[channel] | |
| 653 | - [this->settings.voltage[channel].gain] - | |
| 654 | - this->settings.voltage[channel].offsetReal) * | |
| 655 | - this->specification | |
| 656 | - .gainSteps[this->settings.voltage[channel].gain]; | |
| 657 | - } | |
| 658 | - } | |
| 659 | - } | |
| 660 | - } else { | |
| 661 | - // Normal mode, channels are using their separate buffers | |
| 662 | - sampleCount = totalSampleCount / HANTEK_CHANNELS; | |
| 663 | - // if device is 6022BE, drop heading & trailing samples | |
| 664 | - if (this->device->getUniqueModelID() == MODEL_DSO6022BE) | |
| 665 | - sampleCount -= (DROP_DSO6022_HEAD + DROP_DSO6022_TAIL); | |
| 666 | - for (int channel = 0; channel < HANTEK_CHANNELS; ++channel) { | |
| 667 | - if (this->settings.voltage[channel].used) { | |
| 668 | - // Resize sample vector | |
| 669 | - if (samples[channel].size() < sampleCount) { | |
| 670 | - this->samples[channel].resize(sampleCount); | |
| 671 | - } | |
| 729 | + ++realPosition, bufferPosition += HANTEK_CHANNELS) { | |
| 730 | + if (bufferPosition >= totalSampleCount) | |
| 731 | + bufferPosition %= totalSampleCount; | |
| 672 | 732 | |
| 673 | - // Convert data from the oscilloscope and write it into the sample | |
| 674 | - // buffer | |
| 675 | - unsigned int bufferPosition = this->settings.trigger.point * 2; | |
| 676 | - if (this->specification.sampleSize > 8) { | |
| 677 | - // Additional most significant bits after the normal data | |
| 678 | - unsigned int extraBitsSize = | |
| 679 | - this->specification.sampleSize - 8; // Number of extra bits | |
| 680 | - unsigned short int extraBitsMask = | |
| 681 | - (0x00ff << extraBitsSize) & | |
| 682 | - 0xff00; // Mask for extra bits extraction | |
| 683 | - unsigned int extraBitsIndex = | |
| 684 | - 8 - | |
| 685 | - channel * 2; // Bit position offset for extra bits extraction | |
| 686 | - | |
| 687 | - for (unsigned int realPosition = 0; realPosition < sampleCount; | |
| 688 | - ++realPosition, bufferPosition += HANTEK_CHANNELS) { | |
| 689 | - if (bufferPosition >= totalSampleCount) | |
| 690 | - bufferPosition %= totalSampleCount; | |
| 691 | - | |
| 692 | - this->samples[channel][realPosition] = | |
| 693 | - ((double)((unsigned short int) | |
| 694 | - data[bufferPosition + HANTEK_CHANNELS - 1 - | |
| 695 | - channel] + | |
| 696 | - (((unsigned short int) | |
| 697 | - data[totalSampleCount + bufferPosition] | |
| 698 | - << extraBitsIndex) & | |
| 699 | - extraBitsMask)) / | |
| 733 | + if (this->device->getUniqueModelID() == MODEL_DSO6022BE) { | |
| 734 | + double dataBuf = (double)((int)(data[bufferPosition] - 0x83)); | |
| 735 | + result.data[channel][realPosition] = | |
| 736 | + (dataBuf / | |
| 737 | + this->specification | |
| 738 | + .voltageLimit[channel] | |
| 739 | + [settings.voltage[channel].gain]) * | |
| 700 | 740 | this->specification |
| 701 | - .voltageLimit[channel] | |
| 702 | - [this->settings.voltage[channel].gain] - | |
| 703 | - this->settings.voltage[channel].offsetReal) * | |
| 741 | + .gainSteps[settings.voltage[channel].gain]; | |
| 742 | + } else { | |
| 743 | + double dataBuf = (double)((int)(data[bufferPosition])); | |
| 744 | + result.data[channel][realPosition] = | |
| 745 | + (dataBuf / | |
| 746 | + this->specification.voltageLimit | |
| 747 | + [channel][settings.voltage[channel].gain] - | |
| 748 | + settings.voltage[channel].offsetReal) * | |
| 704 | 749 | this->specification |
| 705 | - .gainSteps[this->settings.voltage[channel].gain]; | |
| 706 | - } | |
| 707 | - } else { | |
| 708 | - if (this->device->getUniqueModelID() == MODEL_DSO6022BE) { | |
| 709 | - bufferPosition += channel; | |
| 710 | - // if device is 6022BE, offset DROP_DSO6022_HEAD incrementally | |
| 711 | - bufferPosition += DROP_DSO6022_HEAD * 2; | |
| 712 | - } else | |
| 713 | - bufferPosition += HANTEK_CHANNELS - 1 - channel; | |
| 714 | - | |
| 715 | - for (unsigned int realPosition = 0; realPosition < sampleCount; | |
| 716 | - ++realPosition, bufferPosition += HANTEK_CHANNELS) { | |
| 717 | - if (bufferPosition >= totalSampleCount) | |
| 718 | - bufferPosition %= totalSampleCount; | |
| 719 | - | |
| 720 | - if (this->device->getUniqueModelID() == MODEL_DSO6022BE) { | |
| 721 | - double dataBuf = (double)((int)(data[bufferPosition] - 0x83)); | |
| 722 | - this->samples[channel][realPosition] = | |
| 723 | - (dataBuf / | |
| 724 | - this->specification | |
| 725 | - .voltageLimit[channel] | |
| 726 | - [this->settings.voltage[channel].gain]) * | |
| 727 | - this->specification | |
| 728 | - .gainSteps[this->settings.voltage[channel].gain]; | |
| 729 | - } else { | |
| 730 | - double dataBuf = (double)((int)(data[bufferPosition])); | |
| 731 | - this->samples[channel][realPosition] = | |
| 732 | - (dataBuf / | |
| 733 | - this->specification.voltageLimit | |
| 734 | - [channel][this->settings.voltage[channel].gain] - | |
| 735 | - this->settings.voltage[channel].offsetReal) * | |
| 736 | - this->specification | |
| 737 | - .gainSteps[this->settings.voltage[channel].gain]; | |
| 738 | - } | |
| 750 | + .gainSteps[settings.voltage[channel].gain]; | |
| 739 | 751 | } |
| 740 | 752 | } |
| 741 | - } else { | |
| 742 | - // Clear unused channels | |
| 743 | - this->samples[channel].clear(); | |
| 744 | 753 | } |
| 754 | + } else { | |
| 755 | + // Clear unused channels | |
| 756 | + result.data[channel].clear(); | |
| 745 | 757 | } |
| 746 | 758 | } |
| 747 | - | |
| 748 | -#ifdef DEBUG | |
| 749 | - static unsigned int id = 0; | |
| 750 | - ++id; | |
| 751 | - timestampDebug(QString("Received packet %1").arg(id)); | |
| 752 | -#endif | |
| 753 | - emit samplesAvailable( | |
| 754 | - &(this->samples), this->settings.samplerate.current, | |
| 755 | - this->settings.samplerate.limits | |
| 756 | - ->recordLengths[this->settings.recordLengthId] == UINT_MAX, | |
| 757 | - &(this->samplesMutex)); | |
| 758 | 759 | } |
| 759 | 760 | |
| 761 | + static unsigned int id = 0; | |
| 762 | + ++id; | |
| 763 | + timestampDebug(QString("Received packet %1").arg(id)); | |
| 764 | + | |
| 765 | + emit samplesAvailable(); | |
| 766 | + | |
| 760 | 767 | return errorCode; |
| 761 | 768 | } |
| 762 | 769 | |
| ... | ... | @@ -787,18 +794,18 @@ double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate, |
| 787 | 794 | // Get downsampling factor that would provide the requested rate |
| 788 | 795 | double bestDownsampler = |
| 789 | 796 | (double)limits->base / |
| 790 | - this->specification.bufferDividers[this->settings.recordLengthId] / | |
| 797 | + this->specification.bufferDividers[settings.recordLengthId] / | |
| 791 | 798 | samplerate; |
| 792 | 799 | // Base samplerate sufficient, or is the maximum better? |
| 793 | 800 | if (bestDownsampler < 1.0 && |
| 794 | 801 | (samplerate <= limits->max / |
| 795 | 802 | this->specification |
| 796 | - .bufferDividers[this->settings.recordLengthId] || | |
| 803 | + .bufferDividers[settings.recordLengthId] || | |
| 797 | 804 | !maximum)) { |
| 798 | 805 | bestDownsampler = 0.0; |
| 799 | 806 | bestSamplerate = |
| 800 | 807 | limits->max / |
| 801 | - this->specification.bufferDividers[this->settings.recordLengthId]; | |
| 808 | + this->specification.bufferDividers[settings.recordLengthId]; | |
| 802 | 809 | } else { |
| 803 | 810 | switch (this->specification.command.bulk.setSamplerate) { |
| 804 | 811 | case BULK_SETTRIGGERANDSAMPLERATE: |
| ... | ... | @@ -866,7 +873,7 @@ double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate, |
| 866 | 873 | |
| 867 | 874 | bestSamplerate = |
| 868 | 875 | limits->base / bestDownsampler / |
| 869 | - this->specification.bufferDividers[this->settings.recordLengthId]; | |
| 876 | + this->specification.bufferDividers[settings.recordLengthId]; | |
| 870 | 877 | } |
| 871 | 878 | |
| 872 | 879 | if (downsampler) |
| ... | ... | @@ -878,11 +885,8 @@ double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate, |
| 878 | 885 | /// \param fastRate Is set to the state of the fast rate mode when provided. |
| 879 | 886 | /// \return The total number of samples the scope should return. |
| 880 | 887 | unsigned int HantekDsoControl::getSampleCount(bool *fastRate) { |
| 881 | - unsigned int totalSampleCount = | |
| 882 | - this->settings.samplerate.limits | |
| 883 | - ->recordLengths[this->settings.recordLengthId]; | |
| 884 | - bool fastRateEnabled = | |
| 885 | - this->settings.samplerate.limits == &this->specification.samplerate.multi; | |
| 888 | + unsigned int totalSampleCount = settings.samplerate.limits->recordLengths[settings.recordLengthId]; | |
| 889 | + bool fastRateEnabled = settings.samplerate.limits == &this->specification.samplerate.multi; | |
| 886 | 890 | |
| 887 | 891 | if (totalSampleCount == UINT_MAX) { |
| 888 | 892 | // Roll mode |
| ... | ... | @@ -905,7 +909,7 @@ unsigned int HantekDsoControl::getSampleCount(bool *fastRate) { |
| 905 | 909 | /// \return The record length that has been set, 0 on error. |
| 906 | 910 | unsigned int HantekDsoControl::updateRecordLength(unsigned int index) { |
| 907 | 911 | if (index >= |
| 908 | - (unsigned int)this->settings.samplerate.limits->recordLengths.size()) | |
| 912 | + (unsigned int)settings.samplerate.limits->recordLengths.size()) | |
| 909 | 913 | return 0; |
| 910 | 914 | |
| 911 | 915 | switch (this->specification.command.bulk.setRecordLength) { |
| ... | ... | @@ -947,9 +951,9 @@ unsigned int HantekDsoControl::updateRecordLength(unsigned int index) { |
| 947 | 951 | // Check if the divider has changed and adapt samplerate limits accordingly |
| 948 | 952 | bool bDividerChanged = |
| 949 | 953 | this->specification.bufferDividers[index] != |
| 950 | - this->specification.bufferDividers[this->settings.recordLengthId]; | |
| 954 | + this->specification.bufferDividers[settings.recordLengthId]; | |
| 951 | 955 | |
| 952 | - this->settings.recordLengthId = index; | |
| 956 | + settings.recordLengthId = index; | |
| 953 | 957 | |
| 954 | 958 | if (bDividerChanged) { |
| 955 | 959 | this->updateSamplerateLimits(); |
| ... | ... | @@ -958,7 +962,7 @@ unsigned int HantekDsoControl::updateRecordLength(unsigned int index) { |
| 958 | 962 | this->restoreTargets(); |
| 959 | 963 | } |
| 960 | 964 | |
| 961 | - return this->settings.samplerate.limits->recordLengths[index]; | |
| 965 | + return settings.samplerate.limits->recordLengths[index]; | |
| 962 | 966 | } |
| 963 | 967 | |
| 964 | 968 | /// \brief Sets the samplerate based on the parameters calculated by |
| ... | ... | @@ -1068,49 +1072,49 @@ unsigned int HantekDsoControl::updateSamplerate(unsigned int downsampler, |
| 1068 | 1072 | } |
| 1069 | 1073 | |
| 1070 | 1074 | // Update settings |
| 1071 | - bool fastRateChanged = fastRate != (this->settings.samplerate.limits == | |
| 1075 | + bool fastRateChanged = fastRate != (settings.samplerate.limits == | |
| 1072 | 1076 | &this->specification.samplerate.multi); |
| 1073 | 1077 | if (fastRateChanged) { |
| 1074 | - this->settings.samplerate.limits = limits; | |
| 1078 | + settings.samplerate.limits = limits; | |
| 1075 | 1079 | } |
| 1076 | 1080 | |
| 1077 | - this->settings.samplerate.downsampler = downsampler; | |
| 1081 | + settings.samplerate.downsampler = downsampler; | |
| 1078 | 1082 | if (downsampler) |
| 1079 | - this->settings.samplerate.current = | |
| 1080 | - this->settings.samplerate.limits->base / | |
| 1081 | - this->specification.bufferDividers[this->settings.recordLengthId] / | |
| 1083 | + settings.samplerate.current = | |
| 1084 | + settings.samplerate.limits->base / | |
| 1085 | + this->specification.bufferDividers[settings.recordLengthId] / | |
| 1082 | 1086 | downsampler; |
| 1083 | 1087 | else |
| 1084 | - this->settings.samplerate.current = | |
| 1085 | - this->settings.samplerate.limits->max / | |
| 1086 | - this->specification.bufferDividers[this->settings.recordLengthId]; | |
| 1088 | + settings.samplerate.current = | |
| 1089 | + settings.samplerate.limits->max / | |
| 1090 | + this->specification.bufferDividers[settings.recordLengthId]; | |
| 1087 | 1091 | |
| 1088 | 1092 | // Update dependencies |
| 1089 | - this->setPretriggerPosition(this->settings.trigger.position); | |
| 1093 | + this->setPretriggerPosition(settings.trigger.position); | |
| 1090 | 1094 | |
| 1091 | 1095 | // Emit signals for changed settings |
| 1092 | 1096 | if (fastRateChanged) { |
| 1093 | 1097 | emit availableRecordLengthsChanged( |
| 1094 | - this->settings.samplerate.limits->recordLengths); | |
| 1098 | + settings.samplerate.limits->recordLengths); | |
| 1095 | 1099 | emit recordLengthChanged( |
| 1096 | - this->settings.samplerate.limits | |
| 1097 | - ->recordLengths[this->settings.recordLengthId]); | |
| 1100 | + settings.samplerate.limits | |
| 1101 | + ->recordLengths[settings.recordLengthId]); | |
| 1098 | 1102 | } |
| 1099 | 1103 | |
| 1100 | 1104 | // Check for Roll mode |
| 1101 | - if (this->settings.samplerate.limits | |
| 1102 | - ->recordLengths[this->settings.recordLengthId] != UINT_MAX) | |
| 1103 | - emit recordTimeChanged((double)this->settings.samplerate.limits | |
| 1104 | - ->recordLengths[this->settings.recordLengthId] / | |
| 1105 | - this->settings.samplerate.current); | |
| 1106 | - emit samplerateChanged(this->settings.samplerate.current); | |
| 1105 | + if (settings.samplerate.limits | |
| 1106 | + ->recordLengths[settings.recordLengthId] != UINT_MAX) | |
| 1107 | + emit recordTimeChanged((double)settings.samplerate.limits | |
| 1108 | + ->recordLengths[settings.recordLengthId] / | |
| 1109 | + settings.samplerate.current); | |
| 1110 | + emit samplerateChanged(settings.samplerate.current); | |
| 1107 | 1111 | |
| 1108 | 1112 | return downsampler; |
| 1109 | 1113 | } |
| 1110 | 1114 | |
| 1111 | 1115 | /// \brief Restore the samplerate/timebase targets after divider updates. |
| 1112 | 1116 | void HantekDsoControl::restoreTargets() { |
| 1113 | - if (this->settings.samplerate.target.samplerateSet) | |
| 1117 | + if (settings.samplerate.target.samplerateSet) | |
| 1114 | 1118 | this->setSamplerate(); |
| 1115 | 1119 | else |
| 1116 | 1120 | this->setRecordTime(); |
| ... | ... | @@ -1121,15 +1125,15 @@ void HantekDsoControl::updateSamplerateLimits() { |
| 1121 | 1125 | // Works only if the minimum samplerate for normal mode is lower than for fast |
| 1122 | 1126 | // rate mode, which is the case for all models |
| 1123 | 1127 | ControlSamplerateLimits *limits = |
| 1124 | - (this->settings.usedChannels <= 1) | |
| 1128 | + (settings.usedChannels <= 1) | |
| 1125 | 1129 | ? &this->specification.samplerate.multi |
| 1126 | 1130 | : &this->specification.samplerate.single; |
| 1127 | 1131 | emit samplerateLimitsChanged( |
| 1128 | 1132 | (double)this->specification.samplerate.single.base / |
| 1129 | 1133 | this->specification.samplerate.single.maxDownsampler / |
| 1130 | - this->specification.bufferDividers[this->settings.recordLengthId], | |
| 1134 | + this->specification.bufferDividers[settings.recordLengthId], | |
| 1131 | 1135 | limits->max / |
| 1132 | - this->specification.bufferDividers[this->settings.recordLengthId]); | |
| 1136 | + this->specification.bufferDividers[settings.recordLengthId]); | |
| 1133 | 1137 | } |
| 1134 | 1138 | |
| 1135 | 1139 | /// \brief Sets the size of the oscilloscopes sample buffer. |
| ... | ... | @@ -1143,12 +1147,12 @@ unsigned int HantekDsoControl::setRecordLength(unsigned int index) { |
| 1143 | 1147 | return 0; |
| 1144 | 1148 | |
| 1145 | 1149 | this->restoreTargets(); |
| 1146 | - this->setPretriggerPosition(this->settings.trigger.position); | |
| 1150 | + this->setPretriggerPosition(settings.trigger.position); | |
| 1147 | 1151 | |
| 1148 | - emit recordLengthChanged(this->settings.samplerate.limits | |
| 1149 | - ->recordLengths[this->settings.recordLengthId]); | |
| 1150 | - return this->settings.samplerate.limits | |
| 1151 | - ->recordLengths[this->settings.recordLengthId]; | |
| 1152 | + emit recordLengthChanged(settings.samplerate.limits | |
| 1153 | + ->recordLengths[settings.recordLengthId]); | |
| 1154 | + return settings.samplerate.limits | |
| 1155 | + ->recordLengths[settings.recordLengthId]; | |
| 1152 | 1156 | } |
| 1153 | 1157 | |
| 1154 | 1158 | /// \brief Sets the samplerate of the oscilloscope. |
| ... | ... | @@ -1160,20 +1164,20 @@ double HantekDsoControl::setSamplerate(double samplerate) { |
| 1160 | 1164 | return 0.0; |
| 1161 | 1165 | |
| 1162 | 1166 | if (samplerate == 0.0) { |
| 1163 | - samplerate = this->settings.samplerate.target.samplerate; | |
| 1167 | + samplerate = settings.samplerate.target.samplerate; | |
| 1164 | 1168 | } else { |
| 1165 | - this->settings.samplerate.target.samplerate = samplerate; | |
| 1166 | - this->settings.samplerate.target.samplerateSet = true; | |
| 1169 | + settings.samplerate.target.samplerate = samplerate; | |
| 1170 | + settings.samplerate.target.samplerateSet = true; | |
| 1167 | 1171 | } |
| 1168 | 1172 | |
| 1169 | 1173 | if (this->device->getUniqueModelID() != MODEL_DSO6022BE) { |
| 1170 | 1174 | // When possible, enable fast rate if it is required to reach the requested |
| 1171 | 1175 | // samplerate |
| 1172 | 1176 | bool fastRate = |
| 1173 | - (this->settings.usedChannels <= 1) && | |
| 1177 | + (settings.usedChannels <= 1) && | |
| 1174 | 1178 | (samplerate > |
| 1175 | 1179 | this->specification.samplerate.single.max / |
| 1176 | - this->specification.bufferDividers[this->settings.recordLengthId]); | |
| 1180 | + this->specification.bufferDividers[settings.recordLengthId]); | |
| 1177 | 1181 | |
| 1178 | 1182 | // What is the nearest, at least as high samplerate the scope can provide? |
| 1179 | 1183 | unsigned int downsampler = 0; |
| ... | ... | @@ -1196,18 +1200,18 @@ double HantekDsoControl::setSamplerate(double samplerate) { |
| 1196 | 1200 | static_cast<ControlSetTimeDIV *>(this->control[CONTROLINDEX_SETTIMEDIV]) |
| 1197 | 1201 | ->setDiv(this->specification.sampleDiv[sampleId]); |
| 1198 | 1202 | this->controlPending[CONTROLINDEX_SETTIMEDIV] = true; |
| 1199 | - this->settings.samplerate.current = samplerate; | |
| 1203 | + settings.samplerate.current = samplerate; | |
| 1200 | 1204 | |
| 1201 | 1205 | // Provide margin for SW trigger |
| 1202 | 1206 | unsigned int sampleMargin = 2000; |
| 1203 | 1207 | // Check for Roll mode |
| 1204 | - if (this->settings.samplerate.limits | |
| 1205 | - ->recordLengths[this->settings.recordLengthId] != UINT_MAX) | |
| 1208 | + if (settings.samplerate.limits | |
| 1209 | + ->recordLengths[settings.recordLengthId] != UINT_MAX) | |
| 1206 | 1210 | emit recordTimeChanged( |
| 1207 | - (double)(this->settings.samplerate.limits | |
| 1208 | - ->recordLengths[this->settings.recordLengthId] - sampleMargin) / | |
| 1209 | - this->settings.samplerate.current); | |
| 1210 | - emit samplerateChanged(this->settings.samplerate.current); | |
| 1211 | + (double)(settings.samplerate.limits | |
| 1212 | + ->recordLengths[settings.recordLengthId] - sampleMargin) / | |
| 1213 | + settings.samplerate.current); | |
| 1214 | + emit samplerateChanged(settings.samplerate.current); | |
| 1211 | 1215 | |
| 1212 | 1216 | return samplerate; |
| 1213 | 1217 | } |
| ... | ... | @@ -1222,26 +1226,26 @@ double HantekDsoControl::setRecordTime(double duration) { |
| 1222 | 1226 | return 0.0; |
| 1223 | 1227 | |
| 1224 | 1228 | if (duration == 0.0) { |
| 1225 | - duration = this->settings.samplerate.target.duration; | |
| 1229 | + duration = settings.samplerate.target.duration; | |
| 1226 | 1230 | } else { |
| 1227 | - this->settings.samplerate.target.duration = duration; | |
| 1228 | - this->settings.samplerate.target.samplerateSet = false; | |
| 1231 | + settings.samplerate.target.duration = duration; | |
| 1232 | + settings.samplerate.target.samplerateSet = false; | |
| 1229 | 1233 | } |
| 1230 | 1234 | |
| 1231 | 1235 | if (this->device->getUniqueModelID() != MODEL_DSO6022BE) { |
| 1232 | 1236 | // Calculate the maximum samplerate that would still provide the requested |
| 1233 | 1237 | // duration |
| 1234 | 1238 | double maxSamplerate = (double)this->specification.samplerate.single |
| 1235 | - .recordLengths[this->settings.recordLengthId] / | |
| 1239 | + .recordLengths[settings.recordLengthId] / | |
| 1236 | 1240 | duration; |
| 1237 | 1241 | |
| 1238 | 1242 | // When possible, enable fast rate if the record time can't be set that low |
| 1239 | 1243 | // to improve resolution |
| 1240 | 1244 | bool fastRate = |
| 1241 | - (this->settings.usedChannels <= 1) && | |
| 1245 | + (settings.usedChannels <= 1) && | |
| 1242 | 1246 | (maxSamplerate >= |
| 1243 | 1247 | this->specification.samplerate.multi.base / |
| 1244 | - this->specification.bufferDividers[this->settings.recordLengthId]); | |
| 1248 | + this->specification.bufferDividers[settings.recordLengthId]); | |
| 1245 | 1249 | |
| 1246 | 1250 | // What is the nearest, at most as high samplerate the scope can provide? |
| 1247 | 1251 | unsigned int downsampler = 0; |
| ... | ... | @@ -1252,8 +1256,8 @@ double HantekDsoControl::setRecordTime(double duration) { |
| 1252 | 1256 | if (this->updateSamplerate(downsampler, fastRate) == UINT_MAX) |
| 1253 | 1257 | return 0.0; |
| 1254 | 1258 | else { |
| 1255 | - return (double)this->settings.samplerate.limits | |
| 1256 | - ->recordLengths[this->settings.recordLengthId] / | |
| 1259 | + return (double)settings.samplerate.limits | |
| 1260 | + ->recordLengths[settings.recordLengthId] / | |
| 1257 | 1261 | bestSamplerate; |
| 1258 | 1262 | } |
| 1259 | 1263 | } else { |
| ... | ... | @@ -1278,11 +1282,11 @@ double HantekDsoControl::setRecordTime(double duration) { |
| 1278 | 1282 | static_cast<ControlSetTimeDIV *>(this->control[CONTROLINDEX_SETTIMEDIV]) |
| 1279 | 1283 | ->setDiv(this->specification.sampleDiv[sampleId]); |
| 1280 | 1284 | this->controlPending[CONTROLINDEX_SETTIMEDIV] = true; |
| 1281 | - this->settings.samplerate.current = | |
| 1285 | + settings.samplerate.current = | |
| 1282 | 1286 | this->specification.sampleSteps[sampleId]; |
| 1283 | 1287 | |
| 1284 | - emit samplerateChanged(this->settings.samplerate.current); | |
| 1285 | - return this->settings.samplerate.current; | |
| 1288 | + emit samplerateChanged(settings.samplerate.current); | |
| 1289 | + return settings.samplerate.current; | |
| 1286 | 1290 | } |
| 1287 | 1291 | } |
| 1288 | 1292 | |
| ... | ... | @@ -1298,19 +1302,19 @@ int HantekDsoControl::setChannelUsed(unsigned int channel, bool used) { |
| 1298 | 1302 | return Dso::ERROR_PARAMETER; |
| 1299 | 1303 | |
| 1300 | 1304 | // Update settings |
| 1301 | - this->settings.voltage[channel].used = used; | |
| 1305 | + settings.voltage[channel].used = used; | |
| 1302 | 1306 | unsigned int channelCount = 0; |
| 1303 | 1307 | for (int channelCounter = 0; channelCounter < HANTEK_CHANNELS; |
| 1304 | 1308 | ++channelCounter) { |
| 1305 | - if (this->settings.voltage[channelCounter].used) | |
| 1309 | + if (settings.voltage[channelCounter].used) | |
| 1306 | 1310 | ++channelCount; |
| 1307 | 1311 | } |
| 1308 | 1312 | |
| 1309 | 1313 | // Calculate the UsedChannels field for the command |
| 1310 | 1314 | unsigned char usedChannels = USED_CH1; |
| 1311 | 1315 | |
| 1312 | - if (this->settings.voltage[1].used) { | |
| 1313 | - if (this->settings.voltage[0].used) { | |
| 1316 | + if (settings.voltage[1].used) { | |
| 1317 | + if (settings.voltage[0].used) { | |
| 1314 | 1318 | usedChannels = USED_CH1CH2; |
| 1315 | 1319 | } else { |
| 1316 | 1320 | // DSO-2250 uses a different value for channel 2 |
| ... | ... | @@ -1352,8 +1356,8 @@ int HantekDsoControl::setChannelUsed(unsigned int channel, bool used) { |
| 1352 | 1356 | |
| 1353 | 1357 | // Check if fast rate mode availability changed |
| 1354 | 1358 | bool fastRateChanged = |
| 1355 | - (this->settings.usedChannels <= 1) != (channelCount <= 1); | |
| 1356 | - this->settings.usedChannels = channelCount; | |
| 1359 | + (settings.usedChannels <= 1) != (channelCount <= 1); | |
| 1360 | + settings.usedChannels = channelCount; | |
| 1357 | 1361 | |
| 1358 | 1362 | if (fastRateChanged) |
| 1359 | 1363 | this->updateSamplerateLimits(); |
| ... | ... | @@ -1430,9 +1434,9 @@ double HantekDsoControl::setGain(unsigned int channel, double gain) { |
| 1430 | 1434 | this->controlPending[CONTROLINDEX_SETRELAYS] = true; |
| 1431 | 1435 | } |
| 1432 | 1436 | |
| 1433 | - this->settings.voltage[channel].gain = gainId; | |
| 1437 | + settings.voltage[channel].gain = gainId; | |
| 1434 | 1438 | |
| 1435 | - this->setOffset(channel, this->settings.voltage[channel].offset); | |
| 1439 | + this->setOffset(channel, settings.voltage[channel].offset); | |
| 1436 | 1440 | |
| 1437 | 1441 | return this->specification.gainSteps[gainId]; |
| 1438 | 1442 | } |
| ... | ... | @@ -1453,23 +1457,23 @@ double HantekDsoControl::setOffset(unsigned int channel, double offset) { |
| 1453 | 1457 | unsigned short int minimum = |
| 1454 | 1458 | ((unsigned short int)*((unsigned char *)&( |
| 1455 | 1459 | this->specification |
| 1456 | - .offsetLimit[channel][this->settings.voltage[channel].gain] | |
| 1460 | + .offsetLimit[channel][settings.voltage[channel].gain] | |
| 1457 | 1461 | [OFFSET_START])) |
| 1458 | 1462 | << 8) + |
| 1459 | 1463 | *((unsigned char *)&( |
| 1460 | 1464 | this->specification |
| 1461 | - .offsetLimit[channel][this->settings.voltage[channel].gain] | |
| 1465 | + .offsetLimit[channel][settings.voltage[channel].gain] | |
| 1462 | 1466 | [OFFSET_START]) + |
| 1463 | 1467 | 1); |
| 1464 | 1468 | unsigned short int maximum = |
| 1465 | 1469 | ((unsigned short int)*((unsigned char *)&( |
| 1466 | 1470 | this->specification |
| 1467 | - .offsetLimit[channel][this->settings.voltage[channel].gain] | |
| 1471 | + .offsetLimit[channel][settings.voltage[channel].gain] | |
| 1468 | 1472 | [OFFSET_END])) |
| 1469 | 1473 | << 8) + |
| 1470 | 1474 | *((unsigned char *)&( |
| 1471 | 1475 | this->specification |
| 1472 | - .offsetLimit[channel][this->settings.voltage[channel].gain] | |
| 1476 | + .offsetLimit[channel][settings.voltage[channel].gain] | |
| 1473 | 1477 | [OFFSET_END]) + |
| 1474 | 1478 | 1); |
| 1475 | 1479 | unsigned short int offsetValue = offset * (maximum - minimum) + minimum + 0.5; |
| ... | ... | @@ -1484,10 +1488,10 @@ double HantekDsoControl::setOffset(unsigned int channel, double offset) { |
| 1484 | 1488 | this->controlPending[CONTROLINDEX_SETOFFSET] = true; |
| 1485 | 1489 | } |
| 1486 | 1490 | |
| 1487 | - this->settings.voltage[channel].offset = offset; | |
| 1488 | - this->settings.voltage[channel].offsetReal = offsetReal; | |
| 1491 | + settings.voltage[channel].offset = offset; | |
| 1492 | + settings.voltage[channel].offsetReal = offsetReal; | |
| 1489 | 1493 | |
| 1490 | - this->setTriggerLevel(channel, this->settings.trigger.level[channel]); | |
| 1494 | + this->setTriggerLevel(channel, settings.trigger.level[channel]); | |
| 1491 | 1495 | |
| 1492 | 1496 | return offsetReal; |
| 1493 | 1497 | } |
| ... | ... | @@ -1501,7 +1505,7 @@ int HantekDsoControl::setTriggerMode(Dso::TriggerMode mode) { |
| 1501 | 1505 | if (mode < Dso::TRIGGERMODE_AUTO || mode >= Dso::TRIGGERMODE_COUNT) |
| 1502 | 1506 | return Dso::ERROR_PARAMETER; |
| 1503 | 1507 | |
| 1504 | - this->settings.trigger.mode = mode; | |
| 1508 | + settings.trigger.mode = mode; | |
| 1505 | 1509 | return Dso::ERROR_NONE; |
| 1506 | 1510 | } |
| 1507 | 1511 | |
| ... | ... | @@ -1551,8 +1555,8 @@ int HantekDsoControl::setTriggerSource(bool special, unsigned int id) { |
| 1551 | 1555 | ->setTrigger(special); |
| 1552 | 1556 | this->controlPending[CONTROLINDEX_SETRELAYS] = true; |
| 1553 | 1557 | |
| 1554 | - this->settings.trigger.special = special; | |
| 1555 | - this->settings.trigger.source = id; | |
| 1558 | + settings.trigger.special = special; | |
| 1559 | + settings.trigger.source = id; | |
| 1556 | 1560 | |
| 1557 | 1561 | // Apply trigger level of the new source |
| 1558 | 1562 | if (special) { |
| ... | ... | @@ -1561,7 +1565,7 @@ int HantekDsoControl::setTriggerSource(bool special, unsigned int id) { |
| 1561 | 1565 | ->setTrigger(0x7f); |
| 1562 | 1566 | this->controlPending[CONTROLINDEX_SETOFFSET] = true; |
| 1563 | 1567 | } else |
| 1564 | - this->setTriggerLevel(id, this->settings.trigger.level[id]); | |
| 1568 | + this->setTriggerLevel(id, settings.trigger.level[id]); | |
| 1565 | 1569 | |
| 1566 | 1570 | return Dso::ERROR_NONE; |
| 1567 | 1571 | } |
| ... | ... | @@ -1589,23 +1593,23 @@ double HantekDsoControl::setTriggerLevel(unsigned int channel, double level) { |
| 1589 | 1593 | minimum = |
| 1590 | 1594 | ((unsigned short int)*((unsigned char *)&( |
| 1591 | 1595 | this->specification |
| 1592 | - .offsetLimit[channel][this->settings.voltage[channel].gain] | |
| 1596 | + .offsetLimit[channel][settings.voltage[channel].gain] | |
| 1593 | 1597 | [OFFSET_START])) |
| 1594 | 1598 | << 8) + |
| 1595 | 1599 | *((unsigned char *)&( |
| 1596 | 1600 | this->specification |
| 1597 | - .offsetLimit[channel][this->settings.voltage[channel].gain] | |
| 1601 | + .offsetLimit[channel][settings.voltage[channel].gain] | |
| 1598 | 1602 | [OFFSET_START]) + |
| 1599 | 1603 | 1); |
| 1600 | 1604 | maximum = |
| 1601 | 1605 | ((unsigned short int)*((unsigned char *)&( |
| 1602 | 1606 | this->specification |
| 1603 | - .offsetLimit[channel][this->settings.voltage[channel].gain] | |
| 1607 | + .offsetLimit[channel][settings.voltage[channel].gain] | |
| 1604 | 1608 | [OFFSET_END])) |
| 1605 | 1609 | << 8) + |
| 1606 | 1610 | *((unsigned char *)&( |
| 1607 | 1611 | this->specification |
| 1608 | - .offsetLimit[channel][this->settings.voltage[channel].gain] | |
| 1612 | + .offsetLimit[channel][settings.voltage[channel].gain] | |
| 1609 | 1613 | [OFFSET_END]) + |
| 1610 | 1614 | 1); |
| 1611 | 1615 | break; |
| ... | ... | @@ -1620,18 +1624,18 @@ double HantekDsoControl::setTriggerLevel(unsigned int channel, double level) { |
| 1620 | 1624 | // Never get out of the limits |
| 1621 | 1625 | unsigned short int levelValue = qBound( |
| 1622 | 1626 | (long int)minimum, |
| 1623 | - (long int)((this->settings.voltage[channel].offsetReal + | |
| 1627 | + (long int)((settings.voltage[channel].offsetReal + | |
| 1624 | 1628 | level / |
| 1625 | 1629 | this->specification |
| 1626 | - .gainSteps[this->settings.voltage[channel].gain]) * | |
| 1630 | + .gainSteps[settings.voltage[channel].gain]) * | |
| 1627 | 1631 | (maximum - minimum) + |
| 1628 | 1632 | 0.5) + |
| 1629 | 1633 | minimum, |
| 1630 | 1634 | (long int)maximum); |
| 1631 | 1635 | |
| 1632 | 1636 | // Check if the set channel is the trigger source |
| 1633 | - if (!this->settings.trigger.special && | |
| 1634 | - channel == this->settings.trigger.source && | |
| 1637 | + if (!settings.trigger.special && | |
| 1638 | + channel == settings.trigger.source && | |
| 1635 | 1639 | this->device->getUniqueModelID() != MODEL_DSO6022BE) { |
| 1636 | 1640 | // SetOffset control command for trigger level |
| 1637 | 1641 | static_cast<ControlSetOffset *>(this->control[CONTROLINDEX_SETOFFSET]) |
| ... | ... | @@ -1641,10 +1645,10 @@ double HantekDsoControl::setTriggerLevel(unsigned int channel, double level) { |
| 1641 | 1645 | |
| 1642 | 1646 | /// \todo Get alternating trigger in here |
| 1643 | 1647 | |
| 1644 | - this->settings.trigger.level[channel] = level; | |
| 1648 | + settings.trigger.level[channel] = level; | |
| 1645 | 1649 | return (double)((levelValue - minimum) / (maximum - minimum) - |
| 1646 | - this->settings.voltage[channel].offsetReal) * | |
| 1647 | - this->specification.gainSteps[this->settings.voltage[channel].gain]; | |
| 1650 | + settings.voltage[channel].offsetReal) * | |
| 1651 | + this->specification.gainSteps[settings.voltage[channel].gain]; | |
| 1648 | 1652 | } |
| 1649 | 1653 | |
| 1650 | 1654 | /// \brief Set the trigger slope. |
| ... | ... | @@ -1686,7 +1690,7 @@ int HantekDsoControl::setTriggerSlope(Dso::Slope slope) { |
| 1686 | 1690 | return Dso::ERROR_UNSUPPORTED; |
| 1687 | 1691 | } |
| 1688 | 1692 | |
| 1689 | - this->settings.trigger.slope = slope; | |
| 1693 | + settings.trigger.slope = slope; | |
| 1690 | 1694 | return Dso::ERROR_NONE; |
| 1691 | 1695 | } |
| 1692 | 1696 | |
| ... | ... | @@ -1703,13 +1707,13 @@ double HantekDsoControl::setPretriggerPosition(double position) { |
| 1703 | 1707 | return -2; |
| 1704 | 1708 | |
| 1705 | 1709 | // All trigger positions are measured in samples |
| 1706 | - unsigned int positionSamples = position * this->settings.samplerate.current; | |
| 1710 | + unsigned int positionSamples = position * settings.samplerate.current; | |
| 1707 | 1711 | unsigned int recordLength = |
| 1708 | - this->settings.samplerate.limits | |
| 1709 | - ->recordLengths[this->settings.recordLengthId]; | |
| 1712 | + settings.samplerate.limits | |
| 1713 | + ->recordLengths[settings.recordLengthId]; | |
| 1710 | 1714 | bool rollMode = recordLength == UINT_MAX; |
| 1711 | 1715 | // Fast rate mode uses both channels |
| 1712 | - if (this->settings.samplerate.limits == &this->specification.samplerate.multi) | |
| 1716 | + if (settings.samplerate.limits == &this->specification.samplerate.multi) | |
| 1713 | 1717 | positionSamples /= HANTEK_CHANNELS; |
| 1714 | 1718 | |
| 1715 | 1719 | switch (this->specification.command.bulk.setPretrigger) { |
| ... | ... | @@ -1758,8 +1762,8 @@ double HantekDsoControl::setPretriggerPosition(double position) { |
| 1758 | 1762 | return Dso::ERROR_UNSUPPORTED; |
| 1759 | 1763 | } |
| 1760 | 1764 | |
| 1761 | - this->settings.trigger.position = position; | |
| 1762 | - return (double)positionSamples / this->settings.samplerate.current; | |
| 1765 | + settings.trigger.position = position; | |
| 1766 | + return (double)positionSamples / settings.samplerate.current; | |
| 1763 | 1767 | } |
| 1764 | 1768 | |
| 1765 | 1769 | #ifdef DEBUG |
| ... | ... | @@ -1888,8 +1892,8 @@ void HantekDsoControl::run() { |
| 1888 | 1892 | } |
| 1889 | 1893 | |
| 1890 | 1894 | // State machine for the device communication |
| 1891 | - if (this->settings.samplerate.limits | |
| 1892 | - ->recordLengths[this->settings.recordLengthId] == UINT_MAX) { | |
| 1895 | + if (settings.samplerate.limits | |
| 1896 | + ->recordLengths[settings.recordLengthId] == UINT_MAX) { | |
| 1893 | 1897 | // Roll mode |
| 1894 | 1898 | this->captureState = CAPTURE_WAITING; |
| 1895 | 1899 | bool toNextState = true; |
| ... | ... | @@ -1964,7 +1968,7 @@ void HantekDsoControl::run() { |
| 1964 | 1968 | #endif |
| 1965 | 1969 | |
| 1966 | 1970 | // Check if we're in single trigger mode |
| 1967 | - if (this->settings.trigger.mode == Dso::TRIGGERMODE_SINGLE && | |
| 1971 | + if (settings.trigger.mode == Dso::TRIGGERMODE_SINGLE && | |
| 1968 | 1972 | this->_samplingStarted) |
| 1969 | 1973 | this->stopSampling(); |
| 1970 | 1974 | |
| ... | ... | @@ -2016,7 +2020,7 @@ void HantekDsoControl::run() { |
| 2016 | 2020 | #endif |
| 2017 | 2021 | |
| 2018 | 2022 | // Check if we're in single trigger mode |
| 2019 | - if (this->settings.trigger.mode == Dso::TRIGGERMODE_SINGLE && | |
| 2023 | + if (settings.trigger.mode == Dso::TRIGGERMODE_SINGLE && | |
| 2020 | 2024 | this->_samplingStarted) |
| 2021 | 2025 | this->stopSampling(); |
| 2022 | 2026 | |
| ... | ... | @@ -2032,12 +2036,12 @@ void HantekDsoControl::run() { |
| 2032 | 2036 | this->previousSampleCount = this->getSampleCount(); |
| 2033 | 2037 | |
| 2034 | 2038 | if (this->_samplingStarted && |
| 2035 | - this->lastTriggerMode == this->settings.trigger.mode) { | |
| 2039 | + this->lastTriggerMode == settings.trigger.mode) { | |
| 2036 | 2040 | ++this->cycleCounter; |
| 2037 | 2041 | |
| 2038 | 2042 | if (this->cycleCounter == this->startCycle && |
| 2039 | - this->settings.samplerate.limits | |
| 2040 | - ->recordLengths[this->settings.recordLengthId] != | |
| 2043 | + settings.samplerate.limits | |
| 2044 | + ->recordLengths[settings.recordLengthId] != | |
| 2041 | 2045 | UINT_MAX) { |
| 2042 | 2046 | // Buffer refilled completely since start of sampling, enable the |
| 2043 | 2047 | // trigger now |
| ... | ... | @@ -2054,7 +2058,7 @@ void HantekDsoControl::run() { |
| 2054 | 2058 | timestampDebug("Enabling trigger"); |
| 2055 | 2059 | #endif |
| 2056 | 2060 | } else if (this->cycleCounter >= 8 + this->startCycle && |
| 2057 | - this->settings.trigger.mode == Dso::TRIGGERMODE_AUTO) { | |
| 2061 | + settings.trigger.mode == Dso::TRIGGERMODE_AUTO) { | |
| 2058 | 2062 | // Force triggering |
| 2059 | 2063 | errorCode = |
| 2060 | 2064 | this->device->bulkCommand(this->command[BULK_FORCETRIGGER]); |
| ... | ... | @@ -2090,8 +2094,8 @@ void HantekDsoControl::run() { |
| 2090 | 2094 | |
| 2091 | 2095 | this->_samplingStarted = true; |
| 2092 | 2096 | this->cycleCounter = 0; |
| 2093 | - this->startCycle = this->settings.trigger.position * 1000 / cycleTime + 1; | |
| 2094 | - this->lastTriggerMode = this->settings.trigger.mode; | |
| 2097 | + this->startCycle = settings.trigger.position * 1000 / cycleTime + 1; | |
| 2098 | + this->lastTriggerMode = settings.trigger.mode; | |
| 2095 | 2099 | break; |
| 2096 | 2100 | |
| 2097 | 2101 | case CAPTURE_SAMPLING: | ... | ... |
openhantek/src/hantek/hantekdsocontrol.h
| ... | ... | @@ -7,6 +7,7 @@ |
| 7 | 7 | #include "bulkStructs.h" |
| 8 | 8 | #include "stateStructs.h" |
| 9 | 9 | #include "utils/printutils.h" |
| 10 | +#include "dsosamples.h" | |
| 10 | 11 | |
| 11 | 12 | #include <vector> |
| 12 | 13 | |
| ... | ... | @@ -17,14 +18,12 @@ |
| 17 | 18 | |
| 18 | 19 | class USBDevice; |
| 19 | 20 | |
| 20 | -////////////////////////////////////////////////////////////////////////////// | |
| 21 | -/// \class Control hantek/control.h | |
| 22 | 21 | /// \brief The DsoControl abstraction layer for %Hantek USB DSOs. |
| 23 | 22 | class HantekDsoControl : public QObject { |
| 24 | 23 | Q_OBJECT |
| 25 | 24 | |
| 26 | 25 | public: |
| 27 | - /** | |
| 26 | + /** | |
| 28 | 27 | * Creates a dsoControl object. The actual event loop / timer is not started. |
| 29 | 28 | * You can optionally create a thread and move the created object to the thread. |
| 30 | 29 | * You need to call updateInterval() to start the timer. |
| ... | ... | @@ -44,15 +43,13 @@ public: |
| 44 | 43 | |
| 45 | 44 | const QStringList *getSpecialTriggerSources(); |
| 46 | 45 | const USBDevice* getDevice() const; |
| 46 | + const DSOsamples& getLastSamples(); | |
| 47 | 47 | |
| 48 | 48 | signals: |
| 49 | 49 | void samplingStarted(); ///< The oscilloscope started sampling/waiting for trigger |
| 50 | 50 | void samplingStopped(); ///< The oscilloscope stopped sampling/waiting for trigger |
| 51 | - void statusMessage(const QString &message, | |
| 52 | - int timeout); ///< Status message about the oscilloscope | |
| 53 | - void samplesAvailable(const std::vector<std::vector<double>> *data, | |
| 54 | - double samplerate, bool append, | |
| 55 | - QMutex *mutex); ///< New sample data is available | |
| 51 | + void statusMessage(const QString &message, int timeout); ///< Status message about the oscilloscope | |
| 52 | + void samplesAvailable(); ///< New sample data is available | |
| 56 | 53 | |
| 57 | 54 | void availableRecordLengthsChanged(const QList<unsigned int> &recordLengths); ///< The available record lengths, empty list for continuous |
| 58 | 55 | void samplerateLimitsChanged(double minimum, double maximum); ///< The minimum or maximum samplerate has changed |
| ... | ... | @@ -93,10 +90,8 @@ protected: |
| 93 | 90 | Hantek::ControlSettings settings; ///< The current settings of the device |
| 94 | 91 | |
| 95 | 92 | // Results |
| 96 | - std::vector<std::vector<double>> samples; ///< Sample data vectors sent to the data analyzer | |
| 97 | - unsigned int previousSampleCount; ///< The expected total number of samples at | |
| 98 | - ///the last check before sampling started | |
| 99 | - QMutex samplesMutex; ///< Mutex for the sample data | |
| 93 | + DSOsamples result; | |
| 94 | + unsigned int previousSampleCount; ///< The expected total number of samples at the last check before sampling started | |
| 100 | 95 | |
| 101 | 96 | // State of the communication thread |
| 102 | 97 | int captureState = Hantek::CAPTURE_WAITING; | ... | ... |
openhantek/src/main.cpp
| ... | ... | @@ -119,25 +119,38 @@ int main(int argc, char *argv[]) { |
| 119 | 119 | |
| 120 | 120 | //////// Create DSO control object and move it to a separate thread //////// |
| 121 | 121 | QThread dsoControlThread; |
| 122 | - std::shared_ptr<HantekDsoControl> dsoControl(new HantekDsoControl(device.get())); | |
| 123 | - dsoControl->moveToThread(&dsoControlThread); | |
| 124 | - QObject::connect(&dsoControlThread,&QThread::started,dsoControl.get(),&HantekDsoControl::run); | |
| 125 | - QObject::connect(dsoControl.get(), &HantekDsoControl::communicationError, QCoreApplication::instance(), &QCoreApplication::quit); | |
| 126 | - QObject::connect(device.get(), &USBDevice::deviceDisconnected, QCoreApplication::instance(), &QCoreApplication::quit); | |
| 122 | + dsoControlThread.setObjectName("dsoControlThread"); | |
| 123 | + HantekDsoControl dsoControl(device.get()); | |
| 124 | + dsoControl.moveToThread(&dsoControlThread); | |
| 125 | + QObject::connect(&dsoControlThread,&QThread::started, &dsoControl,&HantekDsoControl::run); | |
| 126 | + QObject::connect(&dsoControl, &HantekDsoControl::communicationError, | |
| 127 | + QCoreApplication::instance(), &QCoreApplication::quit); | |
| 128 | + QObject::connect(device.get(), &USBDevice::deviceDisconnected, | |
| 129 | + QCoreApplication::instance(), &QCoreApplication::quit); | |
| 127 | 130 | |
| 128 | 131 | //////// Create data analyser object //////// |
| 129 | - std::shared_ptr<DataAnalyzer> dataAnalyser(new DataAnalyzer()); | |
| 132 | + QThread dataAnalyzerThread; | |
| 133 | + dataAnalyzerThread.setObjectName("dataAnalyzerThread"); | |
| 134 | + DataAnalyzer dataAnalyser; | |
| 135 | + dataAnalyser.setSourceData(&dsoControl.getLastSamples()); | |
| 136 | + dataAnalyser.moveToThread(&dataAnalyzerThread); | |
| 137 | + QObject::connect(&dsoControl, &HantekDsoControl::samplesAvailable, | |
| 138 | + &dataAnalyser, &DataAnalyzer::samplesAvailable); | |
| 130 | 139 | |
| 131 | 140 | //////// Create main window //////// |
| 132 | - OpenHantekMainWindow *openHantekMainWindow = new OpenHantekMainWindow(dsoControl, dataAnalyser); | |
| 141 | + OpenHantekMainWindow *openHantekMainWindow = new OpenHantekMainWindow(&dsoControl, &dataAnalyser); | |
| 133 | 142 | openHantekMainWindow->show(); |
| 134 | 143 | |
| 135 | 144 | //////// Start DSO thread and go into GUI main loop |
| 145 | + dataAnalyzerThread.start(); | |
| 136 | 146 | dsoControlThread.start(); |
| 137 | 147 | int res = openHantekApplication.exec(); |
| 138 | 148 | |
| 139 | 149 | //////// Clean up //////// |
| 140 | 150 | dsoControlThread.quit(); |
| 141 | 151 | dsoControlThread.wait(10000); |
| 152 | + | |
| 153 | + dataAnalyzerThread.quit(); | |
| 154 | + dataAnalyzerThread.wait(10000); | |
| 142 | 155 | return res; |
| 143 | 156 | } | ... | ... |
openhantek/src/mainwindow.cpp
| ... | ... | @@ -28,7 +28,7 @@ |
| 28 | 28 | /// \brief Initializes the gui elements of the main window. |
| 29 | 29 | /// \param parent The parent widget. |
| 30 | 30 | /// \param flags Flags for the window manager. |
| 31 | -OpenHantekMainWindow::OpenHantekMainWindow(std::shared_ptr<HantekDsoControl> dsoControl, std::shared_ptr<DataAnalyzer> dataAnalyzer) | |
| 31 | +OpenHantekMainWindow::OpenHantekMainWindow(HantekDsoControl *dsoControl, DataAnalyzer *dataAnalyzer) | |
| 32 | 32 | :dsoControl(dsoControl),dataAnalyzer(dataAnalyzer) { |
| 33 | 33 | |
| 34 | 34 | // Window title |
| ... | ... | @@ -45,7 +45,11 @@ OpenHantekMainWindow::OpenHantekMainWindow(std::shared_ptr<HantekDsoControl> dso |
| 45 | 45 | createDockWindows(); |
| 46 | 46 | |
| 47 | 47 | // Central oszilloscope widget |
| 48 | - dsoWidget = new DsoWidget(settings, dataAnalyzer.get()); | |
| 48 | + dataAnalyzer->applySettings(&settings->scope); | |
| 49 | + dsoWidget = new DsoWidget(settings); | |
| 50 | + connect(dataAnalyzer, &DataAnalyzer::analyzed, [this]() { | |
| 51 | + dsoWidget->showNewData(this->dataAnalyzer->getNextResult()); | |
| 52 | + }); | |
| 49 | 53 | setCentralWidget(dsoWidget); |
| 50 | 54 | |
| 51 | 55 | // Subroutines for window elements |
| ... | ... | @@ -260,91 +264,85 @@ void OpenHantekMainWindow::createDockWindows() { |
| 260 | 264 | /// \brief Connect general signals and device management signals. |
| 261 | 265 | void OpenHantekMainWindow::connectSignals() { |
| 262 | 266 | // Connect general signals |
| 263 | - connect(this, SIGNAL(settingsChanged()), this, SLOT(applySettings())); | |
| 267 | + connect(this, &OpenHantekMainWindow::settingsChanged, this, &OpenHantekMainWindow::applySettings); | |
| 264 | 268 | // connect(dsoWidget, SIGNAL(stopped()), this, SLOT(stopped())); |
| 265 | - connect(dsoControl.get(), SIGNAL(statusMessage(QString, int)), | |
| 266 | - statusBar(), SLOT(showMessage(QString, int))); | |
| 267 | - connect(dsoControl.get(), | |
| 268 | - SIGNAL(samplesAvailable(const std::vector<std::vector<double>> *, | |
| 269 | - double, bool, QMutex *)), | |
| 270 | - dataAnalyzer.get(), | |
| 271 | - SLOT(analyze(const std::vector<std::vector<double>> *, double, bool, | |
| 272 | - QMutex *))); | |
| 269 | + connect(dsoControl, &HantekDsoControl::statusMessage, | |
| 270 | + statusBar(), &QStatusBar::showMessage); | |
| 273 | 271 | |
| 274 | 272 | // Connect signals to DSO controller and widget |
| 275 | - connect(horizontalDock, SIGNAL(samplerateChanged(double)), this, | |
| 276 | - SLOT(samplerateSelected())); | |
| 277 | - connect(horizontalDock, SIGNAL(timebaseChanged(double)), this, | |
| 278 | - SLOT(timebaseSelected())); | |
| 279 | - connect(horizontalDock, SIGNAL(frequencybaseChanged(double)), | |
| 280 | - dsoWidget, SLOT(updateFrequencybase(double))); | |
| 281 | - connect(horizontalDock, SIGNAL(recordLengthChanged(unsigned long)), | |
| 282 | - this, SLOT(recordLengthSelected(unsigned long))); | |
| 273 | + connect(horizontalDock, &HorizontalDock::samplerateChanged, this, | |
| 274 | + &OpenHantekMainWindow::samplerateSelected); | |
| 275 | + connect(horizontalDock, &HorizontalDock::timebaseChanged, this, | |
| 276 | + &OpenHantekMainWindow::timebaseSelected); | |
| 277 | + connect(horizontalDock, &HorizontalDock::frequencybaseChanged, | |
| 278 | + dsoWidget, &DsoWidget::updateFrequencybase); | |
| 279 | + connect(horizontalDock, &HorizontalDock::recordLengthChanged, | |
| 280 | + this, &OpenHantekMainWindow::recordLengthSelected); | |
| 283 | 281 | // connect(horizontalDock, SIGNAL(formatChanged(HorizontalFormat)), |
| 284 | 282 | // dsoWidget, SLOT(horizontalFormatChanged(HorizontalFormat))); |
| 285 | 283 | |
| 286 | - connect(triggerDock, SIGNAL(modeChanged(Dso::TriggerMode)), | |
| 287 | - dsoControl.get(), SLOT(setTriggerMode(Dso::TriggerMode))); | |
| 288 | - connect(triggerDock, SIGNAL(modeChanged(Dso::TriggerMode)), | |
| 289 | - dsoWidget, SLOT(updateTriggerMode())); | |
| 290 | - connect(triggerDock, SIGNAL(sourceChanged(bool, unsigned int)), | |
| 291 | - dsoControl.get(), SLOT(setTriggerSource(bool, unsigned int))); | |
| 292 | - connect(triggerDock, SIGNAL(sourceChanged(bool, unsigned int)), | |
| 293 | - dsoWidget, SLOT(updateTriggerSource())); | |
| 294 | - connect(triggerDock, SIGNAL(slopeChanged(Dso::Slope)), dsoControl.get(), | |
| 295 | - SLOT(setTriggerSlope(Dso::Slope))); | |
| 296 | - connect(triggerDock, SIGNAL(slopeChanged(Dso::Slope)), dsoWidget, | |
| 297 | - SLOT(updateTriggerSlope())); | |
| 298 | - connect(dsoWidget, SIGNAL(triggerPositionChanged(double)), | |
| 299 | - dsoControl.get(), SLOT(setPretriggerPosition(double))); | |
| 300 | - connect(dsoWidget, SIGNAL(triggerLevelChanged(unsigned int, double)), | |
| 301 | - dsoControl.get(), SLOT(setTriggerLevel(unsigned int, double))); | |
| 302 | - | |
| 303 | - connect(voltageDock, SIGNAL(usedChanged(unsigned int, bool)), this, | |
| 304 | - SLOT(updateUsed(unsigned int))); | |
| 305 | - connect(voltageDock, SIGNAL(usedChanged(unsigned int, bool)), | |
| 306 | - dsoWidget, SLOT(updateVoltageUsed(unsigned int, bool))); | |
| 284 | + connect(triggerDock, &TriggerDock::modeChanged, | |
| 285 | + dsoControl, &HantekDsoControl::setTriggerMode); | |
| 286 | + connect(triggerDock, &TriggerDock::modeChanged, | |
| 287 | + dsoWidget, &DsoWidget::updateTriggerMode); | |
| 288 | + connect(triggerDock, &TriggerDock::sourceChanged, | |
| 289 | + dsoControl, &HantekDsoControl::setTriggerSource); | |
| 290 | + connect(triggerDock, &TriggerDock::sourceChanged, | |
| 291 | + dsoWidget, &DsoWidget::updateTriggerSource); | |
| 292 | + connect(triggerDock, &TriggerDock::slopeChanged, dsoControl, | |
| 293 | + &HantekDsoControl::setTriggerSlope); | |
| 294 | + connect(triggerDock, &TriggerDock::slopeChanged, dsoWidget, | |
| 295 | + &DsoWidget::updateTriggerSlope); | |
| 296 | + connect(dsoWidget, &DsoWidget::triggerPositionChanged, | |
| 297 | + dsoControl, &HantekDsoControl::setPretriggerPosition); | |
| 298 | + connect(dsoWidget, &DsoWidget::triggerLevelChanged, | |
| 299 | + dsoControl, &HantekDsoControl::setTriggerLevel); | |
| 300 | + | |
| 301 | + connect(voltageDock, &VoltageDock::usedChanged, this, | |
| 302 | + &OpenHantekMainWindow::updateUsed); | |
| 303 | + connect(voltageDock, &VoltageDock::usedChanged, | |
| 304 | + dsoWidget, &DsoWidget::updateVoltageUsed); | |
| 307 | 305 | connect(voltageDock, |
| 308 | - SIGNAL(couplingChanged(unsigned int, Dso::Coupling)), | |
| 309 | - dsoControl.get(), SLOT(setCoupling(unsigned int, Dso::Coupling))); | |
| 306 | + &VoltageDock::couplingChanged, | |
| 307 | + dsoControl, &HantekDsoControl::setCoupling); | |
| 310 | 308 | connect(voltageDock, |
| 311 | - SIGNAL(couplingChanged(unsigned int, Dso::Coupling)), dsoWidget, | |
| 312 | - SLOT(updateVoltageCoupling(unsigned int))); | |
| 313 | - connect(voltageDock, SIGNAL(modeChanged(Dso::MathMode)), | |
| 314 | - dsoWidget, SLOT(updateMathMode())); | |
| 315 | - connect(voltageDock, SIGNAL(gainChanged(unsigned int, double)), this, | |
| 316 | - SLOT(updateVoltageGain(unsigned int))); | |
| 317 | - connect(voltageDock, SIGNAL(gainChanged(unsigned int, double)), | |
| 318 | - dsoWidget, SLOT(updateVoltageGain(unsigned int))); | |
| 319 | - connect(dsoWidget, SIGNAL(offsetChanged(unsigned int, double)), this, | |
| 320 | - SLOT(updateOffset(unsigned int))); | |
| 321 | - | |
| 322 | - connect(spectrumDock, SIGNAL(usedChanged(unsigned int, bool)), this, | |
| 323 | - SLOT(updateUsed(unsigned int))); | |
| 324 | - connect(spectrumDock, SIGNAL(usedChanged(unsigned int, bool)), | |
| 325 | - dsoWidget, SLOT(updateSpectrumUsed(unsigned int, bool))); | |
| 326 | - connect(spectrumDock, SIGNAL(magnitudeChanged(unsigned int, double)), | |
| 327 | - dsoWidget, SLOT(updateSpectrumMagnitude(unsigned int))); | |
| 309 | + &VoltageDock::couplingChanged, dsoWidget, | |
| 310 | + &DsoWidget::updateVoltageCoupling); | |
| 311 | + connect(voltageDock, &VoltageDock::modeChanged, | |
| 312 | + dsoWidget, &DsoWidget::updateMathMode); | |
| 313 | + connect(voltageDock, &VoltageDock::gainChanged, this, | |
| 314 | + &OpenHantekMainWindow::updateVoltageGain); | |
| 315 | + connect(voltageDock, &VoltageDock::gainChanged, | |
| 316 | + dsoWidget, &DsoWidget::updateVoltageGain); | |
| 317 | + connect(dsoWidget, &DsoWidget::offsetChanged, this, | |
| 318 | + &OpenHantekMainWindow::updateOffset); | |
| 319 | + | |
| 320 | + connect(spectrumDock, &SpectrumDock::usedChanged, this, | |
| 321 | + &OpenHantekMainWindow::updateUsed); | |
| 322 | + connect(spectrumDock, &SpectrumDock::usedChanged, | |
| 323 | + dsoWidget, &DsoWidget::updateSpectrumUsed); | |
| 324 | + connect(spectrumDock, &SpectrumDock::magnitudeChanged, | |
| 325 | + dsoWidget, &DsoWidget::updateSpectrumMagnitude); | |
| 328 | 326 | |
| 329 | 327 | // Started/stopped signals from oscilloscope |
| 330 | - connect(dsoControl.get(), SIGNAL(samplingStarted()), this, SLOT(started())); | |
| 331 | - connect(dsoControl.get(), SIGNAL(samplingStopped()), this, SLOT(stopped())); | |
| 328 | + connect(dsoControl, &HantekDsoControl::samplingStarted, this, &OpenHantekMainWindow::started); | |
| 329 | + connect(dsoControl, &HantekDsoControl::samplingStopped, this, &OpenHantekMainWindow::stopped); | |
| 332 | 330 | |
| 333 | 331 | // connect(dsoControl, SIGNAL(recordLengthChanged(unsigned long)), this, |
| 334 | 332 | // SLOT(recordLengthChanged())); |
| 335 | - connect(dsoControl.get(), SIGNAL(recordTimeChanged(double)), this, | |
| 336 | - SLOT(recordTimeChanged(double))); | |
| 337 | - connect(dsoControl.get(), SIGNAL(samplerateChanged(double)), this, | |
| 338 | - SLOT(samplerateChanged(double))); | |
| 333 | + connect(dsoControl, &HantekDsoControl::recordTimeChanged, this, | |
| 334 | + &OpenHantekMainWindow::recordTimeChanged); | |
| 335 | + connect(dsoControl, &HantekDsoControl::samplerateChanged, this, | |
| 336 | + &OpenHantekMainWindow::samplerateChanged); | |
| 339 | 337 | |
| 340 | - connect(dsoControl.get(), | |
| 341 | - SIGNAL(availableRecordLengthsChanged(QList<unsigned int>)), | |
| 338 | + connect(dsoControl, | |
| 339 | + &HantekDsoControl::availableRecordLengthsChanged, | |
| 342 | 340 | horizontalDock, |
| 343 | - SLOT(availableRecordLengthsChanged(QList<unsigned int>))); | |
| 344 | - connect(dsoControl.get(), SIGNAL(samplerateLimitsChanged(double, double)), | |
| 345 | - horizontalDock, SLOT(samplerateLimitsChanged(double, double))); | |
| 346 | - connect(dsoControl.get(), SIGNAL(samplerateSet(int, QList<double>)), | |
| 347 | - horizontalDock, SLOT(samplerateSet(int, QList<double>))); | |
| 341 | + &HorizontalDock::availableRecordLengthsChanged); | |
| 342 | + connect(dsoControl, &HantekDsoControl::samplerateLimitsChanged, | |
| 343 | + horizontalDock, &HorizontalDock::samplerateLimitsChanged); | |
| 344 | + connect(dsoControl, &HantekDsoControl::samplerateSet, | |
| 345 | + horizontalDock, &HorizontalDock::samplerateSet); | |
| 348 | 346 | } |
| 349 | 347 | |
| 350 | 348 | /// \brief Initialize the device with the current settings. |
| ... | ... | @@ -444,10 +442,10 @@ void OpenHantekMainWindow::started() { |
| 444 | 442 | startStopAction->setIcon(QIcon(":actions/stop.png")); |
| 445 | 443 | startStopAction->setStatusTip(tr("Stop the oscilloscope")); |
| 446 | 444 | |
| 447 | - disconnect(startStopAction, SIGNAL(triggered()), dsoControl.get(), | |
| 448 | - SLOT(startSampling())); | |
| 449 | - connect(startStopAction, SIGNAL(triggered()), dsoControl.get(), | |
| 450 | - SLOT(stopSampling())); | |
| 445 | + disconnect(startStopAction, &QAction::triggered, dsoControl, | |
| 446 | + &HantekDsoControl::startSampling); | |
| 447 | + connect(startStopAction, &QAction::triggered, dsoControl, | |
| 448 | + &HantekDsoControl::stopSampling); | |
| 451 | 449 | } |
| 452 | 450 | |
| 453 | 451 | /// \brief The oscilloscope stopped sampling. |
| ... | ... | @@ -456,10 +454,10 @@ void OpenHantekMainWindow::stopped() { |
| 456 | 454 | startStopAction->setIcon(QIcon(":actions/start.png")); |
| 457 | 455 | startStopAction->setStatusTip(tr("Start the oscilloscope")); |
| 458 | 456 | |
| 459 | - disconnect(startStopAction, SIGNAL(triggered()), dsoControl.get(), | |
| 460 | - SLOT(stopSampling())); | |
| 461 | - connect(startStopAction, SIGNAL(triggered()), dsoControl.get(), | |
| 462 | - SLOT(startSampling())); | |
| 457 | + disconnect(startStopAction, &QAction::triggered, dsoControl, | |
| 458 | + &HantekDsoControl::stopSampling); | |
| 459 | + connect(startStopAction, &QAction::triggered, dsoControl, | |
| 460 | + &HantekDsoControl::startSampling); | |
| 463 | 461 | } |
| 464 | 462 | |
| 465 | 463 | /// \brief Configure the oscilloscope. | ... | ... |
openhantek/src/mainwindow.h
| ... | ... | @@ -27,7 +27,7 @@ class OpenHantekMainWindow : public QMainWindow { |
| 27 | 27 | Q_OBJECT |
| 28 | 28 | |
| 29 | 29 | public: |
| 30 | - OpenHantekMainWindow(std::shared_ptr<HantekDsoControl> dsoControl, std::shared_ptr<DataAnalyzer> dataAnalyser); | |
| 30 | + OpenHantekMainWindow(HantekDsoControl* dsoControl, DataAnalyzer* dataAnalyser); | |
| 31 | 31 | |
| 32 | 32 | protected: |
| 33 | 33 | void closeEvent(QCloseEvent *event); |
| ... | ... | @@ -87,8 +87,8 @@ private: |
| 87 | 87 | #endif |
| 88 | 88 | |
| 89 | 89 | // Data handling classes |
| 90 | - std::shared_ptr<HantekDsoControl> dsoControl; | |
| 91 | - std::shared_ptr<DataAnalyzer> dataAnalyzer; | |
| 90 | + HantekDsoControl* dsoControl; | |
| 91 | + DataAnalyzer* dataAnalyzer; | |
| 92 | 92 | |
| 93 | 93 | // Other variables |
| 94 | 94 | QString currentFile; | ... | ... |
openhantek/src/utils/printutils.h
| ... | ... | @@ -54,6 +54,10 @@ unsigned int hexParse(const QString dump, unsigned char *data, unsigned int leng |
| 54 | 54 | |
| 55 | 55 | /// \brief Print debug information with timestamp. |
| 56 | 56 | /// \param text Text that will be output via qDebug. |
| 57 | +#ifdef DEBUG | |
| 57 | 58 | inline void timestampDebug(QString text) { |
| 58 | 59 | qDebug("%s: %s", QTime::currentTime().toString("hh:mm:ss.zzz").toLatin1().constData(), text.toLatin1().constData()); |
| 59 | 60 | } |
| 61 | +#else | |
| 62 | +#define timestampDebug(ARG) | |
| 63 | +#endif | ... | ... |