Commit 812e83f5f19ffbfd6ded73181425f61cd68084b3

Authored by David Graeff
1 parent 76118202

Finish refactoring of data flow from USBDevice->HantekDSOControl->DataAnalyser->…

…DSOWidget->GLGenerator. Less thread locks, no deadlocks by design
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-&gt;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&lt;HantekDsoControl&gt; 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
... ...