From d52c9b8280c05d6b0add84dd2005024eb25db356 Mon Sep 17 00:00:00 2001 From: oliverhaag Date: Sun, 25 Nov 2012 19:04:55 +0000 Subject: [PATCH] Record length and samplerate can be set --- openhantek/ChangeLog | 9 +++++++++ openhantek/roadmap.dox | 11 +++++++++-- openhantek/src/dataanalyzer.cpp | 54 +++++++++++++++++++++++++++--------------------------- openhantek/src/dataanalyzer.h | 6 +++--- openhantek/src/dockwindows.cpp | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------- openhantek/src/dockwindows.h | 20 ++++++++++++++++++-- openhantek/src/dsocontrol.h | 16 +++++++++++++--- openhantek/src/dsowidget.cpp | 36 ++++++++++++++++++++---------------- openhantek/src/dsowidget.h | 8 ++++---- openhantek/src/glgenerator.cpp | 2 +- openhantek/src/hantek/control.cpp | 677 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openhantek/src/hantek/control.h | 33 ++++++++++++++++++++++++++------- openhantek/src/hantek/device.cpp | 27 ++++++++++++++------------- openhantek/src/hantek/device.h | 22 ++++++++++------------ openhantek/src/hantek/types.cpp | 99 +++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------- openhantek/src/hantek/types.h | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------------------- openhantek/src/openhantek.cpp | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------ openhantek/src/openhantek.h | 15 ++++++++++----- openhantek/src/settings.cpp | 13 ++++++++++++- openhantek/src/settings.h | 7 ++++--- openhantek/translations/openhantek_de.ts | 334 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 21 files changed, 1150 insertions(+), 724 deletions(-) diff --git a/openhantek/ChangeLog b/openhantek/ChangeLog index fdc001a..83b741b 100644 --- a/openhantek/ChangeLog +++ b/openhantek/ChangeLog @@ -183,3 +183,12 @@ 2012-11-21 Oliver Haag * Bugfix: DSO-2250 detection failed + +2012-11-25 Oliver Haag +* Added samplerate spinbox to horizontal dock +* Moved record length selection from menu into horizontal dock +* Large rework to allow setting either samplerate or timebase +* Calculate the samplerate depending on the set parameter and the record length +* Added signals to DsoControl to update horizontal dock automatically +* Reworks in signal connections to make this work properly +* Bugfix: DSO-2250 used channels wasn't set diff --git a/openhantek/roadmap.dox b/openhantek/roadmap.dox index 1ac93ed..cfc180a 100644 --- a/openhantek/roadmap.dox +++ b/openhantek/roadmap.dox @@ -17,11 +17,18 @@ Released February 9, 2011 \section sec_0_3 OpenHantek 0.3.0 -Planned Q2/Q3, 2011 +Planned Q1, 2013
  • %Hantek DSO-2250 support
  • -
  • USB hotplugging
  • +
  • Proper support for different record lengths
  • +
  • Arbitrary samplerate support
  • +
+ +\section sec_0_4 OpenHantek 0.4.0 +
  • Improved zoom functionality
  • +
  • Self alignment routines
  • +
  • USB hotplugging
