diff --git a/openhantek/CMakeLists.txt b/openhantek/CMakeLists.txt
index 1c90d18..eb984b3 100644
--- a/openhantek/CMakeLists.txt
+++ b/openhantek/CMakeLists.txt
@@ -10,7 +10,7 @@ set(CMAKE_AUTORCC ON)
# include directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
-include_directories(src/ src/dso src/hantek)
+include_directories(src/ src/hantek src/analyse src/widgets src/docks src/configdialog)
# collect sources and other files
file(GLOB_RECURSE SRC "src/*.cpp")
diff --git a/openhantek/src/analyse/dataanalyzer.cpp b/openhantek/src/analyse/dataanalyzer.cpp
new file mode 100644
index 0000000..099c441
--- /dev/null
+++ b/openhantek/src/analyse/dataanalyzer.cpp
@@ -0,0 +1,426 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// OpenHantek
+// dataanalyzer.cpp
+//
+// Copyright (C) 2010 Oliver Haag
+// oliver.haag@gmail.com
+//
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation, either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program. If not, see .
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#define _USE_MATH_DEFINES
+#include
+
+#include
+#include
+#include
+
+#include
+
+#include "dataanalyzer.h"
+
+#include "glscope.h"
+#include "utils/printutils.h"
+#include "settings.h"
+
+std::unique_ptr DataAnalyzer::convertData(const DSOsamples* data, const DsoSettingsScope *scope) {
+ QReadLocker locker(&data->lock);
+
+ unsigned int channelCount = (unsigned int)scope->voltage.size();
+
+ std::unique_ptr result = std::unique_ptr(new DataAnalyzerResult(channelCount));
+
+ for (unsigned int channel = 0; channel < channelCount; ++channel) {
+ DataChannel *const channelData = result->modifyData(channel);
+
+ bool gotDataForChannel = channel < scope->physicalChannels &&
+ channel < (unsigned int)data->data.size() &&
+ !data->data.at(channel).empty();
+ bool isMathChannel = channel >= scope->physicalChannels &&
+ (scope->voltage[channel].used ||
+ scope->spectrum[channel].used) &&
+ result->channelCount() >= 2 &&
+ !result->data(0)->voltage.sample.empty() &&
+ !result->data(1)->voltage.sample.empty();
+
+ if ( !gotDataForChannel && !isMathChannel) {
+ // Clear unused channels
+ channelData->voltage.sample.clear();
+ result->modifyData(scope->physicalChannels)->voltage.interval = 0;
+ continue;
+ }
+
+ // Set sampling interval
+ const double interval = 1.0 / data->samplerate;
+ if (interval != channelData->voltage.interval) {
+ channelData->voltage.interval = interval;
+ if (data->append) // Clear roll buffer if the samplerate changed
+ channelData->voltage.sample.clear();
+ }
+
+ unsigned int size;
+ if (channel < scope->physicalChannels) {
+ size = data->data.at(channel).size();
+ if (data->append)
+ size += channelData->voltage.sample.size();
+ result->challengeMaxSamples(size);
+ } else
+ size = result->getMaxSamples();
+
+ // Physical channels
+ if (channel < scope->physicalChannels) {
+ // Copy the buffer of the oscilloscope into the sample buffer
+ if (data->append)
+ channelData->voltage.sample.insert(
+ channelData->voltage.sample.end(),
+ data->data.at(channel).begin(),
+ data->data.at(channel).end());
+ else
+ channelData->voltage.sample = data->data.at(channel);
+ } else { // Math channel
+ // Resize the sample vector
+ channelData->voltage.sample.resize(size);
+ // Set sampling interval
+ result->modifyData(scope->physicalChannels)->voltage.interval =
+ result->data(0)->voltage.interval;
+
+ // Resize the sample vector
+ result->modifyData(scope->physicalChannels)->
+ voltage.sample.resize(qMin(result->data(0)->voltage.sample.size(),
+ result->data(1)->voltage.sample.size()));
+
+ // Calculate values and write them into the sample buffer
+ std::vector::const_iterator ch1Iterator =
+ result->data(0)->voltage.sample.begin();
+ std::vector::const_iterator ch2Iterator =
+ result->data(1)->voltage.sample.begin();
+ std::vector &resultData =
+ result->modifyData(scope->physicalChannels)->voltage.sample;
+ for (std::vector::iterator resultIterator = resultData.begin();
+ resultIterator != resultData.end(); ++resultIterator) {
+ switch (scope->voltage[scope->physicalChannels].misc) {
+ case Dso::MATHMODE_1ADD2:
+ *resultIterator = *ch1Iterator + *ch2Iterator;
+ break;
+ case Dso::MATHMODE_1SUB2:
+ *resultIterator = *ch1Iterator - *ch2Iterator;
+ break;
+ case Dso::MATHMODE_2SUB1:
+ *resultIterator = *ch2Iterator - *ch1Iterator;
+ break;
+ }
+ ++ch1Iterator;
+ ++ch2Iterator;
+ }
+ }
+ }
+ return result;
+}
+
+/// \brief Analyzes the data from the dso.
+void DataAnalyzer::applySettings(DsoSettingsScope *scope)
+{
+ this->scope=scope;
+}
+
+void DataAnalyzer::setSourceData(const DSOsamples *data)
+{
+ sourceData = data;
+}
+
+std::unique_ptr DataAnalyzer::getNextResult()
+{
+ return std::move(lastResult);
+}
+
+void DataAnalyzer::samplesAvailable()
+{
+ if (sourceData == nullptr) return;
+ std::unique_ptr result = convertData(sourceData, scope);
+ spectrumAnalysis(result.get(), lastWindow, lastRecordLength, scope);
+ lastResult = std::move(result);
+ emit analyzed();
+
+}
+
+void DataAnalyzer::spectrumAnalysis(DataAnalyzerResult* result,
+ Dso::WindowFunction& lastWindow, unsigned int lastRecordLength,
+ const DsoSettingsScope *scope) {
+ // Calculate frequencies, peak-to-peak voltages and spectrums
+ for (unsigned int channel = 0; channel < result->channelCount(); ++channel) {
+ DataChannel *const channelData = result->modifyData(channel);
+
+ if (!channelData->voltage.sample.empty()) {
+ // Calculate new window
+ unsigned int sampleCount = channelData->voltage.sample.size();
+ if (lastWindow != scope->spectrumWindow ||
+ lastRecordLength != sampleCount) {
+ if (lastRecordLength != sampleCount) {
+ lastRecordLength = sampleCount;
+
+ if (result->window)
+ fftw_free(result->window);
+ result->window =
+ (double *)fftw_malloc(sizeof(double) * lastRecordLength);
+ }
+
+ unsigned int windowEnd = lastRecordLength - 1;
+ lastWindow = scope->spectrumWindow;
+
+ switch (scope->spectrumWindow) {
+ case Dso::WINDOW_HAMMING:
+ for (unsigned int windowPosition = 0;
+ windowPosition < lastRecordLength; ++windowPosition)
+ *(result->window + windowPosition) =
+ 0.54 - 0.46 * cos(2.0 * M_PI * windowPosition / windowEnd);
+ break;
+ case Dso::WINDOW_HANN:
+ for (unsigned int windowPosition = 0;
+ windowPosition < lastRecordLength; ++windowPosition)
+ *(result->window + windowPosition) =
+ 0.5 * (1.0 - cos(2.0 * M_PI * windowPosition / windowEnd));
+ break;
+ case Dso::WINDOW_COSINE:
+ for (unsigned int windowPosition = 0;
+ windowPosition < lastRecordLength; ++windowPosition)
+ *(result->window + windowPosition) =
+ sin(M_PI * windowPosition / windowEnd);
+ break;
+ case Dso::WINDOW_LANCZOS:
+ for (unsigned int windowPosition = 0;
+ windowPosition < lastRecordLength; ++windowPosition) {
+ double sincParameter =
+ (2.0 * windowPosition / windowEnd - 1.0) * M_PI;
+ if (sincParameter == 0)
+ *(result->window + windowPosition) = 1;
+ else
+ *(result->window + windowPosition) =
+ sin(sincParameter) / sincParameter;
+ }
+ break;
+ case Dso::WINDOW_BARTLETT:
+ for (unsigned int windowPosition = 0;
+ windowPosition < lastRecordLength; ++windowPosition)
+ *(result->window + windowPosition) =
+ 2.0 / windowEnd *
+ (windowEnd / 2 -
+ std::abs((double)(windowPosition - windowEnd / 2.0)));
+ break;
+ case Dso::WINDOW_TRIANGULAR:
+ for (unsigned int windowPosition = 0;
+ windowPosition < lastRecordLength; ++windowPosition)
+ *(result->window + windowPosition) =
+ 2.0 / lastRecordLength *
+ (lastRecordLength / 2 -
+ std::abs((double)(windowPosition - windowEnd / 2.0)));
+ break;
+ case Dso::WINDOW_GAUSS: {
+ double sigma = 0.4;
+ for (unsigned int windowPosition = 0;
+ windowPosition < lastRecordLength; ++windowPosition)
+ *(result->window + windowPosition) =
+ exp(-0.5 * pow(((windowPosition - windowEnd / 2) /
+ (sigma * windowEnd / 2)),
+ 2));
+ } break;
+ case Dso::WINDOW_BARTLETTHANN:
+ for (unsigned int windowPosition = 0;
+ windowPosition < lastRecordLength; ++windowPosition)
+ *(result->window + windowPosition) =
+ 0.62 -
+ 0.48 * std::abs((double)(windowPosition / windowEnd - 0.5)) -
+ 0.38 * cos(2.0 * M_PI * windowPosition / windowEnd);
+ break;
+ case Dso::WINDOW_BLACKMAN: {
+ double alpha = 0.16;
+ for (unsigned int windowPosition = 0;
+ windowPosition < lastRecordLength; ++windowPosition)
+ *(result->window + windowPosition) =
+ (1 - alpha) / 2 -
+ 0.5 * cos(2.0 * M_PI * windowPosition / windowEnd) +
+ alpha / 2 * cos(4.0 * M_PI * windowPosition / windowEnd);
+ } break;
+ // case WINDOW_KAISER:
+ // TODO
+ // double alpha = 3.0;
+ // for(unsigned int windowPosition = 0; windowPosition <
+ // lastRecordLength; ++windowPosition)
+ //*(result->window + windowPosition) = ;
+ // break;
+ case Dso::WINDOW_NUTTALL:
+ for (unsigned int windowPosition = 0;
+ windowPosition < lastRecordLength; ++windowPosition)
+ *(result->window + windowPosition) =
+ 0.355768 -
+ 0.487396 * cos(2 * M_PI * windowPosition / windowEnd) +
+ 0.144232 * cos(4 * M_PI * windowPosition / windowEnd) -
+ 0.012604 * cos(6 * M_PI * windowPosition / windowEnd);
+ break;
+ case Dso::WINDOW_BLACKMANHARRIS:
+ for (unsigned int windowPosition = 0;
+ windowPosition < lastRecordLength; ++windowPosition)
+ *(result->window + windowPosition) =
+ 0.35875 - 0.48829 * cos(2 * M_PI * windowPosition / windowEnd) +
+ 0.14128 * cos(4 * M_PI * windowPosition / windowEnd) -
+ 0.01168 * cos(6 * M_PI * windowPosition / windowEnd);
+ break;
+ case Dso::WINDOW_BLACKMANNUTTALL:
+ for (unsigned int windowPosition = 0;
+ windowPosition < lastRecordLength; ++windowPosition)
+ *(result->window + windowPosition) =
+ 0.3635819 -
+ 0.4891775 * cos(2 * M_PI * windowPosition / windowEnd) +
+ 0.1365995 * cos(4 * M_PI * windowPosition / windowEnd) -
+ 0.0106411 * cos(6 * M_PI * windowPosition / windowEnd);
+ break;
+ case Dso::WINDOW_FLATTOP:
+ for (unsigned int windowPosition = 0;
+ windowPosition < lastRecordLength; ++windowPosition)
+ *(result->window + windowPosition) =
+ 1.0 - 1.93 * cos(2 * M_PI * windowPosition / windowEnd) +
+ 1.29 * cos(4 * M_PI * windowPosition / windowEnd) -
+ 0.388 * cos(6 * M_PI * windowPosition / windowEnd) +
+ 0.032 * cos(8 * M_PI * windowPosition / windowEnd);
+ break;
+ default: // Dso::WINDOW_RECTANGULAR
+ for (unsigned int windowPosition = 0;
+ windowPosition < lastRecordLength; ++windowPosition)
+ *(result->window + windowPosition) = 1.0;
+ }
+ }
+
+ // Set sampling interval
+ channelData->spectrum.interval =
+ 1.0 / channelData->voltage.interval / sampleCount;
+
+ // Number of real/complex samples
+ unsigned int dftLength = sampleCount / 2;
+
+ // Reallocate memory for samples if the sample count has changed
+ channelData->spectrum.sample.resize(sampleCount);
+
+ // Create sample buffer and apply window
+ double *windowedValues = new double[sampleCount];
+ for (unsigned int position = 0; position < sampleCount; ++position)
+ windowedValues[position] =
+ result->window[position] *
+ channelData->voltage.sample[position];
+
+ // Do discrete real to half-complex transformation
+ /// \todo Check if record length is multiple of 2
+ /// \todo Reuse plan and use FFTW_MEASURE to get fastest algorithm
+ fftw_plan fftPlan =
+ fftw_plan_r2r_1d(sampleCount, windowedValues,
+ &channelData->spectrum.sample.front(),
+ FFTW_R2HC, FFTW_ESTIMATE);
+ fftw_execute(fftPlan);
+ fftw_destroy_plan(fftPlan);
+
+ // Do an autocorrelation to get the frequency of the signal
+ double *conjugateComplex =
+ windowedValues; // Reuse the windowedValues buffer
+
+ // Real values
+ unsigned int position;
+ double correctionFactor = 1.0 / dftLength / dftLength;
+ conjugateComplex[0] = (channelData->spectrum.sample[0] *
+ channelData->spectrum.sample[0]) *
+ correctionFactor;
+ for (position = 1; position < dftLength; ++position)
+ conjugateComplex[position] =
+ (channelData->spectrum.sample[position] *
+ channelData->spectrum.sample[position] +
+ channelData->spectrum.sample[sampleCount - position] *
+ channelData->spectrum.sample[sampleCount - position]) *
+ correctionFactor;
+ // Complex values, all zero for autocorrelation
+ conjugateComplex[dftLength] =
+ (channelData->spectrum.sample[dftLength] *
+ channelData->spectrum.sample[dftLength]) *
+ correctionFactor;
+ for (++position; position < sampleCount; ++position)
+ conjugateComplex[position] = 0;
+
+ // Do half-complex to real inverse transformation
+ double *correlation = new double[sampleCount];
+ fftPlan = fftw_plan_r2r_1d(sampleCount, conjugateComplex, correlation,
+ FFTW_HC2R, FFTW_ESTIMATE);
+ fftw_execute(fftPlan);
+ fftw_destroy_plan(fftPlan);
+ delete[] conjugateComplex;
+
+ // Calculate peak-to-peak voltage
+ double minimalVoltage, maximalVoltage;
+ minimalVoltage = maximalVoltage = channelData->voltage.sample[0];
+
+ for (unsigned int position = 1; position < sampleCount; ++position) {
+ if (channelData->voltage.sample[position] < minimalVoltage)
+ minimalVoltage = channelData->voltage.sample[position];
+ else if (channelData->voltage.sample[position] > maximalVoltage)
+ maximalVoltage = channelData->voltage.sample[position];
+ }
+
+ channelData->amplitude = maximalVoltage - minimalVoltage;
+
+ // Get the frequency from the correlation results
+ double minimumCorrelation = correlation[0];
+ double peakCorrelation = 0;
+ unsigned int peakPosition = 0;
+
+ for (unsigned int position = 1; position < sampleCount / 2; ++position) {
+ if (correlation[position] > peakCorrelation &&
+ correlation[position] > minimumCorrelation * 2) {
+ peakCorrelation = correlation[position];
+ peakPosition = position;
+ } else if (correlation[position] < minimumCorrelation)
+ minimumCorrelation = correlation[position];
+ }
+ delete[] correlation;
+
+ // Calculate the frequency in Hz
+ if (peakPosition)
+ channelData->frequency =
+ 1.0 / (channelData->voltage.interval * peakPosition);
+ else
+ channelData->frequency = 0;
+
+ // Finally calculate the real spectrum if we want it
+ if (scope->spectrum[channel].used) {
+ // Convert values into dB (Relative to the reference level)
+ double offset = 60 - scope->spectrumReference -
+ 20 * log10(dftLength);
+ double offsetLimit = scope->spectrumLimit -
+ scope->spectrumReference;
+ for (std::vector::iterator spectrumIterator =
+ channelData->spectrum.sample.begin();
+ spectrumIterator != channelData->spectrum.sample.end();
+ ++spectrumIterator) {
+ double value = 20 * log10(fabs(*spectrumIterator)) + offset;
+
+ // Check if this value has to be limited
+ if (offsetLimit > value)
+ value = offsetLimit;
+
+ *spectrumIterator = value;
+ }
+ }
+ } else if (!channelData->spectrum.sample.empty()) {
+ // Clear unused channels
+ channelData->spectrum.interval = 0;
+ channelData->spectrum.sample.clear();
+ }
+ }
+}
diff --git a/openhantek/src/analyse/dataanalyzer.h b/openhantek/src/analyse/dataanalyzer.h
new file mode 100644
index 0000000..95cea4e
--- /dev/null
+++ b/openhantek/src/analyse/dataanalyzer.h
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#pragma once
+
+#include
+
+#include
+#include
+#include
+
+#include "definitions.h"
+#include "utils/printutils.h"
+#include "dataanalyzerresult.h"
+#include "dsosamples.h"
+
+struct DsoSettingsScope;
+
+////////////////////////////////////////////////////////////////////////////////
+/// \class DataAnalyzer dataanalyzer.h
+/// \brief Analyzes the data from the dso.
+/// Calculates the spectrum and various data about the signal and saves the
+/// time-/frequencysteps between two values.
+class DataAnalyzer : public QObject {
+ Q_OBJECT
+
+public:
+ void applySettings(DsoSettingsScope* scope);
+ void setSourceData(const DSOsamples* data);
+ std::unique_ptr getNextResult();
+ /**
+ * Call this if the source data changed.
+ */
+ void samplesAvailable();
+private:
+ static std::unique_ptr convertData(const DSOsamples *data, const DsoSettingsScope *scope);
+ static void spectrumAnalysis(DataAnalyzerResult* result,
+ Dso::WindowFunction &lastWindow,
+ unsigned int lastRecordLength,
+ const DsoSettingsScope* scope);
+private:
+ DsoSettingsScope* scope;
+ unsigned int lastRecordLength=0; ///< The record length of the previously analyzed data
+ Dso::WindowFunction lastWindow=(Dso::WindowFunction)-1; ///< The previously used dft window function
+ const DSOsamples *sourceData=nullptr;
+ std::unique_ptr lastResult;
+signals:
+ void analyzed();
+};
diff --git a/openhantek/src/analyse/dataanalyzerresult.cpp b/openhantek/src/analyse/dataanalyzerresult.cpp
new file mode 100644
index 0000000..8c1af5c
--- /dev/null
+++ b/openhantek/src/analyse/dataanalyzerresult.cpp
@@ -0,0 +1,44 @@
+#include "dataanalyzerresult.h"
+#include
+
+DataAnalyzerResult::DataAnalyzerResult(unsigned int channelCount)
+{
+ analyzedData.resize(channelCount);
+}
+
+/// \brief Returns the analyzed data.
+/// \param channel Channel, whose data should be returned.
+/// \return Analyzed data as AnalyzedData struct.
+const DataChannel *DataAnalyzerResult::data(int channel) const {
+ if (channel >= (int)this->analyzedData.size())
+ return 0;
+
+ return &this->analyzedData[(size_t)channel];
+}
+
+DataChannel *DataAnalyzerResult::modifyData(int channel) {
+ if (channel >= (int)this->analyzedData.size())
+ throw new std::runtime_error("If you modfiy the DataAnalyzerResult, you need to set the channels first!");
+
+ return &this->analyzedData[(size_t)channel];
+}
+
+/// \brief Returns the sample count of the analyzed data.
+/// \return The maximum sample count of the last analyzed data.
+unsigned int DataAnalyzerResult::sampleCount() const { return this->maxSamples; }
+
+unsigned int DataAnalyzerResult::channelCount() const
+{
+ return analyzedData.size();
+}
+
+void DataAnalyzerResult::challengeMaxSamples(unsigned int newMaxSamples)
+{
+ if (newMaxSamples > this->maxSamples)
+ this->maxSamples = newMaxSamples;
+}
+
+unsigned int DataAnalyzerResult::getMaxSamples() const
+{
+ return maxSamples;
+}
diff --git a/openhantek/src/analyse/dataanalyzerresult.h b/openhantek/src/analyse/dataanalyzerresult.h
new file mode 100644
index 0000000..639d362
--- /dev/null
+++ b/openhantek/src/analyse/dataanalyzerresult.h
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#pragma once
+
+#include
+
+////////////////////////////////////////////////////////////////////////////////
+/// \struct SampleValues dataanalyzer.h
+/// \brief Struct for a array of sample values.
+struct SampleValues {
+ std::vector sample; ///< Vector holding the sampling data
+ double interval=0.0; ///< The interval between two sample values
+};
+
+////////////////////////////////////////////////////////////////////////////////
+/// \struct AnalyzedData dataanalyzer.h
+/// \brief Struct for the analyzed data.
+struct DataChannel {
+ SampleValues voltage; ///< The time-domain voltage levels (V)
+ SampleValues spectrum; ///< The frequency-domain power levels (dB)
+ double amplitude = 0.0; ///< The amplitude of the signal
+ double frequency = 0.0; ///< The frequency of the signal
+};
+
+class DataAnalyzerResult
+{
+public:
+ DataAnalyzerResult(unsigned int channelCount);
+ const DataChannel *data(int channel) const;
+ DataChannel *modifyData(int channel);
+ unsigned int sampleCount() const;
+ unsigned int channelCount() const;
+ double *window = nullptr; ///< The array for the dft window factors
+
+ /**
+ * Applies a new maximum samples value, if the given value is higher than the already stored one
+ * @param newMaxSamples Maximum samples value
+ */
+ void challengeMaxSamples(unsigned int newMaxSamples);
+ unsigned int getMaxSamples() const;
+private:
+ std::vector analyzedData; ///< The analyzed data for each channel
+ unsigned int maxSamples = 0; ///< The maximum record length of the analyzed data
+};
diff --git a/openhantek/src/dataanalyzer.cpp b/openhantek/src/dataanalyzer.cpp
deleted file mode 100644
index e9894d3..0000000
--- a/openhantek/src/dataanalyzer.cpp
+++ /dev/null
@@ -1,483 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-//
-// OpenHantek
-// dataanalyzer.cpp
-//
-// Copyright (C) 2010 Oliver Haag
-// oliver.haag@gmail.com
-//
-// This program is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program. If not, see .
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#define _USE_MATH_DEFINES
-#include
-
-#include
-#include
-
-#include
-
-#include "dataanalyzer.h"
-
-#include "glscope.h"
-#include "utils/printutils.h"
-#include "settings.h"
-
-////////////////////////////////////////////////////////////////////////////////
-// struct SampleValues
-/// \brief Initializes the members to their default values.
-SampleValues::SampleValues() { this->interval = 0.0; }
-
-////////////////////////////////////////////////////////////////////////////////
-// struct AnalyzedData
-/// \brief Initializes the members to their default values.
-AnalyzedData::AnalyzedData() {
- this->amplitude = 0.0;
- this->frequency = 0.0;
-}
-
-/// \brief Returns the analyzed data.
-/// \param channel Channel, whose data should be returned.
-/// \return Analyzed data as AnalyzedData struct.
-AnalyzedData const *DataAnalyzer::data(unsigned int channel) const {
- if (channel >= this->analyzedData.size())
- return 0;
-
- return &this->analyzedData[channel];
-}
-
-/// \brief Returns the sample count of the analyzed data.
-/// \return The maximum sample count of the last analyzed data.
-unsigned int DataAnalyzer::sampleCount() { return this->maxSamples; }
-
-QMutex *DataAnalyzer::mutex() const { return this->analyzedDataMutex; }
-
-void DataAnalyzer::transferData() {
- QMutexLocker locker(waitingDataMutex);
- QMutexLocker locker2(analyzedDataMutex);
-
- unsigned int maxSamples = 0;
- unsigned int channelCount =
- (unsigned int)scope->voltage.size();
-
- // Adapt the number of channels for analyzed data
- this->analyzedData.resize(channelCount);
-
- for (unsigned int channel = 0; channel < channelCount; ++channel) {
- AnalyzedData *const channelData = &this->analyzedData[channel];
-
- if ( // Check...
- ( // ...if we got data for this channel...
- channel < scope->physicalChannels &&
- channel < (unsigned int)this->waitingData->size() &&
- !this->waitingData->at(channel).empty()) ||
- ( // ...or if it's a math channel that can be calculated
- channel >= scope->physicalChannels &&
- (scope->voltage[channel].used ||
- scope->spectrum[channel].used) &&
- this->analyzedData.size() >= 2 &&
- !this->analyzedData[0].samples.voltage.sample.empty() &&
- !this->analyzedData[1].samples.voltage.sample.empty())) {
- // Set sampling interval
- const double interval = 1.0 / this->waitingDataSamplerate;
- if (interval != channelData->samples.voltage.interval) {
- channelData->samples.voltage.interval = interval;
- if (this->waitingDataAppend) // Clear roll buffer if the samplerate
- // changed
- channelData->samples.voltage.sample.clear();
- }
-
- unsigned int size;
- if (channel < scope->physicalChannels) {
- size = this->waitingData->at(channel).size();
- if (this->waitingDataAppend)
- size += channelData->samples.voltage.sample.size();
- if (size > maxSamples)
- maxSamples = size;
- } else
- size = maxSamples;
-
- // Physical channels
- if (channel < scope->physicalChannels) {
- // Copy the buffer of the oscilloscope into the sample buffer
- if (this->waitingDataAppend)
- channelData->samples.voltage.sample.insert(
- channelData->samples.voltage.sample.end(),
- this->waitingData->at(channel).begin(),
- this->waitingData->at(channel).end());
- else
- channelData->samples.voltage.sample = this->waitingData->at(channel);
- }
- // Math channel
- else {
- // Resize the sample vector
- channelData->samples.voltage.sample.resize(size);
- // Set sampling interval
- this->analyzedData[scope->physicalChannels]
- .samples.voltage.interval =
- this->analyzedData[0].samples.voltage.interval;
-
- // Resize the sample vector
- this->analyzedData[scope->physicalChannels]
- .samples.voltage.sample.resize(
- qMin(this->analyzedData[0].samples.voltage.sample.size(),
- this->analyzedData[1].samples.voltage.sample.size()));
-
- // Calculate values and write them into the sample buffer
- std::vector::const_iterator ch1Iterator =
- this->analyzedData[0].samples.voltage.sample.begin();
- std::vector::const_iterator ch2Iterator =
- this->analyzedData[1].samples.voltage.sample.begin();
- std::vector &resultData =
- this->analyzedData[scope->physicalChannels]
- .samples.voltage.sample;
- for (std::vector::iterator resultIterator = resultData.begin();
- resultIterator != resultData.end(); ++resultIterator) {
- switch (scope->voltage[scope->physicalChannels].misc) {
- case Dso::MATHMODE_1ADD2:
- *resultIterator = *ch1Iterator + *ch2Iterator;
- break;
- case Dso::MATHMODE_1SUB2:
- *resultIterator = *ch1Iterator - *ch2Iterator;
- break;
- case Dso::MATHMODE_2SUB1:
- *resultIterator = *ch2Iterator - *ch1Iterator;
- break;
- }
- ++ch1Iterator;
- ++ch2Iterator;
- }
- }
- } else {
- // Clear unused channels
- channelData->samples.voltage.sample.clear();
- this->analyzedData[scope->physicalChannels]
- .samples.voltage.interval = 0;
- }
- }
-}
-
-/// \brief Analyzes the data from the dso.
-void DataAnalyzer::run(DsoSettingsOptions* options,DsoSettingsScope* scope,DsoSettingsView* view) {
- this->options=options;
- this->scope=scope;
- this->view=view;
-
- transferData();
-
- // Lower priority for spectrum calculation
- this->setPriority(QThread::LowPriority);
-
- QMutexLocker locker(analyzedDataMutex);
-
- // Calculate frequencies, peak-to-peak voltages and spectrums
- for (unsigned int channel = 0; channel < this->analyzedData.size();
- ++channel) {
- AnalyzedData *const channelData = &this->analyzedData[channel];
-
- if (!channelData->samples.voltage.sample.empty()) {
- // Calculate new window
- unsigned int sampleCount = channelData->samples.voltage.sample.size();
- if (this->lastWindow != scope->spectrumWindow ||
- this->lastRecordLength != sampleCount) {
- if (this->lastRecordLength != sampleCount) {
- this->lastRecordLength = sampleCount;
-
- if (this->window)
- fftw_free(this->window);
- this->window =
- (double *)fftw_malloc(sizeof(double) * this->lastRecordLength);
- }
-
- unsigned int windowEnd = this->lastRecordLength - 1;
- this->lastWindow = scope->spectrumWindow;
-
- switch (scope->spectrumWindow) {
- case Dso::WINDOW_HAMMING:
- for (unsigned int windowPosition = 0;
- windowPosition < this->lastRecordLength; ++windowPosition)
- *(this->window + windowPosition) =
- 0.54 - 0.46 * cos(2.0 * M_PI * windowPosition / windowEnd);
- break;
- case Dso::WINDOW_HANN:
- for (unsigned int windowPosition = 0;
- windowPosition < this->lastRecordLength; ++windowPosition)
- *(this->window + windowPosition) =
- 0.5 * (1.0 - cos(2.0 * M_PI * windowPosition / windowEnd));
- break;
- case Dso::WINDOW_COSINE:
- for (unsigned int windowPosition = 0;
- windowPosition < this->lastRecordLength; ++windowPosition)
- *(this->window + windowPosition) =
- sin(M_PI * windowPosition / windowEnd);
- break;
- case Dso::WINDOW_LANCZOS:
- for (unsigned int windowPosition = 0;
- windowPosition < this->lastRecordLength; ++windowPosition) {
- double sincParameter =
- (2.0 * windowPosition / windowEnd - 1.0) * M_PI;
- if (sincParameter == 0)
- *(this->window + windowPosition) = 1;
- else
- *(this->window + windowPosition) =
- sin(sincParameter) / sincParameter;
- }
- break;
- case Dso::WINDOW_BARTLETT:
- for (unsigned int windowPosition = 0;
- windowPosition < this->lastRecordLength; ++windowPosition)
- *(this->window + windowPosition) =
- 2.0 / windowEnd *
- (windowEnd / 2 -
- std::abs((double)(windowPosition - windowEnd / 2.0)));
- break;
- case Dso::WINDOW_TRIANGULAR:
- for (unsigned int windowPosition = 0;
- windowPosition < this->lastRecordLength; ++windowPosition)
- *(this->window + windowPosition) =
- 2.0 / this->lastRecordLength *
- (this->lastRecordLength / 2 -
- std::abs((double)(windowPosition - windowEnd / 2.0)));
- break;
- case Dso::WINDOW_GAUSS: {
- double sigma = 0.4;
- for (unsigned int windowPosition = 0;
- windowPosition < this->lastRecordLength; ++windowPosition)
- *(this->window + windowPosition) =
- exp(-0.5 * pow(((windowPosition - windowEnd / 2) /
- (sigma * windowEnd / 2)),
- 2));
- } break;
- case Dso::WINDOW_BARTLETTHANN:
- for (unsigned int windowPosition = 0;
- windowPosition < this->lastRecordLength; ++windowPosition)
- *(this->window + windowPosition) =
- 0.62 -
- 0.48 * std::abs((double)(windowPosition / windowEnd - 0.5)) -
- 0.38 * cos(2.0 * M_PI * windowPosition / windowEnd);
- break;
- case Dso::WINDOW_BLACKMAN: {
- double alpha = 0.16;
- for (unsigned int windowPosition = 0;
- windowPosition < this->lastRecordLength; ++windowPosition)
- *(this->window + windowPosition) =
- (1 - alpha) / 2 -
- 0.5 * cos(2.0 * M_PI * windowPosition / windowEnd) +
- alpha / 2 * cos(4.0 * M_PI * windowPosition / windowEnd);
- } break;
- // case WINDOW_KAISER:
- // TODO
- // double alpha = 3.0;
- // for(unsigned int windowPosition = 0; windowPosition <
- // this->lastRecordLength; ++windowPosition)
- //*(this->window + windowPosition) = ;
- // break;
- case Dso::WINDOW_NUTTALL:
- for (unsigned int windowPosition = 0;
- windowPosition < this->lastRecordLength; ++windowPosition)
- *(this->window + windowPosition) =
- 0.355768 -
- 0.487396 * cos(2 * M_PI * windowPosition / windowEnd) +
- 0.144232 * cos(4 * M_PI * windowPosition / windowEnd) -
- 0.012604 * cos(6 * M_PI * windowPosition / windowEnd);
- break;
- case Dso::WINDOW_BLACKMANHARRIS:
- for (unsigned int windowPosition = 0;
- windowPosition < this->lastRecordLength; ++windowPosition)
- *(this->window + windowPosition) =
- 0.35875 - 0.48829 * cos(2 * M_PI * windowPosition / windowEnd) +
- 0.14128 * cos(4 * M_PI * windowPosition / windowEnd) -
- 0.01168 * cos(6 * M_PI * windowPosition / windowEnd);
- break;
- case Dso::WINDOW_BLACKMANNUTTALL:
- for (unsigned int windowPosition = 0;
- windowPosition < this->lastRecordLength; ++windowPosition)
- *(this->window + windowPosition) =
- 0.3635819 -
- 0.4891775 * cos(2 * M_PI * windowPosition / windowEnd) +
- 0.1365995 * cos(4 * M_PI * windowPosition / windowEnd) -
- 0.0106411 * cos(6 * M_PI * windowPosition / windowEnd);
- break;
- case Dso::WINDOW_FLATTOP:
- for (unsigned int windowPosition = 0;
- windowPosition < this->lastRecordLength; ++windowPosition)
- *(this->window + windowPosition) =
- 1.0 - 1.93 * cos(2 * M_PI * windowPosition / windowEnd) +
- 1.29 * cos(4 * M_PI * windowPosition / windowEnd) -
- 0.388 * cos(6 * M_PI * windowPosition / windowEnd) +
- 0.032 * cos(8 * M_PI * windowPosition / windowEnd);
- break;
- default: // Dso::WINDOW_RECTANGULAR
- for (unsigned int windowPosition = 0;
- windowPosition < this->lastRecordLength; ++windowPosition)
- *(this->window + windowPosition) = 1.0;
- }
- }
-
- // Set sampling interval
- channelData->samples.spectrum.interval =
- 1.0 / channelData->samples.voltage.interval / sampleCount;
-
- // Number of real/complex samples
- unsigned int dftLength = sampleCount / 2;
-
- // Reallocate memory for samples if the sample count has changed
- channelData->samples.spectrum.sample.resize(sampleCount);
-
- // Create sample buffer and apply window
- double *windowedValues = new double[sampleCount];
- for (unsigned int position = 0; position < sampleCount; ++position)
- windowedValues[position] =
- this->window[position] *
- channelData->samples.voltage.sample[position];
-
- // Do discrete real to half-complex transformation
- /// \todo Check if record length is multiple of 2
- /// \todo Reuse plan and use FFTW_MEASURE to get fastest algorithm
- fftw_plan fftPlan =
- fftw_plan_r2r_1d(sampleCount, windowedValues,
- &channelData->samples.spectrum.sample.front(),
- FFTW_R2HC, FFTW_ESTIMATE);
- fftw_execute(fftPlan);
- fftw_destroy_plan(fftPlan);
-
- // Do an autocorrelation to get the frequency of the signal
- double *conjugateComplex =
- windowedValues; // Reuse the windowedValues buffer
-
- // Real values
- unsigned int position;
- double correctionFactor = 1.0 / dftLength / dftLength;
- conjugateComplex[0] = (channelData->samples.spectrum.sample[0] *
- channelData->samples.spectrum.sample[0]) *
- correctionFactor;
- for (position = 1; position < dftLength; ++position)
- conjugateComplex[position] =
- (channelData->samples.spectrum.sample[position] *
- channelData->samples.spectrum.sample[position] +
- channelData->samples.spectrum.sample[sampleCount - position] *
- channelData->samples.spectrum.sample[sampleCount - position]) *
- correctionFactor;
- // Complex values, all zero for autocorrelation
- conjugateComplex[dftLength] =
- (channelData->samples.spectrum.sample[dftLength] *
- channelData->samples.spectrum.sample[dftLength]) *
- correctionFactor;
- for (++position; position < sampleCount; ++position)
- conjugateComplex[position] = 0;
-
- // Do half-complex to real inverse transformation
- double *correlation = new double[sampleCount];
- fftPlan = fftw_plan_r2r_1d(sampleCount, conjugateComplex, correlation,
- FFTW_HC2R, FFTW_ESTIMATE);
- fftw_execute(fftPlan);
- fftw_destroy_plan(fftPlan);
- delete[] conjugateComplex;
-
- // Calculate peak-to-peak voltage
- double minimalVoltage, maximalVoltage;
- minimalVoltage = maximalVoltage = channelData->samples.voltage.sample[0];
-
- for (unsigned int position = 1; position < sampleCount; ++position) {
- if (channelData->samples.voltage.sample[position] < minimalVoltage)
- minimalVoltage = channelData->samples.voltage.sample[position];
- else if (channelData->samples.voltage.sample[position] > maximalVoltage)
- maximalVoltage = channelData->samples.voltage.sample[position];
- }
-
- channelData->amplitude = maximalVoltage - minimalVoltage;
-
- // Get the frequency from the correlation results
- double minimumCorrelation = correlation[0];
- double peakCorrelation = 0;
- unsigned int peakPosition = 0;
-
- for (unsigned int position = 1; position < sampleCount / 2; ++position) {
- if (correlation[position] > peakCorrelation &&
- correlation[position] > minimumCorrelation * 2) {
- peakCorrelation = correlation[position];
- peakPosition = position;
- } else if (correlation[position] < minimumCorrelation)
- minimumCorrelation = correlation[position];
- }
- delete[] correlation;
-
- // Calculate the frequency in Hz
- if (peakPosition)
- channelData->frequency =
- 1.0 / (channelData->samples.voltage.interval * peakPosition);
- else
- channelData->frequency = 0;
-
- // Finally calculate the real spectrum if we want it
- if (scope->spectrum[channel].used) {
- // Convert values into dB (Relative to the reference level)
- double offset = 60 - scope->spectrumReference -
- 20 * log10(dftLength);
- double offsetLimit = scope->spectrumLimit -
- scope->spectrumReference;
- for (std::vector::iterator spectrumIterator =
- channelData->samples.spectrum.sample.begin();
- spectrumIterator != channelData->samples.spectrum.sample.end();
- ++spectrumIterator) {
- double value = 20 * log10(fabs(*spectrumIterator)) + offset;
-
- // Check if this value has to be limited
- if (offsetLimit > value)
- value = offsetLimit;
-
- *spectrumIterator = value;
- }
- }
- } else if (!channelData->samples.spectrum.sample.empty()) {
- // Clear unused channels
- channelData->samples.spectrum.interval = 0;
- channelData->samples.spectrum.sample.clear();
- }
- }
-
- this->maxSamples = maxSamples;
- emit analyzed(maxSamples);
-}
-
-/// \brief Starts the analyzing of new input data.
-/// \param data The data arrays with the input data.
-/// \param size The sizes of the data arrays.
-/// \param samplerate The samplerate for all input data.
-/// \param append The data will be appended to the previously analyzed data
-/// (Roll mode).
-/// \param mutex The mutex for all input data.
-void DataAnalyzer::analyze(const std::vector> *data,
- double samplerate, bool append, QMutex *mutex) {
- // Previous analysis still running, drop the new data
- if (this->isRunning()) {
-#ifdef DEBUG
- timestampDebug("Analyzer overload, dropping packets!");
-#endif
- return;
- }
-
- // The thread will analyze it, just save the pointers
- this->waitingData = data;
- this->waitingDataAppend = append;
- this->waitingDataMutex = mutex;
- this->waitingDataSamplerate = samplerate;
- this->start();
-#ifdef DEBUG
- static unsigned long id = 0;
- ++id;
- timestampDebug(QString("Analyzed packet %1").arg(id));
-#endif
-}
diff --git a/openhantek/src/dataanalyzer.h b/openhantek/src/dataanalyzer.h
deleted file mode 100644
index 701b3f2..0000000
--- a/openhantek/src/dataanalyzer.h
+++ /dev/null
@@ -1,113 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-//
-// OpenHantek
-/// \file dataanalyzer.h
-/// \brief Declares the DataAnalyzer class.
-//
-// Copyright (C) 2010 Oliver Haag
-// oliver.haag@gmail.com
-//
-// This program is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program. If not, see .
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef DATAANALYZER_H
-#define DATAANALYZER_H
-
-#include
-
-#include
-#include
-
-#include "definitions.h"
-#include "utils/printutils.h"
-
-struct DsoSettingsOptions;
-struct DsoSettingsScope;
-struct DsoSettingsView;
-////////////////////////////////////////////////////////////////////////////////
-/// \struct SampleValues dataanalyzer.h
-/// \brief Struct for a array of sample values.
-struct SampleValues {
- std::vector sample; ///< Vector holding the sampling data
- double interval; ///< The interval between two sample values
-
- SampleValues();
-};
-
-////////////////////////////////////////////////////////////////////////////////
-/// \struct SampleData dataanalyzer.h
-/// \brief Struct for the sample value arrays.
-struct SampleData {
- SampleValues voltage; ///< The time-domain voltage levels (V)
- SampleValues spectrum; ///< The frequency-domain power levels (dB)
-};
-
-////////////////////////////////////////////////////////////////////////////////
-/// \struct AnalyzedData dataanalyzer.h
-/// \brief Struct for the analyzed data.
-struct AnalyzedData {
- SampleData samples; ///< Voltage and spectrum values
- double amplitude; ///< The amplitude of the signal
- double frequency; ///< The frequency of the signal
-
- AnalyzedData();
-};
-
-////////////////////////////////////////////////////////////////////////////////
-/// \class DataAnalyzer dataanalyzer.h
-/// \brief Analyzes the data from the dso.
-/// Calculates the spectrum and various data about the signal and saves the
-/// time-/frequencysteps between two values.
-class DataAnalyzer : public QThread {
- Q_OBJECT
-
-public:
- const AnalyzedData *data(unsigned int channel) const;
- unsigned int sampleCount();
- /// \brief Returns the mutex for the data.
- /// \return Mutex for the analyzed data.
- QMutex *mutex() const;
-
-protected:
- void run(DsoSettingsOptions* options,DsoSettingsScope* scope,DsoSettingsView* view);
- void transferData();
-
- DsoSettingsOptions* options; ///< General options of the program
- DsoSettingsScope* scope; ///< All oscilloscope related settings
- DsoSettingsView* view; ///< All view related settings
-
- std::vector analyzedData; ///< The analyzed data for each channel
- QMutex *analyzedDataMutex= new QMutex(); ///< A mutex for the analyzed data of all channels
-
- unsigned int lastRecordLength=0; ///< The record length of the previously analyzed data
- unsigned int maxSamples=0; ///< The maximum record length of the analyzed data
- Dso::WindowFunction lastWindow=(Dso::WindowFunction)-1; ///< The previously used dft window function
- double *window=nullptr; ///< The array for the dft window factors
-
- const std::vector> *waitingData; ///< Pointer to input data from device
- double waitingDataSamplerate=0.0; ///< The samplerate of the input data
- bool waitingDataAppend; ///< true, if waiting data should be appended
- QMutex *waitingDataMutex=nullptr; ///< A mutex for the input data
-
-public slots:
- void analyze(const std::vector> *data, double samplerate,
- bool append, QMutex *mutex);
-
-signals:
- void analyzed(unsigned long samples); ///< The data with that much samples has
- ///been analyzed
-};
-
-#endif
diff --git a/openhantek/src/dsowidget.cpp b/openhantek/src/dsowidget.cpp
index cd28612..d993d31 100644
--- a/openhantek/src/dsowidget.cpp
+++ b/openhantek/src/dsowidget.cpp
@@ -1,25 +1,4 @@
-////////////////////////////////////////////////////////////////////////////////
-//
-// OpenHantek
-// dsowidget.cpp
-//
-// Copyright (C) 2010 Oliver Haag
-// oliver.haag@gmail.com
-//
-// This program is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program. If not, see .
-//
-////////////////////////////////////////////////////////////////////////////////
+// SPDX-License-Identifier: GPL-2.0+
#include
@@ -33,437 +12,413 @@
#include "hantek/definitions.h"
#include "utils/printutils.h"
#include "utils/dsoStrings.h"
-#include "dataanalyzer.h"
#include "exporter.h"
#include "glscope.h"
-#include "levelslider.h"
+#include "widgets/levelslider.h"
#include "settings.h"
-////////////////////////////////////////////////////////////////////////////////
-// class DsoWidget
-/// \brief Initializes the components of the oszilloscope-screen.
-/// \param settings The settings object containing the oscilloscope settings.
-/// \param dataAnalyzer The data analyzer that should be used as data source.
-/// \param parent The parent widget.
-/// \param flags Flags for the window manager.
-DsoWidget::DsoWidget(DsoSettings *settings, DataAnalyzer *dataAnalyzer,
- QWidget *parent, Qt::WindowFlags flags)
- : QWidget(parent, flags) {
- this->settings = settings;
- this->dataAnalyzer = dataAnalyzer;
-
- // Palette for this widget
- QPalette palette;
- palette.setColor(QPalette::Background,
- this->settings->view.color.screen.background);
- palette.setColor(QPalette::WindowText,
- this->settings->view.color.screen.text);
-
- // The OpenGL accelerated scope widgets
- this->generator = new GlGenerator(this->settings, this);
- this->generator->setDataAnalyzer(this->dataAnalyzer);
- this->mainScope = new GlScope(this->settings);
- this->mainScope->setGenerator(this->generator);
- this->zoomScope = new GlScope(this->settings);
- this->zoomScope->setGenerator(this->generator);
- this->zoomScope->setZoomMode(true);
-
-#ifdef OS_DARWIN
- // Workaround for https://bugreports.qt-project.org/browse/QTBUG-8580
- this->mainScope->hide();
- this->mainScope->show();
-#endif
-
- // The offset sliders for all possible channels
- this->offsetSlider = new LevelSlider(Qt::RightArrow);
- for (int channel = 0; channel < this->settings->scope.voltage.count();
- ++channel) {
- this->offsetSlider->addSlider(this->settings->scope.voltage[channel].name,
- channel);
- this->offsetSlider->setColor(
- channel, this->settings->view.color.screen.voltage[channel]);
- this->offsetSlider->setLimits(channel, -DIVS_VOLTAGE / 2, DIVS_VOLTAGE / 2);
- this->offsetSlider->setStep(channel, 0.2);
- this->offsetSlider->setValue(channel,
- this->settings->scope.voltage[channel].offset);
- this->offsetSlider->setVisible(channel,
- this->settings->scope.voltage[channel].used);
- }
- for (int channel = 0; channel < this->settings->scope.voltage.count();
- ++channel) {
- this->offsetSlider->addSlider(this->settings->scope.spectrum[channel].name,
- this->settings->scope.voltage.count() +
+DsoWidget::DsoWidget(DsoSettings *settings, QWidget *parent, Qt::WindowFlags flags)
+ : QWidget(parent, flags), settings(settings),
+ generator(new GlGenerator(&settings->scope, &settings->view)),
+ mainScope(new GlScope(settings, generator)),
+ zoomScope(new GlScope(settings, generator)) {
+
+ // Palette for this widget
+ QPalette palette;
+ palette.setColor(QPalette::Background, this->settings->view.color.screen.background);
+ palette.setColor(QPalette::WindowText, this->settings->view.color.screen.text);
+
+ // The OpenGL accelerated scope widgets
+ this->zoomScope->setZoomMode(true);
+
+ // The offset sliders for all possible channels
+ this->offsetSlider = new LevelSlider(Qt::RightArrow);
+ for (int channel = 0; channel < this->settings->scope.voltage.count();
+ ++channel) {
+ this->offsetSlider->addSlider(this->settings->scope.voltage[channel].name,
channel);
- this->offsetSlider->setColor(
- this->settings->scope.voltage.count() + channel,
- this->settings->view.color.screen.spectrum[channel]);
- this->offsetSlider->setLimits(this->settings->scope.voltage.count() +
+ this->offsetSlider->setColor(
+ channel, this->settings->view.color.screen.voltage[channel]);
+ this->offsetSlider->setLimits(channel, -DIVS_VOLTAGE / 2, DIVS_VOLTAGE / 2);
+ this->offsetSlider->setStep(channel, 0.2);
+ this->offsetSlider->setValue(channel,
+ this->settings->scope.voltage[channel].offset);
+ this->offsetSlider->setVisible(channel,
+ this->settings->scope.voltage[channel].used);
+ }
+ for (int channel = 0; channel < this->settings->scope.voltage.count();
+ ++channel) {
+ this->offsetSlider->addSlider(this->settings->scope.spectrum[channel].name,
+ this->settings->scope.voltage.count() +
+ channel);
+ this->offsetSlider->setColor(
+ this->settings->scope.voltage.count() + channel,
+ this->settings->view.color.screen.spectrum[channel]);
+ this->offsetSlider->setLimits(this->settings->scope.voltage.count() +
channel,
- -DIVS_VOLTAGE / 2, DIVS_VOLTAGE / 2);
- this->offsetSlider->setStep(this->settings->scope.voltage.count() + channel,
- 0.2);
- this->offsetSlider->setValue(
- this->settings->scope.voltage.count() + channel,
- this->settings->scope.spectrum[channel].offset);
- this->offsetSlider->setVisible(
- this->settings->scope.voltage.count() + channel,
- this->settings->scope.spectrum[channel].used);
- }
-
- // The triggerPosition slider
- this->triggerPositionSlider = new LevelSlider(Qt::DownArrow);
- this->triggerPositionSlider->addSlider();
- this->triggerPositionSlider->setLimits(0, 0.0, 1.0);
- this->triggerPositionSlider->setStep(0, 0.2 / DIVS_TIME);
- this->triggerPositionSlider->setValue(0,
- this->settings->scope.trigger.position);
- this->triggerPositionSlider->setVisible(0, true);
-
- // The sliders for the trigger levels
- this->triggerLevelSlider = new LevelSlider(Qt::LeftArrow);
- for (int channel = 0; channel < (int)this->settings->scope.physicalChannels;
- ++channel) {
- this->triggerLevelSlider->addSlider(channel);
- this->triggerLevelSlider->setColor(
- channel,
- (!this->settings->scope.trigger.special &&
- channel == (int)this->settings->scope.trigger.source)
- ? this->settings->view.color.screen.voltage[channel]
- : this->settings->view.color.screen.voltage[channel].darker());
- this->adaptTriggerLevelSlider(channel);
- this->triggerLevelSlider->setValue(
- channel, this->settings->scope.voltage[channel].trigger);
- this->triggerLevelSlider->setVisible(
- channel, this->settings->scope.voltage[channel].used);
- }
-
- // The marker slider
- this->markerSlider = new LevelSlider(Qt::UpArrow);
- for (int marker = 0; marker < MARKER_COUNT; ++marker) {
- this->markerSlider->addSlider(QString::number(marker + 1), marker);
- this->markerSlider->setLimits(marker, -DIVS_TIME / 2, DIVS_TIME / 2);
- this->markerSlider->setStep(marker, 0.2);
- this->markerSlider->setValue(
- marker, this->settings->scope.horizontal.marker[marker]);
- this->markerSlider->setVisible(marker, true);
- this->settings->scope.horizontal.marker_visible[marker] = true;
- }
-
- // The table for the settings
- this->settingsTriggerLabel = new QLabel();
- this->settingsTriggerLabel->setMinimumWidth(160);
- this->settingsRecordLengthLabel = new QLabel();
- this->settingsRecordLengthLabel->setAlignment(Qt::AlignRight);
- this->settingsRecordLengthLabel->setPalette(palette);
- this->settingsSamplerateLabel = new QLabel();
- this->settingsSamplerateLabel->setAlignment(Qt::AlignRight);
- this->settingsSamplerateLabel->setPalette(palette);
- this->settingsTimebaseLabel = new QLabel();
- this->settingsTimebaseLabel->setAlignment(Qt::AlignRight);
- this->settingsTimebaseLabel->setPalette(palette);
- this->settingsFrequencybaseLabel = new QLabel();
- this->settingsFrequencybaseLabel->setAlignment(Qt::AlignRight);
- this->settingsFrequencybaseLabel->setPalette(palette);
- this->settingsLayout = new QHBoxLayout();
- this->settingsLayout->addWidget(this->settingsTriggerLabel);
- this->settingsLayout->addWidget(this->settingsRecordLengthLabel, 1);
- this->settingsLayout->addWidget(this->settingsSamplerateLabel, 1);
- this->settingsLayout->addWidget(this->settingsTimebaseLabel, 1);
- this->settingsLayout->addWidget(this->settingsFrequencybaseLabel, 1);
-
- // The table for the marker details
- this->markerInfoLabel = new QLabel();
- this->markerInfoLabel->setMinimumWidth(160);
- this->markerInfoLabel->setPalette(palette);
- this->markerTimeLabel = new QLabel();
- this->markerTimeLabel->setAlignment(Qt::AlignRight);
- this->markerTimeLabel->setPalette(palette);
- this->markerFrequencyLabel = new QLabel();
- this->markerFrequencyLabel->setAlignment(Qt::AlignRight);
- this->markerFrequencyLabel->setPalette(palette);
- this->markerTimebaseLabel = new QLabel();
- this->markerTimebaseLabel->setAlignment(Qt::AlignRight);
- this->markerTimebaseLabel->setPalette(palette);
- this->markerFrequencybaseLabel = new QLabel();
- this->markerFrequencybaseLabel->setAlignment(Qt::AlignRight);
- this->markerFrequencybaseLabel->setPalette(palette);
- this->markerLayout = new QHBoxLayout();
- this->markerLayout->addWidget(this->markerInfoLabel);
- this->markerLayout->addWidget(this->markerTimeLabel, 1);
- this->markerLayout->addWidget(this->markerFrequencyLabel, 1);
- this->markerLayout->addWidget(this->markerTimebaseLabel, 1);
- this->markerLayout->addWidget(this->markerFrequencybaseLabel, 1);
-
- // The table for the measurements
- QPalette tablePalette = palette;
- this->measurementLayout = new QGridLayout();
- this->measurementLayout->setColumnMinimumWidth(0, 64);
- this->measurementLayout->setColumnMinimumWidth(1, 32);
- this->measurementLayout->setColumnStretch(2, 2);
- this->measurementLayout->setColumnStretch(3, 2);
- this->measurementLayout->setColumnStretch(4, 3);
- this->measurementLayout->setColumnStretch(5, 3);
- for (int channel = 0; channel < this->settings->scope.voltage.count();
- ++channel) {
- tablePalette.setColor(QPalette::WindowText,
- this->settings->view.color.screen.voltage[channel]);
- this->measurementNameLabel.append(
- new QLabel(this->settings->scope.voltage[channel].name));
- this->measurementNameLabel[channel]->setPalette(tablePalette);
- this->measurementMiscLabel.append(new QLabel());
- this->measurementMiscLabel[channel]->setPalette(tablePalette);
- this->measurementGainLabel.append(new QLabel());
- this->measurementGainLabel[channel]->setAlignment(Qt::AlignRight);
- this->measurementGainLabel[channel]->setPalette(tablePalette);
- tablePalette.setColor(QPalette::WindowText,
- this->settings->view.color.screen.spectrum[channel]);
- this->measurementMagnitudeLabel.append(new QLabel());
- this->measurementMagnitudeLabel[channel]->setAlignment(Qt::AlignRight);
- this->measurementMagnitudeLabel[channel]->setPalette(tablePalette);
- this->measurementAmplitudeLabel.append(new QLabel());
- this->measurementAmplitudeLabel[channel]->setAlignment(Qt::AlignRight);
- this->measurementAmplitudeLabel[channel]->setPalette(palette);
- this->measurementFrequencyLabel.append(new QLabel());
- this->measurementFrequencyLabel[channel]->setAlignment(Qt::AlignRight);
- this->measurementFrequencyLabel[channel]->setPalette(palette);
- this->setMeasurementVisible(channel,
- this->settings->scope.voltage[channel].used);
- this->measurementLayout->addWidget(this->measurementNameLabel[channel],
- channel, 0);
- this->measurementLayout->addWidget(this->measurementMiscLabel[channel],
- channel, 1);
- this->measurementLayout->addWidget(this->measurementGainLabel[channel],
- channel, 2);
- this->measurementLayout->addWidget(this->measurementMagnitudeLabel[channel],
- channel, 3);
- this->measurementLayout->addWidget(this->measurementAmplitudeLabel[channel],
- channel, 4);
- this->measurementLayout->addWidget(this->measurementFrequencyLabel[channel],
- channel, 5);
- if ((unsigned int)channel < this->settings->scope.physicalChannels)
- this->updateVoltageCoupling(channel);
- else
- this->updateMathMode();
- this->updateVoltageDetails(channel);
- this->updateSpectrumDetails(channel);
- }
-
- // The layout for the widgets
- this->mainLayout = new QGridLayout();
- this->mainLayout->setColumnStretch(2, 1); // Scopes increase their size
- this->mainLayout->setRowStretch(3, 1);
- // Bars around the scope, needed because the slider-drawing-area is outside
- // the scope at min/max
- this->mainLayout->setColumnMinimumWidth(
- 1, this->triggerPositionSlider->preMargin());
- this->mainLayout->setColumnMinimumWidth(
- 3, this->triggerPositionSlider->postMargin());
- this->mainLayout->setRowMinimumHeight(2, this->offsetSlider->preMargin());
- this->mainLayout->setRowMinimumHeight(4, this->offsetSlider->postMargin());
- this->mainLayout->setRowMinimumHeight(6, 4);
- this->mainLayout->setRowMinimumHeight(8, 4);
- this->mainLayout->setRowMinimumHeight(10, 8);
- this->mainLayout->setSpacing(0);
- this->mainLayout->addLayout(this->settingsLayout, 0, 0, 1, 5);
- this->mainLayout->addWidget(this->mainScope, 3, 2);
- this->mainLayout->addWidget(this->offsetSlider, 2, 0, 3, 2, Qt::AlignRight);
- this->mainLayout->addWidget(this->triggerPositionSlider, 1, 1, 2, 3,
- Qt::AlignBottom);
- this->mainLayout->addWidget(this->triggerLevelSlider, 2, 3, 3, 2,
- Qt::AlignLeft);
- this->mainLayout->addWidget(this->markerSlider, 4, 1, 2, 3, Qt::AlignTop);
- this->mainLayout->addLayout(this->markerLayout, 7, 0, 1, 5);
- this->mainLayout->addWidget(this->zoomScope, 9, 2);
- this->mainLayout->addLayout(this->measurementLayout, 11, 0, 1, 5);
-
- // Apply settings and update measured values
- this->updateTriggerDetails();
- this->updateRecordLength(this->settings->scope.horizontal.recordLength);
- this->updateFrequencybase(this->settings->scope.horizontal.frequencybase);
- this->updateSamplerate(this->settings->scope.horizontal.samplerate);
- this->updateTimebase(this->settings->scope.horizontal.timebase);
- this->updateZoom(this->settings->view.zoom);
-
- // The widget itself
- this->setPalette(palette);
- this->setBackgroundRole(QPalette::Background);
- this->setAutoFillBackground(true);
- this->setLayout(this->mainLayout);
-
- // Connect change-signals of sliders
- this->connect(this->offsetSlider, SIGNAL(valueChanged(int, double)), this,
- SLOT(updateOffset(int, double)));
- this->connect(this->triggerPositionSlider, SIGNAL(valueChanged(int, double)),
- this, SLOT(updateTriggerPosition(int, double)));
- this->connect(this->triggerLevelSlider, SIGNAL(valueChanged(int, double)),
- this, SLOT(updateTriggerLevel(int, double)));
- this->connect(this->markerSlider, SIGNAL(valueChanged(int, double)), this,
- SLOT(updateMarker(int, double)));
- this->connect(this->markerSlider, SIGNAL(valueChanged(int, double)),
- this->mainScope, SLOT(update()));
- this->connect(this->markerSlider, SIGNAL(valueChanged(int, double)),
- this->zoomScope, SLOT(update()));
-
- // Connect other signals
- this->connect(this->dataAnalyzer, SIGNAL(analyzed(unsigned long)), this,
- SLOT(dataAnalyzed()));
- this->connect(this->dataAnalyzer, SIGNAL(analyzed(unsigned long)), this,
- SLOT(updateRecordLength(unsigned long)));
+ -DIVS_VOLTAGE / 2, DIVS_VOLTAGE / 2);
+ this->offsetSlider->setStep(this->settings->scope.voltage.count() + channel,
+ 0.2);
+ this->offsetSlider->setValue(
+ this->settings->scope.voltage.count() + channel,
+ this->settings->scope.spectrum[channel].offset);
+ this->offsetSlider->setVisible(
+ this->settings->scope.voltage.count() + channel,
+ this->settings->scope.spectrum[channel].used);
+ }
+
+ // The triggerPosition slider
+ this->triggerPositionSlider = new LevelSlider(Qt::DownArrow);
+ this->triggerPositionSlider->addSlider();
+ this->triggerPositionSlider->setLimits(0, 0.0, 1.0);
+ this->triggerPositionSlider->setStep(0, 0.2 / DIVS_TIME);
+ this->triggerPositionSlider->setValue(0,
+ this->settings->scope.trigger.position);
+ this->triggerPositionSlider->setVisible(0, true);
+
+ // The sliders for the trigger levels
+ this->triggerLevelSlider = new LevelSlider(Qt::LeftArrow);
+ for (int channel = 0; channel < (int)this->settings->scope.physicalChannels;
+ ++channel) {
+ this->triggerLevelSlider->addSlider(channel);
+ this->triggerLevelSlider->setColor(
+ channel,
+ (!this->settings->scope.trigger.special &&
+ channel == (int)this->settings->scope.trigger.source)
+ ? this->settings->view.color.screen.voltage[channel]
+ : this->settings->view.color.screen.voltage[channel].darker());
+ this->adaptTriggerLevelSlider(channel);
+ this->triggerLevelSlider->setValue(
+ channel, this->settings->scope.voltage[channel].trigger);
+ this->triggerLevelSlider->setVisible(
+ channel, this->settings->scope.voltage[channel].used);
+ }
+
+ // The marker slider
+ this->markerSlider = new LevelSlider(Qt::UpArrow);
+ for (int marker = 0; marker < MARKER_COUNT; ++marker) {
+ this->markerSlider->addSlider(QString::number(marker + 1), marker);
+ this->markerSlider->setLimits(marker, -DIVS_TIME / 2, DIVS_TIME / 2);
+ this->markerSlider->setStep(marker, 0.2);
+ this->markerSlider->setValue(
+ marker, this->settings->scope.horizontal.marker[marker]);
+ this->markerSlider->setVisible(marker, true);
+ this->settings->scope.horizontal.marker_visible[marker] = true;
+ }
+
+ // The table for the settings
+ this->settingsTriggerLabel = new QLabel();
+ this->settingsTriggerLabel->setMinimumWidth(160);
+ this->settingsRecordLengthLabel = new QLabel();
+ this->settingsRecordLengthLabel->setAlignment(Qt::AlignRight);
+ this->settingsRecordLengthLabel->setPalette(palette);
+ this->settingsSamplerateLabel = new QLabel();
+ this->settingsSamplerateLabel->setAlignment(Qt::AlignRight);
+ this->settingsSamplerateLabel->setPalette(palette);
+ this->settingsTimebaseLabel = new QLabel();
+ this->settingsTimebaseLabel->setAlignment(Qt::AlignRight);
+ this->settingsTimebaseLabel->setPalette(palette);
+ this->settingsFrequencybaseLabel = new QLabel();
+ this->settingsFrequencybaseLabel->setAlignment(Qt::AlignRight);
+ this->settingsFrequencybaseLabel->setPalette(palette);
+ this->settingsLayout = new QHBoxLayout();
+ this->settingsLayout->addWidget(this->settingsTriggerLabel);
+ this->settingsLayout->addWidget(this->settingsRecordLengthLabel, 1);
+ this->settingsLayout->addWidget(this->settingsSamplerateLabel, 1);
+ this->settingsLayout->addWidget(this->settingsTimebaseLabel, 1);
+ this->settingsLayout->addWidget(this->settingsFrequencybaseLabel, 1);
+
+ // The table for the marker details
+ this->markerInfoLabel = new QLabel();
+ this->markerInfoLabel->setMinimumWidth(160);
+ this->markerInfoLabel->setPalette(palette);
+ this->markerTimeLabel = new QLabel();
+ this->markerTimeLabel->setAlignment(Qt::AlignRight);
+ this->markerTimeLabel->setPalette(palette);
+ this->markerFrequencyLabel = new QLabel();
+ this->markerFrequencyLabel->setAlignment(Qt::AlignRight);
+ this->markerFrequencyLabel->setPalette(palette);
+ this->markerTimebaseLabel = new QLabel();
+ this->markerTimebaseLabel->setAlignment(Qt::AlignRight);
+ this->markerTimebaseLabel->setPalette(palette);
+ this->markerFrequencybaseLabel = new QLabel();
+ this->markerFrequencybaseLabel->setAlignment(Qt::AlignRight);
+ this->markerFrequencybaseLabel->setPalette(palette);
+ this->markerLayout = new QHBoxLayout();
+ this->markerLayout->addWidget(this->markerInfoLabel);
+ this->markerLayout->addWidget(this->markerTimeLabel, 1);
+ this->markerLayout->addWidget(this->markerFrequencyLabel, 1);
+ this->markerLayout->addWidget(this->markerTimebaseLabel, 1);
+ this->markerLayout->addWidget(this->markerFrequencybaseLabel, 1);
+
+ // The table for the measurements
+ QPalette tablePalette = palette;
+ this->measurementLayout = new QGridLayout();
+ this->measurementLayout->setColumnMinimumWidth(0, 64);
+ this->measurementLayout->setColumnMinimumWidth(1, 32);
+ this->measurementLayout->setColumnStretch(2, 2);
+ this->measurementLayout->setColumnStretch(3, 2);
+ this->measurementLayout->setColumnStretch(4, 3);
+ this->measurementLayout->setColumnStretch(5, 3);
+ for (int channel = 0; channel < this->settings->scope.voltage.count();
+ ++channel) {
+ tablePalette.setColor(QPalette::WindowText,
+ this->settings->view.color.screen.voltage[channel]);
+ this->measurementNameLabel.append(
+ new QLabel(this->settings->scope.voltage[channel].name));
+ this->measurementNameLabel[channel]->setPalette(tablePalette);
+ this->measurementMiscLabel.append(new QLabel());
+ this->measurementMiscLabel[channel]->setPalette(tablePalette);
+ this->measurementGainLabel.append(new QLabel());
+ this->measurementGainLabel[channel]->setAlignment(Qt::AlignRight);
+ this->measurementGainLabel[channel]->setPalette(tablePalette);
+ tablePalette.setColor(QPalette::WindowText,
+ this->settings->view.color.screen.spectrum[channel]);
+ this->measurementMagnitudeLabel.append(new QLabel());
+ this->measurementMagnitudeLabel[channel]->setAlignment(Qt::AlignRight);
+ this->measurementMagnitudeLabel[channel]->setPalette(tablePalette);
+ this->measurementAmplitudeLabel.append(new QLabel());
+ this->measurementAmplitudeLabel[channel]->setAlignment(Qt::AlignRight);
+ this->measurementAmplitudeLabel[channel]->setPalette(palette);
+ this->measurementFrequencyLabel.append(new QLabel());
+ this->measurementFrequencyLabel[channel]->setAlignment(Qt::AlignRight);
+ this->measurementFrequencyLabel[channel]->setPalette(palette);
+ this->setMeasurementVisible(channel,
+ this->settings->scope.voltage[channel].used);
+ this->measurementLayout->addWidget(this->measurementNameLabel[channel],
+ channel, 0);
+ this->measurementLayout->addWidget(this->measurementMiscLabel[channel],
+ channel, 1);
+ this->measurementLayout->addWidget(this->measurementGainLabel[channel],
+ channel, 2);
+ this->measurementLayout->addWidget(this->measurementMagnitudeLabel[channel],
+ channel, 3);
+ this->measurementLayout->addWidget(this->measurementAmplitudeLabel[channel],
+ channel, 4);
+ this->measurementLayout->addWidget(this->measurementFrequencyLabel[channel],
+ channel, 5);
+ if ((unsigned int)channel < this->settings->scope.physicalChannels)
+ this->updateVoltageCoupling(channel);
+ else
+ this->updateMathMode();
+ this->updateVoltageDetails(channel);
+ this->updateSpectrumDetails(channel);
+ }
+
+ // The layout for the widgets
+ this->mainLayout = new QGridLayout();
+ this->mainLayout->setColumnStretch(2, 1); // Scopes increase their size
+ this->mainLayout->setRowStretch(3, 1);
+ // Bars around the scope, needed because the slider-drawing-area is outside
+ // the scope at min/max
+ this->mainLayout->setColumnMinimumWidth(
+ 1, this->triggerPositionSlider->preMargin());
+ this->mainLayout->setColumnMinimumWidth(
+ 3, this->triggerPositionSlider->postMargin());
+ this->mainLayout->setRowMinimumHeight(2, this->offsetSlider->preMargin());
+ this->mainLayout->setRowMinimumHeight(4, this->offsetSlider->postMargin());
+ this->mainLayout->setRowMinimumHeight(6, 4);
+ this->mainLayout->setRowMinimumHeight(8, 4);
+ this->mainLayout->setRowMinimumHeight(10, 8);
+ this->mainLayout->setSpacing(0);
+ this->mainLayout->addLayout(this->settingsLayout, 0, 0, 1, 5);
+ this->mainLayout->addWidget(this->mainScope, 3, 2);
+ this->mainLayout->addWidget(this->offsetSlider, 2, 0, 3, 2, Qt::AlignRight);
+ this->mainLayout->addWidget(this->triggerPositionSlider, 1, 1, 2, 3,
+ Qt::AlignBottom);
+ this->mainLayout->addWidget(this->triggerLevelSlider, 2, 3, 3, 2,
+ Qt::AlignLeft);
+ this->mainLayout->addWidget(this->markerSlider, 4, 1, 2, 3, Qt::AlignTop);
+ this->mainLayout->addLayout(this->markerLayout, 7, 0, 1, 5);
+ this->mainLayout->addWidget(this->zoomScope, 9, 2);
+ this->mainLayout->addLayout(this->measurementLayout, 11, 0, 1, 5);
+
+ // Apply settings and update measured values
+ this->updateTriggerDetails();
+ this->updateRecordLength(this->settings->scope.horizontal.recordLength);
+ this->updateFrequencybase(this->settings->scope.horizontal.frequencybase);
+ this->updateSamplerate(this->settings->scope.horizontal.samplerate);
+ this->updateTimebase(this->settings->scope.horizontal.timebase);
+ this->updateZoom(this->settings->view.zoom);
+
+ // The widget itself
+ this->setPalette(palette);
+ this->setBackgroundRole(QPalette::Background);
+ this->setAutoFillBackground(true);
+ this->setLayout(this->mainLayout);
+
+ // Connect change-signals of sliders
+ this->connect(this->offsetSlider, SIGNAL(valueChanged(int, double)), this,
+ SLOT(updateOffset(int, double)));
+ this->connect(this->triggerPositionSlider, SIGNAL(valueChanged(int, double)),
+ this, SLOT(updateTriggerPosition(int, double)));
+ this->connect(this->triggerLevelSlider, SIGNAL(valueChanged(int, double)),
+ this, SLOT(updateTriggerLevel(int, double)));
+ this->connect(this->markerSlider, SIGNAL(valueChanged(int, double)), this,
+ SLOT(updateMarker(int, double)));
+ this->connect(this->markerSlider, SIGNAL(valueChanged(int, double)),
+ this->mainScope, SLOT(update()));
+ this->connect(this->markerSlider, SIGNAL(valueChanged(int, double)),
+ this->zoomScope, SLOT(update()));
}
-/// \brief Stops the oscilloscope thread and the timer.
-DsoWidget::~DsoWidget() {}
+void DsoWidget::showNewData(std::unique_ptr data)
+{
+ if (!data) return;
+ this->data = std::move(data);
+ emit doShowNewData();
+}
/// \brief Set the trigger level sliders minimum and maximum to the new values.
void DsoWidget::adaptTriggerLevelSlider(unsigned int channel) {
- this->triggerLevelSlider->setLimits(
- channel,
- (-DIVS_VOLTAGE / 2 - this->settings->scope.voltage[channel].offset) *
- this->settings->scope.voltage[channel].gain,
- (DIVS_VOLTAGE / 2 - this->settings->scope.voltage[channel].offset) *
- this->settings->scope.voltage[channel].gain);
- this->triggerLevelSlider->setStep(
- channel, this->settings->scope.voltage[channel].gain * 0.2);
+ this->triggerLevelSlider->setLimits(
+ channel,
+ (-DIVS_VOLTAGE / 2 - this->settings->scope.voltage[channel].offset) *
+ this->settings->scope.voltage[channel].gain,
+ (DIVS_VOLTAGE / 2 - this->settings->scope.voltage[channel].offset) *
+ this->settings->scope.voltage[channel].gain);
+ this->triggerLevelSlider->setStep(
+ channel, this->settings->scope.voltage[channel].gain * 0.2);
}
/// \brief Show/Hide a line of the measurement table.
void DsoWidget::setMeasurementVisible(unsigned int channel, bool visible) {
- this->measurementNameLabel[channel]->setVisible(visible);
- this->measurementMiscLabel[channel]->setVisible(visible);
- this->measurementGainLabel[channel]->setVisible(visible);
- this->measurementMagnitudeLabel[channel]->setVisible(visible);
- this->measurementAmplitudeLabel[channel]->setVisible(visible);
- this->measurementFrequencyLabel[channel]->setVisible(visible);
- if (!visible) {
- this->measurementGainLabel[channel]->setText(QString());
- this->measurementMagnitudeLabel[channel]->setText(QString());
- this->measurementAmplitudeLabel[channel]->setText(QString());
- this->measurementFrequencyLabel[channel]->setText(QString());
- }
+ this->measurementNameLabel[channel]->setVisible(visible);
+ this->measurementMiscLabel[channel]->setVisible(visible);
+ this->measurementGainLabel[channel]->setVisible(visible);
+ this->measurementMagnitudeLabel[channel]->setVisible(visible);
+ this->measurementAmplitudeLabel[channel]->setVisible(visible);
+ this->measurementFrequencyLabel[channel]->setVisible(visible);
+ if (!visible) {
+ this->measurementGainLabel[channel]->setText(QString());
+ this->measurementMagnitudeLabel[channel]->setText(QString());
+ this->measurementAmplitudeLabel[channel]->setText(QString());
+ this->measurementFrequencyLabel[channel]->setText(QString());
+ }
}
/// \brief Update the label about the marker measurements
void DsoWidget::updateMarkerDetails() {
- double divs = fabs(this->settings->scope.horizontal.marker[1] -
- this->settings->scope.horizontal.marker[0]);
- double time = divs * this->settings->scope.horizontal.timebase;
-
- if (this->settings->view.zoom) {
- this->markerInfoLabel->setText(
- tr("Zoom x%L1").arg(DIVS_TIME / divs, -1, 'g', 3));
- this->markerTimebaseLabel->setText(
- valueToString(time / DIVS_TIME, UNIT_SECONDS, 3) +
- tr("/div"));
- this->markerFrequencybaseLabel->setText(
- valueToString(
- divs * this->settings->scope.horizontal.frequencybase / DIVS_TIME,
- UNIT_HERTZ, 4) +
- tr("/div"));
- }
- this->markerTimeLabel->setText(
- valueToString(time, UNIT_SECONDS, 4));
- this->markerFrequencyLabel->setText(
- valueToString(1.0 / time, UNIT_HERTZ, 4));
+ double divs = fabs(this->settings->scope.horizontal.marker[1] -
+ this->settings->scope.horizontal.marker[0]);
+ double time = divs * this->settings->scope.horizontal.timebase;
+
+ if (this->settings->view.zoom) {
+ this->markerInfoLabel->setText(
+ tr("Zoom x%L1").arg(DIVS_TIME / divs, -1, 'g', 3));
+ this->markerTimebaseLabel->setText(
+ valueToString(time / DIVS_TIME, UNIT_SECONDS, 3) +
+ tr("/div"));
+ this->markerFrequencybaseLabel->setText(
+ valueToString(
+ divs * this->settings->scope.horizontal.frequencybase / DIVS_TIME,
+ UNIT_HERTZ, 4) +
+ tr("/div"));
+ }
+ this->markerTimeLabel->setText(
+ valueToString(time, UNIT_SECONDS, 4));
+ this->markerFrequencyLabel->setText(
+ valueToString(1.0 / time, UNIT_HERTZ, 4));
}
/// \brief Update the label about the trigger settings
void DsoWidget::updateSpectrumDetails(unsigned int channel) {
- this->setMeasurementVisible(channel,
- this->settings->scope.voltage[channel].used ||
- this->settings->scope.spectrum[channel].used);
+ this->setMeasurementVisible(channel,
+ this->settings->scope.voltage[channel].used ||
+ this->settings->scope.spectrum[channel].used);
- if (this->settings->scope.spectrum[channel].used)
- this->measurementMagnitudeLabel[channel]->setText(
- valueToString(this->settings->scope.spectrum[channel].magnitude,
+ if (this->settings->scope.spectrum[channel].used)
+ this->measurementMagnitudeLabel[channel]->setText(
+ valueToString(this->settings->scope.spectrum[channel].magnitude,
UNIT_DECIBEL, 3) +
- tr("/div"));
- else
- this->measurementMagnitudeLabel[channel]->setText(QString());
+ tr("/div"));
+ else
+ this->measurementMagnitudeLabel[channel]->setText(QString());
}
/// \brief Update the label about the trigger settings
void DsoWidget::updateTriggerDetails() {
- // Update the trigger details
- QPalette tablePalette = this->palette();
- tablePalette.setColor(QPalette::WindowText,
- this->settings->view.color.screen
- .voltage[this->settings->scope.trigger.source]);
- this->settingsTriggerLabel->setPalette(tablePalette);
- QString levelString = valueToString(
- this->settings->scope.voltage[this->settings->scope.trigger.source]
- .trigger,
- UNIT_VOLTS, 3);
- QString pretriggerString =
- tr("%L1%").arg((int)(this->settings->scope.trigger.position * 100 + 0.5));
- this->settingsTriggerLabel->setText(
- tr("%1 %2 %3 %4")
- .arg(this->settings->scope
- .voltage[this->settings->scope.trigger.source]
- .name,
- Dso::slopeString(this->settings->scope.trigger.slope),
- levelString, pretriggerString));
-
- /// \todo This won't work for special trigger sources
+ // Update the trigger details
+ QPalette tablePalette = this->palette();
+ tablePalette.setColor(QPalette::WindowText,
+ this->settings->view.color.screen
+ .voltage[this->settings->scope.trigger.source]);
+ this->settingsTriggerLabel->setPalette(tablePalette);
+ QString levelString = valueToString(
+ this->settings->scope.voltage[this->settings->scope.trigger.source]
+ .trigger,
+ UNIT_VOLTS, 3);
+ QString pretriggerString =
+ tr("%L1%").arg((int)(this->settings->scope.trigger.position * 100 + 0.5));
+ this->settingsTriggerLabel->setText(
+ tr("%1 %2 %3 %4")
+ .arg(this->settings->scope
+ .voltage[this->settings->scope.trigger.source]
+ .name,
+ Dso::slopeString(this->settings->scope.trigger.slope),
+ levelString, pretriggerString));
+
+ /// \todo This won't work for special trigger sources
}
/// \brief Update the label about the trigger settings
void DsoWidget::updateVoltageDetails(unsigned int channel) {
- if (channel >= (unsigned int)this->settings->scope.voltage.count())
- return;
+ if (channel >= (unsigned int)this->settings->scope.voltage.count())
+ return;
- this->setMeasurementVisible(channel,
- this->settings->scope.voltage[channel].used ||
- this->settings->scope.spectrum[channel].used);
+ this->setMeasurementVisible(channel,
+ this->settings->scope.voltage[channel].used ||
+ this->settings->scope.spectrum[channel].used);
- if (this->settings->scope.voltage[channel].used)
- this->measurementGainLabel[channel]->setText(
- valueToString(this->settings->scope.voltage[channel].gain,
+ if (this->settings->scope.voltage[channel].used)
+ this->measurementGainLabel[channel]->setText(
+ valueToString(this->settings->scope.voltage[channel].gain,
UNIT_VOLTS, 3) +
- tr("/div"));
- else
- this->measurementGainLabel[channel]->setText(QString());
+ tr("/div"));
+ else
+ this->measurementGainLabel[channel]->setText(QString());
}
/// \brief Handles frequencybaseChanged signal from the horizontal dock.
/// \param frequencybase The frequencybase used for displaying the trace.
void DsoWidget::updateFrequencybase(double frequencybase) {
- this->settingsFrequencybaseLabel->setText(
- valueToString(frequencybase, UNIT_HERTZ, 4) + tr("/div"));
+ this->settingsFrequencybaseLabel->setText(
+ valueToString(frequencybase, UNIT_HERTZ, 4) + tr("/div"));
}
/// \brief Updates the samplerate field after changing the samplerate.
/// \param samplerate The samplerate set in the oscilloscope.
void DsoWidget::updateSamplerate(double samplerate) {
- this->settingsSamplerateLabel->setText(
- valueToString(samplerate, UNIT_SAMPLES, 4) + tr("/s"));
+ this->settingsSamplerateLabel->setText(
+ valueToString(samplerate, UNIT_SAMPLES, 4) + tr("/s"));
}
/// \brief Handles timebaseChanged signal from the horizontal dock.
/// \param timebase The timebase used for displaying the trace.
void DsoWidget::updateTimebase(double timebase) {
- this->settingsTimebaseLabel->setText(
- valueToString(timebase, UNIT_SECONDS, 4) + tr("/div"));
+ this->settingsTimebaseLabel->setText(
+ valueToString(timebase, UNIT_SECONDS, 4) + tr("/div"));
- this->updateMarkerDetails();
+ this->updateMarkerDetails();
}
/// \brief Handles magnitudeChanged signal from the spectrum dock.
/// \param channel The channel whose magnitude was changed.
void DsoWidget::updateSpectrumMagnitude(unsigned int channel) {
- this->updateSpectrumDetails(channel);
+ this->updateSpectrumDetails(channel);
}
/// \brief Handles usedChanged signal from the spectrum dock.
/// \param channel The channel whose used-state was changed.
/// \param used The new used-state for the channel.
void DsoWidget::updateSpectrumUsed(unsigned int channel, bool used) {
- if (channel >= (unsigned int)this->settings->scope.voltage.count())
- return;
+ if (channel >= (unsigned int)this->settings->scope.voltage.count())
+ return;
- this->offsetSlider->setVisible(
- this->settings->scope.voltage.count() + channel, used);
+ this->offsetSlider->setVisible(
+ this->settings->scope.voltage.count() + channel, used);
- this->updateSpectrumDetails(channel);
+ this->updateSpectrumDetails(channel);
}
/// \brief Handles modeChanged signal from the trigger dock.
@@ -474,163 +429,150 @@ void DsoWidget::updateTriggerSlope() { this->updateTriggerDetails(); }
/// \brief Handles sourceChanged signal from the trigger dock.
void DsoWidget::updateTriggerSource() {
- // Change the colors of the trigger sliders
- if (this->settings->scope.trigger.special ||
- this->settings->scope.trigger.source >=
- this->settings->scope.physicalChannels)
- this->triggerPositionSlider->setColor(
- 0, this->settings->view.color.screen.border);
- else
- this->triggerPositionSlider->setColor(
- 0, this->settings->view.color.screen
- .voltage[this->settings->scope.trigger.source]);
-
- for (int channel = 0; channel < (int)this->settings->scope.physicalChannels;
- ++channel)
- this->triggerLevelSlider->setColor(
- channel,
- (!this->settings->scope.trigger.special &&
- channel == (int)this->settings->scope.trigger.source)
- ? this->settings->view.color.screen.voltage[channel]
- : this->settings->view.color.screen.voltage[channel].darker());
-
- this->updateTriggerDetails();
+ // Change the colors of the trigger sliders
+ if (this->settings->scope.trigger.special ||
+ this->settings->scope.trigger.source >=
+ this->settings->scope.physicalChannels)
+ this->triggerPositionSlider->setColor(
+ 0, this->settings->view.color.screen.border);
+ else
+ this->triggerPositionSlider->setColor(
+ 0, this->settings->view.color.screen
+ .voltage[this->settings->scope.trigger.source]);
+
+ for (int channel = 0; channel < (int)this->settings->scope.physicalChannels;
+ ++channel)
+ this->triggerLevelSlider->setColor(
+ channel,
+ (!this->settings->scope.trigger.special &&
+ channel == (int)this->settings->scope.trigger.source)
+ ? this->settings->view.color.screen.voltage[channel]
+ : this->settings->view.color.screen.voltage[channel].darker());
+
+ this->updateTriggerDetails();
}
/// \brief Handles couplingChanged signal from the voltage dock.
/// \param channel The channel whose coupling was changed.
void DsoWidget::updateVoltageCoupling(unsigned int channel) {
- if (channel >= (unsigned int)this->settings->scope.voltage.count())
- return;
+ if (channel >= (unsigned int)this->settings->scope.voltage.count())
+ return;
- this->measurementMiscLabel[channel]->setText(Dso::couplingString(
- (Dso::Coupling)this->settings->scope.voltage[channel].misc));
+ this->measurementMiscLabel[channel]->setText(Dso::couplingString(
+ (Dso::Coupling)this->settings->scope.voltage[channel].misc));
}
/// \brief Handles modeChanged signal from the voltage dock.
void DsoWidget::updateMathMode() {
- this->measurementMiscLabel[this->settings->scope.physicalChannels]->setText(
- Dso::mathModeString((Dso::MathMode)this->settings->scope
- .voltage[this->settings->scope.physicalChannels]
- .misc));
+ this->measurementMiscLabel[this->settings->scope.physicalChannels]->setText(
+ Dso::mathModeString((Dso::MathMode)this->settings->scope
+ .voltage[this->settings->scope.physicalChannels]
+ .misc));
}
/// \brief Handles gainChanged signal from the voltage dock.
/// \param channel The channel whose gain was changed.
void DsoWidget::updateVoltageGain(unsigned int channel) {
- if (channel >= (unsigned int)this->settings->scope.voltage.count())
- return;
+ if (channel >= (unsigned int)this->settings->scope.voltage.count())
+ return;
- if (channel < this->settings->scope.physicalChannels)
- this->adaptTriggerLevelSlider(channel);
+ if (channel < this->settings->scope.physicalChannels)
+ this->adaptTriggerLevelSlider(channel);
- this->updateVoltageDetails(channel);
+ this->updateVoltageDetails(channel);
}
/// \brief Handles usedChanged signal from the voltage dock.
/// \param channel The channel whose used-state was changed.
/// \param used The new used-state for the channel.
void DsoWidget::updateVoltageUsed(unsigned int channel, bool used) {
- if (channel >= (unsigned int)this->settings->scope.voltage.count())
- return;
+ if (channel >= (unsigned int)this->settings->scope.voltage.count())
+ return;
- this->offsetSlider->setVisible(channel, used);
- this->triggerLevelSlider->setVisible(channel, used);
- this->setMeasurementVisible(channel,
- this->settings->scope.voltage[channel].used);
+ this->offsetSlider->setVisible(channel, used);
+ this->triggerLevelSlider->setVisible(channel, used);
+ this->setMeasurementVisible(channel,
+ this->settings->scope.voltage[channel].used);
- this->updateVoltageDetails(channel);
+ this->updateVoltageDetails(channel);
}
/// \brief Change the record length.
void DsoWidget::updateRecordLength(unsigned long size) {
- this->settingsRecordLengthLabel->setText(
- valueToString(size, UNIT_SAMPLES, 4));
+ this->settingsRecordLengthLabel->setText(
+ valueToString(size, UNIT_SAMPLES, 4));
}
/// \brief Export the oscilloscope screen to a file.
/// \return true if the document was exported successfully.
-bool DsoWidget::exportAs() {
- QStringList filters;
- filters << tr("Portable Document Format (*.pdf)")
- << tr("Image (*.png *.xpm *.jpg)")
- << tr("Comma-Separated Values (*.csv)");
-
- QFileDialog fileDialog(static_cast(this->parent()),
- tr("Export file..."), QString(), filters.join(";;"));
- fileDialog.setFileMode(QFileDialog::AnyFile);
- fileDialog.setAcceptMode(QFileDialog::AcceptSave);
- if (fileDialog.exec() != QDialog::Accepted)
- return false;
-
- Exporter exporter(this->settings, this->dataAnalyzer,
- static_cast(this->parent()));
- exporter.setFilename(fileDialog.selectedFiles().first());
- exporter.setFormat((ExportFormat)(
- EXPORT_FORMAT_PDF + filters.indexOf(fileDialog.selectedNameFilter())));
-
- return exporter.doExport();
+void DsoWidget::exportAs() {
+ exportNextFrame.reset(Exporter::createSaveToFileExporter(settings));
}
/// \brief Print the oscilloscope screen.
/// \return true if the document was sent to the printer successfully.
-bool DsoWidget::print() {
- Exporter exporter(this->settings, this->dataAnalyzer,
- static_cast(this->parent()));
- exporter.setFormat(EXPORT_FORMAT_PRINTER);
-
- return exporter.doExport();
+void DsoWidget::print() {
+ exportNextFrame.reset(Exporter::createPrintExporter(settings));
}
/// \brief Stop the oscilloscope.
void DsoWidget::updateZoom(bool enabled) {
- this->mainLayout->setRowStretch(9, enabled ? 1 : 0);
- this->zoomScope->setVisible(enabled);
-
- // Show time-/frequencybase and zoom factor if the magnified scope is shown
- this->markerLayout->setStretch(3, enabled ? 1 : 0);
- this->markerTimebaseLabel->setVisible(enabled);
- this->markerLayout->setStretch(4, enabled ? 1 : 0);
- this->markerFrequencybaseLabel->setVisible(enabled);
- if (enabled)
- this->updateMarkerDetails();
- else
- this->markerInfoLabel->setText(tr("Marker 1/2"));
+ this->mainLayout->setRowStretch(9, enabled ? 1 : 0);
+ this->zoomScope->setVisible(enabled);
+
+ // Show time-/frequencybase and zoom factor if the magnified scope is shown
+ this->markerLayout->setStretch(3, enabled ? 1 : 0);
+ this->markerTimebaseLabel->setVisible(enabled);
+ this->markerLayout->setStretch(4, enabled ? 1 : 0);
+ this->markerFrequencybaseLabel->setVisible(enabled);
+ if (enabled)
+ this->updateMarkerDetails();
+ else
+ this->markerInfoLabel->setText(tr("Marker 1/2"));
- this->repaint();
+ this->repaint();
}
/// \brief Prints analyzed data.
-void DsoWidget::dataAnalyzed() {
- for (int channel = 0; channel < this->settings->scope.voltage.count();
- ++channel) {
- if (this->settings->scope.voltage[channel].used &&
- this->dataAnalyzer->data(channel)) {
- // Amplitude string representation (4 significant digits)
- this->measurementAmplitudeLabel[channel]->setText(valueToString(
- this->dataAnalyzer->data(channel)->amplitude, UNIT_VOLTS, 4));
- // Frequency string representation (5 significant digits)
- this->measurementFrequencyLabel[channel]->setText(valueToString(
- this->dataAnalyzer->data(channel)->frequency, UNIT_HERTZ, 5));
+void DsoWidget::doShowNewData() {
+ if (exportNextFrame){
+ exportNextFrame->exportSamples(data.get());
+ exportNextFrame.reset(nullptr);
+ }
+
+ generator->generateGraphs(data.get());
+
+ updateRecordLength(data.get()->getMaxSamples());
+
+ for (int channel = 0; channel < this->settings->scope.voltage.count();
+ ++channel) {
+ if (this->settings->scope.voltage[channel].used &&
+ data.get()->data(channel)) {
+ // Amplitude string representation (4 significant digits)
+ this->measurementAmplitudeLabel[channel]->setText(valueToString(
+ data.get()->data(channel)->amplitude, UNIT_VOLTS, 4));
+ // Frequency string representation (5 significant digits)
+ this->measurementFrequencyLabel[channel]->setText(valueToString(
+ data.get()->data(channel)->frequency, UNIT_HERTZ, 5));
+ }
}
- }
}
/// \brief Handles valueChanged signal from the offset sliders.
/// \param channel The channel whose offset was changed.
/// \param value The new offset for the channel.
void DsoWidget::updateOffset(int channel, double value) {
- if (channel < this->settings->scope.voltage.count()) {
- this->settings->scope.voltage[channel].offset = value;
+ if (channel < this->settings->scope.voltage.count()) {
+ this->settings->scope.voltage[channel].offset = value;
- if (channel < (int)this->settings->scope.physicalChannels)
- this->adaptTriggerLevelSlider(channel);
- } else if (channel < this->settings->scope.voltage.count() * 2)
- this->settings->scope
- .spectrum[channel - this->settings->scope.voltage.count()]
- .offset = value;
+ if (channel < (int)this->settings->scope.physicalChannels)
+ this->adaptTriggerLevelSlider(channel);
+ } else if (channel < this->settings->scope.voltage.count() * 2)
+ this->settings->scope
+ .spectrum[channel - this->settings->scope.voltage.count()]
+ .offset = value;
- emit offsetChanged(channel, value);
+ emit offsetChanged(channel, value);
}
/// \brief Handles valueChanged signal from the triggerPosition slider.
@@ -638,35 +580,35 @@ void DsoWidget::updateOffset(int channel, double value) {
/// \param value The new triggerPosition in seconds relative to the first
/// sample.
void DsoWidget::updateTriggerPosition(int index, double value) {
- if (index != 0)
- return;
+ if (index != 0)
+ return;
- this->settings->scope.trigger.position = value;
+ this->settings->scope.trigger.position = value;
- this->updateTriggerDetails();
+ this->updateTriggerDetails();
- emit triggerPositionChanged(
- value * this->settings->scope.horizontal.timebase * DIVS_TIME);
+ emit triggerPositionChanged(
+ value * this->settings->scope.horizontal.timebase * DIVS_TIME);
}
/// \brief Handles valueChanged signal from the trigger level slider.
/// \param channel The index of the slider.
/// \param value The new trigger level.
void DsoWidget::updateTriggerLevel(int channel, double value) {
- this->settings->scope.voltage[channel].trigger = value;
+ this->settings->scope.voltage[channel].trigger = value;
- this->updateTriggerDetails();
+ this->updateTriggerDetails();
- emit triggerLevelChanged(channel, value);
+ emit triggerLevelChanged(channel, value);
}
/// \brief Handles valueChanged signal from the marker slider.
/// \param marker The index of the slider.
/// \param value The new marker position.
void DsoWidget::updateMarker(int marker, double value) {
- this->settings->scope.horizontal.marker[marker] = value;
+ this->settings->scope.horizontal.marker[marker] = value;
- this->updateMarkerDetails();
+ this->updateMarkerDetails();
- emit markerChanged(marker, value);
+ emit markerChanged(marker, value);
}
diff --git a/openhantek/src/dsowidget.h b/openhantek/src/dsowidget.h
index 6a2ffe4..35be142 100644
--- a/openhantek/src/dsowidget.h
+++ b/openhantek/src/dsowidget.h
@@ -1,148 +1,126 @@
-////////////////////////////////////////////////////////////////////////////////
-//
-// OpenHantek
-/// \file dsowidget.h
-/// \brief Declares the DsoWidget class.
-//
-// Copyright (C) 2010 Oliver Haag
-// oliver.haag@gmail.com
-//
-// This program is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program. If not, see .
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef DSOWIDGET_H
-#define DSOWIDGET_H
+// SPDX-License-Identifier: GPL-2.0+
+
+#pragma once
#include
+#include
#include "dockwindows.h"
#include "glscope.h"
#include "levelslider.h"
+#include "exporter.h"
class DataAnalyzer;
class DsoSettings;
class QGridLayout;
-////////////////////////////////////////////////////////////////////////////////
-/// \class DsoWidget dsowidget.h
+/// \class DsoWidget
/// \brief The widget for the oszilloscope-screen
/// This widget contains the scopes and all level sliders.
class DsoWidget : public QWidget {
- Q_OBJECT
+ Q_OBJECT
public:
- DsoWidget(DsoSettings *settings, DataAnalyzer *dataAnalyzer,
- QWidget *parent = 0, Qt::WindowFlags flags = 0);
- ~DsoWidget();
-
+ /// \brief Initializes the components of the oszilloscope-screen.
+ /// \param settings The settings object containing the oscilloscope settings.
+ /// \param dataAnalyzer The data analyzer that should be used as data source.
+ /// \param parent The parent widget.
+ /// \param flags Flags for the window manager.
+ DsoWidget(DsoSettings *settings, QWidget *parent = 0, Qt::WindowFlags flags = 0);
+ void showNewData(std::unique_ptr data);
protected:
- void adaptTriggerLevelSlider(unsigned int channel);
- void setMeasurementVisible(unsigned int channel, bool visible);
- void updateMarkerDetails();
- void updateSpectrumDetails(unsigned int channel);
- void updateTriggerDetails();
- void updateVoltageDetails(unsigned int channel);
-
- QGridLayout *mainLayout; ///< The main layout for this widget
- GlGenerator *generator; ///< The generator for the OpenGL vertex arrays
- GlScope *mainScope; ///< The main scope screen
- GlScope *zoomScope; ///< The optional magnified scope screen
- LevelSlider *offsetSlider; ///< The sliders for the graph offsets
- LevelSlider *triggerPositionSlider; ///< The slider for the pretrigger
- LevelSlider *triggerLevelSlider; ///< The sliders for the trigger level
- LevelSlider *markerSlider; ///< The sliders for the markers
-
- QHBoxLayout *settingsLayout; ///< The table for the settings info
- QLabel *settingsTriggerLabel; ///< The trigger details
- QLabel *settingsRecordLengthLabel; ///< The record length
- QLabel *settingsSamplerateLabel; ///< The samplerate
- QLabel *settingsTimebaseLabel; ///< The timebase of the main scope
- QLabel *settingsFrequencybaseLabel; ///< The frequencybase of the main scope
-
- QHBoxLayout *markerLayout; ///< The table for the marker details
- QLabel *markerInfoLabel; ///< The info about the zoom factor
- QLabel *markerTimeLabel; ///< The time period between the markers
- QLabel *markerFrequencyLabel; ///< The frequency for the time period
- QLabel *markerTimebaseLabel; ///< The timebase for the zoomed scope
- QLabel *markerFrequencybaseLabel; ///< The frequencybase for the zoomed scope
-
- QGridLayout *measurementLayout; ///< The table for the signal details
- QList measurementNameLabel; ///< The name of the channel
- QList measurementGainLabel; ///< The gain for the voltage (V/div)
- QList
- measurementMagnitudeLabel; ///< The magnitude for the spectrum (dB/div)
- QList measurementMiscLabel; ///< Coupling or math mode
- QList measurementAmplitudeLabel; ///< Amplitude of the signal (V)
- QList measurementFrequencyLabel; ///< Frequency of the signal (Hz)
-
- DsoSettings *settings; ///< The settings provided by the main window
-
- DataAnalyzer *dataAnalyzer; ///< The data source provided by the main window
-
+ void adaptTriggerLevelSlider(unsigned int channel);
+ void setMeasurementVisible(unsigned int channel, bool visible);
+ void updateMarkerDetails();
+ void updateSpectrumDetails(unsigned int channel);
+ void updateTriggerDetails();
+ void updateVoltageDetails(unsigned int channel);
+
+ QGridLayout *mainLayout; ///< The main layout for this widget
+ LevelSlider *offsetSlider; ///< The sliders for the graph offsets
+ LevelSlider *triggerPositionSlider; ///< The slider for the pretrigger
+ LevelSlider *triggerLevelSlider; ///< The sliders for the trigger level
+ LevelSlider *markerSlider; ///< The sliders for the markers
+
+ QHBoxLayout *settingsLayout; ///< The table for the settings info
+ QLabel *settingsTriggerLabel; ///< The trigger details
+ QLabel *settingsRecordLengthLabel; ///< The record length
+ QLabel *settingsSamplerateLabel; ///< The samplerate
+ QLabel *settingsTimebaseLabel; ///< The timebase of the main scope
+ QLabel *settingsFrequencybaseLabel; ///< The frequencybase of the main scope
+
+ QHBoxLayout *markerLayout; ///< The table for the marker details
+ QLabel *markerInfoLabel; ///< The info about the zoom factor
+ QLabel *markerTimeLabel; ///< The time period between the markers
+ QLabel *markerFrequencyLabel; ///< The frequency for the time period
+ QLabel *markerTimebaseLabel; ///< The timebase for the zoomed scope
+ QLabel *markerFrequencybaseLabel; ///< The frequencybase for the zoomed scope
+
+ QGridLayout *measurementLayout; ///< The table for the signal details
+ QList measurementNameLabel; ///< The name of the channel
+ QList measurementGainLabel; ///< The gain for the voltage (V/div)
+ QList
+ measurementMagnitudeLabel; ///< The magnitude for the spectrum (dB/div)
+ QList measurementMiscLabel; ///< Coupling or math mode
+ QList measurementAmplitudeLabel; ///< Amplitude of the signal (V)
+ QList measurementFrequencyLabel; ///< Frequency of the signal (Hz)
+
+ DsoSettings *settings; ///< The settings provided by the main window
+ GlGenerator *generator; ///< The generator for the OpenGL vertex arrays
+ GlScope *mainScope; ///< The main scope screen
+ GlScope *zoomScope; ///< The optional magnified scope screen
+ std::unique_ptr exportNextFrame;
+ std::unique_ptr data;
public slots:
- // Horizontal axis
- // void horizontalFormatChanged(HorizontalFormat format);
- void updateFrequencybase(double frequencybase);
- void updateSamplerate(double samplerate);
- void updateTimebase(double timebase);
-
- // Trigger
- void updateTriggerMode();
- void updateTriggerSlope();
- void updateTriggerSource();
-
- // Spectrum
- void updateSpectrumMagnitude(unsigned int channel);
- void updateSpectrumUsed(unsigned int channel, bool used);
-
- // Vertical axis
- void updateVoltageCoupling(unsigned int channel);
- void updateMathMode();
- void updateVoltageGain(unsigned int channel);
- void updateVoltageUsed(unsigned int channel, bool used);
-
- // Menus
- void updateRecordLength(unsigned long size);
-
- // Export
- bool exportAs();
- bool print();
-
- // Scope control
- void updateZoom(bool enabled);
-
- // Data analyzer
- void dataAnalyzed();
-
-protected slots:
- // Sliders
- void updateOffset(int channel, double value);
- void updateTriggerPosition(int index, double value);
- void updateTriggerLevel(int channel, double value);
- void updateMarker(int marker, double value);
+ // Horizontal axis
+ // void horizontalFormatChanged(HorizontalFormat format);
+ void updateFrequencybase(double frequencybase);
+ void updateSamplerate(double samplerate);
+ void updateTimebase(double timebase);
+
+ // Trigger
+ void updateTriggerMode();
+ void updateTriggerSlope();
+ void updateTriggerSource();
+
+ // Spectrum
+ void updateSpectrumMagnitude(unsigned int channel);
+ void updateSpectrumUsed(unsigned int channel, bool used);
+
+ // Vertical axis
+ void updateVoltageCoupling(unsigned int channel);
+ void updateMathMode();
+ void updateVoltageGain(unsigned int channel);
+ void updateVoltageUsed(unsigned int channel, bool used);
+
+ // Menus
+ void updateRecordLength(unsigned long size);
+
+ // Export
+ void exportAs();
+ void print();
+
+ // Scope control
+ void updateZoom(bool enabled);
+
+ // Data analyzer
+ void doShowNewData();
+
+private slots:
+ // Sliders
+ void updateOffset(int channel, double value);
+ void updateTriggerPosition(int index, double value);
+ void updateTriggerLevel(int channel, double value);
+ void updateMarker(int marker, double value);
signals:
- // Sliders
- void offsetChanged(unsigned int channel,
- double value); ///< A graph offset has been changed
- void
- triggerPositionChanged(double value); ///< The pretrigger has been changed
- void triggerLevelChanged(unsigned int channel,
- double value); ///< A trigger level has been changed
- void markerChanged(unsigned int marker,
- double value); ///< A marker position has been changed
+ // Sliders
+ void offsetChanged(unsigned int channel,
+ double value); ///< A graph offset has been changed
+ void
+ triggerPositionChanged(double value); ///< The pretrigger has been changed
+ void triggerLevelChanged(unsigned int channel,
+ double value); ///< A trigger level has been changed
+ void markerChanged(unsigned int marker,
+ double value); ///< A marker position has been changed
};
-
-#endif
diff --git a/openhantek/src/glgenerator.cpp b/openhantek/src/glgenerator.cpp
index bf21061..ce1b8ef 100644
--- a/openhantek/src/glgenerator.cpp
+++ b/openhantek/src/glgenerator.cpp
@@ -1,25 +1,4 @@
-////////////////////////////////////////////////////////////////////////////////
-//
-// OpenHantek
-// glgenerator.cpp
-//
-// Copyright (C) 2010 Oliver Haag
-// oliver.haag@gmail.com
-//
-// This program is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program. If not, see .
-//
-////////////////////////////////////////////////////////////////////////////////
+// SPDX-License-Identifier: GPL-2.0+
#include
@@ -28,400 +7,353 @@
#include "dataanalyzer.h"
#include "settings.h"
-////////////////////////////////////////////////////////////////////////////////
-// class GlGenerator
-/// \brief Initializes the scope widget.
-/// \param settings The target settings object.
-/// \param parent The parent widget.
-GlGenerator::GlGenerator(DsoSettings *settings, QObject *parent)
- : QObject(parent) {
- this->settings = settings;
+GlGenerator::GlGenerator(DsoSettingsScope *scope, DsoSettingsView *view) : settings(scope), view(view) {
+ // Grid
+ vaGrid[0].resize(((DIVS_TIME * DIVS_SUB - 2) * (DIVS_VOLTAGE - 2) +
+ (DIVS_VOLTAGE * DIVS_SUB - 2) * (DIVS_TIME - 2) -
+ ((DIVS_TIME - 2) * (DIVS_VOLTAGE - 2))) *
+ 2);
+ std::vector::iterator glIterator = vaGrid[0].begin();
+ // Draw vertical lines
+ for (int div = 1; div < DIVS_TIME / 2; ++div) {
+ for (int dot = 1; dot < DIVS_VOLTAGE / 2 * DIVS_SUB; ++dot) {
+ float dotPosition = (float)dot / DIVS_SUB;
+ *(glIterator++) = -div;
+ *(glIterator++) = -dotPosition;
+ *(glIterator++) = -div;
+ *(glIterator++) = dotPosition;
+ *(glIterator++) = div;
+ *(glIterator++) = -dotPosition;
+ *(glIterator++) = div;
+ *(glIterator++) = dotPosition;
+ }
+ }
+ // Draw horizontal lines
+ for (int div = 1; div < DIVS_VOLTAGE / 2; ++div) {
+ for (int dot = 1; dot < DIVS_TIME / 2 * DIVS_SUB; ++dot) {
+ if (dot % DIVS_SUB == 0)
+ continue; // Already done by vertical lines
+ float dotPosition = (float)dot / DIVS_SUB;
+ *(glIterator++) = -dotPosition;
+ *(glIterator++) = -div;
+ *(glIterator++) = dotPosition;
+ *(glIterator++) = -div;
+ *(glIterator++) = -dotPosition;
+ *(glIterator++) = div;
+ *(glIterator++) = dotPosition;
+ *(glIterator++) = div;
+ }
+ }
- this->dataAnalyzer = 0;
- this->digitalPhosphorDepth = 0;
+ // Axes
+ vaGrid[1].resize(
+ (2 + (DIVS_TIME * DIVS_SUB - 2) + (DIVS_VOLTAGE * DIVS_SUB - 2)) * 4);
+ glIterator = vaGrid[1].begin();
+ // Horizontal axis
+ *(glIterator++) = -DIVS_TIME / 2;
+ *(glIterator++) = 0;
+ *(glIterator++) = DIVS_TIME / 2;
+ *(glIterator++) = 0;
+ // Vertical axis
+ *(glIterator++) = 0;
+ *(glIterator++) = -DIVS_VOLTAGE / 2;
+ *(glIterator++) = 0;
+ *(glIterator++) = DIVS_VOLTAGE / 2;
+ // Subdiv lines on horizontal axis
+ for (int line = 1; line < DIVS_TIME / 2 * DIVS_SUB; ++line) {
+ float linePosition = (float)line / DIVS_SUB;
+ *(glIterator++) = linePosition;
+ *(glIterator++) = -0.05;
+ *(glIterator++) = linePosition;
+ *(glIterator++) = 0.05;
+ *(glIterator++) = -linePosition;
+ *(glIterator++) = -0.05;
+ *(glIterator++) = -linePosition;
+ *(glIterator++) = 0.05;
+ }
+ // Subdiv lines on vertical axis
+ for (int line = 1; line < DIVS_VOLTAGE / 2 * DIVS_SUB; ++line) {
+ float linePosition = (float)line / DIVS_SUB;
+ *(glIterator++) = -0.05;
+ *(glIterator++) = linePosition;
+ *(glIterator++) = 0.05;
+ *(glIterator++) = linePosition;
+ *(glIterator++) = -0.05;
+ *(glIterator++) = -linePosition;
+ *(glIterator++) = 0.05;
+ *(glIterator++) = -linePosition;
+ }
- this->generateGrid();
+ // Border
+ vaGrid[2].resize(4 * 2);
+ glIterator = vaGrid[2].begin();
+ *(glIterator++) = -DIVS_TIME / 2;
+ *(glIterator++) = -DIVS_VOLTAGE / 2;
+ *(glIterator++) = DIVS_TIME / 2;
+ *(glIterator++) = -DIVS_VOLTAGE / 2;
+ *(glIterator++) = DIVS_TIME / 2;
+ *(glIterator++) = DIVS_VOLTAGE / 2;
+ *(glIterator++) = -DIVS_TIME / 2;
+ *(glIterator++) = DIVS_VOLTAGE / 2;
}
-/// \brief Deletes OpenGL objects.
-GlGenerator::~GlGenerator() {
- /// \todo Clean up vaChannel
+const std::vector &GlGenerator::channel(int mode, int channel, int index) const {
+ return vaChannel[mode][channel][index];
}
-/// \brief Set the data analyzer whose data will be drawn.
-/// \param dataAnalyzer Pointer to the DataAnalyzer class.
-void GlGenerator::setDataAnalyzer(DataAnalyzer *dataAnalyzer) {
- if (this->dataAnalyzer)
- disconnect(this->dataAnalyzer, SIGNAL(finished()), this,
- SLOT(generateGraphs()));
- this->dataAnalyzer = dataAnalyzer;
- connect(this->dataAnalyzer, SIGNAL(finished()), this, SLOT(generateGraphs()));
+const std::vector &GlGenerator::grid(int a) const {
+ return vaGrid[a];
}
-/// \brief Prepare arrays for drawing the data we get from the data analyzer.
-void GlGenerator::generateGraphs() {
- if (!this->dataAnalyzer)
- return;
-
- // Adapt the number of graphs
- for (int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT;
- ++mode)
- this->vaChannel[mode].resize(this->settings->scope.voltage.count());
-
- // Set digital phosphor depth to one if we don't use it
- if (this->settings->view.digitalPhosphor)
- this->digitalPhosphorDepth = this->settings->view.digitalPhosphorDepth;
- else
- this->digitalPhosphorDepth = 1;
-
- // Handle all digital phosphor related list manipulations
- for (int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT;
- ++mode) {
- for (unsigned int channel = 0; channel < this->vaChannel[mode].size();
- ++channel) {
- // Move the last list element to the front
- this->vaChannel[mode][channel].push_front(std::vector());
-
- // Resize lists for vector array to fit the digital phosphor depth
- this->vaChannel[mode][channel].resize(this->digitalPhosphorDepth);
- }
- }
-
- QMutexLocker locker(this->dataAnalyzer->mutex());
-
- unsigned int preTrigSamples = 0;
- unsigned int postTrigSamples = 0;
- switch (this->settings->scope.horizontal.format) {
- case Dso::GRAPHFORMAT_TY: {
- unsigned int swTriggerStart = 0;
- // check trigger point for software trigger
- if (this->settings->scope.trigger.mode == Dso::TRIGGERMODE_SOFTWARE &&
- this->settings->scope.trigger.source <= 1) {
- int channel = this->settings->scope.trigger.source;
- if (this->settings->scope.voltage[channel].used &&
- this->dataAnalyzer->data(channel) &&
- !this->dataAnalyzer->data(channel)->samples.voltage.sample.empty()) {
- double value;
- double level = this->settings->scope.voltage[channel].trigger;
- unsigned int sampleCount =
- this->dataAnalyzer->data(channel)->samples.voltage.sample.size();
- double timeDisplay = this->settings->scope.horizontal.timebase * 10;
- double samplesDisplay =
- timeDisplay * this->settings->scope.horizontal.samplerate;
- if (samplesDisplay >= sampleCount) {
-// For sure not enough samples to adjust for jitter.
-// Following options exist:
-// 1: Decrease sample rate
-// 2: Change trigger mode to auto
-// 3: Ignore samples
-// For now #3 is chosen
-#ifdef DEBUG
- timestampDebug(QString("Too few samples to make a steady "
- "picture. Decrease sample rate"));
-#endif
- return;
+bool GlGenerator::isReady() const
+{
+ return ready;
+}
+
+
+void GlGenerator::generateGraphs(const DataAnalyzerResult* result) {
+
+ int digitalPhosphorDepth = view->digitalPhosphorDepth;
+
+ // Handle all digital phosphor related list manipulations
+ for (int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT; ++mode) {
+ // Adapt the number of graphs
+ vaChannel[mode].resize(settings->voltage.count());
+
+ for (unsigned int channel = 0; channel < vaChannel[mode].size(); ++channel) {
+ // Move the last list element to the front
+ vaChannel[mode][channel].push_front(std::vector());
+
+ // Resize lists for vector array to fit the digital phosphor depth
+ vaChannel[mode][channel].resize(digitalPhosphorDepth);
}
- preTrigSamples =
- (this->settings->scope.trigger.position * samplesDisplay);
- postTrigSamples = sampleCount - (samplesDisplay - preTrigSamples);
- // std::vector::const_iterator dataIterator =
- // this->dataAnalyzer->data(channel)->samples.voltage.sample.begin();
-
- if (this->settings->scope.trigger.slope == Dso::SLOPE_POSITIVE) {
- double prev = INT_MAX;
- for (unsigned int i = preTrigSamples; i < postTrigSamples; i++) {
- value =
- this->dataAnalyzer->data(channel)->samples.voltage.sample[i];
- if (value > level && prev <= level) {
- int rising = 0;
- for (unsigned int k = i + 1; k < i + 11 && k < sampleCount; k++) {
- if (this->dataAnalyzer->data(channel)
- ->samples.voltage.sample[k] >= value) {
- rising++;
+ }
+
+ ready = true;
+
+ unsigned int preTrigSamples = 0;
+ unsigned int postTrigSamples = 0;
+ switch (settings->horizontal.format) {
+ case Dso::GRAPHFORMAT_TY: {
+ unsigned int swTriggerStart = 0;
+ // check trigger point for software trigger
+ if (settings->trigger.mode == Dso::TRIGGERMODE_SOFTWARE &&
+ settings->trigger.source <= 1) {
+ unsigned int channel = settings->trigger.source;
+ if (settings->voltage[channel].used &&
+ result->data(channel) &&
+ !result->data(channel)->voltage.sample.empty()) {
+ double value;
+ double level = settings->voltage[channel].trigger;
+ unsigned int sampleCount =
+ result->data(channel)->voltage.sample.size();
+ double timeDisplay = settings->horizontal.timebase * 10;
+ double samplesDisplay =
+ timeDisplay * settings->horizontal.samplerate;
+ if (samplesDisplay >= sampleCount) {
+ // For sure not enough samples to adjust for jitter.
+ // Following options exist:
+ // 1: Decrease sample rate
+ // 2: Change trigger mode to auto
+ // 3: Ignore samples
+ // For now #3 is chosen
+ timestampDebug(QString("Too few samples to make a steady "
+ "picture. Decrease sample rate"));
+ return;
}
- }
- if (rising > 7) {
- swTriggerStart = i;
- break;
- }
- }
- prev = value;
- }
- } else if (this->settings->scope.trigger.slope == Dso::SLOPE_NEGATIVE) {
- double prev = INT_MIN;
- for (unsigned int i = preTrigSamples; i < postTrigSamples; i++) {
- value =
- this->dataAnalyzer->data(channel)->samples.voltage.sample[i];
- if (value < level && prev >= level) {
- int falling = 0;
- for (unsigned int k = i + 1; k < i + 11 && k < sampleCount; k++) {
- if (this->dataAnalyzer->data(channel)
- ->samples.voltage.sample[k] < value) {
- falling++;
+ preTrigSamples = (settings->trigger.position * samplesDisplay);
+ postTrigSamples = sampleCount - (samplesDisplay - preTrigSamples);
+
+ if (settings->trigger.slope == Dso::SLOPE_POSITIVE) {
+ double prev = INT_MAX;
+ for (unsigned int i = preTrigSamples; i < postTrigSamples; i++) {
+ value = result->data(channel)->voltage.sample[i];
+ if (value > level && prev <= level) {
+ int rising = 0;
+ for (unsigned int k = i + 1; k < i + 11 && k < sampleCount; k++) {
+ if (result->data(channel)
+ ->voltage.sample[k] >= value) {
+ rising++;
+ }
+ }
+ if (rising > 7) {
+ swTriggerStart = i;
+ break;
+ }
+ }
+ prev = value;
+ }
+ } else if (settings->trigger.slope == Dso::SLOPE_NEGATIVE) {
+ double prev = INT_MIN;
+ for (unsigned int i = preTrigSamples; i < postTrigSamples; i++) {
+ value = result->data(channel)->voltage.sample[i];
+ if (value < level && prev >= level) {
+ int falling = 0;
+ for (unsigned int k = i + 1; k < i + 11 && k < sampleCount; k++) {
+ if (result->data(channel)->voltage.sample[k] < value) {
+ falling++;
+ }
+ }
+ if (falling > 7) {
+ swTriggerStart = i;
+ break;
+ }
+ }
+ prev = value;
+ }
}
- }
- if (falling > 7) {
- swTriggerStart = i;
- break;
- }
}
- prev = value;
- }
+ if (swTriggerStart == 0) {
+ timestampDebug(QString("Trigger not asserted. Data ignored"));
+ return;
+ }
}
- }
- if (swTriggerStart == 0) {
-#ifdef DEBUG
- timestampDebug(QString("Trigger not asserted. Data ignored"));
-#endif
- return;
- }
- }
- // Add graphs for channels
- for (int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT;
- ++mode) {
- for (int channel = 0; channel < this->settings->scope.voltage.size();
- ++channel) {
- // Check if this channel is used and available at the data analyzer
- if (((mode == Dso::CHANNELMODE_VOLTAGE)
- ? this->settings->scope.voltage[channel].used
- : this->settings->scope.spectrum[channel].used) &&
- this->dataAnalyzer->data(channel) &&
- !this->dataAnalyzer->data(channel)
- ->samples.voltage.sample.empty()) {
- // Check if the sample count has changed
- unsigned int sampleCount = (mode == Dso::CHANNELMODE_VOLTAGE)
- ? this->dataAnalyzer->data(channel)
- ->samples.voltage.sample.size()
- : this->dataAnalyzer->data(channel)
- ->samples.spectrum.sample.size();
- if (mode == Dso::CHANNELMODE_VOLTAGE)
- sampleCount -= (swTriggerStart - preTrigSamples);
- unsigned int neededSize = sampleCount * 2;
+ // Add graphs for channels
+ for (int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT; ++mode) {
+ for (int channel = 0; channel < (int)settings->voltage.size(); ++channel) {
+ // Check if this channel is used and available at the data analyzer
+ if (((mode == Dso::CHANNELMODE_VOLTAGE)
+ ? settings->voltage[channel].used
+ : settings->spectrum[channel].used) &&
+ result->data(channel) &&
+ !result->data(channel)
+ ->voltage.sample.empty()) {
+ // Check if the sample count has changed
+ size_t sampleCount = (mode == Dso::CHANNELMODE_VOLTAGE)
+ ? result->data(channel)->voltage.sample.size()
+ : result->data(channel)->spectrum.sample.size();
+ if (mode == Dso::CHANNELMODE_VOLTAGE)
+ sampleCount -= (swTriggerStart - preTrigSamples);
+ size_t neededSize = sampleCount * 2;
#if 0
- for(unsigned int index = 0; index < this->digitalPhosphorDepth; ++index) {
- if(this->vaChannel[mode][channel][index].size() != neededSize)
- this->vaChannel[mode][channel][index].clear(); // Something was changed, drop old traces
- }
+ for(unsigned int index = 0; index < digitalPhosphorDepth; ++index) {
+ if(vaChannel[mode][channel][index].size() != neededSize)
+ vaChannel[mode][channel][index].clear(); // Something was changed, drop old traces
+ }
#endif
- // Set size directly to avoid reallocations
- this->vaChannel[mode][channel].front().resize(neededSize);
-
- // Iterator to data for direct access
- std::vector::iterator glIterator =
- this->vaChannel[mode][channel].front().begin();
-
- // What's the horizontal distance between sampling points?
- double horizontalFactor;
- if (mode == Dso::CHANNELMODE_VOLTAGE)
- horizontalFactor =
- this->dataAnalyzer->data(channel)->samples.voltage.interval /
- this->settings->scope.horizontal.timebase;
- else
- horizontalFactor =
- this->dataAnalyzer->data(channel)->samples.spectrum.interval /
- this->settings->scope.horizontal.frequencybase;
-
- // Fill vector array
- if (mode == Dso::CHANNELMODE_VOLTAGE) {
- std::vector::const_iterator dataIterator =
- this->dataAnalyzer->data(channel)
- ->samples.voltage.sample.begin();
- const double gain = this->settings->scope.voltage[channel].gain;
- const double offset = this->settings->scope.voltage[channel].offset;
-
- std::advance(dataIterator, swTriggerStart - preTrigSamples);
-
- for (unsigned int position = 0; position < sampleCount;
- ++position) {
- *(glIterator++) = position * horizontalFactor - DIVS_TIME / 2;
- *(glIterator++) = *(dataIterator++) / gain + offset;
- }
- } else {
- std::vector::const_iterator dataIterator =
- this->dataAnalyzer->data(channel)
- ->samples.spectrum.sample.begin();
- const double magnitude =
- this->settings->scope.spectrum[channel].magnitude;
- const double offset =
- this->settings->scope.spectrum[channel].offset;
-
- for (unsigned int position = 0; position < sampleCount;
- ++position) {
- *(glIterator++) = position * horizontalFactor - DIVS_TIME / 2;
- *(glIterator++) = *(dataIterator++) / magnitude + offset;
+ // Set size directly to avoid reallocations
+ vaChannel[mode][(size_t)channel].front().resize(neededSize);
+
+ // Iterator to data for direct access
+ std::vector::iterator glIterator =
+ vaChannel[mode][(size_t)channel].front().begin();
+
+ // What's the horizontal distance between sampling points?
+ double horizontalFactor;
+ if (mode == Dso::CHANNELMODE_VOLTAGE)
+ horizontalFactor =
+ result->data(channel)->voltage.interval /
+ settings->horizontal.timebase;
+ else
+ horizontalFactor =
+ result->data(channel)->spectrum.interval /
+ settings->horizontal.frequencybase;
+
+ // Fill vector array
+ if (mode == Dso::CHANNELMODE_VOLTAGE) {
+ std::vector::const_iterator dataIterator =
+ result->data(channel)
+ ->voltage.sample.begin();
+ const double gain = settings->voltage[channel].gain;
+ const double offset = settings->voltage[channel].offset;
+
+ std::advance(dataIterator, swTriggerStart - preTrigSamples);
+
+ for (unsigned int position = 0; position < sampleCount;
+ ++position) {
+ *(glIterator++) = position * horizontalFactor - DIVS_TIME / 2;
+ *(glIterator++) = *(dataIterator++) / gain + offset;
+ }
+ } else {
+ std::vector::const_iterator dataIterator =
+ result->data(channel)
+ ->spectrum.sample.begin();
+ const double magnitude =
+ settings->spectrum[channel].magnitude;
+ const double offset =
+ settings->spectrum[channel].offset;
+
+ for (unsigned int position = 0; position < sampleCount;
+ ++position) {
+ *(glIterator++) = position * horizontalFactor - DIVS_TIME / 2;
+ *(glIterator++) = *(dataIterator++) / magnitude + offset;
+ }
+ }
+ } else {
+ // Delete all vector arrays
+ for (unsigned index = 0; index < (unsigned)digitalPhosphorDepth; ++index)
+ vaChannel[mode][channel][index].clear();
+ }
}
- }
- } else {
- // Delete all vector arrays
- for (unsigned int index = 0; index < this->digitalPhosphorDepth;
- ++index)
- this->vaChannel[mode][channel][index].clear();
- }
- }
- }
- } break;
-
- case Dso::GRAPHFORMAT_XY:
- for (int channel = 0; channel < this->settings->scope.voltage.size();
- ++channel) {
- // For even channel numbers check if this channel is used and this and the
- // following channel are available at the data analyzer
- if (channel % 2 == 0 &&
- channel + 1 < this->settings->scope.voltage.size() &&
- this->settings->scope.voltage[channel].used &&
- this->dataAnalyzer->data(channel) &&
- !this->dataAnalyzer->data(channel)->samples.voltage.sample.empty() &&
- this->dataAnalyzer->data(channel + 1) &&
- !this->dataAnalyzer->data(channel + 1)
- ->samples.voltage.sample.empty()) {
- // Check if the sample count has changed
- const unsigned int sampleCount = qMin(
- this->dataAnalyzer->data(channel)->samples.voltage.sample.size(),
- this->dataAnalyzer->data(channel + 1)
- ->samples.voltage.sample.size());
- const unsigned int neededSize = sampleCount * 2;
- for (unsigned int index = 0; index < this->digitalPhosphorDepth;
- ++index) {
- if (this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index]
- .size() != neededSize)
- this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index]
- .clear(); // Something was changed, drop old traces
- }
-
- // Set size directly to avoid reallocations
- this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel].front().resize(
- neededSize);
-
- // Iterator to data for direct access
- std::vector::iterator glIterator =
- this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel].front().begin();
-
- // Fill vector array
- unsigned int xChannel = channel;
- unsigned int yChannel = channel + 1;
- std::vector::const_iterator xIterator =
- this->dataAnalyzer->data(xChannel)->samples.voltage.sample.begin();
- std::vector::const_iterator yIterator =
- this->dataAnalyzer->data(yChannel)->samples.voltage.sample.begin();
- const double xGain = this->settings->scope.voltage[xChannel].gain;
- const double yGain = this->settings->scope.voltage[yChannel].gain;
- const double xOffset = this->settings->scope.voltage[xChannel].offset;
- const double yOffset = this->settings->scope.voltage[yChannel].offset;
-
- for (unsigned int position = 0; position < sampleCount; ++position) {
- *(glIterator++) = *(xIterator++) / xGain + xOffset;
- *(glIterator++) = *(yIterator++) / yGain + yOffset;
}
- } else {
- // Delete all vector arrays
- for (unsigned int index = 0; index < this->digitalPhosphorDepth;
- ++index)
- this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index].clear();
- }
-
- // Delete all spectrum graphs
- for (unsigned int index = 0; index < this->digitalPhosphorDepth; ++index)
- this->vaChannel[Dso::CHANNELMODE_SPECTRUM][channel][index].clear();
- }
- break;
+ } break;
+
+ case Dso::GRAPHFORMAT_XY:
+ for (int channel = 0; channel < settings->voltage.size();
+ ++channel) {
+ // For even channel numbers check if this channel is used and this and the
+ // following channel are available at the data analyzer
+ if (channel % 2 == 0 &&
+ channel + 1 < settings->voltage.size() &&
+ settings->voltage[channel].used &&
+ result->data(channel) &&
+ !result->data(channel)->voltage.sample.empty() &&
+ result->data(channel + 1) &&
+ !result->data(channel + 1)
+ ->voltage.sample.empty()) {
+ // Check if the sample count has changed
+ const unsigned sampleCount = qMin(
+ result->data(channel)->voltage.sample.size(),
+ result->data(channel + 1)
+ ->voltage.sample.size());
+ const unsigned neededSize = sampleCount * 2;
+ for (unsigned index = 0; index < (unsigned)digitalPhosphorDepth; ++index) {
+ if (vaChannel[Dso::CHANNELMODE_VOLTAGE][(size_t)channel][index].size() != neededSize)
+ vaChannel[Dso::CHANNELMODE_VOLTAGE][(size_t)channel][index].clear(); // Something was changed, drop old traces
+ }
- default:
- break;
- }
+ // Set size directly to avoid reallocations
+ vaChannel[Dso::CHANNELMODE_VOLTAGE][(size_t)channel].front().resize(
+ neededSize);
+
+ // Iterator to data for direct access
+ std::vector::iterator glIterator =
+ vaChannel[Dso::CHANNELMODE_VOLTAGE][channel].front().begin();
+
+ // Fill vector array
+ unsigned int xChannel = channel;
+ unsigned int yChannel = channel + 1;
+ std::vector::const_iterator xIterator =
+ result->data(xChannel)->voltage.sample.begin();
+ std::vector::const_iterator yIterator =
+ result->data(yChannel)->voltage.sample.begin();
+ const double xGain = settings->voltage[xChannel].gain;
+ const double yGain = settings->voltage[yChannel].gain;
+ const double xOffset = settings->voltage[xChannel].offset;
+ const double yOffset = settings->voltage[yChannel].offset;
+
+ for (unsigned int position = 0; position < sampleCount; ++position) {
+ *(glIterator++) = *(xIterator++) / xGain + xOffset;
+ *(glIterator++) = *(yIterator++) / yGain + yOffset;
+ }
+ } else {
+ // Delete all vector arrays
+ for (unsigned int index = 0; index < (unsigned)digitalPhosphorDepth; ++index)
+ vaChannel[Dso::CHANNELMODE_VOLTAGE][(size_t)channel][index].clear();
+ }
- emit graphsGenerated();
-}
+ // Delete all spectrum graphs
+ for (unsigned int index = 0; index < (unsigned)digitalPhosphorDepth; ++index)
+ vaChannel[Dso::CHANNELMODE_SPECTRUM][(size_t)channel][index].clear();
+ }
+ break;
-/// \brief Create the needed OpenGL vertex arrays for the grid.
-void GlGenerator::generateGrid() {
- // Grid
- this->vaGrid[0].resize(((DIVS_TIME * DIVS_SUB - 2) * (DIVS_VOLTAGE - 2) +
- (DIVS_VOLTAGE * DIVS_SUB - 2) * (DIVS_TIME - 2) -
- ((DIVS_TIME - 2) * (DIVS_VOLTAGE - 2))) *
- 2);
- std::vector::iterator glIterator = this->vaGrid[0].begin();
- // Draw vertical lines
- for (int div = 1; div < DIVS_TIME / 2; ++div) {
- for (int dot = 1; dot < DIVS_VOLTAGE / 2 * DIVS_SUB; ++dot) {
- float dotPosition = (float)dot / DIVS_SUB;
- *(glIterator++) = -div;
- *(glIterator++) = -dotPosition;
- *(glIterator++) = -div;
- *(glIterator++) = dotPosition;
- *(glIterator++) = div;
- *(glIterator++) = -dotPosition;
- *(glIterator++) = div;
- *(glIterator++) = dotPosition;
+ default:
+ break;
}
- }
- // Draw horizontal lines
- for (int div = 1; div < DIVS_VOLTAGE / 2; ++div) {
- for (int dot = 1; dot < DIVS_TIME / 2 * DIVS_SUB; ++dot) {
- if (dot % DIVS_SUB == 0)
- continue; // Already done by vertical lines
- float dotPosition = (float)dot / DIVS_SUB;
- *(glIterator++) = -dotPosition;
- *(glIterator++) = -div;
- *(glIterator++) = dotPosition;
- *(glIterator++) = -div;
- *(glIterator++) = -dotPosition;
- *(glIterator++) = div;
- *(glIterator++) = dotPosition;
- *(glIterator++) = div;
- }
- }
-
- // Axes
- this->vaGrid[1].resize(
- (2 + (DIVS_TIME * DIVS_SUB - 2) + (DIVS_VOLTAGE * DIVS_SUB - 2)) * 4);
- glIterator = this->vaGrid[1].begin();
- // Horizontal axis
- *(glIterator++) = -DIVS_TIME / 2;
- *(glIterator++) = 0;
- *(glIterator++) = DIVS_TIME / 2;
- *(glIterator++) = 0;
- // Vertical axis
- *(glIterator++) = 0;
- *(glIterator++) = -DIVS_VOLTAGE / 2;
- *(glIterator++) = 0;
- *(glIterator++) = DIVS_VOLTAGE / 2;
- // Subdiv lines on horizontal axis
- for (int line = 1; line < DIVS_TIME / 2 * DIVS_SUB; ++line) {
- float linePosition = (float)line / DIVS_SUB;
- *(glIterator++) = linePosition;
- *(glIterator++) = -0.05;
- *(glIterator++) = linePosition;
- *(glIterator++) = 0.05;
- *(glIterator++) = -linePosition;
- *(glIterator++) = -0.05;
- *(glIterator++) = -linePosition;
- *(glIterator++) = 0.05;
- }
- // Subdiv lines on vertical axis
- for (int line = 1; line < DIVS_VOLTAGE / 2 * DIVS_SUB; ++line) {
- float linePosition = (float)line / DIVS_SUB;
- *(glIterator++) = -0.05;
- *(glIterator++) = linePosition;
- *(glIterator++) = 0.05;
- *(glIterator++) = linePosition;
- *(glIterator++) = -0.05;
- *(glIterator++) = -linePosition;
- *(glIterator++) = 0.05;
- *(glIterator++) = -linePosition;
- }
-
- // Border
- this->vaGrid[2].resize(4 * 2);
- glIterator = this->vaGrid[2].begin();
- *(glIterator++) = -DIVS_TIME / 2;
- *(glIterator++) = -DIVS_VOLTAGE / 2;
- *(glIterator++) = DIVS_TIME / 2;
- *(glIterator++) = -DIVS_VOLTAGE / 2;
- *(glIterator++) = DIVS_TIME / 2;
- *(glIterator++) = DIVS_VOLTAGE / 2;
- *(glIterator++) = -DIVS_TIME / 2;
- *(glIterator++) = DIVS_VOLTAGE / 2;
+
+ emit graphsGenerated();
}
diff --git a/openhantek/src/glgenerator.h b/openhantek/src/glgenerator.h
index 6470c6c..8cea0db 100644
--- a/openhantek/src/glgenerator.h
+++ b/openhantek/src/glgenerator.h
@@ -1,79 +1,45 @@
-////////////////////////////////////////////////////////////////////////////////
-//
-// OpenHantek
-/// \file glgenerator.h
-/// \brief Declares the GlScope class.
-//
-// Copyright (C) 2008, 2009 Oleg Khudyakov
-// prcoder@potrebitel.ru
-// Copyright (C) 2010 Oliver Haag
-// oliver.haag@gmail.com
-//
-// This program is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program. If not, see .
-//
-////////////////////////////////////////////////////////////////////////////////
+// SPDX-License-Identifier: GPL-2.0+
-#ifndef GLGENERATOR_H
-#define GLGENERATOR_H
+#pragma once
#include
#include
#include
-#include "definitions.h"
+#include "scopesettings.h"
+#include "viewsettings.h"
+#include "dataanalyzerresult.h"
#define DIVS_TIME 10.0 ///< Number of horizontal screen divs
#define DIVS_VOLTAGE 8.0 ///< Number of vertical screen divs
#define DIVS_SUB 5 ///< Number of sub-divisions per div
-class DataAnalyzer;
-class DsoSettings;
class GlScope;
////////////////////////////////////////////////////////////////////////////////
-/// \class GlGenerator glgenerator.h
+/// \class GlGenerator
/// \brief Generates the vertex arrays for the GlScope classes.
class GlGenerator : public QObject {
- Q_OBJECT
-
- friend class GlScope;
+ Q_OBJECT
public:
- GlGenerator(DsoSettings *settings, QObject *parent = 0);
- ~GlGenerator();
-
- void setDataAnalyzer(DataAnalyzer *dataAnalyzer);
-
-protected:
- void generateGrid();
-
+ /// \brief Initializes the scope widget.
+ /// \param settings The target settings object.
+ /// \param parent The parent widget.
+ GlGenerator(DsoSettingsScope *scope, DsoSettingsView* view);
+ void generateGraphs(const DataAnalyzerResult *result);
+ const std::vector& channel(int mode, int channel, int index) const;
+ const std::vector& grid(int a) const;
+ bool isReady() const;
private:
- DataAnalyzer *dataAnalyzer;
- DsoSettings *settings;
-
- std::vector>>
- vaChannel[Dso::CHANNELMODE_COUNT];
- std::vector vaGrid[3];
-
- unsigned int digitalPhosphorDepth;
-
-public slots:
- void generateGraphs();
-
+ DsoSettingsScope *settings;
+ DsoSettingsView *view;
+ std::vector>> vaChannel[Dso::CHANNELMODE_COUNT];
+ std::vector vaGrid[3];
+ bool ready = false;
signals:
- void graphsGenerated(); ///< The graphs are ready to be drawn
+ void graphsGenerated(); ///< The graphs are ready to be drawn
};
-#endif
+
diff --git a/openhantek/src/glscope.cpp b/openhantek/src/glscope.cpp
index 47e5189..f2732f9 100644
--- a/openhantek/src/glscope.cpp
+++ b/openhantek/src/glscope.cpp
@@ -1,27 +1,4 @@
-////////////////////////////////////////////////////////////////////////////////
-//
-// OpenHantek
-// glscope.cpp
-//
-// Copyright (C) 2008, 2009 Oleg Khudyakov
-// prcoder@potrebitel.ru
-// Copyright (C) 2010 Oliver Haag
-// oliver.haag@gmail.com
-//
-// This program is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program. If not, see .
-//
-////////////////////////////////////////////////////////////////////////////////
+// SPDX-License-Identifier: GPL-2.0+
#include
@@ -33,250 +10,212 @@
#include "glgenerator.h"
#include "settings.h"
-////////////////////////////////////////////////////////////////////////////////
-// class GlScope
-/// \brief Initializes the scope widget.
-/// \param settings The settings that should be used.
-/// \param parent The parent widget.
-GlScope::GlScope(DsoSettings *settings, QWidget *parent) : GL_WIDGET_CLASS(parent) {
- this->settings = settings;
-
- this->generator = 0;
- this->zoomed = false;
+GlScope::GlScope(DsoSettings *settings, const GlGenerator *generator, QWidget *parent) : GL_WIDGET_CLASS(parent),
+ settings(settings), generator(generator) {
+ connect(generator, &GlGenerator::graphsGenerated, [this]() {update();});
}
-/// \brief Deletes OpenGL objects.
-GlScope::~GlScope() {}
-
/// \brief Initializes OpenGL output.
void GlScope::initializeGL() {
- glDisable(GL_DEPTH_TEST);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_DEPTH_TEST);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glPointSize(1);
+ glPointSize(1);
- QColor bg = settings->view.color.screen.background;
- glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF());
+ QColor bg = settings->view.color.screen.background;
+ glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF());
- glShadeModel(GL_SMOOTH /*GL_FLAT*/);
- glLineStipple(1, 0x3333);
+ glShadeModel(GL_SMOOTH /*GL_FLAT*/);
+ glLineStipple(1, 0x3333);
- glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
}
/// \brief Draw the graphs and the grid.
void GlScope::paintGL() {
- if (!this->isVisible())
- return;
+ if (!this->isVisible() || !this->generator->isReady())
+ return;
+
+ // Clear OpenGL buffer and configure settings
+ glClear(GL_COLOR_BUFFER_BIT);
+ glLineWidth(1);
+
+ if (this->settings->view.digitalPhosphorDepth > 0) {
+ drawGraph();
+ }
+
+ if (!this->zoomed) {
+ // Draw vertical lines at marker positions
+ glEnable(GL_LINE_STIPPLE);
+ QColor trColor = settings->view.color.screen.markers;
+ glColor4f(trColor.redF(), trColor.greenF(), trColor.blueF(), trColor.alphaF());
+
+ for (int marker = 0; marker < MARKER_COUNT; ++marker) {
+ if (!this->settings->scope.horizontal.marker_visible[marker])
+ continue;
+ if (this->vaMarker[marker].size() != 4) {
+ this->vaMarker[marker].resize(2 * 2);
+ this->vaMarker[marker][1] = -DIVS_VOLTAGE;
+ this->vaMarker[marker][3] = DIVS_VOLTAGE;
+ }
- // Clear OpenGL buffer and configure settings
- glClear(GL_COLOR_BUFFER_BIT);
- glLineWidth(1);
+ this->vaMarker[marker][0] =
+ this->settings->scope.horizontal.marker[marker];
+ this->vaMarker[marker][2] =
+ this->settings->scope.horizontal.marker[marker];
- // Draw the graphs
- if (this->generator && this->generator->digitalPhosphorDepth > 0) {
+ glVertexPointer(2, GL_FLOAT, 0, &this->vaMarker[marker].front());
+ glDrawArrays(GL_LINES, 0, this->vaMarker[marker].size() / 2);
+ }
+
+ glDisable(GL_LINE_STIPPLE);
+ }
+
+ // Draw grid
+ this->drawGrid();
+}
+
+/// \brief Resize the widget.
+/// \param width The new width of the widget.
+/// \param height The new height of the widget.
+void GlScope::resizeGL(int width, int height) {
+ glViewport(0, 0, (GLint)width, (GLint)height);
+
+ glMatrixMode(GL_PROJECTION);
+
+ // Set axes to div-scale and apply correction for exact pixelization
+ glLoadIdentity();
+ GLdouble pixelizationWidthCorrection =
+ (width > 0) ? (GLdouble)width / (width - 1) : 1;
+ GLdouble pixelizationHeightCorrection =
+ (height > 0) ? (GLdouble)height / (height - 1) : 1;
+ glOrtho(-(DIVS_TIME / 2) * pixelizationWidthCorrection,
+ (DIVS_TIME / 2) * pixelizationWidthCorrection,
+ -(DIVS_VOLTAGE / 2) * pixelizationHeightCorrection,
+ (DIVS_VOLTAGE / 2) * pixelizationHeightCorrection, -1.0, 1.0);
+
+ glMatrixMode(GL_MODELVIEW);
+}
+
+/// \brief Set the zoom mode for this GlScope.
+/// \param zoomed true magnifies the area between the markers.
+void GlScope::setZoomMode(bool zoomed) { this->zoomed = zoomed; }
+
+/// \brief Draw the grid.
+void GlScope::drawGrid() {
+ glDisable(GL_POINT_SMOOTH);
+ glDisable(GL_LINE_SMOOTH);
+
+ QColor color;
+ // Grid
+ color = this->settings->view.color.screen.grid;
+ glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF());
+ glVertexPointer(2, GL_FLOAT, 0, &generator->grid(0).front());
+ glDrawArrays(GL_POINTS, 0, generator->grid(0).size() / 2);
+ // Axes
+ color = settings->view.color.screen.axes;
+ glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF());
+ glVertexPointer(2, GL_FLOAT, 0, &generator->grid(1).front());
+ glDrawArrays(GL_LINES, 0, generator->grid(1).size() / 2);
+ // Border
+ color = settings->view.color.screen.border;
+ glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF());
+ glVertexPointer(2, GL_FLOAT, 0, &generator->grid(2).front());
+ glDrawArrays(GL_LINE_LOOP, 0, generator->grid(2).size() / 2);
+}
+
+void GlScope::drawGraphDepth(int mode, int channel, int index)
+{
+ if (generator->channel(mode, channel, index).empty()) return;
+ QColor trColor;
+ if (mode == Dso::CHANNELMODE_VOLTAGE)
+ trColor = settings->view.color.screen.voltage[channel]
+ .darker(fadingFactor[index]);
+ else
+ trColor = settings->view.color.screen.spectrum[channel]
+ .darker(fadingFactor[index]);
+ glColor4f(trColor.redF(), trColor.greenF(), trColor.blueF(),
+ trColor.alphaF());
+ glVertexPointer(
+ 2, GL_FLOAT, 0,
+ &generator->channel(mode, channel, index).front());
+ glDrawArrays(
+ (this->settings->view.interpolation == Dso::INTERPOLATION_OFF) ? GL_POINTS : GL_LINE_STRIP,
+ 0, generator->channel(mode, channel, index).size() /
+ 2);
+}
+
+void GlScope::drawGraph()
+{
if (this->settings->view.antialiasing) {
- glEnable(GL_POINT_SMOOTH);
- glEnable(GL_LINE_SMOOTH);
- glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
+ glEnable(GL_POINT_SMOOTH);
+ glEnable(GL_LINE_SMOOTH);
+ glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
}
// Apply zoom settings via matrix transformation
if (this->zoomed) {
- glPushMatrix();
- glScalef(DIVS_TIME / fabs(this->settings->scope.horizontal.marker[1] -
- this->settings->scope.horizontal.marker[0]),
- 1.0, 1.0);
- glTranslatef(-(this->settings->scope.horizontal.marker[0] +
+ glPushMatrix();
+ glScalef(DIVS_TIME / fabs(this->settings->scope.horizontal.marker[1] -
+ this->settings->scope.horizontal.marker[0]),
+ 1.0, 1.0);
+ glTranslatef(-(this->settings->scope.horizontal.marker[0] +
this->settings->scope.horizontal.marker[1]) /
- 2,
- 0.0, 0.0);
+ 2,
+ 0.0, 0.0);
}
// Values we need for the fading of the digital phosphor
- double *fadingFactor = new double[this->generator->digitalPhosphorDepth];
- fadingFactor[0] = 100;
- double fadingRatio = pow(10.0, 2.0 / this->generator->digitalPhosphorDepth);
- for (unsigned int index = 1; index < this->generator->digitalPhosphorDepth;
- ++index)
- fadingFactor[index] = fadingFactor[index - 1] * fadingRatio;
+ if ((int)fadingFactor.size() != this->settings->view.digitalPhosphorDepth) {
+ fadingFactor.resize((size_t)this->settings->view.digitalPhosphorDepth);
+ fadingFactor[0] = 100;
+ double fadingRatio = pow(10.0, 2.0 / this->settings->view.digitalPhosphorDepth);
+ for (unsigned index = 1; index < this->settings->view.digitalPhosphorDepth; ++index)
+ fadingFactor[index] = fadingFactor[index - 1] * fadingRatio;
+ }
switch (this->settings->scope.horizontal.format) {
case Dso::GRAPHFORMAT_TY:
- // Real and virtual channels
- for (int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT;
- ++mode) {
- for (int channel = 0; channel < this->settings->scope.voltage.count();
- ++channel) {
- if ((mode == Dso::CHANNELMODE_VOLTAGE)
- ? this->settings->scope.voltage[channel].used
- : this->settings->scope.spectrum[channel].used) {
- // Draw graph for all available depths
- for (int index = this->generator->digitalPhosphorDepth - 1;
- index >= 0; index--) {
- if (!this->generator->vaChannel[mode][channel][index].empty()) {
- QColor trColor;
- if (mode == Dso::CHANNELMODE_VOLTAGE)
- trColor = settings->view.color.screen.voltage[channel]
- .darker(fadingFactor[index]);
- else
- trColor = settings->view.color.screen.spectrum[channel]
- .darker(fadingFactor[index]);
- glColor4f(trColor.redF(), trColor.greenF(), trColor.blueF(),
- trColor.alphaF());
- glVertexPointer(
- 2, GL_FLOAT, 0,
- &this->generator->vaChannel[mode][channel][index].front());
- glDrawArrays(
- (this->settings->view.interpolation ==
- Dso::INTERPOLATION_OFF)
- ? GL_POINTS
- : GL_LINE_STRIP,
- 0, this->generator->vaChannel[mode][channel][index].size() /
- 2);
- }
+ // Real and virtual channels
+ for (int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT; ++mode) {
+ for (int channel = 0; channel < this->settings->scope.voltage.count(); ++channel) {
+ if (!channelUsed(mode, channel))
+ continue;
+
+ // Draw graph for all available depths
+ for (int index = this->settings->view.digitalPhosphorDepth - 1; index >= 0; index--) {
+ drawGraphDepth(mode, channel, index);
+ }
}
- }
}
- }
- break;
+ break;
case Dso::GRAPHFORMAT_XY:
- // Real and virtual channels
- for (int channel = 0; channel < this->settings->scope.voltage.count() - 1;
- channel += 2) {
- if (this->settings->scope.voltage[channel].used) {
- // Draw graph for all available depths
- for (int index = this->generator->digitalPhosphorDepth - 1;
- index >= 0; index--) {
- if (!this->generator
- ->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index]
- .empty()) {
- QColor trColor = settings->view.color.screen.voltage[channel]
- .darker(fadingFactor[index]);
- glColor4f(trColor.redF(), trColor.greenF(), trColor.blueF(),
- trColor.alphaF());
- glVertexPointer(
- 2, GL_FLOAT, 0,
- &this->generator
- ->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index]
- .front());
- glDrawArrays(
- (this->settings->view.interpolation == Dso::INTERPOLATION_OFF)
- ? GL_POINTS
- : GL_LINE_STRIP,
- 0,
- this->generator
- ->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index]
- .size() /
- 2);
+ // Real and virtual channels
+ for (int channel = 0; channel < this->settings->scope.voltage.count() - 1; channel += 2) {
+ if (this->settings->scope.voltage[channel].used) {
+ for (int index = this->settings->view.digitalPhosphorDepth - 1; index >= 0; index--) {
+ drawGraphDepth(Dso::CHANNELMODE_VOLTAGE, channel, index);
+ }
}
- }
}
- }
- break;
+ break;
default:
- break;
+ break;
}
- delete[] fadingFactor;
-
glDisable(GL_POINT_SMOOTH);
glDisable(GL_LINE_SMOOTH);
if (this->zoomed)
- glPopMatrix();
- }
-
- if (!this->zoomed) {
- // Draw vertical lines at marker positions
- glEnable(GL_LINE_STIPPLE);
- QColor trColor = settings->view.color.screen.markers;
- glColor4f(trColor.redF(), trColor.greenF(), trColor.blueF(),
- trColor.alphaF());
-
- for (int marker = 0; marker < MARKER_COUNT; ++marker) {
- if (!this->settings->scope.horizontal.marker_visible[marker])
- continue;
- if (this->vaMarker[marker].size() != 4) {
- this->vaMarker[marker].resize(2 * 2);
- this->vaMarker[marker][1] = -DIVS_VOLTAGE;
- this->vaMarker[marker][3] = DIVS_VOLTAGE;
- }
-
- this->vaMarker[marker][0] =
- this->settings->scope.horizontal.marker[marker];
- this->vaMarker[marker][2] =
- this->settings->scope.horizontal.marker[marker];
-
- glVertexPointer(2, GL_FLOAT, 0, &this->vaMarker[marker].front());
- glDrawArrays(GL_LINES, 0, this->vaMarker[marker].size() / 2);
- }
-
- glDisable(GL_LINE_STIPPLE);
- }
-
- // Draw grid
- this->drawGrid();
-}
-
-/// \brief Resize the widget.
-/// \param width The new width of the widget.
-/// \param height The new height of the widget.
-void GlScope::resizeGL(int width, int height) {
- glViewport(0, 0, (GLint)width, (GLint)height);
-
- glMatrixMode(GL_PROJECTION);
-
- // Set axes to div-scale and apply correction for exact pixelization
- glLoadIdentity();
- GLdouble pixelizationWidthCorrection =
- (width > 0) ? (GLdouble)width / (width - 1) : 1;
- GLdouble pixelizationHeightCorrection =
- (height > 0) ? (GLdouble)height / (height - 1) : 1;
- glOrtho(-(DIVS_TIME / 2) * pixelizationWidthCorrection,
- (DIVS_TIME / 2) * pixelizationWidthCorrection,
- -(DIVS_VOLTAGE / 2) * pixelizationHeightCorrection,
- (DIVS_VOLTAGE / 2) * pixelizationHeightCorrection, -1.0, 1.0);
-
- glMatrixMode(GL_MODELVIEW);
-}
-
-/// \brief Set the generator that provides the vertex arrays.
-/// \param generator Pointer to the GlGenerator class.
-void GlScope::setGenerator(GlGenerator *generator) {
- if (this->generator)
- disconnect(this->generator, SIGNAL(graphsGenerated()), this,
- SLOT(update()));
- this->generator = generator;
- connect(this->generator, SIGNAL(graphsGenerated()), this, SLOT(update()));
+ glPopMatrix();
}
-/// \brief Set the zoom mode for this GlScope.
-/// \param zoomed true magnifies the area between the markers.
-void GlScope::setZoomMode(bool zoomed) { this->zoomed = zoomed; }
-
-/// \brief Draw the grid.
-void GlScope::drawGrid() {
- glDisable(GL_POINT_SMOOTH);
- glDisable(GL_LINE_SMOOTH);
-
- QColor color;
- // Grid
- color = this->settings->view.color.screen.grid;
- glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF());
- glVertexPointer(2, GL_FLOAT, 0, &this->generator->vaGrid[0].front());
- glDrawArrays(GL_POINTS, 0, this->generator->vaGrid[0].size() / 2);
- // Axes
- color = settings->view.color.screen.axes;
- glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF());
- glVertexPointer(2, GL_FLOAT, 0, &this->generator->vaGrid[1].front());
- glDrawArrays(GL_LINES, 0, this->generator->vaGrid[1].size() / 2);
- // Border
- color = settings->view.color.screen.border;
- glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF());
- glVertexPointer(2, GL_FLOAT, 0, &this->generator->vaGrid[2].front());
- glDrawArrays(GL_LINE_LOOP, 0, this->generator->vaGrid[2].size() / 2);
+bool GlScope::channelUsed(int mode, int channel)
+{
+ return (mode == Dso::CHANNELMODE_VOLTAGE)
+ ? settings->scope.voltage[channel].used
+ : settings->scope.spectrum[channel].used;
}
diff --git a/openhantek/src/glscope.h b/openhantek/src/glscope.h
index 466999e..e82c8e5 100644
--- a/openhantek/src/glscope.h
+++ b/openhantek/src/glscope.h
@@ -1,33 +1,10 @@
-////////////////////////////////////////////////////////////////////////////////
-//
-// OpenHantek
-/// \file glscope.h
-/// \brief Declares the GlScope class.
-//
-// Copyright (C) 2008, 2009 Oleg Khudyakov
-// prcoder@potrebitel.ru
-// Copyright (C) 2010 Oliver Haag
-// oliver.haag@gmail.com
-//
-// This program is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program. If not, see .
-//
-////////////////////////////////////////////////////////////////////////////////
+// SPDX-License-Identifier: GPL-2.0+
-#ifndef GLSCOPE_H
-#define GLSCOPE_H
+#pragma once
+#include
#include
+#include
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
#include
using GL_WIDGET_CLASS = QOpenGLWidget;
@@ -46,28 +23,30 @@ class DsoSettings;
/// \class GlScope glscope.h
/// \brief OpenGL accelerated widget that displays the oscilloscope screen.
class GlScope : public GL_WIDGET_CLASS {
- Q_OBJECT
+ Q_OBJECT
public:
- GlScope(DsoSettings *settings, QWidget *parent = 0);
- ~GlScope();
+ /// \brief Initializes the scope widget.
+ /// \param settings The settings that should be used.
+ /// \param parent The parent widget.
+ GlScope(DsoSettings *settings, const GlGenerator *generator, QWidget *parent = 0);
- void setGenerator(GlGenerator *generator);
- void setZoomMode(bool zoomed);
+ void setZoomMode(bool zoomed);
protected:
- void initializeGL();
- void paintGL();
- void resizeGL(int width, int height);
-
- void drawGrid();
-
+ void initializeGL() override;
+ void paintGL() override;
+ void resizeGL(int width, int height) override;
+
+ void drawGrid();
+ void drawGraphDepth(int mode, int channel, int index);
+ void drawGraph();
+ bool channelUsed(int mode, int channel);
private:
- GlGenerator *generator;
- DsoSettings *settings;
+ DsoSettings *settings;
+ const GlGenerator *generator;
+ std::vector fadingFactor;
- std::vector vaMarker[2];
- bool zoomed;
+ std::vector vaMarker[2];
+ bool zoomed = false;
};
-
-#endif
diff --git a/openhantek/src/hantek/dsosamples.h b/openhantek/src/hantek/dsosamples.h
new file mode 100644
index 0000000..8bb75a3
--- /dev/null
+++ b/openhantek/src/hantek/dsosamples.h
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+struct DSOsamples {
+ std::vector> data; ///< Pointer to input data from device
+ double samplerate = 0.0; ///< The samplerate of the input data
+ bool append = false; ///< true, if waiting data should be appended
+ mutable QReadWriteLock lock;
+};
diff --git a/openhantek/src/hantek/hantekdsocontrol.cpp b/openhantek/src/hantek/hantekdsocontrol.cpp
index 49c5d09..241821c 100644
--- a/openhantek/src/hantek/hantekdsocontrol.cpp
+++ b/openhantek/src/hantek/hantekdsocontrol.cpp
@@ -40,6 +40,11 @@ const USBDevice *HantekDsoControl::getDevice() const
return device;
}
+const DSOsamples &HantekDsoControl::getLastSamples()
+{
+ return result;
+}
+
/// \brief Initializes the command buffers and lists.
/// \param parent The parent widget.
HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) {
@@ -73,24 +78,24 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) {
}
// Set settings to default values
- this->settings.samplerate.limits = &(this->specification.samplerate.single);
- this->settings.samplerate.downsampler = 1;
- this->settings.samplerate.current = 1e8;
- this->settings.trigger.position = 0;
- this->settings.trigger.point = 0;
- this->settings.trigger.mode = Dso::TRIGGERMODE_NORMAL;
- this->settings.trigger.slope = Dso::SLOPE_POSITIVE;
- this->settings.trigger.special = false;
- this->settings.trigger.source = 0;
+ settings.samplerate.limits = &(this->specification.samplerate.single);
+ settings.samplerate.downsampler = 1;
+ settings.samplerate.current = 1e8;
+ settings.trigger.position = 0;
+ settings.trigger.point = 0;
+ settings.trigger.mode = Dso::TRIGGERMODE_NORMAL;
+ settings.trigger.slope = Dso::SLOPE_POSITIVE;
+ settings.trigger.special = false;
+ settings.trigger.source = 0;
for (unsigned int channel = 0; channel < HANTEK_CHANNELS; ++channel) {
- this->settings.trigger.level[channel] = 0.0;
- this->settings.voltage[channel].gain = 0;
- this->settings.voltage[channel].offset = 0.0;
- this->settings.voltage[channel].offsetReal = 0.0;
- this->settings.voltage[channel].used = false;
+ settings.trigger.level[channel] = 0.0;
+ settings.voltage[channel].gain = 0;
+ settings.voltage[channel].offset = 0.0;
+ settings.voltage[channel].offsetReal = 0.0;
+ settings.voltage[channel].used = false;
}
- this->settings.recordLengthId = 1;
- this->settings.usedChannels = 0;
+ settings.recordLengthId = 1;
+ settings.usedChannels = 0;
// Special trigger sources
this->specialTriggerSources << tr("EXT") << tr("EXT/10");
@@ -117,7 +122,7 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) {
this->lastTriggerMode = (Dso::TriggerMode)-1;
// Sample buffers
- this->samples.resize(HANTEK_CHANNELS);
+ result.data.resize(HANTEK_CHANNELS);
this->previousSampleCount = 0;
@@ -390,9 +395,9 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) {
this->specification.sampleSize = 8;
break;
}
- this->settings.recordLengthId = 1;
- this->settings.samplerate.limits = &(this->specification.samplerate.single);
- this->settings.samplerate.downsampler = 1;
+ settings.recordLengthId = 1;
+ settings.samplerate.limits = &(this->specification.samplerate.single);
+ settings.samplerate.downsampler = 1;
this->previousSampleCount = 0;
// Get channel level data
@@ -408,16 +413,16 @@ HantekDsoControl::HantekDsoControl(USBDevice* device) : device(device) {
// Emit signals for initial settings
emit availableRecordLengthsChanged(
- this->settings.samplerate.limits->recordLengths);
+ settings.samplerate.limits->recordLengths);
updateSamplerateLimits();
- emit recordLengthChanged(this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId]);
- if (this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId] != UINT_MAX)
- emit recordTimeChanged((double)this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId] /
- this->settings.samplerate.current);
- emit samplerateChanged(this->settings.samplerate.current);
+ emit recordLengthChanged(settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId]);
+ if (settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId] != UINT_MAX)
+ emit recordTimeChanged((double)settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId] /
+ settings.samplerate.current);
+ emit samplerateChanged(settings.samplerate.current);
if (this->device->getUniqueModelID() == MODEL_DSO6022BE) {
QList sampleSteps;
@@ -445,7 +450,7 @@ unsigned int HantekDsoControl::getChannelCount() { return HANTEK_CHANNELS; }
/// \brief Get available record lengths for this oscilloscope.
/// \return The number of physical channels, empty list for continuous.
QList *HantekDsoControl::getAvailableRecordLengths() {
- return &this->settings.samplerate.limits->recordLengths;
+ return &settings.samplerate.limits->recordLengths;
}
/// \brief Get minimum samplerate for this oscilloscope.
@@ -459,7 +464,7 @@ double HantekDsoControl::getMinSamplerate() {
/// \return The maximum samplerate for the current configuration in S/s.
double HantekDsoControl::getMaxSamplerate() {
ControlSamplerateLimits *limits =
- (this->settings.usedChannels <= 1)
+ (settings.usedChannels <= 1)
? &this->specification.samplerate.multi
: &this->specification.samplerate.single;
return limits->max;
@@ -469,18 +474,18 @@ double HantekDsoControl::getMaxSamplerate() {
void HantekDsoControl::updateInterval() {
// Check the current oscilloscope state everytime 25% of the time the buffer
// should be refilled
- if (this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId] == UINT_MAX)
+ if (settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId] == UINT_MAX)
cycleTime = (int)((double)this->device->getPacketSize() /
- ((this->settings.samplerate.limits ==
+ ((settings.samplerate.limits ==
&this->specification.samplerate.multi)
? 1
: HANTEK_CHANNELS) /
- this->settings.samplerate.current * 250);
+ settings.samplerate.current * 250);
else
- cycleTime = (int)((double)this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId] /
- this->settings.samplerate.current * 250);
+ cycleTime = (int)((double)settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId] /
+ settings.samplerate.current * 250);
// Not more often than every 10 ms though but at least once every second
cycleTime = qBound(10, cycleTime, 1000);
@@ -519,7 +524,7 @@ int HantekDsoControl::getCaptureState() {
if (errorCode < 0)
return errorCode;
- this->settings.trigger.point =
+ settings.trigger.point =
this->calculateTriggerPoint(response.getTriggerPoint());
return (int)response.getCaptureState();
@@ -571,192 +576,194 @@ int HantekDsoControl::getSamples(bool process) {
return errorCode;
// Process the data only if we want it
- if (process) {
- // How much data did we really receive?
- dataLength = errorCode;
- if (this->specification.sampleSize > 8)
- totalSampleCount = dataLength / 2;
- else
- totalSampleCount = dataLength;
+ if (!process)
+ return LIBUSB_SUCCESS;
- QMutexLocker locker(&samplesMutex);
+ // How much data did we really receive?
+ dataLength = errorCode;
+ if (this->specification.sampleSize > 8)
+ totalSampleCount = dataLength / 2;
+ else
+ totalSampleCount = dataLength;
+
+ // Convert channel data
+ if (fastRate) {
+ QWriteLocker locker(&result.lock);
+ result.samplerate = settings.samplerate.current;
+ result.append = settings.samplerate.limits->recordLengths[settings.recordLengthId] == UINT_MAX;
+
+ // Fast rate mode, one channel is using all buffers
+ sampleCount = totalSampleCount;
+ int channel = 0;
+ for (; channel < HANTEK_CHANNELS; ++channel) {
+ if (settings.voltage[channel].used)
+ break;
+ }
- // Convert channel data
- if (fastRate) {
- // Fast rate mode, one channel is using all buffers
- sampleCount = totalSampleCount;
- int channel = 0;
- for (; channel < HANTEK_CHANNELS; ++channel) {
- if (this->settings.voltage[channel].used)
- break;
- }
+ // Clear unused channels
+ for (int channelCounter = 0; channelCounter < HANTEK_CHANNELS;
+ ++channelCounter)
+ if (channelCounter != channel) {
- // Clear unused channels
- for (int channelCounter = 0; channelCounter < HANTEK_CHANNELS;
- ++channelCounter)
- if (channelCounter != channel) {
+ result.data[channelCounter].clear();
+ }
- this->samples[channelCounter].clear();
+ if (channel < HANTEK_CHANNELS) {
+ // Resize sample vector
+ result.data[channel].resize(sampleCount);
+
+ // Convert data from the oscilloscope and write it into the sample
+ // buffer
+ unsigned int bufferPosition = settings.trigger.point * 2;
+ if (this->specification.sampleSize > 8) {
+ // Additional most significant bits after the normal data
+ unsigned int extraBitsPosition; // Track the position of the extra
+ // bits in the additional byte
+ unsigned int extraBitsSize =
+ this->specification.sampleSize - 8; // Number of extra bits
+ unsigned short int extraBitsMask =
+ (0x00ff << extraBitsSize) &
+ 0xff00; // Mask for extra bits extraction
+
+ for (unsigned int realPosition = 0; realPosition < sampleCount;
+ ++realPosition, ++bufferPosition) {
+ if (bufferPosition >= sampleCount)
+ bufferPosition %= sampleCount;
+
+ extraBitsPosition = bufferPosition % HANTEK_CHANNELS;
+
+ result.data[channel][realPosition] =
+ ((double)((unsigned short int)data[bufferPosition] +
+ (((unsigned short int)
+ data[sampleCount + bufferPosition -
+ extraBitsPosition]
+ << (8 -
+ (HANTEK_CHANNELS - 1 - extraBitsPosition) *
+ extraBitsSize)) &
+ extraBitsMask)) /
+ this->specification
+ .voltageLimit[channel]
+ [settings.voltage[channel].gain] -
+ settings.voltage[channel].offsetReal) *
+ this->specification
+ .gainSteps[settings.voltage[channel].gain];
}
-
- if (channel < HANTEK_CHANNELS) {
+ } else {
+ for (unsigned int realPosition = 0; realPosition < sampleCount;
+ ++realPosition, ++bufferPosition) {
+ if (bufferPosition >= sampleCount)
+ bufferPosition %= sampleCount;
+
+ double dataBuf = (double)((int)data[bufferPosition]);
+ result.data[channel][realPosition] =
+ (dataBuf /
+ this->specification
+ .voltageLimit[channel]
+ [settings.voltage[channel].gain] -
+ settings.voltage[channel].offsetReal) *
+ this->specification
+ .gainSteps[settings.voltage[channel].gain];
+ }
+ }
+ }
+ } else {
+ QWriteLocker locker(&result.lock);
+ result.samplerate = settings.samplerate.current;
+ result.append = settings.samplerate.limits->recordLengths[settings.recordLengthId] == UINT_MAX;
+
+ // Normal mode, channels are using their separate buffers
+ sampleCount = totalSampleCount / HANTEK_CHANNELS;
+ // if device is 6022BE, drop heading & trailing samples
+ if (this->device->getUniqueModelID() == MODEL_DSO6022BE)
+ sampleCount -= (DROP_DSO6022_HEAD + DROP_DSO6022_TAIL);
+ for (int channel = 0; channel < HANTEK_CHANNELS; ++channel) {
+ if (settings.voltage[channel].used) {
// Resize sample vector
- this->samples[channel].resize(sampleCount);
+ if (result.data[channel].size() < sampleCount) {
+ result.data[channel].resize(sampleCount);
+ }
// Convert data from the oscilloscope and write it into the sample
// buffer
- unsigned int bufferPosition = this->settings.trigger.point * 2;
+ unsigned int bufferPosition = settings.trigger.point * 2;
if (this->specification.sampleSize > 8) {
// Additional most significant bits after the normal data
- unsigned int extraBitsPosition; // Track the position of the extra
- // bits in the additional byte
unsigned int extraBitsSize =
this->specification.sampleSize - 8; // Number of extra bits
unsigned short int extraBitsMask =
(0x00ff << extraBitsSize) &
0xff00; // Mask for extra bits extraction
+ unsigned int extraBitsIndex =
+ 8 -
+ channel * 2; // Bit position offset for extra bits extraction
for (unsigned int realPosition = 0; realPosition < sampleCount;
- ++realPosition, ++bufferPosition) {
- if (bufferPosition >= sampleCount)
- bufferPosition %= sampleCount;
-
- extraBitsPosition = bufferPosition % HANTEK_CHANNELS;
-
- this->samples[channel][realPosition] =
- ((double)((unsigned short int)data[bufferPosition] +
- (((unsigned short int)
- data[sampleCount + bufferPosition -
- extraBitsPosition]
- << (8 -
- (HANTEK_CHANNELS - 1 - extraBitsPosition) *
- extraBitsSize)) &
- extraBitsMask)) /
+ ++realPosition, bufferPosition += HANTEK_CHANNELS) {
+ if (bufferPosition >= totalSampleCount)
+ bufferPosition %= totalSampleCount;
+
+ result.data[channel][realPosition] =
+ ((double)((unsigned short int)
+ data[bufferPosition + HANTEK_CHANNELS - 1 -
+ channel] +
+ (((unsigned short int)
+ data[totalSampleCount + bufferPosition]
+ << extraBitsIndex) &
+ extraBitsMask)) /
this->specification
.voltageLimit[channel]
- [this->settings.voltage[channel].gain] -
- this->settings.voltage[channel].offsetReal) *
+ [settings.voltage[channel].gain] -
+ settings.voltage[channel].offsetReal) *
this->specification
- .gainSteps[this->settings.voltage[channel].gain];
+ .gainSteps[settings.voltage[channel].gain];
}
} else {
+ if (this->device->getUniqueModelID() == MODEL_DSO6022BE) {
+ bufferPosition += channel;
+ // if device is 6022BE, offset DROP_DSO6022_HEAD incrementally
+ bufferPosition += DROP_DSO6022_HEAD * 2;
+ } else
+ bufferPosition += HANTEK_CHANNELS - 1 - channel;
+
for (unsigned int realPosition = 0; realPosition < sampleCount;
- ++realPosition, ++bufferPosition) {
- if (bufferPosition >= sampleCount)
- bufferPosition %= sampleCount;
-
- double dataBuf = (double)((int)data[bufferPosition]);
- this->samples[channel][realPosition] =
- (dataBuf /
- this->specification
- .voltageLimit[channel]
- [this->settings.voltage[channel].gain] -
- this->settings.voltage[channel].offsetReal) *
- this->specification
- .gainSteps[this->settings.voltage[channel].gain];
- }
- }
- }
- } else {
- // Normal mode, channels are using their separate buffers
- sampleCount = totalSampleCount / HANTEK_CHANNELS;
- // if device is 6022BE, drop heading & trailing samples
- if (this->device->getUniqueModelID() == MODEL_DSO6022BE)
- sampleCount -= (DROP_DSO6022_HEAD + DROP_DSO6022_TAIL);
- for (int channel = 0; channel < HANTEK_CHANNELS; ++channel) {
- if (this->settings.voltage[channel].used) {
- // Resize sample vector
- if (samples[channel].size() < sampleCount) {
- this->samples[channel].resize(sampleCount);
- }
+ ++realPosition, bufferPosition += HANTEK_CHANNELS) {
+ if (bufferPosition >= totalSampleCount)
+ bufferPosition %= totalSampleCount;
- // Convert data from the oscilloscope and write it into the sample
- // buffer
- unsigned int bufferPosition = this->settings.trigger.point * 2;
- if (this->specification.sampleSize > 8) {
- // Additional most significant bits after the normal data
- unsigned int extraBitsSize =
- this->specification.sampleSize - 8; // Number of extra bits
- unsigned short int extraBitsMask =
- (0x00ff << extraBitsSize) &
- 0xff00; // Mask for extra bits extraction
- unsigned int extraBitsIndex =
- 8 -
- channel * 2; // Bit position offset for extra bits extraction
-
- for (unsigned int realPosition = 0; realPosition < sampleCount;
- ++realPosition, bufferPosition += HANTEK_CHANNELS) {
- if (bufferPosition >= totalSampleCount)
- bufferPosition %= totalSampleCount;
-
- this->samples[channel][realPosition] =
- ((double)((unsigned short int)
- data[bufferPosition + HANTEK_CHANNELS - 1 -
- channel] +
- (((unsigned short int)
- data[totalSampleCount + bufferPosition]
- << extraBitsIndex) &
- extraBitsMask)) /
+ if (this->device->getUniqueModelID() == MODEL_DSO6022BE) {
+ double dataBuf = (double)((int)(data[bufferPosition] - 0x83));
+ result.data[channel][realPosition] =
+ (dataBuf /
+ this->specification
+ .voltageLimit[channel]
+ [settings.voltage[channel].gain]) *
this->specification
- .voltageLimit[channel]
- [this->settings.voltage[channel].gain] -
- this->settings.voltage[channel].offsetReal) *
+ .gainSteps[settings.voltage[channel].gain];
+ } else {
+ double dataBuf = (double)((int)(data[bufferPosition]));
+ result.data[channel][realPosition] =
+ (dataBuf /
+ this->specification.voltageLimit
+ [channel][settings.voltage[channel].gain] -
+ settings.voltage[channel].offsetReal) *
this->specification
- .gainSteps[this->settings.voltage[channel].gain];
- }
- } else {
- if (this->device->getUniqueModelID() == MODEL_DSO6022BE) {
- bufferPosition += channel;
- // if device is 6022BE, offset DROP_DSO6022_HEAD incrementally
- bufferPosition += DROP_DSO6022_HEAD * 2;
- } else
- bufferPosition += HANTEK_CHANNELS - 1 - channel;
-
- for (unsigned int realPosition = 0; realPosition < sampleCount;
- ++realPosition, bufferPosition += HANTEK_CHANNELS) {
- if (bufferPosition >= totalSampleCount)
- bufferPosition %= totalSampleCount;
-
- if (this->device->getUniqueModelID() == MODEL_DSO6022BE) {
- double dataBuf = (double)((int)(data[bufferPosition] - 0x83));
- this->samples[channel][realPosition] =
- (dataBuf /
- this->specification
- .voltageLimit[channel]
- [this->settings.voltage[channel].gain]) *
- this->specification
- .gainSteps[this->settings.voltage[channel].gain];
- } else {
- double dataBuf = (double)((int)(data[bufferPosition]));
- this->samples[channel][realPosition] =
- (dataBuf /
- this->specification.voltageLimit
- [channel][this->settings.voltage[channel].gain] -
- this->settings.voltage[channel].offsetReal) *
- this->specification
- .gainSteps[this->settings.voltage[channel].gain];
- }
+ .gainSteps[settings.voltage[channel].gain];
}
}
- } else {
- // Clear unused channels
- this->samples[channel].clear();
}
+ } else {
+ // Clear unused channels
+ result.data[channel].clear();
}
}
-
-#ifdef DEBUG
- static unsigned int id = 0;
- ++id;
- timestampDebug(QString("Received packet %1").arg(id));
-#endif
- emit samplesAvailable(
- &(this->samples), this->settings.samplerate.current,
- this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId] == UINT_MAX,
- &(this->samplesMutex));
}
+ static unsigned int id = 0;
+ ++id;
+ timestampDebug(QString("Received packet %1").arg(id));
+
+ emit samplesAvailable();
+
return errorCode;
}
@@ -787,18 +794,18 @@ double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate,
// Get downsampling factor that would provide the requested rate
double bestDownsampler =
(double)limits->base /
- this->specification.bufferDividers[this->settings.recordLengthId] /
+ this->specification.bufferDividers[settings.recordLengthId] /
samplerate;
// Base samplerate sufficient, or is the maximum better?
if (bestDownsampler < 1.0 &&
(samplerate <= limits->max /
this->specification
- .bufferDividers[this->settings.recordLengthId] ||
+ .bufferDividers[settings.recordLengthId] ||
!maximum)) {
bestDownsampler = 0.0;
bestSamplerate =
limits->max /
- this->specification.bufferDividers[this->settings.recordLengthId];
+ this->specification.bufferDividers[settings.recordLengthId];
} else {
switch (this->specification.command.bulk.setSamplerate) {
case BULK_SETTRIGGERANDSAMPLERATE:
@@ -866,7 +873,7 @@ double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate,
bestSamplerate =
limits->base / bestDownsampler /
- this->specification.bufferDividers[this->settings.recordLengthId];
+ this->specification.bufferDividers[settings.recordLengthId];
}
if (downsampler)
@@ -878,11 +885,8 @@ double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate,
/// \param fastRate Is set to the state of the fast rate mode when provided.
/// \return The total number of samples the scope should return.
unsigned int HantekDsoControl::getSampleCount(bool *fastRate) {
- unsigned int totalSampleCount =
- this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId];
- bool fastRateEnabled =
- this->settings.samplerate.limits == &this->specification.samplerate.multi;
+ unsigned int totalSampleCount = settings.samplerate.limits->recordLengths[settings.recordLengthId];
+ bool fastRateEnabled = settings.samplerate.limits == &this->specification.samplerate.multi;
if (totalSampleCount == UINT_MAX) {
// Roll mode
@@ -905,7 +909,7 @@ unsigned int HantekDsoControl::getSampleCount(bool *fastRate) {
/// \return The record length that has been set, 0 on error.
unsigned int HantekDsoControl::updateRecordLength(unsigned int index) {
if (index >=
- (unsigned int)this->settings.samplerate.limits->recordLengths.size())
+ (unsigned int)settings.samplerate.limits->recordLengths.size())
return 0;
switch (this->specification.command.bulk.setRecordLength) {
@@ -947,9 +951,9 @@ unsigned int HantekDsoControl::updateRecordLength(unsigned int index) {
// Check if the divider has changed and adapt samplerate limits accordingly
bool bDividerChanged =
this->specification.bufferDividers[index] !=
- this->specification.bufferDividers[this->settings.recordLengthId];
+ this->specification.bufferDividers[settings.recordLengthId];
- this->settings.recordLengthId = index;
+ settings.recordLengthId = index;
if (bDividerChanged) {
this->updateSamplerateLimits();
@@ -958,7 +962,7 @@ unsigned int HantekDsoControl::updateRecordLength(unsigned int index) {
this->restoreTargets();
}
- return this->settings.samplerate.limits->recordLengths[index];
+ return settings.samplerate.limits->recordLengths[index];
}
/// \brief Sets the samplerate based on the parameters calculated by
@@ -1068,49 +1072,49 @@ unsigned int HantekDsoControl::updateSamplerate(unsigned int downsampler,
}
// Update settings
- bool fastRateChanged = fastRate != (this->settings.samplerate.limits ==
+ bool fastRateChanged = fastRate != (settings.samplerate.limits ==
&this->specification.samplerate.multi);
if (fastRateChanged) {
- this->settings.samplerate.limits = limits;
+ settings.samplerate.limits = limits;
}
- this->settings.samplerate.downsampler = downsampler;
+ settings.samplerate.downsampler = downsampler;
if (downsampler)
- this->settings.samplerate.current =
- this->settings.samplerate.limits->base /
- this->specification.bufferDividers[this->settings.recordLengthId] /
+ settings.samplerate.current =
+ settings.samplerate.limits->base /
+ this->specification.bufferDividers[settings.recordLengthId] /
downsampler;
else
- this->settings.samplerate.current =
- this->settings.samplerate.limits->max /
- this->specification.bufferDividers[this->settings.recordLengthId];
+ settings.samplerate.current =
+ settings.samplerate.limits->max /
+ this->specification.bufferDividers[settings.recordLengthId];
// Update dependencies
- this->setPretriggerPosition(this->settings.trigger.position);
+ this->setPretriggerPosition(settings.trigger.position);
// Emit signals for changed settings
if (fastRateChanged) {
emit availableRecordLengthsChanged(
- this->settings.samplerate.limits->recordLengths);
+ settings.samplerate.limits->recordLengths);
emit recordLengthChanged(
- this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId]);
+ settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId]);
}
// Check for Roll mode
- if (this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId] != UINT_MAX)
- emit recordTimeChanged((double)this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId] /
- this->settings.samplerate.current);
- emit samplerateChanged(this->settings.samplerate.current);
+ if (settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId] != UINT_MAX)
+ emit recordTimeChanged((double)settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId] /
+ settings.samplerate.current);
+ emit samplerateChanged(settings.samplerate.current);
return downsampler;
}
/// \brief Restore the samplerate/timebase targets after divider updates.
void HantekDsoControl::restoreTargets() {
- if (this->settings.samplerate.target.samplerateSet)
+ if (settings.samplerate.target.samplerateSet)
this->setSamplerate();
else
this->setRecordTime();
@@ -1121,15 +1125,15 @@ void HantekDsoControl::updateSamplerateLimits() {
// Works only if the minimum samplerate for normal mode is lower than for fast
// rate mode, which is the case for all models
ControlSamplerateLimits *limits =
- (this->settings.usedChannels <= 1)
+ (settings.usedChannels <= 1)
? &this->specification.samplerate.multi
: &this->specification.samplerate.single;
emit samplerateLimitsChanged(
(double)this->specification.samplerate.single.base /
this->specification.samplerate.single.maxDownsampler /
- this->specification.bufferDividers[this->settings.recordLengthId],
+ this->specification.bufferDividers[settings.recordLengthId],
limits->max /
- this->specification.bufferDividers[this->settings.recordLengthId]);
+ this->specification.bufferDividers[settings.recordLengthId]);
}
/// \brief Sets the size of the oscilloscopes sample buffer.
@@ -1143,12 +1147,12 @@ unsigned int HantekDsoControl::setRecordLength(unsigned int index) {
return 0;
this->restoreTargets();
- this->setPretriggerPosition(this->settings.trigger.position);
+ this->setPretriggerPosition(settings.trigger.position);
- emit recordLengthChanged(this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId]);
- return this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId];
+ emit recordLengthChanged(settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId]);
+ return settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId];
}
/// \brief Sets the samplerate of the oscilloscope.
@@ -1160,20 +1164,20 @@ double HantekDsoControl::setSamplerate(double samplerate) {
return 0.0;
if (samplerate == 0.0) {
- samplerate = this->settings.samplerate.target.samplerate;
+ samplerate = settings.samplerate.target.samplerate;
} else {
- this->settings.samplerate.target.samplerate = samplerate;
- this->settings.samplerate.target.samplerateSet = true;
+ settings.samplerate.target.samplerate = samplerate;
+ settings.samplerate.target.samplerateSet = true;
}
if (this->device->getUniqueModelID() != MODEL_DSO6022BE) {
// When possible, enable fast rate if it is required to reach the requested
// samplerate
bool fastRate =
- (this->settings.usedChannels <= 1) &&
+ (settings.usedChannels <= 1) &&
(samplerate >
this->specification.samplerate.single.max /
- this->specification.bufferDividers[this->settings.recordLengthId]);
+ this->specification.bufferDividers[settings.recordLengthId]);
// What is the nearest, at least as high samplerate the scope can provide?
unsigned int downsampler = 0;
@@ -1196,18 +1200,18 @@ double HantekDsoControl::setSamplerate(double samplerate) {
static_cast(this->control[CONTROLINDEX_SETTIMEDIV])
->setDiv(this->specification.sampleDiv[sampleId]);
this->controlPending[CONTROLINDEX_SETTIMEDIV] = true;
- this->settings.samplerate.current = samplerate;
+ settings.samplerate.current = samplerate;
// Provide margin for SW trigger
unsigned int sampleMargin = 2000;
// Check for Roll mode
- if (this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId] != UINT_MAX)
+ if (settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId] != UINT_MAX)
emit recordTimeChanged(
- (double)(this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId] - sampleMargin) /
- this->settings.samplerate.current);
- emit samplerateChanged(this->settings.samplerate.current);
+ (double)(settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId] - sampleMargin) /
+ settings.samplerate.current);
+ emit samplerateChanged(settings.samplerate.current);
return samplerate;
}
@@ -1222,26 +1226,26 @@ double HantekDsoControl::setRecordTime(double duration) {
return 0.0;
if (duration == 0.0) {
- duration = this->settings.samplerate.target.duration;
+ duration = settings.samplerate.target.duration;
} else {
- this->settings.samplerate.target.duration = duration;
- this->settings.samplerate.target.samplerateSet = false;
+ settings.samplerate.target.duration = duration;
+ settings.samplerate.target.samplerateSet = false;
}
if (this->device->getUniqueModelID() != MODEL_DSO6022BE) {
// Calculate the maximum samplerate that would still provide the requested
// duration
double maxSamplerate = (double)this->specification.samplerate.single
- .recordLengths[this->settings.recordLengthId] /
+ .recordLengths[settings.recordLengthId] /
duration;
// When possible, enable fast rate if the record time can't be set that low
// to improve resolution
bool fastRate =
- (this->settings.usedChannels <= 1) &&
+ (settings.usedChannels <= 1) &&
(maxSamplerate >=
this->specification.samplerate.multi.base /
- this->specification.bufferDividers[this->settings.recordLengthId]);
+ this->specification.bufferDividers[settings.recordLengthId]);
// What is the nearest, at most as high samplerate the scope can provide?
unsigned int downsampler = 0;
@@ -1252,8 +1256,8 @@ double HantekDsoControl::setRecordTime(double duration) {
if (this->updateSamplerate(downsampler, fastRate) == UINT_MAX)
return 0.0;
else {
- return (double)this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId] /
+ return (double)settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId] /
bestSamplerate;
}
} else {
@@ -1278,11 +1282,11 @@ double HantekDsoControl::setRecordTime(double duration) {
static_cast(this->control[CONTROLINDEX_SETTIMEDIV])
->setDiv(this->specification.sampleDiv[sampleId]);
this->controlPending[CONTROLINDEX_SETTIMEDIV] = true;
- this->settings.samplerate.current =
+ settings.samplerate.current =
this->specification.sampleSteps[sampleId];
- emit samplerateChanged(this->settings.samplerate.current);
- return this->settings.samplerate.current;
+ emit samplerateChanged(settings.samplerate.current);
+ return settings.samplerate.current;
}
}
@@ -1298,19 +1302,19 @@ int HantekDsoControl::setChannelUsed(unsigned int channel, bool used) {
return Dso::ERROR_PARAMETER;
// Update settings
- this->settings.voltage[channel].used = used;
+ settings.voltage[channel].used = used;
unsigned int channelCount = 0;
for (int channelCounter = 0; channelCounter < HANTEK_CHANNELS;
++channelCounter) {
- if (this->settings.voltage[channelCounter].used)
+ if (settings.voltage[channelCounter].used)
++channelCount;
}
// Calculate the UsedChannels field for the command
unsigned char usedChannels = USED_CH1;
- if (this->settings.voltage[1].used) {
- if (this->settings.voltage[0].used) {
+ if (settings.voltage[1].used) {
+ if (settings.voltage[0].used) {
usedChannels = USED_CH1CH2;
} else {
// DSO-2250 uses a different value for channel 2
@@ -1352,8 +1356,8 @@ int HantekDsoControl::setChannelUsed(unsigned int channel, bool used) {
// Check if fast rate mode availability changed
bool fastRateChanged =
- (this->settings.usedChannels <= 1) != (channelCount <= 1);
- this->settings.usedChannels = channelCount;
+ (settings.usedChannels <= 1) != (channelCount <= 1);
+ settings.usedChannels = channelCount;
if (fastRateChanged)
this->updateSamplerateLimits();
@@ -1430,9 +1434,9 @@ double HantekDsoControl::setGain(unsigned int channel, double gain) {
this->controlPending[CONTROLINDEX_SETRELAYS] = true;
}
- this->settings.voltage[channel].gain = gainId;
+ settings.voltage[channel].gain = gainId;
- this->setOffset(channel, this->settings.voltage[channel].offset);
+ this->setOffset(channel, settings.voltage[channel].offset);
return this->specification.gainSteps[gainId];
}
@@ -1453,23 +1457,23 @@ double HantekDsoControl::setOffset(unsigned int channel, double offset) {
unsigned short int minimum =
((unsigned short int)*((unsigned char *)&(
this->specification
- .offsetLimit[channel][this->settings.voltage[channel].gain]
+ .offsetLimit[channel][settings.voltage[channel].gain]
[OFFSET_START]))
<< 8) +
*((unsigned char *)&(
this->specification
- .offsetLimit[channel][this->settings.voltage[channel].gain]
+ .offsetLimit[channel][settings.voltage[channel].gain]
[OFFSET_START]) +
1);
unsigned short int maximum =
((unsigned short int)*((unsigned char *)&(
this->specification
- .offsetLimit[channel][this->settings.voltage[channel].gain]
+ .offsetLimit[channel][settings.voltage[channel].gain]
[OFFSET_END]))
<< 8) +
*((unsigned char *)&(
this->specification
- .offsetLimit[channel][this->settings.voltage[channel].gain]
+ .offsetLimit[channel][settings.voltage[channel].gain]
[OFFSET_END]) +
1);
unsigned short int offsetValue = offset * (maximum - minimum) + minimum + 0.5;
@@ -1484,10 +1488,10 @@ double HantekDsoControl::setOffset(unsigned int channel, double offset) {
this->controlPending[CONTROLINDEX_SETOFFSET] = true;
}
- this->settings.voltage[channel].offset = offset;
- this->settings.voltage[channel].offsetReal = offsetReal;
+ settings.voltage[channel].offset = offset;
+ settings.voltage[channel].offsetReal = offsetReal;
- this->setTriggerLevel(channel, this->settings.trigger.level[channel]);
+ this->setTriggerLevel(channel, settings.trigger.level[channel]);
return offsetReal;
}
@@ -1501,7 +1505,7 @@ int HantekDsoControl::setTriggerMode(Dso::TriggerMode mode) {
if (mode < Dso::TRIGGERMODE_AUTO || mode >= Dso::TRIGGERMODE_COUNT)
return Dso::ERROR_PARAMETER;
- this->settings.trigger.mode = mode;
+ settings.trigger.mode = mode;
return Dso::ERROR_NONE;
}
@@ -1551,8 +1555,8 @@ int HantekDsoControl::setTriggerSource(bool special, unsigned int id) {
->setTrigger(special);
this->controlPending[CONTROLINDEX_SETRELAYS] = true;
- this->settings.trigger.special = special;
- this->settings.trigger.source = id;
+ settings.trigger.special = special;
+ settings.trigger.source = id;
// Apply trigger level of the new source
if (special) {
@@ -1561,7 +1565,7 @@ int HantekDsoControl::setTriggerSource(bool special, unsigned int id) {
->setTrigger(0x7f);
this->controlPending[CONTROLINDEX_SETOFFSET] = true;
} else
- this->setTriggerLevel(id, this->settings.trigger.level[id]);
+ this->setTriggerLevel(id, settings.trigger.level[id]);
return Dso::ERROR_NONE;
}
@@ -1589,23 +1593,23 @@ double HantekDsoControl::setTriggerLevel(unsigned int channel, double level) {
minimum =
((unsigned short int)*((unsigned char *)&(
this->specification
- .offsetLimit[channel][this->settings.voltage[channel].gain]
+ .offsetLimit[channel][settings.voltage[channel].gain]
[OFFSET_START]))
<< 8) +
*((unsigned char *)&(
this->specification
- .offsetLimit[channel][this->settings.voltage[channel].gain]
+ .offsetLimit[channel][settings.voltage[channel].gain]
[OFFSET_START]) +
1);
maximum =
((unsigned short int)*((unsigned char *)&(
this->specification
- .offsetLimit[channel][this->settings.voltage[channel].gain]
+ .offsetLimit[channel][settings.voltage[channel].gain]
[OFFSET_END]))
<< 8) +
*((unsigned char *)&(
this->specification
- .offsetLimit[channel][this->settings.voltage[channel].gain]
+ .offsetLimit[channel][settings.voltage[channel].gain]
[OFFSET_END]) +
1);
break;
@@ -1620,18 +1624,18 @@ double HantekDsoControl::setTriggerLevel(unsigned int channel, double level) {
// Never get out of the limits
unsigned short int levelValue = qBound(
(long int)minimum,
- (long int)((this->settings.voltage[channel].offsetReal +
+ (long int)((settings.voltage[channel].offsetReal +
level /
this->specification
- .gainSteps[this->settings.voltage[channel].gain]) *
+ .gainSteps[settings.voltage[channel].gain]) *
(maximum - minimum) +
0.5) +
minimum,
(long int)maximum);
// Check if the set channel is the trigger source
- if (!this->settings.trigger.special &&
- channel == this->settings.trigger.source &&
+ if (!settings.trigger.special &&
+ channel == settings.trigger.source &&
this->device->getUniqueModelID() != MODEL_DSO6022BE) {
// SetOffset control command for trigger level
static_cast(this->control[CONTROLINDEX_SETOFFSET])
@@ -1641,10 +1645,10 @@ double HantekDsoControl::setTriggerLevel(unsigned int channel, double level) {
/// \todo Get alternating trigger in here
- this->settings.trigger.level[channel] = level;
+ settings.trigger.level[channel] = level;
return (double)((levelValue - minimum) / (maximum - minimum) -
- this->settings.voltage[channel].offsetReal) *
- this->specification.gainSteps[this->settings.voltage[channel].gain];
+ settings.voltage[channel].offsetReal) *
+ this->specification.gainSteps[settings.voltage[channel].gain];
}
/// \brief Set the trigger slope.
@@ -1686,7 +1690,7 @@ int HantekDsoControl::setTriggerSlope(Dso::Slope slope) {
return Dso::ERROR_UNSUPPORTED;
}
- this->settings.trigger.slope = slope;
+ settings.trigger.slope = slope;
return Dso::ERROR_NONE;
}
@@ -1703,13 +1707,13 @@ double HantekDsoControl::setPretriggerPosition(double position) {
return -2;
// All trigger positions are measured in samples
- unsigned int positionSamples = position * this->settings.samplerate.current;
+ unsigned int positionSamples = position * settings.samplerate.current;
unsigned int recordLength =
- this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId];
+ settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId];
bool rollMode = recordLength == UINT_MAX;
// Fast rate mode uses both channels
- if (this->settings.samplerate.limits == &this->specification.samplerate.multi)
+ if (settings.samplerate.limits == &this->specification.samplerate.multi)
positionSamples /= HANTEK_CHANNELS;
switch (this->specification.command.bulk.setPretrigger) {
@@ -1758,8 +1762,8 @@ double HantekDsoControl::setPretriggerPosition(double position) {
return Dso::ERROR_UNSUPPORTED;
}
- this->settings.trigger.position = position;
- return (double)positionSamples / this->settings.samplerate.current;
+ settings.trigger.position = position;
+ return (double)positionSamples / settings.samplerate.current;
}
#ifdef DEBUG
@@ -1888,8 +1892,8 @@ void HantekDsoControl::run() {
}
// State machine for the device communication
- if (this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId] == UINT_MAX) {
+ if (settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId] == UINT_MAX) {
// Roll mode
this->captureState = CAPTURE_WAITING;
bool toNextState = true;
@@ -1964,7 +1968,7 @@ void HantekDsoControl::run() {
#endif
// Check if we're in single trigger mode
- if (this->settings.trigger.mode == Dso::TRIGGERMODE_SINGLE &&
+ if (settings.trigger.mode == Dso::TRIGGERMODE_SINGLE &&
this->_samplingStarted)
this->stopSampling();
@@ -2016,7 +2020,7 @@ void HantekDsoControl::run() {
#endif
// Check if we're in single trigger mode
- if (this->settings.trigger.mode == Dso::TRIGGERMODE_SINGLE &&
+ if (settings.trigger.mode == Dso::TRIGGERMODE_SINGLE &&
this->_samplingStarted)
this->stopSampling();
@@ -2032,12 +2036,12 @@ void HantekDsoControl::run() {
this->previousSampleCount = this->getSampleCount();
if (this->_samplingStarted &&
- this->lastTriggerMode == this->settings.trigger.mode) {
+ this->lastTriggerMode == settings.trigger.mode) {
++this->cycleCounter;
if (this->cycleCounter == this->startCycle &&
- this->settings.samplerate.limits
- ->recordLengths[this->settings.recordLengthId] !=
+ settings.samplerate.limits
+ ->recordLengths[settings.recordLengthId] !=
UINT_MAX) {
// Buffer refilled completely since start of sampling, enable the
// trigger now
@@ -2054,7 +2058,7 @@ void HantekDsoControl::run() {
timestampDebug("Enabling trigger");
#endif
} else if (this->cycleCounter >= 8 + this->startCycle &&
- this->settings.trigger.mode == Dso::TRIGGERMODE_AUTO) {
+ settings.trigger.mode == Dso::TRIGGERMODE_AUTO) {
// Force triggering
errorCode =
this->device->bulkCommand(this->command[BULK_FORCETRIGGER]);
@@ -2090,8 +2094,8 @@ void HantekDsoControl::run() {
this->_samplingStarted = true;
this->cycleCounter = 0;
- this->startCycle = this->settings.trigger.position * 1000 / cycleTime + 1;
- this->lastTriggerMode = this->settings.trigger.mode;
+ this->startCycle = settings.trigger.position * 1000 / cycleTime + 1;
+ this->lastTriggerMode = settings.trigger.mode;
break;
case CAPTURE_SAMPLING:
diff --git a/openhantek/src/hantek/hantekdsocontrol.h b/openhantek/src/hantek/hantekdsocontrol.h
index ee111aa..73b7523 100644
--- a/openhantek/src/hantek/hantekdsocontrol.h
+++ b/openhantek/src/hantek/hantekdsocontrol.h
@@ -7,6 +7,7 @@
#include "bulkStructs.h"
#include "stateStructs.h"
#include "utils/printutils.h"
+#include "dsosamples.h"
#include
@@ -17,14 +18,12 @@
class USBDevice;
-//////////////////////////////////////////////////////////////////////////////
-/// \class Control hantek/control.h
/// \brief The DsoControl abstraction layer for %Hantek USB DSOs.
class HantekDsoControl : public QObject {
Q_OBJECT
public:
- /**
+ /**
* Creates a dsoControl object. The actual event loop / timer is not started.
* You can optionally create a thread and move the created object to the thread.
* You need to call updateInterval() to start the timer.
@@ -44,15 +43,13 @@ public:
const QStringList *getSpecialTriggerSources();
const USBDevice* getDevice() const;
+ const DSOsamples& getLastSamples();
signals:
void samplingStarted(); ///< The oscilloscope started sampling/waiting for trigger
void samplingStopped(); ///< The oscilloscope stopped sampling/waiting for trigger
- void statusMessage(const QString &message,
- int timeout); ///< Status message about the oscilloscope
- void samplesAvailable(const std::vector> *data,
- double samplerate, bool append,
- QMutex *mutex); ///< New sample data is available
+ void statusMessage(const QString &message, int timeout); ///< Status message about the oscilloscope
+ void samplesAvailable(); ///< New sample data is available
void availableRecordLengthsChanged(const QList &recordLengths); ///< The available record lengths, empty list for continuous
void samplerateLimitsChanged(double minimum, double maximum); ///< The minimum or maximum samplerate has changed
@@ -93,10 +90,8 @@ protected:
Hantek::ControlSettings settings; ///< The current settings of the device
// Results
- std::vector> samples; ///< Sample data vectors sent to the data analyzer
- unsigned int previousSampleCount; ///< The expected total number of samples at
- ///the last check before sampling started
- QMutex samplesMutex; ///< Mutex for the sample data
+ DSOsamples result;
+ unsigned int previousSampleCount; ///< The expected total number of samples at the last check before sampling started
// State of the communication thread
int captureState = Hantek::CAPTURE_WAITING;
diff --git a/openhantek/src/main.cpp b/openhantek/src/main.cpp
index 517b648..da9edb4 100644
--- a/openhantek/src/main.cpp
+++ b/openhantek/src/main.cpp
@@ -119,25 +119,38 @@ int main(int argc, char *argv[]) {
//////// Create DSO control object and move it to a separate thread ////////
QThread dsoControlThread;
- std::shared_ptr dsoControl(new HantekDsoControl(device.get()));
- dsoControl->moveToThread(&dsoControlThread);
- QObject::connect(&dsoControlThread,&QThread::started,dsoControl.get(),&HantekDsoControl::run);
- QObject::connect(dsoControl.get(), &HantekDsoControl::communicationError, QCoreApplication::instance(), &QCoreApplication::quit);
- QObject::connect(device.get(), &USBDevice::deviceDisconnected, QCoreApplication::instance(), &QCoreApplication::quit);
+ dsoControlThread.setObjectName("dsoControlThread");
+ HantekDsoControl dsoControl(device.get());
+ dsoControl.moveToThread(&dsoControlThread);
+ QObject::connect(&dsoControlThread,&QThread::started, &dsoControl,&HantekDsoControl::run);
+ QObject::connect(&dsoControl, &HantekDsoControl::communicationError,
+ QCoreApplication::instance(), &QCoreApplication::quit);
+ QObject::connect(device.get(), &USBDevice::deviceDisconnected,
+ QCoreApplication::instance(), &QCoreApplication::quit);
//////// Create data analyser object ////////
- std::shared_ptr dataAnalyser(new DataAnalyzer());
+ QThread dataAnalyzerThread;
+ dataAnalyzerThread.setObjectName("dataAnalyzerThread");
+ DataAnalyzer dataAnalyser;
+ dataAnalyser.setSourceData(&dsoControl.getLastSamples());
+ dataAnalyser.moveToThread(&dataAnalyzerThread);
+ QObject::connect(&dsoControl, &HantekDsoControl::samplesAvailable,
+ &dataAnalyser, &DataAnalyzer::samplesAvailable);
//////// Create main window ////////
- OpenHantekMainWindow *openHantekMainWindow = new OpenHantekMainWindow(dsoControl, dataAnalyser);
+ OpenHantekMainWindow *openHantekMainWindow = new OpenHantekMainWindow(&dsoControl, &dataAnalyser);
openHantekMainWindow->show();
//////// Start DSO thread and go into GUI main loop
+ dataAnalyzerThread.start();
dsoControlThread.start();
int res = openHantekApplication.exec();
//////// Clean up ////////
dsoControlThread.quit();
dsoControlThread.wait(10000);
+
+ dataAnalyzerThread.quit();
+ dataAnalyzerThread.wait(10000);
return res;
}
diff --git a/openhantek/src/mainwindow.cpp b/openhantek/src/mainwindow.cpp
index a84ec1d..ad8e2c3 100644
--- a/openhantek/src/mainwindow.cpp
+++ b/openhantek/src/mainwindow.cpp
@@ -28,7 +28,7 @@
/// \brief Initializes the gui elements of the main window.
/// \param parent The parent widget.
/// \param flags Flags for the window manager.
-OpenHantekMainWindow::OpenHantekMainWindow(std::shared_ptr dsoControl, std::shared_ptr dataAnalyzer)
+OpenHantekMainWindow::OpenHantekMainWindow(HantekDsoControl *dsoControl, DataAnalyzer *dataAnalyzer)
:dsoControl(dsoControl),dataAnalyzer(dataAnalyzer) {
// Window title
@@ -45,7 +45,11 @@ OpenHantekMainWindow::OpenHantekMainWindow(std::shared_ptr dso
createDockWindows();
// Central oszilloscope widget
- dsoWidget = new DsoWidget(settings, dataAnalyzer.get());
+ dataAnalyzer->applySettings(&settings->scope);
+ dsoWidget = new DsoWidget(settings);
+ connect(dataAnalyzer, &DataAnalyzer::analyzed, [this]() {
+ dsoWidget->showNewData(this->dataAnalyzer->getNextResult());
+ });
setCentralWidget(dsoWidget);
// Subroutines for window elements
@@ -260,91 +264,85 @@ void OpenHantekMainWindow::createDockWindows() {
/// \brief Connect general signals and device management signals.
void OpenHantekMainWindow::connectSignals() {
// Connect general signals
- connect(this, SIGNAL(settingsChanged()), this, SLOT(applySettings()));
+ connect(this, &OpenHantekMainWindow::settingsChanged, this, &OpenHantekMainWindow::applySettings);
// connect(dsoWidget, SIGNAL(stopped()), this, SLOT(stopped()));
- connect(dsoControl.get(), SIGNAL(statusMessage(QString, int)),
- statusBar(), SLOT(showMessage(QString, int)));
- connect(dsoControl.get(),
- SIGNAL(samplesAvailable(const std::vector> *,
- double, bool, QMutex *)),
- dataAnalyzer.get(),
- SLOT(analyze(const std::vector> *, double, bool,
- QMutex *)));
+ connect(dsoControl, &HantekDsoControl::statusMessage,
+ statusBar(), &QStatusBar::showMessage);
// Connect signals to DSO controller and widget
- connect(horizontalDock, SIGNAL(samplerateChanged(double)), this,
- SLOT(samplerateSelected()));
- connect(horizontalDock, SIGNAL(timebaseChanged(double)), this,
- SLOT(timebaseSelected()));
- connect(horizontalDock, SIGNAL(frequencybaseChanged(double)),
- dsoWidget, SLOT(updateFrequencybase(double)));
- connect(horizontalDock, SIGNAL(recordLengthChanged(unsigned long)),
- this, SLOT(recordLengthSelected(unsigned long)));
+ connect(horizontalDock, &HorizontalDock::samplerateChanged, this,
+ &OpenHantekMainWindow::samplerateSelected);
+ connect(horizontalDock, &HorizontalDock::timebaseChanged, this,
+ &OpenHantekMainWindow::timebaseSelected);
+ connect(horizontalDock, &HorizontalDock::frequencybaseChanged,
+ dsoWidget, &DsoWidget::updateFrequencybase);
+ connect(horizontalDock, &HorizontalDock::recordLengthChanged,
+ this, &OpenHantekMainWindow::recordLengthSelected);
// connect(horizontalDock, SIGNAL(formatChanged(HorizontalFormat)),
// dsoWidget, SLOT(horizontalFormatChanged(HorizontalFormat)));
- connect(triggerDock, SIGNAL(modeChanged(Dso::TriggerMode)),
- dsoControl.get(), SLOT(setTriggerMode(Dso::TriggerMode)));
- connect(triggerDock, SIGNAL(modeChanged(Dso::TriggerMode)),
- dsoWidget, SLOT(updateTriggerMode()));
- connect(triggerDock, SIGNAL(sourceChanged(bool, unsigned int)),
- dsoControl.get(), SLOT(setTriggerSource(bool, unsigned int)));
- connect(triggerDock, SIGNAL(sourceChanged(bool, unsigned int)),
- dsoWidget, SLOT(updateTriggerSource()));
- connect(triggerDock, SIGNAL(slopeChanged(Dso::Slope)), dsoControl.get(),
- SLOT(setTriggerSlope(Dso::Slope)));
- connect(triggerDock, SIGNAL(slopeChanged(Dso::Slope)), dsoWidget,
- SLOT(updateTriggerSlope()));
- connect(dsoWidget, SIGNAL(triggerPositionChanged(double)),
- dsoControl.get(), SLOT(setPretriggerPosition(double)));
- connect(dsoWidget, SIGNAL(triggerLevelChanged(unsigned int, double)),
- dsoControl.get(), SLOT(setTriggerLevel(unsigned int, double)));
-
- connect(voltageDock, SIGNAL(usedChanged(unsigned int, bool)), this,
- SLOT(updateUsed(unsigned int)));
- connect(voltageDock, SIGNAL(usedChanged(unsigned int, bool)),
- dsoWidget, SLOT(updateVoltageUsed(unsigned int, bool)));
+ connect(triggerDock, &TriggerDock::modeChanged,
+ dsoControl, &HantekDsoControl::setTriggerMode);
+ connect(triggerDock, &TriggerDock::modeChanged,
+ dsoWidget, &DsoWidget::updateTriggerMode);
+ connect(triggerDock, &TriggerDock::sourceChanged,
+ dsoControl, &HantekDsoControl::setTriggerSource);
+ connect(triggerDock, &TriggerDock::sourceChanged,
+ dsoWidget, &DsoWidget::updateTriggerSource);
+ connect(triggerDock, &TriggerDock::slopeChanged, dsoControl,
+ &HantekDsoControl::setTriggerSlope);
+ connect(triggerDock, &TriggerDock::slopeChanged, dsoWidget,
+ &DsoWidget::updateTriggerSlope);
+ connect(dsoWidget, &DsoWidget::triggerPositionChanged,
+ dsoControl, &HantekDsoControl::setPretriggerPosition);
+ connect(dsoWidget, &DsoWidget::triggerLevelChanged,
+ dsoControl, &HantekDsoControl::setTriggerLevel);
+
+ connect(voltageDock, &VoltageDock::usedChanged, this,
+ &OpenHantekMainWindow::updateUsed);
+ connect(voltageDock, &VoltageDock::usedChanged,
+ dsoWidget, &DsoWidget::updateVoltageUsed);
connect(voltageDock,
- SIGNAL(couplingChanged(unsigned int, Dso::Coupling)),
- dsoControl.get(), SLOT(setCoupling(unsigned int, Dso::Coupling)));
+ &VoltageDock::couplingChanged,
+ dsoControl, &HantekDsoControl::setCoupling);
connect(voltageDock,
- SIGNAL(couplingChanged(unsigned int, Dso::Coupling)), dsoWidget,
- SLOT(updateVoltageCoupling(unsigned int)));
- connect(voltageDock, SIGNAL(modeChanged(Dso::MathMode)),
- dsoWidget, SLOT(updateMathMode()));
- connect(voltageDock, SIGNAL(gainChanged(unsigned int, double)), this,
- SLOT(updateVoltageGain(unsigned int)));
- connect(voltageDock, SIGNAL(gainChanged(unsigned int, double)),
- dsoWidget, SLOT(updateVoltageGain(unsigned int)));
- connect(dsoWidget, SIGNAL(offsetChanged(unsigned int, double)), this,
- SLOT(updateOffset(unsigned int)));
-
- connect(spectrumDock, SIGNAL(usedChanged(unsigned int, bool)), this,
- SLOT(updateUsed(unsigned int)));
- connect(spectrumDock, SIGNAL(usedChanged(unsigned int, bool)),
- dsoWidget, SLOT(updateSpectrumUsed(unsigned int, bool)));
- connect(spectrumDock, SIGNAL(magnitudeChanged(unsigned int, double)),
- dsoWidget, SLOT(updateSpectrumMagnitude(unsigned int)));
+ &VoltageDock::couplingChanged, dsoWidget,
+ &DsoWidget::updateVoltageCoupling);
+ connect(voltageDock, &VoltageDock::modeChanged,
+ dsoWidget, &DsoWidget::updateMathMode);
+ connect(voltageDock, &VoltageDock::gainChanged, this,
+ &OpenHantekMainWindow::updateVoltageGain);
+ connect(voltageDock, &VoltageDock::gainChanged,
+ dsoWidget, &DsoWidget::updateVoltageGain);
+ connect(dsoWidget, &DsoWidget::offsetChanged, this,
+ &OpenHantekMainWindow::updateOffset);
+
+ connect(spectrumDock, &SpectrumDock::usedChanged, this,
+ &OpenHantekMainWindow::updateUsed);
+ connect(spectrumDock, &SpectrumDock::usedChanged,
+ dsoWidget, &DsoWidget::updateSpectrumUsed);
+ connect(spectrumDock, &SpectrumDock::magnitudeChanged,
+ dsoWidget, &DsoWidget::updateSpectrumMagnitude);
// Started/stopped signals from oscilloscope
- connect(dsoControl.get(), SIGNAL(samplingStarted()), this, SLOT(started()));
- connect(dsoControl.get(), SIGNAL(samplingStopped()), this, SLOT(stopped()));
+ connect(dsoControl, &HantekDsoControl::samplingStarted, this, &OpenHantekMainWindow::started);
+ connect(dsoControl, &HantekDsoControl::samplingStopped, this, &OpenHantekMainWindow::stopped);
// connect(dsoControl, SIGNAL(recordLengthChanged(unsigned long)), this,
// SLOT(recordLengthChanged()));
- connect(dsoControl.get(), SIGNAL(recordTimeChanged(double)), this,
- SLOT(recordTimeChanged(double)));
- connect(dsoControl.get(), SIGNAL(samplerateChanged(double)), this,
- SLOT(samplerateChanged(double)));
+ connect(dsoControl, &HantekDsoControl::recordTimeChanged, this,
+ &OpenHantekMainWindow::recordTimeChanged);
+ connect(dsoControl, &HantekDsoControl::samplerateChanged, this,
+ &OpenHantekMainWindow::samplerateChanged);
- connect(dsoControl.get(),
- SIGNAL(availableRecordLengthsChanged(QList)),
+ connect(dsoControl,
+ &HantekDsoControl::availableRecordLengthsChanged,
horizontalDock,
- SLOT(availableRecordLengthsChanged(QList)));
- connect(dsoControl.get(), SIGNAL(samplerateLimitsChanged(double, double)),
- horizontalDock, SLOT(samplerateLimitsChanged(double, double)));
- connect(dsoControl.get(), SIGNAL(samplerateSet(int, QList)),
- horizontalDock, SLOT(samplerateSet(int, QList)));
+ &HorizontalDock::availableRecordLengthsChanged);
+ connect(dsoControl, &HantekDsoControl::samplerateLimitsChanged,
+ horizontalDock, &HorizontalDock::samplerateLimitsChanged);
+ connect(dsoControl, &HantekDsoControl::samplerateSet,
+ horizontalDock, &HorizontalDock::samplerateSet);
}
/// \brief Initialize the device with the current settings.
@@ -444,10 +442,10 @@ void OpenHantekMainWindow::started() {
startStopAction->setIcon(QIcon(":actions/stop.png"));
startStopAction->setStatusTip(tr("Stop the oscilloscope"));
- disconnect(startStopAction, SIGNAL(triggered()), dsoControl.get(),
- SLOT(startSampling()));
- connect(startStopAction, SIGNAL(triggered()), dsoControl.get(),
- SLOT(stopSampling()));
+ disconnect(startStopAction, &QAction::triggered, dsoControl,
+ &HantekDsoControl::startSampling);
+ connect(startStopAction, &QAction::triggered, dsoControl,
+ &HantekDsoControl::stopSampling);
}
/// \brief The oscilloscope stopped sampling.
@@ -456,10 +454,10 @@ void OpenHantekMainWindow::stopped() {
startStopAction->setIcon(QIcon(":actions/start.png"));
startStopAction->setStatusTip(tr("Start the oscilloscope"));
- disconnect(startStopAction, SIGNAL(triggered()), dsoControl.get(),
- SLOT(stopSampling()));
- connect(startStopAction, SIGNAL(triggered()), dsoControl.get(),
- SLOT(startSampling()));
+ disconnect(startStopAction, &QAction::triggered, dsoControl,
+ &HantekDsoControl::stopSampling);
+ connect(startStopAction, &QAction::triggered, dsoControl,
+ &HantekDsoControl::startSampling);
}
/// \brief Configure the oscilloscope.
diff --git a/openhantek/src/mainwindow.h b/openhantek/src/mainwindow.h
index cddb1da..a4e8736 100644
--- a/openhantek/src/mainwindow.h
+++ b/openhantek/src/mainwindow.h
@@ -27,7 +27,7 @@ class OpenHantekMainWindow : public QMainWindow {
Q_OBJECT
public:
- OpenHantekMainWindow(std::shared_ptr dsoControl, std::shared_ptr dataAnalyser);
+ OpenHantekMainWindow(HantekDsoControl* dsoControl, DataAnalyzer* dataAnalyser);
protected:
void closeEvent(QCloseEvent *event);
@@ -87,8 +87,8 @@ private:
#endif
// Data handling classes
- std::shared_ptr dsoControl;
- std::shared_ptr dataAnalyzer;
+ HantekDsoControl* dsoControl;
+ DataAnalyzer* dataAnalyzer;
// Other variables
QString currentFile;
diff --git a/openhantek/src/utils/printutils.h b/openhantek/src/utils/printutils.h
index 0dc4abb..061d11b 100644
--- a/openhantek/src/utils/printutils.h
+++ b/openhantek/src/utils/printutils.h
@@ -54,6 +54,10 @@ unsigned int hexParse(const QString dump, unsigned char *data, unsigned int leng
/// \brief Print debug information with timestamp.
/// \param text Text that will be output via qDebug.
+#ifdef DEBUG
inline void timestampDebug(QString text) {
qDebug("%s: %s", QTime::currentTime().toString("hh:mm:ss.zzz").toLatin1().constData(), text.toLatin1().constData());
}
+#else
+#define timestampDebug(ARG)
+#endif