Protocol adjustment + exposing settings for DFT

This commit is contained in:
Jan Käberich 2020-11-07 13:22:10 +01:00
parent ce475fa042
commit a2389fca13
19 changed files with 314 additions and 205 deletions

View File

@ -111,22 +111,40 @@ uint8_t *USBInBuffer::getBuffer() const
return buffer;
}
static Protocol::DeviceLimits limits = {
.minFreq = 0,
.maxFreq = 6000000000,
.minIFBW = 10,
.maxIFBW = 50000,
.maxPoints = 4501,
.cdbm_min = -4000,
.cdbm_max = 0,
.minRBW = 10,
.maxRBW = 100000,
static constexpr Protocol::DeviceInfo defaultInfo = {
.ProtocolVersion = Protocol::Version,
.FW_major = 0,
.FW_minor = 0,
.FW_patch = 0,
.HW_Revision = '0',
.extRefAvailable = 0,
.extRefInUse = 0,
.FPGA_configured = 0,
.source_locked = 0,
.LO1_locked = 0,
.ADC_overload = 0,
.temp_source = 0,
.temp_LO1 = 0,
.temp_MCU = 0,
.limits_minFreq = 0,
.limits_maxFreq = 6000000000,
.limits_minIFBW = 10,
.limits_maxIFBW = 50000,
.limits_maxPoints = 4501,
.limits_cdbm_min = -4000,
.limits_cdbm_max = 0,
.limits_minRBW = 15,
.limits_maxRBW = 100000,
};
Protocol::DeviceInfo Device::lastInfo = defaultInfo;
Device::Device(QString serial)
{
qDebug() << "Starting device connection...";
lastInfo = defaultInfo;
m_handle = nullptr;
lastInfoValid = false;
libusb_init(&m_context);
@ -182,8 +200,8 @@ Device::Device(QString serial)
connect(this, &Device::receivedAnswer, this, &Device::transmissionFinished, Qt::QueuedConnection);
transmissionTimer.setSingleShot(true);
transmissionActive = false;
// got a new connection, request limits
SendCommandWithoutPayload(Protocol::PacketType::RequestDeviceLimits);
// got a new connection, request info
SendCommandWithoutPayload(Protocol::PacketType::RequestDeviceInfo);
}
Device::~Device()
@ -282,11 +300,6 @@ std::set<QString> Device::GetDevices()
return serials;
}
Protocol::DeviceLimits Device::Limits()
{
return limits;
}
void Device::USBHandleThread()
{
qInfo() << "Receive thread started" << flush;
@ -366,7 +379,7 @@ void Device::SearchDevices(std::function<bool (libusb_device_handle *, QString)>
libusb_free_device_list(devList, 1);
}
Protocol::DeviceInfo Device::getLastInfo() const
const Protocol::DeviceInfo &Device::Info()
{
return lastInfo;
}
@ -379,8 +392,8 @@ QString Device::getLastDeviceInfoString()
} else {
ret.append("HW Rev.");
ret.append(lastInfo.HW_Revision);
ret.append(" FW "+QString::number(lastInfo.FW_major)+"."+QString::number(lastInfo.FW_minor).rightJustified(2, '0'));
ret.append(" Temps: "+QString::number(lastInfo.temperatures.source)+"°C/"+QString::number(lastInfo.temperatures.LO1)+"°C/"+QString::number(lastInfo.temperatures.MCU)+"°C");
ret.append(" FW "+QString::number(lastInfo.FW_major)+"."+QString::number(lastInfo.FW_minor)+"."+QString::number(lastInfo.FW_patch));
ret.append(" Temps: "+QString::number(lastInfo.temp_source)+"°C/"+QString::number(lastInfo.temp_LO1)+"°C/"+QString::number(lastInfo.temp_MCU)+"°C");
ret.append(" Reference:");
if(lastInfo.extRefInUse) {
ret.append("External");
@ -412,7 +425,13 @@ void Device::ReceivedData()
emit SpectrumResultReceived(packet.spectrumResult);
break;
case Protocol::PacketType::DeviceInfo:
lastInfo = packet.info;
if(packet.info.ProtocolVersion != Protocol::Version) {
if(!lastInfoValid) {
emit NeedsFirmwareUpdate(packet.info.ProtocolVersion, Protocol::Version);
}
} else {
lastInfo = packet.info;
}
lastInfoValid = true;
emit DeviceInfoUpdated();
break;
@ -424,10 +443,7 @@ void Device::ReceivedData()
emit NackReceived();
emit receivedAnswer(TransmissionResult::Nack);
break;
case Protocol::PacketType::DeviceLimits:
limits = packet.limits;
break;
default:
default:
break;
}
} while (handled_len > 0);

View File

