diff --git a/Software/PC_Application/Application.pro b/Software/PC_Application/Application.pro
index 763208d..c011c19 100644
--- a/Software/PC_Application/Application.pro
+++ b/Software/PC_Application/Application.pro
@@ -7,6 +7,7 @@ HEADERS += \
Calibration/calkitdialog.h \
Calibration/json.hpp \
Calibration/measurementmodel.h \
+ Calibration/receivercaldialog.h \
Calibration/sourcecaldialog.h \
CustomWidgets/colorpickerbutton.h \
CustomWidgets/informationbox.h \
@@ -56,6 +57,7 @@ SOURCES += \
Calibration/calkit.cpp \
Calibration/calkitdialog.cpp \
Calibration/measurementmodel.cpp \
+ Calibration/receivercaldialog.cpp \
Calibration/sourcecaldialog.cpp \
CustomWidgets/colorpickerbutton.cpp \
CustomWidgets/informationbox.cpp \
@@ -106,6 +108,7 @@ win32:LIBS += -LC:\Qwt-6.1.4\lib -lqwt
QT += widgets
FORMS += \
+ Calibration/addamplitudepointsdialog.ui \
Calibration/amplitudecaldialog.ui \
Calibration/calibrationtracedialog.ui \
Calibration/calkitdialog.ui \
diff --git a/Software/PC_Application/Calibration/addamplitudepointsdialog.ui b/Software/PC_Application/Calibration/addamplitudepointsdialog.ui
new file mode 100644
index 0000000..67b6521
--- /dev/null
+++ b/Software/PC_Application/Calibration/addamplitudepointsdialog.ui
@@ -0,0 +1,138 @@
+
+
+ AddAmplitudePointsDialog
+
+
+
+ 0
+ 0
+ 564
+ 139
+
+
+
+ Add points
+
+
+ true
+
+
+ -
+
+
-
+
+
+ Single Point at
+
+
+ groupType
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Array of
+
+
+ groupType
+
+
+
+ -
+
+
+ 1
+
+
+ 64
+
+
+ 10
+
+
+
+ -
+
+
+ points from
+
+
+
+ -
+
+
+ -
+
+
+ to
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ Delete already existing points
+
+
+
+ -
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+ SIUnitEdit
+ QLineEdit
+ CustomWidgets/siunitedit.h
+
+
+
+
+
+
+
+
diff --git a/Software/PC_Application/Calibration/amplitudecaldialog.cpp b/Software/PC_Application/Calibration/amplitudecaldialog.cpp
index 3c0119e..6cd4182 100644
--- a/Software/PC_Application/Calibration/amplitudecaldialog.cpp
+++ b/Software/PC_Application/Calibration/amplitudecaldialog.cpp
@@ -3,12 +3,17 @@
#include "mode.h"
#include "unit.h"
#include
+#include "ui_addamplitudepointsdialog.h"
+#include
+
+using namespace std;
AmplitudeCalDialog::AmplitudeCalDialog(Device *dev, QWidget *parent) :
QDialog(parent),
ui(new Ui::AmplitudeCalDialog),
dev(dev),
- model(this)
+ model(this),
+ mode(CalibrationMode::BothPorts)
{
activeMode = Mode::getActiveMode();
activeMode->deactivate();
@@ -20,9 +25,40 @@ AmplitudeCalDialog::AmplitudeCalDialog(Device *dev, QWidget *parent) :
ui->view->setColumnWidth(AmplitudeModel::ColIndexPort1, 150);
ui->view->setColumnWidth(AmplitudeModel::ColIndexPort2, 150);
connect(dev, &Device::AmplitudeCorrectionPointReceived, this, &AmplitudeCalDialog::ReceivedPoint);
- connect(ui->load, &QPushButton::clicked, this, &AmplitudeCalDialog::LoadFromDevice);
- connect(ui->add, &QPushButton::clicked, this, &AmplitudeCalDialog::AddPoint);
- connect(ui->remove, &QPushButton::clicked, this, &AmplitudeCalDialog::RemovePoint);
+ connect(ui->load, &QPushButton::clicked, [=](){
+ if(ConfirmActionIfEdited()) {
+ LoadFromDevice();
+ }
+ });
+ connect(ui->save, &QPushButton::clicked, this, &AmplitudeCalDialog::SaveToDevice);
+ connect(ui->add, &QPushButton::clicked, this, &AmplitudeCalDialog::AddPointDialog);
+ connect(ui->remove, &QPushButton::clicked, [=](){
+ unsigned int row = ui->view->currentIndex().row();
+ if(row < points.size()) {
+ RemovePoint(row);
+ }
+ });
+ auto selectionModel = ui->view->selectionModel();
+ connect(selectionModel, &QItemSelectionModel::currentChanged, [=](const QModelIndex ¤t, const QModelIndex&) {
+ if(current.isValid() && (current.column() == model.ColIndexPort1 || current.column() == model.ColIndexPort2)) {
+ SelectedPoint(points[current.row()].frequency, current.column() == model.ColIndexPort2);
+ } else {
+ // Invalid selection
+ SelectedPoint(0, false);
+ }
+ });
+ connect(ui->modeBoth, &QPushButton::pressed, [=](){
+ mode = CalibrationMode::BothPorts;
+ model.dataChanged(model.index(0, model.ColIndexPort1), model.index(points.size(), model.ColIndexPort2));
+ });
+ connect(ui->modePort1, &QPushButton::pressed, [=](){
+ mode = CalibrationMode::OnlyPort1;
+ model.dataChanged(model.index(0, model.ColIndexPort1), model.index(points.size(), model.ColIndexPort2));
+ });
+ connect(ui->modePort2, &QPushButton::pressed, [=](){
+ mode = CalibrationMode::OnlyPort2;
+ model.dataChanged(model.index(0, model.ColIndexPort1), model.index(points.size(), model.ColIndexPort2));
+ });
}
AmplitudeCalDialog::~AmplitudeCalDialog()
@@ -33,8 +69,9 @@ AmplitudeCalDialog::~AmplitudeCalDialog()
void AmplitudeCalDialog::reject()
{
- // TODO check for unsaved data
- delete this;
+ if(ConfirmActionIfEdited()) {
+ delete this;
+ }
}
std::vector AmplitudeCalDialog::getPoints() const
@@ -49,9 +86,24 @@ void AmplitudeCalDialog::setAmplitude(double amplitude, unsigned int point, bool
}
if(port2) {
points[point].amplitudePort2 = amplitude;
+ points[point].port2set = true;
} else {
points[point].amplitudePort1 = amplitude;
+ points[point].port1set = true;
}
+ edited = true;
+ AmplitudeChanged(points[point], port2);
+ if(mode == CalibrationMode::OnlyPort1 && !port2) {
+ // apply result from port 1 to port 2 as well
+ points[point].correctionPort2 = points[point].correctionPort1;
+ points[point].port2set = points[point].port1set;
+ } else if(mode == CalibrationMode::OnlyPort2 && port2) {
+ // apply result from port 2 to port 1 as well
+ points[point].correctionPort1 = points[point].correctionPort2;
+ points[point].port1set = points[point].port2set;
+ }
+ model.dataChanged(model.index(point, model.ColIndexCorrectionFactors), model.index(point, model.ColIndexPort2));
+ UpdateSaveButton();
}
void AmplitudeCalDialog::ReceivedPoint(Protocol::AmplitudeCorrectionPoint p)
@@ -61,7 +113,10 @@ void AmplitudeCalDialog::ReceivedPoint(Protocol::AmplitudeCorrectionPoint p)
c.frequency = p.freq * 10.0;
c.correctionPort1 = p.port1;
c.correctionPort2 = p.port2;
+ c.port1set = false;
+ c.port2set = false;
model.beginInsertRows(QModelIndex(), points.size(), points.size());
+ UpdateAmplitude(c);
points.push_back(c);
model.endInsertRows();
emit pointsUpdated();
@@ -69,15 +124,17 @@ void AmplitudeCalDialog::ReceivedPoint(Protocol::AmplitudeCorrectionPoint p)
void AmplitudeCalDialog::LoadFromDevice()
{
- model.beginResetModel();
- points.clear();
- model.endResetModel();
+ dev->SetIdle();
+ RemoveAllPoints();
qDebug() << "Asking for amplitude calibration";
dev->SendCommandWithoutPayload(requestCommand());
+ edited = false;
+ ui->save->setEnabled(false);
}
void AmplitudeCalDialog::SaveToDevice()
{
+ dev->SetIdle();
for(unsigned int i=0;iSendPacket(info);
}
+ edited = false;
+ ui->save->setEnabled(false);
}
-void AmplitudeCalDialog::RemovePoint()
+void AmplitudeCalDialog::RemovePoint(unsigned int i)
{
- unsigned int row = ui->view->currentIndex().row();
- if(row < points.size()) {
- model.beginRemoveRows(QModelIndex(), row, row);
- points.erase(points.begin() + row);
- model.endInsertRows();
+ model.beginRemoveRows(QModelIndex(), i, i);
+ points.erase(points.begin() + i);
+ model.endRemoveRows();
+ edited = true;
+ UpdateSaveButton();
+}
+
+void AmplitudeCalDialog::RemoveAllPoints()
+{
+ model.beginResetModel();
+ points.clear();
+ model.endResetModel();
+ edited = true;
+ ui->save->setEnabled(false);
+ UpdateSaveButton();
+}
+
+void AmplitudeCalDialog::AddPoint(double frequency)
+{
+ if(points.size() >= Device::Info().limits_maxAmplitudePoints) {
+ qWarning() << "Unable to add amplitude point, already maximum limit (" << Device::Info().limits_maxAmplitudePoints << ")";
+ return;
+ }
+ // find position at which this frequency gets inserted
+ auto index = upper_bound(points.begin(), points.end(), frequency, [](double value, const CorrectionPoint& p){
+ return value < p.frequency;
+ });
+ model.beginInsertRows(QModelIndex(), index - points.begin(), index - points.begin());
+ CorrectionPoint newPoint;
+ newPoint.frequency = frequency;
+ newPoint.correctionPort1 = 0;
+ newPoint.correctionPort2 = 0;
+ newPoint.port1set = false;
+ newPoint.port2set = false;
+ emit newPointCreated(newPoint);
+ points.insert(index, newPoint);
+ model.endInsertRows();
+ edited = true;
+ UpdateSaveButton();
+}
+
+void AmplitudeCalDialog::AddPointDialog()
+{
+ auto d = new QDialog();
+ auto ui = new Ui::AddAmplitudePointsDialog();
+ ui->setupUi(d);
+ ui->frequency->setUnit("Hz");
+ ui->frequency->setPrefixes(" kMG");
+ ui->startFreq->setUnit("Hz");
+ ui->startFreq->setPrefixes(" kMG");
+ ui->stopFreq->setUnit("Hz");
+ ui->stopFreq->setPrefixes(" kMG");
+ ui->frequency->setValue(1000000000.0);
+ ui->startFreq->setValue(Device::Info().limits_minFreq);
+ ui->stopFreq->setValue(Device::Info().limits_maxFreq);
+ connect(ui->singlePoint, &QRadioButton::toggled, [=](bool single) {
+ ui->stopFreq->setEnabled(!single);
+ ui->startFreq->setEnabled(!single);
+ ui->numPoints->setEnabled(!single);
+ ui->frequency->setEnabled(single);
+ });
+ ui->singlePoint->setChecked(true);
+ ui->frequency->setFocus();
+
+ connect(ui->buttonBox, &QDialogButtonBox::accepted, [=](){
+ // okay clicked, apply selected action
+ if(ui->deleteExisting->isChecked()) {
+ RemoveAllPoints();
+ }
+ if(ui->singlePoint->isChecked()) {
+ AddPoint(ui->frequency->value());
+ } else {
+ double freq_start = ui->startFreq->value();
+ double freq_stop = ui->stopFreq->value();
+ unsigned int points = ui->numPoints->value();
+ double freq_step = (freq_stop - freq_start) / (points - 1);
+ for(unsigned int i=0;ibuttonBox, &QDialogButtonBox::rejected);
+ connect(ui->buttonBox, &QDialogButtonBox::rejected, [=](){
+ // aborted, nothing to do
+ delete d;
+ });
+ d->show();
+}
+
+bool AmplitudeCalDialog::ConfirmActionIfEdited()
+{
+ if(edited) {
+ auto reply = QMessageBox::question(this, "Confirm action", "Some points have been edited but not saved in the device yet. If you continue, all changes will be lost. Do you want to continue?",
+ QMessageBox::Yes|QMessageBox::No);
+ return reply == QMessageBox::Yes;
+ } else {
+ // not edited yet, nothing to confirm
+ return true;
}
}
-void AmplitudeCalDialog::AddPoint()
+void AmplitudeCalDialog::UpdateSaveButton()
{
+ bool enable = true;
+ if(points.size() == 0) {
+ // needs at least one point
+ enable = false;
+ }
+ for(auto p : points) {
+ if(!p.port1set || !p.port2set) {
+ // some points are not set yet
+ enable = false;
+ break;
+ }
+ }
+ ui->save->setEnabled(enable);
+}
+AmplitudeCalDialog::CalibrationMode AmplitudeCalDialog::getMode() const
+{
+ return mode;
}
AmplitudeModel::AmplitudeModel(AmplitudeCalDialog *c) :
@@ -134,10 +304,18 @@ QVariant AmplitudeModel::data(const QModelIndex &index, int role) const
return QString::number(p.correctionPort1) + ", " + QString::number(p.correctionPort2);
break;
case ColIndexPort1:
- return Unit::ToString(p.amplitudePort1, "dbm", " ", 4);
+ if (p.port1set) {
+ return Unit::ToString(p.amplitudePort1, "dbm", " ", 4);
+ } else {
+ return "No data";
+ }
break;
case ColIndexPort2:
- return Unit::ToString(p.amplitudePort2, "dbm", " ", 4);
+ if (p.port2set) {
+ return Unit::ToString(p.amplitudePort2, "dbm", " ", 4);
+ } else {
+ return "No data";
+ }
break;
}
}
@@ -180,8 +358,16 @@ Qt::ItemFlags AmplitudeModel::flags(const QModelIndex &index) const
{
int flags = Qt::NoItemFlags;
switch(index.column()) {
- case ColIndexPort1: flags |= Qt::ItemIsEnabled | Qt::ItemIsEditable; break;
- case ColIndexPort2: flags |= Qt::ItemIsEnabled | Qt::ItemIsEditable; break;
+ case ColIndexPort1:
+ if(c->getMode() != AmplitudeCalDialog::CalibrationMode::OnlyPort2) {
+ flags |= Qt::ItemIsEnabled | Qt::ItemIsEditable;
+ }
+ break;
+ case ColIndexPort2:
+ if(c->getMode() != AmplitudeCalDialog::CalibrationMode::OnlyPort1) {
+ flags |= Qt::ItemIsEnabled | Qt::ItemIsEditable;
+ }
+ break;
}
return (Qt::ItemFlags) flags;
}
diff --git a/Software/PC_Application/Calibration/amplitudecaldialog.h b/Software/PC_Application/Calibration/amplitudecaldialog.h
index 9736fa3..d28d444 100644
--- a/Software/PC_Application/Calibration/amplitudecaldialog.h
+++ b/Software/PC_Application/Calibration/amplitudecaldialog.h
@@ -1,4 +1,4 @@
-#ifndef AMPLITUDECALDIALOG_H
+#ifndef AMPLITUDECALDIALOG_H
#define AMPLITUDECALDIALOG_H
#include
@@ -48,30 +48,53 @@ public:
class CorrectionPoint {
public:
double frequency;
- double correctionPort1;
- double correctionPort2;
+ int16_t correctionPort1;
+ int16_t correctionPort2;
double amplitudePort1;
double amplitudePort2;
+ bool port1set;
+ bool port2set;
};
std::vector getPoints() const;
void setAmplitude(double amplitude, unsigned int point, bool port2);
+ enum class CalibrationMode {
+ BothPorts,
+ OnlyPort1,
+ OnlyPort2,
+ };
+
+ CalibrationMode getMode() const;
+
protected slots:
void ReceivedPoint(Protocol::AmplitudeCorrectionPoint p);
void LoadFromDevice();
void SaveToDevice();
- void RemovePoint();
- void AddPoint();
+ void RemovePoint(unsigned int i);
+ void RemoveAllPoints();
+ void AddPoint(double frequency);
+ void AddPointDialog();
signals:
void pointsUpdated();
+ void newPointCreated(CorrectionPoint& p);
protected:
+ bool ConfirmActionIfEdited();
+ void UpdateSaveButton();
virtual Protocol::PacketType requestCommand() = 0;
virtual Protocol::PacketType pointType() = 0;
+ // will get called whenever a new cell is selected (frequency=0 means invalid selection)
+ virtual void SelectedPoint(double frequency, bool port2) = 0;
+ // will get called whenever the amplitude is changed. Derived class is responsible for updating correction factor
+ virtual void AmplitudeChanged(CorrectionPoint& point, bool port2) = 0;
+ // called whenver the correction factor have been retrieved from the device and the amplitudes need to be updated
+ virtual void UpdateAmplitude(CorrectionPoint& point) = 0;
std::vector points;
Ui::AmplitudeCalDialog *ui;
Device *dev;
Mode *activeMode;
AmplitudeModel model;
+ bool edited;
+ CalibrationMode mode;
};
#endif // SOURCECALDIALOG_H
diff --git a/Software/PC_Application/Calibration/amplitudecaldialog.ui b/Software/PC_Application/Calibration/amplitudecaldialog.ui
index 53a1e4b..baf0099 100644
--- a/Software/PC_Application/Calibration/amplitudecaldialog.ui
+++ b/Software/PC_Application/Calibration/amplitudecaldialog.ui
@@ -28,11 +28,11 @@
-
-
+
-
- Add Point
+ Add Point(s)
@@ -57,7 +57,8 @@
Help
-
+
+ ..
@@ -74,6 +75,9 @@
-
+
+ false
+
Save to Device
@@ -83,6 +87,101 @@
+ -
+
+
+ Calibration Mode
+
+
+
-
+
+
+ Both Ports
+
+
+ true
+
+
+ groupMode
+
+
+
+ -
+
+
+
+ 8
+
+
+
+ Best accuracy
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Only Port 1
+
+
+ groupMode
+
+
+
+ -
+
+
+
+ 8
+
+
+
+ Use same correction for port 2
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Only Port 2
+
+
+ groupMode
+
+
+
+ -
+
+
+
+ 8
+
+
+
+ Use same correction for port 1
+
+
+
+
+
+
-
@@ -104,4 +203,7 @@
+
+
+
diff --git a/Software/PC_Application/Calibration/receivercaldialog.cpp b/Software/PC_Application/Calibration/receivercaldialog.cpp
new file mode 100644
index 0000000..fff53d3
--- /dev/null
+++ b/Software/PC_Application/Calibration/receivercaldialog.cpp
@@ -0,0 +1,58 @@
+#include "receivercaldialog.h"
+
+ReceiverCalDialog::ReceiverCalDialog(Device *dev)
+ : AmplitudeCalDialog(dev)
+{
+ setWindowTitle("Receiver Calibration Dialog");
+ LoadFromDevice();
+ connect(dev, &Device::SpectrumResultReceived, [=](Protocol::SpectrumAnalyzerResult res) {
+ if(res.pointNum == 1) {
+ // store result in center of sweep of 3 points
+ port1_result = 20*log10(res.port1);
+ port2_result = 20*log10(res.port2);
+ }
+ });
+}
+
+void ReceiverCalDialog::SelectedPoint(double frequency, bool port2)
+{
+ if(frequency > 0) {
+ Protocol::PacketInfo p;
+ p.type = Protocol::PacketType::SpectrumAnalyzerSettings;
+ p.spectrumSettings.RBW = 10000;
+ p.spectrumSettings.UseDFT = 0;
+ // setup 3 points centered around the measurement frequency (zero span not supported yet)
+ p.spectrumSettings.f_stop = frequency + 1.0;
+ p.spectrumSettings.f_start = frequency - 1.0;
+ p.spectrumSettings.pointNum = 3;
+ p.spectrumSettings.Detector = 0;
+ p.spectrumSettings.SignalID = 1;
+ p.spectrumSettings.WindowType = 3;
+ p.spectrumSettings.applyReceiverCorrection = 0;
+ dev->SendPacket(p);
+ } else {
+ // invalid frequency, disable
+ dev->SetIdle();
+ }
+}
+
+void ReceiverCalDialog::AmplitudeChanged(AmplitudeCalDialog::CorrectionPoint &point, bool port2)
+{
+ auto *factor = port2 ? &point.correctionPort2 : &point.correctionPort1;
+ const auto *amplitude = port2 ? &point.amplitudePort2 : &point.amplitudePort1;
+ const auto *measured = port2 ? &port2_result : &port1_result;
+ // calculate correction factor by comparing expected with measured amplitude
+ *factor = (*measured - *amplitude) * 100.0;
+}
+
+void ReceiverCalDialog::UpdateAmplitude(AmplitudeCalDialog::CorrectionPoint &point)
+{
+ // This point was just received from the device, it is not possible to know the actual amplitude because the
+ // applied power level during the calibration is not saved (only the correction value). This is not a problem
+ // because the correction value is still valid but the missing values look weird in the GUI
+ // TODO change this?
+ point.amplitudePort1 = std::numeric_limits::quiet_NaN();
+ point.amplitudePort2 = std::numeric_limits::quiet_NaN();
+ point.port1set = true;
+ point.port2set = true;
+}
diff --git a/Software/PC_Application/Calibration/receivercaldialog.h b/Software/PC_Application/Calibration/receivercaldialog.h
new file mode 100644
index 0000000..b10ffbc
--- /dev/null
+++ b/Software/PC_Application/Calibration/receivercaldialog.h
@@ -0,0 +1,22 @@
+#ifndef RECEIVERCALDIALOG_H
+#define RECEIVERCALDIALOG_H
+
+#include "amplitudecaldialog.h"
+
+class ReceiverCalDialog : public AmplitudeCalDialog
+{
+ Q_OBJECT
+public:
+ ReceiverCalDialog(Device *dev);
+protected:
+ Protocol::PacketType requestCommand() override { return Protocol::PacketType::RequestReceiverCal; }
+ Protocol::PacketType pointType() override { return Protocol::PacketType::ReceiverCalPoint; }
+ void SelectedPoint(double frequency, bool port2) override;
+ void AmplitudeChanged(CorrectionPoint &point, bool port2) override;
+ void UpdateAmplitude(CorrectionPoint& point) override;
+private:
+ static constexpr double excitationAmplitude = -20.0;
+ double port1_result, port2_result; // raw (uncorrected) measurements from device
+};
+
+#endif // RECEIVERCALDIALOG_H
diff --git a/Software/PC_Application/Calibration/sourcecaldialog.cpp b/Software/PC_Application/Calibration/sourcecaldialog.cpp
index 1247bfd..9d047b1 100644
--- a/Software/PC_Application/Calibration/sourcecaldialog.cpp
+++ b/Software/PC_Application/Calibration/sourcecaldialog.cpp
@@ -1,8 +1,47 @@
#include "sourcecaldialog.h"
+#include
+
SourceCalDialog::SourceCalDialog(Device *dev)
: AmplitudeCalDialog(dev)
{
setWindowTitle("Source Calibration Dialog");
LoadFromDevice();
}
+
+void SourceCalDialog::SelectedPoint(double frequency, bool port2)
+{
+ Protocol::PacketInfo p;
+ p.type = Protocol::PacketType::Generator;
+
+ p.generator.frequency = frequency;
+ p.generator.cdbm_level = excitationAmplitude * 100.0;
+ if(frequency > 0) {
+ if(port2) {
+ p.generator.activePort = 2;
+ } else {
+ p.generator.activePort = 1;
+ }
+ } else {
+ // invalid frequency, disable both ports
+ p.generator.activePort = 0;
+ }
+ p.generator.applyAmplitudeCorrection = 0;
+ dev->SendPacket(p);
+}
+
+void SourceCalDialog::AmplitudeChanged(AmplitudeCalDialog::CorrectionPoint &point, bool port2)
+{
+ auto *factor = port2 ? &point.correctionPort2 : &point.correctionPort1;
+ const auto *amplitude = port2 ? &point.amplitudePort2 : &point.amplitudePort1;
+ // calculate correction factor by comparing expected with measured amplitude
+ *factor = (excitationAmplitude - *amplitude) * 100.0;
+}
+
+void SourceCalDialog::UpdateAmplitude(AmplitudeCalDialog::CorrectionPoint &point)
+{
+ point.amplitudePort1 = excitationAmplitude - (double) point.correctionPort1 / 100.0;
+ point.amplitudePort2 = excitationAmplitude - (double) point.correctionPort2 / 100.0;
+ point.port1set = true;
+ point.port2set = true;
+}
diff --git a/Software/PC_Application/Calibration/sourcecaldialog.h b/Software/PC_Application/Calibration/sourcecaldialog.h
index cc6813f..15de04d 100644
--- a/Software/PC_Application/Calibration/sourcecaldialog.h
+++ b/Software/PC_Application/Calibration/sourcecaldialog.h
@@ -12,6 +12,11 @@ public:
protected:
Protocol::PacketType requestCommand() override { return Protocol::PacketType::RequestSourceCal; }
Protocol::PacketType pointType() override { return Protocol::PacketType::SourceCalPoint; }
+ void SelectedPoint(double frequency, bool port2) override;
+ void AmplitudeChanged(CorrectionPoint &point, bool port2) override;
+ void UpdateAmplitude(CorrectionPoint& point) override;
+private:
+ static constexpr double excitationAmplitude = -20.0;
};
#endif // SOURCECALDIALOG_H
diff --git a/Software/PC_Application/Device/device.cpp b/Software/PC_Application/Device/device.cpp
index 5e63852..c5fde83 100644
--- a/Software/PC_Application/Device/device.cpp
+++ b/Software/PC_Application/Device/device.cpp
@@ -136,6 +136,7 @@ static constexpr Protocol::DeviceInfo defaultInfo = {
.limits_cdbm_max = 1000,
.limits_minRBW = 1,
.limits_maxRBW = 1000000,
+ .limits_maxAmplitudePoints = 255,
};
Protocol::DeviceInfo Device::lastInfo = defaultInfo;
diff --git a/Software/PC_Application/Generator/signalgenwidget.cpp b/Software/PC_Application/Generator/signalgenwidget.cpp
index 68735eb..603bf86 100644
--- a/Software/PC_Application/Generator/signalgenwidget.cpp
+++ b/Software/PC_Application/Generator/signalgenwidget.cpp
@@ -53,6 +53,7 @@ Protocol::GeneratorSettings SignalgeneratorWidget::getDeviceStatus()
} else {
s.activePort = 0;
}
+ s.applyAmplitudeCorrection = 1;
return s;
}
diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp
index a20ab65..cc5ed83 100644
--- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp
+++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp
@@ -229,9 +229,6 @@ using namespace std;
void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d)
{
- // TODO level adjustment in device
- d.port1 /= 126500000.0;
- d.port2 /= 126500000.0;
d = average.process(d);
traceModel.addSAData(d, settings);
emit dataChanged();
@@ -248,6 +245,7 @@ void SpectrumAnalyzer::SettingsChanged()
} else {
settings.pointNum = settings.f_stop - settings.f_start + 1;
}
+ settings.applyReceiverCorrection = 1;
auto pref = Preferences::getInstance();
if(pref.Acquisition.useDFTinSAmode && settings.RBW <= pref.Acquisition.RBWLimitForDFT) {
diff --git a/Software/PC_Application/appwindow.cpp b/Software/PC_Application/appwindow.cpp
index a007bbb..ec39160 100644
--- a/Software/PC_Application/appwindow.cpp
+++ b/Software/PC_Application/appwindow.cpp
@@ -45,6 +45,7 @@
#include "Generator/generator.h"
#include "SpectrumAnalyzer/spectrumanalyzer.h"
#include "Calibration/sourcecaldialog.h"
+#include "Calibration/receivercaldialog.h"
using namespace std;
@@ -102,6 +103,7 @@ AppWindow::AppWindow(QWidget *parent)
connect(ui->actionManual_Control, &QAction::triggered, this, &AppWindow::StartManualControl);
connect(ui->actionFirmware_Update, &QAction::triggered, this, &AppWindow::StartFirmwareUpdateDialog);
connect(ui->actionSource_Calibration, &QAction::triggered, this, &AppWindow::SourceCalibrationDialog);
+ connect(ui->actionReceiver_Calibration, &QAction::triggered, this, &AppWindow::ReceiverCalibrationDialog);
connect(ui->actionPreferences, &QAction::triggered, [=](){
Preferences::getInstance().edit();
// settings might have changed, update necessary stuff
@@ -187,6 +189,7 @@ void AppWindow::ConnectToDevice(QString serial)
ui->actionManual_Control->setEnabled(true);
ui->actionFirmware_Update->setEnabled(true);
ui->actionSource_Calibration->setEnabled(true);
+ ui->actionReceiver_Calibration->setEnabled(true);
Mode::getActiveMode()->initializeDevice();
UpdateReference();
@@ -214,6 +217,7 @@ void AppWindow::DisconnectDevice()
ui->actionManual_Control->setEnabled(false);
ui->actionFirmware_Update->setEnabled(false);
ui->actionSource_Calibration->setEnabled(false);
+ ui->actionReceiver_Calibration->setEnabled(false);
for(auto a : deviceActionGroup->actions()) {
a->setChecked(false);
}
@@ -362,6 +366,12 @@ void AppWindow::SourceCalibrationDialog()
d->exec();
}
+void AppWindow::ReceiverCalibrationDialog()
+{
+ auto d = new ReceiverCalDialog(device);
+ d->exec();
+}
+
Device *AppWindow::getDevice() const
{
return device;
diff --git a/Software/PC_Application/appwindow.h b/Software/PC_Application/appwindow.h
index 1d32ee4..d6b9ef3 100644
--- a/Software/PC_Application/appwindow.h
+++ b/Software/PC_Application/appwindow.h
@@ -45,6 +45,7 @@ private slots:
void StartFirmwareUpdateDialog();
void DeviceNeedsUpdate(int reported, int expected);
void SourceCalibrationDialog();
+ void ReceiverCalibrationDialog();
private:
void DeviceConnectionLost();
void CreateToolbars();
diff --git a/Software/PC_Application/main.ui b/Software/PC_Application/main.ui
index 2f50b04..91de82d 100644
--- a/Software/PC_Application/main.ui
+++ b/Software/PC_Application/main.ui
@@ -50,6 +50,7 @@
+
diff --git a/Software/VNA_embedded/Application/AmplitudeCal.cpp b/Software/VNA_embedded/Application/AmplitudeCal.cpp
index a181805..c6984f7 100644
--- a/Software/VNA_embedded/Application/AmplitudeCal.cpp
+++ b/Software/VNA_embedded/Application/AmplitudeCal.cpp
@@ -44,7 +44,12 @@ bool AmplitudeCal::Save() {
if(!HWHAL::flash.eraseRange(flash_address, flash_size)) {
return false;
}
- return HWHAL::flash.write(flash_address, sizeof(cal), &cal);
+ uint32_t write_size = sizeof(cal);
+ if(write_size % Flash::PageSize != 0) {
+ // round up to next page
+ write_size += Flash::PageSize - write_size % Flash::PageSize;
+ }
+ return HWHAL::flash.write(flash_address, write_size, &cal);
}
void AmplitudeCal::SetDefault() {
@@ -78,9 +83,9 @@ static AmplitudeCal::Correction InterpolateCorrection(const CorrectionTable& tab
ret.port2 = table.port2Correction[table.usedPoints - 1];
} else {
// frequency is between i and i-1, interpolate
- float alpha = (freq - table.freq[i - 1]) / (table.freq[i] - table.freq[i - 1]);
- ret.port1 = table.port1Correction[i - 1] * (1 - alpha) + table.port1Correction[i] * alpha;
- ret.port2 = table.port2Correction[i - 1] * (1 - alpha) + table.port2Correction[i] * alpha;
+ float alpha = (float) (freq - table.freq[i - 1]) / (table.freq[i] - table.freq[i - 1]);
+ ret.port1 = table.port1Correction[i - 1] * (1.0f - alpha) + table.port1Correction[i] * alpha;
+ ret.port2 = table.port2Correction[i - 1] * (1.0f - alpha) + table.port2Correction[i] * alpha;
}
return ret;
}
@@ -116,6 +121,10 @@ void AmplitudeCal::SendReceiver() {
}
static void addPoint(CorrectionTable& table, const Protocol::AmplitudeCorrectionPoint& p) {
+ if(p.pointNum >= AmplitudeCal::maxPoints) {
+ // ignore out-of-bounds point
+ return;
+ }
table.freq[p.pointNum] = p.freq;
table.port1Correction[p.pointNum] = p.port1;
table.port2Correction[p.pointNum] = p.port2;
diff --git a/Software/VNA_embedded/Application/Communication/Protocol.cpp b/Software/VNA_embedded/Application/Communication/Protocol.cpp
index 0fc5bf7..8ad534a 100644
--- a/Software/VNA_embedded/Application/Communication/Protocol.cpp
+++ b/Software/VNA_embedded/Application/Communication/Protocol.cpp
@@ -223,7 +223,8 @@ static Protocol::GeneratorSettings DecodeGeneratorSettings(uint8_t *buf) {
Decoder e(buf);
e.get(d.frequency);
e.get(d.cdbm_level);
- e.get(d.activePort);
+ d.activePort = e.getBits(2);
+ d.applyAmplitudeCorrection = e.getBits(1);
return d;
}
static int16_t EncodeGeneratorSettings(Protocol::GeneratorSettings d, uint8_t *buf,
@@ -231,7 +232,8 @@ static int16_t EncodeGeneratorSettings(Protocol::GeneratorSettings d, uint8_t *b
Encoder e(buf, bufSize);
e.add(d.frequency);
e.add(d.cdbm_level);
- e.add(d.activePort);
+ e.addBits(d.activePort, 2);
+ e.addBits(d.applyAmplitudeCorrection, 1);
return e.getSize();
}
@@ -261,6 +263,7 @@ static Protocol::DeviceInfo DecodeDeviceInfo(uint8_t *buf) {
e.get(d.limits_cdbm_max);
e.get(d.limits_minRBW);
e.get(d.limits_maxRBW);
+ e.get(d.limits_maxAmplitudePoints);
return d;
}
static int16_t EncodeDeviceInfo(Protocol::DeviceInfo d, uint8_t *buf,
@@ -290,6 +293,7 @@ static int16_t EncodeDeviceInfo(Protocol::DeviceInfo d, uint8_t *buf,
e.add(d.limits_cdbm_max);
e.add(d.limits_minRBW);
e.add(d.limits_maxRBW);
+ e.add(d.limits_maxAmplitudePoints);
return e.getSize();
}
@@ -402,6 +406,7 @@ static Protocol::SpectrumAnalyzerSettings DecodeSpectrumAnalyzerSettings(uint8_t
d.SignalID = e.getBits(1);
d.Detector = e.getBits(3);
d.UseDFT = e.getBits(1);
+ d.applyReceiverCorrection = e.getBits(1);
return d;
}
static int16_t EncodeSpectrumAnalyzerSettings(Protocol::SpectrumAnalyzerSettings d, uint8_t *buf,
@@ -415,6 +420,7 @@ static int16_t EncodeSpectrumAnalyzerSettings(Protocol::SpectrumAnalyzerSettings
e.addBits(d.SignalID, 1);
e.addBits(d.Detector, 3);
e.addBits(d.UseDFT, 1);
+ e.addBits(d.applyReceiverCorrection, 1);
return e.getSize();
}
diff --git a/Software/VNA_embedded/Application/Communication/Protocol.hpp b/Software/VNA_embedded/Application/Communication/Protocol.hpp
index 12618d8..adb3f1e 100644
--- a/Software/VNA_embedded/Application/Communication/Protocol.hpp
+++ b/Software/VNA_embedded/Application/Communication/Protocol.hpp
@@ -4,7 +4,7 @@
namespace Protocol {
-static constexpr uint16_t Version = 1;
+static constexpr uint16_t Version = 2;
// When changing/adding/removing variables from these structs also adjust the decode/encode functions in Protocol.cpp
@@ -37,7 +37,8 @@ using ReferenceSettings = struct _referenceSettings {
using GeneratorSettings = struct _generatorSettings {
uint64_t frequency;
int16_t cdbm_level;
- uint8_t activePort;
+ uint8_t activePort :2;
+ uint8_t applyAmplitudeCorrection :1;
};
using DeviceInfo = struct _deviceInfo {
@@ -64,6 +65,7 @@ using DeviceInfo = struct _deviceInfo {
int16_t limits_cdbm_max;
uint32_t limits_minRBW;
uint32_t limits_maxRBW;
+ uint8_t limits_maxAmplitudePoints;
};
using ManualStatus = struct _manualstatus {
@@ -119,6 +121,7 @@ using SpectrumAnalyzerSettings = struct _spectrumAnalyzerSettings {
uint8_t SignalID :1;
uint8_t Detector :3;
uint8_t UseDFT :1;
+ uint8_t applyReceiverCorrection :1;
};
using SpectrumAnalyzerResult = struct _spectrumAnalyzerResult {
diff --git a/Software/VNA_embedded/Application/Drivers/Flash.cpp b/Software/VNA_embedded/Application/Drivers/Flash.cpp
index ac9571f..a59a85d 100644
--- a/Software/VNA_embedded/Application/Drivers/Flash.cpp
+++ b/Software/VNA_embedded/Application/Drivers/Flash.cpp
@@ -30,7 +30,7 @@ void Flash::read(uint32_t address, uint16_t length, void *dest) {
}
bool Flash::write(uint32_t address, uint16_t length, void *src) {
- if((address & 0xFF) != 0 || length%256 != 0) {
+ if(address % PageSize != 0 || length%PageSize != 0) {
// only writes to complete pages allowed
LOG_ERR("Invalid write address/size: %lu/%u", address, length);
return false;
@@ -46,7 +46,7 @@ bool Flash::write(uint32_t address, uint16_t length, void *src) {
(uint8_t) (address >> 8) & 0xFF,
(uint8_t) (address & 0xFF),
};
- // issue read command
+ // issue write command
HAL_SPI_Transmit(spi, cmd, 4, 100);
// write data
HAL_SPI_Transmit(spi, (uint8_t*) src, 256, 1000);
diff --git a/Software/VNA_embedded/Application/Drivers/Flash.hpp b/Software/VNA_embedded/Application/Drivers/Flash.hpp
index a011d15..d904290 100644
--- a/Software/VNA_embedded/Application/Drivers/Flash.hpp
+++ b/Software/VNA_embedded/Application/Drivers/Flash.hpp
@@ -28,11 +28,12 @@ public:
const SPI_HandleTypeDef* const getSpi() const {
return spi;
}
-
-private:
+ static constexpr uint32_t PageSize = 256;
static constexpr uint32_t SectorSize = 4096;
static constexpr uint32_t Block32Size = 32768;
static constexpr uint32_t Block64Size = 65536;
+
+private:
void CS(bool high) {
if(high) {
CS_gpio->BSRR = CS_pin;
diff --git a/Software/VNA_embedded/Application/Generator.cpp b/Software/VNA_embedded/Application/Generator.cpp
index cedb9d8..1c03e64 100644
--- a/Software/VNA_embedded/Application/Generator.cpp
+++ b/Software/VNA_embedded/Application/Generator.cpp
@@ -3,6 +3,7 @@
#include "Hardware.hpp"
#include "max2871.hpp"
#include "Si5351C.hpp"
+#include "AmplitudeCal.hpp"
static constexpr uint32_t BandSwitchFrequency = 25000000;
@@ -25,6 +26,25 @@ void Generator::Setup(Protocol::GeneratorSettings g) {
m.RefEN = 0;
m.Samples = 131072;
m.WindowType = (int) FPGA::Window::None;
+
+ switch(g.activePort) {
+ case 1:
+ m.AmplifierEN = 1;
+ m.PortSwitch = 0;
+ break;
+ case 2:
+ m.AmplifierEN = 1;
+ m.PortSwitch = 1;
+ break;
+ }
+ if (g.applyAmplitudeCorrection) {
+ auto correction = AmplitudeCal::SourceCorrection(g.frequency);
+ if (g.activePort == 1) {
+ g.cdbm_level += correction.port1;
+ } else {
+ g.cdbm_level += correction.port2;
+ }
+ }
// Select correct source
if(g.frequency < BandSwitchFrequency) {
m.SourceLowEN = 1;
@@ -35,6 +55,16 @@ void Generator::Setup(Protocol::GeneratorSettings g) {
m.SourceHighLowpass = (int) FPGA::LowpassFilter::M947;
m.SourceHighPower = (int) MAX2871::Power::n4dbm;
m.SourceHighband = false;
+ if(g.cdbm_level <= HW::LowBandMinPower) {
+ // can use the low power setting
+ m.SourceLowPower = (int) Si5351C::DriveStrength::mA2;
+ g.cdbm_level -= HW::LowBandMinPower;
+ } else {
+ // needs the high power setting
+ m.SourceLowPower = (int) Si5351C::DriveStrength::mA8;
+ g.cdbm_level -= HW::LowBandMaxPower;
+ }
+ m.SourceHighPower = (int) MAX2871::Power::n4dbm;
} else {
m.SourceLowEN = 0;
m.SourceLowFrequency = BandSwitchFrequency;
@@ -51,32 +81,25 @@ void Generator::Setup(Protocol::GeneratorSettings g) {
m.SourceHighLowpass = (int) FPGA::LowpassFilter::None;
}
m.SourceHighband = true;
+ if(g.cdbm_level <= HW::HighBandMinPower) {
+ // can use the low power setting
+ m.SourceHighPower = (int) MAX2871::Power::n4dbm;
+ g.cdbm_level -= HW::HighBandMinPower;
+ } else {
+ // needs the high power setting
+ m.SourceHighPower = (int) MAX2871::Power::p5dbm;
+ g.cdbm_level -= HW::HighBandMaxPower;
+ }
+ m.SourceLowPower = (int) MAX2871::Power::n4dbm;
}
- switch(g.activePort) {
- case 1:
- m.AmplifierEN = 1;
- m.PortSwitch = 0;
- break;
- case 2:
- m.AmplifierEN = 1;
- m.PortSwitch = 1;
- break;
- }
- // Set level (not very accurate)
- if(g.cdbm_level > -1000) {
- // use higher source power (approx 0dbm with no attenuation)
- m.SourceHighPower = (int) MAX2871::Power::p5dbm;
- m.SourceLowPower = (int) Si5351C::DriveStrength::mA8;
- } else {
- // use lower source power (approx -10dbm with no attenuation)
- m.SourceHighPower = (int) MAX2871::Power::n4dbm;
- m.SourceLowPower = (int) Si5351C::DriveStrength::mA4;
- g.cdbm_level += 1000;
- }
+
// calculate required attenuation
- uint16_t attval = -g.cdbm_level / 25;
+ int16_t attval = -g.cdbm_level / 25;
+ // TODO set some flag if attenuator limit reached?
if(attval > 127) {
attval = 127;
+ } else if(attval < 0) {
+ attval = 0;
}
m.attenuator = attval;
Manual::Setup(m);
diff --git a/Software/VNA_embedded/Application/Hardware.hpp b/Software/VNA_embedded/Application/Hardware.hpp
index fca7f24..fdf2177 100644
--- a/Software/VNA_embedded/Application/Hardware.hpp
+++ b/Software/VNA_embedded/Application/Hardware.hpp
@@ -3,6 +3,7 @@
#include
#include "Protocol.hpp"
#include "FPGA/FPGA.hpp"
+#include "AmplitudeCal.hpp"
#define USE_DEBUG_PINS
@@ -38,6 +39,12 @@ 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");
+// approximate output power at low frequencies with different source strength settings (attenuator = 0) in cdbm
+static constexpr int16_t LowBandMinPower = -1350;
+static constexpr int16_t LowBandMaxPower = -190;
+static constexpr int16_t HighBandMinPower = -1060;
+static constexpr int16_t HighBandMaxPower = -160;
+
static constexpr Protocol::DeviceInfo Info = {
.ProtocolVersion = Protocol::Version,
.FW_major = FW_MAJOR,
@@ -62,6 +69,7 @@ static constexpr Protocol::DeviceInfo Info = {
.limits_cdbm_max = 0,
.limits_minRBW = (uint32_t) (ADCSamplerate * 2.23f / MaxSamples),
.limits_maxRBW = (uint32_t) (ADCSamplerate * 2.23f / MinSamples),
+ .limits_maxAmplitudePoints = AmplitudeCal::maxPoints,
};
enum class Mode {
diff --git a/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp b/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp
index 0b63c89..cf7f5cc 100644
--- a/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp
+++ b/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp
@@ -219,8 +219,8 @@ bool SA::MeasurementDone(const FPGA::SamplingResult &result) {
port1 = dft.P1;
port2 = dft.P2;
} else {
- port1 = abs(std::complex(result.P1I, result.P1Q));
- port2 = abs(std::complex(result.P2I, result.P2Q));
+ port1 = fabs(std::complex(result.P1I, result.P1Q));
+ port2 = fabs(std::complex(result.P2I, result.P2Q));
}
port1 /= sampleNum;
port2 /= sampleNum;
@@ -315,8 +315,16 @@ void SA::Work() {
// Send result to application
p.type = Protocol::PacketType::SpectrumAnalyzerResult;
// measurements are already up to date, fill remaining fields
- p.spectrumResult.pointNum = binIndex;
p.spectrumResult.frequency = s.f_start + (s.f_stop - s.f_start) * binIndex / (s.pointNum - 1);
+ // scale approximately (constant determined empirically)
+ p.spectrumResult.port1 /= 253000000.0;
+ p.spectrumResult.port2 /= 253000000.0;
+ if (s.applyReceiverCorrection) {
+ auto correction = AmplitudeCal::ReceiverCorrection(p.spectrumResult.frequency);
+ p.spectrumResult.port1 *= powf(10.0f, (float) correction.port1 / 100.0f / 20.0f);
+ p.spectrumResult.port2 *= powf(10.0f, (float) correction.port2 / 100.0f / 20.0f);
+ }
+ p.spectrumResult.pointNum = binIndex;
Communication::Send(p);
}
}