diff --git a/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.h b/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.h new file mode 100644 index 0000000..53511ec --- /dev/null +++ b/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.h @@ -0,0 +1,429 @@ +#ifndef DEVICEDRIVER_H +#define DEVICEDRIVER_H + +/** + * This file contains the device interface. + * + * To add support for a new hardware device perform the following steps: + * - Derive from this class + * - Implement all pure virtual functions + * - Implement the virtual functions if the device supports the specific function + * - TODO register the driver during application start + */ + +#include "Tools/parameters.h" +#include "savable.h" + +#include +#include + +#include +#include + +class DeviceDriver : public QObject +{ + Q_OBJECT +public: + DeviceDriver(); + virtual ~DeviceDriver(); + + /** + * @brief Returns the driver name. It must be unique across all implemented drivers and is used to identify the driver + * @return driver name + */ + virtual QString getDriverName() = 0; + /** + * @brief Lists all available devices by their serial numbers + * @return Serial numbers of detected devices + */ + virtual std::set GetAvailableDevices() = 0; + /** + * @brief Connects to a device, given by its serial number + * @param serial Serial number of device that should be connected to + * @return true if connection successful, otherwise false + */ + virtual bool connectTo(QString serial) = 0; + /** + * @brief Disconnects from device. Has no effect if no device was connected + */ + virtual void disconnect() = 0; + + /** + * @brief Returns the serial number of the connected device + * @return Serial number of connected device (empty string if no device is connected) + */ + virtual QString serial() = 0; + + enum class Feature { + // VNA features + VNA, + VNAFrequencySweep, + VNAPowerSweep, + VNAZeroSpan, + VNALogSweep, + // Generator features + Generator, + // Spectrum analyzer features + SA, + SASignalID, + SATrackingGenerator, + SATrackingOffset, + // External reference + ExtRefIn, + ExtRefOut, + }; + + class Info { + public: + QString firmware_version; + QString hardware_version; + std::set supportedFeatures; + struct { + struct { + // Number of VNA ports + unsigned int ports; + // Sweep frequency limits in Hz + double minFreq, maxFreq; + // IF bandwidth limits in Hz + double minIFBW, maxIFBW; + // Maximum number of points per sweep + unsigned int maxPoints; + // Stimulus level limits in dBm + double mindBm, maxdBm; + } VNA; + struct { + // Number of ports + unsigned int ports; + // Frequency limits in Hz + double minFreq, maxFreq; + // Output level limits in dBm + double mindBm, maxdBm; + } Generator; + struct { + // Number of ports + unsigned int ports; + // Sweep frequency limits in Hz + double minFreq, maxFreq; + // RBW limits in Hz + double minRBW, maxRBW; + } SA; + } Limits; + }; + + /** + * @brief Returns the device information. This function will be called when a device has been connected. Its return value must be valid + * directly after returning from DeviceDriver::connectTo() + * + * Emit the InfoUpdate() signal whenever the return value of this function changes. + * + * @return Device information + */ + virtual Info getInfo() = 0; +signals: + /** + * @brief Emit this signal whenever the device info has been updated + */ + void InfoUpdated(); +public: + /** + * @brief Checks whether a specific feature is supported + * @param f Feature to check + * @return true if feature is supported, false otherwise + */ + bool supports(Feature f) { return getInfo().supportedFeatures.count(f);} + + /** + * Status flags + */ + enum class Flag { + // The input is overloaded with a signal that is too large + Overload, + // A PLL failed to lock + Unlocked, + // The requested output amplitude can not be reached + Unlevel, + // The external reference input is in use + ExtRef, + }; + + /** + * @brief Returns a set of all active flags + * + * There is also a convenience function to check a specific flag, see DeviceDriver::asserted() + * + * @return Set of active flags + */ + virtual std::set getFlags() = 0; + + /** + * @brief Checks whether a specific flag is asserted + * @param f Flag to check + * @return true if flag is asserted, false otherwise + */ + bool asserted(Flag f) {return getFlags().count(f);} + + /** + * @brief Returns the device status string. It will be displayed in the status bar of the application + * + * Emit the StatusUpdated() signal whenever the return value of this function changes + * + * @return Status string + */ + virtual QString getStatus() {return "";} +signals: + /** + * @brief Emit this signal whenever the device status has changed (return value of getStatus() has changed) + */ + void StatusUpdated(); +public: + /** + * @brief Returns the driver specific settings + * + * The settings are returned as a map. + * Key: user-readable setting name + * Value: SettingDescription, consisting of: + * - var: Pointer to the setting variable (should be a private member of the derived class) + * - name: Arbitrary string used to persistently store this setting (never visible to the user) + * - def: Default value of the setting + * + * These settings will be persistent across reboots. For each device driver, a section within the preferences + * will be created where these settings can be changed. + * + * @return Map of driver specific settings + */ + virtual std::map driverSpecificSettings() {return std::map();} + + /** + * @brief Return driver specific actions. + * + * The returned actions will be appended to the device menu. + * + * @return List of actions + */ + virtual std::vector driverSpecificActions() {return std::vector();} + + class VNASettings { + public: + // Start/stop frequency. Both values will be identical for power sweeps and zero span + double freqStart, freqStop; + // Start/stop stimulus level. Both values will be identical for frequency sweeps and zero span + double dBmStart, dBmStop; + // IF bandwidth + double IFBW; + // Number of points in the sweep + int points; + // Logarithmic sweep flag, set if sweep should be logarithmic + bool logSweep; + // List of ports that should be excited during the sweep (port count starts at 1) + std::vector excitedPorts; + }; + + class VNAMeasurement { + public: + // Number of the point in the sweep (starts at 0) + unsigned int pointNum; + // Characteristic impedance of the measurement result (typically 50 Ohm) + double Z0; + union { + struct { + // for non-zero span + // Frequency of the point + double frequency; + // Stimulus level of the point + double dBm; + }; + struct { + // for zero span + // time in us since first datapoint + double us; + }; + }; + // S parameter measurements + // Key: S parameter name, e.g. "S11" + // Value: complex measurement in real/imag (linear, not in dB) + std::map> measurements; + + Sparam toSparam(int port1, int port2) const; + void fromSparam(Sparam S, int port1, int port2); + VNAMeasurement interpolateTo(const VNAMeasurement &to, double a); + }; + + /** + * @brief Names of available measurements. + * + * The names must be identical to the names used in the returned VNAMeasurement. + * Typically the S parameters, e.g. this function may return {"S11","S12","S21","S22"} but any other names are also allowed. + * + * @return List of available VNA measurement parameters + */ + virtual QStringList availableVNAMeasurements() {return {};} + + /** + * @brief Configures the VNA and starts a sweep + * @param s VNA settings + * @param cb Callback, must be called after the VNA has been configured + * @return true if configuration successful, false otherwise + */ + virtual bool setVNA(const VNASettings &s, std::function cb = nullptr) {Q_UNUSED(s) Q_UNUSED(cb) return false;} +signals: + /** + * @brief This signal must be emitted whenever a VNA measurement is complete and should be passed on to the GUI + * @param m VNA measurement + */ + void VNAmeasurementReceived(VNAMeasurement m); + +public: + class SASettings { + public: + enum class Window { + None = 0, + Kaiser = 1, + Hann = 2, + FlatTop = 3, + Last + }; + enum class Detector { + PPeak = 0, + NPeak = 1, + Sample = 2, + Normal = 3, + Average = 4, + Last + }; + // Start/stop frequency. Both values will be identical for zero span + double freqStart, freqStop; + // Resolution bandwidth + double RBW; + // Window type + Window window; + // Detector type + Detector detector; + // Tracking generator enable + bool trackingGenerator; + // Port at which the tracking generator is active. Port count starts at 1 + int trackingPort; + // Offset frequency of the tracking generator + double trackingOffset; + // Output level of the tracking generator + double trackingPower; + }; + class SAMeasurement { + public: + // Number of point in the sweep + int pointNum; + union { + struct { + // for non-zero span + double frequency; + }; + struct { + // for zero span + double us; // time in us since first datapoint + }; + }; + // S parameter measurements + // Key: S parameter name, e.g. "PORT1" + // Value: measurement in mW (linear, not in dB). A value of 1.0 means 0dBm + std::map measurements; + }; + + /** + * @brief Names of available measurements. + * + * The names must be identical to the names used in the returned SAMeasurement. + * Typically the port names, e.g. this function may return {"PORT1","PORT2"} but any other names are also allowed. + * + * @return List of available SA measurement parameters + */ + virtual QStringList availableSAMeasurements() {return {};} + /** + * @brief Configures the SA and starts a sweep + * @param s SA settings + * @param cb Callback, must be called after the SA has been configured + * @return true if configuration successful, false otherwise + */ + virtual bool setSA(const SASettings &s, std::function cb = nullptr) {Q_UNUSED(s) Q_UNUSED(cb) return false;} +signals: + /** + * @brief This signal must be emitted whenever a SA measurement is complete and should be passed on to the GUI + * @param m SA measurement + */ + void SAmeasurementReceived(SAMeasurement m); + +public: + class SGSettings { + public: + // Output frequency + double freq; + // Output signal level + double dBm; + // Output port. Port count starts at 1, set to zero to disable all ports + int port; + }; + + /** + * @brief Names of available generator ports. + * + * Typically the port names, e.g. this function may return {"PORT1","PORT2"} but any other names are also allowed. + * + * @return List of available SA measurement parameters + */ + virtual QStringList availableSGPorts() {return {};} + /** + * @brief Configures the generator + * @param s Generator settings + * @return true if configuration successful, false otherwise + */ + virtual bool setSG(const SGSettings &s) {Q_UNUSED(s) return false;} + + /** + * @brief Sets the device to idle + * + * Stops all sweeps and signal generation + * + * @param cb Callback, must be called after the device has stopped all operations + * @return true if configuration successful, false otherwise + */ + virtual bool setIdle(std::function cb = nullptr) {Q_UNUSED(cb) return false;} + + /** + * @brief Returns the available options for the external reference input + * @return External reference input options + */ + virtual QStringList availableExtRefInSettings() {return {};} + + /** + * @brief Returns the available options for the external reference output + * @return External reference output options + */ + virtual QStringList availableExtRefOutSettings() {return {};} + + /** + * @brief Configures the external reference input/output + * @param option_in Reference input option (one of the options returned by availableExtRefInSettings()) + * @param option_out Reference output option (one of the options returned by availableExtRefOutSettings()) + * @return true if configuration successful, false otherwise + */ + virtual bool setExtRef(QString option_in, QString option_out) {Q_UNUSED(option_in) Q_UNUSED(option_out) return false;} + + /** + * @brief maximumSupportedPorts Maximum number of supported ports by the GUI. No device driver may report a higher number of ports than this value + */ + static constexpr unsigned int maximumSupportedPorts = 8; + +signals: + /** + * @brief Emit this signal when the device connection has been lost unexpectedly + */ + void ConnectionLost(); + /** + * @brief Emit this signal whenever a debug log line from the device has been received. May be left unused if device does not support debug output + * @param line + */ + void LogLineReceived(QString line); +}; + +Q_DECLARE_METATYPE(DeviceDriver::VNAMeasurement) +Q_DECLARE_METATYPE(DeviceDriver::SAMeasurement) + +#endif // VIRTUALDEVICE_H diff --git a/Software/PC_Application/LibreVNA-GUI/Device/librevnadriver.cpp b/Software/PC_Application/LibreVNA-GUI/Device/librevnadriver.cpp new file mode 100644 index 0000000..67f5243 --- /dev/null +++ b/Software/PC_Application/LibreVNA-GUI/Device/librevnadriver.cpp @@ -0,0 +1,6 @@ +#include "librevnadriver.h" + +LibreVNADriver::LibreVNADriver() +{ + connected = false; +} diff --git a/Software/PC_Application/LibreVNA-GUI/Device/librevnadriver.h b/Software/PC_Application/LibreVNA-GUI/Device/librevnadriver.h new file mode 100644 index 0000000..527e21c --- /dev/null +++ b/Software/PC_Application/LibreVNA-GUI/Device/librevnadriver.h @@ -0,0 +1,29 @@ +#ifndef LIBREVNADRIVER_H +#define LIBREVNADRIVER_H + +#include "devicedriver.h" + +#include "../../VNA_embedded/Application/Communication/Protocol.hpp" + +class LibreVNADriver : public DeviceDriver +{ + Q_OBJECT +public: + enum class TransmissionResult { + Ack, + Nack, + Timeout, + InternalError, + }; + Q_ENUM(TransmissionResult) + + LibreVNADriver(); + +protected: + virtual bool SendPacket(const Protocol::PacketInfo& packet, std::function cb = nullptr, unsigned int timeout = 500) = 0; + virtual void ReceivedData(const uint8_t data, unsigned int len) = 0; + + bool connected; +}; + +#endif // LIBREVNADRIVER_H diff --git a/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro b/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro index 2621729..65a1f5c 100644 --- a/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro +++ b/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro @@ -26,10 +26,12 @@ HEADERS += \ Device/compounddevice.h \ Device/compounddeviceeditdialog.h \ Device/device.h \ + Device/devicedriver.h \ Device/devicelog.h \ Device/deviceusblog.h \ Device/deviceusblogview.h \ Device/firmwareupdatedialog.h \ + Device/librevnadriver.h \ Device/manualcontroldialog.h \ Device/virtualdevice.h \ Generator/generator.h \ @@ -178,6 +180,7 @@ SOURCES += \ Device/deviceusblog.cpp \ Device/deviceusblogview.cpp \ Device/firmwareupdatedialog.cpp \ + Device/librevnadriver.cpp \ Device/manualcontroldialog.cpp \ Device/virtualdevice.cpp \ Generator/generator.cpp \