Dokit
Internal development documentation
Loading...
Searching...
No Matches
dsocommand.cpp
1// SPDX-FileCopyrightText: 2022-2026 Paul Colby <git@colby.id.au>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4#include "dsocommand.h"
6
8
9#include <QJsonDocument>
10#include <QJsonObject>
11
12#include <iostream>
13
15
16/*!
17 * \class DsoCommand
18 *
19 * The DsoCommand class implements the `dso` CLI command.
20 */
21
22/*!
23 * Construct a new DsoCommand object with \a parent.
24 */
29
31{
33 u"mode"_s,
34 u"range"_s,
35 };
36}
37
39{
41 u"interval"_s,
42 u"samples"_s,
43 u"sample-rate"_s,
44 u"trigger-level"_s,
45 u"trigger-mode"_s,
46 u"window-size"_s,
47 };
48}
49
50/*!
51 * \copybrief DeviceCommand::processOptions
52 *
53 * This implementation extends DeviceCommand::processOptions to process additional CLI options
54 * supported (or required) by this command.
55 */
57{
59 if (!errors.isEmpty()) {
60 return errors;
61 }
62
63 // Parse the (required) mode option.
64 if (const QString mode = parser.value(u"mode"_s).trimmed().toLower();
65 mode.startsWith(u"ac v"_s) || mode.startsWith(u"vac"_s)) {
67 } else if (mode.startsWith(u"dc v"_s) || mode.startsWith(u"vdc"_s)) {
69 } else if (mode.startsWith(u"ac c"_s) || mode.startsWith(u"aac"_s)) {
71 } else if (mode.startsWith(u"dc c"_s) || mode.startsWith(u"adc"_s)) {
73 } else {
74 errors.append(tr("Unknown DSO mode: %1").arg(parser.value(u"mode"_s)));
75 return errors;
76 }
77
78 // Parse the (required) range option.
79 QString unit;
80 {
81 const QString value = parser.value(u"range"_s);
82 quint32 sensibleMinimum = 0;
83 switch (settings.mode) {
85 Q_ASSERT(false); // Not possible, since the mode parsing above never allows Idle.
86 break;
90 unit = u"V"_s;
91 sensibleMinimum = 50; // mV.
92 break;
96 unit = u"A"_s;
97 sensibleMinimum = 5; // mA.
98 break;
99 }
100 Q_ASSERT(!unit.isEmpty());
101 rangeOptionValue = parseNumber<std::milli>(value, unit, sensibleMinimum);
102 if (rangeOptionValue == 0) {
103 errors.append(tr("Invalid range value: %1").arg(value));
104 }
105 }
106
107 // Parse the trigger-level option.
108 if (parser.isSet(u"trigger-level"_s)) {
109 float sign = 1.0;
110 const QString rawValue = parser.value(u"trigger-level"_s);
111 QString absValue = rawValue;
112 DOKIT_STRING_INDEX_TYPE nonSpacePos;
113 for (nonSpacePos = 0; (nonSpacePos < rawValue.length()) && (rawValue.at(nonSpacePos) == u' '); ++nonSpacePos);
114 if ((nonSpacePos < rawValue.length()) && (rawValue.at(nonSpacePos) == u'-')) {
115 absValue = rawValue.mid(nonSpacePos+1);
116 sign = -1.0;
117 }
118 const float level = parseNumber<std::ratio<1>,float>(absValue, unit, 0.f);
119 qCDebug(lc) << "Trigger level" << rawValue << absValue << nonSpacePos << sign << level;
120 if (qIsNaN(level)) {
121 errors.append(tr("Invalid trigger-level value: %1").arg(rawValue));
122 } else {
123 settings.triggerLevel = sign * level;
124 qCDebug(lc) << "Trigger level" << settings.triggerLevel;
125 // Check the trigger level is within the Votage / Current range.
126 if ((rangeOptionValue != 0) && (qAbs(settings.triggerLevel) > (rangeOptionValue/1000.0))) {
127 errors.append(tr("Trigger-level %1%2 is outside range ±%3%2").arg(
128 appendSiPrefix(settings.triggerLevel), unit, appendSiPrefix(rangeOptionValue / 1000.0)));
129 }
130 }
131 }
132
133 // Parse the trigger-mode option.
134 if (parser.isSet(u"trigger-mode"_s)) {
135 const QString triggerMode = parser.value(u"trigger-mode"_s).trimmed().toLower();
136 if (triggerMode.startsWith(u"free"_s)) {
138 } else if (triggerMode.startsWith(u"ris"_s)) {
140 } else if (triggerMode.startsWith(u"fall"_s)) {
142 } else {
143 errors.append(tr("Unknown trigger mode: %1").arg(parser.value(u"trigger-mode"_s)));
144 }
145 }
146
147 // Ensure that if either trigger option is present, then both are.
148 if (parser.isSet(u"trigger-level"_s) != parser.isSet(u"trigger-mode"_s)) {
149 errors.append(tr("If either option is provided, then both must be: trigger-level, trigger-mode"));
150 }
151
152 // Parse the sample-rate option.
153 if (parser.isSet(u"sample-rate"_s)) {
154 const QString value = parser.value(u"sample-rate"_s);
155 sampleRateValue = parseNumber<std::ratio<1,1>>(value, u"Hz"_s, (quint32)50'000);
156 if (sampleRateValue == 0) {
157 errors.append(tr("Invalid sample-rate value: %1").arg(value));
158 } else if (sampleRateValue > 1'000'000) {
159 qCWarning(lc).noquote() << tr("Pokit devices do not officially support sample rates greater than 1Mhz");
160 }
161 }
162
163 // Parse the interval option.
164 if (parser.isSet(u"interval"_s)) {
165 const QString value = parser.value(u"interval"_s);
166 const quint32 interval = parseNumber<std::micro>(value, u"s"_s, (quint32)500'000);
167 if (interval == 0) {
168 errors.append(tr("Invalid interval value: %1").arg(value));
169 } else {
170 settings.samplingWindow = interval;
171 }
172 }
173
174 // Parse the window-size option.
175 if (parser.isSet(u"window-size"_s)) {
176 const QString value = parser.value(u"window-size"_s);
177 const quint32 samples = parseNumber<std::ratio<1>>(value, u"S"_s);
178 if (samples == 0) {
179 errors.append(tr("Invalid window-size value: %1").arg(value));
180 } else if (samples > std::numeric_limits<quint16>::max()) {
181 errors.append(tr("Window size value (%1) must be no greater than %2")
182 .arg(value).arg(std::numeric_limits<quint16>::max()));
183 } else {
184 settings.numberOfSamples = (quint16)samples;
185 if (const auto maxSamples = maxWindowSize(PokitProduct::PokitPro); settings.numberOfSamples > maxSamples) {
186 qCWarning(lc).noquote() <<
187 tr("No Pokit device officially supports windows greater than %L1 samples").arg(maxSamples);
188 }
189 }
190 }
191
192 // Ensure that we have at least: sample-rate, or both interval and window-size.
193 if ((!parser.isSet(u"sample-rate"_s)) && !(parser.isSet(u"interval"_s) && parser.isSet(u"window-size"_s))) {
194 errors.append(tr("Missing required option/s: either sample-rate, or both interval and window-size"));
195 }
196
197 // If we have all three sample-rate related options, ensure they agree.
198 if ((sampleRateValue != 0) && (settings.numberOfSamples != 0) && (settings.samplingWindow != 0)) {
199 const quint32 sampleRate = settings.numberOfSamples * 1'000'000ull / settings.samplingWindow;
200 if (sampleRate != sampleRateValue) {
201 errors.append(tr("Windows size (%1 samples) and interval (%2ns) yield a sample rate of %3Hz, which does "
202 "not match the supplied sample-rate (%4Hz). Tip: leave one option unset to have dokit calculate the "
203 "remaining option.").arg(settings.numberOfSamples).arg(settings.samplingWindow).arg(sampleRate)
205 }
206 }
207
208 // Parse the samples option.
209 if (parser.isSet(u"samples"_s)) {
210 const QString value = parser.value(u"samples"_s);
211 samplesValue = parseNumber<std::ratio<1>>(value, u"S"_s);
212 if (samplesValue == 0) {
213 errors.append(tr("Invalid samples value: %1").arg(value));
214 }
215 }
216 return errors;
217}
218
219/*!
220 * \copybrief DeviceCommand::getService
221 *
222 * This override returns a pointer to a DsoService object.
223 */
236
237/*!
238 * Returns the \a product's maximum sampling window size.
239 *
240 * \pokitApi Pokit's official documentation claim the maximum is 8,192. However, my Pokit Meter fails for window size
241 * greater than 8,191, while my Pokit Pro supports up to 16,384 samples per window.
242 */
244{
245 switch (product) {
247 return 8'191;
249 return 16'384;
250 }
251 Q_ASSERT_X(false, "DsoCommand::maxWindowSize", "Unknown PokitProduct enum value");
252 return 0;
253}
254
255/*!
256 * Configures the \a settings.numberOfSamples and/or \a settings.samplingWindow, if not already set, according to the
257 * requested \a sampleRate. The chosen \a settings will be limited to \a product's capaibilities.
258 *
259 * Returns \c true os settings were set (either by this function, or they were already set), or \c false if the
260 * settings could not be determined succesfully (eg, because \a sampleRate was too high for the \a product).
261 */
262bool DsoCommand::configureWindow(const PokitProduct product, const quint32 sampleRate, DsoService::Settings &settings)
263{
264 const quint32 maxSampleRate = 1'000'000; // Pokit Meter and Pokit Pro both sample up to 1MHz.
265 const quint32 maxWindowSize = DsoCommand::maxWindowSize(product);
266
267 if (sampleRate > maxSampleRate) {
268 qCWarning(lc).noquote() <<
269 tr("The requested sample rate (%1Hz) likely exceeds the connected device's limit (%2Hz)")
270 .arg(sampleRate).arg(maxSampleRate);
271 }
272
273 if (settings.numberOfSamples > maxWindowSize) {
274 qCWarning(lc).noquote() <<
275 tr("Requested window size (%1 samples) likely exceeds the connected device's limit (%2) samples")
276 .arg(settings.samplingWindow).arg(maxWindowSize);
277 }
278
279 if ((settings.numberOfSamples != 0) && (settings.samplingWindow != 0)) {
280 qCDebug(lc).noquote() << "Both numberOfSamples and samplingWindow are set, so no need to derive either";
281 if (sampleRate != 0) {
282 const quint32 derivedRate = settings.numberOfSamples * 1'000'000ull / settings.samplingWindow;
283 qCDebug(lc).noquote() << "derivedRate" << derivedRate << sampleRate;
284 if (sampleRate != derivedRate) {
285 qCWarning(lc).noquote() << tr("Ignoring sample-rate, as interval and window-size both provided");
286 }
287 }
288 return true; // Nothing more to do.
289 }
290 Q_ASSERT_X(sampleRate > 0, "DsoCommand::configureWindow", "processOptions should have rejected already");
291
292 // If both window parameters are unset, choose the best window size (we'll choose a window period later).
293 if ((settings.numberOfSamples == 0) && (settings.samplingWindow == 0)) {
294 qCDebug(lc).noquote() << tr("Choosing best number-of-samples for sample-rate %2Hz").arg(sampleRate);
295 double smallestDifference = std::numeric_limits<double>::quiet_NaN();
296 for (quint32 windowSize = maxWindowSize; windowSize > 0; --windowSize) {
297 const quint32 period = windowSize * 1'000'000ull / sampleRate;
298 const double effectiveRate = double(windowSize) * 1'000'000.0 / (double)period;
299 if (effectiveRate > maxSampleRate) continue; // Skip sizes that would exceed the device's max sample rate.
300 if (const quint32 effectivePeriod = windowSize * 1'000'000ull / sampleRate;
301 effectivePeriod > 1'000'000) continue; // Skip sizes that would take longer than 1s to fetch.
302 const double difference = qAbs(effectiveRate - sampleRate);
303 // qCDebug(lc).noquote() << tr("%1 samples, %2us, %3Hz, %4Hz, ±%5Hz").arg(windowSize).arg(period)
304 // .arg(effectiveRate, 0, 'f').arg(sampleRate).arg(difference, 0, 'f');
305 if ((settings.numberOfSamples == 0) || (difference < smallestDifference)) {
306 settings.numberOfSamples = windowSize;
307 smallestDifference = difference;
308 }
309 }
310 qCDebug(lc).noquote() << tr("Chose %Ln sample/s, with error ±%2Hz", nullptr,
311 settings.numberOfSamples).arg(smallestDifference, 0, 'f');
312 if (settings.numberOfSamples == 0) {
313 qCCritical(lc).noquote() << tr("Failed to select a compatible window size for sample rate %1Hz").arg(sampleRate);
314 return false;
315 }
316 }
317
318 if (settings.numberOfSamples == 0) {
319 qCDebug(lc).noquote() << tr("Calculating number-of-samples for %1us window at %2Hz")
320 .arg(settings.samplingWindow).arg(sampleRate);
321 Q_ASSERT(settings.samplingWindow != 0);
322 const auto numberOfSamples = sampleRate * settings.samplingWindow / 1'000'000ull;
323 qCDebug(lc).noquote() << tr("Calculated %Ln sample/s", nullptr, numberOfSamples);
324 if ((numberOfSamples == 0) || (numberOfSamples > maxWindowSize)) {
325 qCCritical(lc).noquote() << tr("Failed to calculate a valid number of samples for a %L1us period at %2Hz")
326 .arg(settings.samplingWindow).arg(sampleRate);
327 return false;
328 }
329 settings.numberOfSamples = numberOfSamples; // Note the implicit uint64 to uint16 conversion.
330 Q_ASSERT(settings.numberOfSamples * 1'000'000ull / settings.samplingWindow <= sampleRate); // Due to integer truncation.
331 }
332
333 if (settings.samplingWindow == 0) {
334 qCDebug(lc).noquote() << tr("Calculating sampling-window for %Ln sample/s at %1Hz", nullptr,
335 settings.numberOfSamples).arg(sampleRate);
336 Q_ASSERT(settings.numberOfSamples != 0);
337 settings.samplingWindow = settings.numberOfSamples * 1'000'000ull / sampleRate;
338 qCDebug(lc).noquote() << tr("Calculated %1us").arg(settings.samplingWindow);
339 if (settings.samplingWindow == 0) {
340 qCCritical(lc).noquote() << tr("Failed to calculate a valid sampling window for a %L1 samples at %1Hz")
341 .arg(settings.numberOfSamples).arg(sampleRate);
342 return false;
343 }
344 }
345 return true;
346}
347
348/*!
349 * \copybrief DeviceCommand::serviceDetailsDiscovered
350 *
351 * This override fetches the current device's status, and outputs it in the selected format.
352 */
354{
355 DeviceCommand::serviceDetailsDiscovered(); // Just logs consistently.
356 settings.range = (minRangeFunc == nullptr) ? 0 : minRangeFunc(*service->pokitProduct(), rangeOptionValue);
357 if (!configureWindow(*service->pokitProduct(), sampleRateValue, settings)) {
358 disconnect(EXIT_FAILURE);
359 return;
360 }
361 if (samplesValue == 0) samplesValue = settings.numberOfSamples;
362 const QString range = service->toString(settings.range, settings.mode);
363 const QString triggerInfo = (settings.command == DsoService::Command::FreeRunning) ? QString() :
364 tr(", and a %1 at %2%3%4 (%5Hz)").arg(DsoService::toString(settings.command).toLower(),
365 (settings.triggerLevel < 0.) ? u"-"_s : u""_s, appendSiPrefix(qAbs(settings.triggerLevel)),
366 range.at(range.size()-1));
367 qCInfo(lc).noquote() << tr("Sampling %1, with range %2, at %L3Hz (%Ln sample/s over %L4us)%5", nullptr, settings.numberOfSamples)
368 .arg(DsoService::toString(settings.mode), (range.isNull()) ? QString::fromLatin1("N/A") : range)
369 .arg(settings.numberOfSamples * 1'000'000ull / settings.samplingWindow).arg(settings.samplingWindow)
370 .arg(triggerInfo);
371 if (!service->enableMetadataNotifications()) {
372 qCCritical(lc).noquote() << tr("Failed to enable metadata notifications");
373 disconnect(EXIT_FAILURE);
374 return;
375 }
376 if (!service->enableReadingNotifications()) {
377 qCCritical(lc).noquote() << tr("Failed to enable reading notifications");
378 disconnect(EXIT_FAILURE);
379 return;
380 }
381 service->setSettings(settings);
382}
383
384/*!
385 * \var DsoCommand::minRangeFunc
386 *
387 * Pointer to function for converting #rangeOptionValue to a Pokit device's range enumerator. This function pointer
388 * is assigned during the command line parsing, but is not invoked until after the device's services are discovered,
389 * because prior to that discovery, we don't know which product (Meter vs Pro vs Clamp, etc) we're talking to and thus
390 * which enumerator list to be using.
391 *
392 * If the current mode does not support ranges (eg diode, and continuity modes), then this member will be \c nullptr.
393 *
394 * \see processOptions
395 * \see serviceDetailsDiscovered
396 */
397
398/*!
399 * Invoked when the DSO settings have been written.
400 */
402{
403 Q_ASSERT(service);
404 qCDebug(lc).noquote() << tr("Settings written; DSO has started.");
405}
406
407/*!
408 * Invoked when \a metadata has been received from the DSO.
409 */
411{
412 qCDebug(lc) << "status:" << (int)(data.status);
413 qCDebug(lc) << "scale:" << data.scale;
414 qCDebug(lc) << "mode:" << DsoService::toString(data.mode);
415 qCDebug(lc) << "range:" << service->toString(data.range, data.mode);
416 qCDebug(lc) << "samplingWindow:" << data.samplingWindow;
417 qCDebug(lc) << "numberOfSamples:" << data.numberOfSamples;
418 qCDebug(lc) << "samplingRate:" << data.samplingRate << "Hz";
419 this->metadata = data;
420 this->samplesToGo = data.numberOfSamples;
421}
422
423/*!
424 * Outputs DSO \a samples in the selected output format.
425 */
427{
428 QString unit;
429 switch (metadata.mode) {
430 case DsoService::Mode::DcVoltage: unit = u"Vdc"_s; break;
431 case DsoService::Mode::AcVoltage: unit = u"Vac"_s; break;
432 case DsoService::Mode::DcCurrent: unit = u"Adc"_s; break;
433 case DsoService::Mode::AcCurrent: unit = u"Aac"_s; break;
434 default:
435 qCDebug(lc).noquote() << tr(R"(No known unit for mode %1 "%2".)").arg((int)metadata.mode)
437 }
438 const QString range = service->toString(metadata.range, metadata.mode);
439
440 for (const qint16 &sample: samples) {
441 static quint32 sampleNumber = 0; ++sampleNumber;
442 const float value = sample * metadata.scale;
443 switch (format) {
445 for (; showCsvHeader; showCsvHeader = false) {
446 std::cout << qUtf8Printable(tr("sample_number,value,unit,range\n"));
447 }
448 std::cout << qUtf8Printable(QString::fromLatin1("%1,%2,%3,%4\n")
449 .arg(sampleNumber).arg(value).arg(unit, range));
450 break;
452 std::cout << QJsonDocument(QJsonObject{
453 { u"value"_s, value },
454 { u"unit"_s, unit },
455 { u"range"_s, range },
456 { u"mode"_s, DsoService::toString(metadata.mode) },
457 }).toJson().toStdString();
458 break;
460 std::cout << qUtf8Printable(tr("%1 %2 %3\n").arg(sampleNumber).arg(value).arg(unit));
461 break;
462 }
463 --samplesToGo;
464
465 if ((sampleNumber > samplesValue) && (samplesValue != 0)) {
466 qCInfo(lc).noquote() << tr("Finished fetching %Ln sample/s. Disconnecting.", nullptr, sampleNumber);
467 if (device) disconnect(); // Will exit the application once disconnected.
468 return;
469 }
470 }
471
472 // If we've received all the data for the current window, begin fetching another.
473 if (samplesToGo <= 0) {
474 qCDebug(lc).noquote() << tr("Finished fetching %Ln window sample/s (with %L2 to remaining).",
475 nullptr, metadata.numberOfSamples).arg(samplesToGo);
477 }
478}
virtual QStringList supportedOptions(const QCommandLineParser &parser) const
Returns a list of CLI option names supported by this command.
static QString appendSiPrefix(const double value, int precision=6)
Returns a human-readable version of value, with up-to precision decimal digits, and SI unit prefix wh...
static T parseNumber(const QString &value, const QString &unit, const T sensibleMinimum=0)
Returns value as an integer multiple of the ratio R, as number of type T.
OutputFormat format
Selected output format.
@ Text
Plain unstructured text.
@ Csv
RFC 4180 compliant CSV text.
@ Json
RFC 8259 compliant JSON text.
virtual QStringList processOptions(const QCommandLineParser &parser)
Processes the relevant options from the command line parser.
virtual QStringList requiredOptions(const QCommandLineParser &parser) const
Returns a list of CLI option names required by this command.
The AbstractPokitService class provides a common base for Pokit services classes.
PokitDevice * device
Pokit Bluetooth device (if any) this command interacts with.
DeviceCommand(QObject *const parent=nullptr)
Construct a new DeviceCommand object with parent.
virtual void serviceDetailsDiscovered()
Handles service detail discovery events.
static quint8 minVoltageRange(const PokitProduct product, const quint32 maxValue)
Returns the product's lowest voltage range that can measure at least up to maxValue (mV),...
static quint8 minCurrentRange(const PokitProduct product, const quint32 maxValue)
Returns the product's lowest current range that can measure at least up to maxValue (μA),...
void disconnect(int exitCode=EXIT_SUCCESS)
Disconnects the underlying Pokit device, and sets exitCode to be return to the OS once the disconnect...
DsoService::Settings settings
Settings for the Pokit device's DSO mode.
Definition dsocommand.h:37
static quint16 maxWindowSize(const PokitProduct product)
Returns the product's maximum sampling window size.
quint8(* minRangeFunc)(const PokitProduct product, const quint32 maxValue)
Pointer to function for converting rangeOptionValue to a Pokit device's range enumerator.
Definition dsocommand.h:32
qint32 samplesToGo
Number of samples we're expecting in the current window.
Definition dsocommand.h:42
bool showCsvHeader
Whether or not to show a header as the first line of CSV output.
Definition dsocommand.h:43
void serviceDetailsDiscovered() override
Handles service detail discovery events.
void outputSamples(const DsoService::Samples &samples)
Outputs DSO samples in the selected output format.
static bool configureWindow(const PokitProduct product, const quint32 sampleRate, DsoService::Settings &settings)
Configures the settings.numberOfSamples and/or settings.samplingWindow, if not already set,...
QStringList supportedOptions(const QCommandLineParser &parser) const override
Returns a list of CLI option names supported by this command.
DsoCommand(QObject *const parent=nullptr)
Construct a new DsoCommand object with parent.
DsoService * service
Bluetooth service this command interacts with.
Definition dsocommand.h:36
QStringList processOptions(const QCommandLineParser &parser) override
Processes the relevant options from the command line parser.
void settingsWritten()
Invoked when the DSO settings have been written.
quint32 rangeOptionValue
The parsed value of range option.
Definition dsocommand.h:33
QStringList requiredOptions(const QCommandLineParser &parser) const override
Returns a list of CLI option names required by this command.
AbstractPokitService * getService() override
Returns a Pokit service object for the derived command class.
quint32 sampleRateValue
The parsed value of the sample-rate option.
Definition dsocommand.h:34
quint32 samplesValue
The parsed value of the samples option.
Definition dsocommand.h:35
DsoService::Metadata metadata
Most recent DSO metadata.
Definition dsocommand.h:41
void metadataRead(const DsoService::Metadata &data)
Invoked when metadata has been received from the DSO.
void metadataRead(const DsoService::Metadata &meta)
This signal is emitted when the Metadata characteristic has been read successfully.
QVector< qint16 > Samples
Raw samples from the Reading characteristic.
Definition dsoservice.h:95
void settingsWritten()
This signal is emitted when the Settings characteristic has been written successfully.
static QString toString(const Command &command)
Returns command as a user-friendly string.
@ DcVoltage
Measure DC voltage.
Definition dsoservice.h:55
@ AcCurrent
Measure AC current.
Definition dsoservice.h:58
@ AcVoltage
Measure AC voltage.
Definition dsoservice.h:56
@ Idle
Make device idle.
Definition dsoservice.h:54
@ DcCurrent
Measure DC current.
Definition dsoservice.h:57
@ FreeRunning
Run free, without waiting for edge triggers.
Definition dsoservice.h:45
@ RisingEdgeTrigger
Trigger on a rising edge.
Definition dsoservice.h:46
@ FallingEdgeTrigger
Trigger on a falling edge.
Definition dsoservice.h:47
void samplesRead(const DsoService::Samples &samples)
This signal is emitted when the Reading characteristic has been notified.
Declares the PokitDevice class.
PokitProduct
Pokit products known to, and supported by, the QtPokit library.
@ PokitPro
Pokit Pro.
@ PokitMeter
Pokit Meter.
bool isSet(const QString &name) const const
QString value(const QString &optionName) const const
void append(const T &value)
bool isEmpty() const const
QObject(QObject *parent)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
const QChar at(int position) const const
QString fromLatin1(const char *str, int size)
bool isEmpty() const const
bool isNull() const const
int length() const const
QString mid(int position, int n) const const
int size() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString toLower() const const
QString trimmed() const const
Declares the DOKIT_USE_STRINGLITERALS macro, and related functions.
#define DOKIT_STRING_INDEX_TYPE
Internal macro for matching the index type used by QString methods.
#define DOKIT_USE_STRINGLITERALS
Internal macro for using either official Qt string literals (added in Qt 6.4), or our own equivalent ...
Attributes included in the Metadata characteristic.
Definition dsoservice.h:85
quint32 samplingRate
Sampling rate used during last acquisition (1 to 1MHz).
Definition dsoservice.h:92
DsoStatus status
Current DSO status.
Definition dsoservice.h:86
float scale
Scale to apply to read samples.
Definition dsoservice.h:87
quint16 numberOfSamples
Number of samples acquired (1 to 8,192|16,384 for Pokit Meter|Pro).
Definition dsoservice.h:91
quint8 range
Range used during last acquisition.
Definition dsoservice.h:89
Mode mode
Operation mode used during last acquisition.
Definition dsoservice.h:88
quint32 samplingWindow
Sampling window (microseconds) used during last acquisition.
Definition dsoservice.h:90
Attributes included in the Settings characteristic.
Definition dsoservice.h:68