@ -65,12 +65,11 @@ public:
bool SendFirmwareChunk(Protocol::FirmwarePacket &fw);
bool SendCommandWithoutPayload(Protocol::PacketType type);
QString serial() const;
Protocol::DeviceInfo getLastInfo() const;
static const Protocol::DeviceInfo& Info();
QString getLastDeviceInfoString();
// Returns serial numbers of all connected devices
static std::set<QString> GetDevices();
static Protocol::DeviceLimits Limits();
signals:
void DatapointReceived(Protocol::Datapoint);
void ManualStatusReceived(Protocol::ManualStatus);
@ -80,6 +79,7 @@ signals:
void AckReceived();
void NackReceived();
void LogLineReceived(QString line);
void NeedsFirmwareUpdate(int usedProtocol, int requiredProtocol);
private slots:
void ReceivedData();
void ReceivedLog();
@ -119,7 +119,7 @@ private:
QString m_serial;
bool m_connected;
std::thread *m_receiveThread;
Protocol::DeviceInfo lastInfo;
static Protocol::DeviceInfo lastInfo;
bool lastInfoValid;
};

View File

@ -10,10 +10,10 @@ SignalgeneratorWidget::SignalgeneratorWidget(QWidget *parent) :
ui->frequency->setPrefixes(" kMG");
connect(ui->frequency, &SIUnitEdit::valueChanged, [=](double newval) {
if(newval < Device::Limits().minFreq) {
newval = Device::Limits().minFreq;
} else if (newval > Device::Limits().maxFreq) {
newval = Device::Limits().maxFreq;
if(newval < Device::Info().limits_minFreq) {
newval = Device::Info().limits_minFreq;
} else if (newval > Device::Info().limits_maxFreq) {
newval = Device::Info().limits_maxFreq;
}
ui->frequency->setValueQuiet(newval);
emit SettingsChanged();

View File

@ -251,6 +251,13 @@ void SpectrumAnalyzer::SettingsChanged()
settings.pointNum = settings.f_stop - settings.f_start + 1;
}
auto pref = Preferences::getInstance();
if(pref.Acquisition.useDFTinSAmode && settings.RBW <= pref.Acquisition.RBWLimitForDFT) {
settings.UseDFT = 1;
} else {
settings.UseDFT = 0;
}
if(window->getDevice()) {
window->getDevice()->Configure(settings);
}
@ -311,8 +318,8 @@ void SpectrumAnalyzer::SetSpan(double span)
void SpectrumAnalyzer::SetFullSpan()
{
settings.f_start = Device::Limits().minFreq;
settings.f_stop = Device::Limits().maxFreq;
settings.f_start = Device::Info().limits_minFreq;
settings.f_stop = Device::Info().limits_maxFreq;
ConstrainAndUpdateFrequencies();
}
@ -340,10 +347,10 @@ void SpectrumAnalyzer::SpanZoomOut()
void SpectrumAnalyzer::SetRBW(double bandwidth)
{
if(bandwidth > Device::Limits().maxRBW) {
bandwidth = Device::Limits().maxRBW;
} else if(bandwidth < Device::Limits().minRBW) {
bandwidth = Device::Limits().minRBW;
if(bandwidth > Device::Info().limits_maxRBW) {
bandwidth = Device::Info().limits_maxRBW;
} else if(bandwidth < Device::Info().limits_minRBW) {
bandwidth = Device::Info().limits_minRBW;
}
settings.RBW = bandwidth;
emit RBWChanged(settings.RBW);
@ -365,14 +372,14 @@ void SpectrumAnalyzer::UpdateAverageCount()
void SpectrumAnalyzer::ConstrainAndUpdateFrequencies()
{
if(settings.f_stop > Device::Limits().maxFreq) {
settings.f_stop = Device::Limits().maxFreq;
if(settings.f_stop > Device::Info().limits_maxFreq) {
settings.f_stop = Device::Info().limits_maxFreq;
}
if(settings.f_start > settings.f_stop) {
settings.f_start = settings.f_stop;
}
if(settings.f_start < Device::Limits().minFreq) {
settings.f_start = Device::Limits().minFreq;
if(settings.f_start < Device::Info().limits_minFreq) {
settings.f_start = Device::Info().limits_minFreq;
}
emit startFreqChanged(settings.f_start);
emit stopFreqChanged(settings.f_stop);

View File

@ -217,7 +217,11 @@ VNA::VNA(AppWindow *window)
points->setSingleStep(100);
points->setToolTip("Points/sweep");
connect(points, qOverload<int>(&QSpinBox::valueChanged), this, &VNA::SetPoints);
connect(this, &VNA::pointsChanged, points, &QSpinBox::setValue);
connect(this, &VNA::pointsChanged, [=](int p) {
points->blockSignals(true);
points->setValue(p);
points->blockSignals(false);
});
tb_acq->addWidget(new QLabel("Points:"));
tb_acq->addWidget(points);
@ -499,8 +503,8 @@ void VNA::SetSpan(double span)
void VNA::SetFullSpan()
{
settings.f_start = Device::Limits().minFreq;
settings.f_stop = Device::Limits().maxFreq;
settings.f_start = Device::Info().limits_minFreq;
settings.f_stop = Device::Info().limits_maxFreq;
ConstrainAndUpdateFrequencies();
}
@ -528,10 +532,10 @@ void VNA::SpanZoomOut()
void VNA::SetSourceLevel(double level)
{
if(level > Device::Limits().cdbm_max / 100.0) {
level = Device::Limits().cdbm_max / 100.0;
} else if(level < Device::Limits().cdbm_min / 100.0) {
level = Device::Limits().cdbm_min / 100.0;
if(level > Device::Info().limits_cdbm_max / 100.0) {
level = Device::Info().limits_cdbm_max / 100.0;
} else if(level < Device::Info().limits_cdbm_min / 100.0) {
level = Device::Info().limits_cdbm_min / 100.0;
}
emit sourceLevelChanged(level);
settings.cdbm_excitation = level * 100;
@ -540,10 +544,10 @@ void VNA::SetSourceLevel(double level)
void VNA::SetPoints(unsigned int points)
{
if (points < 2) {
if(points > Device::Info().limits_maxPoints) {
points = Device::Info().limits_maxPoints;
} else if (points < 2) {
points = 2;
} else if(points > Device::Limits().maxPoints) {
points = Device::Limits().maxPoints;
}
emit pointsChanged(points);
settings.points = points;
@ -552,10 +556,10 @@ void VNA::SetPoints(unsigned int points)
void VNA::SetIFBandwidth(double bandwidth)
{
if(bandwidth > Device::Limits().maxIFBW) {
bandwidth = Device::Limits().maxIFBW;
} else if(bandwidth < Device::Limits().minIFBW) {
bandwidth = Device::Limits().minIFBW;
if(bandwidth > Device::Info().limits_maxIFBW) {
bandwidth = Device::Info().limits_maxIFBW;
} else if(bandwidth < Device::Info().limits_minIFBW) {
bandwidth = Device::Info().limits_minIFBW;
}
settings.if_bandwidth = bandwidth;
emit IFBandwidthChanged(bandwidth);
@ -651,14 +655,14 @@ void VNA::StartCalibrationMeasurement(Calibration::Measurement m)
void VNA::ConstrainAndUpdateFrequencies()
{
if(settings.f_stop > Device::Limits().maxFreq) {
settings.f_stop = Device::Limits().maxFreq;
if(settings.f_stop > Device::Info().limits_maxFreq) {
settings.f_stop = Device::Info().limits_maxFreq;
}
if(settings.f_start > settings.f_stop) {
settings.f_start = settings.f_stop;
}
if(settings.f_start < Device::Limits().minFreq) {
settings.f_start = Device::Limits().minFreq;
if(settings.f_start < Device::Info().limits_minFreq) {
settings.f_start = Device::Info().limits_minFreq;
}
emit startFreqChanged(settings.f_start);
emit stopFreqChanged(settings.f_stop);

View File

@ -95,14 +95,7 @@ AppWindow::AppWindow(QWidget *parent)
connect(ui->actionDisconnect, &QAction::triggered, this, &AppWindow::DisconnectDevice);
connect(ui->actionQuit, &QAction::triggered, this, &AppWindow::close);
connect(ui->actionManual_Control, &QAction::triggered, this, &AppWindow::StartManualControl);
connect(ui->actionFirmware_Update, &QAction::triggered, [=](){
if(device) {
auto fw_update = new FirmwareUpdateDialog(device);
connect(fw_update, &FirmwareUpdateDialog::DeviceRebooting, this, &AppWindow::DisconnectDevice);
connect(fw_update, &FirmwareUpdateDialog::DeviceRebooted, this, &AppWindow::ConnectToDevice);
fw_update->exec();
}
});
connect(ui->actionFirmware_Update, &QAction::triggered, this, &AppWindow::StartFirmwareUpdateDialog);
connect(ui->actionPreferences, &QAction::triggered, [=](){
Preferences::getInstance().edit();
// settings might have changed, update necessary stuff
@ -172,6 +165,7 @@ void AppWindow::ConnectToDevice(QString serial)
connect(device, &Device::DeviceInfoUpdated, [this]() {
lDeviceInfo.setText(device->getLastDeviceInfoString());
});
connect(device, &Device::NeedsFirmwareUpdate, this, &AppWindow::DeviceNeedsUpdate);
ui->actionDisconnect->setEnabled(true);
ui->actionManual_Control->setEnabled(true);
ui->actionFirmware_Update->setEnabled(true);
@ -316,6 +310,28 @@ void AppWindow::UpdateReference()
device->SendPacket(p);
}
void AppWindow::StartFirmwareUpdateDialog()
{
if(device) {
auto fw_update = new FirmwareUpdateDialog(device);
connect(fw_update, &FirmwareUpdateDialog::DeviceRebooting, this, &AppWindow::DisconnectDevice);
connect(fw_update, &FirmwareUpdateDialog::DeviceRebooted, this, &AppWindow::ConnectToDevice);
fw_update->exec();
}
}
void AppWindow::DeviceNeedsUpdate(int reported, int expected)
{
auto ret = QMessageBox::warning(this, "Warning",
"The device reports are different protocol"
"version (" + QString::number(reported) + ") than expected (" + QString::number(expected) + ").\n"
"A firmware update is strongly suggested. Do you want to update now?",
QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) {
StartFirmwareUpdateDialog();
}
}
Device *AppWindow::getDevice() const
{
return device;

View File

@ -42,7 +42,8 @@ private slots:
int UpdateDeviceList();
void StartManualControl();
void UpdateReference();
void StartFirmwareUpdateDialog();
void DeviceNeedsUpdate(int reported, int expected);
private:
void DeviceConnectionLost();
void CreateToolbars();

View File

@ -67,6 +67,13 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
ui->StartupSARBW->setUnit("Hz");
ui->StartupSARBW->setPrefixes(" k");
// Acquisition page
ui->AcquisitionDFTlimitRBW->setUnit("Hz");
ui->AcquisitionDFTlimitRBW->setPrefixes(" k");
connect(ui->AcquisitionUseDFT, &QCheckBox::toggled, [=](bool enabled) {
ui->AcquisitionDFTlimitRBW->setEnabled(enabled);
});
// Page selection
connect(ui->treeWidget, &QTreeWidget::currentItemChanged, [=](QTreeWidgetItem *current, QTreeWidgetItem *) {
auto name = current->text(0);
@ -107,6 +114,8 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
p->Startup.SA.signalID = ui->StartupSASignalID->isChecked();
p->Acquisition.alwaysExciteBothPorts = ui->AcquisitionAlwaysExciteBoth->isChecked();
p->Acquisition.suppressPeaks = ui->AcquisitionSuppressPeaks->isChecked();
p->Acquisition.useDFTinSAmode = ui->AcquisitionUseDFT->isChecked();
p->Acquisition.RBWLimitForDFT = ui->AcquisitionDFTlimitRBW->value();
p->General.graphColors.background = ui->GeneralGraphBackground->getColor();
p->General.graphColors.axis = ui->GeneralGraphAxis->getColor();
p->General.graphColors.divisions = ui->GeneralGraphDivisions->getColor();
@ -147,6 +156,8 @@ void PreferencesDialog::setInitialGUIState()
ui->AcquisitionAlwaysExciteBoth->setChecked(p->Acquisition.alwaysExciteBothPorts);
ui->AcquisitionSuppressPeaks->setChecked(p->Acquisition.suppressPeaks);
ui->AcquisitionUseDFT->setChecked(p->Acquisition.useDFTinSAmode);
ui->AcquisitionDFTlimitRBW->setValue(p->Acquisition.RBWLimitForDFT);
ui->GeneralGraphBackground->setColor(p->General.graphColors.background);
ui->GeneralGraphAxis->setColor(p->General.graphColors.axis);

View File

@ -44,6 +44,8 @@ public:
struct {
bool alwaysExciteBothPorts;
bool suppressPeaks;
bool useDFTinSAmode;
double RBWLimitForDFT;
} Acquisition;
struct {
struct {
@ -60,7 +62,7 @@ private:
QString name;
QVariant def;
};
const std::array<SettingDescription, 22> descr = {{
const std::array<SettingDescription, 24> descr = {{
{&Startup.ConnectToFirstDevice, "Startup.ConnectToFirstDevice", true},
{&Startup.RememberSweepSettings, "Startup.RememberSweepSettings", false},
{&Startup.DefaultSweep.start, "Startup.DefaultSweep.start", 1000000.0},
@ -80,6 +82,8 @@ private:
{&Startup.SA.signalID, "Startup.SA.signalID", true},
{&Acquisition.alwaysExciteBothPorts, "Acquisition.alwaysExciteBothPorts", true},
{&Acquisition.suppressPeaks, "Acquisition.suppressPeaks", true},
{&Acquisition.useDFTinSAmode, "Acquisition.useDFTinSAmode", true},
{&Acquisition.RBWLimitForDFT, "Acquisition.RBWLimitForDFT", 3000.0},
{&General.graphColors.background, "General.graphColors.background", QColor(Qt::black)},
{&General.graphColors.axis, "General.graphColors.axis", QColor(Qt::white)},
{&General.graphColors.divisions, "General.graphColors.divisions", QColor(Qt::gray)},

View File

@ -73,7 +73,7 @@
</size>
</property>
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="Startup">
<layout class="QHBoxLayout" name="horizontalLayout_4">
@ -454,23 +454,58 @@
<widget class="QWidget" name="Acquisition">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QCheckBox" name="AcquisitionAlwaysExciteBoth">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If only S11/S21 or S22/S12 are enabled, faster sweeps are possible by only exciting one port. Checking this option forces the device to always excite both ports even when the measurements from one port will not be used.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Always perform full 2-port measurement</string>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Vector Network Analyzer</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QCheckBox" name="AcquisitionAlwaysExciteBoth">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If only S11/S21 or S22/S12 are enabled, faster sweeps are possible by only exciting one port. Checking this option forces the device to always excite both ports even when the measurements from one port will not be used.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Always perform full 2-port measurement</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="AcquisitionSuppressPeaks">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Due to limited fractional divider settings, the source and 1.LO PLLs are not able to reach every frequency exactly. At some specific frequencies this causes the final IF to shift. At these frequencies there will be a positive or negative peak in the trace measurement that is not actually there.&lt;br/&gt;&lt;br/&gt;Checking this option shifts the 2.LO for points where this could be an issue. This will remove the peaks but slows down the sweep slightly.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Suppress invalid peaks</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="AcquisitionSuppressPeaks">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Due to limited fractional divider settings, the source and 1.LO PLLs are not able to reach every frequency exactly. At some specific frequencies this causes the final IF to shift. At these frequencies there will be a positive or negative peak in the trace measurement that is not actually there.&lt;br/&gt;&lt;br/&gt;Checking this option shifts the 2.LO for points where this could be an issue. This will remove the peaks but slows down the sweep slightly.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Suppress invalid peaks</string>
<widget class="QGroupBox" name="groupBox_7">
<property name="title">
<string>Spectrum Analyzer</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="0">
<widget class="QCheckBox" name="AcquisitionUseDFT">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Normally, the spectrum analyzer mode tunes the LO for each point and measures the final IF only at one frequency. When this option is enabled, a DFT of the final IF is calculated instead which covers multiple frequencies with one measurement.&lt;/p&gt;&lt;p&gt;This can speed up the measurement at low RBWs significantly.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use DFT when RBW is below</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="SIUnitEdit" name="AcquisitionDFTlimitRBW"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>

View File

@ -189,6 +189,8 @@
<listOptionValue builtIn="false" value="FW_MAJOR=0"/>
<listOptionValue builtIn="false" value="FW_PATCH=0"/>
<listOptionValue builtIn="false" value="FW_MINOR=1"/>
<listOptionValue builtIn="false" value="USE_FULL_LL_DRIVER"/>

View File

@ -155,10 +155,10 @@ void App_Start() {
SA::Setup(recv_packet.spectrumSettings);
Communication::SendWithoutPayload(Protocol::PacketType::Ack);
break;
case Protocol::PacketType::RequestDeviceLimits:
case Protocol::PacketType::RequestDeviceInfo:
Protocol::PacketInfo p;
p.type = Protocol::PacketType::DeviceLimits;
p.limits = HW::Limits;
p.type = Protocol::PacketType::DeviceInfo;
HW::fillDeviceInfo(&p.info);
Communication::Send(p);
break;
#ifdef HAS_FLASH

View File

@ -238,35 +238,58 @@ static int16_t EncodeGeneratorSettings(Protocol::GeneratorSettings d, uint8_t *b
static Protocol::DeviceInfo DecodeDeviceInfo(uint8_t *buf) {
Protocol::DeviceInfo d;
Decoder e(buf);
e.get<uint16_t>(d.FW_major);
e.get<uint16_t>(d.FW_minor);
e.get<char>(d.HW_Revision);
e.get(d.ProtocolVersion);
e.get(d.FW_major);
e.get(d.FW_minor);
e.get(d.FW_patch);
e.get(d.HW_Revision);
d.extRefAvailable = e.getBits(1);
d.extRefInUse = e.getBits(1);
d.FPGA_configured = e.getBits(1);
d.source_locked = e.getBits(1);
d.LO1_locked = e.getBits(1);
d.ADC_overload = e.getBits(1);
e.get<uint8_t>(d.temperatures.source);
e.get<uint8_t>(d.temperatures.LO1);
e.get<uint8_t>(d.temperatures.MCU);
e.get(d.temp_source);
e.get(d.temp_LO1);
e.get(d.temp_MCU);
e.get(d.limits_minFreq);
e.get(d.limits_maxFreq);
e.get(d.limits_minIFBW);
e.get(d.limits_maxIFBW);
e.get(d.limits_maxPoints);
e.get(d.limits_cdbm_min);
e.get(d.limits_cdbm_max);
e.get(d.limits_minRBW);
e.get(d.limits_maxRBW);
return d;
}
static int16_t EncodeDeviceInfo(Protocol::DeviceInfo d, uint8_t *buf,
uint16_t bufSize) {
d.ProtocolVersion = Protocol::Version;
Encoder e(buf, bufSize);
e.add<uint16_t>(d.FW_major);
e.add<uint16_t>(d.FW_minor);
e.add<char>(d.HW_Revision);
e.add(d.ProtocolVersion);
e.add(d.FW_major);
e.add(d.FW_minor);
e.add(d.FW_patch);
e.add(d.HW_Revision);
e.addBits(d.extRefAvailable, 1);
e.addBits(d.extRefInUse, 1);
e.addBits(d.FPGA_configured, 1);
e.addBits(d.source_locked, 1);
e.addBits(d.LO1_locked, 1);
e.addBits(d.ADC_overload, 1);
e.add<uint8_t>(d.temperatures.source);
e.add<uint8_t>(d.temperatures.LO1);
e.add<uint8_t>(d.temperatures.MCU);
e.add(d.temp_source);
e.add(d.temp_LO1);
e.add(d.temp_MCU);
e.add(d.limits_minFreq);
e.add(d.limits_maxFreq);
e.add(d.limits_minIFBW);
e.add(d.limits_maxIFBW);
e.add(d.limits_maxPoints);
e.add(d.limits_cdbm_min);
e.add(d.limits_cdbm_max);
e.add(d.limits_minRBW);
e.add(d.limits_maxRBW);
return e.getSize();
}
@ -378,6 +401,7 @@ static Protocol::SpectrumAnalyzerSettings DecodeSpectrumAnalyzerSettings(uint8_t
d.WindowType = e.getBits(2);
d.SignalID = e.getBits(1);
d.Detector = e.getBits(3);
d.UseDFT = e.getBits(1);
return d;
}
static int16_t EncodeSpectrumAnalyzerSettings(Protocol::SpectrumAnalyzerSettings d, uint8_t *buf,
@ -390,6 +414,7 @@ static int16_t EncodeSpectrumAnalyzerSettings(Protocol::SpectrumAnalyzerSettings
e.addBits(d.WindowType, 2);
e.addBits(d.SignalID, 1);
e.addBits(d.Detector, 3);
e.addBits(d.UseDFT, 1);
return e.getSize();
}
@ -412,35 +437,6 @@ static int16_t EncodeSpectrumAnalyzerResult(Protocol::SpectrumAnalyzerResult d,
return e.getSize();
}
static Protocol::DeviceLimits DecodeDeviceLimits(uint8_t *buf) {
Protocol::DeviceLimits d;
Decoder e(buf);
e.get(d.minFreq);
e.get(d.maxFreq);
e.get(d.minIFBW);
e.get(d.maxIFBW);
e.get(d.maxPoints);
e.get(d.cdbm_min);
e.get(d.cdbm_max);
e.get(d.minRBW);
e.get(d.maxRBW);
return d;
}
static int16_t EncodeDeviceLimits(Protocol::DeviceLimits d, uint8_t *buf,
uint16_t bufSize) {
Encoder e(buf, bufSize);
e.add(d.minFreq);
e.add(d.maxFreq);
e.add(d.minIFBW);
e.add(d.maxIFBW);
e.add(d.maxPoints);
e.add(d.cdbm_min);
e.add(d.cdbm_max);
e.add(d.minRBW);
e.add(d.maxRBW);
return e.getSize();
}
static Protocol::FirmwarePacket DecodeFirmwarePacket(uint8_t *buf) {
Protocol::FirmwarePacket d;
// simple packet format, memcpy is faster than using the decoder
@ -545,14 +541,11 @@ uint16_t Protocol::DecodeBuffer(uint8_t *buf, uint16_t len, PacketInfo *info) {
case PacketType::SpectrumAnalyzerResult:
info->spectrumResult = DecodeSpectrumAnalyzerResult(&data[4]);
break;
case PacketType::DeviceLimits:
info->limits = DecodeDeviceLimits(&data[4]);
break;
case PacketType::Ack:
case PacketType::PerformFirmwareUpdate:
case PacketType::ClearFlash:
case PacketType::Nack:
case PacketType::RequestDeviceLimits:
case PacketType::RequestDeviceInfo:
// no payload, nothing to do
break;
case PacketType::None:
@ -595,14 +588,11 @@ uint16_t Protocol::EncodePacket(const PacketInfo &packet, uint8_t *dest, uint16_
case PacketType::SpectrumAnalyzerResult:
payload_size = EncodeSpectrumAnalyzerResult(packet.spectrumResult, &dest[4], destsize - 8);
break;
case PacketType::DeviceLimits:
payload_size = EncodeDeviceLimits(packet.limits, &dest[4], destsize - 8);
break;
case PacketType::Ack:
case PacketType::PerformFirmwareUpdate:
case PacketType::ClearFlash:
case PacketType::Nack:
case PacketType::RequestDeviceLimits:
case PacketType::RequestDeviceInfo:
// no payload, nothing to do
break;
case PacketType::None:

View File

@ -4,6 +4,8 @@
namespace Protocol {
static constexpr uint16_t Version = 1;
// When changing/adding/removing variables from these structs also adjust the decode/encode functions in Protocol.cpp
using Datapoint = struct _datapoint {
@ -39,8 +41,10 @@ using GeneratorSettings = struct _generatorSettings {
};
using DeviceInfo = struct _deviceInfo {
uint16_t FW_major;
uint16_t FW_minor;
uint16_t ProtocolVersion;
uint8_t FW_major;
uint8_t FW_minor;
uint8_t FW_patch;
char HW_Revision;
uint8_t extRefAvailable:1;
uint8_t extRefInUse:1;
@ -48,11 +52,18 @@ using DeviceInfo = struct _deviceInfo {
uint8_t source_locked:1;
uint8_t LO1_locked:1;
uint8_t ADC_overload:1;
struct {
uint8_t source;
uint8_t LO1;
uint8_t MCU;
} temperatures;
uint8_t temp_source;
uint8_t temp_LO1;
uint8_t temp_MCU;
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;
};
using ManualStatus = struct _manualstatus {
@ -107,6 +118,7 @@ using SpectrumAnalyzerSettings = struct _spectrumAnalyzerSettings {
uint8_t WindowType :2;
uint8_t SignalID :1;
uint8_t Detector :3;
uint8_t UseDFT :1;
};
using SpectrumAnalyzerResult = struct _spectrumAnalyzerResult {
@ -116,18 +128,6 @@ using SpectrumAnalyzerResult = struct _spectrumAnalyzerResult {
uint16_t pointNum;
};
using DeviceLimits = struct _deviceLimits {
uint64_t minFreq;
uint64_t maxFreq;
uint32_t minIFBW;
uint32_t maxIFBW;
uint16_t maxPoints;
int16_t cdbm_min;
int16_t cdbm_max;
uint32_t minRBW;
uint32_t maxRBW;
};
static constexpr uint16_t FirmwareChunkSize = 256;
using FirmwarePacket = struct _firmwarePacket {
uint32_t address;
@ -150,8 +150,7 @@ enum class PacketType : uint8_t {
Generator = 12,
SpectrumAnalyzerSettings = 13,
SpectrumAnalyzerResult = 14,
RequestDeviceLimits = 15,
DeviceLimits = 16,
RequestDeviceInfo = 15,
};
using PacketInfo = struct _packetinfo {
@ -167,7 +166,6 @@ using PacketInfo = struct _packetinfo {
ManualStatus status;
SpectrumAnalyzerSettings spectrumSettings;
SpectrumAnalyzerResult spectrumResult;
DeviceLimits limits;
};
};

View File

@ -7,6 +7,7 @@
#include "VNA.hpp"
#include "Manual.hpp"
#include "SpectrumAnalyzer.hpp"
#include <cstring>
#define LOG_LEVEL LOG_LEVEL_INFO
#define LOG_MODULE "HW"
@ -222,33 +223,39 @@ void HW::SetIdle() {
FPGA::Enable(FPGA::Periphery::PortSwitch, false);
}
void HW::fillDeviceInfo(Protocol::DeviceInfo *info) {
// read PLL temperatures
uint8_t tempSource, tempLO;
GetTemps(&tempSource, &tempLO);
LOG_INFO("PLL temperatures: %u/%u", tempSource, tempLO);
// Read ADC min/max
auto limits = FPGA::GetADCLimits();
LOG_INFO("ADC limits: P1: %d/%d P2: %d/%d R: %d/%d",
limits.P1min, limits.P1max, limits.P2min, limits.P2max,
limits.Rmin, limits.Rmax);
#define ADC_LIMIT 30000
if(limits.P1min < -ADC_LIMIT || limits.P1max > ADC_LIMIT
|| limits.P2min < -ADC_LIMIT || limits.P2max > ADC_LIMIT
|| limits.Rmin < -ADC_LIMIT || limits.Rmax > ADC_LIMIT) {
info->ADC_overload = true;
} else {
info->ADC_overload = false;
void HW::fillDeviceInfo(Protocol::DeviceInfo *info, bool updateEvenWhenBusy) {
// copy constant default values
memcpy(info, &HW::Info, sizeof(HW::Info));
if(activeMode == Mode::Idle || updateEvenWhenBusy) {
// updating values from FPGA allowed
// read PLL temperatures
uint8_t tempSource, tempLO;
GetTemps(&tempSource, &tempLO);
LOG_INFO("PLL temperatures: %u/%u", tempSource, tempLO);
// Read ADC min/max
auto limits = FPGA::GetADCLimits();
LOG_INFO("ADC limits: P1: %d/%d P2: %d/%d R: %d/%d",
limits.P1min, limits.P1max, limits.P2min, limits.P2max,
limits.Rmin, limits.Rmax);
#define ADC_LIMIT 30000
if(limits.P1min < -ADC_LIMIT || limits.P1max > ADC_LIMIT
|| limits.P2min < -ADC_LIMIT || limits.P2max > ADC_LIMIT
|| limits.Rmin < -ADC_LIMIT || limits.Rmax > ADC_LIMIT) {
info->ADC_overload = true;
} else {
info->ADC_overload = false;
}
auto status = FPGA::GetStatus();
info->LO1_locked = (status & (int) FPGA::Interrupt::LO1Unlock) ? 0 : 1;
info->source_locked = (status & (int) FPGA::Interrupt::SourceUnlock) ? 0 : 1;
info->extRefAvailable = Ref::available();
info->extRefInUse = extRefInUse;
info->temp_LO1 = tempLO;
info->temp_source = tempSource;
FPGA::ResetADCLimits();
}
auto status = FPGA::GetStatus();
info->LO1_locked = (status & (int) FPGA::Interrupt::LO1Unlock) ? 0 : 1;
info->source_locked = (status & (int) FPGA::Interrupt::SourceUnlock) ? 0 : 1;
info->extRefAvailable = Ref::available();
info->extRefInUse = extRefInUse;
info->temperatures.LO1 = tempLO;
info->temperatures.source = tempSource;
info->temperatures.MCU = STM::getTemperature();
FPGA::ResetADCLimits();
info->temp_MCU = STM::getTemperature();
}
bool HW::Ref::available() {

View File

@ -38,16 +38,30 @@ static_assert(ADCprescaler * ADCSamplerate == FPGA::Clockrate, "ADCSamplerate ca
static constexpr uint16_t DFTphaseInc = 4096 * IF2 / ADCSamplerate;
static_assert(DFTphaseInc * ADCSamplerate == 4096 * IF2, "DFT can not be computed for 2.IF");
static constexpr Protocol::DeviceLimits Limits = {
.minFreq = 0,
.maxFreq = 6000000000,
.minIFBW = ADCSamplerate / MaxSamples,
.maxIFBW = ADCSamplerate / MinSamples,
.maxPoints = FPGA::MaxPoints,
.cdbm_min = -4000,
.cdbm_max = 0,
.minRBW = (uint32_t) (ADCSamplerate * 2.23f / MaxSamples),
.maxRBW = (uint32_t) (ADCSamplerate * 2.23f / MinSamples),
static constexpr Protocol::DeviceInfo Info = {
.ProtocolVersion = Protocol::Version,
.FW_major = FW_MAJOR,
.FW_minor = FW_MINOR,
.FW_patch = FW_PATCH,
.HW_Revision = HW_REVISION,
.extRefAvailable = 0,
.extRefInUse = 0,
.FPGA_configured = 0,
.source_locked = 0,
.LO1_locked = 0,
.ADC_overload = 0,
.temp_source = 0,
.temp_LO1 = 0,
.temp_MCU = 0,
.limits_minFreq = 0,
.limits_maxFreq = 6000000000,
.limits_minIFBW = ADCSamplerate / MaxSamples,
.limits_maxIFBW = ADCSamplerate / MinSamples,
.limits_maxPoints = FPGA::MaxPoints,
.limits_cdbm_min = -4000,
.limits_cdbm_max = 0,
.limits_minRBW = (uint32_t) (ADCSamplerate * 2.23f / MaxSamples),
.limits_maxRBW = (uint32_t) (ADCSamplerate * 2.23f / MinSamples),
};
enum class Mode {
@ -63,7 +77,7 @@ void SetIdle();
void Work();
bool GetTemps(uint8_t *source, uint8_t *lo);
void fillDeviceInfo(Protocol::DeviceInfo *info);
void fillDeviceInfo(Protocol::DeviceInfo *info, bool updateEvenWhenBusy = false);
namespace Ref {
bool available();
// reference won't change until update is called

View File

@ -23,7 +23,6 @@ static Protocol::PacketInfo p;
static bool active = false;
static uint32_t lastLO2;
static uint32_t actualRBW;
static bool usingDFT;
static uint16_t DFTpoints;
static bool negativeDFT; // if true, a positive frequency shift at input results in a negative shift at the 2.IF. Handle DFT accordingly
@ -106,7 +105,7 @@ static void StartNextSample() {
Si5351.SetCLK(SiChannel::Port2LO2, LO2freq, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
lastLO2 = LO2freq;
}
if (usingDFT) {
if (s.UseDFT) {
uint32_t spacing = (s.f_stop - s.f_start) / (points - 1);
uint32_t start = HW::IF2;
if(negativeDFT) {
@ -166,11 +165,16 @@ void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) {
FPGA::Enable(FPGA::Periphery::Port1Mixer);
FPGA::Enable(FPGA::Periphery::Port2Mixer);
// automatically select DFT mode for lower RBWs
usingDFT = actualRBW <= 1000;
if (usingDFT) {
DFTpoints = FPGA::DFTbins; // use full DFT in FPGA
if (s.UseDFT) {
uint32_t spacing = (s.f_stop - s.f_start) / (points - 1);
// The DFT can only look at a small bandwidth otherwise the passband of the final ADC filter is visible in the data
// Limit to about 30kHz
uint32_t maxDFTpoints = 30000 / spacing;
// Limit to actual supported number of bins
if(maxDFTpoints > FPGA::DFTbins) {
maxDFTpoints = FPGA::DFTbins;
}
DFTpoints = maxDFTpoints;
FPGA::DisableInterrupt(FPGA::Interrupt::NewData);
} else {
DFTpoints = 1; // can only measure one point at a time
@ -191,7 +195,7 @@ bool SA::MeasurementDone(const FPGA::SamplingResult &result) {
for(uint16_t i=0;i<DFTpoints;i++) {
float port1, port2;
if (usingDFT) {
if (s.UseDFT) {
// use DFT result
auto dft = FPGA::ReadDFTResult();
port1 = dft.P1;
@ -217,7 +221,7 @@ bool SA::MeasurementDone(const FPGA::SamplingResult &result) {
}
}
if (usingDFT) {
if (s.UseDFT) {
FPGA::StopDFT();
// will be started again in StartNextSample
}

View File

@ -264,9 +264,8 @@ void VNA::Work() {
packet.info.FW_major = FW_MAJOR;
packet.info.FW_minor = FW_MINOR;
packet.info.HW_Revision = HW_REVISION;
HW::fillDeviceInfo(&packet.info);
HW::fillDeviceInfo(&packet.info, true);
Communication::Send(packet);
FPGA::ResetADCLimits();
// Start next sweep
FPGA::StartSweep();
}

View File

@ -101,6 +101,7 @@ MCU = $(CPU) -mthumb $(FLOAT-ABI) $(FPU)
C_DEFS = \
-DFW_MAJOR=0 \
-DFW_MINOR=1 \
-DFW_PATCH=0 \
-DUSE_FULL_LL_DRIVER \
-DHW_REVISION="'B'" \
-D__weak="__attribute__((weak))" \