**/ diff --git a/openhantek/src/dataanalyzer.cpp b/openhantek/src/dataanalyzer.cpp index c510f75..8ded032 100644 --- a/openhantek/src/dataanalyzer.cpp +++ b/openhantek/src/dataanalyzer.cpp @@ -139,7 +139,7 @@ void DataAnalyzer::run() { // Set sampling interval channelData->samples.voltage.interval = 1.0 / this->waitingDataSamplerate; - unsigned int size; + unsigned long int size; if(channel < this->settings->scope.physicalChannels) { size = this->waitingDataSize[channel]; if(size > maxSamples) @@ -159,7 +159,7 @@ void DataAnalyzer::run() { if(channel < this->settings->scope.physicalChannels) { // Copy the buffer of the oscilloscope into the sample buffer if(channel < (unsigned int) this->waitingData.count()) - for(unsigned int position = 0; position < this->waitingDataSize[channel]; ++position) + for(unsigned long int position = 0; position < this->waitingDataSize[channel]; ++position) channelData->samples.voltage.sample[position] = this->waitingData[channel][position]; } // Math channel @@ -176,7 +176,7 @@ void DataAnalyzer::run() { } // Calculate values and write them into the sample buffer - for(unsigned int realPosition = 0; realPosition < this->analyzedData[this->settings->scope.physicalChannels]->samples.voltage.count; ++realPosition) { + for(unsigned long int realPosition = 0; realPosition < this->analyzedData[this->settings->scope.physicalChannels]->samples.voltage.count; ++realPosition) { switch(this->settings->scope.voltage[this->settings->scope.physicalChannels].misc) { case Dso::MATHMODE_1ADD2: this->analyzedData[this->settings->scope.physicalChannels]->samples.voltage.sample[realPosition] = this->analyzedData[0]->samples.voltage.sample[realPosition] + this->analyzedData[1]->samples.voltage.sample[realPosition]; @@ -223,24 +223,24 @@ void DataAnalyzer::run() { this->window = (double *) fftw_malloc(sizeof(double) * this->lastRecordLength); } - unsigned int windowEnd = this->lastRecordLength - 1; + unsigned long int windowEnd = this->lastRecordLength - 1; this->lastWindow = this->settings->scope.spectrumWindow; switch(this->settings->scope.spectrumWindow) { case Dso::WINDOW_HAMMING: - for(unsigned int windowPosition = 0; windowPosition < this->lastRecordLength; ++windowPosition) + for(unsigned long 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) + for(unsigned long 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) + for(unsigned long 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) { + for(unsigned long int windowPosition = 0; windowPosition < this->lastRecordLength; ++windowPosition) { double sincParameter = (2.0 * windowPosition / windowEnd - 1.0) * M_PI; if(sincParameter == 0) *(this->window + windowPosition) = 1; @@ -249,55 +249,55 @@ void DataAnalyzer::run() { } break; case Dso::WINDOW_BARTLETT: - for(unsigned int windowPosition = 0; windowPosition < this->lastRecordLength; ++windowPosition) + for(unsigned long int windowPosition = 0; windowPosition < this->lastRecordLength; ++windowPosition) *(this->window + windowPosition) = 2.0 / windowEnd * (windowEnd / 2 - abs(windowPosition - windowEnd / 2)); break; case Dso::WINDOW_TRIANGULAR: - for(unsigned int windowPosition = 0; windowPosition < this->lastRecordLength; ++windowPosition) + for(unsigned long int windowPosition = 0; windowPosition < this->lastRecordLength; ++windowPosition) *(this->window + windowPosition) = 2.0 / this->lastRecordLength * (this->lastRecordLength / 2 - abs(windowPosition - windowEnd / 2)); break; case Dso::WINDOW_GAUSS: { double sigma = 0.4; - for(unsigned int windowPosition = 0; windowPosition < this->lastRecordLength; ++windowPosition) + for(unsigned long 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) + for(unsigned long int windowPosition = 0; windowPosition < this->lastRecordLength; ++windowPosition) *(this->window + windowPosition) = 0.62 - 0.48 * abs(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) + for(unsigned long 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) + //for(unsigned long int windowPosition = 0; windowPosition < this->lastRecordLength; ++windowPosition) //*(this->window + windowPosition) = ; //break; case Dso::WINDOW_NUTTALL: - for(unsigned int windowPosition = 0; windowPosition < this->lastRecordLength; ++windowPosition) + for(unsigned long 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) + for(unsigned long 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) + for(unsigned long 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) + for(unsigned long 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) + for(unsigned long int windowPosition = 0; windowPosition < this->lastRecordLength; ++windowPosition) *(this->window + windowPosition) = 1.0; } } @@ -306,7 +306,7 @@ void DataAnalyzer::run() { channelData->samples.spectrum.interval = 1.0 / channelData->samples.voltage.interval / channelData->samples.voltage.count; // Number of real/complex samples - unsigned int dftLength = channelData->samples.voltage.count / 2; + unsigned long int dftLength = channelData->samples.voltage.count / 2; // Reallocate memory for samples if the sample count has changed if(channelData->samples.spectrum.count != dftLength) { @@ -318,7 +318,7 @@ void DataAnalyzer::run() { // Create sample buffer and apply window double *windowedValues = new double[channelData->samples.voltage.count]; - for(unsigned int position = 0; position < channelData->samples.voltage.count; ++position) + for(unsigned long int position = 0; position < channelData->samples.voltage.count; ++position) windowedValues[position] = this->window[position] * channelData->samples.voltage.sample[position]; // Do discrete real to half-complex transformation @@ -332,7 +332,7 @@ void DataAnalyzer::run() { double *conjugateComplex = windowedValues; // Reuse the windowedValues buffer // Real values - unsigned int position; + unsigned long 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) @@ -353,7 +353,7 @@ void DataAnalyzer::run() { double minimalVoltage, maximalVoltage; minimalVoltage = maximalVoltage = channelData->samples.voltage.sample[0]; - for(unsigned int position = 1; position < channelData->samples.voltage.count; ++position) { + for(unsigned long int position = 1; position < channelData->samples.voltage.count; ++position) { if(channelData->samples.voltage.sample[position] < minimalVoltage) minimalVoltage = channelData->samples.voltage.sample[position]; else if(channelData->samples.voltage.sample[position] > maximalVoltage) @@ -365,9 +365,9 @@ void DataAnalyzer::run() { // Get the frequency from the correlation results double minimumCorrelation = correlation[0]; double peakCorrelation = 0; - unsigned int peakPosition = 0; + unsigned long int peakPosition = 0; - for(unsigned int position = 1; position < channelData->samples.voltage.count / 2; ++position) { + for(unsigned long int position = 1; position < channelData->samples.voltage.count / 2; ++position) { if(correlation[position] > peakCorrelation && correlation[position] > minimumCorrelation * 2) { peakCorrelation = correlation[position]; peakPosition = position; @@ -388,7 +388,7 @@ void DataAnalyzer::run() { // Convert values into dB (Relative to the reference level) double offset = 60 - this->settings->scope.spectrumReference - 20 * log10(dftLength); double offsetLimit = this->settings->scope.spectrumLimit - this->settings->scope.spectrumReference; - for(unsigned int position = 0; position < channelData->samples.spectrum.count; ++position) { + for(unsigned long int position = 0; position < channelData->samples.spectrum.count; ++position) { channelData->samples.spectrum.sample[position] = 20 * log10(fabs(channelData->samples.spectrum.sample[position])) + offset; // Check if this value has to be limited @@ -417,7 +417,7 @@ void DataAnalyzer::run() { /// \param size The sizes of the data arrays. /// \param samplerate The samplerate for all input data. /// \param mutex The mutex for all input data. -void DataAnalyzer::analyze(const QList *data, const QList *size, double samplerate, QMutex *mutex) { +void DataAnalyzer::analyze(const QList *data, const QList *size, double samplerate, QMutex *mutex) { // Previous analysis still running, drop the new data if(this->isRunning()) return; diff --git a/openhantek/src/dataanalyzer.h b/openhantek/src/dataanalyzer.h index 70eb83d..f7d2f0c 100644 --- a/openhantek/src/dataanalyzer.h +++ b/openhantek/src/dataanalyzer.h @@ -95,15 +95,15 @@ class DataAnalyzer : public QThread { double *window; ///< The array for the dft window factors QList waitingData; ///< Pointer to input data from device - QList waitingDataSize; ///< Number of input data samples + QList waitingDataSize; ///< Number of input data samples double waitingDataSamplerate; ///< The samplerate of the input data QMutex *waitingDataMutex; ///< A mutex for the input data public slots: - void analyze(const QList *data, const QList *size, double samplerate, QMutex *mutex); + void analyze(const QList *data, const QList *size, double samplerate, QMutex *mutex); signals: - void analyzed(unsigned int samples); ///< The data with that much samples has been analyzed + void analyzed(unsigned long samples); ///< The data with that much samples has been analyzed }; #endif diff --git a/openhantek/src/dockwindows.cpp b/openhantek/src/dockwindows.cpp index 5fc27ba..7aebe63 100644 --- a/openhantek/src/dockwindows.cpp +++ b/openhantek/src/dockwindows.cpp @@ -46,20 +46,29 @@ HorizontalDock::HorizontalDock(DsoSettings *settings, QWidget *parent, Qt::Windo this->settings = settings; // Initialize elements + this->samplerateLabel = new QLabel(tr("Samplerate")); + this->samplerateSiSpinBox = new SiSpinBox(Helper::UNIT_SAMPLES); + this->samplerateSiSpinBox->setMinimum(1); + this->samplerateSiSpinBox->setMaximum(1e8); + this->samplerateSiSpinBox->setUnitPostfix("/s"); + + QList timebaseSteps; + timebaseSteps << 1.0 << 2.0 << 4.0 << 10.0; + this->timebaseLabel = new QLabel(tr("Timebase")); this->timebaseSiSpinBox = new SiSpinBox(Helper::UNIT_SECONDS); + this->timebaseSiSpinBox->setSteps(timebaseSteps); this->timebaseSiSpinBox->setMinimum(1e-9); this->timebaseSiSpinBox->setMaximum(3.6e3); - QList frequencybaseSteps; - frequencybaseSteps << 1.0 << 2.0 << 5.0 << 10.0; - this->frequencybaseLabel = new QLabel(tr("Frequencybase")); this->frequencybaseSiSpinBox = new SiSpinBox(Helper::UNIT_HERTZ); - this->frequencybaseSiSpinBox->setSteps(frequencybaseSteps); this->frequencybaseSiSpinBox->setMinimum(1.0); this->frequencybaseSiSpinBox->setMaximum(100e6); + this->recordLengthLabel = new QLabel(tr("Record length")); + this->recordLengthComboBox = new QComboBox(); + this->formatLabel = new QLabel(tr("Format")); this->formatComboBox = new QComboBox(); for(int format = Dso::GRAPHFORMAT_TY; format < Dso::GRAPHFORMAT_COUNT; ++format) @@ -68,12 +77,16 @@ HorizontalDock::HorizontalDock(DsoSettings *settings, QWidget *parent, Qt::Windo this->dockLayout = new QGridLayout(); this->dockLayout->setColumnMinimumWidth(0, 64); this->dockLayout->setColumnStretch(1, 1); - this->dockLayout->addWidget(this->timebaseLabel, 0, 0); - this->dockLayout->addWidget(this->timebaseSiSpinBox, 0, 1); - this->dockLayout->addWidget(this->frequencybaseLabel, 1, 0); - this->dockLayout->addWidget(this->frequencybaseSiSpinBox, 1, 1); - this->dockLayout->addWidget(this->formatLabel, 2, 0); - this->dockLayout->addWidget(this->formatComboBox, 2, 1); + this->dockLayout->addWidget(this->samplerateLabel, 0, 0); + this->dockLayout->addWidget(this->samplerateSiSpinBox, 0, 1); + this->dockLayout->addWidget(this->timebaseLabel, 1, 0); + this->dockLayout->addWidget(this->timebaseSiSpinBox, 1, 1); + this->dockLayout->addWidget(this->frequencybaseLabel, 2, 0); + this->dockLayout->addWidget(this->frequencybaseSiSpinBox, 2, 1); + this->dockLayout->addWidget(this->recordLengthLabel, 3, 0); + this->dockLayout->addWidget(this->recordLengthComboBox, 3, 1); + this->dockLayout->addWidget(this->formatLabel, 4, 0); + this->dockLayout->addWidget(this->formatComboBox, 4, 1); this->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); @@ -82,13 +95,17 @@ HorizontalDock::HorizontalDock(DsoSettings *settings, QWidget *parent, Qt::Windo this->setWidget(this->dockWidget); // Connect signals and slots - connect(this->frequencybaseSiSpinBox, SIGNAL(valueChanged(double)), this, SLOT(frequencybaseSelected(double))); + connect(this->samplerateSiSpinBox, SIGNAL(valueChanged(double)), this, SLOT(samplerateSelected(double))); connect(this->timebaseSiSpinBox, SIGNAL(valueChanged(double)), this, SLOT(timebaseSelected(double))); + connect(this->frequencybaseSiSpinBox, SIGNAL(valueChanged(double)), this, SLOT(frequencybaseSelected(double))); + connect(this->recordLengthComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(recordLengthSelected(int))); connect(this->formatComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(formatSelected(int))); // Set values + this->setSamplerate(this->settings->scope.horizontal.samplerate); this->setTimebase(this->settings->scope.horizontal.timebase); this->setFrequencybase(this->settings->scope.horizontal.frequencybase); + this->setRecordLength(this->settings->scope.horizontal.recordLength); this->setFormat(this->settings->scope.horizontal.format); } @@ -104,16 +121,40 @@ void HorizontalDock::closeEvent(QCloseEvent *event) { event->accept(); } -/// \brief Changes the frequencybase if the new value is supported. +/// \brief Changes the frequencybase. /// \param frequencybase The frequencybase in hertz. void HorizontalDock::setFrequencybase(double frequencybase) { + this->suppressSignals = true; this->frequencybaseSiSpinBox->setValue(frequencybase); + this->suppressSignals = false; } -/// \brief Changes the timebase if the new value is supported. +/// \brief Changes the samplerate. +/// \param samplerate The samplerate in seconds. +void HorizontalDock::setSamplerate(double samplerate) { + this->suppressSignals = true; + this->samplerateSiSpinBox->setValue(samplerate); + this->suppressSignals = false; +} + +/// \brief Changes the timebase. /// \param timebase The timebase in seconds. void HorizontalDock::setTimebase(double timebase) { + this->suppressSignals = true; this->timebaseSiSpinBox->setValue(timebase); + this->suppressSignals = false; +} + +/// \brief Changes the record length if the new value is supported. +/// \param recordLength The record length in samples. +void HorizontalDock::setRecordLength(unsigned long int recordLength) { + int index = this->recordLengthComboBox->findData((uint) recordLength); // QVariant doesn't take unsigned long int, doesn't matter for 32 bit platforms at least + + if(index != -1) { + this->suppressSignals = true; + this->recordLengthComboBox->setCurrentIndex(index); + this->suppressSignals = false; + } } /// \brief Changes the format if the new value is supported. @@ -121,32 +162,96 @@ void HorizontalDock::setTimebase(double timebase) { /// \return Index of format-value, -1 on error. int HorizontalDock::setFormat(Dso::GraphFormat format) { if(format >= Dso::GRAPHFORMAT_TY && format <= Dso::GRAPHFORMAT_XY) { + this->suppressSignals = true; this->formatComboBox->setCurrentIndex(format); + this->suppressSignals = false; return format; } return -1; } -/// \brief Called when the frequencybase combo box changes it's value. +/// \brief Updates the available record lengths in the combo box. +/// \param recordLengths The available record lengths for the combo box. +void HorizontalDock::availableRecordLengthsChanged(const QList &recordLengths) { + /// \todo Empty lists should be interpreted as scope supporting continuous record length values. + this->recordLengthComboBox->blockSignals(true); // Avoid messing up the settings + this->recordLengthComboBox->setUpdatesEnabled(false); + + // Update existing elements to avoid unnecessary index updates + int index = 0; + for(; index < recordLengths.size(); ++index) { + unsigned long int recordLengthItem = recordLengths[index]; + if(index < this->recordLengthComboBox->count()) { + this->recordLengthComboBox->setItemData(index, (unsigned int) recordLengthItem); + this->recordLengthComboBox->setItemText(index, recordLengthItem == ULONG_MAX ? tr("Roll") : Helper::valueToString(recordLengthItem, Helper::UNIT_SAMPLES, 3)); + } + else { + this->recordLengthComboBox->addItem(recordLengthItem == ULONG_MAX ? tr("Roll") : Helper::valueToString(recordLengthItem, Helper::UNIT_SAMPLES, 3), (uint) recordLengthItem); + } + } + // Remove extra elements + for(int extraIndex = this->recordLengthComboBox->count() - 1; extraIndex > index; --extraIndex) { + this->recordLengthComboBox->removeItem(extraIndex); + } + + this->setRecordLength(this->settings->scope.horizontal.recordLength); + this->recordLengthComboBox->setUpdatesEnabled(true); + this->recordLengthComboBox->blockSignals(false); +} + +/// \brief Updates the minimum and maximum of the samplerate spin box. +/// \param minimum The minimum value the spin box should accept. +/// \param maximum The minimum value the spin box should accept. +void HorizontalDock::samplerateLimitsChanged(double minimum, double maximum) { + this->suppressSignals = true; + this->samplerateSiSpinBox->setMinimum(minimum); + this->samplerateSiSpinBox->setMaximum(maximum); + this->suppressSignals = false; +} + +/// \brief Called when the frequencybase spinbox changes its value. /// \param frequencybase The frequencybase in hertz. void HorizontalDock::frequencybaseSelected(double frequencybase) { this->settings->scope.horizontal.frequencybase = frequencybase; - emit frequencybaseChanged(frequencybase); + if(!this->suppressSignals) + emit frequencybaseChanged(frequencybase); } -/// \brief Called when the timebase combo box changes it's value. +/// \brief Called when the samplerate spinbox changes its value. +/// \param samplerate The samplerate in samples/second. +void HorizontalDock::samplerateSelected(double samplerate) { + this->settings->scope.horizontal.samplerate = samplerate; + if(!this->suppressSignals) { + this->settings->scope.horizontal.samplerateSet = true; + emit samplerateChanged(samplerate); + } +} + +/// \brief Called when the timebase spinbox changes its value. /// \param timebase The timebase in seconds. void HorizontalDock::timebaseSelected(double timebase) { this->settings->scope.horizontal.timebase = timebase; - emit timebaseChanged(timebase); + if(!this->suppressSignals) { + this->settings->scope.horizontal.samplerateSet = false; + emit timebaseChanged(timebase); + } +} + +/// \brief Called when the record length combo box changes its value. +/// \param index The index of the combo box item. +void HorizontalDock::recordLengthSelected(int index) { + this->settings->scope.horizontal.recordLength = this->recordLengthComboBox->itemData(index).toUInt(); + if(!this->suppressSignals) + emit recordLengthChanged(index); } -/// \brief Called when the format combo box changes it's value. +/// \brief Called when the format combo box changes its value. /// \param index The index of the combo box item. void HorizontalDock::formatSelected(int index) { this->settings->scope.horizontal.format = (Dso::GraphFormat) index; - emit formatChanged(this->settings->scope.horizontal.format); + if(!this->suppressSignals) + emit formatChanged(this->settings->scope.horizontal.format); } diff --git a/openhantek/src/dockwindows.h b/openhantek/src/dockwindows.h index 5620de2..99ba13b 100644 --- a/openhantek/src/dockwindows.h +++ b/openhantek/src/dockwindows.h @@ -54,7 +54,9 @@ class HorizontalDock : public QDockWidget { ~HorizontalDock(); void setFrequencybase(double timebase); + void setSamplerate(double samplerate); void setTimebase(double timebase); + void setRecordLength(unsigned long int recordLength); int setFormat(Dso::GraphFormat format); protected: @@ -62,25 +64,39 @@ class HorizontalDock : public QDockWidget { QGridLayout *dockLayout; ///< The main layout for the dock window QWidget *dockWidget; ///< The main widget for the dock window - QLabel *timebaseLabel; ///< The label for the timebase combobox - QLabel *frequencybaseLabel; ///< The label for the frequencybase combobox + QLabel *samplerateLabel; ///< The label for the samplerate spinbox + QLabel *timebaseLabel; ///< The label for the timebase spinbox + QLabel *frequencybaseLabel; ///< The label for the frequencybase spinbox + QLabel *recordLengthLabel; ///< The label for the record length combobox QLabel *formatLabel; ///< The label for the format combobox + SiSpinBox *samplerateSiSpinBox; ///< Selects the samplerate for aquisitions SiSpinBox *timebaseSiSpinBox; ///< Selects the timebase for voltage graphs SiSpinBox *frequencybaseSiSpinBox; ///< Selects the frequencybase for spectrum graphs + QComboBox *recordLengthComboBox; ///< Selects the record length for aquisitions QComboBox *formatComboBox; ///< Selects the way the sampled data is interpreted and shown DsoSettings *settings; ///< The settings provided by the parent class QStringList formatStrings; ///< Strings for the formats + + bool suppressSignals; ///< Disable changed-signals temporarily + public slots: + void availableRecordLengthsChanged(const QList &recordLengths); + void samplerateLimitsChanged(double minimum, double maximum); + protected slots: void frequencybaseSelected(double frequencybase); + void samplerateSelected(double samplerate); void timebaseSelected(double timebase); + void recordLengthSelected(int index); void formatSelected(int index); signals: void frequencybaseChanged(double frequencybase); ///< The frequencybase has been changed + void samplerateChanged(double samplerate); ///< The samplerate has been changed void timebaseChanged(double timebase); ///< The timebase has been changed + void recordLengthChanged(unsigned long recordLength); ///< The recordd length has been changed void formatChanged(Dso::GraphFormat format); ///< The viewing format has been changed }; diff --git a/openhantek/src/dsocontrol.h b/openhantek/src/dsocontrol.h index d6ba2b1..13484d5 100644 --- a/openhantek/src/dsocontrol.h +++ b/openhantek/src/dsocontrol.h @@ -47,6 +47,9 @@ class DsoControl : public QThread { DsoControl(QObject *parent = 0); virtual unsigned int getChannelCount() = 0; ///< Get the number of channels for this oscilloscope + virtual QList *getAvailableRecordLengths() = 0; ///< Get available record lengths, empty list for continuous + virtual double getMinSamplerate() = 0; ///< The minimum samplerate supported + virtual double getMaxSamplerate() = 0; ///< The maximum samplerate supported const QStringList *getSpecialTriggerSources(); @@ -62,7 +65,13 @@ class DsoControl : public QThread { 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 QList *data, const QList *size, double samplerate, QMutex *mutex); ///< New sample data is available + void samplesAvailable(const QList *data, const QList *size, double samplerate, QMutex *mutex); ///< New sample data is available + + void recordLengthChanged(unsigned long duration); ///< The record length has changed + void recordTimeChanged(double duration); ///< The record time duration has changed + void samplerateChanged(double samplerate); ///< The samplerate has changed + 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 public slots: virtual void connectDevice(); @@ -71,8 +80,9 @@ class DsoControl : public QThread { virtual void startSampling(); virtual void stopSampling(); - virtual unsigned long int setSamplerate(unsigned long int samplerate) = 0; ///< Set the samplerate that should be met - virtual unsigned long int setRecordLength(unsigned long int size) = 0; ///< Set the required record length + virtual unsigned long int setRecordLength(unsigned long int size) = 0; ///< Set record length id, minimum for continuous + virtual double setSamplerate(double samplerate) = 0; ///< Set the samplerate that should be met + virtual double setRecordTime(double duration) = 0; ///< Set the record time duration that should be met virtual int setTriggerMode(Dso::TriggerMode mode) = 0; ///< Set the trigger mode virtual int setTriggerSource(bool special, unsigned int id) = 0; ///< Set the trigger source diff --git a/openhantek/src/dsowidget.cpp b/openhantek/src/dsowidget.cpp index 46d7dcf..7552bb9 100644 --- a/openhantek/src/dsowidget.cpp +++ b/openhantek/src/dsowidget.cpp @@ -225,9 +225,10 @@ DsoWidget::DsoWidget(DsoSettings *settings, DataAnalyzer *dataAnalyzer, QWidget // Apply settings and update measured values this->updateTriggerDetails(); - this->updateRecordLength(this->settings->scope.horizontal.samples); - this->updateFrequencybase(); - this->updateTimebase(); + 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 @@ -245,8 +246,8 @@ DsoWidget::DsoWidget(DsoSettings *settings, DataAnalyzer *dataAnalyzer, QWidget this->connect(this->markerSlider, SIGNAL(valueChanged(int, double)), this->zoomScope, SLOT(updateGL())); // Connect other signals - this->connect(this->dataAnalyzer, SIGNAL(analyzed(unsigned int)), this, SLOT(dataAnalyzed())); - this->connect(this->dataAnalyzer, SIGNAL(analyzed(unsigned int)), this, SLOT(updateRecordLength(unsigned int))); + this->connect(this->dataAnalyzer, SIGNAL(analyzed(unsigned long)), this, SLOT(dataAnalyzed())); + this->connect(this->dataAnalyzer, SIGNAL(analyzed(unsigned long)), this, SLOT(updateRecordLength(unsigned long))); } /// \brief Stops the oscilloscope thread and the timer. @@ -283,7 +284,7 @@ void DsoWidget::updateMarkerDetails() { if(this->settings->view.zoom) { this->markerInfoLabel->setText(tr("Zoom x%L1").arg(DIVS_TIME / divs, -1, 'g', 3)); this->markerTimebaseLabel->setText(Helper::valueToString(time / DIVS_TIME, Helper::UNIT_SECONDS, 3) + tr("/div")); - this->markerFrequencybaseLabel->setText(Helper::valueToString(divs * this->settings->scope.horizontal.frequencybase / DIVS_TIME, Helper::UNIT_HERTZ, 3) + tr("/div")); + this->markerFrequencybaseLabel->setText(Helper::valueToString(divs * this->settings->scope.horizontal.frequencybase / DIVS_TIME, Helper::UNIT_HERTZ, 4) + tr("/div")); } this->markerTimeLabel->setText(Helper::valueToString(time, Helper::UNIT_SECONDS, 4)); this->markerFrequencyLabel->setText(Helper::valueToString(1.0 / time, Helper::UNIT_HERTZ, 4)); @@ -294,7 +295,7 @@ void DsoWidget::updateSpectrumDetails(unsigned int channel) { 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(Helper::valueToString(this->settings->scope.spectrum[channel].magnitude, Helper::UNIT_DECIBEL, 0) + tr("/div")); + this->measurementMagnitudeLabel[channel]->setText(Helper::valueToString(this->settings->scope.spectrum[channel].magnitude, Helper::UNIT_DECIBEL, 3) + tr("/div")); else this->measurementMagnitudeLabel[channel]->setText(QString()); } @@ -320,24 +321,27 @@ void DsoWidget::updateVoltageDetails(unsigned int channel) { 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(Helper::valueToString(this->settings->scope.voltage[channel].gain, Helper::UNIT_VOLTS, 0) + tr("/div")); + this->measurementGainLabel[channel]->setText(Helper::valueToString(this->settings->scope.voltage[channel].gain, Helper::UNIT_VOLTS, 3) + tr("/div")); else this->measurementGainLabel[channel]->setText(QString()); } /// \brief Handles frequencybaseChanged signal from the horizontal dock. -void DsoWidget::updateFrequencybase() { - this->settingsFrequencybaseLabel->setText(Helper::valueToString(this->settings->scope.horizontal.frequencybase, Helper::UNIT_HERTZ, 0) + tr("/div")); +/// \param frequencybase The frequencybase used for displaying the trace. +void DsoWidget::updateFrequencybase(double frequencybase) { + this->settingsFrequencybaseLabel->setText(Helper::valueToString(frequencybase, Helper::UNIT_HERTZ, 4) + tr("/div")); } /// \brief Updates the samplerate field after changing the samplerate. -void DsoWidget::updateSamplerate() { - this->settingsSamplerateLabel->setText(Helper::valueToString(this->settings->scope.horizontal.samplerate, Helper::UNIT_SAMPLES) + tr("/s")); +/// \param samplerate The samplerate set in the oscilloscope. +void DsoWidget::updateSamplerate(double samplerate) { + this->settingsSamplerateLabel->setText(Helper::valueToString(samplerate, Helper::UNIT_SAMPLES, 4) + tr("/s")); } /// \brief Handles timebaseChanged signal from the horizontal dock. -void DsoWidget::updateTimebase() { - this->settingsTimebaseLabel->setText(Helper::valueToString(this->settings->scope.horizontal.timebase, Helper::UNIT_SECONDS, 0) + tr("/div")); +/// \param timebase The timebase used for displaying the trace. +void DsoWidget::updateTimebase(double timebase) { + this->settingsTimebaseLabel->setText(Helper::valueToString(timebase, Helper::UNIT_SECONDS, 4) + tr("/div")); this->updateMarkerDetails(); } @@ -425,8 +429,8 @@ void DsoWidget::updateVoltageUsed(unsigned int channel, bool used) { } /// \brief Change the record length. -void DsoWidget::updateRecordLength(unsigned int size) { - this->settingsRecordLengthLabel->setText(tr("%1 S").arg(size)); +void DsoWidget::updateRecordLength(unsigned long size) { + this->settingsRecordLengthLabel->setText(Helper::valueToString(size, Helper::UNIT_SAMPLES, 4)); } /// \brief Export the oscilloscope screen to a file. diff --git a/openhantek/src/dsowidget.h b/openhantek/src/dsowidget.h index 97dfefc..629ee4b 100644 --- a/openhantek/src/dsowidget.h +++ b/openhantek/src/dsowidget.h @@ -96,9 +96,9 @@ class DsoWidget : public QWidget { public slots: // Horizontal axis //void horizontalFormatChanged(HorizontalFormat format); - void updateFrequencybase(); - void updateSamplerate(); - void updateTimebase(); + void updateFrequencybase(double frequencybase); + void updateSamplerate(double samplerate); + void updateTimebase(double timebase); // Trigger void updateTriggerMode(); @@ -116,7 +116,7 @@ class DsoWidget : public QWidget { void updateVoltageUsed(unsigned int channel, bool used); // Menus - void updateRecordLength(unsigned int size); + void updateRecordLength(unsigned long size); // Export bool exportAs(); diff --git a/openhantek/src/glgenerator.cpp b/openhantek/src/glgenerator.cpp index f02af00..d8bb911 100644 --- a/openhantek/src/glgenerator.cpp +++ b/openhantek/src/glgenerator.cpp @@ -86,7 +86,7 @@ GlGenerator::GlGenerator(DsoSettings *settings, QObject *parent) : QObject(paren /// \brief Deletes OpenGL objects. GlGenerator::~GlGenerator() { - // todo: Clean up vaChannel + /// \todo Clean up vaChannel } /// \brief Set the data analyzer whose data will be drawn. diff --git a/openhantek/src/hantek/control.cpp b/openhantek/src/hantek/control.cpp index 887cbeb..1d2cd32 100644 --- a/openhantek/src/hantek/control.cpp +++ b/openhantek/src/hantek/control.cpp @@ -5,7 +5,7 @@ // // Copyright (C) 2008, 2009 Oleg Khudyakov // prcoder@potrebitel.ru -// Copyright (C) 2010, 2011 Oliver Haag +// Copyright (C) 2010 - 2012 Oliver Haag // oliver.haag@gmail.com // // This program is free software: you can redistribute it and/or modify it @@ -24,6 +24,7 @@ //////////////////////////////////////////////////////////////////////////////// +#include #include #include @@ -43,7 +44,7 @@ namespace Hantek { Control::Control(QObject *parent) : DsoControl(parent) { // Use DSO-2090 specification as default this->specification.command.bulk.setRecordLength = (BulkCode) -1; - this->specification.command.bulk.setFilter = (BulkCode) -1; + this->specification.command.bulk.setChannels = (BulkCode) -1; this->specification.command.bulk.setGain = (BulkCode) -1; this->specification.command.bulk.setSamplerate = (BulkCode) -1; this->specification.command.bulk.setTrigger = (BulkCode) -1; @@ -53,12 +54,12 @@ namespace Hantek { this->specification.command.values.offsetLimits = (ControlValue) -1; this->specification.command.values.voltageLimits = (ControlValue) -1; - this->specification.recordLengths << 0; - this->specification.samplerate.single.base = 50e6; this->specification.samplerate.single.max = 50e6; + this->specification.samplerate.single.recordLengths << 0; this->specification.samplerate.multi.base = 100e6; this->specification.samplerate.multi.max = 100e6; + this->specification.samplerate.multi.recordLengths << 0; for(unsigned int channel = 0; channel < HANTEK_CHANNELS; ++channel) { for(unsigned int gainId = 0; gainId < 9; ++gainId) { @@ -69,8 +70,8 @@ namespace Hantek { // Set settings to default values this->settings.samplerate.limits = &(this->specification.samplerate.single); - this->settings.samplerate.downsampling = 1; - this->settings.samplerate.current = 0.0; + 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; @@ -114,6 +115,8 @@ namespace Hantek { this->samplesSize.append(0); } + this->previousSampleCount = 0; + connect(this->device, SIGNAL(disconnected()), this, SLOT(disconnectDevice())); } @@ -134,6 +137,25 @@ namespace Hantek { return HANTEK_CHANNELS; } + /// \brief Get available record lengths for this oscilloscope. + /// \return The number of physical channels, empty list for continuous. + QList *Control::getAvailableRecordLengths() { + return &this->settings.samplerate.limits->recordLengths; + } + + /// \brief Get minimum samplerate for this oscilloscope. + /// \return The minimum samplerate for the current configuration in S/s. + double Control::getMinSamplerate() { + return (double) this->specification.samplerate.single.base / this->specification.samplerate.single.maxDownsampler; + } + + /// \brief Get maximum samplerate for this oscilloscope. + /// \return The maximum samplerate for the current configuration in S/s. + double Control::getMaxSamplerate() { + ControlSamplerateLimits *limits = (this->settings.usedChannels <= 1) ? &this->specification.samplerate.multi : &this->specification.samplerate.single; + return limits->max; + } + /// \brief Handles all USB things until the device gets disconnected. void Control::run() { int errorCode, cycleCounter = 0, startCycle = 0; @@ -194,7 +216,7 @@ namespace Hantek { // Check the current oscilloscope state everytime 25% of the time the buffer should be refilled // Not more often than every 10 ms though - int cycleTime = qMax((unsigned long int) (this->specification.recordLengths[this->settings.recordLengthId] / this->settings.samplerate.current * 250), 10lu); + int cycleTime = qMax((unsigned long int) (this->settings.samplerate.limits->recordLengths[this->settings.recordLengthId] / this->settings.samplerate.current * 250), 10lu); this->msleep(cycleTime); if(!this->sampling) { @@ -237,6 +259,12 @@ namespace Hantek { break; case CAPTURE_WAITING: + // Sampling hasn't started, update the expected sample count + if(this->settings.samplerate.limits == &this->specification.samplerate.multi) + this->previousSampleCount = this->specification.samplerate.multi.recordLengths[this->settings.recordLengthId]; + else + this->previousSampleCount = this->specification.samplerate.single.recordLengths[this->settings.recordLengthId] * HANTEK_CHANNELS; + if(samplingStarted && lastTriggerMode == this->settings.trigger.mode) { ++cycleCounter; @@ -338,8 +366,24 @@ namespace Hantek { return errorCode; // Save raw data to temporary buffer - unsigned int dataCount = this->specification.recordLengths[this->settings.recordLengthId] * HANTEK_CHANNELS; - unsigned int dataLength = dataCount; + bool fastRate = this->settings.samplerate.limits == &this->specification.samplerate.multi; + + unsigned long int totalSampleCount = fastRate ? this->specification.samplerate.multi.recordLengths[this->settings.recordLengthId] : this->specification.samplerate.single.recordLengths[this->settings.recordLengthId] * HANTEK_CHANNELS; + + // To make sure no samples will remain in the scope buffer, also check the sample count before the last sampling started + if(totalSampleCount < this->previousSampleCount) { + unsigned long int currentSampleCount = totalSampleCount; + totalSampleCount = this->previousSampleCount; + this->previousSampleCount = currentSampleCount; // Using sampleCount as temporary buffer since it was set to totalSampleCount + } + else { + this->previousSampleCount = totalSampleCount; + } + + unsigned long int sampleCount = totalSampleCount; + if(!fastRate) + sampleCount /= HANTEK_CHANNELS; + unsigned long int dataLength = totalSampleCount; if(this->specification.sampleSize > 8) dataLength *= 2; @@ -353,18 +397,19 @@ namespace Hantek { // How much data did we really receive? dataLength = errorCode; if(this->specification.sampleSize > 8) - dataCount = dataLength / 2; + totalSampleCount = dataLength / 2; else - dataCount = dataLength; + totalSampleCount = dataLength; this->samplesMutex.lock(); // Convert channel data - if(this->settings.samplerate.limits == &this->specification.samplerate.multi) { + 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[0].used) + if(this->settings.voltage[channel].used) break; } @@ -378,34 +423,34 @@ namespace Hantek { if(channel < HANTEK_CHANNELS) { // Reallocate memory for samples if the sample count has changed - if(!this->samples[channel] || this->samplesSize[channel] != dataCount) { + if(!this->samples[channel] || this->samplesSize[channel] != sampleCount) { if(this->samples[channel]) delete this->samples[channel]; - this->samples[channel] = new double[dataCount]; - this->samplesSize[channel] = dataCount; + this->samples[channel] = new double[sampleCount]; + this->samplesSize[channel] = sampleCount; } // Convert data from the oscilloscope and write it into the sample buffer - unsigned int bufferPosition = (this->settings.trigger.point + 1) * 2; + unsigned long int bufferPosition = this->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 < dataCount; ++realPosition, ++bufferPosition) { - if(bufferPosition >= dataCount) - bufferPosition %= dataCount; + for(unsigned long 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[dataCount + bufferPosition - extraBitsPosition] << (8 - (HANTEK_CHANNELS - 1 - extraBitsPosition) * extraBitsSize)) & extraBitsMask)) / this->specification.voltageLimit[channel][this->settings.voltage[channel].gain] - this->settings.voltage[channel].offsetReal) * this->specification.gainSteps[this->settings.voltage[channel].gain]; + this->samples[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][this->settings.voltage[channel].gain] - this->settings.voltage[channel].offsetReal) * this->specification.gainSteps[this->settings.voltage[channel].gain]; } } else { - for(unsigned int realPosition = 0; realPosition < dataCount; ++realPosition, ++bufferPosition) { - if(bufferPosition >= dataCount) - bufferPosition %= dataCount; + for(unsigned long int realPosition = 0; realPosition < sampleCount; ++realPosition, ++bufferPosition) { + if(bufferPosition >= sampleCount) + bufferPosition %= sampleCount; this->samples[channel][realPosition] = ((double) data[bufferPosition] / this->specification.voltageLimit[channel][this->settings.voltage[channel].gain] - this->settings.voltage[channel].offsetReal) * this->specification.gainSteps[this->settings.voltage[channel].gain]; } @@ -414,39 +459,39 @@ namespace Hantek { } else { // Normal mode, channels are using their separate buffers - unsigned int channelDataCount = dataCount / HANTEK_CHANNELS; - + sampleCount = totalSampleCount / HANTEK_CHANNELS; for(int channel = 0; channel < HANTEK_CHANNELS; ++channel) { if(this->settings.voltage[channel].used) { // Reallocate memory for samples if the sample count has changed - if(!this->samples[channel] || this->samplesSize[channel] != channelDataCount) { + if(!this->samples[channel] || this->samplesSize[channel] != sampleCount) { if(this->samples[channel]) delete this->samples[channel]; - this->samples[channel] = new double[channelDataCount]; - this->samplesSize[channel] = channelDataCount; + this->samples[channel] = new double[sampleCount]; + this->samplesSize[channel] = sampleCount; } // Convert data from the oscilloscope and write it into the sample buffer - unsigned int bufferPosition = (this->settings.trigger.point + 1) * 2; + unsigned long 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 < channelDataCount; ++realPosition, bufferPosition += 2) { - if(bufferPosition >= dataCount) - bufferPosition %= dataCount; + for(unsigned long 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[dataCount + bufferPosition] << extraBitsIndex) & extraBitsMask)) / this->specification.voltageLimit[channel][this->settings.voltage[channel].gain] - this->settings.voltage[channel].offsetReal) * this->specification.gainSteps[this->settings.voltage[channel].gain]; + this->samples[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) * this->specification.gainSteps[this->settings.voltage[channel].gain]; } } else { - for(unsigned int realPosition = 0; realPosition < channelDataCount; ++realPosition, bufferPosition += 2) { - if(bufferPosition >= dataCount) - bufferPosition %= dataCount; + bufferPosition += HANTEK_CHANNELS - 1 - channel; + for(unsigned long int realPosition = 0; realPosition < sampleCount; ++realPosition, bufferPosition += HANTEK_CHANNELS) { + if(bufferPosition >= totalSampleCount) + bufferPosition %= totalSampleCount; - this->samples[channel][realPosition] = ((double) data[bufferPosition + HANTEK_CHANNELS - 1 - channel] / this->specification.voltageLimit[channel][this->settings.voltage[channel].gain] - this->settings.voltage[channel].offsetReal) * this->specification.gainSteps[this->settings.voltage[channel].gain]; + this->samples[channel][realPosition] = ((double) data[bufferPosition] / this->specification.voltageLimit[channel][this->settings.voltage[channel].gain] - this->settings.voltage[channel].offsetReal) * this->specification.gainSteps[this->settings.voltage[channel].gain]; } } } @@ -466,29 +511,112 @@ namespace Hantek { return errorCode; } - /// \brief Sets the size of the sample buffer without updating dependencies. - /// \param size The record length that should be met (S). - /// \return The record length that has been set, 0 on error. - unsigned long int Control::updateRecordLength(unsigned long int size) { - // Get the record length supporting the highest samplerate while meeting the requirement - int bestSizeId = -1; - for(int sizeId = 0; sizeId < this->specification.recordLengths.count(); ++sizeId) { - if(this->specification.recordLengths[sizeId] >= size) { - // We meet the size-requirement, check if we provide the highest possible samplerate - if(bestSizeId == -1 || this->specification.recordLengths[bestSizeId] < size || this->specification.bufferDividers[sizeId] < this->specification.bufferDividers[bestSizeId]) - bestSizeId = sizeId; - } - else { - // We don't meet the size-requirement, but maybe we're still the one coming closest - if(bestSizeId == -1 || this->specification.recordLengths[sizeId] > this->specification.recordLengths[bestSizeId]) - bestSizeId = sizeId; + /// \brief Calculated the nearest samplerate supported by the oscilloscope. + /// \param samplerate The target samplerate, that should be met as good as possible. + /// \param fastRate true, if the fast rate mode is enabled. + /// \param maximum The target samplerate is the maximum allowed when true, the minimum otherwise. + /// \param downsampler Pointer to where the selected downsampling factor should be written. + /// \return The nearest samplerate supported, 0.0 on error. + double Control::getBestSamplerate(double samplerate, bool fastRate, bool maximum, unsigned long int *downsampler) { + // Abort if the input value is invalid + if(samplerate <= 0.0) + return 0.0; + + double bestSamplerate = 0.0; + + // Get samplerate specifications for this mode and model + ControlSamplerateLimits *limits; + if(fastRate) + limits = &(this->specification.samplerate.multi); + else + limits = &(this->specification.samplerate.single); + + // Get downsampling factor that would provide the requested rate + double bestDownsampler = (double) limits->base / this->specification.bufferDividers[this->settings.recordLengthId] / samplerate; + // Base samplerate sufficient, or is the maximum better? + if(bestDownsampler < 1.0 && (samplerate <= limits->max / this->specification.bufferDividers[this->settings.recordLengthId] || !maximum)) { + bestDownsampler = 0.0; + bestSamplerate = limits->max / this->specification.bufferDividers[this->settings.recordLengthId]; + } + else { + switch(this->specification.command.bulk.setSamplerate) { + case BULK_SETTRIGGERANDSAMPLERATE: + // DSO-2090 supports the downsampling factors 1, 2, 4 and 5 using valueFast or all even values above using valueSlow + if((maximum && bestDownsampler <= 5.0) || (!maximum && bestDownsampler < 6.0)) { + // valueFast is used + if(maximum) { + // The samplerate shall not be higher, so we round up + bestDownsampler = ceil(bestDownsampler); + if(bestDownsampler > 2.0) // 3 and 4 not possible with the DSO-2090 + bestDownsampler = 5.0; + } + else { + // The samplerate shall not be lower, so we round down + bestDownsampler = floor(bestDownsampler); + if(bestDownsampler > 2.0 && bestDownsampler < 5.0) // 3 and 4 not possible with the DSO-2090 + bestDownsampler = 2.0; + } + } + else { + // valueSlow is used + if(maximum) { + bestDownsampler = ceil(bestDownsampler / 2.0) * 2.0; // Round up to next even value + } + else { + bestDownsampler = floor(bestDownsampler / 2.0) * 2.0; // Round down to next even value + } + if(bestDownsampler > 2.0 * 0x10001) // Check for overflow + bestDownsampler = 2.0 * 0x10001; + } + break; + + case BULK_CSETTRIGGERORSAMPLERATE: + // DSO-5200 may not supports all downsampling factors, requires testing + if(maximum) { + bestDownsampler = ceil(bestDownsampler); // Round up to next integer value + } + else { + bestDownsampler = floor(bestDownsampler); // Round down to next integer value + } + break; + + case BULK_ESETTRIGGERORSAMPLERATE: + // DSO-2250 doesn't have a fast value, so it supports all downsampling factors + if(maximum) { + bestDownsampler = ceil(bestDownsampler); // Round up to next integer value + } + else { + bestDownsampler = floor(bestDownsampler); // Round down to next integer value + } + break; + + default: + return 0.0; } + + // Limit maximum downsampler value to avoid overflows in the sent commands + if(bestDownsampler > limits->maxDownsampler) + bestDownsampler = limits->maxDownsampler; + + bestSamplerate = limits->base / bestDownsampler / this->specification.bufferDividers[this->settings.recordLengthId]; } + if(downsampler) + *downsampler = (unsigned long int) bestDownsampler; + return bestSamplerate; + } + + /// \brief Sets the size of the sample buffer without updating dependencies. + /// \param index The record length index that should be set. + /// \return The record length that has been set, 0 on error. + unsigned long int Control::updateRecordLength(unsigned long int index) { + if(index >= (unsigned long int) this->settings.samplerate.limits->recordLengths.size()) + return 0; + switch(this->specification.command.bulk.setRecordLength) { case BULK_SETTRIGGERANDSAMPLERATE: // SetTriggerAndSamplerate bulk command for record length - static_cast(this->command[BULK_SETTRIGGERANDSAMPLERATE])->setRecordLength(bestSizeId); + static_cast(this->command[BULK_SETTRIGGERANDSAMPLERATE])->setRecordLength(index); this->commandPending[BULK_SETTRIGGERANDSAMPLERATE] = true; break; @@ -498,7 +626,7 @@ namespace Hantek { // Pointers to needed commands BulkSetRecordLength2250 *commandSetRecordLength2250 = static_cast(this->command[BULK_DSETBUFFER]); - commandSetRecordLength2250->setRecordLength(bestSizeId); + commandSetRecordLength2250->setRecordLength(index); } else { // SetBuffer5200 bulk command for record length @@ -506,7 +634,7 @@ namespace Hantek { commandSetBuffer5200->setUsedPre(DTRIGGERPOSITION_ON); commandSetBuffer5200->setUsedPost(DTRIGGERPOSITION_ON); - commandSetBuffer5200->setRecordLength(bestSizeId); + commandSetBuffer5200->setRecordLength(index); } this->commandPending[BULK_DSETBUFFER] = true; @@ -517,9 +645,136 @@ namespace Hantek { return 0; } - this->settings.recordLengthId = bestSizeId; + this->settings.recordLengthId = index; + + return this->settings.samplerate.limits->recordLengths[index]; + } + + /// \brief Sets the samplerate based on the parameters calculated by Control::getBestSamplerate. + /// \param downsampler The downsampling factor. + /// \param fastRate true, if one channel uses all buffers. + /// \return The downsampling factor that has been set. + unsigned long int Control::updateSamplerate(unsigned long int downsampler, bool fastRate) { + // Set the calculated samplerate + switch(this->specification.command.bulk.setSamplerate) { + case BULK_SETTRIGGERANDSAMPLERATE: { + short int downsamplerValue = 0; + unsigned char samplerateId = 0; + bool downsampling = false; + + if(downsampler <= 5) { + // All dividers up to 5 are done using the special samplerate IDs + if(downsampler == 0) + samplerateId = 1; + else if(downsampler <= 2) + samplerateId = downsampler; + else { // Downsampling factors 3 and 4 are not supported + samplerateId = 3; + downsampler = 5; + downsamplerValue = 0xffff; + } + } + else { + // For any dividers above the downsampling factor can be set directly + downsampler &= ~0x0001; // Only even values possible + downsamplerValue = (short int) (0x10001 - (downsampler >> 1)); + + downsampling = true; + } + + // Pointers to needed commands + BulkSetTriggerAndSamplerate *commandSetTriggerAndSamplerate = static_cast(this->command[BULK_SETTRIGGERANDSAMPLERATE]); + + // Store if samplerate ID or downsampling factor is used + commandSetTriggerAndSamplerate->setDownsamplingMode(downsampling); + // Store samplerate ID + commandSetTriggerAndSamplerate->setSamplerateId(samplerateId); + // Store downsampling factor + commandSetTriggerAndSamplerate->setDownsampler(downsamplerValue); + // Set fast rate when used + commandSetTriggerAndSamplerate->setFastRate(false /*fastRate*/); + + this->commandPending[BULK_SETTRIGGERANDSAMPLERATE] = true; + + break; + } + case BULK_CSETTRIGGERORSAMPLERATE: { + // Split the resulting divider into the values understood by the device + // The fast value is kept at 4 (or 3) for slow sample rates + long int valueSlow = qMax(((long int) downsampler - 3) / 2, (long int) 0); + unsigned char valueFast = downsampler - valueSlow * 2; + + // Pointers to needed commands + BulkSetSamplerate5200 *commandSetSamplerate5200 = static_cast(this->command[BULK_CSETTRIGGERORSAMPLERATE]); + BulkSetTrigger5200 *commandSetTrigger5200 = static_cast(this->command[BULK_ESETTRIGGERORSAMPLERATE]); + + // Store samplerate fast value + commandSetSamplerate5200->setSamplerateFast(4 - valueFast); + // Store samplerate slow value (two's complement) + commandSetSamplerate5200->setSamplerateSlow(valueSlow == 0 ? 0 : 0xffff - valueSlow); + // Set fast rate when used + commandSetTrigger5200->setFastRate(fastRate); + + this->commandPending[BULK_CSETTRIGGERORSAMPLERATE] = true; + this->commandPending[BULK_ESETTRIGGERORSAMPLERATE] = true; + + break; + } + case BULK_ESETTRIGGERORSAMPLERATE: { + // Pointers to needed commands + BulkSetSamplerate2250 *commandSetSamplerate2250 = static_cast(this->command[BULK_ESETTRIGGERORSAMPLERATE]); + + bool downsampling = downsampler >= 1; + // Store downsampler state value + commandSetSamplerate2250->setDownsampling(downsampling); + // Store samplerate value + commandSetSamplerate2250->setSamplerate(downsampler > 1 ? 0x10001 - downsampler : 0); + // Set fast rate when used + commandSetSamplerate2250->setFastRate(fastRate); + + this->commandPending[BULK_ESETTRIGGERORSAMPLERATE] = true; + + break; + } + default: + return ULONG_MAX; + } + + // Update settings + bool fastRateChanged = fastRate != (this->settings.samplerate.limits == &this->specification.samplerate.multi); + if(fastRateChanged) { + if(fastRate) + this->settings.samplerate.limits = &this->specification.samplerate.multi; + else + this->settings.samplerate.limits = &this->specification.samplerate.single; + } - return this->specification.recordLengths[this->settings.recordLengthId]; + this->settings.samplerate.downsampler = downsampler; + if(downsampler) + this->settings.samplerate.current = this->settings.samplerate.limits->base / downsampler; + else + this->settings.samplerate.current = this->settings.samplerate.limits->max; + + // Update dependencies + this->setPretriggerPosition(this->settings.trigger.position); + + // Emit signals for changed settings + if(fastRateChanged) { + emit availableRecordLengthsChanged(this->settings.samplerate.limits->recordLengths); + emit recordLengthChanged(this->settings.samplerate.limits->recordLengths[this->settings.recordLengthId]); + } + emit recordTimeChanged((double) this->settings.samplerate.limits->recordLengths[this->settings.recordLengthId] / this->settings.samplerate.current); + emit samplerateChanged(this->settings.samplerate.current); + + return downsampler; + } + + /// \brief Try to connect to the oscilloscope. + void Control::restoreTargets() { + if(this->settings.samplerate.target.samplerateSet) + this->setSamplerate(); + else + this->setRecordTime(); } /// \brief Try to connect to the oscilloscope. @@ -545,7 +800,7 @@ namespace Hantek { this->command[BULK_SETGAIN] = new BulkSetGain(); // Initialize the command versions to the ones used on the DSO-2090 this->specification.command.bulk.setRecordLength = (BulkCode) -1; - this->specification.command.bulk.setFilter = (BulkCode) -1; + this->specification.command.bulk.setChannels = (BulkCode) -1; this->specification.command.bulk.setGain = BULK_SETGAIN; this->specification.command.bulk.setSamplerate = (BulkCode) -1; this->specification.command.bulk.setTrigger = (BulkCode) -1; @@ -563,32 +818,30 @@ namespace Hantek { case MODEL_DSO2090: // Instantiate additional commands for the DSO-2090 - this->command[BULK_SETFILTER] = new BulkSetFilter(); this->command[BULK_SETTRIGGERANDSAMPLERATE] = new BulkSetTriggerAndSamplerate(); this->specification.command.bulk.setRecordLength = BULK_SETTRIGGERANDSAMPLERATE; - this->specification.command.bulk.setFilter = BULK_SETFILTER; + this->specification.command.bulk.setChannels = BULK_SETTRIGGERANDSAMPLERATE; this->specification.command.bulk.setSamplerate = BULK_SETTRIGGERANDSAMPLERATE; this->specification.command.bulk.setTrigger = BULK_SETTRIGGERANDSAMPLERATE; this->specification.command.bulk.setPretrigger = BULK_SETTRIGGERANDSAMPLERATE; // Initialize those as pending - this->commandPending[BULK_SETFILTER] = true; this->commandPending[BULK_SETTRIGGERANDSAMPLERATE] = true; break; case MODEL_DSO2250: // Instantiate additional commands for the DSO-2250 - this->command[BULK_BSETFILTER] = new BulkSetFilter2250(); + this->command[BULK_BSETCHANNELS] = new BulkSetChannels2250(); this->command[BULK_CSETTRIGGERORSAMPLERATE] = new BulkSetTrigger2250(); this->command[BULK_DSETBUFFER] = new BulkSetRecordLength2250(); this->command[BULK_ESETTRIGGERORSAMPLERATE] = new BulkSetSamplerate2250(); this->command[BULK_FSETBUFFER] = new BulkSetBuffer2250(); this->specification.command.bulk.setRecordLength = BULK_DSETBUFFER; - this->specification.command.bulk.setFilter = BULK_BSETFILTER; + this->specification.command.bulk.setChannels = BULK_BSETCHANNELS; this->specification.command.bulk.setSamplerate = BULK_ESETTRIGGERORSAMPLERATE; this->specification.command.bulk.setTrigger = BULK_CSETTRIGGERORSAMPLERATE; this->specification.command.bulk.setPretrigger = BULK_FSETBUFFER; - this->commandPending[BULK_BSETFILTER] = true; + this->commandPending[BULK_BSETCHANNELS] = true; this->commandPending[BULK_CSETTRIGGERORSAMPLERATE] = true; this->commandPending[BULK_DSETBUFFER] = true; this->commandPending[BULK_ESETTRIGGERORSAMPLERATE] = true; @@ -605,6 +858,7 @@ namespace Hantek { this->command[BULK_DSETBUFFER] = new BulkSetBuffer5200(); this->command[BULK_ESETTRIGGERORSAMPLERATE] = new BulkSetTrigger5200(); this->specification.command.bulk.setRecordLength = BULK_DSETBUFFER; + this->specification.command.bulk.setChannels = BULK_ESETTRIGGERORSAMPLERATE; this->specification.command.bulk.setSamplerate = BULK_CSETTRIGGERORSAMPLERATE; this->specification.command.bulk.setTrigger = BULK_ESETTRIGGERORSAMPLERATE; this->specification.command.bulk.setPretrigger = BULK_ESETTRIGGERORSAMPLERATE; @@ -630,7 +884,8 @@ namespace Hantek { // Maximum possible samplerate for a single channel and dividers for record lengths this->specification.bufferDividers.clear(); - this->specification.recordLengths.clear(); + this->specification.samplerate.single.recordLengths.clear(); + this->specification.samplerate.multi.recordLengths.clear(); this->specification.gainSteps.clear(); for(int channel = 0; channel < HANTEK_CHANNELS; ++channel) this->specification.voltageLimit[channel].clear(); @@ -640,10 +895,13 @@ namespace Hantek { case MODEL_DSO5200A: this->specification.samplerate.single.base = 100e6; this->specification.samplerate.single.max = 125e6; + this->specification.samplerate.single.maxDownsampler = 131072; + this->specification.samplerate.single.recordLengths << ULONG_MAX << 10240 << 14336; this->specification.samplerate.multi.base = 200e6; this->specification.samplerate.multi.max = 250e6; + this->specification.samplerate.multi.maxDownsampler = 131072; + this->specification.samplerate.multi.recordLengths << ULONG_MAX << 20480 << 28672; this->specification.bufferDividers << 1000 << 1 << 1; - this->specification.recordLengths << ULONG_MAX << 10240 << 14336; this->specification.gainSteps << 0.16 << 0.40 << 0.80 << 1.60 << 4.00 << 8.0 << 16.0 << 40.0 << 80.0; /// \todo Use calibration data to get the DSO-5200(A) sample ranges @@ -658,10 +916,13 @@ namespace Hantek { case MODEL_DSO2250: this->specification.samplerate.single.base = 100e6; this->specification.samplerate.single.max = 100e6; + this->specification.samplerate.single.maxDownsampler = 65536; + this->specification.samplerate.single.recordLengths << ULONG_MAX << 10240 << 524288; this->specification.samplerate.multi.base = 200e6; this->specification.samplerate.multi.max = 250e6; + this->specification.samplerate.multi.maxDownsampler = 65536; + this->specification.samplerate.multi.recordLengths << ULONG_MAX << 20480 << 1048576; this->specification.bufferDividers << 1000 << 1 << 1; - this->specification.recordLengths << ULONG_MAX << 10240 << 524288; this->specification.gainSteps << 0.08 << 0.16 << 0.40 << 0.80 << 1.60 << 4.00 << 8.0 << 16.0 << 40.0; for(int channel = 0; channel < HANTEK_CHANNELS; ++channel) @@ -675,10 +936,13 @@ namespace Hantek { case MODEL_DSO2150: this->specification.samplerate.single.base = 50e6; this->specification.samplerate.single.max = 75e6; + this->specification.samplerate.single.maxDownsampler = 131072; + this->specification.samplerate.single.recordLengths << ULONG_MAX << 10240 << 32768; this->specification.samplerate.multi.base = 100e6; this->specification.samplerate.multi.max = 150e6; + this->specification.samplerate.multi.maxDownsampler = 131072; + this->specification.samplerate.multi.recordLengths << ULONG_MAX << 20480 << 65536; this->specification.bufferDividers << 1000 << 1 << 1; - this->specification.recordLengths << ULONG_MAX << 10240 << 32768; this->specification.gainSteps << 0.08 << 0.16 << 0.40 << 0.80 << 1.60 << 4.00 << 8.0 << 16.0 << 40.0; for(int channel = 0; channel < HANTEK_CHANNELS; ++channel) @@ -692,10 +956,13 @@ namespace Hantek { default: this->specification.samplerate.single.base = 50e6; this->specification.samplerate.single.max = 50e6; + this->specification.samplerate.single.maxDownsampler = 131072; + this->specification.samplerate.single.recordLengths << ULONG_MAX << 10240 << 32768; this->specification.samplerate.multi.base = 100e6; this->specification.samplerate.multi.max = 100e6; + this->specification.samplerate.multi.maxDownsampler = 131072; + this->specification.samplerate.multi.recordLengths << ULONG_MAX << 20480 << 65536; this->specification.bufferDividers << 1000 << 1 << 1; - this->specification.recordLengths << ULONG_MAX << 10240 << 32768; this->specification.gainSteps << 0.08 << 0.16 << 0.40 << 0.80 << 1.60 << 4.00 << 8.0 << 16.0 << 40.0; for(int channel = 0; channel < HANTEK_CHANNELS; ++channel) @@ -706,8 +973,10 @@ namespace Hantek { this->specification.sampleSize = 8; break; } + this->settings.recordLengthId = 1; this->settings.samplerate.limits = &(this->specification.samplerate.single); - this->settings.samplerate.downsampling = 1; + this->settings.samplerate.downsampler = 1; + this->previousSampleCount = 0; // Get channel level data errorCode = this->device->controlRead(CONTROL_VALUE, (unsigned char *) &(this->specification.offsetLimit), sizeof(this->specification.offsetLimit), (int) VALUE_OFFSETLIMITS); @@ -721,131 +990,78 @@ namespace Hantek { } /// \brief Sets the size of the oscilloscopes sample buffer. - /// \param size The record length that should be met (S). + /// \param index The record length index that should be set. /// \return The record length that has been set, 0 on error. - unsigned long int Control::setRecordLength(unsigned long int size) { - if(!this->device->isConnected()) + unsigned long int Control::setRecordLength(unsigned long int index) { + if(!this->device->isConnected()) return 0; - this->updateRecordLength(size); + if(!this->updateRecordLength(index)) + return 0; - this->setSamplerate(); + this->restoreTargets(); this->setPretriggerPosition(this->settings.trigger.position); - return this->specification.recordLengths[this->settings.recordLengthId]; + emit recordLengthChanged(this->settings.samplerate.limits->recordLengths[this->settings.recordLengthId]); + return this->settings.samplerate.limits->recordLengths[this->settings.recordLengthId]; } /// \brief Sets the samplerate of the oscilloscope. - /// \param samplerate The samplerate that should be met (S/s). - /// \return The samplerate that has been set, 0 on error. - unsigned long int Control::setSamplerate(unsigned long int samplerate) { - if(!this->device->isConnected()) - return 0; + /// \param samplerate The samplerate that should be met (S/s), 0.0 to restore current samplerate. + /// \return The samplerate that has been set, 0.0 on error. + double Control::setSamplerate(double samplerate) { + if(samplerate == 0.0) { + samplerate = this->settings.samplerate.target.samplerate; + } + else { + this->settings.samplerate.target.samplerate = samplerate; + this->settings.samplerate.target.samplerateSet = true; + } - // Keep samplerate if no parameter was given - if(!samplerate) - samplerate = this->settings.samplerate.current; - // Abort samplerate calculation if we didn't get a valid value yet - if(!samplerate) - return samplerate; + // When possible, enable fast rate if it is required to reach the requested samplerate + bool fastRate = (this->settings.usedChannels <= 1) && (samplerate > this->specification.samplerate.single.max); - // Calculate with fast rate first if only one channel is used - bool fastRate = false; - this->settings.samplerate.limits = &(this->specification.samplerate.single); - if(this->settings.usedChannels <= 1) { - fastRate = true; - this->settings.samplerate.limits = &(this->specification.samplerate.multi); + // What is the nearest, at least as high samplerate the scope can provide? + unsigned long int downsampler = 0; + double bestSamplerate = getBestSamplerate(samplerate, fastRate, false, &(downsampler)); + + // Set the calculated samplerate + if(this->updateSamplerate(downsampler, fastRate) == ULONG_MAX) + return 0.0; + else { + return bestSamplerate; + } + } + + /// \brief Sets the time duration of one aquisition by adapting the samplerate. + /// \param duration The record time duration that should be met (s), 0.0 to restore current record time. + /// \return The record time duration that has been set, 0.0 on error. + double Control::setRecordTime(double duration) { + if(duration == 0.0) { + duration = this->settings.samplerate.target.duration; + } + else { + this->settings.samplerate.target.duration = duration; + this->settings.samplerate.target.samplerateSet = false; } - // Get downsampling factor that would provide the requested rate - this->settings.samplerate.downsampling = this->settings.samplerate.limits->base / this->specification.bufferDividers[this->settings.recordLengthId] / samplerate; - // A downsampling factor of zero will result in the maximum rate - if(this->settings.samplerate.downsampling) - this->settings.samplerate.current = this->settings.samplerate.limits->base / this->specification.bufferDividers[this->settings.recordLengthId] / this->settings.samplerate.downsampling; - else - this->settings.samplerate.current = this->settings.samplerate.limits->max / this->specification.bufferDividers[this->settings.recordLengthId]; + // Calculate the maximum samplerate that would still provide the requested duration + double maxSamplerate = (double) this->specification.samplerate.single.recordLengths[this->settings.recordLengthId] / duration; - // Maybe normal mode would be sufficient or even better than fast rate mode - if(fastRate) { - // Don't set the downsampling factor to zero (maximum rate) if we could use fast rate mode anyway - unsigned long int slowDownsampling = qMax(this->specification.samplerate.single.base / this->specification.bufferDividers[this->settings.recordLengthId] / samplerate, (long unsigned int) 1); - - // Use normal mode if we need valueSlow or it would meet the rate at least as exactly as fast rate mode - if(this->settings.samplerate.downsampling > 4 || (qAbs((double) this->specification.samplerate.single.base / this->specification.bufferDividers[this->settings.recordLengthId] / slowDownsampling - samplerate) <= qAbs((double) this->settings.samplerate.current - samplerate))) { - fastRate = false; - this->settings.samplerate.limits = &(this->specification.samplerate.single); - this->settings.samplerate.downsampling = slowDownsampling; - this->settings.samplerate.current = this->specification.samplerate.single.base / this->specification.bufferDividers[this->settings.recordLengthId] / this->settings.samplerate.downsampling; - } - } + // When possible, enable fast rate if the record time can't be set that low to improve resolution + bool fastRate = (this->settings.usedChannels <= 1) && (maxSamplerate >= this->specification.samplerate.multi.base); - switch(this->specification.command.bulk.setSamplerate) { - case BULK_SETTRIGGERANDSAMPLERATE: { - // Split the resulting divider into the values understood by the device - // The fast value is kept at 4 (or 3) for slow sample rates - long int valueSlow = qMax(((long int) this->settings.samplerate.downsampling - 3) / 2, (long int) 0); - unsigned char valueFast = this->settings.samplerate.downsampling - valueSlow * 2; - - // Pointers to needed commands - BulkSetTriggerAndSamplerate *commandSetTriggerAndSamplerate = static_cast(this->command[BULK_SETTRIGGERANDSAMPLERATE]); - - // Store samplerate fast value - commandSetTriggerAndSamplerate->setSamplerateFast(valueFast); - // Store samplerate slow value (two's complement) - commandSetTriggerAndSamplerate->setSamplerateSlow(valueSlow == 0 ? 0 : 0xffff - valueSlow); - // Set fast rate when used - commandSetTriggerAndSamplerate->setFastRate(fastRate); - - this->commandPending[BULK_SETTRIGGERANDSAMPLERATE] = true; - - break; - } - case BULK_CSETTRIGGERORSAMPLERATE: { - // Split the resulting divider into the values understood by the device - // The fast value is kept at 4 (or 3) for slow sample rates - long int valueSlow = qMax(((long int) this->settings.samplerate.downsampling - 3) / 2, (long int) 0); - unsigned char valueFast = this->settings.samplerate.downsampling - valueSlow * 2; - - // Pointers to needed commands - BulkSetSamplerate5200 *commandSetSamplerate5200 = static_cast(this->command[BULK_CSETTRIGGERORSAMPLERATE]); - BulkSetTrigger5200 *commandSetTrigger5200 = static_cast(this->command[BULK_ESETTRIGGERORSAMPLERATE]); - - // Store samplerate fast value - commandSetSamplerate5200->setSamplerateFast(4 - valueFast); - // Store samplerate slow value (two's complement) - commandSetSamplerate5200->setSamplerateSlow(valueSlow == 0 ? 0 : 0xffff - valueSlow); - // Set fast rate when used - commandSetTrigger5200->setFastRate(fastRate); - - this->commandPending[BULK_CSETTRIGGERORSAMPLERATE] = true; - this->commandPending[BULK_ESETTRIGGERORSAMPLERATE] = true; - - break; - } - case BULK_ESETTRIGGERORSAMPLERATE: { - // Pointers to needed commands - BulkSetSamplerate2250 *commandSetSamplerate2250 = static_cast(this->command[BULK_ESETTRIGGERORSAMPLERATE]); - - bool downsampling = this->settings.samplerate.downsampling > 1; - // Store downsampler state value - commandSetSamplerate2250->setDownsampling(downsampling); - // Store samplerate value - commandSetSamplerate2250->setSamplerate(downsampling ? 0x10001 - this->settings.samplerate.downsampling : 0); - // Set fast rate when used - commandSetSamplerate2250->setFastRate(fastRate); - - this->commandPending[BULK_ESETTRIGGERORSAMPLERATE] = true; - - break; - } - default: - return 0; - } + // What is the nearest, at most as high samplerate the scope can provide? + unsigned long int downsampler = 0; + double bestSamplerate = getBestSamplerate(maxSamplerate, fastRate, true, &(downsampler)); - this->updateRecordLength(this->specification.recordLengths[this->settings.recordLengthId]); - this->setPretriggerPosition(this->settings.trigger.position); - return this->settings.samplerate.current; - } + // Set the calculated samplerate + if(this->updateSamplerate(downsampler, fastRate) == ULONG_MAX) + return 0.0; + else { + return (double) this->settings.samplerate.limits->recordLengths[this->settings.recordLengthId] / bestSamplerate; + } + } /// \brief Enables/disables filtering of the given channel. /// \param channel The channel that should be set. @@ -858,28 +1074,6 @@ namespace Hantek { if(channel >= HANTEK_CHANNELS) return Dso::ERROR_PARAMETER; - // Channel filtering commands - switch(this->specification.command.bulk.setFilter) { - case BULK_SETFILTER: { - // SetFilter bulk command for channel filter (used has to be inverted!) - BulkSetFilter *commandSetFilter = static_cast(this->command[BULK_SETFILTER]); - commandSetFilter->setChannel(channel, !used); - this->commandPending[BULK_SETFILTER] = true; - - break; - } - case BULK_BSETFILTER: { - // SetFilter2250 bulk command for channel filter (used has to be inverted!) - BulkSetFilter2250 *commandSetFilter2250 = static_cast(this->command[BULK_BSETFILTER]); - commandSetFilter2250->setChannel(channel, !used); - this->commandPending[BULK_BSETFILTER] = true; - - break; - } - default: - return Dso::ERROR_UNSUPPORTED; - } - // Update settings this->settings.voltage[channel].used = used; unsigned int channelCount = 0; @@ -887,35 +1081,58 @@ namespace Hantek { if(this->settings.voltage[channelCounter].used) ++channelCount; } - this->settings.usedChannels = channelCount; - // Additional UsedChannels field for all models except DSO-2250 - if(this->specification.command.bulk.setTrigger == BULK_SETTRIGGERANDSAMPLERATE || this->specification.command.bulk.setTrigger == BULK_ESETTRIGGERORSAMPLERATE) { - unsigned char usedChannels = USED_CH1; - - if(this->settings.voltage[1].used) { - if(this->settings.voltage[0].used) - usedChannels = USED_CH1CH2; + // Calculate the UsedChannels field for the command + unsigned char usedChannels = USED_CH1; + + if(this->settings.voltage[1].used) { + if(this->settings.voltage[0].used) { + usedChannels = USED_CH1CH2; + } + else { + // DSO-2250 uses a different value for channel 2 + if(this->specification.command.bulk.setTrigger == BULK_BSETCHANNELS) + usedChannels = BUSED_CH2; else usedChannels = USED_CH2; } - - switch(this->specification.command.bulk.setTrigger) { - case BULK_SETTRIGGERANDSAMPLERATE: { - // SetTriggerAndSamplerate bulk command for trigger source - static_cast(this->command[BULK_SETTRIGGERANDSAMPLERATE])->setUsedChannels(usedChannels); - this->commandPending[BULK_SETTRIGGERANDSAMPLERATE] = true; - break; - } - case BULK_ESETTRIGGERORSAMPLERATE: { - // SetTrigger5200s bulk command for trigger source - static_cast(this->command[BULK_ESETTRIGGERORSAMPLERATE])->setUsedChannels(usedChannels); - this->commandPending[BULK_ESETTRIGGERORSAMPLERATE] = true; - break; - } - default: - break; + } + + switch(this->specification.command.bulk.setTrigger) { + case BULK_SETTRIGGERANDSAMPLERATE: { + // SetTriggerAndSamplerate bulk command for trigger source + static_cast(this->command[BULK_SETTRIGGERANDSAMPLERATE])->setUsedChannels(usedChannels); + this->commandPending[BULK_SETTRIGGERANDSAMPLERATE] = true; + break; + } + case BULK_BSETCHANNELS: { + // SetChannels2250 bulk command for active channels + static_cast(this->command[BULK_BSETCHANNELS])->setUsedChannels(usedChannels); + this->commandPending[BULK_BSETCHANNELS] = true; + + break; + } + case BULK_ESETTRIGGERORSAMPLERATE: { + // SetTrigger5200s bulk command for trigger source + static_cast(this->command[BULK_ESETTRIGGERORSAMPLERATE])->setUsedChannels(usedChannels); + this->commandPending[BULK_ESETTRIGGERORSAMPLERATE] = true; + break; } + default: + break; + } + + // Check if fast rate mode availability changed + bool fastRateChanged = (this->settings.usedChannels <= 1) != (channelCount <= 1); + this->settings.usedChannels = channelCount; + + if(fastRateChanged) { + // 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 = (channelCount <= 1) ? &this->specification.samplerate.multi : &this->specification.samplerate.single; + emit samplerateLimitsChanged((double) this->specification.samplerate.single.base / this->specification.samplerate.single.maxDownsampler, limits->max); + + // Samplerate differs for fast rate mode, recalculate it + this->restoreTargets(); } return Dso::ERROR_NONE; @@ -1166,7 +1383,7 @@ namespace Hantek { switch(this->specification.command.bulk.setPretrigger) { case BULK_SETTRIGGERANDSAMPLERATE: { // Calculate the position value (Start point depending on record length) - unsigned long int position = 0x7ffff - this->specification.recordLengths[this->settings.recordLengthId] + positionSamples; + unsigned long int position = 0x7ffff - this->specification.samplerate.single.recordLengths[this->settings.recordLengthId] + positionSamples; // SetTriggerAndSamplerate bulk command for trigger position static_cast(this->command[BULK_SETTRIGGERANDSAMPLERATE])->setTriggerPosition(position); @@ -1176,7 +1393,7 @@ namespace Hantek { } case BULK_FSETBUFFER: { // Calculate the position values (Inverse, maximum is 0x7ffff) - unsigned long int positionPre = 0x7fffful - this->specification.recordLengths[this->settings.recordLengthId] + positionSamples; + unsigned long int positionPre = 0x7fffful - this->specification.samplerate.single.recordLengths[this->settings.recordLengthId] + positionSamples; unsigned long int positionPost = 0x7fffful - positionSamples; // SetBuffer2250 bulk command for trigger position @@ -1189,7 +1406,7 @@ namespace Hantek { } case BULK_ESETTRIGGERORSAMPLERATE: { // Calculate the position values (Inverse, maximum is 0xffff) - unsigned short int positionPre = 0xffff - this->specification.recordLengths[this->settings.recordLengthId] + positionSamples; + unsigned short int positionPre = 0xffff - this->specification.samplerate.single.recordLengths[this->settings.recordLengthId] + positionSamples; unsigned short int positionPost = 0xffff - positionSamples; // SetBuffer5200 bulk command for trigger position diff --git a/openhantek/src/hantek/control.h b/openhantek/src/hantek/control.h index 1ebfddb..268e0d9 100644 --- a/openhantek/src/hantek/control.h +++ b/openhantek/src/hantek/control.h @@ -6,7 +6,7 @@ // // Copyright (C) 2008, 2009 Oleg Khudyakov // prcoder@potrebitel.ru -// Copyright (C) 2010, 2011 Oliver Haag +// Copyright (C) 2010 - 2012 Oliver Haag // oliver.haag@gmail.com // // This program is free software: you can redistribute it and/or modify it @@ -56,7 +56,7 @@ namespace Hantek { /// \struct ControlSpecificationCommandsBulk hantek/control.h /// \brief Stores the bulk command codes used for this device. struct ControlSpecificationCommandsBulk { - BulkCode setFilter; ///< Command for setting used channels + BulkCode setChannels; ///< Command for setting used channels BulkCode setSamplerate; ///< Command for samplerate settings BulkCode setGain; ///< Command for gain settings (Usually in combination with CONTROL_SETRELAYS) BulkCode setRecordLength; ///< Command for buffer settings @@ -95,6 +95,8 @@ namespace Hantek { struct ControlSamplerateLimits { unsigned long int base; ///< The base for sample rate calculations unsigned long int max; ///< The maximum sample rate + unsigned long int maxDownsampler; ///< The maximum downsampling ratio + QList recordLengths; ///< Available record lengths, ULONG_MAX means rolling }; ////////////////////////////////////////////////////////////////////////////// @@ -114,7 +116,6 @@ namespace Hantek { // Limits ControlSpecificationSamplerate samplerate; ///< The samplerate specifications - QList recordLengths; ///< Available record lengths, ULONG_MAX means rolling QList bufferDividers; ///< Samplerate dividers for record lengths QList gainSteps; ///< Available voltage steps in V/screenheight unsigned char sampleSize; ///< Number of bits per sample @@ -129,12 +130,22 @@ namespace Hantek { }; ////////////////////////////////////////////////////////////////////////////// + /// \struct ControlSettingsSamplerateTarget hantek/control.h + /// \brief Stores the target samplerate settings of the device. + struct ControlSettingsSamplerateTarget { + double samplerate; ///< The target samplerate set via setSamplerate + double duration; ///< The target record time set via setRecordTime + bool samplerateSet; ///< true means samplerate was set last, false duration + }; + + ////////////////////////////////////////////////////////////////////////////// /// \struct ControlSettingsSamplerate hantek/control.h /// \brief Stores the current samplerate settings of the device. struct ControlSettingsSamplerate { + ControlSettingsSamplerateTarget target; ///< The target samplerate values ControlSamplerateLimits *limits; ///< The samplerate limits - unsigned long int downsampling; ///< The variable downsampling factor - unsigned long int current; ///< The current samplerate + unsigned long int downsampler; ///< The variable downsampling factor + double current; ///< The current samplerate }; ////////////////////////////////////////////////////////////////////////////// @@ -182,6 +193,9 @@ namespace Hantek { ~Control(); unsigned int getChannelCount(); + QList *getAvailableRecordLengths(); + double getMinSamplerate(); + double getMaxSamplerate(); protected: void run(); @@ -189,7 +203,10 @@ namespace Hantek { unsigned long int calculateTriggerPoint(unsigned long int value); int getCaptureState(); int getSamples(bool process); + double getBestSamplerate(double samplerate, bool fastRate = false, bool maximum = false, unsigned long int *downsampler = 0); unsigned long int updateRecordLength(unsigned long int size); + unsigned long int updateSamplerate(unsigned long int downsampler, bool fastRate); + void restoreTargets(); // Communication with device Device *device; ///< The USB device for the oscilloscope @@ -206,14 +223,16 @@ namespace Hantek { // Results QList samples; ///< Sample data arrays - QList samplesSize; ///< Number of samples data array + QList samplesSize; ///< Number of samples data array + unsigned long int previousSampleCount; ///< The expected total number of samples at the last check before sampling started QMutex samplesMutex; ///< Mutex for the sample data public slots: virtual void connectDevice(); - unsigned long int setSamplerate(unsigned long int samplerate = 0); unsigned long int setRecordLength(unsigned long int size); + double setSamplerate(double samplerate = 0.0); + double setRecordTime(double duration = 0.0); int setChannelUsed(unsigned int channel, bool used); int setCoupling(unsigned int channel, Dso::Coupling coupling); diff --git a/openhantek/src/hantek/device.cpp b/openhantek/src/hantek/device.cpp index 351fb46..953df9c 100644 --- a/openhantek/src/hantek/device.cpp +++ b/openhantek/src/hantek/device.cpp @@ -5,7 +5,7 @@ // // Copyright (C) 2008, 2009 Oleg Khudyakov // prcoder@potrebitel.ru -// Copyright (C) 2010 Oliver Haag +// Copyright (C) 2010 - 2012 Oliver Haag // oliver.haag@gmail.com // // This program is free software: you can redistribute it and/or modify it @@ -274,20 +274,21 @@ namespace Hantek { } #if LIBUSB_VERSION != 0 - /// \brief Bulk transfer to the oscilloscope. + /// \brief Bulk transfer to/from the oscilloscope. /// \param endpoint Endpoint number, also sets the direction of the transfer. /// \param data Buffer for the sent/recieved data. /// \param length The length of the packet. /// \param attempts The number of attempts, that are done on timeouts. + /// \param timeout The timeout in ms. /// \return Number of transferred bytes on success, libusb error code on error. - int Device::bulkTransfer(unsigned char endpoint, unsigned char *data, unsigned int length, int attempts) { + int Device::bulkTransfer(unsigned char endpoint, unsigned char *data, unsigned long int length, int attempts, unsigned int timeout) { if(!this->handle) return LIBUSB_ERROR_NO_DEVICE; int errorCode = LIBUSB_ERROR_TIMEOUT; int transferred; for(int attempt = 0; (attempt < attempts || attempts == -1) && errorCode == LIBUSB_ERROR_TIMEOUT; ++attempt) - errorCode = libusb_bulk_transfer(this->handle, endpoint, data, length, &transferred, HANTEK_TIMEOUT); + errorCode = libusb_bulk_transfer(this->handle, endpoint, data, length, &transferred, timeout); if(errorCode == LIBUSB_ERROR_NO_DEVICE) this->disconnect(); @@ -303,7 +304,7 @@ namespace Hantek { /// \param length The length of the packet. /// \param attempts The number of attempts, that are done on timeouts. /// \return Number of sent bytes on success, libusb error code on error. - int Device::bulkWrite(unsigned char *data, unsigned int length, int attempts) { + int Device::bulkWrite(unsigned char *data, unsigned long int length, int attempts) { if(!this->handle) return LIBUSB_ERROR_NO_DEVICE; @@ -330,7 +331,7 @@ namespace Hantek { /// \param length The length of the packet. /// \param attempts The number of attempts, that are done on timeouts. /// \return Number of received bytes on success, libusb error code on error. - int Device::bulkRead(unsigned char *data, unsigned int length, int attempts) { + int Device::bulkRead(unsigned char *data, unsigned long int length, int attempts) { if(!this->handle) return LIBUSB_ERROR_NO_DEVICE; @@ -374,7 +375,7 @@ namespace Hantek { /// \param length The length of data contained in the packets. /// \param attempts The number of attempts, that are done on timeouts. /// \return Number of received bytes on success, libusb error code on error. - int Device::bulkReadMulti(unsigned char *data, unsigned int length, int attempts) { + int Device::bulkReadMulti(unsigned char *data, unsigned long int length, int attempts) { if(!this->handle) return LIBUSB_ERROR_NO_DEVICE; @@ -385,14 +386,14 @@ namespace Hantek { return errorCode; errorCode = this->inPacketLength; - unsigned int packet, received = 0; + unsigned long int packet, received = 0; for(packet = 0; received < length && errorCode == this->inPacketLength; ++packet) { #if LIBUSB_VERSION == 0 errorCode = LIBUSB_ERROR_TIMEOUT; for(int attempt = 0; (attempt < attempts || attempts == -1) && errorCode == LIBUSB_ERROR_TIMEOUT; ++attempt) - errorCode = usb_bulk_read(this->handle, HANTEK_EP_IN, (char *) data + packet * this->inPacketLength, qMin(length - received, (unsigned int) this->inPacketLength), HANTEK_TIMEOUT); + errorCode = usb_bulk_read(this->handle, HANTEK_EP_IN, (char *) data + packet * this->inPacketLength, qMin(length - received, (unsigned long int) this->inPacketLength), HANTEK_TIMEOUT); #else - errorCode = this->bulkTransfer(HANTEK_EP_IN, data + packet * this->inPacketLength, qMin(length - received, (unsigned int) this->inPacketLength), attempts); + errorCode = this->bulkTransfer(HANTEK_EP_IN, data + packet * this->inPacketLength, qMin(length - received, (unsigned long int) this->inPacketLength), attempts, HANTEK_TIMEOUT_MULTI); #endif if(errorCode > 0) received += errorCode; @@ -413,7 +414,7 @@ namespace Hantek { /// \param index The index field of the packet. /// \param attempts The number of attempts, that are done on timeouts. /// \return Number of transferred bytes on success, libusb error code on error. - int Device::controlTransfer(unsigned char type, unsigned char request, unsigned char *data, unsigned int length, int value, int index, int attempts) { + int Device::controlTransfer(unsigned char type, unsigned char request, unsigned char *data, unsigned long int length, int value, int index, int attempts) { if(!this->handle) return LIBUSB_ERROR_NO_DEVICE; @@ -438,7 +439,7 @@ namespace Hantek { /// \param index The index field of the packet. /// \param attempts The number of attempts, that are done on timeouts. /// \return Number of sent bytes on success, libusb error code on error. - int Device::controlWrite(unsigned char request, unsigned char *data, unsigned int length, int value, int index, int attempts) { + int Device::controlWrite(unsigned char request, unsigned char *data, unsigned long int length, int value, int index, int attempts) { if(!this->handle) return LIBUSB_ERROR_NO_DEVICE; @@ -453,7 +454,7 @@ namespace Hantek { /// \param index The index field of the packet. /// \param attempts The number of attempts, that are done on timeouts. /// \return Number of received bytes on success, libusb error code on error. - int Device::controlRead(unsigned char request, unsigned char *data, unsigned int length, int value, int index, int attempts) { + int Device::controlRead(unsigned char request, unsigned char *data, unsigned long int length, int value, int index, int attempts) { if(!this->handle) return LIBUSB_ERROR_NO_DEVICE; diff --git a/openhantek/src/hantek/device.h b/openhantek/src/hantek/device.h index 598cfdc..633bb20 100644 --- a/openhantek/src/hantek/device.h +++ b/openhantek/src/hantek/device.h @@ -4,10 +4,8 @@ /// \file hantek/device.h /// \brief Declares the Hantek::Device class. // -// Copyright (C) 2008, 2009 Oleg Khudyakov -// prcoder@potrebitel.ru -// Copyright (C) 2010 Oliver Haag -// oliver.haag@gmail.com +/// \copyright (c) 2008, 2009 Oleg Khudyakov +/// \copyright (c) 2010 - 2012 Oliver Haag // // 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 @@ -62,17 +60,17 @@ namespace Hantek { // Various methods to handle USB transfers #if LIBUSB_VERSION != 0 - int bulkTransfer(unsigned char endpoint, unsigned char *data, unsigned int length, int attempts = HANTEK_ATTEMPTS_DEFAULT); + int bulkTransfer(unsigned char endpoint, unsigned char *data, unsigned long int length, int attempts = HANTEK_ATTEMPTS, unsigned int timeout = HANTEK_TIMEOUT); #endif - int bulkWrite(unsigned char *data, unsigned int length, int attempts = HANTEK_ATTEMPTS_DEFAULT); - int bulkRead(unsigned char *data, unsigned int length, int attempts = HANTEK_ATTEMPTS_DEFAULT); + int bulkWrite(unsigned char *data, unsigned long int length, int attempts = HANTEK_ATTEMPTS); + int bulkRead(unsigned char *data, unsigned long int length, int attempts = HANTEK_ATTEMPTS); - int bulkCommand(Helper::DataArray *command, int attempts = HANTEK_ATTEMPTS_DEFAULT); - int bulkReadMulti(unsigned char *data, unsigned int length, int attempts = HANTEK_ATTEMPTS_DEFAULT); + int bulkCommand(Helper::DataArray *command, int attempts = HANTEK_ATTEMPTS); + int bulkReadMulti(unsigned char *data, unsigned long int length, int attempts = HANTEK_ATTEMPTS_MULTI); - int controlTransfer(unsigned char type, unsigned char request, unsigned char *data, unsigned int length, int value, int index, int attempts = HANTEK_ATTEMPTS_DEFAULT); - int controlWrite(unsigned char request, unsigned char *data, unsigned int length, int value = 0, int index = 0, int attempts = HANTEK_ATTEMPTS_DEFAULT); - int controlRead(unsigned char request, unsigned char *data, unsigned int length, int value = 0, int index = 0, int attempts = HANTEK_ATTEMPTS_DEFAULT); + int controlTransfer(unsigned char type, unsigned char request, unsigned char *data, unsigned long int length, int value, int index, int attempts = HANTEK_ATTEMPTS); + int controlWrite(unsigned char request, unsigned char *data, unsigned long int length, int value = 0, int index = 0, int attempts = HANTEK_ATTEMPTS); + int controlRead(unsigned char request, unsigned char *data, unsigned long int length, int value = 0, int index = 0, int attempts = HANTEK_ATTEMPTS); int getConnectionSpeed(); Model getModel(); diff --git a/openhantek/src/hantek/types.cpp b/openhantek/src/hantek/types.cpp index ffb197d..11a9081 100644 --- a/openhantek/src/hantek/types.cpp +++ b/openhantek/src/hantek/types.cpp @@ -101,24 +101,26 @@ namespace Hantek { } /// \brief Sets the data bytes to the specified values. - /// \param samplerateSlow The SamplerateSlow value. + /// \param downsampler The Downsampler value. /// \param triggerPosition The trigger position value. /// \param triggerSource The trigger source id (Tsr1). /// \param recordLength The record length id (Tsr1). - /// \param samplerateFast The samplerateFast value (Tsr1). + /// \param samplerateId The samplerateId value (Tsr1). + /// \param downsamplingMode The downsamplingMode value (Tsr1). /// \param usedChannels The enabled channels (Tsr2). /// \param fastRate The fastRate state (Tsr2). /// \param triggerSlope The triggerSlope value (Tsr2). - BulkSetTriggerAndSamplerate::BulkSetTriggerAndSamplerate(unsigned short int samplerateSlow, unsigned long int triggerPosition, unsigned char triggerSource, unsigned char recordLength, unsigned char samplerateFast, unsigned char usedChannels, bool fastRate, unsigned char triggerSlope) : Helper::DataArray(12) { + BulkSetTriggerAndSamplerate::BulkSetTriggerAndSamplerate(unsigned short int downsampler, unsigned long int triggerPosition, unsigned char triggerSource, unsigned char recordLength, unsigned char samplerateId, bool downsamplingMode, unsigned char usedChannels, bool fastRate, unsigned char triggerSlope) : Helper::DataArray(12) { this->init(); this->setTriggerSource(triggerSource); this->setRecordLength(recordLength); - this->setSamplerateFast(samplerateFast); + this->setSamplerateId(samplerateId); + this->setDownsamplingMode(downsamplingMode); this->setUsedChannels(usedChannels); this->setFastRate(fastRate); this->setTriggerSlope(triggerSlope); - this->setSamplerateSlow(samplerateSlow); + this->setDownsampler(downsampler); this->setTriggerPosition(triggerPosition); } @@ -146,16 +148,28 @@ namespace Hantek { ((Tsr1Bits *) &(this->array[2]))->recordLength = value; } - /// \brief Get the samplerateFast value in Tsr1Bits. - /// \return The samplerateFast value. - unsigned char BulkSetTriggerAndSamplerate::getSamplerateFast() { - return ((Tsr1Bits *) &(this->array[2]))->samplerateFast; + /// \brief Get the samplerateId value in Tsr1Bits. + /// \return The samplerateId value. + unsigned char BulkSetTriggerAndSamplerate::getSamplerateId() { + return ((Tsr1Bits *) &(this->array[2]))->samplerateId; + } + + /// \brief Set the samplerateId in Tsr1Bits to the given value. + /// \param value The new samplerateId value. + void BulkSetTriggerAndSamplerate::setSamplerateId(unsigned char value) { + ((Tsr1Bits *) &(this->array[2]))->samplerateId = value; } - /// \brief Set the samplerateFast in Tsr1Bits to the given value. - /// \param value The new samplerateFast value. - void BulkSetTriggerAndSamplerate::setSamplerateFast(unsigned char value) { - ((Tsr1Bits *) &(this->array[2]))->samplerateFast = value; + /// \brief Get the downsamplerMode value in Tsr1Bits. + /// \return The downsamplerMode value. + bool BulkSetTriggerAndSamplerate::getDownsamplingMode() { + return ((Tsr1Bits *) &(this->array[2]))->downsamplingMode == 1; + } + + /// \brief Set the downsamplerMode in Tsr1Bits to the given value. + /// \param downsampling The new downsamplerMode value. + void BulkSetTriggerAndSamplerate::setDownsamplingMode(bool downsampling) { + ((Tsr1Bits *) &(this->array[2]))->downsamplingMode = downsampling ? 1 : 0; } /// \brief Get the usedChannels value in Tsr2Bits. @@ -194,17 +208,17 @@ namespace Hantek { ((Tsr2Bits *) &(this->array[3]))->triggerSlope = slope; } - /// \brief Get the SamplerateSlow value. - /// \return The SamplerateSlow value. - unsigned short int BulkSetTriggerAndSamplerate::getSamplerateSlow() { + /// \brief Get the Downsampler value. + /// \return The Downsampler value. + unsigned short int BulkSetTriggerAndSamplerate::getDownsampler() { return (unsigned short int) this->array[4] | ((unsigned short int) this->array[5] << 8); } - /// \brief Set the SamplerateSlow to the given value. - /// \param samplerate The new SamplerateSlow value. - void BulkSetTriggerAndSamplerate::setSamplerateSlow(unsigned short int samplerate) { - this->array[4] = (unsigned char) samplerate; - this->array[5] = (unsigned char) (samplerate >> 8); + /// \brief Set the Downsampler to the given value. + /// \param downsampler The new Downsampler value. + void BulkSetTriggerAndSamplerate::setDownsampler(unsigned short int downsampler) { + this->array[4] = (unsigned char) downsampler; + this->array[5] = (unsigned char) (downsampler >> 8); } /// \brief Get the TriggerPosition value. @@ -328,8 +342,6 @@ namespace Hantek { /// \brief Initialize the array to the needed values. void BulkSetGain::init() { this->array[0] = BULK_SETGAIN; - this->array[1] = 0x0f; - ((GainBits *) &(this->array[2]))->reserved = 3; } @@ -363,7 +375,6 @@ namespace Hantek { /// \brief Initialize the array to the needed values. void BulkSetLogicalData::init() { this->array[0] = BULK_SETLOGICALDATA; - this->array[1] = 0x0f; } @@ -378,45 +389,33 @@ namespace Hantek { ////////////////////////////////////////////////////////////////////////////// // class BulkSetFilter2250 /// \brief Sets the data array to needed values. - BulkSetFilter2250::BulkSetFilter2250() : Helper::DataArray(4) { + BulkSetChannels2250::BulkSetChannels2250() : Helper::DataArray(4) { this->init(); } /// \brief Sets the used channels. - /// \param channel1 true if channel 1 is filtered. - /// \param channel2 true if channel 2 is filtered. - BulkSetFilter2250::BulkSetFilter2250(bool channel1, bool channel2) : Helper::DataArray(4) { + /// \param usedChannels The UsedChannels value. + BulkSetChannels2250::BulkSetChannels2250(unsigned char usedChannels) : Helper::DataArray(4) { this->init(); - this->setChannel(0, channel1); - this->setChannel(1, channel2); + this->setUsedChannels(usedChannels); } - /// \brief Gets the filtering state of one channel. - /// \param channel The channel whose filtering state should be returned. - /// \return The filtering state of the channel. - bool BulkSetFilter2250::getChannel(unsigned int channel) { - FilterBits *filterBits = (FilterBits *) &(this->array[2]); - if(channel == 0) - return filterBits->channel1 == 1; - else - return filterBits->channel2 == 1; + /// \brief Get the UsedChannels value + /// \return The UsedChannels value. + unsigned char BulkSetChannels2250::getUsedChannels() { + return this->array[2]; } - /// \brief Enables/disables filtering of one channel. - /// \param channel The channel that should be set. - /// \param filtered true if the channel should be filtered. - void BulkSetFilter2250::setChannel(unsigned int channel, bool filtered) { - FilterBits *filterBits = (FilterBits *) &(this->array[2]); - if(channel == 0) - filterBits->channel1 = filtered ? 1 : 0; - else - filterBits->channel2 = filtered ? 1 : 0; + /// \brief Set the UsedChannels to the given value. + /// \param value The new UsedChannels value. + void BulkSetChannels2250::setUsedChannels(unsigned char value) { + this->array[2] = value; } /// \brief Initialize the array to the needed values. - void BulkSetFilter2250::init() { - this->array[0] = BULK_BSETFILTER; + void BulkSetChannels2250::init() { + this->array[0] = BULK_BSETCHANNELS; } diff --git a/openhantek/src/hantek/types.h b/openhantek/src/hantek/types.h index 08a77e7..45fd443 100644 --- a/openhantek/src/hantek/types.h +++ b/openhantek/src/hantek/types.h @@ -36,7 +36,9 @@ #define HANTEK_EP_OUT 0x02 ///< OUT Endpoint for bulk transfers #define HANTEK_EP_IN 0x86 ///< IN Endpoint for bulk transfers #define HANTEK_TIMEOUT 500 ///< Timeout for USB transfers in ms -#define HANTEK_ATTEMPTS_DEFAULT 3 ///< The number of transfer attempts +#define HANTEK_TIMEOUT_MULTI 10 ///< Timeout for multi packet USB transfers in ms +#define HANTEK_ATTEMPTS 3 ///< The number of transfer attempts +#define HANTEK_ATTEMPTS_MULTI 1 ///< The number of multi packet transfer attempts #define HANTEK_CHANNELS 2 ///< Number of physical channels #define HANTEK_SPECIAL_CHANNELS 2 ///< Number of special channels @@ -57,7 +59,7 @@ namespace Hantek { /// /// /// - /// + /// /// /// /// @@ -67,6 +69,8 @@ namespace Hantek { /// ///
0x000x0f0x00FilterBits0x000x00
///

+ ///

+ /// This command is used by the official %Hantek software, but doesn't seem to be used by the device. ///


BULK_SETFILTER, @@ -79,8 +83,8 @@ namespace Hantek { /// 0x00 /// Tsr1Bits /// Tsr2Bits - /// SamplerateSlow[0] - /// SamplerateSlow[1] + /// Downsampler[0] + /// Downsampler[1] /// /// /// @@ -95,11 +99,20 @@ namespace Hantek { ///
///

///

- /// The samplerate is set relative to the maximum sample rate by a divider that is set in Tsr1Bits.samplerateFast and the 16-bit value in the two SamplerateSlow bytes.
- /// Without using fast rate mode, the samplerate is:
- /// Samplerate = SamplerateMax / (1comp(SamplerateSlow) * 2 + Tsr1Bits.samplerateFast)
- /// SamplerateMax is 50 MHz for the DSO-2090.
- /// When using fast rate mode the resulting samplerate is twice (For DSO-2150 three times) as fast, when using the large buffer it is half as fast. When Tsr1Bits.recordLength is 0 (Roll mode) the sampling rate is divided by 1000. Setting Tsr1Bits.samplerateFast to 0 doesn't work, the result will be the same as Tsr1Bits.samplerateFast = 1. SamplerateSlow can't be used together with fast rate mode, the result is always the the same as SlowValue = 0. + /// The samplerate is set relative to the base samplerate by a divider or to a maximum samplerate.
+ /// This divider is set by Tsr1Bits.samplerateId for values up to 5 with to the following values: + /// + /// + /// + /// + /// + /// + /// + ///
Tsr1Bits.samplerateId0123
SamplerateMaxBaseBase / 2Base / 5
+ /// For higher divider values, the value can be set using the 16-bit value in the two Downsampler bytes. The value of Downsampler is given by:
+ /// Downsampler = 1comp((Base / Samplerate / 2) - 2)
+ /// The Base samplerate is 50 MS/s for the DSO-2090 and DSO-2150. The Max samplerate is also 50 MS/s for the DSO-2090 and 75 MS/s for the DSO-2150.
+ /// When using fast rate mode the Base and Max samplerate is twice as fast. When Tsr1Bits.recordLength is 0 (Roll mode) the sampling rate is divided by 1000. ///

///

/// The TriggerPosition sets the position of the pretrigger in samples. The left side (0 %) is 0x77660 when using the small buffer and 0x78000 when using the large buffer. @@ -225,7 +238,7 @@ namespace Hantek { /// /// /// - /// + /// /// /// /// @@ -245,7 +258,7 @@ namespace Hantek { ///
0x070x0f0x00GainBits0x000x00
/// /// - /// + /// /// /// /// @@ -293,20 +306,20 @@ namespace Hantek { ///


BULK_AUNKNOWN, - /// BulkSetFilter2250 [::MODEL_DSO2250] + /// BulkSetChannels2250 [::MODEL_DSO2250] ///

/// This command sets the activated channels for the DSO-2250: ///

0x080x0f0x00Data | 0x010x000x00
/// /// /// - /// + /// /// /// ///
0x0b0x00FilterBitsBUsedChannels0x00
///

///


- BULK_BSETFILTER, + BULK_BSETCHANNELS, /// BulkSetTrigger2250 [::MODEL_DSO2250] ///

@@ -314,10 +327,10 @@ namespace Hantek { /// /// /// - /// + /// /// /// - /// + /// /// /// /// @@ -340,9 +353,10 @@ namespace Hantek { ///
0x0c0x0f0x00CTriggerBits0x000x020x000x000x000x00
///

///

- /// The values are similar to the ones used with ::BULK_SETTRIGGERANDSAMPLERATE. The formula is a bit different here:
+ /// The samplerate is set relative to the maximum sample rate by a divider that is set in SamplerateFast and the 16-bit value in the two SamplerateSlow bytes.
+ /// Without using fast rate mode, the samplerate is:
/// Samplerate = SamplerateMax / (2comp(SamplerateSlow) * 2 + 4 - SamplerateFast)
- /// SamplerateMax is 100 MS/s for the DSO-5200 in default configuration and 250 MS/s in fast rate mode though, the modifications regarding record length are the the same that apply for the DSO-2090. + /// SamplerateBase is 100 MS/s for the DSO-5200 in normal mode and 200 MS/s in fast rate mode, the modifications regarding record length are the the same that apply for the DSO-2090. The maximum samplerate is 125 MS/s in normal mode and 250 MS/s in fast rate mode, and is reached by setting SamplerateSlow = 0 and SamplerateFast = 4. ///

///


BULK_CSETTRIGGERORSAMPLERATE, @@ -397,17 +411,17 @@ namespace Hantek { /// 0x00 /// ESamplerateBits /// 0x00 - /// SamplerateSlow[0] - /// SamplerateSlow[1] + /// Samplerate[0] + /// Samplerate[1] /// 0x00 /// 0x00 /// /// ///

///

- /// The values are similar to the ones used with ::BULK_SETTRIGGERANDSAMPLERATE. The formula is a bit different here:
- /// Samplerate = SamplerateMax / (2comp(SamplerateSlow) * 2 + ESamplerateBits.samplerateFast)
- /// SamplerateMax is 100 MS/s for the DSO-2250 in default configuration and 250 MS/s in fast rate mode though, the modifications regarding record length are the the same that apply for the DSO-2090. + /// The downsampler can be activated by setting ESamplerateBits.downsampling = 1. If this is the case, the value of Downsampler is given by:
+ /// Downsampler = 1comp((Base / Samplerate) - 2)
+ /// Base is 100 MS/s for the DSO-2250 in standard mode and 200 MS/s in fast rate mode, the modifications regarding record length are the the same that apply for the DSO-2090. The maximum samplerate is 125 MS/s in standard mode and 250 MS/s in fast rate mode and is achieved by setting ESamplerateBits.downsampling = 0. ///

///


/// BulkSetTrigger5200 [::MODEL_DSO5200, ::MODEL_DSO5200A] @@ -436,25 +450,25 @@ namespace Hantek { /// /// 0x0f /// 0x00 - /// TriggerPositionPre[0] - /// TriggerPositionPre[1] - /// FBuffer1Bits + /// TriggerPositionPost[0] + /// TriggerPositionPost[1] + /// TriggerPositionPost[2] /// 0x00 /// /// /// /// - /// - /// - /// + /// + /// + /// + /// /// - /// /// /// ///
TriggerPositionPost[0]TriggerPositionPost[1]FBuffer1BitsTriggerPositionPre[0]TriggerPositionPre[1]TriggerPositionPre[2]0x000x00FBuffer2Bits0x00
///

///

- /// The TriggerPositionPre and TriggerPositionPost values set the pretrigger position. Both values have a range from 0xd7ff (0xc7ff for 14 kiS buffer) to 0xfffe. On the left side (0 %) the TriggerPositionPre value is minimal, on the right side (100 %) it is maximal. The TriggerPositionPost value is maximal for 0 % and minimal for 100%. + /// The TriggerPositionPre and TriggerPositionPost values set the pretrigger position. Both values have a range from 0x7d800 (0x00000 for 512 kiS buffer) to 0x7ffff. On the left side (0 %) the TriggerPositionPre value is minimal, on the right side (100 %) it is maximal. The TriggerPositionPost value is maximal for 0 % and minimal for 100%. ///

///


BULK_FSETBUFFER, @@ -703,6 +717,16 @@ namespace Hantek { }; ////////////////////////////////////////////////////////////////////////////// + /// \enum BUsedChannels hantek/types.h + /// \brief The enabled channels for the DSO-2250. + enum BUsedChannels { + BUSED_CH1, ///< Only channel 1 is activated + BUSED_NONE, ///< No channels are activated + BUSED_CH1CH2, ///< Channel 1 and 2 are both activated + BUSED_CH2 ///< Only channel 2 is activated + }; + + ////////////////////////////////////////////////////////////////////////////// /// \enum DTriggerPositionUsed hantek/types.h /// \brief The trigger position states for the 0x0d command. enum DTriggerPositionUsed { @@ -711,14 +735,6 @@ namespace Hantek { }; ////////////////////////////////////////////////////////////////////////////// - /// \enum FTriggerPositionUsed hantek/types.h - /// \brief The trigger position states for the 0x0f command. - enum FTriggerPositionUsed { - FTRIGGERPOSITION_OFF = 0, ///< Used for Roll mode - FTRIGGERPOSITION_ON = 3 ///< Used for normal operation - }; - - ////////////////////////////////////////////////////////////////////////////// /// \struct FilterBits hantek/types.h /// \brief The bits for BULK_SETFILTER. struct FilterBits { @@ -743,7 +759,8 @@ namespace Hantek { struct Tsr1Bits { unsigned char triggerSource:2; ///< The trigger source, see Hantek::TriggerSource unsigned char recordLength:3; ///< See ::RecordLengthId - unsigned char samplerateFast:3; ///< samplerate value for fast sampling rates + unsigned char samplerateId:2; ///< Samplerate ID when downsampler is disabled + unsigned char downsamplingMode:1; ///< true, if Downsampler is used }; ////////////////////////////////////////////////////////////////////////////// @@ -795,23 +812,6 @@ namespace Hantek { }; ////////////////////////////////////////////////////////////////////////////// - /// \struct FBuffer1Bits hantek/types.h - /// \brief Buffer mode bits for 0x0f command (Byte 1). - struct FBuffer1Bits { - unsigned char triggerPositionUsed:2; ///< See ::DTriggerPositionUsed - unsigned char largeBuffer:1; ///< false, if ::RecordLengthId is ::RECORDLENGTHID_LARGE - unsigned char reserved:5; ///< Unused bits - }; - - ////////////////////////////////////////////////////////////////////////////// - /// \struct FBuffer2Bits hantek/types.h - /// \brief Buffer mode bits for 0x0f command (Byte 2). - struct FBuffer2Bits { - unsigned char reserved:7; ///< Unused bits - unsigned char slowBuffer:1; ///< false, if ::RecordLengthId is ::RECORDLENGTHID_SMALL - }; - - ////////////////////////////////////////////////////////////////////////////// /// \class BulkSetFilter hantek/types.h /// \brief The BULK_SETFILTER builder. class BulkSetFilter : public Helper::DataArray { @@ -834,22 +834,24 @@ namespace Hantek { class BulkSetTriggerAndSamplerate : public Helper::DataArray { public: BulkSetTriggerAndSamplerate(); - BulkSetTriggerAndSamplerate(unsigned short int samplerateSlow, unsigned long int triggerPosition, unsigned char triggerSource = 0, unsigned char recordLength = 0, unsigned char samplerateFast = 0, unsigned char usedChannels = 0, bool fastRate = false, unsigned char triggerSlope = 0); + BulkSetTriggerAndSamplerate(unsigned short int downsampler, unsigned long int triggerPosition, unsigned char triggerSource = 0, unsigned char recordLength = 0, unsigned char samplerateId = 0, bool downsamplingMode = true, unsigned char usedChannels = 0, bool fastRate = false, unsigned char triggerSlope = 0); unsigned char getTriggerSource(); void setTriggerSource(unsigned char value); unsigned char getRecordLength(); void setRecordLength(unsigned char value); - unsigned char getSamplerateFast(); - void setSamplerateFast(unsigned char value); + unsigned char getSamplerateId(); + void setSamplerateId(unsigned char value); + bool getDownsamplingMode(); + void setDownsamplingMode(bool downsampling); unsigned char getUsedChannels(); void setUsedChannels(unsigned char value); bool getFastRate(); void setFastRate(bool fastRate); unsigned char getTriggerSlope(); void setTriggerSlope(unsigned char slope); - unsigned short int getSamplerateSlow(); - void setSamplerateSlow(unsigned short int samplerate); + unsigned short int getDownsampler(); + void setDownsampler(unsigned short int downsampler); unsigned long int getTriggerPosition(); void setTriggerPosition(unsigned long int position); @@ -947,15 +949,15 @@ namespace Hantek { }; ////////////////////////////////////////////////////////////////////////////// - /// \class BulkSetFilter2250 hantek/types.h + /// \class BulkSetChannels2250 hantek/types.h /// \brief The DSO-2250 BULK_BSETFILTER builder. - class BulkSetFilter2250 : public Helper::DataArray { + class BulkSetChannels2250 : public Helper::DataArray { public: - BulkSetFilter2250(); - BulkSetFilter2250(bool channel1, bool channel2); + BulkSetChannels2250(); + BulkSetChannels2250(unsigned char usedChannels); - bool getChannel(unsigned int channel); - void setChannel(unsigned int channel, bool filtered); + unsigned char getUsedChannels(); + void setUsedChannels(unsigned char value); private: void init(); diff --git a/openhantek/src/openhantek.cpp b/openhantek/src/openhantek.cpp index 4635d7c..8a76ff1 100644 --- a/openhantek/src/openhantek.cpp +++ b/openhantek/src/openhantek.cpp @@ -92,61 +92,12 @@ OpenHantekMainWindow::OpenHantekMainWindow(QWidget *parent, Qt::WindowFlags flag this->settings->options.window.position = this->pos(); this->settings->options.window.size = this->size(); - // Connect general signals - connect(this, SIGNAL(settingsChanged()), this, SLOT(applySettings())); - //connect(this->dsoWidget, SIGNAL(stopped()), this, SLOT(stopped())); - connect(this->dsoControl, SIGNAL(statusMessage(QString, int)), this->statusBar(), SLOT(showMessage(QString, int))); - connect(this->dsoControl, SIGNAL(samplesAvailable(const QList *, const QList *, double, QMutex *)), this->dataAnalyzer, SLOT(analyze(const QList *, const QList *, double, QMutex *))); - - // Connect signals to DSO controller and widget - //connect(this->horizontalDock, SIGNAL(formatChanged(HorizontalFormat)), this->dsoWidget, SLOT(horizontalFormatChanged(HorizontalFormat))); - connect(this->horizontalDock, SIGNAL(timebaseChanged(double)), this, SLOT(updateTimebase())); - connect(this->horizontalDock, SIGNAL(timebaseChanged(double)), this->dsoWidget, SLOT(updateTimebase())); - connect(this->horizontalDock, SIGNAL(frequencybaseChanged(double)), this->dsoWidget, SLOT(updateFrequencybase())); - - connect(this->triggerDock, SIGNAL(modeChanged(Dso::TriggerMode)), this->dsoControl, SLOT(setTriggerMode(Dso::TriggerMode))); - connect(this->triggerDock, SIGNAL(modeChanged(Dso::TriggerMode)), this->dsoWidget, SLOT(updateTriggerMode())); - connect(this->triggerDock, SIGNAL(sourceChanged(bool, unsigned int)), this->dsoControl, SLOT(setTriggerSource(bool, unsigned int))); - connect(this->triggerDock, SIGNAL(sourceChanged(bool, unsigned int)), this->dsoWidget, SLOT(updateTriggerSource())); - connect(this->triggerDock, SIGNAL(slopeChanged(Dso::Slope)), this->dsoControl, SLOT(setTriggerSlope(Dso::Slope))); - connect(this->triggerDock, SIGNAL(slopeChanged(Dso::Slope)), this->dsoWidget, SLOT(updateTriggerSlope())); - connect(this->dsoWidget, SIGNAL(triggerPositionChanged(double)), this->dsoControl, SLOT(setPretriggerPosition(double))); - connect(this->dsoWidget, SIGNAL(triggerLevelChanged(unsigned int, double)), this->dsoControl, SLOT(setTriggerLevel(unsigned int, double))); - - connect(this->voltageDock, SIGNAL(usedChanged(unsigned int, bool)), this, SLOT(updateUsed(unsigned int))); - connect(this->voltageDock, SIGNAL(usedChanged(unsigned int, bool)), this->dsoWidget, SLOT(updateVoltageUsed(unsigned int, bool))); - connect(this->voltageDock, SIGNAL(couplingChanged(unsigned int, Dso::Coupling)), this->dsoControl, SLOT(setCoupling(unsigned int, Dso::Coupling))); - connect(this->voltageDock, SIGNAL(couplingChanged(unsigned int, Dso::Coupling)), this->dsoWidget, SLOT(updateVoltageCoupling(unsigned int))); - connect(this->voltageDock, SIGNAL(modeChanged(Dso::MathMode)), this->dsoWidget, SLOT(updateMathMode())); - connect(this->voltageDock, SIGNAL(gainChanged(unsigned int, double)), this, SLOT(updateVoltageGain(unsigned int))); - connect(this->voltageDock, SIGNAL(gainChanged(unsigned int, double)), this->dsoWidget, SLOT(updateVoltageGain(unsigned int))); - connect(this->dsoWidget, SIGNAL(offsetChanged(unsigned int, double)), this, SLOT(updateOffset(unsigned int))); - - connect(this->spectrumDock, SIGNAL(usedChanged(unsigned int, bool)), this, SLOT(updateUsed(unsigned int))); - connect(this->spectrumDock, SIGNAL(usedChanged(unsigned int, bool)), this->dsoWidget, SLOT(updateSpectrumUsed(unsigned int, bool))); - connect(this->spectrumDock, SIGNAL(magnitudeChanged(unsigned int, double)), this->dsoWidget, SLOT(updateSpectrumMagnitude(unsigned int))); - - // Started/stopped signals from oscilloscope - connect(this->dsoControl, SIGNAL(samplingStarted()), this, SLOT(started())); - connect(this->dsoControl, SIGNAL(samplingStopped()), this, SLOT(stopped())); + // Connect all signals + this->connectSignals(); // Set up the oscilloscope this->dsoControl->connectDevice(); - - for(unsigned int channel = 0; channel < this->settings->scope.physicalChannels; ++channel) { - this->dsoControl->setCoupling(channel, (Dso::Coupling) this->settings->scope.voltage[channel].misc); - this->updateVoltageGain(channel); - this->updateOffset(channel); - this->dsoControl->setTriggerLevel(channel, this->settings->scope.voltage[channel].trigger); - } - this->updateUsed(this->settings->scope.physicalChannels); - this->dsoControl->setRecordLength(this->settings->scope.horizontal.samples); - this->updateTimebase(); - this->dsoControl->setTriggerMode(this->settings->scope.trigger.mode); - this->dsoControl->setPretriggerPosition(this->settings->scope.trigger.position * this->settings->scope.horizontal.timebase * DIVS_TIME); - this->dsoControl->setTriggerSlope(this->settings->scope.trigger.slope); - this->dsoControl->setTriggerSource(this->settings->scope.trigger.special, this->settings->scope.trigger.source); - + this->initializeDevice(); this->dsoControl->startSampling(); } @@ -202,21 +153,6 @@ void OpenHantekMainWindow::createActions() { this->startStopAction = new QAction(this); this->startStopAction->setShortcut(tr("Space")); this->stopped(); - - this->recordLengthActionGroup = new QActionGroup(this); - connect(this->recordLengthActionGroup, SIGNAL(selected(QAction *)), this, SLOT(recordLengthSelected(QAction *))); - - this->recordLengthSmallAction = new QAction(tr("&Small"), this); - this->recordLengthSmallAction->setActionGroup(this->recordLengthActionGroup); - this->recordLengthSmallAction->setCheckable(true); - this->recordLengthSmallAction->setChecked(this->settings->scope.horizontal.samples == 10240); - this->recordLengthSmallAction->setStatusTip(tr("10240 Samples")); - - this->recordLengthLargeAction = new QAction(tr("&Large"), this); - this->recordLengthLargeAction->setActionGroup(this->recordLengthActionGroup); - this->recordLengthLargeAction->setCheckable(true); - this->recordLengthLargeAction->setChecked(this->settings->scope.horizontal.samples != 10240); - this->recordLengthLargeAction->setStatusTip(tr("32768 Samples")); this->digitalPhosphorAction = new QAction(QIcon(":actions/digitalphosphor.png"), tr("Digital &phosphor"), this); this->digitalPhosphorAction->setCheckable(true); @@ -276,12 +212,9 @@ void OpenHantekMainWindow::createMenus() { this->oscilloscopeMenu->addSeparator(); this->oscilloscopeMenu->addAction(this->startStopAction); #ifdef DEBUG + this->oscilloscopeMenu->addSeparator(); this->oscilloscopeMenu->addAction(this->commandAction); #endif - this->oscilloscopeMenu->addSeparator(); - this->recordLengthMenu = this->oscilloscopeMenu->addMenu(tr("&Record length")); - this->recordLengthMenu->addAction(this->recordLengthSmallAction); - this->recordLengthMenu->addAction(this->recordLengthLargeAction); this->menuBar()->addSeparator(); @@ -337,6 +270,82 @@ void OpenHantekMainWindow::createDockWindows() this->voltageDock = new VoltageDock(this->settings); } +/// \brief Connect general signals and device management signals. +void OpenHantekMainWindow::connectSignals() { + // Connect general signals + connect(this, SIGNAL(settingsChanged()), this, SLOT(applySettings())); + //connect(this->dsoWidget, SIGNAL(stopped()), this, SLOT(stopped())); + connect(this->dsoControl, SIGNAL(statusMessage(QString, int)), this->statusBar(), SLOT(showMessage(QString, int))); + connect(this->dsoControl, SIGNAL(samplesAvailable(const QList *, const QList *, double, QMutex *)), this->dataAnalyzer, SLOT(analyze(const QList *, const QList *, double, QMutex *))); + + // Connect signals to DSO controller and widget + connect(this->horizontalDock, SIGNAL(samplerateChanged(double)), this, SLOT(samplerateSelected())); + connect(this->horizontalDock, SIGNAL(timebaseChanged(double)), this, SLOT(timebaseSelected())); + connect(this->horizontalDock, SIGNAL(frequencybaseChanged(double)), this->dsoWidget, SLOT(updateFrequencybase(double))); + connect(this->horizontalDock, SIGNAL(recordLengthChanged(unsigned long)), this, SLOT(recordLengthSelected(unsigned long))); + //connect(this->horizontalDock, SIGNAL(formatChanged(HorizontalFormat)), this->dsoWidget, SLOT(horizontalFormatChanged(HorizontalFormat))); + + connect(this->triggerDock, SIGNAL(modeChanged(Dso::TriggerMode)), this->dsoControl, SLOT(setTriggerMode(Dso::TriggerMode))); + connect(this->triggerDock, SIGNAL(modeChanged(Dso::TriggerMode)), this->dsoWidget, SLOT(updateTriggerMode())); + connect(this->triggerDock, SIGNAL(sourceChanged(bool, unsigned int)), this->dsoControl, SLOT(setTriggerSource(bool, unsigned int))); + connect(this->triggerDock, SIGNAL(sourceChanged(bool, unsigned int)), this->dsoWidget, SLOT(updateTriggerSource())); + connect(this->triggerDock, SIGNAL(slopeChanged(Dso::Slope)), this->dsoControl, SLOT(setTriggerSlope(Dso::Slope))); + connect(this->triggerDock, SIGNAL(slopeChanged(Dso::Slope)), this->dsoWidget, SLOT(updateTriggerSlope())); + connect(this->dsoWidget, SIGNAL(triggerPositionChanged(double)), this->dsoControl, SLOT(setPretriggerPosition(double))); + connect(this->dsoWidget, SIGNAL(triggerLevelChanged(unsigned int, double)), this->dsoControl, SLOT(setTriggerLevel(unsigned int, double))); + + connect(this->voltageDock, SIGNAL(usedChanged(unsigned int, bool)), this, SLOT(updateUsed(unsigned int))); + connect(this->voltageDock, SIGNAL(usedChanged(unsigned int, bool)), this->dsoWidget, SLOT(updateVoltageUsed(unsigned int, bool))); + connect(this->voltageDock, SIGNAL(couplingChanged(unsigned int, Dso::Coupling)), this->dsoControl, SLOT(setCoupling(unsigned int, Dso::Coupling))); + connect(this->voltageDock, SIGNAL(couplingChanged(unsigned int, Dso::Coupling)), this->dsoWidget, SLOT(updateVoltageCoupling(unsigned int))); + connect(this->voltageDock, SIGNAL(modeChanged(Dso::MathMode)), this->dsoWidget, SLOT(updateMathMode())); + connect(this->voltageDock, SIGNAL(gainChanged(unsigned int, double)), this, SLOT(updateVoltageGain(unsigned int))); + connect(this->voltageDock, SIGNAL(gainChanged(unsigned int, double)), this->dsoWidget, SLOT(updateVoltageGain(unsigned int))); + connect(this->dsoWidget, SIGNAL(offsetChanged(unsigned int, double)), this, SLOT(updateOffset(unsigned int))); + + connect(this->spectrumDock, SIGNAL(usedChanged(unsigned int, bool)), this, SLOT(updateUsed(unsigned int))); + connect(this->spectrumDock, SIGNAL(usedChanged(unsigned int, bool)), this->dsoWidget, SLOT(updateSpectrumUsed(unsigned int, bool))); + connect(this->spectrumDock, SIGNAL(magnitudeChanged(unsigned int, double)), this->dsoWidget, SLOT(updateSpectrumMagnitude(unsigned int))); + + // Started/stopped signals from oscilloscope + connect(this->dsoControl, SIGNAL(samplingStarted()), this, SLOT(started())); + connect(this->dsoControl, SIGNAL(samplingStopped()), this, SLOT(stopped())); + + //connect(this->dsoControl, SIGNAL(recordLengthChanged(unsigned long)), this, SLOT(recordLengthChanged())); + connect(this->dsoControl, SIGNAL(recordTimeChanged(double)), this, SLOT(recordTimeChanged(double))); + connect(this->dsoControl, SIGNAL(samplerateChanged(double)), this, SLOT(samplerateChanged(double))); + + connect(this->dsoControl, SIGNAL(availableRecordLengthsChanged(QList)), this->horizontalDock, SLOT(availableRecordLengthsChanged(QList))); + connect(this->dsoControl, SIGNAL(samplerateLimitsChanged(double, double)), this->horizontalDock, SLOT(samplerateLimitsChanged(double, double))); +} + +/// \brief Initialize the device with the current settings. +void OpenHantekMainWindow::initializeDevice() { + for(unsigned int channel = 0; channel < this->settings->scope.physicalChannels; ++channel) { + this->dsoControl->setCoupling(channel, (Dso::Coupling) this->settings->scope.voltage[channel].misc); + this->updateVoltageGain(channel); + this->updateOffset(channel); + this->dsoControl->setTriggerLevel(channel, this->settings->scope.voltage[channel].trigger); + } + this->updateUsed(this->settings->scope.physicalChannels); + if(this->dsoControl->getAvailableRecordLengths()->isEmpty()) + this->dsoControl->setRecordLength(this->settings->scope.horizontal.recordLength); + else + this->dsoControl->setRecordLength(this->dsoControl->getAvailableRecordLengths()->indexOf(this->settings->scope.horizontal.recordLength)); + if(this->settings->scope.horizontal.samplerateSet) + this->samplerateSelected(); + else + this->timebaseSelected(); + this->dsoControl->setTriggerMode(this->settings->scope.trigger.mode); + this->dsoControl->setPretriggerPosition(this->settings->scope.trigger.position * this->settings->scope.horizontal.timebase * DIVS_TIME); + this->dsoControl->setTriggerSlope(this->settings->scope.trigger.slope); + this->dsoControl->setTriggerSource(this->settings->scope.trigger.special, this->settings->scope.trigger.source); + + // Apply the limits to the dock widgets + this->horizontalDock->availableRecordLengthsChanged(*this->dsoControl->getAvailableRecordLengths()); + this->horizontalDock->samplerateLimitsChanged(this->dsoControl->getMinSamplerate(), this->dsoControl->getMaxSamplerate()); +} + /// \brief Read the settings from an ini file. /// \param fileName Optional filename to export the settings to a specific file. /// \return 0 on success, negative on error. @@ -598,11 +607,47 @@ void OpenHantekMainWindow::updateSettings() { } } +/// \brief The oscilloscope changed the record time. +/// \param duration The new record time duration in seconds. +void OpenHantekMainWindow::recordTimeChanged(double duration) { + if(this->settings->scope.horizontal.samplerateSet) { + // The samplerate was set, let's adapt the timebase accordingly + this->settings->scope.horizontal.timebase = duration / DIVS_TIME; + this->horizontalDock->setTimebase(this->settings->scope.horizontal.timebase); + } + + // The trigger position should be kept at the same place but the timebase has changed + this->dsoControl->setPretriggerPosition(this->settings->scope.trigger.position * this->settings->scope.horizontal.timebase * DIVS_TIME); + + this->dsoWidget->updateTimebase(this->settings->scope.horizontal.timebase); +} + +/// \brief The oscilloscope changed the samplerate. +/// \param samplerate The new samplerate in samples per second. +void OpenHantekMainWindow::samplerateChanged(double samplerate) { + if(!this->settings->scope.horizontal.samplerateSet) { + // The timebase was set, let's adapt the samplerate accordingly + this->settings->scope.horizontal.samplerate = samplerate; + this->horizontalDock->setSamplerate(samplerate); + } + + this->dsoWidget->updateSamplerate(samplerate); +} + /// \brief Apply new record length to settings. -/// \param action The selected record length menu item. -void OpenHantekMainWindow::recordLengthSelected(QAction *action) { - this->settings->scope.horizontal.samples = (action == this->recordLengthSmallAction) ? 10240 : 32768; - this->dsoControl->setRecordLength(this->settings->scope.horizontal.samples); +/// \param recordLength The selected record length in samples. +void OpenHantekMainWindow::recordLengthSelected(unsigned long recordLength) { + this->dsoControl->setRecordLength(recordLength); +} + +/// \brief Sets the samplerate of the oscilloscope. +void OpenHantekMainWindow::samplerateSelected() { + this->dsoControl->setSamplerate(this->settings->scope.horizontal.samplerate); +} + +/// \brief Sets the record time of the oscilloscope. +void OpenHantekMainWindow::timebaseSelected() { + this->dsoControl->setRecordTime(this->settings->scope.horizontal.timebase * DIVS_TIME); } /// \brief Sets the offset of the oscilloscope for the given channel. @@ -614,15 +659,6 @@ void OpenHantekMainWindow::updateOffset(unsigned int channel) { this->dsoControl->setOffset(channel, (this->settings->scope.voltage[channel].offset / DIVS_VOLTAGE) + 0.5); } -/// \brief Sets the samplerate of the oscilloscope. -void OpenHantekMainWindow::updateTimebase() { - this->settings->scope.horizontal.samplerate = this->dsoControl->setSamplerate(1e3 / this->settings->scope.horizontal.timebase); - this->dsoWidget->updateSamplerate(); - - // The trigger position should be kept at the same place but the timebase has changed - this->dsoControl->setPretriggerPosition(this->settings->scope.trigger.position * this->settings->scope.horizontal.timebase * DIVS_TIME); -} - /// \brief Sets the state of the given oscilloscope channel. /// \param channel The channel whose state has changed. void OpenHantekMainWindow::updateUsed(unsigned int channel) { diff --git a/openhantek/src/openhantek.h b/openhantek/src/openhantek.h index cb9e4b2..e6cd6ae 100644 --- a/openhantek/src/openhantek.h +++ b/openhantek/src/openhantek.h @@ -65,6 +65,10 @@ class OpenHantekMainWindow : public QMainWindow { void createToolBars(); void createStatusBar(); void createDockWindows(); + + // Device management + void connectSignals(); + void initializeDevice(); // Settings int readSettings(const QString &fileName = QString()); @@ -81,8 +85,6 @@ class OpenHantekMainWindow : public QMainWindow { QAction *configAction; QAction *startStopAction; - QActionGroup *recordLengthActionGroup; - QAction *recordLengthSmallAction, *recordLengthLargeAction; QAction *digitalPhosphorAction, *zoomAction; QAction *aboutAction, *aboutQtAction; @@ -94,7 +96,7 @@ class OpenHantekMainWindow : public QMainWindow { // Menus QMenu *fileMenu; QMenu *viewMenu, *dockMenu, *toolbarMenu; - QMenu *oscilloscopeMenu, *recordLengthMenu; + QMenu *oscilloscopeMenu; QMenu *helpMenu; // Toolbars @@ -143,9 +145,12 @@ class OpenHantekMainWindow : public QMainWindow { void applySettings(); void updateSettings(); - void recordLengthSelected(QAction *action); + void recordTimeChanged(double duration); + void samplerateChanged(double samplerate); + void recordLengthSelected(unsigned long recordLength); + void samplerateSelected(); + void timebaseSelected(); void updateOffset(unsigned int channel); - void updateTimebase(); void updateUsed(unsigned int channel); void updateVoltageGain(unsigned int channel); diff --git a/openhantek/src/settings.cpp b/openhantek/src/settings.cpp index f382c42..f2cbf1e 100644 --- a/openhantek/src/settings.cpp +++ b/openhantek/src/settings.cpp @@ -64,8 +64,9 @@ DsoSettings::DsoSettings(QWidget *parent) : QObject(parent) { this->scope.horizontal.marker[0] = -1.0; this->scope.horizontal.marker[1] = 1.0; this->scope.horizontal.timebase = 1e-3; - this->scope.horizontal.samples = 10240; + this->scope.horizontal.recordLength = 0; this->scope.horizontal.samplerate = 1e6; + this->scope.horizontal.samplerateSet = false; // Trigger this->scope.trigger.filter = true; this->scope.trigger.mode = Dso::TRIGGERMODE_NORMAL; @@ -276,6 +277,12 @@ int DsoSettings::load(const QString &fileName) { } if(settingsLoader->contains("timebase")) this->scope.horizontal.timebase = settingsLoader->value("timebase").toDouble(); + if(settingsLoader->contains("recordLength")) + this->scope.horizontal.recordLength = settingsLoader->value("recordLength").toUInt(); + if(settingsLoader->contains("samplerate")) + this->scope.horizontal.samplerate = settingsLoader->value("samplerate").toDouble(); + if(settingsLoader->contains("samplerateSet")) + this->scope.horizontal.samplerateSet = settingsLoader->value("samplerateSet").toBool(); settingsLoader->endGroup(); // Trigger settingsLoader->beginGroup("trigger"); @@ -449,6 +456,9 @@ int DsoSettings::save(const QString &fileName) { for(int marker = 0; marker < 2; ++marker) settingsSaver->setValue(QString("marker%1").arg(marker), this->scope.horizontal.marker[marker]); settingsSaver->setValue("timebase", this->scope.horizontal.timebase); + settingsSaver->setValue("recordLength", (unsigned int) this->scope.horizontal.recordLength); + settingsSaver->setValue("samplerate", this->scope.horizontal.samplerate); + settingsSaver->setValue("samplerateSet", this->scope.horizontal.samplerateSet); settingsSaver->endGroup(); // Trigger settingsSaver->beginGroup("trigger"); @@ -457,6 +467,7 @@ int DsoSettings::save(const QString &fileName) { settingsSaver->setValue("position", this->scope.trigger.position); settingsSaver->setValue("slope", this->scope.trigger.slope); settingsSaver->setValue("source", this->scope.trigger.source); + settingsSaver->setValue("special", this->scope.trigger.special); settingsSaver->endGroup(); // Spectrum for(int channel = 0; channel < this->scope.spectrum.count(); ++channel) { diff --git a/openhantek/src/settings.h b/openhantek/src/settings.h index f4803d0..4746a0a 100644 --- a/openhantek/src/settings.h +++ b/openhantek/src/settings.h @@ -93,8 +93,9 @@ struct DsoSettingsScopeHorizontal { double frequencybase; ///< Frequencybase in Hz/div double marker[2]; ///< Marker positions in div double timebase; ///< Timebase in s/div - unsigned long int samples; ///< Sample count - unsigned long int samplerate; ///< The samplerate of the oscilloscope in S + unsigned long int recordLength; ///< Sample count + double samplerate; ///< The samplerate of the oscilloscope in S + bool samplerateSet; ///< The samplerate was set by the user, not the timebase }; //////////////////////////////////////////////////////////////////////////////// @@ -105,8 +106,8 @@ struct DsoSettingsScopeTrigger { Dso::TriggerMode mode; ///< Automatic, normal or single trigger double position; ///< Horizontal position for pretrigger Dso::Slope slope; ///< Rising or falling edge causes trigger - bool special; ///< true if the trigger source is not a standard channel unsigned int source; ///< Channel that is used as trigger source + bool special; ///< true if the trigger source is not a standard channel }; //////////////////////////////////////////////////////////////////////////////// diff --git a/openhantek/translations/openhantek_de.ts b/openhantek/translations/openhantek_de.ts index 179d167..62394b1 100644 --- a/openhantek/translations/openhantek_de.ts +++ b/openhantek/translations/openhantek_de.ts @@ -270,67 +270,62 @@ DsoWidget - + Zoom x%L1 Zoom x%L1 - - - - - + + + + + /div /div - + Portable Document Format (*.pdf) Portables Dokumentenformat (*.pdf) - + PostScript (*.ps) PostScript (*.ps) - + Export file... Datei exportieren... - + Marker 1/2 Marker 1/2 - + %L1% %L1% - + %1 %2 %3 %4 %1 %2 %3 %4 - + /s /s - - %1 S - %1 S - - - + Image (*.png *.xpm *.jpg) Bild (*.png *.xpm *.jpg) - + Comma-Separated Values (*.csv) Kommagetrennte Werte (*.csv) @@ -343,42 +338,42 @@ Oszillograph drucken - + %L1% %L1% - + %1 %2 %3 %4 %1 %2 %3 %4 - + %1 S %1 S - + /s /s - - - - - + + + + + /div /div - + Zoom x%L1 Zoom x%L1 - + Marker 1/2 Marker 1/2 @@ -386,27 +381,27 @@ Hantek::Control - + EXT EXT - + EXT/10 EXT/10 - + The device has been disconnected Die Verbindung zum Gerät wurde getrennt - + Unknown model Unbekanntes Modell - + Couldn't get channel level data from oscilloscope Konnte Kanalpegeldaten des Oszilloskops nicht lesen @@ -414,41 +409,41 @@ Hantek::Device - + Can't search for Hantek oscilloscopes: %1 Suche nach Hantek Oszilloskopen fehlgeschlagen: %1 - - + + Failed to claim interface %1 of device %2: %3 Anforderung der Schnittstelle %1 des Gerätes %2 fehlgeschlagen: %3 - - + + Device found: Hantek %1 (%2) Gerät gefunden: Hantek %1 (%2) - + Couldn't open device %1 Konnte Gerät %1 nicht öffnen - - + + No Hantek oscilloscope found Keine Hantek Oszilloskope gefunden - - + + Failed to get device list: %1 Abrufen der Geräteliste fehlgeschlagen: %1 - + Couldn't open device %1: %2 Konnte Gerät %1 nicht öffnen: %2 @@ -456,25 +451,41 @@ HorizontalDock - + Horizontal Horizontal - + + Samplerate + Samplerate + + + Timebase Zeitbasis - + Frequencybase Frequenzbasis - + + Record length + Satzlänge + + + Format Format + + + + Roll + Rollen + OpenHantekMainWindow @@ -484,289 +495,264 @@ OpenHantek - + &Open... &Öffnen... - + Ctrl+O Strg+O - + Open saved settings Gespeicherte Einstellungen öffnen - + &Save &Speichern - - + + Ctrl+S Strg+S - + Save the current settings Aktuelle Einstellungen speichern - + Save &as... Speichern &unter... - + Save the current settings to another file Aktuelle Einstellungen in anderer Datei speichern - + &Print... &Drucken... - + Ctrl+P Strg+P - + Print the oscilloscope screen Den Oscilloskopbildschirm drucken - + &Export as... &Exportieren als... - + Ctrl+E Strg+E - + Export the oscilloscope data to a file Die Oszilloskopdaten in eine Datei exportieren - + E&xit &Beenden - + Ctrl+Q Strg+Q - + Exit the application Anwendung beenden - + &Settings &Einstellungen - + Configure the oscilloscope Das Oszilloskop konfigurieren - + Send command Befehl senden - + Shift+C Shift+C - + &Docking windows &Dockfenster - + &Toolbars &Werkzeugleisten - - + + Settings (*.ini) Einstellungen (*.ini) - + Save settings Einstellungen speichern - + &Stop &Stop - + <p>This is a open source software for Hantek USB oscilloscopes.</p><p>Copyright &copy; 2010, 2011 Oliver Haag &lt;oliver.haag@gmail.com&gt;</p> <p>Dies ist ein Open-Source Programm für Hantek USB Oszilloskope.</p><p>Copyright &copy; 2010, 2011 Oliver Haag &lt;oliver.haag@gmail.com&gt;</p> - + Invalid command Ungültiger Befehl - + Space Leertaste - + Stop the oscilloscope Das Oszilloskop anhalten - - &Small - &Klein - - - - 10240 Samples - 10240 Werte - - - - &Large - &Groß - - - - 32768 Samples - 32768 Werte - - - + Digital &phosphor Digitaler &Phosphor - + &Zoom &Zoom - + &About &Über - + Show information about this program Zeige Informationen über dieses Programm - + About &Qt Über &Qt - + Show the Qt library's About box Zeige Dialog zur Qt Bibliothek - + &File &Datei - + &View &Ansicht - + &Oscilloscope &Oszilloskop - - &Buffer size - &Puffergröße - - - + &Help &Hilfe - + File Datei - + Oscilloscope Oszilloskop - + View Ansicht - + Ready Bereit - + Open file Datei öffnen - + &Start &Start - + Start the oscilloscope Startet das Oszilloskop - + Disable fading of previous graphs Nachleuchten von vorigen Graphen deaktivieren - + Enable fading of previous graphs Nachleuchten von vorigen Graphen aktivieren - + Hide magnified scope Vergrößerte Anzeige ausblenden - + Show magnified scope Vergrößerte Anzeige anzeigen - + About OpenHantek %1 *ber OpenHantek %1 @@ -774,187 +760,187 @@ QApplication - + Success (no error) Erfolgreich (Kein Fehler) - + Input/output error Ein-/Ausgabe Fehler - + Invalid parameter Ungültiger Parameter - + Access denied (insufficient permissions) Zugriff verweigert (Unzureichende Berechtigungen) - + No such device (it may have been disconnected) Gerät nicht vorhanden (Möglicherweise wurde es abgesteckt) - + Entity not found Datensatz nicht gefunden - + Resource busy Quelle belegt - + Operation timed out Zeitüberschreitung - + Overflow Überlauf - + Pipe error Leitungsfehler - + System call interrupted (perhaps due to signal) Systemaufruf unterbrochen (Möglicherweise aufgrund eines Signals) - + Insufficient memory Unzureichender Speicher - + Operation not supported or unimplemented on this platform Vorgang auf diesem System nicht unterstützt oder nicht implementiert - + Other error Anderer Fehler - + %L1 µV %L1 µV - + %L1 mV %L1 mV - + %L1 V %L1 V - + %L1 dB %L1 dB - + %L1 ps %L1 ps - + %L1 ns %L1 ns - + %L1 µs %L1 µs - + %L1 ms %L1 ms - + %L1 s %L1 s - + %L1 min %L1 min - + %L1 h %L1 h - + %L1 Hz %L1 Hz - + %L1 kHz %L1 kHz - + %L1 MHz %L1 MHz - + %L1 GHz %L1 GHz - + %L1 S %L1 S - + %L1 kS %L1 kS - + %L1 MS %L1 MS - + %L1 GS %L1 GS - + CH%1 CH%1 - + SP%1 SP%1 - + MATH MATH - + SPM SPM @@ -1112,7 +1098,7 @@ SpectrumDock - + Spectrum Spektrum @@ -1120,27 +1106,27 @@ TriggerDock - + Trigger Trigger - + CH%1 CH%1 - + Mode Modus - + Slope Flanke - + Source Quelle @@ -1148,7 +1134,7 @@ VoltageDock - + Voltage Spannung -- libgit2 0.21.4