WIP: ADC spectrum

This commit is contained in:
Jan Käberich 2025-05-19 16:50:24 +02:00
parent f76e3a209d
commit be7cf38e05
9 changed files with 861 additions and 575 deletions

View file

@ -146,7 +146,7 @@ LibreVNADriver::LibreVNADriver()
break;
}
if(manualControlDialog) {
manualControlDialog->show();
manualControlDialog->showMaximized();
connect(manualControlDialog, &QDialog::finished, this, [=](){
manualControlDialog = nullptr;
});

View file

@ -6,6 +6,7 @@
#include <QComboBox>
#include <QDebug>
#include <QButtonGroup>
#include <QValueAxis>
#include <complex>
@ -18,6 +19,7 @@ ManualControlDialogVFD::ManualControlDialogVFD(LibreVNADriver &dev, QWidget *par
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
setWindowFlags(Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint);
emit dev.acquireControl();
@ -44,6 +46,8 @@ ManualControlDialogVFD::ManualControlDialogVFD(LibreVNADriver &dev, QWidget *par
ui->DACAmplitudeA->setValue(2047);
ui->DACAmplitudeB->setValue(2047);
ui->ADCSamples->setValue(1024);
auto updateVariableAtt = [=](unsigned int value){
ui->SourceVariableAttSlider->setValue(value);
ui->SourceVariableAttEntry->setValueQuiet(value);
@ -64,11 +68,38 @@ ManualControlDialogVFD::ManualControlDialogVFD(LibreVNADriver &dev, QWidget *par
MakeReadOnly(ui->SourceLocked);
MakeReadOnly(ui->LOLocked);
// connect(&dev, &LibreVNADriver::receivedPacket, this, [=](const Protocol::PacketInfo &p){
// if(p.type == Protocol::PacketType::ManualStatus) {
// NewStatus(p.manualStatus);
// }
// }, Qt::QueuedConnection);
// ADC spectrum charts
for(int i=0;i<4;i++) {
charts[i] = new QChart();
spectrumSeries[i] = new QLineSeries;
auto pen = spectrumSeries[i]->pen();
pen.setWidth(2);
pen.setBrush(QBrush("blue"));
spectrumSeries[i]->setPen(pen);
auto xAxis = new QValueAxis;
xAxis->setTitleText("Frequency [MHz]");
xAxis->setRange(0,12.5);
auto yAxis = new QValueAxis;
yAxis->setTitleText("Amplitude [dBFS]");
yAxis->setRange(-120, 0);
charts[i]->legend()->hide();
charts[i]->addSeries(spectrumSeries[i]);
charts[i]->addAxis(xAxis, Qt::AlignBottom);
charts[i]->addAxis(yAxis, Qt::AlignLeft);
spectrumSeries[i]->attachAxis(xAxis);
spectrumSeries[i]->attachAxis(yAxis);
}
ui->spectrumA->setChart(charts[0]);
charts[0]->setTitle("ADC A Spectrum");
ui->spectrumB->setChart(charts[1]);
charts[1]->setTitle("ADC B Spectrum");
ui->spectrumC->setChart(charts[2]);
charts[2]->setTitle("ADC C Spectrum");
ui->spectrumD->setChart(charts[3]);
charts[3]->setTitle("ADC D Spectrum");
connect(ui->SourceCE, &QCheckBox::toggled, [=](bool) { UpdateDevice(); });
connect(ui->SourceRFEN, &QCheckBox::toggled, [=](bool) { UpdateDevice(); });
@ -79,11 +110,13 @@ ManualControlDialogVFD::ManualControlDialogVFD(LibreVNADriver &dev, QWidget *par
connect(ui->SourceAmp2En, &QCheckBox::toggled, [=](bool) { UpdateDevice(); });
connect(ui->LOAmpEn, &QCheckBox::toggled, [=](bool) { UpdateDevice(); });
connect(ui->DACEnable, &QCheckBox::toggled, [=](bool) { UpdateDevice(); });
connect(ui->ADCEnable, &QCheckBox::toggled, [=](bool) { UpdateDevice(); });
connect(ui->SourceFilter, qOverload<int>(&QComboBox::currentIndexChanged), [=](int) { UpdateDevice(); });
connect(ui->SourceBandsel, qOverload<int>(&QComboBox::currentIndexChanged), [=](int) { UpdateDevice(); });
connect(ui->SourcePortSel, qOverload<int>(&QComboBox::currentIndexChanged), [=](int) { UpdateDevice(); });
connect(ui->LOMode, qOverload<int>(&QComboBox::currentIndexChanged), [=](int) { UpdateDevice(); });
connect(ui->ADCTestPattern, qOverload<int>(&QComboBox::currentIndexChanged), [=](int) { UpdateDevice(); });
connect(ui->SourceFrequency, &SIUnitEdit::valueChanged, [=](double) { UpdateDevice(); });
connect(ui->LOFrequency, &SIUnitEdit::valueChanged, [=](double) { UpdateDevice(); });
@ -93,6 +126,7 @@ ManualControlDialogVFD::ManualControlDialogVFD(LibreVNADriver &dev, QWidget *par
connect(ui->SourceStepAtt, qOverload<int>(&QSpinBox::valueChanged), [=](int) { UpdateDevice(); });
connect(ui->DACAmplitudeA, qOverload<int>(&QSpinBox::valueChanged), [=](int) { UpdateDevice(); });
connect(ui->DACAmplitudeB, qOverload<int>(&QSpinBox::valueChanged), [=](int) { UpdateDevice(); });
connect(ui->ADCSamples, qOverload<int>(&QSpinBox::valueChanged), [=](int) { UpdateDevice(); });
// Create the SCPI commands
@ -333,6 +367,47 @@ ManualControlDialogVFD::ManualControlDialogVFD(LibreVNADriver &dev, QWidget *par
addDoubleManualSetting("MANual:DACB_FREQ", &ManualControlDialogVFD::setDACBFrequency, &ManualControlDialogVFD::getDACBFrequency);
addIntegerManualSetting("MANual:DACB_AMPlitude", &ManualControlDialogVFD::setDACBAmplitude, &ManualControlDialogVFD::getDACBAmplitude);
addBooleanManualSetting("MANual:ADC_EN", &ManualControlDialogVFD::setADCEnable, &ManualControlDialogVFD::getADCEnable);
addIntegerManualSetting("MANual:ADC_SAMPLES", &ManualControlDialogVFD::setADCSamples, &ManualControlDialogVFD::getADCSamples);
addIntegerManualSetting("MANual:ADC_TESTPATTERN", &ManualControlDialogVFD::setADCTestPattern, &ManualControlDialogVFD::getADCTestPattern);
connect(&dev, &LibreVNADriver::receivedPacket, this, [=](const Protocol::PacketInfo &p){
if(p.type == Protocol::PacketType::ArrayData) {
// incoming spectrum data
const Protocol::ArrayData data = p.arrayData;
int index = 0;
auto series = spectrumSeries[data.id];
auto chart = charts[data.id];
chart->removeSeries(series);
// qDebug() << "Incoming spectrum data";
for(unsigned int i=0;i<data.values/2;i++) {
auto freq = data.xbegin + (data.xend - data.xbegin) * i / (data.values/2);
freq /= 1000000; // convert to MHz
auto d = std::complex<double>(data.data[i*2], data.data[i*2+1]);
auto dB = 20*log10(abs(d));
auto point = QPointF(freq, dB);
// get index at which this point should be inserted
while(index < series->count() && series->at(index).x() < freq) {
index++;
}
// qDebug() << "point at" << freq <<": " << dB << "(index:" << index << ")";
if(index >= series->count()) {
// append at the end
series->append(point);
} else if(freq == series->at(index).x()) {
// replace existing point
series->replace(index, point);
} else {
// insert at position
series->insert(index, point);
}
}
chart->addSeries(series);
series->attachAxis(chart->axes(Qt::Horizontal)[0]);
series->attachAxis(chart->axes(Qt::Vertical)[0]);
}
}, Qt::QueuedConnection);
for(auto c : commands) {
emit dev.addSCPICommand(c);
}
@ -627,6 +702,36 @@ int ManualControlDialogVFD::getDACBAmplitude()
return ui->DACAmplitudeB->value();
}
void ManualControlDialogVFD::setADCEnable(bool enable)
{
ui->ADCEnable->setChecked(enable);
}
bool ManualControlDialogVFD::getADCEnable()
{
return ui->ADCEnable->isChecked();
}
void ManualControlDialogVFD::setADCSamples(int samples)
{
ui->ADCSamples->setValue(samples);
}
int ManualControlDialogVFD::getADCSamples()
{
return ui->ADCSamples->value();
}
void ManualControlDialogVFD::setADCTestPattern(int tp)
{
ui->ADCTestPattern->setCurrentIndex(tp);
}
int ManualControlDialogVFD::getADCTestPattern()
{
return ui->ADCTestPattern->currentIndex();
}
void ManualControlDialogVFD::UpdateDevice()
{
Protocol::PacketInfo p;
@ -659,6 +764,11 @@ void ManualControlDialogVFD::UpdateDevice()
m.DACAmpB = ui->DACAmplitudeB->value();
m.DACEn = ui->DACEnable->isChecked();
// ADC
m.ADCEn = ui->ADCEnable->isChecked();
m.ADCSamples = ui->ADCSamples->value();
m.ADCTestPattern = ui->ADCTestPattern->currentIndex();
qDebug() << "Updating manual control state";
dev.SendPacket(p);

View file

@ -4,6 +4,8 @@
#include "librevnadriver.h"
#include <QDialog>
#include <QChart>
#include <QLineSeries>
#include <complex>
namespace Ui {
@ -106,12 +108,24 @@ public:
void setDACBAmplitude(int a);
int getDACBAmplitude();
void setADCEnable(bool enable);
bool getADCEnable();
void setADCSamples(int samples);
int getADCSamples();
void setADCTestPattern(int tp);
int getADCTestPattern();
private:
void UpdateDevice();
Ui::ManualControlDialogVFD *ui;
LibreVNADriver &dev;
std::vector<SCPICommand*> commands;
std::array<QChart*, 4> charts;
std::array<QLineSeries*, 4> spectrumSeries;
};
#endif // MANUALCONTROLDIALOGV1_H

View file

@ -9,20 +9,20 @@
<rect>
<x>0</x>
<y>0</y>
<width>640</width>
<height>453</height>
<width>1270</width>
<height>827</height>
</rect>
</property>
<property name="windowTitle">
<string>Manual Control</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1">
<item>
<widget class="QGroupBox" name="groupBox_10">
<property name="title">
<string>Signal Generation</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
@ -265,162 +265,292 @@
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>LO PLL</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Frequency:</string>
</property>
</widget>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>DAC</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Enabled:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="DACEnable">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Output A Frequency:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="DACFrequencyA"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Output A Amplitude:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="DACAmplitudeA">
<property name="maximum">
<number>2047</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Output A Frequency:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="DACFrequencyB"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Output A Amplitude:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="DACAmplitudeB">
<property name="maximum">
<number>2047</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>LO PLL</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Frequency:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="LOFrequency"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Mode:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="LOMode">
<item>
<property name="text">
<string>Internal - HF</string>
</property>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="LOFrequency"/>
<item>
<property name="text">
<string>Internal -LF</string>
</property>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Mode:</string>
</property>
</widget>
<item>
<property name="text">
<string>External</string>
</property>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="LOMode">
<item>
<property name="text">
<string>Internal - HF</string>
</property>
</item>
<item>
<property name="text">
<string>Internal -LF</string>
</property>
</item>
<item>
<property name="text">
<string>External</string>
</property>
</item>
</widget>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="LOLocked">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Locked</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="LORFEN">
<property name="text">
<string>RF Enable</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="LOCE">
<property name="text">
<string>Chip Enable</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Amplifier:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="LOAmpEn">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Data Acquisition</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Spectrum View</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QChartView" name="spectrumA"/>
</item>
<item row="0" column="1">
<widget class="QChartView" name="spectrumB"/>
</item>
<item row="1" column="0">
<widget class="QChartView" name="spectrumC"/>
</item>
<item row="1" column="1">
<widget class="QChartView" name="spectrumD"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Settings</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>ADC Enable:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="ADCEnable">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Samples:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="ADCSamples">
<property name="minimum">
<number>16</number>
</property>
<property name="maximum">
<number>131072</number>
</property>
<property name="singleStep">
<number>16</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Testpattern:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="ADCTestPattern">
<item>
<property name="text">
<string>Off</string>
</property>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="LOLocked">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Locked</string>
</property>
</widget>
<item>
<property name="text">
<string>Zeros</string>
</property>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="LORFEN">
<property name="text">
<string>RF Enable</string>
</property>
</widget>
<item>
<property name="text">
<string>Ones</string>
</property>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="LOCE">
<property name="text">
<string>Chip Enable</string>
</property>
</widget>
<item>
<property name="text">
<string>Toggle</string>
</property>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Amplifier:</string>
</property>
</widget>
<item>
<property name="text">
<string>Ramp</string>
</property>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="LOAmpEn">
<property name="text">
<string/>
</property>
</widget>
<item>
<property name="text">
<string>Custom</string>
</property>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>DAC</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Enabled:</string>
</property>
</widget>
<item>
<property name="text">
<string>Deskew</string>
</property>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="DACEnable">
<property name="text">
<string/>
</property>
</widget>
<item>
<property name="text">
<string>Reserved</string>
</property>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Output A Frequency:</string>
</property>
</widget>
<item>
<property name="text">
<string>PRBS</string>
</property>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="DACFrequencyA"/>
<item>
<property name="text">
<string>Sine</string>
</property>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Output A Amplitude:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="DACAmplitudeA">
<property name="maximum">
<number>2047</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Output A Frequency:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="DACFrequencyB"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Output A Amplitude:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="DACAmplitudeB">
<property name="maximum">
<number>2047</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
@ -433,6 +563,11 @@
<extends>QLineEdit</extends>
<header>CustomWidgets/siunitedit.h</header>
</customwidget>
<customwidget>
<class>QChartView</class>
<extends>QGraphicsView</extends>
<header>QtCharts</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View file

@ -330,7 +330,7 @@ mac{
PKGCONFIG += libusb-1.0
}
QT += widgets network
QT += widgets network charts
FORMS += \
Calibration/CalStandardLineEditDialog.ui \

View file

@ -1,4 +1,4 @@
QT += testlib widgets network
QT += testlib widgets network charts
CONFIG += qt console warn_on depend_includepath testcase
CONFIG -= app_bundle

View file

@ -5,45 +5,48 @@
namespace PacketConstants {
// USB protocol packet field constants
// USB protocol packet field constants
static constexpr uint8_t PCKT_HEADER_DATA = 0x5A;
static constexpr uint8_t PCKT_HEADER_DATA = 0x5A;
/////////////////////////////////////////////////////////////
// General packet structure field sizes and offsets in bytes
static constexpr uint8_t PCKT_HEADER_OFFSET = 0;
static constexpr uint8_t PCKT_HEADER_LEN = 1;
static constexpr uint8_t PCKT_LENGTH_OFFSET = PCKT_HEADER_OFFSET + PCKT_HEADER_LEN; // offset one byte
static constexpr uint8_t PCKT_LENGTH_LEN = 2;
static constexpr uint8_t PCKT_TYPE_OFFSET = PCKT_LENGTH_OFFSET + PCKT_LENGTH_LEN; // offset three bytes
static constexpr uint8_t PCKT_TYPE_LEN = 1;
static constexpr uint8_t PCKT_PAYLOAD_OFFSET = PCKT_TYPE_OFFSET + PCKT_TYPE_LEN; // offset four bytes
static constexpr uint8_t PCKT_CRC_LEN = 4;
/////////////////////////////////////////////////////////////
// General packet structure field sizes and offsets in bytes
static constexpr uint8_t PCKT_HEADER_OFFSET = 0;
static constexpr uint8_t PCKT_HEADER_LEN = 1;
static constexpr uint8_t PCKT_LENGTH_OFFSET = PCKT_HEADER_OFFSET + PCKT_HEADER_LEN; // offset one byte
static constexpr uint8_t PCKT_LENGTH_LEN = 2;
static constexpr uint8_t PCKT_TYPE_OFFSET = PCKT_LENGTH_OFFSET + PCKT_LENGTH_LEN; // offset three bytes
static constexpr uint8_t PCKT_TYPE_LEN = 1;
static constexpr uint8_t PCKT_PAYLOAD_OFFSET = PCKT_TYPE_OFFSET + PCKT_TYPE_LEN; // offset four bytes
static constexpr uint8_t PCKT_CRC_LEN = 4;
static constexpr uint8_t PCKT_COMBINED_HEADER_LEN = PCKT_HEADER_LEN + PCKT_LENGTH_LEN + PCKT_TYPE_LEN; // combined length of fields preceding payload
static constexpr uint8_t PCKT_EXCL_PAYLOAD_LEN = PCKT_COMBINED_HEADER_LEN + PCKT_CRC_LEN; // combined length of all packet fields, excluding payload
static constexpr uint8_t PCKT_COMBINED_HEADER_LEN = PCKT_HEADER_LEN + PCKT_LENGTH_LEN + PCKT_TYPE_LEN; // combined length of fields preceding payload
static constexpr uint8_t PCKT_EXCL_PAYLOAD_LEN = PCKT_COMBINED_HEADER_LEN + PCKT_CRC_LEN; // combined length of all packet fields, excluding payload
/////////////////////////////////////////////////////////////
// Payload content
/////////////////////////////////////////////////////////////
// Payload content
// Firmware packets
static constexpr uint16_t FW_CHUNK_SIZE = 256;
// Firmware packets
static constexpr uint16_t FW_CHUNK_SIZE = 256;
// VNADataPoint payload fields in bytes
static constexpr uint8_t DPNT_FREQ_LEN = 8;
static constexpr uint8_t DPNT_POW_LVL_LEN = 2;
static constexpr uint8_t DPNT_PNT_NUM_LEN = 2;
static constexpr uint8_t DPNT_REAL_PART_LEN = 4;
static constexpr uint8_t DPNT_IMAG_PART_LEN = 4;
static constexpr uint8_t DPNT_DESC_LEN = 1;
// VNADataPoint payload fields in bytes
static constexpr uint8_t DPNT_FREQ_LEN = 8;
static constexpr uint8_t DPNT_POW_LVL_LEN = 2;
static constexpr uint8_t DPNT_PNT_NUM_LEN = 2;
static constexpr uint8_t DPNT_REAL_PART_LEN = 4;
static constexpr uint8_t DPNT_IMAG_PART_LEN = 4;
static constexpr uint8_t DPNT_DESC_LEN = 1;
// VNADataPoint configuration bitmask offsets in bits
static constexpr uint8_t DPNT_CONF_P1_OFFSET = 0;
static constexpr uint8_t DPNT_CONF_P2_OFFSET = 1;
static constexpr uint8_t DPNT_CONF_P3_OFFSET = 2;
static constexpr uint8_t DPNT_CONF_P4_OFFSET = 3;
static constexpr uint8_t DPNT_CONF_REF_OFFSET = 4;
static constexpr uint8_t DPNT_CONF_STAGE_OFFSET = 5;
// VNADataPoint configuration bitmask offsets in bits
static constexpr uint8_t DPNT_CONF_P1_OFFSET = 0;
static constexpr uint8_t DPNT_CONF_P2_OFFSET = 1;
static constexpr uint8_t DPNT_CONF_P3_OFFSET = 2;
static constexpr uint8_t DPNT_CONF_P4_OFFSET = 3;
static constexpr uint8_t DPNT_CONF_REF_OFFSET = 4;
static constexpr uint8_t DPNT_CONF_STAGE_OFFSET = 5;
// DataArray packets
static constexpr uint16_t ARRAY_PCKT_MAX_NUM = 62;
}

View file

@ -13,42 +13,42 @@
#define CRC32_POLYGON 0xEDB88320
uint32_t Protocol::CRC32(uint32_t crc, const void *data, uint32_t len) {
uint8_t *u8buf = (uint8_t*) data;
int k;
uint8_t *u8buf = (uint8_t*) data;
int k;
crc = ~crc;
while (len--) {
crc ^= *u8buf++;
for (k = 0; k < 8; k++)
crc = crc & 1 ? (crc >> 1) ^ CRC32_POLYGON : crc >> 1;
}
return ~crc;
crc = ~crc;
while (len--) {
crc ^= *u8buf++;
for (k = 0; k < 8; k++)
crc = crc & 1 ? (crc >> 1) ^ CRC32_POLYGON : crc >> 1;
}
return ~crc;
}
uint16_t Protocol::DecodeBuffer(uint8_t *buf, uint16_t len, PacketInfo *info) {
if (!info || !len) {
info->type = PacketType::None;
return 0;
}
uint8_t *data = buf;
/* Remove any out-of-order bytes in front of the frame */
while (*data != PCKT_HEADER_DATA) {
data++;
if(--len == 0) {
/* Reached end of data */
/* No frame contained in data */
info->type = PacketType::None;
return data - buf;
}
}
/* At this point, data points to the beginning of the frame */
if(len < PCKT_COMBINED_HEADER_LEN) {
/* the frame header has not been completely received */
info->type = PacketType::None;
return data - buf;
}
return 0;
}
uint8_t *data = buf;
/* Remove any out-of-order bytes in front of the frame */
while (*data != PCKT_HEADER_DATA) {
data++;
if(--len == 0) {
/* Reached end of data */
/* No frame contained in data */
info->type = PacketType::None;
return data - buf;
}
}
/* At this point, data points to the beginning of the frame */
if(len < PCKT_COMBINED_HEADER_LEN) {
/* the frame header has not been completely received */
info->type = PacketType::None;
return data - buf;
}
/* Evaluate frame size */
/* Evaluate frame size */
uint16_t length = data[PCKT_LENGTH_OFFSET] | ((uint16_t) data[2] << 8);
if(length > sizeof(PacketInfo) * 2 || length < PCKT_EXCL_PAYLOAD_LEN) {
@ -58,46 +58,46 @@ uint16_t Protocol::DecodeBuffer(uint8_t *buf, uint16_t len, PacketInfo *info) {
}
if(len < length) {
/* The frame payload has not been completely received */
info->type = PacketType::None;
return data - buf;
}
/* The frame payload has not been completely received */
info->type = PacketType::None;
return data - buf;
}
/* The complete frame has been received, check checksum */
auto type = (PacketType) data[PCKT_TYPE_OFFSET];
/* The complete frame has been received, check checksum */
auto type = (PacketType) data[PCKT_TYPE_OFFSET];
uint32_t crc = (uint32_t) data[length-4] | ((uint32_t) data[length-3] << 8) | ((uint32_t) data[length-2] << 16) | ((uint32_t) data[length-1] << 24);
if(type != PacketType::VNADatapoint) {
uint32_t compare = CRC32(0, data, length - PCKT_CRC_LEN);
if(crc != compare) {
// CRC mismatch, remove header
data += 1;
info->type = PacketType::None;
return data - buf;
}
// Valid packet, copy packet type and payload
memcpy(info, &data[PCKT_TYPE_OFFSET], length - 7);
} else {
// Datapoint has the CRC set to zero
if(crc != 0x00000000) {
data += 1;
info->type = PacketType::None;
return data - buf;
}
// Create the datapoint
info->type = (PacketType) data[PCKT_TYPE_OFFSET];
info->VNAdatapoint = new VNADatapoint<32>;
info->VNAdatapoint->decode(&data[PCKT_PAYLOAD_OFFSET], length - PCKT_EXCL_PAYLOAD_LEN);
}
if(type != PacketType::VNADatapoint) {
uint32_t compare = CRC32(0, data, length - PCKT_CRC_LEN);
if(crc != compare) {
// CRC mismatch, remove header
data += 1;
info->type = PacketType::None;
return data - buf;
}
// Valid packet, copy packet type and payload
memcpy(info, &data[PCKT_TYPE_OFFSET], length - 7);
} else {
// Datapoint has the CRC set to zero
if(crc != 0x00000000) {
data += 1;
info->type = PacketType::None;
return data - buf;
}
// Create the datapoint
info->type = (PacketType) data[PCKT_TYPE_OFFSET];
info->VNAdatapoint = new VNADatapoint<32>;
info->VNAdatapoint->decode(&data[PCKT_PAYLOAD_OFFSET], length - PCKT_EXCL_PAYLOAD_LEN);
}
return data - buf + length;
return data - buf + length;
}
uint16_t Protocol::EncodePacket(const PacketInfo &packet, uint8_t *dest, uint16_t destsize) {
int16_t payload_size = 0;
switch (packet.type) {
// case PacketType::Datapoint: payload_size = sizeof(packet.datapoint); break;
case PacketType::SweepSettings: payload_size = sizeof(packet.settings); break;
case PacketType::Reference: payload_size = sizeof(packet.reference); break;
uint16_t Protocol::GetRequiredSize(const PacketInfo &packet) {
int16_t payload_size = 0;
switch (packet.type) {
// case PacketType::Datapoint: payload_size = sizeof(packet.datapoint); break;
case PacketType::SweepSettings: payload_size = sizeof(packet.settings); break;
case PacketType::Reference: payload_size = sizeof(packet.reference); break;
case PacketType::DeviceInfo: payload_size = sizeof(packet.info); break;
case PacketType::DeviceStatus: payload_size = sizeof(packet.status); break;
case PacketType::ManualStatus: payload_size = sizeof(packet.manualStatus); break;
@ -129,32 +129,38 @@ uint16_t Protocol::EncodePacket(const PacketInfo &packet, uint8_t *dest, uint16_
// no payload
break;
case PacketType::VNADatapoint: payload_size = packet.VNAdatapoint->requiredBufferSize(); break;
case PacketType::ArrayData: payload_size = 12 + packet.arrayData.values * 4; break;
case PacketType::None:
break;
}
if (payload_size < 0 || payload_size + PCKT_EXCL_PAYLOAD_LEN > destsize) {
// encoding failed, buffer too small
return 0;
}
// Write header
dest[PCKT_HEADER_OFFSET] = PCKT_HEADER_DATA;
uint16_t overall_size = payload_size + PCKT_EXCL_PAYLOAD_LEN;
memcpy(&dest[PCKT_LENGTH_OFFSET], &overall_size, PCKT_LENGTH_LEN);
// Further encoding uses a special case for VNADatapoint packettype
uint32_t crc = 0x00000000;
if(packet.type == PacketType::VNADatapoint) {
// CRC calculation takes about 18us which is the bulk of the time required to encode and transmit a datapoint.
// Skip CRC for data points to optimize throughput
dest[PCKT_TYPE_OFFSET] = (uint8_t) packet.type;
packet.VNAdatapoint->encode(&dest[PCKT_PAYLOAD_OFFSET], destsize - PCKT_EXCL_PAYLOAD_LEN);
crc = 0x00000000;
} else {
// Copy rest of the packet
memcpy(&dest[PCKT_TYPE_OFFSET], &packet, payload_size + PCKT_TYPE_LEN); // one additional byte for the packet type
// Calculate the CRC
crc = CRC32(0, dest, overall_size - PCKT_CRC_LEN);
}
memcpy(&dest[overall_size - PCKT_CRC_LEN], &crc, PCKT_CRC_LEN);
return overall_size;
return payload_size + PCKT_EXCL_PAYLOAD_LEN;
}
uint16_t Protocol::EncodePacket(const PacketInfo &packet, uint8_t *dest, uint16_t destsize) {
uint16_t overall_size = GetRequiredSize(packet);
if(overall_size > destsize) {
// encoding faile, buffer too small
return 0;
}
// Write header
dest[PCKT_HEADER_OFFSET] = PCKT_HEADER_DATA;
memcpy(&dest[PCKT_LENGTH_OFFSET], &overall_size, PCKT_LENGTH_LEN);
// Further encoding uses a special case for VNADatapoint packettype
uint32_t crc = 0x00000000;
if(packet.type == PacketType::VNADatapoint) {
// CRC calculation takes about 18us which is the bulk of the time required to encode and transmit a datapoint.
// Skip CRC for data points to optimize throughput
dest[PCKT_TYPE_OFFSET] = (uint8_t) packet.type;
packet.VNAdatapoint->encode(&dest[PCKT_PAYLOAD_OFFSET], destsize - PCKT_EXCL_PAYLOAD_LEN);
crc = 0x00000000;
} else {
// Copy rest of the packet
memcpy(&dest[PCKT_TYPE_OFFSET], &packet, overall_size - PCKT_EXCL_PAYLOAD_LEN + PCKT_TYPE_LEN); // one additional byte for the packet type
// Calculate the CRC
crc = CRC32(0, dest, overall_size - PCKT_CRC_LEN);
}
memcpy(&dest[overall_size - PCKT_CRC_LEN], &crc, PCKT_CRC_LEN);
return overall_size;
}

View file

@ -15,56 +15,56 @@ static constexpr uint16_t Version = 14;
#pragma pack(push, 1)
enum class Source : uint8_t {
Port1 = 0x01,
Port2 = 0x02,
Port3 = 0x04,
Port4 = 0x08,
Reference = 0x10,
Port1 = 0x01,
Port2 = 0x02,
Port3 = 0x04,
Port4 = 0x08,
Reference = 0x10,
};
template<int s> class VNADatapoint {
public:
VNADatapoint() {
clear();
}
VNADatapoint() {
clear();
}
void clear() {
num_values = 0;
pointNum = 0;
cdBm = 0;
frequency = 0;
}
bool addValue(float real, float imag, uint8_t stage, int sourceMask) {
if(num_values >= s) {
return false;
}
real_values[num_values] = real;
imag_values[num_values] = imag;
descr_values[num_values] = stage << DPNT_CONF_STAGE_OFFSET | sourceMask;
num_values++;
return true;
}
void clear() {
num_values = 0;
pointNum = 0;
cdBm = 0;
frequency = 0;
}
bool addValue(float real, float imag, uint8_t stage, int sourceMask) {
if(num_values >= s) {
return false;
}
real_values[num_values] = real;
imag_values[num_values] = imag;
descr_values[num_values] = stage << DPNT_CONF_STAGE_OFFSET | sourceMask;
num_values++;
return true;
}
bool encode(uint8_t *dest, uint16_t destSize) {
if(requiredBufferSize() > destSize) {
return false;
}
memcpy(dest, &frequency, DPNT_FREQ_LEN);
dest += DPNT_FREQ_LEN;
memcpy(dest, &cdBm, DPNT_POW_LVL_LEN);
dest += DPNT_POW_LVL_LEN;
memcpy(dest, &pointNum, DPNT_PNT_NUM_LEN);
bool encode(uint8_t *dest, uint16_t destSize) {
if(requiredBufferSize() > destSize) {
return false;
}
memcpy(dest, &frequency, DPNT_FREQ_LEN);
dest += DPNT_FREQ_LEN;
memcpy(dest, &cdBm, DPNT_POW_LVL_LEN);
dest += DPNT_POW_LVL_LEN;
memcpy(dest, &pointNum, DPNT_PNT_NUM_LEN);
dest += DPNT_PNT_NUM_LEN;
memcpy(dest, real_values, num_values * DPNT_REAL_PART_LEN);
dest += num_values * DPNT_REAL_PART_LEN;
memcpy(dest, imag_values, num_values * DPNT_IMAG_PART_LEN);
dest += num_values * DPNT_IMAG_PART_LEN;
memcpy(dest, descr_values, num_values);
return true;
}
memcpy(dest, real_values, num_values * DPNT_REAL_PART_LEN);
dest += num_values * DPNT_REAL_PART_LEN;
memcpy(dest, imag_values, num_values * DPNT_IMAG_PART_LEN);
dest += num_values * DPNT_IMAG_PART_LEN;
memcpy(dest, descr_values, num_values);
return true;
}
void decode(const uint8_t *buffer, uint16_t size) {
num_values = (size - (DPNT_FREQ_LEN + DPNT_POW_LVL_LEN + DPNT_PNT_NUM_LEN)) /
(DPNT_REAL_PART_LEN + DPNT_IMAG_PART_LEN + DPNT_DESC_LEN);
(DPNT_REAL_PART_LEN + DPNT_IMAG_PART_LEN + DPNT_DESC_LEN);
memcpy(&frequency, buffer, DPNT_FREQ_LEN);
buffer += DPNT_FREQ_LEN;
memcpy(&cdBm, buffer, DPNT_POW_LVL_LEN);
@ -76,25 +76,25 @@ public:
memcpy(imag_values, buffer, num_values * DPNT_IMAG_PART_LEN);
buffer += num_values * DPNT_IMAG_PART_LEN;
memcpy(descr_values, buffer, num_values);
}
}
std::complex<double> getValue(uint8_t stage, uint8_t port, bool reference) {
uint8_t sourceMask = 0;
sourceMask |= 0x01 << port;
if(reference) {
sourceMask |= (int) Source::Reference;
}
for(int i=0;i<num_values;i++) {
if(descr_values[i] >> DPNT_CONF_STAGE_OFFSET != stage) {
continue;
}
if((descr_values[i] & sourceMask) != sourceMask) {
continue;
}
return std::complex<double>(real_values[i], imag_values[i]);
}
return std::numeric_limits<std::complex<double>>::quiet_NaN();
}
std::complex<double> getValue(uint8_t stage, uint8_t port, bool reference) {
uint8_t sourceMask = 0;
sourceMask |= 0x01 << port;
if(reference) {
sourceMask |= (int) Source::Reference;
}
for(int i=0;i<num_values;i++) {
if(descr_values[i] >> DPNT_CONF_STAGE_OFFSET != stage) {
continue;
}
if((descr_values[i] & sourceMask) != sourceMask) {
continue;
}
return std::complex<double>(real_values[i], imag_values[i]);
}
return std::numeric_limits<std::complex<double>>::quiet_NaN();
}
class Value {
public:
@ -115,104 +115,104 @@ public:
return num_values;
}
uint16_t requiredBufferSize() {
return DPNT_FREQ_LEN + DPNT_POW_LVL_LEN + DPNT_PNT_NUM_LEN +
num_values * (DPNT_REAL_PART_LEN + DPNT_IMAG_PART_LEN + DPNT_DESC_LEN);
}
uint16_t requiredBufferSize() {
return DPNT_FREQ_LEN + DPNT_POW_LVL_LEN + DPNT_PNT_NUM_LEN +
num_values * (DPNT_REAL_PART_LEN + DPNT_IMAG_PART_LEN + DPNT_DESC_LEN);
}
union {
uint64_t frequency;
uint64_t us;
};
int16_t cdBm;
uint16_t pointNum;
union {
uint64_t frequency;
uint64_t us;
};
int16_t cdBm;
uint16_t pointNum;
private:
float real_values[s];
float imag_values[s];
uint8_t descr_values[s];
uint8_t num_values;
float real_values[s];
float imag_values[s];
uint8_t descr_values[s];
uint8_t num_values;
};
using Datapoint = struct _datapoint {
float real_S11, imag_S11;
float real_S21, imag_S21;
float real_S12, imag_S12;
float real_S22, imag_S22;
union {
struct {
// for non-zero span
uint64_t frequency;
int16_t cdbm;
};
struct {
// for zero span
uint64_t us; // time in us since first datapoint
};
};
float real_S11, imag_S11;
float real_S21, imag_S21;
float real_S12, imag_S12;
float real_S22, imag_S22;
union {
struct {
// for non-zero span
uint64_t frequency;
int16_t cdbm;
};
struct {
// for zero span
uint64_t us; // time in us since first datapoint
};
};
uint16_t pointNum;
};
using SweepSettings = struct _sweepSettings {
uint64_t f_start;
uint64_t f_stop;
uint64_t f_start;
uint64_t f_stop;
uint16_t points;
uint32_t if_bandwidth;
int16_t cdbm_excitation_start; // in 1/100 dbm
uint8_t standby:1;
uint8_t syncMaster:1;
uint8_t suppressPeaks:1;
uint8_t fixedPowerSetting:1; // if set the attenuator and source PLL power will not be changed across the sweep
uint8_t logSweep:1;
/*
* 0: no synchronization
* 1: USB synchronization
* 2: External reference synchronization
* 3: Trigger synchronization (not supported yet by hardware)
*/
uint8_t syncMode:2;
uint8_t unused1:1;
uint8_t syncMaster:1;
uint8_t suppressPeaks:1;
uint8_t fixedPowerSetting:1; // if set the attenuator and source PLL power will not be changed across the sweep
uint8_t logSweep:1;
/*
* 0: no synchronization
* 1: USB synchronization
* 2: External reference synchronization
* 3: Trigger synchronization (not supported yet by hardware)
*/
uint8_t syncMode:2;
uint8_t unused1:1;
uint16_t stages:3;
uint16_t port1Stage:3;
uint16_t port2Stage:3;
uint16_t port3Stage:3;
uint16_t port4Stage:3;
uint16_t unused2:1;
uint16_t stages:3;
uint16_t port1Stage:3;
uint16_t port2Stage:3;
uint16_t port3Stage:3;
uint16_t port4Stage:3;
uint16_t unused2:1;
int16_t cdbm_excitation_stop; // in 1/100 dbm
uint16_t dwell_time; // in us
};
using ReferenceSettings = struct _referenceSettings {
uint32_t ExtRefOuputFreq;
uint8_t AutomaticSwitch:1;
uint8_t UseExternalRef:1;
uint32_t ExtRefOuputFreq;
uint8_t AutomaticSwitch:1;
uint8_t UseExternalRef:1;
};
using GeneratorSettings = struct _generatorSettings {
uint64_t frequency;
int16_t cdbm_level;
uint64_t frequency;
int16_t cdbm_level;
uint8_t activePort :3;
uint8_t applyAmplitudeCorrection :1;
uint8_t unused :4;
};
using DeviceInfo = struct _deviceInfo {
uint16_t ProtocolVersion;
uint16_t ProtocolVersion;
uint8_t FW_major;
uint8_t FW_minor;
uint8_t FW_patch;
uint8_t hardware_version;
char HW_Revision;
uint64_t limits_minFreq;
uint64_t limits_maxFreq;
uint32_t limits_minIFBW;
uint32_t limits_maxIFBW;
uint16_t limits_maxPoints;
int16_t limits_cdbm_min;
int16_t limits_cdbm_max;
uint32_t limits_minRBW;
uint32_t limits_maxRBW;
uint64_t limits_minFreq;
uint64_t limits_maxFreq;
uint32_t limits_minIFBW;
uint32_t limits_maxIFBW;
uint16_t limits_maxPoints;
int16_t limits_cdbm_min;
int16_t limits_cdbm_max;
uint32_t limits_minRBW;
uint32_t limits_maxRBW;
uint8_t limits_maxAmplitudePoints;
uint64_t limits_maxFreqHarmonic;
uint8_t num_ports;
@ -220,26 +220,26 @@ using DeviceInfo = struct _deviceInfo {
};
using DeviceStatus = struct _deviceStatus {
union {
struct {
uint8_t extRefAvailable:1;
uint8_t extRefInUse:1;
uint8_t FPGA_configured:1;
uint8_t source_locked:1;
uint8_t LO1_locked:1;
uint8_t ADC_overload:1;
uint8_t unlevel:1;
uint8_t temp_source;
uint8_t temp_LO1;
uint8_t temp_MCU;
} V1;
struct {
uint8_t source_locked:1;
uint8_t LO_locked:1;
uint8_t ADC_overload:1;
uint8_t unlevel:1;
uint8_t temp_MCU;
} VFF;
union {
struct {
uint8_t extRefAvailable:1;
uint8_t extRefInUse:1;
uint8_t FPGA_configured:1;
uint8_t source_locked:1;
uint8_t LO1_locked:1;
uint8_t ADC_overload:1;
uint8_t unlevel:1;
uint8_t temp_source;
uint8_t temp_LO1;
uint8_t temp_MCU;
} V1;
struct {
uint8_t source_locked:1;
uint8_t LO_locked:1;
uint8_t ADC_overload:1;
uint8_t unlevel:1;
uint8_t temp_MCU;
} VFF;
struct {
uint8_t source_locked:1;
uint8_t LO_locked:1;
@ -249,32 +249,32 @@ using DeviceStatus = struct _deviceStatus {
uint16_t temp_eCal; // in 1/100 °C
uint16_t power_heater; // in mW
} VFE;
};
};
};
using ManualStatus = struct _manualstatus {
union {
struct {
int16_t port1min, port1max;
int16_t port2min, port2max;
int16_t refmin, refmax;
float port1real, port1imag;
float port2real, port2imag;
float refreal, refimag;
uint8_t temp_source;
uint8_t temp_LO;
uint8_t source_locked :1;
uint8_t LO_locked :1;
} V1;
struct {
int16_t portmin, portmax;
int16_t refmin, refmax;
float portreal, portimag;
float refreal, refimag;
uint8_t source_locked :1;
uint8_t LO_locked :1;
} VFF;
union {
struct {
int16_t port1min, port1max;
int16_t port2min, port2max;
int16_t refmin, refmax;
float port1real, port1imag;
float port2real, port2imag;
float refreal, refimag;
uint8_t temp_source;
uint8_t temp_LO;
uint8_t source_locked :1;
uint8_t LO_locked :1;
} V1;
struct {
int16_t portmin, portmax;
int16_t refmin, refmax;
float portreal, portimag;
float refreal, refimag;
uint8_t source_locked :1;
uint8_t LO_locked :1;
} VFF;
struct {
int16_t portmin, portmax;
int16_t refmin, refmax;
@ -285,41 +285,41 @@ using ManualStatus = struct _manualstatus {
uint16_t temp_eCal; // in 1/100 °C
uint16_t power_heater; // in mW
} VFE;
};
};
};
using ManualControl = struct _manualControl {
union {
struct {
// Highband Source
uint8_t SourceHighCE :1;
uint8_t SourceHighRFEN :1;
uint8_t SourceHighPower :2;
uint8_t SourceHighLowpass :2;
uint64_t SourceHighFrequency;
// Lowband Source
uint8_t SourceLowEN :1;
uint8_t SourceLowPower :2;
uint32_t SourceLowFrequency;
// Source signal path
uint8_t attenuator :7;
uint8_t SourceHighband :1;
uint8_t AmplifierEN :1;
uint8_t PortSwitch :1;
// LO1
uint8_t LO1CE :1;
uint8_t LO1RFEN :1;
uint64_t LO1Frequency;
// LO2
uint8_t LO2EN :1;
uint32_t LO2Frequency;
// Acquisition
uint8_t Port1EN :1;
uint8_t Port2EN :1;
uint8_t RefEN :1;
uint32_t Samples;
uint8_t WindowType :2;
} V1;
union {
struct {
// Highband Source
uint8_t SourceHighCE :1;
uint8_t SourceHighRFEN :1;
uint8_t SourceHighPower :2;
uint8_t SourceHighLowpass :2;
uint64_t SourceHighFrequency;
// Lowband Source
uint8_t SourceLowEN :1;
uint8_t SourceLowPower :2;
uint32_t SourceLowFrequency;
// Source signal path
uint8_t attenuator :7;
uint8_t SourceHighband :1;
uint8_t AmplifierEN :1;
uint8_t PortSwitch :1;
// LO1
uint8_t LO1CE :1;
uint8_t LO1RFEN :1;
uint64_t LO1Frequency;
// LO2
uint8_t LO2EN :1;
uint32_t LO2Frequency;
// Acquisition
uint8_t Port1EN :1;
uint8_t Port2EN :1;
uint8_t RefEN :1;
uint32_t Samples;
uint8_t WindowType :2;
} V1;
struct {
uint64_t SourceFrequency;
@ -351,30 +351,35 @@ using ManualControl = struct _manualControl {
uint32_t DACFreqB;
uint16_t DACAmpA;
uint16_t DACAmpB;
// ADC
uint8_t ADCTestPattern :4;
uint8_t ADCEn :1;
uint32_t ADCSamples;
} VFD;
struct {
// Source
uint8_t SourceCE :1;
uint8_t SourceRFEN :1;
struct {
// Source
uint8_t SourceCE :1;
uint8_t SourceRFEN :1;
uint8_t SourcePower :3;
uint64_t SourceFrequency;
// Source signal path
uint8_t attenuator :7;
// Source signal path
uint8_t attenuator :7;
uint8_t SourceAmplifierEN :1;
// LO
uint8_t LOCE :1;
uint8_t LORFEN :1;
// LO
uint8_t LOCE :1;
uint8_t LORFEN :1;
uint8_t LOAmplifierEN :1;
uint8_t LOexternal :1;
uint64_t LOFrequency;
// Acquisition
uint16_t PortEN :1;
uint16_t RefEN :1;
uint16_t WindowType :2;
uint16_t PortGain :4;
uint16_t RefGain :4;
uint16_t Samples;
} VFF;
// Acquisition
uint16_t PortEN :1;
uint16_t RefEN :1;
uint16_t WindowType :2;
uint16_t PortGain :4;
uint16_t RefGain :4;
uint16_t Samples;
} VFF;
struct {
// Source
uint8_t SourceCE :1;
@ -399,28 +404,28 @@ using ManualControl = struct _manualControl {
uint8_t eCal_state :2;
uint16_t eCal_target; // in 1/100 °C
} VFE;
};
};
};
using SpectrumAnalyzerSettings = struct _spectrumAnalyzerSettings {
uint64_t f_start;
uint64_t f_stop;
uint32_t RBW;
uint16_t pointNum;
uint8_t WindowType :2;
uint8_t SignalID :1;
uint8_t Detector :3;
uint8_t UseDFT :1;
uint64_t f_start;
uint64_t f_stop;
uint32_t RBW;
uint16_t pointNum;
uint8_t WindowType :2;
uint8_t SignalID :1;
uint8_t Detector :3;
uint8_t UseDFT :1;
uint8_t applyReceiverCorrection :1;
uint8_t trackingGenerator :1;
uint8_t applySourceCorrection :1;
uint8_t trackingGeneratorPort :2; // port count starts at zero
/*
* 0: no synchronization
* 1: Protocol synchronization (via SetTrigger and ClearTrigger packets)
* 2: Reserved
* 3: Trigger synchronization (not supported yet by hardware)
*/
/*
* 0: no synchronization
* 1: Protocol synchronization (via SetTrigger and ClearTrigger packets)
* 2: Reserved
* 3: Trigger synchronization (not supported yet by hardware)
*/
uint8_t syncMode :2;
uint8_t syncMaster :1;
int64_t trackingGeneratorOffset;
@ -428,21 +433,21 @@ using SpectrumAnalyzerSettings = struct _spectrumAnalyzerSettings {
};
using SpectrumAnalyzerResult = struct _spectrumAnalyzerResult {
float port1;
float port2;
float port3;
float port4;
union {
struct {
// for non-zero span
uint64_t frequency;
};
struct {
// for zero span
uint64_t us; // time in us since first datapoint
};
};
uint16_t pointNum;
float port1;
float port2;
float port3;
float port4;
union {
struct {
// for non-zero span
uint64_t frequency;
};
struct {
// for zero span
uint64_t us; // time in us since first datapoint
};
};
uint16_t pointNum;
};
@ -452,89 +457,100 @@ using FirmwarePacket = struct _firmwarePacket {
};
using AmplitudeCorrectionPoint = struct _amplitudecorrectionpoint {
uint8_t totalPoints;
uint8_t pointNum;
uint32_t freq;
int16_t port1;
int16_t port2;
int16_t port3;
int16_t port4;
uint8_t totalPoints;
uint8_t pointNum;
uint32_t freq;
int16_t port1;
int16_t port2;
int16_t port3;
int16_t port4;
};
using FrequencyCorrection = struct _frequencycorrection {
float ppm;
float ppm;
};
using DeviceConfig = struct _deviceconfig {
union {
struct {
uint32_t IF1;
uint8_t ADCprescaler;
uint16_t DFTphaseInc;
uint8_t PLLSettlingDelay;
} V1;
struct {
uint32_t ip;
uint32_t mask;
uint32_t gw;
union {
struct {
uint32_t IF1;
uint8_t ADCprescaler;
uint16_t DFTphaseInc;
uint8_t PLLSettlingDelay;
} V1;
struct {
uint32_t ip;
uint32_t mask;
uint32_t gw;
uint8_t dhcp :1;
uint8_t unused :7;
uint16_t autogain :1;
uint16_t portGain :4;
uint16_t refGain :4;
} VFF;
} VFF;
struct {
uint16_t autogain :1;
uint16_t portGain :4;
uint16_t refGain :4;
} VFE;
};
};
};
#define PROTOCOL_
using ArrayData = struct _arraydata {
uint16_t id;
uint16_t values;
float xbegin;
float xend;
float data[ARRAY_PCKT_MAX_NUM];
};
enum class PacketType : uint8_t {
None = 0,
//Datapoint = 1, // Deprecated, replaced by VNADatapoint
SweepSettings = 2,
None = 0,
//Datapoint = 1, // Deprecated, replaced by VNADatapoint
SweepSettings = 2,
ManualStatus = 3,
ManualControl = 4,
DeviceInfo = 5,
FirmwarePacket = 6,
Ack = 7,
ClearFlash = 8,
PerformFirmwareUpdate = 9,
Nack = 10,
Reference = 11,
Generator = 12,
SpectrumAnalyzerSettings = 13,
SpectrumAnalyzerResult = 14,
ClearFlash = 8,
PerformFirmwareUpdate = 9,
Nack = 10,
Reference = 11,
Generator = 12,
SpectrumAnalyzerSettings = 13,
SpectrumAnalyzerResult = 14,
RequestDeviceInfo = 15,
RequestSourceCal = 16,
RequestReceiverCal = 17,
SourceCalPoint = 18,
ReceiverCalPoint = 19,
SetIdle = 20,
RequestFrequencyCorrection = 21,
FrequencyCorrection = 22,
RequestDeviceConfiguration = 23,
DeviceConfiguration = 24,
DeviceStatus = 25,
RequestDeviceStatus = 26,
VNADatapoint = 27,
SetTrigger = 28,
ClearTrigger = 29,
StopStatusUpdates = 30,
StartStatusUpdates = 31,
InitiateSweep = 32
RequestSourceCal = 16,
RequestReceiverCal = 17,
SourceCalPoint = 18,
ReceiverCalPoint = 19,
SetIdle = 20,
RequestFrequencyCorrection = 21,
FrequencyCorrection = 22,
RequestDeviceConfiguration = 23,
DeviceConfiguration = 24,
DeviceStatus = 25,
RequestDeviceStatus = 26,
VNADatapoint = 27,
SetTrigger = 28,
ClearTrigger = 29,
StopStatusUpdates = 30,
StartStatusUpdates = 31,
InitiateSweep = 32,
ArrayData = 33,
};
using PacketInfo = struct _packetinfo {
PacketType type;
union {
// Datapoint datapoint; // Deprecated, use VNADatapoint instead
SweepSettings settings;
ReferenceSettings reference;
GeneratorSettings generator;
DeviceStatus status;
PacketType type;
union {
// Datapoint datapoint; // Deprecated, use VNADatapoint instead
SweepSettings settings;
ReferenceSettings reference;
GeneratorSettings generator;
DeviceStatus status;
DeviceInfo info;
ManualControl manual;
FirmwarePacket firmware;
@ -549,13 +565,15 @@ using PacketInfo = struct _packetinfo {
* When decoding: VNADatapoint is created on heap by DecodeBuffer, freeing is up to the caller
*/
VNADatapoint<32> *VNAdatapoint;
};
ArrayData arrayData;
};
};
#pragma pack(pop)
uint32_t CRC32(uint32_t crc, const void *data, uint32_t len);
uint16_t DecodeBuffer(uint8_t *buf, uint16_t len, PacketInfo *info);
uint16_t GetRequiredSize(const PacketInfo &packet);
uint16_t EncodePacket(const PacketInfo &packet, uint8_t *dest, uint16_t destsize);
}