diff --git a/openhantek/src/hantek/definitions.h b/openhantek/src/hantek/definitions.h index 5b1c169..f2d0ba8 100644 --- a/openhantek/src/hantek/definitions.h +++ b/openhantek/src/hantek/definitions.h @@ -19,7 +19,7 @@ namespace Dso { /// \enum ErrorCode hantek/control.h /// \brief The return codes for device control methods. -enum ErrorCode { +enum class ErrorCode { ERROR_NONE = 0, ///< Successful operation ERROR_CONNECTION = -1, ///< Device not connected or communication error ERROR_UNSUPPORTED = -2, ///< Not supported by this device @@ -821,7 +821,8 @@ enum CaptureState { CAPTURE_SAMPLING = 1, ///< The scope is sampling data after triggering CAPTURE_READY = 2, ///< Sampling data is available (DSO-2090/DSO-2150) CAPTURE_READY2250 = 3, ///< Sampling data is available (DSO-2250) - CAPTURE_READY5200 = 7 ///< Sampling data is available (DSO-5200/DSO-5200A) + CAPTURE_READY5200 = 7, ///< Sampling data is available (DSO-5200/DSO-5200A) + CAPTURE_ERROR = 1000 }; ////////////////////////////////////////////////////////////////////////////// diff --git a/openhantek/src/hantek/hantekdsocontrol.cpp b/openhantek/src/hantek/hantekdsocontrol.cpp index 93ad40d..66ad3c7 100644 --- a/openhantek/src/hantek/hantekdsocontrol.cpp +++ b/openhantek/src/hantek/hantekdsocontrol.cpp @@ -32,9 +32,7 @@ void HantekDsoControl::startSampling() { if (specification.isSoftwareTriggerDevice) { // Convert to GUI presentable values (1e5 -> 1.0, 48e6 -> 480.0 etc) QList sampleSteps; - for (double v: specification.sampleSteps) { - sampleSteps << v/1e5; - } + for (double v : specification.sampleSteps) { sampleSteps << v / 1e5; } emit samplerateSet(1, sampleSteps); } @@ -47,15 +45,12 @@ void HantekDsoControl::stopSampling() { emit samplingStopped(); } -/// \brief Get a list of the names of the special trigger sources. const QStringList *HantekDsoControl::getSpecialTriggerSources() { return &(specialTriggerSources); } const USBDevice *HantekDsoControl::getDevice() const { return device; } const DSOsamples &HantekDsoControl::getLastSamples() { return result; } -/// \brief Initializes the command buffers and lists. -/// \param parent The parent widget. HantekDsoControl::HantekDsoControl(USBDevice *device) : device(device) { if (device == nullptr) throw new std::runtime_error("No usb device for HantekDsoControl"); @@ -73,10 +68,8 @@ HantekDsoControl::HantekDsoControl(USBDevice *device) : device(device) { specification.samplerate.single.base = 50e6; specification.samplerate.single.max = 50e6; - specification.samplerate.single.recordLengths << 0; specification.samplerate.multi.base = 100e6; specification.samplerate.multi.max = 100e6; - specification.samplerate.multi.recordLengths << 0; for (unsigned channel = 0; channel < HANTEK_CHANNELS; ++channel) { for (unsigned gainId = 0; gainId < 9; ++gainId) { @@ -102,8 +95,8 @@ HantekDsoControl::HantekDsoControl(USBDevice *device) : device(device) { controlsettings.voltage[channel].offsetReal = 0.0; controlsettings.voltage[channel].used = false; } - controlsettings.recordLengthId = 1; controlsettings.usedChannels = 0; + controlsettings.recordLengthId = 1; // Transmission-ready control commands this->control[CONTROLINDEX_SETOFFSET] = new ControlSetOffset(); @@ -113,9 +106,6 @@ HantekDsoControl::HantekDsoControl(USBDevice *device) : device(device) { for (int cIndex = 0; cIndex < CONTROLINDEX_COUNT; ++cIndex) this->controlPending[cIndex] = false; - // Sample buffers - result.data.resize(HANTEK_CHANNELS); - int errorCode; // Instantiate the commands needed for all models @@ -153,11 +143,11 @@ HantekDsoControl::HantekDsoControl(USBDevice *device) : device(device) { specification.samplerate.single.base = 50e6; specification.samplerate.single.max = 75e6; specification.samplerate.single.maxDownsampler = 131072; - specification.samplerate.single.recordLengths << UINT_MAX << 10240 << 32768; + specification.samplerate.single.recordLengths = {UINT_MAX, 10240, 32768}; specification.samplerate.multi.base = 100e6; specification.samplerate.multi.max = 150e6; specification.samplerate.multi.maxDownsampler = 131072; - specification.samplerate.multi.recordLengths << UINT_MAX << 20480 << 65536; + specification.samplerate.multi.recordLengths = {UINT_MAX, 20480, 65536}; specification.bufferDividers << 1000 << 1 << 1; 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) @@ -179,11 +169,11 @@ HantekDsoControl::HantekDsoControl(USBDevice *device) : device(device) { specification.samplerate.single.base = 50e6; specification.samplerate.single.max = 50e6; specification.samplerate.single.maxDownsampler = 131072; - specification.samplerate.single.recordLengths << UINT_MAX << 10240 << 32768; + specification.samplerate.single.recordLengths = {UINT_MAX, 10240, 32768}; specification.samplerate.multi.base = 100e6; specification.samplerate.multi.max = 100e6; specification.samplerate.multi.maxDownsampler = 131072; - specification.samplerate.multi.recordLengths << UINT_MAX << 20480 << 65536; + specification.samplerate.multi.recordLengths = {UINT_MAX, 20480, 65536}; specification.bufferDividers << 1000 << 1 << 1; 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) @@ -216,11 +206,11 @@ HantekDsoControl::HantekDsoControl(USBDevice *device) : device(device) { specification.samplerate.single.base = 100e6; specification.samplerate.single.max = 100e6; specification.samplerate.single.maxDownsampler = 65536; - specification.samplerate.single.recordLengths << UINT_MAX << 10240 << 524288; + specification.samplerate.single.recordLengths = {UINT_MAX, 10240, 524288}; specification.samplerate.multi.base = 200e6; specification.samplerate.multi.max = 250e6; specification.samplerate.multi.maxDownsampler = 65536; - specification.samplerate.multi.recordLengths << UINT_MAX << 20480 << 1048576; + specification.samplerate.multi.recordLengths = {UINT_MAX, 20480, 1048576}; specification.bufferDividers << 1000 << 1 << 1; 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) @@ -251,11 +241,11 @@ HantekDsoControl::HantekDsoControl(USBDevice *device) : device(device) { specification.samplerate.single.base = 100e6; specification.samplerate.single.max = 125e6; specification.samplerate.single.maxDownsampler = 131072; - specification.samplerate.single.recordLengths << UINT_MAX << 10240 << 14336; + specification.samplerate.single.recordLengths = {UINT_MAX, 10240, 14336}; specification.samplerate.multi.base = 200e6; specification.samplerate.multi.max = 250e6; specification.samplerate.multi.maxDownsampler = 131072; - specification.samplerate.multi.recordLengths << UINT_MAX << 20480 << 28672; + specification.samplerate.multi.recordLengths = {UINT_MAX, 20480, 28672}; specification.bufferDividers << 1000 << 1 << 1; 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 @@ -297,11 +287,11 @@ HantekDsoControl::HantekDsoControl(USBDevice *device) : device(device) { specification.samplerate.single.base = 1e6; specification.samplerate.single.max = 48e6; specification.samplerate.single.maxDownsampler = 10; - specification.samplerate.single.recordLengths << UINT_MAX << 10240; + specification.samplerate.single.recordLengths = {UINT_MAX, 10240}; specification.samplerate.multi.base = 1e6; specification.samplerate.multi.max = 48e6; specification.samplerate.multi.maxDownsampler = 10; - specification.samplerate.multi.recordLengths << UINT_MAX << 20480; + specification.samplerate.multi.recordLengths = {UINT_MAX, 20480}; specification.bufferDividers << 1000 << 1 << 1; specification.gainSteps << 0.08 << 0.16 << 0.40 << 0.80 << 1.60 << 4.00 << 8.0 << 16.0 << 40.0; // This data was based on testing and depends on Divider. @@ -318,45 +308,36 @@ HantekDsoControl::HantekDsoControl(USBDevice *device) : device(device) { throw new std::runtime_error("unknown model"); } - controlsettings.recordLengthId = 1; - controlsettings.samplerate.limits = &(specification.samplerate.single); - controlsettings.samplerate.downsampler = 1; this->previousSampleCount = 0; // Get channel level data errorCode = device->controlRead(CONTROL_VALUE, (unsigned char *)&(specification.offsetLimit), sizeof(specification.offsetLimit), (int)VALUE_OFFSETLIMITS); if (errorCode < 0) { - device->disconnect(); + qWarning() << tr("Couldn't get channel level data from oscilloscope"); emit statusMessage(tr("Couldn't get channel level data from oscilloscope"), 0); + emit communicationError(); return; } sampling = false; } -/// \brief Disconnects the device. HantekDsoControl::~HantekDsoControl() { // Clean up commands for (int cIndex = 0; cIndex < BULK_COUNT; ++cIndex) { delete command[cIndex]; } } -/// \brief Gets the physical channel count for this oscilloscope. -/// \return The number of physical channels. unsigned HantekDsoControl::getChannelCount() { return HANTEK_CHANNELS; } -/// \brief Get available record lengths for this oscilloscope. -/// \return The number of physical channels, empty list for continuous. -QList *HantekDsoControl::getAvailableRecordLengths() { return &controlsettings.samplerate.limits->recordLengths; } +const std::vector &HantekDsoControl::getAvailableRecordLengths() { // + return controlsettings.samplerate.limits->recordLengths; +} -/// \brief Get minimum samplerate for this oscilloscope. -/// \return The minimum samplerate for the current configuration in S/s. double HantekDsoControl::getMinSamplerate() { return (double)specification.samplerate.single.base / specification.samplerate.single.maxDownsampler; } -/// \brief Get maximum samplerate for this oscilloscope. -/// \return The maximum samplerate for the current configuration in S/s. double HantekDsoControl::getMaxSamplerate() { ControlSamplerateLimits *limits = (controlsettings.usedChannels <= 1) ? &specification.samplerate.multi : &specification.samplerate.single; @@ -368,8 +349,7 @@ void HantekDsoControl::updateInterval() { // Check the current oscilloscope state everytime 25% of the time the buffer // should be refilled if (isRollMode()) - cycleTime = (int)((double)device->getPacketSize() / - ((controlsettings.samplerate.limits == &specification.samplerate.multi) ? 1 : HANTEK_CHANNELS) / + cycleTime = (int)((double)device->getPacketSize() / (isFastRate() ? 1 : HANTEK_CHANNELS) / controlsettings.samplerate.current * 250); else cycleTime = (int)((double)getRecordLength() / controlsettings.samplerate.current * 250); @@ -378,15 +358,18 @@ void HantekDsoControl::updateInterval() { cycleTime = qBound(10, cycleTime, 1000); } -bool HantekDsoControl::isRollMode() { +bool HantekDsoControl::isRollMode() const { return controlsettings.samplerate.limits->recordLengths[controlsettings.recordLengthId] == UINT_MAX; } -int HantekDsoControl::getRecordLength() { return controlsettings.samplerate.limits->recordLengths[controlsettings.recordLengthId]; } +bool HantekDsoControl::isFastRate() const { + return controlsettings.samplerate.limits == &specification.samplerate.multi; +} + +int HantekDsoControl::getRecordLength() const { + return controlsettings.samplerate.limits->recordLengths[controlsettings.recordLengthId]; +} -/// \brief Calculates the trigger point from the CommandGetCaptureState data. -/// \param value The data value that contains the trigger point. -/// \return The calculated trigger point for the given data. unsigned HantekDsoControl::calculateTriggerPoint(unsigned value) { unsigned result = value; @@ -397,235 +380,192 @@ unsigned HantekDsoControl::calculateTriggerPoint(unsigned value) { return result; } -/// \brief Gets the current state. -/// \return The current CaptureState of the oscilloscope, libusb error code on -/// error. -int HantekDsoControl::getCaptureState() { +std::pair HantekDsoControl::getCaptureState() const { int errorCode; - if (!specification.supportsCaptureState) return CAPTURE_READY; + if (!specification.supportsCaptureState) return std::make_pair(CAPTURE_READY, 0); errorCode = device->bulkCommand(command[BULK_GETCAPTURESTATE], 1); - if (errorCode < 0) return errorCode; + if (errorCode < 0) { + qWarning() << "Getting capture state failed: " << libUsbErrorString(errorCode); + return std::make_pair(CAPTURE_ERROR, 0); + } BulkResponseGetCaptureState response; errorCode = device->bulkRead(response.data(), response.getSize()); - if (errorCode < 0) return errorCode; - - controlsettings.trigger.point = this->calculateTriggerPoint(response.getTriggerPoint()); + if (errorCode < 0) { + qWarning() << "Getting capture state failed: " << libUsbErrorString(errorCode); + return std::make_pair(CAPTURE_ERROR, 0); + } - return (int)response.getCaptureState(); + return std::make_pair((int)response.getCaptureState(), response.getTriggerPoint()); } -/// \brief Gets sample data from the oscilloscope and converts it. -/// \return sample count on success, libusb error code on error. -/// TODO Refactor. MODEL_DSO6022BE needs to be handled differently most of the time -int HantekDsoControl::getSamples(bool process) { - int errorCode; - - const unsigned DROP_DSO6022_HEAD = 0x410; - const unsigned DROP_DSO6022_TAIL = 0x3F0; - - if (device->getUniqueModelID() != MODEL_DSO6022BE) { +std::vector HantekDsoControl::getSamples(unsigned &previousSampleCount) const { + if (!specification.useControlNoBulk) { // Request data - errorCode = device->bulkCommand(command[BULK_GETDATA], 1); - if (errorCode < 0) return errorCode; + int errorCode = device->bulkCommand(command[BULK_GETDATA], 1); + if (errorCode < 0) { + qWarning() << "Getting sample data failed: " << libUsbErrorString(errorCode); + emit communicationError(); + return std::vector(); + } } - // Save raw data to temporary buffer - bool fastRate = false; - unsigned totalSampleCount = this->getSampleCount(&fastRate); - if (totalSampleCount == UINT_MAX) return LIBUSB_ERROR_INVALID_PARAM; + unsigned totalSampleCount = this->getSampleCount(); // 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 currentSampleCount = totalSampleCount; - totalSampleCount = this->previousSampleCount; - this->previousSampleCount = currentSampleCount; // Using sampleCount as temporary buffer since it - // was set to totalSampleCount + if (totalSampleCount < previousSampleCount) { + std::swap(totalSampleCount, previousSampleCount); } else { - this->previousSampleCount = totalSampleCount; + previousSampleCount = totalSampleCount; } - unsigned sampleCount = totalSampleCount; - if (!fastRate) sampleCount /= HANTEK_CHANNELS; - unsigned dataLength = totalSampleCount; - if (specification.sampleSize > 8) dataLength *= 2; + unsigned dataLength = (specification.sampleSize > 8) ? totalSampleCount * 2 : totalSampleCount; + // Save raw data to temporary buffer std::vector data(dataLength); - errorCode = device->bulkReadMulti(data.data(), dataLength); - if (errorCode < 0) return errorCode; + int errorcode = device->bulkReadMulti(data.data(), dataLength); + if (errorcode < 0) { + qWarning() << "Getting sample data failed: " << libUsbErrorString(errorcode); + return std::vector(); + } + data.resize((size_t)errorcode); - // Process the data only if we want it - if (!process) return LIBUSB_SUCCESS; + static unsigned id = 0; + ++id; + timestampDebug(QString("Received packet %1").arg(id)); - // How much data did we really receive? - dataLength = errorCode; - if (specification.sampleSize > 8) - totalSampleCount = dataLength / 2; - else - totalSampleCount = dataLength; + return data; +} - // Convert channel data - if (fastRate) { - QWriteLocker locker(&result.lock); - result.samplerate = controlsettings.samplerate.current; - result.append = isRollMode(); +void HantekDsoControl::convertRawDataToSamples(const std::vector &rawData) { + const size_t totalSampleCount = (specification.sampleSize > 8) ? rawData.size() / 2 : rawData.size(); + + QWriteLocker locker(&result.lock); + result.samplerate = controlsettings.samplerate.current; + result.append = isRollMode(); + // Prepare result buffers + result.data.resize(HANTEK_CHANNELS); + for (int channelCounter = 0; channelCounter < HANTEK_CHANNELS; ++channelCounter) + result.data[channelCounter].clear(); + + const unsigned extraBitsSize = specification.sampleSize - 8; // Number of extra bits + const unsigned short extraBitsMask = (0x00ff << extraBitsSize) & 0xff00; // Mask for extra bits extraction + // Convert channel data + if (isFastRate()) { // Fast rate mode, one channel is using all buffers - sampleCount = totalSampleCount; - int channel = 0; + unsigned channel = 0; for (; channel < HANTEK_CHANNELS; ++channel) { if (controlsettings.voltage[channel].used) break; } - // Clear unused channels - for (int channelCounter = 0; channelCounter < HANTEK_CHANNELS; ++channelCounter) - if (channelCounter != channel) { result.data[channelCounter].clear(); } + if (channel >= HANTEK_CHANNELS) return; - if (channel < HANTEK_CHANNELS) { - // Resize sample vector - result.data[channel].resize(sampleCount); + // Resize sample vector + result.data[channel].resize(totalSampleCount); + + const int gainID = (int)controlsettings.voltage[channel].gain; + const unsigned short limit = specification.voltageLimit[channel][gainID]; + const double offset = controlsettings.voltage[channel].offsetReal; + const double gainStep = specification.gainSteps[gainID]; + + // Convert data from the oscilloscope and write it into the sample buffer + unsigned bufferPosition = controlsettings.trigger.point * 2; + if (specification.sampleSize > 8) { + for (unsigned pos = 0; pos < totalSampleCount; ++pos, ++bufferPosition) { + if (bufferPosition >= totalSampleCount) bufferPosition %= totalSampleCount; + + const unsigned short low = rawData[bufferPosition]; + const unsigned extraBitsPosition = bufferPosition % HANTEK_CHANNELS; + const unsigned shift = (8 - (HANTEK_CHANNELS - 1 - extraBitsPosition) * extraBitsSize); + const unsigned short high = + ((unsigned short int)rawData[totalSampleCount + bufferPosition - extraBitsPosition] << shift) & + extraBitsMask; + + result.data[channel][pos] = ((double)(low + high) / limit - offset) * gainStep; + } + } else { + for (unsigned pos = 0; pos < totalSampleCount; ++pos, ++bufferPosition) { + if (bufferPosition >= totalSampleCount) bufferPosition %= totalSampleCount; + + double dataBuf = (double)((int)rawData[bufferPosition]); + result.data[channel][pos] = (dataBuf / limit - offset) * gainStep; + } + } + } else { + // Normal mode, channels are using their separate buffers + for (unsigned channel = 0; channel < HANTEK_CHANNELS; ++channel) { + result.data[channel].resize(totalSampleCount / HANTEK_CHANNELS); - // Convert data from the oscilloscope and write it into the sample - // buffer + const int gainID = controlsettings.voltage[channel].gain; + const unsigned short limit = specification.voltageLimit[channel][gainID]; + const double offset = controlsettings.voltage[channel].offsetReal; + const double gainStep = specification.gainSteps[gainID]; + + // Convert data from the oscilloscope and write it into the sample buffer unsigned bufferPosition = controlsettings.trigger.point * 2; if (specification.sampleSize > 8) { // Additional most significant bits after the normal data - unsigned extraBitsPosition; // Track the position of the extra - // bits in the additional byte - unsigned extraBitsSize = specification.sampleSize - 8; // Number of extra bits - unsigned short int extraBitsMask = (0x00ff << extraBitsSize) & 0xff00; // Mask for extra bits extraction - - for (unsigned realPosition = 0; realPosition < sampleCount; ++realPosition, ++bufferPosition) { - if (bufferPosition >= sampleCount) bufferPosition %= sampleCount; - - extraBitsPosition = bufferPosition % HANTEK_CHANNELS; - - result.data[channel][realPosition] = - ((double)((unsigned short int)data[bufferPosition] + - (((unsigned short int)data[sampleCount + bufferPosition - extraBitsPosition] - << (8 - (HANTEK_CHANNELS - 1 - extraBitsPosition) * extraBitsSize)) & - extraBitsMask)) / - specification.voltageLimit[channel][controlsettings.voltage[channel].gain] - - controlsettings.voltage[channel].offsetReal) * - specification.gainSteps[controlsettings.voltage[channel].gain]; + unsigned extraBitsIndex = 8 - channel * 2; // Bit position offset for extra bits extraction + + for (unsigned realPosition = 0; realPosition < result.data[channel].size(); + ++realPosition, bufferPosition += HANTEK_CHANNELS) { + if (bufferPosition >= totalSampleCount) bufferPosition %= totalSampleCount; + + const unsigned short low = rawData[bufferPosition + HANTEK_CHANNELS - 1 - channel]; + const unsigned short high = + ((unsigned short int)rawData[totalSampleCount + bufferPosition] << extraBitsIndex) & + extraBitsMask; + + result.data[channel][realPosition] = ((double)(low + high) / limit - offset) * gainStep; } - } else { - for (unsigned realPosition = 0; realPosition < sampleCount; ++realPosition, ++bufferPosition) { - if (bufferPosition >= sampleCount) bufferPosition %= sampleCount; - - double dataBuf = (double)((int)data[bufferPosition]); - result.data[channel][realPosition] = - (dataBuf / specification.voltageLimit[channel][controlsettings.voltage[channel].gain] - - controlsettings.voltage[channel].offsetReal) * - specification.gainSteps[controlsettings.voltage[channel].gain]; + } else if (device->getUniqueModelID() == MODEL_DSO6022BE) { + // if device is 6022BE, drop heading & trailing samples + const unsigned DROP_DSO6022_HEAD = 0x410; + const unsigned DROP_DSO6022_TAIL = 0x3F0; + if (!isRollMode()) { + result.data[channel].resize(result.data[channel].size() - (DROP_DSO6022_HEAD + DROP_DSO6022_TAIL)); + // if device is 6022BE, offset DROP_DSO6022_HEAD incrementally + bufferPosition += DROP_DSO6022_HEAD * 2; } - } - } - } else { - QWriteLocker locker(&result.lock); - result.samplerate = controlsettings.samplerate.current; - result.append = isRollMode(); - - // Normal mode, channels are using their separate buffers - sampleCount = totalSampleCount / HANTEK_CHANNELS; - // if device is 6022BE, drop heading & trailing samples - if (device->getUniqueModelID() == MODEL_DSO6022BE) sampleCount -= (DROP_DSO6022_HEAD + DROP_DSO6022_TAIL); - for (int channel = 0; channel < HANTEK_CHANNELS; ++channel) { - if (controlsettings.voltage[channel].used) { - // Resize sample vector - if (result.data[channel].size() < sampleCount) { result.data[channel].resize(sampleCount); } - - // Convert data from the oscilloscope and write it into the sample - // buffer - unsigned bufferPosition = controlsettings.trigger.point * 2; - if (specification.sampleSize > 8) { - // Additional most significant bits after the normal data - unsigned extraBitsSize = specification.sampleSize - 8; // Number of extra bits - unsigned short int extraBitsMask = - (0x00ff << extraBitsSize) & 0xff00; // Mask for extra bits extraction - unsigned extraBitsIndex = 8 - channel * 2; // Bit position offset for extra bits extraction - - for (unsigned realPosition = 0; realPosition < sampleCount; - ++realPosition, bufferPosition += HANTEK_CHANNELS) { - if (bufferPosition >= totalSampleCount) bufferPosition %= totalSampleCount; - - result.data[channel][realPosition] = - ((double)((unsigned short int)data[bufferPosition + HANTEK_CHANNELS - 1 - channel] + - (((unsigned short int)data[totalSampleCount + bufferPosition] << extraBitsIndex) & - extraBitsMask)) / - specification.voltageLimit[channel][controlsettings.voltage[channel].gain] - - controlsettings.voltage[channel].offsetReal) * - specification.gainSteps[controlsettings.voltage[channel].gain]; - } - } else { - if (device->getUniqueModelID() == MODEL_DSO6022BE) { - bufferPosition += channel; - // if device is 6022BE, offset DROP_DSO6022_HEAD incrementally - bufferPosition += DROP_DSO6022_HEAD * 2; - } else - bufferPosition += HANTEK_CHANNELS - 1 - channel; - - for (unsigned realPosition = 0; realPosition < sampleCount; - ++realPosition, bufferPosition += HANTEK_CHANNELS) { - if (bufferPosition >= totalSampleCount) bufferPosition %= totalSampleCount; - - if (device->getUniqueModelID() == MODEL_DSO6022BE) { - double dataBuf = (double)((int)(data[bufferPosition] - 0x83)); - result.data[channel][realPosition] = - (dataBuf / specification.voltageLimit[channel][controlsettings.voltage[channel].gain]) * - specification.gainSteps[controlsettings.voltage[channel].gain]; - } else { - double dataBuf = (double)((int)(data[bufferPosition])); - result.data[channel][realPosition] = - (dataBuf / specification.voltageLimit[channel][controlsettings.voltage[channel].gain] - - controlsettings.voltage[channel].offsetReal) * - specification.gainSteps[controlsettings.voltage[channel].gain]; - } - } + bufferPosition += channel; + for (unsigned pos = 0; pos < result.data[channel].size(); ++pos, bufferPosition += HANTEK_CHANNELS) { + if (bufferPosition >= totalSampleCount) bufferPosition %= totalSampleCount; + double dataBuf = (double)((int)(rawData[bufferPosition] - 0x83)); + result.data[channel][pos] = (dataBuf / limit) * gainStep; } } else { - // Clear unused channels - result.data[channel].clear(); + bufferPosition += HANTEK_CHANNELS - 1 - channel; + for (unsigned pos = 0; pos < result.data[channel].size(); ++pos, bufferPosition += HANTEK_CHANNELS) { + if (bufferPosition >= totalSampleCount) bufferPosition %= totalSampleCount; + double dataBuf = (double)((int)(rawData[bufferPosition])); + result.data[channel][pos] = (dataBuf / limit - offset) * gainStep; + } } } } - - static unsigned id = 0; - ++id; - timestampDebug(QString("Received packet %1").arg(id)); - - emit samplesAvailable(); - - return errorCode; } -/// \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 HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate, bool maximum, unsigned *downsampler) { +double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate, bool maximum, + unsigned *downsampler) const { // 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; + const ControlSamplerateLimits *limits; if (fastRate) limits = &(specification.samplerate.multi); else limits = &(specification.samplerate.single); // Get downsampling factor that would provide the requested rate - double bestDownsampler = (double)limits->base / specification.bufferDividers[controlsettings.recordLengthId] / samplerate; + double bestDownsampler = + (double)limits->base / specification.bufferDividers[controlsettings.recordLengthId] / samplerate; // Base samplerate sufficient, or is the maximum better? if (bestDownsampler < 1.0 && (samplerate <= limits->max / specification.bufferDividers[controlsettings.recordLengthId] || !maximum)) { @@ -694,30 +634,17 @@ double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate, boo return bestSamplerate; } -/// \brief Get the count of samples that are expected returned by the scope. -/// \param fastRate Is set to the state of the fast rate mode when provided. -/// \return The total number of samples the scope should return. -unsigned HantekDsoControl::getSampleCount(bool *fastRate) { - unsigned totalSampleCount = getRecordLength(); - bool fastRateEnabled = controlsettings.samplerate.limits == &specification.samplerate.multi; - - if (totalSampleCount == UINT_MAX) { - // Roll mode - const int packetSize = device->getPacketSize(); - if (packetSize < 0) - totalSampleCount = UINT_MAX; - else - totalSampleCount = packetSize; +unsigned HantekDsoControl::getSampleCount() const { + if (isRollMode()) { + return device->getPacketSize(); } else { - if (!fastRateEnabled) totalSampleCount *= HANTEK_CHANNELS; + if (isFastRate()) + return getRecordLength(); + else + return getRecordLength() * HANTEK_CHANNELS; } - if (fastRate) *fastRate = fastRateEnabled; - return totalSampleCount; } -/// \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 HantekDsoControl::updateRecordLength(unsigned index) { if (index >= (unsigned)controlsettings.samplerate.limits->recordLengths.size()) return 0; @@ -754,7 +681,8 @@ unsigned HantekDsoControl::updateRecordLength(unsigned index) { } // Check if the divider has changed and adapt samplerate limits accordingly - bool bDividerChanged = specification.bufferDividers[index] != specification.bufferDividers[controlsettings.recordLengthId]; + bool bDividerChanged = + specification.bufferDividers[index] != specification.bufferDividers[controlsettings.recordLengthId]; controlsettings.recordLengthId = index; @@ -768,11 +696,6 @@ unsigned HantekDsoControl::updateRecordLength(unsigned index) { return controlsettings.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 HantekDsoControl::updateSamplerate(unsigned downsampler, bool fastRate) { // Get samplerate limits Hantek::ControlSamplerateLimits *limits = @@ -872,8 +795,8 @@ unsigned HantekDsoControl::updateSamplerate(unsigned downsampler, bool fastRate) controlsettings.samplerate.downsampler = downsampler; if (downsampler) - controlsettings.samplerate.current = - controlsettings.samplerate.limits->base / specification.bufferDividers[controlsettings.recordLengthId] / downsampler; + controlsettings.samplerate.current = controlsettings.samplerate.limits->base / + specification.bufferDividers[controlsettings.recordLengthId] / downsampler; else controlsettings.samplerate.current = controlsettings.samplerate.limits->max / specification.bufferDividers[controlsettings.recordLengthId]; @@ -894,7 +817,6 @@ unsigned HantekDsoControl::updateSamplerate(unsigned downsampler, bool fastRate) return downsampler; } -/// \brief Restore the samplerate/timebase targets after divider updates. void HantekDsoControl::restoreTargets() { if (controlsettings.samplerate.target.samplerateSet) this->setSamplerate(); @@ -902,7 +824,6 @@ void HantekDsoControl::restoreTargets() { this->setRecordTime(); } -/// \brief Update the minimum and maximum supported samplerate. void HantekDsoControl::updateSamplerateLimits() { // Works only if the minimum samplerate for normal mode is lower than for fast // rate mode, which is the case for all models @@ -917,24 +838,24 @@ void HantekDsoControl::updateSamplerateLimits() { /// \brief Sets the size of the oscilloscopes sample buffer. /// \param index The record length index that should be set. /// \return The record length that has been set, 0 on error. -unsigned HantekDsoControl::setRecordLength(unsigned index) { - if (!device->isConnected()) return 0; +Dso::ErrorCode HantekDsoControl::setRecordLength(unsigned index) { + if (!device->isConnected()) return Dso::ErrorCode::ERROR_CONNECTION; - if (!this->updateRecordLength(index)) return 0; + if (!this->updateRecordLength(index)) return Dso::ErrorCode::ERROR_PARAMETER; this->restoreTargets(); this->setPretriggerPosition(controlsettings.trigger.position); emit recordLengthChanged(getRecordLength()); - return getRecordLength(); + return Dso::ErrorCode::ERROR_NONE; } /// \brief Sets the samplerate of the oscilloscope. /// \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 HantekDsoControl::setSamplerate(double samplerate) { - if (!device->isConnected()) return 0.0; +Dso::ErrorCode HantekDsoControl::setSamplerate(double samplerate) { + if (!device->isConnected()) return Dso::ErrorCode::ERROR_CONNECTION; if (samplerate == 0.0) { samplerate = controlsettings.samplerate.target.samplerate; @@ -946,19 +867,19 @@ double HantekDsoControl::setSamplerate(double samplerate) { if (!specification.isSoftwareTriggerDevice) { // When possible, enable fast rate if it is required to reach the requested // samplerate - bool fastRate = - (controlsettings.usedChannels <= 1) && - (samplerate > specification.samplerate.single.max / specification.bufferDividers[controlsettings.recordLengthId]); + bool fastRate = (controlsettings.usedChannels <= 1) && + (samplerate > specification.samplerate.single.max / + specification.bufferDividers[controlsettings.recordLengthId]); // What is the nearest, at least as high samplerate the scope can provide? unsigned downsampler = 0; - double bestSamplerate = getBestSamplerate(samplerate, fastRate, false, &(downsampler)); + getBestSamplerate(samplerate, fastRate, false, &(downsampler)); // Set the calculated samplerate if (this->updateSamplerate(downsampler, fastRate) == UINT_MAX) - return 0.0; + return Dso::ErrorCode::ERROR_PARAMETER; else { - return bestSamplerate; + return Dso::ErrorCode::ERROR_NONE; } } else { int sampleId; @@ -977,7 +898,7 @@ double HantekDsoControl::setSamplerate(double samplerate) { emit recordTimeChanged((double)(getRecordLength() - sampleMargin) / controlsettings.samplerate.current); emit samplerateChanged(controlsettings.samplerate.current); - return samplerate; + return Dso::ErrorCode::ERROR_NONE; } } @@ -985,8 +906,8 @@ double HantekDsoControl::setSamplerate(double 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 HantekDsoControl::setRecordTime(double duration) { - if (!device->isConnected()) return 0.0; +Dso::ErrorCode HantekDsoControl::setRecordTime(double duration) { + if (!device->isConnected()) return Dso::ErrorCode::ERROR_CONNECTION; if (duration == 0.0) { duration = controlsettings.samplerate.target.duration; @@ -1004,18 +925,17 @@ double HantekDsoControl::setRecordTime(double duration) { // When possible, enable fast rate if the record time can't be set that low // to improve resolution bool fastRate = (controlsettings.usedChannels <= 1) && - (maxSamplerate >= - specification.samplerate.multi.base / specification.bufferDividers[controlsettings.recordLengthId]); + (maxSamplerate >= specification.samplerate.multi.base / + specification.bufferDividers[controlsettings.recordLengthId]); // What is the nearest, at most as high samplerate the scope can provide? unsigned downsampler = 0; - double bestSamplerate = getBestSamplerate(maxSamplerate, fastRate, true, &(downsampler)); // Set the calculated samplerate if (this->updateSamplerate(downsampler, fastRate) == UINT_MAX) - return 0.0; + return Dso::ErrorCode::ERROR_PARAMETER; else { - return (double)getRecordLength() / bestSamplerate; + return Dso::ErrorCode::ERROR_NONE; } } else { // For now - we go for the 10240 size sampling - the other seems not to be @@ -1039,7 +959,7 @@ double HantekDsoControl::setRecordTime(double duration) { controlsettings.samplerate.current = specification.sampleSteps[sampleId]; emit samplerateChanged(controlsettings.samplerate.current); - return controlsettings.samplerate.current; + return Dso::ErrorCode::ERROR_NONE; } } @@ -1047,10 +967,10 @@ double HantekDsoControl::setRecordTime(double duration) { /// \param channel The channel that should be set. /// \param used true if the channel should be sampled. /// \return See ::Dso::ErrorCode. -int HantekDsoControl::setChannelUsed(unsigned channel, bool used) { - if (!device->isConnected()) return Dso::ERROR_CONNECTION; +Dso::ErrorCode HantekDsoControl::setChannelUsed(unsigned channel, bool used) { + if (!device->isConnected()) return Dso::ErrorCode::ERROR_CONNECTION; - if (channel >= HANTEK_CHANNELS) return Dso::ERROR_PARAMETER; + if (channel >= HANTEK_CHANNELS) return Dso::ErrorCode::ERROR_PARAMETER; // Update settings controlsettings.voltage[channel].used = used; @@ -1105,17 +1025,17 @@ int HantekDsoControl::setChannelUsed(unsigned channel, bool used) { if (fastRateChanged) this->updateSamplerateLimits(); - return Dso::ERROR_NONE; + return Dso::ErrorCode::ERROR_NONE; } /// \brief Set the coupling for the given channel. /// \param channel The channel that should be set. /// \param coupling The new coupling for the channel. /// \return See ::Dso::ErrorCode. -int HantekDsoControl::setCoupling(unsigned channel, Dso::Coupling coupling) { - if (!device->isConnected()) return Dso::ERROR_CONNECTION; +Dso::ErrorCode HantekDsoControl::setCoupling(unsigned channel, Dso::Coupling coupling) { + if (!device->isConnected()) return Dso::ErrorCode::ERROR_CONNECTION; - if (channel >= HANTEK_CHANNELS) return Dso::ERROR_PARAMETER; + if (channel >= HANTEK_CHANNELS) return Dso::ErrorCode::ERROR_PARAMETER; // SetRelays control command for coupling relays if (specification.supportsCouplingRelays) { @@ -1124,17 +1044,18 @@ int HantekDsoControl::setCoupling(unsigned channel, Dso::Coupling coupling) { this->controlPending[CONTROLINDEX_SETRELAYS] = true; } - return Dso::ERROR_NONE; + return Dso::ErrorCode::ERROR_NONE; } /// \brief Sets the gain for the given channel. +/// Get the actual gain by specification.gainSteps[gainId] /// \param channel The channel that should be set. /// \param gain The gain that should be met (V/div). /// \return The gain that has been set, ::Dso::ErrorCode on error. -double HantekDsoControl::setGain(unsigned channel, double gain) { - if (!device->isConnected()) return Dso::ERROR_CONNECTION; +Dso::ErrorCode HantekDsoControl::setGain(unsigned channel, double gain) { + if (!device->isConnected()) return Dso::ErrorCode::ERROR_CONNECTION; - if (channel >= HANTEK_CHANNELS) return Dso::ERROR_PARAMETER; + if (channel >= HANTEK_CHANNELS) return Dso::ErrorCode::ERROR_PARAMETER; // Find lowest gain voltage thats at least as high as the requested int gainId; @@ -1168,30 +1089,25 @@ double HantekDsoControl::setGain(unsigned channel, double gain) { this->setOffset(channel, controlsettings.voltage[channel].offset); - return specification.gainSteps[gainId]; + return Dso::ErrorCode::ERROR_NONE; } /// \brief Set the offset for the given channel. +/// Get the actual offset for the channel from controlsettings.voltage[channel].offsetReal /// \param channel The channel that should be set. /// \param offset The new offset value (0.0 - 1.0). -/// \return The offset that has been set, ::Dso::ErrorCode on error. -double HantekDsoControl::setOffset(unsigned channel, double offset) { - if (!device->isConnected()) return Dso::ERROR_CONNECTION; +Dso::ErrorCode HantekDsoControl::setOffset(unsigned channel, double offset) { + if (!device->isConnected()) return Dso::ErrorCode::ERROR_CONNECTION; - if (channel >= HANTEK_CHANNELS) return Dso::ERROR_PARAMETER; + if (channel >= HANTEK_CHANNELS) return Dso::ErrorCode::ERROR_PARAMETER; + unsigned short *channelOffLimit = specification.offsetLimit[channel][controlsettings.voltage[channel].gain]; // Calculate the offset value // The range is given by the calibration data (convert from big endian) - unsigned short int minimum = - ((unsigned short int)*( - (unsigned char *)&(specification.offsetLimit[channel][controlsettings.voltage[channel].gain][OFFSET_START])) - << 8) + - *((unsigned char *)&(specification.offsetLimit[channel][controlsettings.voltage[channel].gain][OFFSET_START]) + 1); - unsigned short int maximum = - ((unsigned short int)*( - (unsigned char *)&(specification.offsetLimit[channel][controlsettings.voltage[channel].gain][OFFSET_END])) - << 8) + - *((unsigned char *)&(specification.offsetLimit[channel][controlsettings.voltage[channel].gain][OFFSET_END]) + 1); + unsigned short int minimum = ((unsigned short int)*((unsigned char *)&(channelOffLimit[OFFSET_START])) << 8) + + *((unsigned char *)&(channelOffLimit[OFFSET_START]) + 1); + unsigned short int maximum = ((unsigned short int)*((unsigned char *)&(channelOffLimit[OFFSET_END])) << 8) + + *((unsigned char *)&(channelOffLimit[OFFSET_END]) + 1); unsigned short int offsetValue = offset * (maximum - minimum) + minimum + 0.5; double offsetReal = (double)(offsetValue - minimum) / (maximum - minimum); @@ -1205,28 +1121,29 @@ double HantekDsoControl::setOffset(unsigned channel, double offset) { this->setTriggerLevel(channel, controlsettings.trigger.level[channel]); - return offsetReal; + return Dso::ErrorCode::ERROR_NONE; } /// \brief Set the trigger mode. /// \return See ::Dso::ErrorCode. -int HantekDsoControl::setTriggerMode(Dso::TriggerMode mode) { - if (!device->isConnected()) return Dso::ERROR_CONNECTION; +Dso::ErrorCode HantekDsoControl::setTriggerMode(Dso::TriggerMode mode) { + if (!device->isConnected()) return Dso::ErrorCode::ERROR_CONNECTION; - if (mode < Dso::TRIGGERMODE_AUTO || mode >= Dso::TRIGGERMODE_COUNT) return Dso::ERROR_PARAMETER; + if (mode < Dso::TRIGGERMODE_AUTO || mode >= Dso::TRIGGERMODE_COUNT) return Dso::ErrorCode::ERROR_PARAMETER; controlsettings.trigger.mode = mode; - return Dso::ERROR_NONE; + return Dso::ErrorCode::ERROR_NONE; } /// \brief Set the trigger source. /// \param special true for a special channel (EXT, ...) as trigger source. /// \param id The number of the channel, that should be used as trigger. /// \return See ::Dso::ErrorCode. -int HantekDsoControl::setTriggerSource(bool special, unsigned id) { - if (!device->isConnected()) return Dso::ERROR_CONNECTION; +Dso::ErrorCode HantekDsoControl::setTriggerSource(bool special, unsigned id) { + if (!device->isConnected()) return Dso::ErrorCode::ERROR_CONNECTION; - if ((!special && id >= HANTEK_CHANNELS) || (special && id >= HANTEK_SPECIAL_CHANNELS)) return Dso::ERROR_PARAMETER; + if ((!special && id >= HANTEK_CHANNELS) || (special && id >= HANTEK_SPECIAL_CHANNELS)) + return Dso::ErrorCode::ERROR_PARAMETER; switch (specification.command.bulk.setTrigger) { case BULK_SETTRIGGERANDSAMPLERATE: @@ -1251,7 +1168,7 @@ int HantekDsoControl::setTriggerSource(bool special, unsigned id) { break; default: - return Dso::ERROR_UNSUPPORTED; + return Dso::ErrorCode::ERROR_UNSUPPORTED; } // SetRelays control command for external trigger relay @@ -1269,32 +1186,27 @@ int HantekDsoControl::setTriggerSource(bool special, unsigned id) { } else this->setTriggerLevel(id, controlsettings.trigger.level[id]); - return Dso::ERROR_NONE; + return Dso::ErrorCode::ERROR_NONE; } /// \brief Set the trigger level. /// \param channel The channel that should be set. /// \param level The new trigger level (V). /// \return The trigger level that has been set, ::Dso::ErrorCode on error. -double HantekDsoControl::setTriggerLevel(unsigned channel, double level) { - if (!device->isConnected()) return Dso::ERROR_CONNECTION; +Dso::ErrorCode HantekDsoControl::setTriggerLevel(unsigned channel, double level) { + if (!device->isConnected()) return Dso::ErrorCode::ERROR_CONNECTION; - if (channel >= HANTEK_CHANNELS) return Dso::ERROR_PARAMETER; + if (channel >= HANTEK_CHANNELS) return Dso::ErrorCode::ERROR_PARAMETER; // Calculate the trigger level value - unsigned short int minimum, maximum; - if (specification.sampleSize>8) { + unsigned short minimum, maximum; + if (specification.sampleSize > 8) { + const unsigned short *offsetLimit = specification.offsetLimit[channel][controlsettings.voltage[channel].gain]; // The range is the same as used for the offsets for 10 bit models - minimum = - ((unsigned short int)*( - (unsigned char *)&(specification.offsetLimit[channel][controlsettings.voltage[channel].gain][OFFSET_START])) - << 8) + - *((unsigned char *)&(specification.offsetLimit[channel][controlsettings.voltage[channel].gain][OFFSET_START]) + 1); - maximum = - ((unsigned short int)*( - (unsigned char *)&(specification.offsetLimit[channel][controlsettings.voltage[channel].gain][OFFSET_END])) - << 8) + - *((unsigned char *)&(specification.offsetLimit[channel][controlsettings.voltage[channel].gain][OFFSET_END]) + 1); + minimum = ((unsigned short int)*((unsigned char *)&(offsetLimit[OFFSET_START])) << 8) + + *((unsigned char *)&(offsetLimit[OFFSET_START]) + 1); + maximum = ((unsigned short int)*((unsigned char *)&(offsetLimit[OFFSET_END])) << 8) + + *((unsigned char *)&(offsetLimit[OFFSET_END]) + 1); } else { // It's from 0x00 to 0xfd for the 8 bit models minimum = 0x00; @@ -1302,14 +1214,10 @@ double HantekDsoControl::setTriggerLevel(unsigned channel, double level) { } // Never get out of the limits - unsigned short int levelValue = - qBound((long int)minimum, - (long int)((controlsettings.voltage[channel].offsetReal + - level / specification.gainSteps[controlsettings.voltage[channel].gain]) * - (maximum - minimum) + - 0.5) + - minimum, - (long int)maximum); + const double offsetReal = controlsettings.voltage[channel].offsetReal; + const double gainStep = specification.gainSteps[controlsettings.voltage[channel].gain]; + unsigned short levelValue = qBound( + minimum, (unsigned short)(((offsetReal + level / gainStep) * (maximum - minimum) + 0.5) + minimum), maximum); // Check if the set channel is the trigger source if (!controlsettings.trigger.special && channel == controlsettings.trigger.source && specification.supportsOffset) { @@ -1321,17 +1229,16 @@ double HantekDsoControl::setTriggerLevel(unsigned channel, double level) { /// \todo Get alternating trigger in here controlsettings.trigger.level[channel] = level; - return (double)((levelValue - minimum) / (maximum - minimum) - controlsettings.voltage[channel].offsetReal) * - specification.gainSteps[controlsettings.voltage[channel].gain]; + return Dso::ErrorCode::ERROR_NONE; } /// \brief Set the trigger slope. /// \param slope The Slope that should cause a trigger. /// \return See ::Dso::ErrorCode. -int HantekDsoControl::setTriggerSlope(Dso::Slope slope) { - if (!device->isConnected()) return Dso::ERROR_CONNECTION; +Dso::ErrorCode HantekDsoControl::setTriggerSlope(Dso::Slope slope) { + if (!device->isConnected()) return Dso::ErrorCode::ERROR_CONNECTION; - if (slope >= Dso::SLOPE_COUNT) return Dso::ERROR_PARAMETER; + if (slope >= Dso::SLOPE_COUNT) return Dso::ErrorCode::ERROR_PARAMETER; switch (specification.command.bulk.setTrigger) { case BULK_SETTRIGGERANDSAMPLERATE: { @@ -1353,23 +1260,20 @@ int HantekDsoControl::setTriggerSlope(Dso::Slope slope) { break; } default: - return Dso::ERROR_UNSUPPORTED; + return Dso::ErrorCode::ERROR_UNSUPPORTED; } controlsettings.trigger.slope = slope; - return Dso::ERROR_NONE; + return Dso::ErrorCode::ERROR_NONE; } -int HantekDsoControl::forceTrigger() { - commandPending[BULK_FORCETRIGGER] = true; - return 0; -} +void HantekDsoControl::forceTrigger() { commandPending[BULK_FORCETRIGGER] = true; } /// \brief Set the trigger position. /// \param position The new trigger position (in s). /// \return The trigger position that has been set. -double HantekDsoControl::setPretriggerPosition(double position) { - if (!device->isConnected()) return -2; +Dso::ErrorCode HantekDsoControl::setPretriggerPosition(double position) { + if (!device->isConnected()) return Dso::ErrorCode::ERROR_CONNECTION; // All trigger positions are measured in samples unsigned positionSamples = position * controlsettings.samplerate.current; @@ -1415,23 +1319,23 @@ double HantekDsoControl::setPretriggerPosition(double position) { break; } default: - return Dso::ERROR_UNSUPPORTED; + return Dso::ErrorCode::ERROR_UNSUPPORTED; } controlsettings.trigger.position = position; - return (double)positionSamples / controlsettings.samplerate.current; + return Dso::ErrorCode::ERROR_NONE; } -int HantekDsoControl::stringCommand(const QString &commandString) { - if (!device->isConnected()) return Dso::ERROR_CONNECTION; +Dso::ErrorCode HantekDsoControl::stringCommand(const QString &commandString) { + if (!device->isConnected()) return Dso::ErrorCode::ERROR_CONNECTION; QStringList commandParts = commandString.split(' ', QString::SkipEmptyParts); - if (commandParts.count() < 1) return Dso::ERROR_PARAMETER; + if (commandParts.count() < 1) return Dso::ErrorCode::ERROR_PARAMETER; - if (commandParts[0] != "send") return Dso::ERROR_UNSUPPORTED; + if (commandParts[0] != "send") return Dso::ErrorCode::ERROR_UNSUPPORTED; - if (commandParts.count() < 2) return Dso::ERROR_PARAMETER; + if (commandParts.count() < 2) return Dso::ErrorCode::ERROR_PARAMETER; if (commandParts[1] == "bulk") { QString data = commandString.section(' ', 2, -1, QString::SectionSkipEmpty); @@ -1439,12 +1343,12 @@ int HantekDsoControl::stringCommand(const QString &commandString) { // Read command code (First byte) hexParse(commandParts[2], &commandCode, 1); - if (commandCode > BULK_COUNT) return Dso::ERROR_UNSUPPORTED; + if (commandCode > BULK_COUNT) return Dso::ErrorCode::ERROR_UNSUPPORTED; // Update bulk command and mark as pending hexParse(data, command[commandCode]->data(), command[commandCode]->getSize()); commandPending[commandCode] = true; - return Dso::ERROR_NONE; + return Dso::ErrorCode::ERROR_NONE; } else if (commandParts[1] == "control") { unsigned char controlCode = 0; @@ -1454,16 +1358,16 @@ int HantekDsoControl::stringCommand(const QString &commandString) { for (cIndex = 0; cIndex < CONTROLINDEX_COUNT; ++cIndex) { if (this->controlCode[cIndex] == controlCode) break; } - if (cIndex >= CONTROLINDEX_COUNT) return Dso::ERROR_UNSUPPORTED; + if (cIndex >= CONTROLINDEX_COUNT) return Dso::ErrorCode::ERROR_UNSUPPORTED; QString data = commandString.section(' ', 3, -1, QString::SectionSkipEmpty); // Update control command and mark as pending hexParse(data, this->control[cIndex]->data(), this->control[cIndex]->getSize()); this->controlPending[cIndex] = true; - return Dso::ERROR_NONE; + return Dso::ErrorCode::ERROR_NONE; } else - return Dso::ERROR_UNSUPPORTED; + return Dso::ErrorCode::ERROR_UNSUPPORTED; } void HantekDsoControl::run() { @@ -1479,11 +1383,8 @@ void HantekDsoControl::run() { errorCode = device->bulkCommand(command[cIndex]); if (errorCode < 0) { qWarning("Sending bulk command %02x failed: %s", cIndex, libUsbErrorString(errorCode).toLocal8Bit().data()); - - if (errorCode == LIBUSB_ERROR_NO_DEVICE) { - emit communicationError(); - return; - } + emit communicationError(); + return; } else commandPending[cIndex] = false; } @@ -1570,13 +1471,13 @@ void HantekDsoControl::run() { break; - case ROLL_GETDATA: - // Get data and process it, if we're still sampling - errorCode = this->getSamples(this->_samplingStarted); - if (errorCode < 0) - qWarning("Getting sample data failed: %s", libUsbErrorString(errorCode).toLocal8Bit().data()); - else - timestampDebug(QString("Received %1 B of sampling data").arg(errorCode)); + case ROLL_GETDATA: { + std::vector rawData = this->getSamples(previousSampleCount); + if (this->_samplingStarted) { + convertRawDataToSamples(rawData); + emit samplesAvailable(); + } + } // Check if we're in single trigger mode if (controlsettings.trigger.mode == Dso::TRIGGERMODE_SINGLE && this->_samplingStarted) this->stopSampling(); @@ -1598,23 +1499,25 @@ void HantekDsoControl::run() { this->rollState = ROLL_STARTSAMPLING; const int lastCaptureState = this->captureState; - this->captureState = this->getCaptureState(); - if (this->captureState < 0) - qWarning("Getting capture state failed: %s", libUsbErrorString(this->captureState).toLocal8Bit().data()); - - else if (this->captureState != lastCaptureState) + unsigned triggerPoint; + std::tie(captureState, triggerPoint) = this->getCaptureState(); + controlsettings.trigger.point = calculateTriggerPoint(triggerPoint); + if (this->captureState < 0) { + qWarning() << tr("Getting capture state failed: %1").arg(libUsbErrorString(this->captureState)); + emit statusMessage(tr("Getting capture state failed: %1").arg(libUsbErrorString(this->captureState)), 0); + } else if (this->captureState != lastCaptureState) timestampDebug(QString("Capture state changed to %1").arg(this->captureState)); switch (this->captureState) { case CAPTURE_READY: case CAPTURE_READY2250: - case CAPTURE_READY5200: - // Get data and process it, if we're still sampling - errorCode = this->getSamples(this->_samplingStarted); - if (errorCode < 0) - qWarning("Getting sample data failed: %s", libUsbErrorString(errorCode).toLocal8Bit().data()); - else - timestampDebug(QString("Received %1 B of sampling data").arg(errorCode)); + case CAPTURE_READY5200: { + std::vector rawData = this->getSamples(previousSampleCount); + if (this->_samplingStarted) { + convertRawDataToSamples(rawData); + emit samplesAvailable(); + } + } // Check if we're in single trigger mode if (controlsettings.trigger.mode == Dso::TRIGGERMODE_SINGLE && this->_samplingStarted) this->stopSampling(); diff --git a/openhantek/src/hantek/hantekdsocontrol.h b/openhantek/src/hantek/hantekdsocontrol.h index eee81b2..45ca4d9 100644 --- a/openhantek/src/hantek/hantekdsocontrol.h +++ b/openhantek/src/hantek/hantekdsocontrol.h @@ -27,10 +27,13 @@ class HantekDsoControl : public QObject { * Creates a dsoControl object. The actual event loop / timer is not started. * You can optionally create a thread and move the created object to the * thread. - * You need to call updateInterval() to start the timer. - * @param device + * You need to call updateInterval() to start the timer. This is done implicitly + * if run() is called. + * @param device The usb device. This object does not take ownership. */ HantekDsoControl(USBDevice *device); + + /// \brief Cleans up ~HantekDsoControl(); /// Call this to start the processing. This method will call itself @@ -39,13 +42,29 @@ class HantekDsoControl : public QObject { /// there. void run(); + /// \brief Gets the physical channel count for this oscilloscope. + /// \return The number of physical channels. unsigned getChannelCount(); - QList *getAvailableRecordLengths(); + + /// \brief Get available record lengths for this oscilloscope. + /// \return The number of physical channels, empty list for continuous. + const std::vector &getAvailableRecordLengths(); + + /// \brief Get minimum samplerate for this oscilloscope. + /// \return The minimum samplerate for the current configuration in S/s. double getMinSamplerate(); + + /// \brief Get maximum samplerate for this oscilloscope. + /// \return The maximum samplerate for the current configuration in S/s. double getMaxSamplerate(); + /// \brief Get a list of the names of the special trigger sources. const QStringList *getSpecialTriggerSources(); + + /// Return the associated usb device. const USBDevice *getDevice() const; + + /// Return the last sample set const DSOsamples &getLastSamples(); /// \brief Sends bulk/control commands directly. @@ -59,63 +78,91 @@ class HantekDsoControl : public QObject { ///

/// \param command The command as string (Has to be parsed). /// \return See ::Dso::ErrorCode. - int stringCommand(const QString &commandString); - signals: - void samplingStarted(); ///< The oscilloscope started sampling/waiting for trigger - void samplingStopped(); ///< The oscilloscope stopped sampling/waiting for trigger - void statusMessage(const QString &message, int timeout); ///< Status message about the oscilloscope - void samplesAvailable(); ///< New sample data is available + Dso::ErrorCode stringCommand(const QString &commandString); + + private: + bool isRollMode() const; + bool isFastRate() const; + int getRecordLength() const; + + /// \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 getBestSamplerate(double samplerate, bool fastRate = false, bool maximum = false, + unsigned *downsampler = 0) const; + + /// Get the number of samples that are expected returned by the scope. + /// In rolling mode this is depends on the usb speed and packet size. + /// \return The total number of samples the scope should return. + unsigned getSampleCount() const; - 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 - 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 samplerateSet(int mode, QList sampleSteps); ///< The samplerate has changed + void updateInterval(); - void communicationError(); + /// \brief Calculates the trigger point from the CommandGetCaptureState data. + /// \param value The data value that contains the trigger point. + /// \return The calculated trigger point for the given data. + static unsigned calculateTriggerPoint(unsigned value); - protected: - bool isRollMode(); - int getRecordLength(); - void updateInterval(); - unsigned calculateTriggerPoint(unsigned value); - int getCaptureState(); - int getSamples(bool process); - double getBestSamplerate(double samplerate, bool fastRate = false, bool maximum = false, unsigned *downsampler = 0); - unsigned getSampleCount(bool *fastRate = 0); + /// \brief Gets the current state. + /// \return The current CaptureState of the oscilloscope. + std::pair getCaptureState() const; + + /// \brief Gets sample data from the oscilloscope + std::vector getSamples(unsigned &previousSampleCount) const; + + /// \brief Converts raw oscilloscope data to sample data + void convertRawDataToSamples(const std::vector &rawData); + + /// \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 updateRecordLength(unsigned size); + + /// \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 updateSamplerate(unsigned downsampler, bool fastRate); + + /// \brief Restore the samplerate/timebase targets after divider updates. void restoreTargets(); + + /// \brief Update the minimum and maximum supported samplerate. void updateSamplerateLimits(); + private: // Communication with device USBDevice *device; ///< The USB device for the oscilloscope bool sampling = false; ///< true, if the oscilloscope is taking samples - QStringList specialTriggerSources = {tr("EXT"),tr("EXT/10")}; ///< Names of the special trigger sources + QStringList specialTriggerSources = {tr("EXT"), tr("EXT/10")}; ///< Names of the special trigger sources DataArray *command[Hantek::BULK_COUNT] = {0}; ///< Pointers to bulk - /// commands, ready to + /// commands, ready to /// be transmitted - bool commandPending[Hantek::BULK_COUNT] = {false}; ///< true, when the command should be - /// executed + bool commandPending[Hantek::BULK_COUNT] = {false}; ///< true, when the command should be + /// executed DataArray *control[Hantek::CONTROLINDEX_COUNT] = {0}; ///< Pointers to control commands - unsigned char controlCode[Hantek::CONTROLINDEX_COUNT]; ///< Request codes for - /// control commands - bool controlPending[Hantek::CONTROLINDEX_COUNT]= {false}; ///< true, when the control + unsigned char controlCode[Hantek::CONTROLINDEX_COUNT]; ///< Request codes for + /// control commands + bool controlPending[Hantek::CONTROLINDEX_COUNT] = {false}; ///< true, when the control /// command should be executed // Device setup Hantek::ControlSpecification specification; ///< The specifications of the device - Hantek::ControlSettings controlsettings; ///< The current settings of the device + Hantek::ControlSettings controlsettings; ///< The current settings of the device // Results DSOsamples result; unsigned previousSampleCount = 0; ///< The expected total number of samples at - /// the last check before sampling started + /// the last check before sampling started // State of the communication thread int captureState = Hantek::CAPTURE_WAITING; @@ -130,19 +177,36 @@ class HantekDsoControl : public QObject { void startSampling(); void stopSampling(); - unsigned setRecordLength(unsigned size); - double setSamplerate(double samplerate = 0.0); - double setRecordTime(double duration = 0.0); - - int setChannelUsed(unsigned channel, bool used); - int setCoupling(unsigned channel, Dso::Coupling coupling); - double setGain(unsigned channel, double gain); - double setOffset(unsigned channel, double offset); - - int setTriggerMode(Dso::TriggerMode mode); - int setTriggerSource(bool special, unsigned id); - double setTriggerLevel(unsigned channel, double level); - int setTriggerSlope(Dso::Slope slope); - double setPretriggerPosition(double position); - int forceTrigger(); + Dso::ErrorCode setRecordLength(unsigned size); + Dso::ErrorCode setSamplerate(double samplerate = 0.0); + Dso::ErrorCode setRecordTime(double duration = 0.0); + + Dso::ErrorCode setChannelUsed(unsigned channel, bool used); + Dso::ErrorCode setCoupling(unsigned channel, Dso::Coupling coupling); + Dso::ErrorCode setGain(unsigned channel, double gain); + Dso::ErrorCode setOffset(unsigned channel, double offset); + + Dso::ErrorCode setTriggerMode(Dso::TriggerMode mode); + Dso::ErrorCode setTriggerSource(bool special, unsigned id); + Dso::ErrorCode setTriggerLevel(unsigned channel, double level); + Dso::ErrorCode setTriggerSlope(Dso::Slope slope); + Dso::ErrorCode setPretriggerPosition(double position); + void forceTrigger(); + + signals: + void samplingStarted(); ///< The oscilloscope started sampling/waiting for trigger + void samplingStopped(); ///< The oscilloscope stopped sampling/waiting for trigger + void statusMessage(const QString &message, int timeout); ///< Status message about the oscilloscope + void samplesAvailable(); ///< New sample data is available + + void availableRecordLengthsChanged(const std::vector &recordLengths); ///< The available record + /// lengths, empty list for + + void samplerateLimitsChanged(double minimum, double maximum); ///< The minimum or maximum samplerate has changed + 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 samplerateSet(int mode, QList sampleSteps); ///< The samplerate has changed + + void communicationError() const; }; diff --git a/openhantek/src/main.cpp b/openhantek/src/main.cpp index f08ffb1..51dc880 100644 --- a/openhantek/src/main.cpp +++ b/openhantek/src/main.cpp @@ -93,19 +93,22 @@ int main(int argc, char *argv[]) { QString modelName = QString::fromStdString(i->getModel().name); if (i->needsFirmware()) { - w->addItem(QCoreApplication::translate("Firmware upload dialog", "%1: Firmware upload failed").arg(modelName)); + w->addItem( + QCoreApplication::translate("Firmware upload dialog", "%1: Firmware upload failed").arg(modelName)); continue; } QString errorMessage; if (i->connectDevice(errorMessage)) { w->addItem(QCoreApplication::translate("Firmware upload dialog", "%1: Ready").arg(modelName)); - w->setCurrentRow(w->count()-1); + w->setCurrentRow(w->count() - 1); } else { - w->addItem(QCoreApplication::translate("Firmware upload dialog", "%1: %2").arg(modelName).arg(findDevices.getErrorMessage())); + w->addItem(QCoreApplication::translate("Firmware upload dialog", "%1: %2") + .arg(modelName) + .arg(findDevices.getErrorMessage())); } } - if (w->currentRow() == -1 || devices.size()>1) { + if (w->currentRow() == -1 || devices.size() > 1) { QPushButton *btn = new QPushButton(QCoreApplication::translate("", "Connect to first device"), dialog.get()); dialog->move(QApplication::desktop()->screen()->rect().center() - w->rect().center()); dialog->setWindowTitle(QCoreApplication::translate("", "Firmware upload")); diff --git a/openhantek/src/mainwindow.cpp b/openhantek/src/mainwindow.cpp index 103b396..5d3d369 100644 --- a/openhantek/src/mainwindow.cpp +++ b/openhantek/src/mainwindow.cpp @@ -262,12 +262,12 @@ void OpenHantekMainWindow::addManualCommandEdit() { commandEdit->setFocus(); }); connect(commandEdit, &QLineEdit::returnPressed, [this]() { - int errorCode = dsoControl->stringCommand(commandEdit->text()); + Dso::ErrorCode errorCode = dsoControl->stringCommand(commandEdit->text()); commandEdit->hide(); commandEdit->clear(); - if (errorCode < 0) statusBar()->showMessage(tr("Invalid command"), 3000); + if (errorCode != Dso::ErrorCode::ERROR_NONE) statusBar()->showMessage(tr("Invalid command"), 3000); }); } @@ -343,10 +343,12 @@ void OpenHantekMainWindow::applySettingsToDevice() { samplerateSelected(); else timebaseSelected(); - if (dsoControl->getAvailableRecordLengths()->isEmpty()) + if (dsoControl->getAvailableRecordLengths().empty()) dsoControl->setRecordLength(settings->scope.horizontal.recordLength); else { - int index = dsoControl->getAvailableRecordLengths()->indexOf(settings->scope.horizontal.recordLength); + auto recLenVec = dsoControl->getAvailableRecordLengths(); + ptrdiff_t index = std::distance( + recLenVec.begin(), std::find(recLenVec.begin(), recLenVec.end(), settings->scope.horizontal.recordLength)); dsoControl->setRecordLength(index < 0 ? 1 : index); } dsoControl->setTriggerMode(settings->scope.trigger.mode);