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,7 +10,7 @@ set(CMAKE_AUTORCC ON)
10 10
11 # include directories 11 # include directories
12 set(CMAKE_INCLUDE_CURRENT_DIR ON) 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 # collect sources and other files 15 # collect sources and other files
16 file(GLOB_RECURSE SRC "src/*.cpp") 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 #include <cmath> 3 #include <cmath>
25 4
@@ -33,437 +12,413 @@ @@ -33,437 +12,413 @@
33 #include "hantek/definitions.h" 12 #include "hantek/definitions.h"
34 #include "utils/printutils.h" 13 #include "utils/printutils.h"
35 #include "utils/dsoStrings.h" 14 #include "utils/dsoStrings.h"
36 -#include "dataanalyzer.h"  
37 #include "exporter.h" 15 #include "exporter.h"
38 #include "glscope.h" 16 #include "glscope.h"
39 -#include "levelslider.h" 17 +#include "widgets/levelslider.h"
40 #include "settings.h" 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 channel); 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 channel, 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 /// \brief Set the trigger level sliders minimum and maximum to the new values. 272 /// \brief Set the trigger level sliders minimum and maximum to the new values.
318 void DsoWidget::adaptTriggerLevelSlider(unsigned int channel) { 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 /// \brief Show/Hide a line of the measurement table. 284 /// \brief Show/Hide a line of the measurement table.
330 void DsoWidget::setMeasurementVisible(unsigned int channel, bool visible) { 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 /// \brief Update the label about the marker measurements 300 /// \brief Update the label about the marker measurements
346 void DsoWidget::updateMarkerDetails() { 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 /// \brief Update the label about the trigger settings 324 /// \brief Update the label about the trigger settings
370 void DsoWidget::updateSpectrumDetails(unsigned int channel) { 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 UNIT_DECIBEL, 3) + 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 /// \brief Update the label about the trigger settings 339 /// \brief Update the label about the trigger settings
385 void DsoWidget::updateTriggerDetails() { 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 /// \brief Update the label about the trigger settings 364 /// \brief Update the label about the trigger settings
410 void DsoWidget::updateVoltageDetails(unsigned int channel) { 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 UNIT_VOLTS, 3) + 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 /// \brief Handles frequencybaseChanged signal from the horizontal dock. 382 /// \brief Handles frequencybaseChanged signal from the horizontal dock.
428 /// \param frequencybase The frequencybase used for displaying the trace. 383 /// \param frequencybase The frequencybase used for displaying the trace.
429 void DsoWidget::updateFrequencybase(double frequencybase) { 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 /// \brief Updates the samplerate field after changing the samplerate. 389 /// \brief Updates the samplerate field after changing the samplerate.
435 /// \param samplerate The samplerate set in the oscilloscope. 390 /// \param samplerate The samplerate set in the oscilloscope.
436 void DsoWidget::updateSamplerate(double samplerate) { 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 /// \brief Handles timebaseChanged signal from the horizontal dock. 396 /// \brief Handles timebaseChanged signal from the horizontal dock.
442 /// \param timebase The timebase used for displaying the trace. 397 /// \param timebase The timebase used for displaying the trace.
443 void DsoWidget::updateTimebase(double timebase) { 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 /// \brief Handles magnitudeChanged signal from the spectrum dock. 405 /// \brief Handles magnitudeChanged signal from the spectrum dock.
451 /// \param channel The channel whose magnitude was changed. 406 /// \param channel The channel whose magnitude was changed.
452 void DsoWidget::updateSpectrumMagnitude(unsigned int channel) { 407 void DsoWidget::updateSpectrumMagnitude(unsigned int channel) {
453 - this->updateSpectrumDetails(channel); 408 + this->updateSpectrumDetails(channel);
454 } 409 }
455 410
456 /// \brief Handles usedChanged signal from the spectrum dock. 411 /// \brief Handles usedChanged signal from the spectrum dock.
457 /// \param channel The channel whose used-state was changed. 412 /// \param channel The channel whose used-state was changed.
458 /// \param used The new used-state for the channel. 413 /// \param used The new used-state for the channel.
459 void DsoWidget::updateSpectrumUsed(unsigned int channel, bool used) { 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 /// \brief Handles modeChanged signal from the trigger dock. 424 /// \brief Handles modeChanged signal from the trigger dock.
@@ -474,163 +429,150 @@ void DsoWidget::updateTriggerSlope() { this-&gt;updateTriggerDetails(); } @@ -474,163 +429,150 @@ void DsoWidget::updateTriggerSlope() { this-&gt;updateTriggerDetails(); }
474 429
475 /// \brief Handles sourceChanged signal from the trigger dock. 430 /// \brief Handles sourceChanged signal from the trigger dock.
476 void DsoWidget::updateTriggerSource() { 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 /// \brief Handles couplingChanged signal from the voltage dock. 455 /// \brief Handles couplingChanged signal from the voltage dock.
501 /// \param channel The channel whose coupling was changed. 456 /// \param channel The channel whose coupling was changed.
502 void DsoWidget::updateVoltageCoupling(unsigned int channel) { 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 /// \brief Handles modeChanged signal from the voltage dock. 465 /// \brief Handles modeChanged signal from the voltage dock.
511 void DsoWidget::updateMathMode() { 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 /// \brief Handles gainChanged signal from the voltage dock. 473 /// \brief Handles gainChanged signal from the voltage dock.
519 /// \param channel The channel whose gain was changed. 474 /// \param channel The channel whose gain was changed.
520 void DsoWidget::updateVoltageGain(unsigned int channel) { 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 /// \brief Handles usedChanged signal from the voltage dock. 485 /// \brief Handles usedChanged signal from the voltage dock.
531 /// \param channel The channel whose used-state was changed. 486 /// \param channel The channel whose used-state was changed.
532 /// \param used The new used-state for the channel. 487 /// \param used The new used-state for the channel.
533 void DsoWidget::updateVoltageUsed(unsigned int channel, bool used) { 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 /// \brief Change the record length. 500 /// \brief Change the record length.
546 void DsoWidget::updateRecordLength(unsigned long size) { 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 /// \brief Export the oscilloscope screen to a file. 506 /// \brief Export the oscilloscope screen to a file.
552 /// \return true if the document was exported successfully. 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 /// \brief Print the oscilloscope screen. 512 /// \brief Print the oscilloscope screen.
576 /// \return true if the document was sent to the printer successfully. 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 /// \brief Stop the oscilloscope. 518 /// \brief Stop the oscilloscope.
586 void DsoWidget::updateZoom(bool enabled) { 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 /// \brief Prints analyzed data. 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 /// \brief Handles valueChanged signal from the offset sliders. 561 /// \brief Handles valueChanged signal from the offset sliders.
620 /// \param channel The channel whose offset was changed. 562 /// \param channel The channel whose offset was changed.
621 /// \param value The new offset for the channel. 563 /// \param value The new offset for the channel.
622 void DsoWidget::updateOffset(int channel, double value) { 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 /// \brief Handles valueChanged signal from the triggerPosition slider. 578 /// \brief Handles valueChanged signal from the triggerPosition slider.
@@ -638,35 +580,35 @@ void DsoWidget::updateOffset(int channel, double value) { @@ -638,35 +580,35 @@ void DsoWidget::updateOffset(int channel, double value) {
638 /// \param value The new triggerPosition in seconds relative to the first 580 /// \param value The new triggerPosition in seconds relative to the first
639 /// sample. 581 /// sample.
640 void DsoWidget::updateTriggerPosition(int index, double value) { 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 /// \brief Handles valueChanged signal from the trigger level slider. 594 /// \brief Handles valueChanged signal from the trigger level slider.
653 /// \param channel The index of the slider. 595 /// \param channel The index of the slider.
654 /// \param value The new trigger level. 596 /// \param value The new trigger level.
655 void DsoWidget::updateTriggerLevel(int channel, double value) { 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 /// \brief Handles valueChanged signal from the marker slider. 605 /// \brief Handles valueChanged signal from the marker slider.
664 /// \param marker The index of the slider. 606 /// \param marker The index of the slider.
665 /// \param value The new marker position. 607 /// \param value The new marker position.
666 void DsoWidget::updateMarker(int marker, double value) { 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 #include <QWidget> 5 #include <QWidget>
  6 +#include <memory>
29 7
30 #include "dockwindows.h" 8 #include "dockwindows.h"
31 #include "glscope.h" 9 #include "glscope.h"
32 #include "levelslider.h" 10 #include "levelslider.h"
  11 +#include "exporter.h"
33 12
34 class DataAnalyzer; 13 class DataAnalyzer;
35 class DsoSettings; 14 class DsoSettings;
36 class QGridLayout; 15 class QGridLayout;
37 16
38 -////////////////////////////////////////////////////////////////////////////////  
39 -/// \class DsoWidget dsowidget.h 17 +/// \class DsoWidget
40 /// \brief The widget for the oszilloscope-screen 18 /// \brief The widget for the oszilloscope-screen
41 /// This widget contains the scopes and all level sliders. 19 /// This widget contains the scopes and all level sliders.
42 class DsoWidget : public QWidget { 20 class DsoWidget : public QWidget {
43 - Q_OBJECT 21 + Q_OBJECT
44 22
45 public: 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 protected: 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 public slots: 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 signals: 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 #include <QMutex> 3 #include <QMutex>
25 4
@@ -28,400 +7,353 @@ @@ -28,400 +7,353 @@
28 #include "dataanalyzer.h" 7 #include "dataanalyzer.h"
29 #include "settings.h" 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 #if 0 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 #endif 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 #include <deque> 5 #include <deque>
31 6
32 #include <QGLFunctions> 7 #include <QGLFunctions>
33 #include <QObject> 8 #include <QObject>
34 9
35 -#include "definitions.h" 10 +#include "scopesettings.h"
  11 +#include "viewsettings.h"
  12 +#include "dataanalyzerresult.h"
36 13
37 #define DIVS_TIME 10.0 ///< Number of horizontal screen divs 14 #define DIVS_TIME 10.0 ///< Number of horizontal screen divs
38 #define DIVS_VOLTAGE 8.0 ///< Number of vertical screen divs 15 #define DIVS_VOLTAGE 8.0 ///< Number of vertical screen divs
39 #define DIVS_SUB 5 ///< Number of sub-divisions per div 16 #define DIVS_SUB 5 ///< Number of sub-divisions per div
40 17
41 -class DataAnalyzer;  
42 -class DsoSettings;  
43 class GlScope; 18 class GlScope;
44 19
45 //////////////////////////////////////////////////////////////////////////////// 20 ////////////////////////////////////////////////////////////////////////////////
46 -/// \class GlGenerator glgenerator.h 21 +/// \class GlGenerator
47 /// \brief Generates the vertex arrays for the GlScope classes. 22 /// \brief Generates the vertex arrays for the GlScope classes.
48 class GlGenerator : public QObject { 23 class GlGenerator : public QObject {
49 - Q_OBJECT  
50 -  
51 - friend class GlScope; 24 + Q_OBJECT
52 25
53 public: 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 private: 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 signals: 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 #include <cmath> 3 #include <cmath>
27 4
@@ -33,250 +10,212 @@ @@ -33,250 +10,212 @@
33 #include "glgenerator.h" 10 #include "glgenerator.h"
34 #include "settings.h" 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 /// \brief Initializes OpenGL output. 18 /// \brief Initializes OpenGL output.
52 void GlScope::initializeGL() { 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 /// \brief Draw the graphs and the grid. 35 /// \brief Draw the graphs and the grid.
69 void GlScope::paintGL() { 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 if (this->settings->view.antialiasing) { 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 // Apply zoom settings via matrix transformation 157 // Apply zoom settings via matrix transformation
86 if (this->zoomed) { 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 this->settings->scope.horizontal.marker[1]) / 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 // Values we need for the fading of the digital phosphor 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 switch (this->settings->scope.horizontal.format) { 178 switch (this->settings->scope.horizontal.format) {
106 case Dso::GRAPHFORMAT_TY: 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 case Dso::GRAPHFORMAT_XY: 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 default: 205 default:
181 - break; 206 + break;
182 } 207 }
183 208
184 - delete[] fadingFactor;  
185 -  
186 glDisable(GL_POINT_SMOOTH); 209 glDisable(GL_POINT_SMOOTH);
187 glDisable(GL_LINE_SMOOTH); 210 glDisable(GL_LINE_SMOOTH);
188 211
189 if (this->zoomed) 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 #include <QtGlobal> 6 #include <QtGlobal>
  7 +#include <QMetaObject>
31 #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) 8 #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
32 #include <QOpenGLWidget> 9 #include <QOpenGLWidget>
33 using GL_WIDGET_CLASS = QOpenGLWidget; 10 using GL_WIDGET_CLASS = QOpenGLWidget;
@@ -46,28 +23,30 @@ class DsoSettings; @@ -46,28 +23,30 @@ class DsoSettings;
46 /// \class GlScope glscope.h 23 /// \class GlScope glscope.h
47 /// \brief OpenGL accelerated widget that displays the oscilloscope screen. 24 /// \brief OpenGL accelerated widget that displays the oscilloscope screen.
48 class GlScope : public GL_WIDGET_CLASS { 25 class GlScope : public GL_WIDGET_CLASS {
49 - Q_OBJECT 26 + Q_OBJECT
50 27
51 public: 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 protected: 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 private: 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,6 +40,11 @@ const USBDevice *HantekDsoControl::getDevice() const
40 return device; 40 return device;
41 } 41 }
42 42
  43 +const DSOsamples &HantekDsoControl::getLastSamples()
  44 +{
  45 + return result;
  46 +}
  47 +
43 /// \brief Initializes the command buffers and lists. 48 /// \brief Initializes the command buffers and lists.
44 /// \param parent The parent widget. 49 /// \param parent The parent widget.
45 HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) { 50 HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) {
@@ -73,24 +78,24 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) { @@ -73,24 +78,24 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) {
73 } 78 }
74 79
75 // Set settings to default values 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 for (unsigned int channel = 0; channel < HANTEK_CHANNELS; ++channel) { 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 // Special trigger sources 100 // Special trigger sources
96 this->specialTriggerSources << tr("EXT") << tr("EXT/10"); 101 this->specialTriggerSources << tr("EXT") << tr("EXT/10");
@@ -117,7 +122,7 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) { @@ -117,7 +122,7 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) {
117 this->lastTriggerMode = (Dso::TriggerMode)-1; 122 this->lastTriggerMode = (Dso::TriggerMode)-1;
118 123
119 // Sample buffers 124 // Sample buffers
120 - this->samples.resize(HANTEK_CHANNELS); 125 + result.data.resize(HANTEK_CHANNELS);
121 126
122 this->previousSampleCount = 0; 127 this->previousSampleCount = 0;
123 128
@@ -390,9 +395,9 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) { @@ -390,9 +395,9 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) {
390 this->specification.sampleSize = 8; 395 this->specification.sampleSize = 8;
391 break; 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 this->previousSampleCount = 0; 401 this->previousSampleCount = 0;
397 402
398 // Get channel level data 403 // Get channel level data
@@ -408,16 +413,16 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) { @@ -408,16 +413,16 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) {
408 413
409 // Emit signals for initial settings 414 // Emit signals for initial settings
410 emit availableRecordLengthsChanged( 415 emit availableRecordLengthsChanged(
411 - this->settings.samplerate.limits->recordLengths); 416 + settings.samplerate.limits->recordLengths);
412 updateSamplerateLimits(); 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 if (this->device->getUniqueModelID() == MODEL_DSO6022BE) { 427 if (this->device->getUniqueModelID() == MODEL_DSO6022BE) {
423 QList<double> sampleSteps; 428 QList<double> sampleSteps;
@@ -445,7 +450,7 @@ unsigned int HantekDsoControl::getChannelCount() { return HANTEK_CHANNELS; } @@ -445,7 +450,7 @@ unsigned int HantekDsoControl::getChannelCount() { return HANTEK_CHANNELS; }
445 /// \brief Get available record lengths for this oscilloscope. 450 /// \brief Get available record lengths for this oscilloscope.
446 /// \return The number of physical channels, empty list for continuous. 451 /// \return The number of physical channels, empty list for continuous.
447 QList<unsigned int> *HantekDsoControl::getAvailableRecordLengths() { 452 QList<unsigned int> *HantekDsoControl::getAvailableRecordLengths() {
448 - return &this->settings.samplerate.limits->recordLengths; 453 + return &settings.samplerate.limits->recordLengths;
449 } 454 }
450 455
451 /// \brief Get minimum samplerate for this oscilloscope. 456 /// \brief Get minimum samplerate for this oscilloscope.
@@ -459,7 +464,7 @@ double HantekDsoControl::getMinSamplerate() { @@ -459,7 +464,7 @@ double HantekDsoControl::getMinSamplerate() {
459 /// \return The maximum samplerate for the current configuration in S/s. 464 /// \return The maximum samplerate for the current configuration in S/s.
460 double HantekDsoControl::getMaxSamplerate() { 465 double HantekDsoControl::getMaxSamplerate() {
461 ControlSamplerateLimits *limits = 466 ControlSamplerateLimits *limits =
462 - (this->settings.usedChannels <= 1) 467 + (settings.usedChannels <= 1)
463 ? &this->specification.samplerate.multi 468 ? &this->specification.samplerate.multi
464 : &this->specification.samplerate.single; 469 : &this->specification.samplerate.single;
465 return limits->max; 470 return limits->max;
@@ -469,18 +474,18 @@ double HantekDsoControl::getMaxSamplerate() { @@ -469,18 +474,18 @@ double HantekDsoControl::getMaxSamplerate() {
469 void HantekDsoControl::updateInterval() { 474 void HantekDsoControl::updateInterval() {
470 // Check the current oscilloscope state everytime 25% of the time the buffer 475 // Check the current oscilloscope state everytime 25% of the time the buffer
471 // should be refilled 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 cycleTime = (int)((double)this->device->getPacketSize() / 479 cycleTime = (int)((double)this->device->getPacketSize() /
475 - ((this->settings.samplerate.limits == 480 + ((settings.samplerate.limits ==
476 &this->specification.samplerate.multi) 481 &this->specification.samplerate.multi)
477 ? 1 482 ? 1
478 : HANTEK_CHANNELS) / 483 : HANTEK_CHANNELS) /
479 - this->settings.samplerate.current * 250); 484 + settings.samplerate.current * 250);
480 else 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 // Not more often than every 10 ms though but at least once every second 490 // Not more often than every 10 ms though but at least once every second
486 cycleTime = qBound(10, cycleTime, 1000); 491 cycleTime = qBound(10, cycleTime, 1000);
@@ -519,7 +524,7 @@ int HantekDsoControl::getCaptureState() { @@ -519,7 +524,7 @@ int HantekDsoControl::getCaptureState() {
519 if (errorCode < 0) 524 if (errorCode < 0)
520 return errorCode; 525 return errorCode;
521 526
522 - this->settings.trigger.point = 527 + settings.trigger.point =
523 this->calculateTriggerPoint(response.getTriggerPoint()); 528 this->calculateTriggerPoint(response.getTriggerPoint());
524 529
525 return (int)response.getCaptureState(); 530 return (int)response.getCaptureState();
@@ -571,192 +576,194 @@ int HantekDsoControl::getSamples(bool process) { @@ -571,192 +576,194 @@ int HantekDsoControl::getSamples(bool process) {
571 return errorCode; 576 return errorCode;
572 577
573 // Process the data only if we want it 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 // Resize sample vector 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 // Convert data from the oscilloscope and write it into the sample 686 // Convert data from the oscilloscope and write it into the sample
607 // buffer 687 // buffer
608 - unsigned int bufferPosition = this->settings.trigger.point * 2; 688 + unsigned int bufferPosition = settings.trigger.point * 2;
609 if (this->specification.sampleSize > 8) { 689 if (this->specification.sampleSize > 8) {
610 // Additional most significant bits after the normal data 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 unsigned int extraBitsSize = 691 unsigned int extraBitsSize =
614 this->specification.sampleSize - 8; // Number of extra bits 692 this->specification.sampleSize - 8; // Number of extra bits
615 unsigned short int extraBitsMask = 693 unsigned short int extraBitsMask =
616 (0x00ff << extraBitsSize) & 694 (0x00ff << extraBitsSize) &
617 0xff00; // Mask for extra bits extraction 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 for (unsigned int realPosition = 0; realPosition < sampleCount; 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 this->specification 713 this->specification
636 .voltageLimit[channel] 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 this->specification 717 this->specification
640 - .gainSteps[this->settings.voltage[channel].gain]; 718 + .gainSteps[settings.voltage[channel].gain];
641 } 719 }
642 } else { 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 for (unsigned int realPosition = 0; realPosition < sampleCount; 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 this->specification 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 this->specification 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 return errorCode; 767 return errorCode;
761 } 768 }
762 769
@@ -787,18 +794,18 @@ double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate, @@ -787,18 +794,18 @@ double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate,
787 // Get downsampling factor that would provide the requested rate 794 // Get downsampling factor that would provide the requested rate
788 double bestDownsampler = 795 double bestDownsampler =
789 (double)limits->base / 796 (double)limits->base /
790 - this->specification.bufferDividers[this->settings.recordLengthId] / 797 + this->specification.bufferDividers[settings.recordLengthId] /
791 samplerate; 798 samplerate;
792 // Base samplerate sufficient, or is the maximum better? 799 // Base samplerate sufficient, or is the maximum better?
793 if (bestDownsampler < 1.0 && 800 if (bestDownsampler < 1.0 &&
794 (samplerate <= limits->max / 801 (samplerate <= limits->max /
795 this->specification 802 this->specification
796 - .bufferDividers[this->settings.recordLengthId] || 803 + .bufferDividers[settings.recordLengthId] ||
797 !maximum)) { 804 !maximum)) {
798 bestDownsampler = 0.0; 805 bestDownsampler = 0.0;
799 bestSamplerate = 806 bestSamplerate =
800 limits->max / 807 limits->max /
801 - this->specification.bufferDividers[this->settings.recordLengthId]; 808 + this->specification.bufferDividers[settings.recordLengthId];
802 } else { 809 } else {
803 switch (this->specification.command.bulk.setSamplerate) { 810 switch (this->specification.command.bulk.setSamplerate) {
804 case BULK_SETTRIGGERANDSAMPLERATE: 811 case BULK_SETTRIGGERANDSAMPLERATE:
@@ -866,7 +873,7 @@ double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate, @@ -866,7 +873,7 @@ double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate,
866 873
867 bestSamplerate = 874 bestSamplerate =
868 limits->base / bestDownsampler / 875 limits->base / bestDownsampler /
869 - this->specification.bufferDividers[this->settings.recordLengthId]; 876 + this->specification.bufferDividers[settings.recordLengthId];
870 } 877 }
871 878
872 if (downsampler) 879 if (downsampler)
@@ -878,11 +885,8 @@ double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate, @@ -878,11 +885,8 @@ double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate,
878 /// \param fastRate Is set to the state of the fast rate mode when provided. 885 /// \param fastRate Is set to the state of the fast rate mode when provided.
879 /// \return The total number of samples the scope should return. 886 /// \return The total number of samples the scope should return.
880 unsigned int HantekDsoControl::getSampleCount(bool *fastRate) { 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 if (totalSampleCount == UINT_MAX) { 891 if (totalSampleCount == UINT_MAX) {
888 // Roll mode 892 // Roll mode
@@ -905,7 +909,7 @@ unsigned int HantekDsoControl::getSampleCount(bool *fastRate) { @@ -905,7 +909,7 @@ unsigned int HantekDsoControl::getSampleCount(bool *fastRate) {
905 /// \return The record length that has been set, 0 on error. 909 /// \return The record length that has been set, 0 on error.
906 unsigned int HantekDsoControl::updateRecordLength(unsigned int index) { 910 unsigned int HantekDsoControl::updateRecordLength(unsigned int index) {
907 if (index >= 911 if (index >=
908 - (unsigned int)this->settings.samplerate.limits->recordLengths.size()) 912 + (unsigned int)settings.samplerate.limits->recordLengths.size())
909 return 0; 913 return 0;
910 914
911 switch (this->specification.command.bulk.setRecordLength) { 915 switch (this->specification.command.bulk.setRecordLength) {
@@ -947,9 +951,9 @@ unsigned int HantekDsoControl::updateRecordLength(unsigned int index) { @@ -947,9 +951,9 @@ unsigned int HantekDsoControl::updateRecordLength(unsigned int index) {
947 // Check if the divider has changed and adapt samplerate limits accordingly 951 // Check if the divider has changed and adapt samplerate limits accordingly
948 bool bDividerChanged = 952 bool bDividerChanged =
949 this->specification.bufferDividers[index] != 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 if (bDividerChanged) { 958 if (bDividerChanged) {
955 this->updateSamplerateLimits(); 959 this->updateSamplerateLimits();
@@ -958,7 +962,7 @@ unsigned int HantekDsoControl::updateRecordLength(unsigned int index) { @@ -958,7 +962,7 @@ unsigned int HantekDsoControl::updateRecordLength(unsigned int index) {
958 this->restoreTargets(); 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 /// \brief Sets the samplerate based on the parameters calculated by 968 /// \brief Sets the samplerate based on the parameters calculated by
@@ -1068,49 +1072,49 @@ unsigned int HantekDsoControl::updateSamplerate(unsigned int downsampler, @@ -1068,49 +1072,49 @@ unsigned int HantekDsoControl::updateSamplerate(unsigned int downsampler,
1068 } 1072 }
1069 1073
1070 // Update settings 1074 // Update settings
1071 - bool fastRateChanged = fastRate != (this->settings.samplerate.limits == 1075 + bool fastRateChanged = fastRate != (settings.samplerate.limits ==
1072 &this->specification.samplerate.multi); 1076 &this->specification.samplerate.multi);
1073 if (fastRateChanged) { 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 if (downsampler) 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 downsampler; 1086 downsampler;
1083 else 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 // Update dependencies 1092 // Update dependencies
1089 - this->setPretriggerPosition(this->settings.trigger.position); 1093 + this->setPretriggerPosition(settings.trigger.position);
1090 1094
1091 // Emit signals for changed settings 1095 // Emit signals for changed settings
1092 if (fastRateChanged) { 1096 if (fastRateChanged) {
1093 emit availableRecordLengthsChanged( 1097 emit availableRecordLengthsChanged(
1094 - this->settings.samplerate.limits->recordLengths); 1098 + settings.samplerate.limits->recordLengths);
1095 emit recordLengthChanged( 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 // Check for Roll mode 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 return downsampler; 1112 return downsampler;
1109 } 1113 }
1110 1114
1111 /// \brief Restore the samplerate/timebase targets after divider updates. 1115 /// \brief Restore the samplerate/timebase targets after divider updates.
1112 void HantekDsoControl::restoreTargets() { 1116 void HantekDsoControl::restoreTargets() {
1113 - if (this->settings.samplerate.target.samplerateSet) 1117 + if (settings.samplerate.target.samplerateSet)
1114 this->setSamplerate(); 1118 this->setSamplerate();
1115 else 1119 else
1116 this->setRecordTime(); 1120 this->setRecordTime();
@@ -1121,15 +1125,15 @@ void HantekDsoControl::updateSamplerateLimits() { @@ -1121,15 +1125,15 @@ void HantekDsoControl::updateSamplerateLimits() {
1121 // Works only if the minimum samplerate for normal mode is lower than for fast 1125 // Works only if the minimum samplerate for normal mode is lower than for fast
1122 // rate mode, which is the case for all models 1126 // rate mode, which is the case for all models
1123 ControlSamplerateLimits *limits = 1127 ControlSamplerateLimits *limits =
1124 - (this->settings.usedChannels <= 1) 1128 + (settings.usedChannels <= 1)
1125 ? &this->specification.samplerate.multi 1129 ? &this->specification.samplerate.multi
1126 : &this->specification.samplerate.single; 1130 : &this->specification.samplerate.single;
1127 emit samplerateLimitsChanged( 1131 emit samplerateLimitsChanged(
1128 (double)this->specification.samplerate.single.base / 1132 (double)this->specification.samplerate.single.base /
1129 this->specification.samplerate.single.maxDownsampler / 1133 this->specification.samplerate.single.maxDownsampler /
1130 - this->specification.bufferDividers[this->settings.recordLengthId], 1134 + this->specification.bufferDividers[settings.recordLengthId],
1131 limits->max / 1135 limits->max /
1132 - this->specification.bufferDividers[this->settings.recordLengthId]); 1136 + this->specification.bufferDividers[settings.recordLengthId]);
1133 } 1137 }
1134 1138
1135 /// \brief Sets the size of the oscilloscopes sample buffer. 1139 /// \brief Sets the size of the oscilloscopes sample buffer.
@@ -1143,12 +1147,12 @@ unsigned int HantekDsoControl::setRecordLength(unsigned int index) { @@ -1143,12 +1147,12 @@ unsigned int HantekDsoControl::setRecordLength(unsigned int index) {
1143 return 0; 1147 return 0;
1144 1148
1145 this->restoreTargets(); 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 /// \brief Sets the samplerate of the oscilloscope. 1158 /// \brief Sets the samplerate of the oscilloscope.
@@ -1160,20 +1164,20 @@ double HantekDsoControl::setSamplerate(double samplerate) { @@ -1160,20 +1164,20 @@ double HantekDsoControl::setSamplerate(double samplerate) {
1160 return 0.0; 1164 return 0.0;
1161 1165
1162 if (samplerate == 0.0) { 1166 if (samplerate == 0.0) {
1163 - samplerate = this->settings.samplerate.target.samplerate; 1167 + samplerate = settings.samplerate.target.samplerate;
1164 } else { 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 if (this->device->getUniqueModelID() != MODEL_DSO6022BE) { 1173 if (this->device->getUniqueModelID() != MODEL_DSO6022BE) {
1170 // When possible, enable fast rate if it is required to reach the requested 1174 // When possible, enable fast rate if it is required to reach the requested
1171 // samplerate 1175 // samplerate
1172 bool fastRate = 1176 bool fastRate =
1173 - (this->settings.usedChannels <= 1) && 1177 + (settings.usedChannels <= 1) &&
1174 (samplerate > 1178 (samplerate >
1175 this->specification.samplerate.single.max / 1179 this->specification.samplerate.single.max /
1176 - this->specification.bufferDividers[this->settings.recordLengthId]); 1180 + this->specification.bufferDividers[settings.recordLengthId]);
1177 1181
1178 // What is the nearest, at least as high samplerate the scope can provide? 1182 // What is the nearest, at least as high samplerate the scope can provide?
1179 unsigned int downsampler = 0; 1183 unsigned int downsampler = 0;
@@ -1196,18 +1200,18 @@ double HantekDsoControl::setSamplerate(double samplerate) { @@ -1196,18 +1200,18 @@ double HantekDsoControl::setSamplerate(double samplerate) {
1196 static_cast<ControlSetTimeDIV *>(this->control[CONTROLINDEX_SETTIMEDIV]) 1200 static_cast<ControlSetTimeDIV *>(this->control[CONTROLINDEX_SETTIMEDIV])
1197 ->setDiv(this->specification.sampleDiv[sampleId]); 1201 ->setDiv(this->specification.sampleDiv[sampleId]);
1198 this->controlPending[CONTROLINDEX_SETTIMEDIV] = true; 1202 this->controlPending[CONTROLINDEX_SETTIMEDIV] = true;
1199 - this->settings.samplerate.current = samplerate; 1203 + settings.samplerate.current = samplerate;
1200 1204
1201 // Provide margin for SW trigger 1205 // Provide margin for SW trigger
1202 unsigned int sampleMargin = 2000; 1206 unsigned int sampleMargin = 2000;
1203 // Check for Roll mode 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 emit recordTimeChanged( 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 return samplerate; 1216 return samplerate;
1213 } 1217 }
@@ -1222,26 +1226,26 @@ double HantekDsoControl::setRecordTime(double duration) { @@ -1222,26 +1226,26 @@ double HantekDsoControl::setRecordTime(double duration) {
1222 return 0.0; 1226 return 0.0;
1223 1227
1224 if (duration == 0.0) { 1228 if (duration == 0.0) {
1225 - duration = this->settings.samplerate.target.duration; 1229 + duration = settings.samplerate.target.duration;
1226 } else { 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 if (this->device->getUniqueModelID() != MODEL_DSO6022BE) { 1235 if (this->device->getUniqueModelID() != MODEL_DSO6022BE) {
1232 // Calculate the maximum samplerate that would still provide the requested 1236 // Calculate the maximum samplerate that would still provide the requested
1233 // duration 1237 // duration
1234 double maxSamplerate = (double)this->specification.samplerate.single 1238 double maxSamplerate = (double)this->specification.samplerate.single
1235 - .recordLengths[this->settings.recordLengthId] / 1239 + .recordLengths[settings.recordLengthId] /
1236 duration; 1240 duration;
1237 1241
1238 // When possible, enable fast rate if the record time can't be set that low 1242 // When possible, enable fast rate if the record time can't be set that low
1239 // to improve resolution 1243 // to improve resolution
1240 bool fastRate = 1244 bool fastRate =
1241 - (this->settings.usedChannels <= 1) && 1245 + (settings.usedChannels <= 1) &&
1242 (maxSamplerate >= 1246 (maxSamplerate >=
1243 this->specification.samplerate.multi.base / 1247 this->specification.samplerate.multi.base /
1244 - this->specification.bufferDividers[this->settings.recordLengthId]); 1248 + this->specification.bufferDividers[settings.recordLengthId]);
1245 1249
1246 // What is the nearest, at most as high samplerate the scope can provide? 1250 // What is the nearest, at most as high samplerate the scope can provide?
1247 unsigned int downsampler = 0; 1251 unsigned int downsampler = 0;
@@ -1252,8 +1256,8 @@ double HantekDsoControl::setRecordTime(double duration) { @@ -1252,8 +1256,8 @@ double HantekDsoControl::setRecordTime(double duration) {
1252 if (this->updateSamplerate(downsampler, fastRate) == UINT_MAX) 1256 if (this->updateSamplerate(downsampler, fastRate) == UINT_MAX)
1253 return 0.0; 1257 return 0.0;
1254 else { 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 bestSamplerate; 1261 bestSamplerate;
1258 } 1262 }
1259 } else { 1263 } else {
@@ -1278,11 +1282,11 @@ double HantekDsoControl::setRecordTime(double duration) { @@ -1278,11 +1282,11 @@ double HantekDsoControl::setRecordTime(double duration) {
1278 static_cast<ControlSetTimeDIV *>(this->control[CONTROLINDEX_SETTIMEDIV]) 1282 static_cast<ControlSetTimeDIV *>(this->control[CONTROLINDEX_SETTIMEDIV])
1279 ->setDiv(this->specification.sampleDiv[sampleId]); 1283 ->setDiv(this->specification.sampleDiv[sampleId]);
1280 this->controlPending[CONTROLINDEX_SETTIMEDIV] = true; 1284 this->controlPending[CONTROLINDEX_SETTIMEDIV] = true;
1281 - this->settings.samplerate.current = 1285 + settings.samplerate.current =
1282 this->specification.sampleSteps[sampleId]; 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,19 +1302,19 @@ int HantekDsoControl::setChannelUsed(unsigned int channel, bool used) {
1298 return Dso::ERROR_PARAMETER; 1302 return Dso::ERROR_PARAMETER;
1299 1303
1300 // Update settings 1304 // Update settings
1301 - this->settings.voltage[channel].used = used; 1305 + settings.voltage[channel].used = used;
1302 unsigned int channelCount = 0; 1306 unsigned int channelCount = 0;
1303 for (int channelCounter = 0; channelCounter < HANTEK_CHANNELS; 1307 for (int channelCounter = 0; channelCounter < HANTEK_CHANNELS;
1304 ++channelCounter) { 1308 ++channelCounter) {
1305 - if (this->settings.voltage[channelCounter].used) 1309 + if (settings.voltage[channelCounter].used)
1306 ++channelCount; 1310 ++channelCount;
1307 } 1311 }
1308 1312
1309 // Calculate the UsedChannels field for the command 1313 // Calculate the UsedChannels field for the command
1310 unsigned char usedChannels = USED_CH1; 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 usedChannels = USED_CH1CH2; 1318 usedChannels = USED_CH1CH2;
1315 } else { 1319 } else {
1316 // DSO-2250 uses a different value for channel 2 1320 // DSO-2250 uses a different value for channel 2
@@ -1352,8 +1356,8 @@ int HantekDsoControl::setChannelUsed(unsigned int channel, bool used) { @@ -1352,8 +1356,8 @@ int HantekDsoControl::setChannelUsed(unsigned int channel, bool used) {
1352 1356
1353 // Check if fast rate mode availability changed 1357 // Check if fast rate mode availability changed
1354 bool fastRateChanged = 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 if (fastRateChanged) 1362 if (fastRateChanged)
1359 this->updateSamplerateLimits(); 1363 this->updateSamplerateLimits();
@@ -1430,9 +1434,9 @@ double HantekDsoControl::setGain(unsigned int channel, double gain) { @@ -1430,9 +1434,9 @@ double HantekDsoControl::setGain(unsigned int channel, double gain) {
1430 this->controlPending[CONTROLINDEX_SETRELAYS] = true; 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 return this->specification.gainSteps[gainId]; 1441 return this->specification.gainSteps[gainId];
1438 } 1442 }
@@ -1453,23 +1457,23 @@ double HantekDsoControl::setOffset(unsigned int channel, double offset) { @@ -1453,23 +1457,23 @@ double HantekDsoControl::setOffset(unsigned int channel, double offset) {
1453 unsigned short int minimum = 1457 unsigned short int minimum =
1454 ((unsigned short int)*((unsigned char *)&( 1458 ((unsigned short int)*((unsigned char *)&(
1455 this->specification 1459 this->specification
1456 - .offsetLimit[channel][this->settings.voltage[channel].gain] 1460 + .offsetLimit[channel][settings.voltage[channel].gain]
1457 [OFFSET_START])) 1461 [OFFSET_START]))
1458 << 8) + 1462 << 8) +
1459 *((unsigned char *)&( 1463 *((unsigned char *)&(
1460 this->specification 1464 this->specification
1461 - .offsetLimit[channel][this->settings.voltage[channel].gain] 1465 + .offsetLimit[channel][settings.voltage[channel].gain]
1462 [OFFSET_START]) + 1466 [OFFSET_START]) +
1463 1); 1467 1);
1464 unsigned short int maximum = 1468 unsigned short int maximum =
1465 ((unsigned short int)*((unsigned char *)&( 1469 ((unsigned short int)*((unsigned char *)&(
1466 this->specification 1470 this->specification
1467 - .offsetLimit[channel][this->settings.voltage[channel].gain] 1471 + .offsetLimit[channel][settings.voltage[channel].gain]
1468 [OFFSET_END])) 1472 [OFFSET_END]))
1469 << 8) + 1473 << 8) +
1470 *((unsigned char *)&( 1474 *((unsigned char *)&(
1471 this->specification 1475 this->specification
1472 - .offsetLimit[channel][this->settings.voltage[channel].gain] 1476 + .offsetLimit[channel][settings.voltage[channel].gain]
1473 [OFFSET_END]) + 1477 [OFFSET_END]) +
1474 1); 1478 1);
1475 unsigned short int offsetValue = offset * (maximum - minimum) + minimum + 0.5; 1479 unsigned short int offsetValue = offset * (maximum - minimum) + minimum + 0.5;
@@ -1484,10 +1488,10 @@ double HantekDsoControl::setOffset(unsigned int channel, double offset) { @@ -1484,10 +1488,10 @@ double HantekDsoControl::setOffset(unsigned int channel, double offset) {
1484 this->controlPending[CONTROLINDEX_SETOFFSET] = true; 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 return offsetReal; 1496 return offsetReal;
1493 } 1497 }
@@ -1501,7 +1505,7 @@ int HantekDsoControl::setTriggerMode(Dso::TriggerMode mode) { @@ -1501,7 +1505,7 @@ int HantekDsoControl::setTriggerMode(Dso::TriggerMode mode) {
1501 if (mode < Dso::TRIGGERMODE_AUTO || mode >= Dso::TRIGGERMODE_COUNT) 1505 if (mode < Dso::TRIGGERMODE_AUTO || mode >= Dso::TRIGGERMODE_COUNT)
1502 return Dso::ERROR_PARAMETER; 1506 return Dso::ERROR_PARAMETER;
1503 1507
1504 - this->settings.trigger.mode = mode; 1508 + settings.trigger.mode = mode;
1505 return Dso::ERROR_NONE; 1509 return Dso::ERROR_NONE;
1506 } 1510 }
1507 1511
@@ -1551,8 +1555,8 @@ int HantekDsoControl::setTriggerSource(bool special, unsigned int id) { @@ -1551,8 +1555,8 @@ int HantekDsoControl::setTriggerSource(bool special, unsigned int id) {
1551 ->setTrigger(special); 1555 ->setTrigger(special);
1552 this->controlPending[CONTROLINDEX_SETRELAYS] = true; 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 // Apply trigger level of the new source 1561 // Apply trigger level of the new source
1558 if (special) { 1562 if (special) {
@@ -1561,7 +1565,7 @@ int HantekDsoControl::setTriggerSource(bool special, unsigned int id) { @@ -1561,7 +1565,7 @@ int HantekDsoControl::setTriggerSource(bool special, unsigned int id) {
1561 ->setTrigger(0x7f); 1565 ->setTrigger(0x7f);
1562 this->controlPending[CONTROLINDEX_SETOFFSET] = true; 1566 this->controlPending[CONTROLINDEX_SETOFFSET] = true;
1563 } else 1567 } else
1564 - this->setTriggerLevel(id, this->settings.trigger.level[id]); 1568 + this->setTriggerLevel(id, settings.trigger.level[id]);
1565 1569
1566 return Dso::ERROR_NONE; 1570 return Dso::ERROR_NONE;
1567 } 1571 }
@@ -1589,23 +1593,23 @@ double HantekDsoControl::setTriggerLevel(unsigned int channel, double level) { @@ -1589,23 +1593,23 @@ double HantekDsoControl::setTriggerLevel(unsigned int channel, double level) {
1589 minimum = 1593 minimum =
1590 ((unsigned short int)*((unsigned char *)&( 1594 ((unsigned short int)*((unsigned char *)&(
1591 this->specification 1595 this->specification
1592 - .offsetLimit[channel][this->settings.voltage[channel].gain] 1596 + .offsetLimit[channel][settings.voltage[channel].gain]
1593 [OFFSET_START])) 1597 [OFFSET_START]))
1594 << 8) + 1598 << 8) +
1595 *((unsigned char *)&( 1599 *((unsigned char *)&(
1596 this->specification 1600 this->specification
1597 - .offsetLimit[channel][this->settings.voltage[channel].gain] 1601 + .offsetLimit[channel][settings.voltage[channel].gain]
1598 [OFFSET_START]) + 1602 [OFFSET_START]) +
1599 1); 1603 1);
1600 maximum = 1604 maximum =
1601 ((unsigned short int)*((unsigned char *)&( 1605 ((unsigned short int)*((unsigned char *)&(
1602 this->specification 1606 this->specification
1603 - .offsetLimit[channel][this->settings.voltage[channel].gain] 1607 + .offsetLimit[channel][settings.voltage[channel].gain]
1604 [OFFSET_END])) 1608 [OFFSET_END]))
1605 << 8) + 1609 << 8) +
1606 *((unsigned char *)&( 1610 *((unsigned char *)&(
1607 this->specification 1611 this->specification
1608 - .offsetLimit[channel][this->settings.voltage[channel].gain] 1612 + .offsetLimit[channel][settings.voltage[channel].gain]
1609 [OFFSET_END]) + 1613 [OFFSET_END]) +
1610 1); 1614 1);
1611 break; 1615 break;
@@ -1620,18 +1624,18 @@ double HantekDsoControl::setTriggerLevel(unsigned int channel, double level) { @@ -1620,18 +1624,18 @@ double HantekDsoControl::setTriggerLevel(unsigned int channel, double level) {
1620 // Never get out of the limits 1624 // Never get out of the limits
1621 unsigned short int levelValue = qBound( 1625 unsigned short int levelValue = qBound(
1622 (long int)minimum, 1626 (long int)minimum,
1623 - (long int)((this->settings.voltage[channel].offsetReal + 1627 + (long int)((settings.voltage[channel].offsetReal +
1624 level / 1628 level /
1625 this->specification 1629 this->specification
1626 - .gainSteps[this->settings.voltage[channel].gain]) * 1630 + .gainSteps[settings.voltage[channel].gain]) *
1627 (maximum - minimum) + 1631 (maximum - minimum) +
1628 0.5) + 1632 0.5) +
1629 minimum, 1633 minimum,
1630 (long int)maximum); 1634 (long int)maximum);
1631 1635
1632 // Check if the set channel is the trigger source 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 this->device->getUniqueModelID() != MODEL_DSO6022BE) { 1639 this->device->getUniqueModelID() != MODEL_DSO6022BE) {
1636 // SetOffset control command for trigger level 1640 // SetOffset control command for trigger level
1637 static_cast<ControlSetOffset *>(this->control[CONTROLINDEX_SETOFFSET]) 1641 static_cast<ControlSetOffset *>(this->control[CONTROLINDEX_SETOFFSET])
@@ -1641,10 +1645,10 @@ double HantekDsoControl::setTriggerLevel(unsigned int channel, double level) { @@ -1641,10 +1645,10 @@ double HantekDsoControl::setTriggerLevel(unsigned int channel, double level) {
1641 1645
1642 /// \todo Get alternating trigger in here 1646 /// \todo Get alternating trigger in here
1643 1647
1644 - this->settings.trigger.level[channel] = level; 1648 + settings.trigger.level[channel] = level;
1645 return (double)((levelValue - minimum) / (maximum - minimum) - 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 /// \brief Set the trigger slope. 1654 /// \brief Set the trigger slope.
@@ -1686,7 +1690,7 @@ int HantekDsoControl::setTriggerSlope(Dso::Slope slope) { @@ -1686,7 +1690,7 @@ int HantekDsoControl::setTriggerSlope(Dso::Slope slope) {
1686 return Dso::ERROR_UNSUPPORTED; 1690 return Dso::ERROR_UNSUPPORTED;
1687 } 1691 }
1688 1692
1689 - this->settings.trigger.slope = slope; 1693 + settings.trigger.slope = slope;
1690 return Dso::ERROR_NONE; 1694 return Dso::ERROR_NONE;
1691 } 1695 }
1692 1696
@@ -1703,13 +1707,13 @@ double HantekDsoControl::setPretriggerPosition(double position) { @@ -1703,13 +1707,13 @@ double HantekDsoControl::setPretriggerPosition(double position) {
1703 return -2; 1707 return -2;
1704 1708
1705 // All trigger positions are measured in samples 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 unsigned int recordLength = 1711 unsigned int recordLength =
1708 - this->settings.samplerate.limits  
1709 - ->recordLengths[this->settings.recordLengthId]; 1712 + settings.samplerate.limits
  1713 + ->recordLengths[settings.recordLengthId];
1710 bool rollMode = recordLength == UINT_MAX; 1714 bool rollMode = recordLength == UINT_MAX;
1711 // Fast rate mode uses both channels 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 positionSamples /= HANTEK_CHANNELS; 1717 positionSamples /= HANTEK_CHANNELS;
1714 1718
1715 switch (this->specification.command.bulk.setPretrigger) { 1719 switch (this->specification.command.bulk.setPretrigger) {
@@ -1758,8 +1762,8 @@ double HantekDsoControl::setPretriggerPosition(double position) { @@ -1758,8 +1762,8 @@ double HantekDsoControl::setPretriggerPosition(double position) {
1758 return Dso::ERROR_UNSUPPORTED; 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 #ifdef DEBUG 1769 #ifdef DEBUG
@@ -1888,8 +1892,8 @@ void HantekDsoControl::run() { @@ -1888,8 +1892,8 @@ void HantekDsoControl::run() {
1888 } 1892 }
1889 1893
1890 // State machine for the device communication 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 // Roll mode 1897 // Roll mode
1894 this->captureState = CAPTURE_WAITING; 1898 this->captureState = CAPTURE_WAITING;
1895 bool toNextState = true; 1899 bool toNextState = true;
@@ -1964,7 +1968,7 @@ void HantekDsoControl::run() { @@ -1964,7 +1968,7 @@ void HantekDsoControl::run() {
1964 #endif 1968 #endif
1965 1969
1966 // Check if we're in single trigger mode 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 this->_samplingStarted) 1972 this->_samplingStarted)
1969 this->stopSampling(); 1973 this->stopSampling();
1970 1974
@@ -2016,7 +2020,7 @@ void HantekDsoControl::run() { @@ -2016,7 +2020,7 @@ void HantekDsoControl::run() {
2016 #endif 2020 #endif
2017 2021
2018 // Check if we're in single trigger mode 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 this->_samplingStarted) 2024 this->_samplingStarted)
2021 this->stopSampling(); 2025 this->stopSampling();
2022 2026
@@ -2032,12 +2036,12 @@ void HantekDsoControl::run() { @@ -2032,12 +2036,12 @@ void HantekDsoControl::run() {
2032 this->previousSampleCount = this->getSampleCount(); 2036 this->previousSampleCount = this->getSampleCount();
2033 2037
2034 if (this->_samplingStarted && 2038 if (this->_samplingStarted &&
2035 - this->lastTriggerMode == this->settings.trigger.mode) { 2039 + this->lastTriggerMode == settings.trigger.mode) {
2036 ++this->cycleCounter; 2040 ++this->cycleCounter;
2037 2041
2038 if (this->cycleCounter == this->startCycle && 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 UINT_MAX) { 2045 UINT_MAX) {
2042 // Buffer refilled completely since start of sampling, enable the 2046 // Buffer refilled completely since start of sampling, enable the
2043 // trigger now 2047 // trigger now
@@ -2054,7 +2058,7 @@ void HantekDsoControl::run() { @@ -2054,7 +2058,7 @@ void HantekDsoControl::run() {
2054 timestampDebug("Enabling trigger"); 2058 timestampDebug("Enabling trigger");
2055 #endif 2059 #endif
2056 } else if (this->cycleCounter >= 8 + this->startCycle && 2060 } else if (this->cycleCounter >= 8 + this->startCycle &&
2057 - this->settings.trigger.mode == Dso::TRIGGERMODE_AUTO) { 2061 + settings.trigger.mode == Dso::TRIGGERMODE_AUTO) {
2058 // Force triggering 2062 // Force triggering
2059 errorCode = 2063 errorCode =
2060 this->device->bulkCommand(this->command[BULK_FORCETRIGGER]); 2064 this->device->bulkCommand(this->command[BULK_FORCETRIGGER]);
@@ -2090,8 +2094,8 @@ void HantekDsoControl::run() { @@ -2090,8 +2094,8 @@ void HantekDsoControl::run() {
2090 2094
2091 this->_samplingStarted = true; 2095 this->_samplingStarted = true;
2092 this->cycleCounter = 0; 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 break; 2099 break;
2096 2100
2097 case CAPTURE_SAMPLING: 2101 case CAPTURE_SAMPLING:
openhantek/src/hantek/hantekdsocontrol.h
@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
7 #include "bulkStructs.h" 7 #include "bulkStructs.h"
8 #include "stateStructs.h" 8 #include "stateStructs.h"
9 #include "utils/printutils.h" 9 #include "utils/printutils.h"
  10 +#include "dsosamples.h"
10 11
11 #include <vector> 12 #include <vector>
12 13
@@ -17,14 +18,12 @@ @@ -17,14 +18,12 @@
17 18
18 class USBDevice; 19 class USBDevice;
19 20
20 -//////////////////////////////////////////////////////////////////////////////  
21 -/// \class Control hantek/control.h  
22 /// \brief The DsoControl abstraction layer for %Hantek USB DSOs. 21 /// \brief The DsoControl abstraction layer for %Hantek USB DSOs.
23 class HantekDsoControl : public QObject { 22 class HantekDsoControl : public QObject {
24 Q_OBJECT 23 Q_OBJECT
25 24
26 public: 25 public:
27 - /** 26 + /**
28 * Creates a dsoControl object. The actual event loop / timer is not started. 27 * Creates a dsoControl object. The actual event loop / timer is not started.
29 * You can optionally create a thread and move the created object to the thread. 28 * You can optionally create a thread and move the created object to the thread.
30 * You need to call updateInterval() to start the timer. 29 * You need to call updateInterval() to start the timer.
@@ -44,15 +43,13 @@ public: @@ -44,15 +43,13 @@ public:
44 43
45 const QStringList *getSpecialTriggerSources(); 44 const QStringList *getSpecialTriggerSources();
46 const USBDevice* getDevice() const; 45 const USBDevice* getDevice() const;
  46 + const DSOsamples& getLastSamples();
47 47
48 signals: 48 signals:
49 void samplingStarted(); ///< The oscilloscope started sampling/waiting for trigger 49 void samplingStarted(); ///< The oscilloscope started sampling/waiting for trigger
50 void samplingStopped(); ///< The oscilloscope stopped sampling/waiting for trigger 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 void availableRecordLengthsChanged(const QList<unsigned int> &recordLengths); ///< The available record lengths, empty list for continuous 54 void availableRecordLengthsChanged(const QList<unsigned int> &recordLengths); ///< The available record lengths, empty list for continuous
58 void samplerateLimitsChanged(double minimum, double maximum); ///< The minimum or maximum samplerate has changed 55 void samplerateLimitsChanged(double minimum, double maximum); ///< The minimum or maximum samplerate has changed
@@ -93,10 +90,8 @@ protected: @@ -93,10 +90,8 @@ protected:
93 Hantek::ControlSettings settings; ///< The current settings of the device 90 Hantek::ControlSettings settings; ///< The current settings of the device
94 91
95 // Results 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 // State of the communication thread 96 // State of the communication thread
102 int captureState = Hantek::CAPTURE_WAITING; 97 int captureState = Hantek::CAPTURE_WAITING;
openhantek/src/main.cpp
@@ -119,25 +119,38 @@ int main(int argc, char *argv[]) { @@ -119,25 +119,38 @@ int main(int argc, char *argv[]) {
119 119
120 //////// Create DSO control object and move it to a separate thread //////// 120 //////// Create DSO control object and move it to a separate thread ////////
121 QThread dsoControlThread; 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 //////// Create data analyser object //////// 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 //////// Create main window //////// 140 //////// Create main window ////////
132 - OpenHantekMainWindow *openHantekMainWindow = new OpenHantekMainWindow(dsoControl, dataAnalyser); 141 + OpenHantekMainWindow *openHantekMainWindow = new OpenHantekMainWindow(&dsoControl, &dataAnalyser);
133 openHantekMainWindow->show(); 142 openHantekMainWindow->show();
134 143
135 //////// Start DSO thread and go into GUI main loop 144 //////// Start DSO thread and go into GUI main loop
  145 + dataAnalyzerThread.start();
136 dsoControlThread.start(); 146 dsoControlThread.start();
137 int res = openHantekApplication.exec(); 147 int res = openHantekApplication.exec();
138 148
139 //////// Clean up //////// 149 //////// Clean up ////////
140 dsoControlThread.quit(); 150 dsoControlThread.quit();
141 dsoControlThread.wait(10000); 151 dsoControlThread.wait(10000);
  152 +
  153 + dataAnalyzerThread.quit();
  154 + dataAnalyzerThread.wait(10000);
142 return res; 155 return res;
143 } 156 }
openhantek/src/mainwindow.cpp
@@ -28,7 +28,7 @@ @@ -28,7 +28,7 @@
28 /// \brief Initializes the gui elements of the main window. 28 /// \brief Initializes the gui elements of the main window.
29 /// \param parent The parent widget. 29 /// \param parent The parent widget.
30 /// \param flags Flags for the window manager. 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 :dsoControl(dsoControl),dataAnalyzer(dataAnalyzer) { 32 :dsoControl(dsoControl),dataAnalyzer(dataAnalyzer) {
33 33
34 // Window title 34 // Window title
@@ -45,7 +45,11 @@ OpenHantekMainWindow::OpenHantekMainWindow(std::shared_ptr&lt;HantekDsoControl&gt; dso @@ -45,7 +45,11 @@ OpenHantekMainWindow::OpenHantekMainWindow(std::shared_ptr&lt;HantekDsoControl&gt; dso
45 createDockWindows(); 45 createDockWindows();
46 46
47 // Central oszilloscope widget 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 setCentralWidget(dsoWidget); 53 setCentralWidget(dsoWidget);
50 54
51 // Subroutines for window elements 55 // Subroutines for window elements
@@ -260,91 +264,85 @@ void OpenHantekMainWindow::createDockWindows() { @@ -260,91 +264,85 @@ void OpenHantekMainWindow::createDockWindows() {
260 /// \brief Connect general signals and device management signals. 264 /// \brief Connect general signals and device management signals.
261 void OpenHantekMainWindow::connectSignals() { 265 void OpenHantekMainWindow::connectSignals() {
262 // Connect general signals 266 // Connect general signals
263 - connect(this, SIGNAL(settingsChanged()), this, SLOT(applySettings())); 267 + connect(this, &OpenHantekMainWindow::settingsChanged, this, &OpenHantekMainWindow::applySettings);
264 // connect(dsoWidget, SIGNAL(stopped()), this, SLOT(stopped())); 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 // Connect signals to DSO controller and widget 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 // connect(horizontalDock, SIGNAL(formatChanged(HorizontalFormat)), 281 // connect(horizontalDock, SIGNAL(formatChanged(HorizontalFormat)),
284 // dsoWidget, SLOT(horizontalFormatChanged(HorizontalFormat))); 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 connect(voltageDock, 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 connect(voltageDock, 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 // Started/stopped signals from oscilloscope 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 // connect(dsoControl, SIGNAL(recordLengthChanged(unsigned long)), this, 331 // connect(dsoControl, SIGNAL(recordLengthChanged(unsigned long)), this,
334 // SLOT(recordLengthChanged())); 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 horizontalDock, 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 /// \brief Initialize the device with the current settings. 348 /// \brief Initialize the device with the current settings.
@@ -444,10 +442,10 @@ void OpenHantekMainWindow::started() { @@ -444,10 +442,10 @@ void OpenHantekMainWindow::started() {
444 startStopAction->setIcon(QIcon(":actions/stop.png")); 442 startStopAction->setIcon(QIcon(":actions/stop.png"));
445 startStopAction->setStatusTip(tr("Stop the oscilloscope")); 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 /// \brief The oscilloscope stopped sampling. 451 /// \brief The oscilloscope stopped sampling.
@@ -456,10 +454,10 @@ void OpenHantekMainWindow::stopped() { @@ -456,10 +454,10 @@ void OpenHantekMainWindow::stopped() {
456 startStopAction->setIcon(QIcon(":actions/start.png")); 454 startStopAction->setIcon(QIcon(":actions/start.png"));
457 startStopAction->setStatusTip(tr("Start the oscilloscope")); 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 /// \brief Configure the oscilloscope. 463 /// \brief Configure the oscilloscope.
openhantek/src/mainwindow.h
@@ -27,7 +27,7 @@ class OpenHantekMainWindow : public QMainWindow { @@ -27,7 +27,7 @@ class OpenHantekMainWindow : public QMainWindow {
27 Q_OBJECT 27 Q_OBJECT
28 28
29 public: 29 public:
30 - OpenHantekMainWindow(std::shared_ptr<HantekDsoControl> dsoControl, std::shared_ptr<DataAnalyzer> dataAnalyser); 30 + OpenHantekMainWindow(HantekDsoControl* dsoControl, DataAnalyzer* dataAnalyser);
31 31
32 protected: 32 protected:
33 void closeEvent(QCloseEvent *event); 33 void closeEvent(QCloseEvent *event);
@@ -87,8 +87,8 @@ private: @@ -87,8 +87,8 @@ private:
87 #endif 87 #endif
88 88
89 // Data handling classes 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 // Other variables 93 // Other variables
94 QString currentFile; 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,6 +54,10 @@ unsigned int hexParse(const QString dump, unsigned char *data, unsigned int leng
54 54
55 /// \brief Print debug information with timestamp. 55 /// \brief Print debug information with timestamp.
56 /// \param text Text that will be output via qDebug. 56 /// \param text Text that will be output via qDebug.
  57 +#ifdef DEBUG
57 inline void timestampDebug(QString text) { 58 inline void timestampDebug(QString text) {
58 qDebug("%s: %s", QTime::currentTime().toString("hh:mm:ss.zzz").toLatin1().constData(), text.toLatin1().constData()); 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