Partial source calibration dialog

This commit is contained in:
Jan Käberich 2020-11-16 20:05:29 +01:00
parent 6f717de0f1
commit 875f3b0170
28 changed files with 784 additions and 33 deletions

View File

@ -1,11 +1,13 @@
HEADERS += \
../VNA_embedded/Application/Communication/Protocol.hpp \
Calibration/amplitudecaldialog.h \
Calibration/calibration.h \
Calibration/calibrationtracedialog.h \
Calibration/calkit.h \
Calibration/calkitdialog.h \
Calibration/json.hpp \
Calibration/measurementmodel.h \
Calibration/sourcecaldialog.h \
CustomWidgets/colorpickerbutton.h \
CustomWidgets/informationbox.h \
CustomWidgets/siunitedit.h \
@ -48,11 +50,13 @@ HEADERS += \
SOURCES += \
../VNA_embedded/Application/Communication/Protocol.cpp \
Calibration/amplitudecaldialog.cpp \
Calibration/calibration.cpp \
Calibration/calibrationtracedialog.cpp \
Calibration/calkit.cpp \
Calibration/calkitdialog.cpp \
Calibration/measurementmodel.cpp \
Calibration/sourcecaldialog.cpp \
CustomWidgets/colorpickerbutton.cpp \
CustomWidgets/informationbox.cpp \
CustomWidgets/qwtplotpiecewisecurve.cpp \
@ -102,6 +106,7 @@ win32:LIBS += -LC:\Qwt-6.1.4\lib -lqwt
QT += widgets
FORMS += \
Calibration/amplitudecaldialog.ui \
Calibration/calibrationtracedialog.ui \
Calibration/calkitdialog.ui \
CustomWidgets/tilewidget.ui \

View File

@ -0,0 +1,187 @@
#include "amplitudecaldialog.h"
#include "ui_amplitudecaldialog.h"
#include "mode.h"
#include "unit.h"
#include <QDebug>
AmplitudeCalDialog::AmplitudeCalDialog(Device *dev, QWidget *parent) :
QDialog(parent),
ui(new Ui::AmplitudeCalDialog),
dev(dev),
model(this)
{
activeMode = Mode::getActiveMode();
activeMode->deactivate();
dev->SetIdle();
ui->setupUi(this);
ui->view->setModel(&model);
ui->view->setColumnWidth(AmplitudeModel::ColIndexFreq, 100);
ui->view->setColumnWidth(AmplitudeModel::ColIndexCorrectionFactors, 150);
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);
}
AmplitudeCalDialog::~AmplitudeCalDialog()
{
delete ui;
activeMode->activate();
}
void AmplitudeCalDialog::reject()
{
// TODO check for unsaved data
delete this;
}
std::vector<AmplitudeCalDialog::CorrectionPoint> AmplitudeCalDialog::getPoints() const
{
return points;
}
void AmplitudeCalDialog::setAmplitude(double amplitude, unsigned int point, bool port2)
{
if(point >= points.size()) {
return;
}
if(port2) {
points[point].amplitudePort2 = amplitude;
} else {
points[point].amplitudePort1 = amplitude;
}
}
void AmplitudeCalDialog::ReceivedPoint(Protocol::AmplitudeCorrectionPoint p)
{
qDebug() << "Received a point";
CorrectionPoint c;
c.frequency = p.freq * 10.0;
c.correctionPort1 = p.port1;
c.correctionPort2 = p.port2;
model.beginInsertRows(QModelIndex(), points.size(), points.size());
points.push_back(c);
model.endInsertRows();
emit pointsUpdated();
}
void AmplitudeCalDialog::LoadFromDevice()
{
model.beginResetModel();
points.clear();
model.endResetModel();
qDebug() << "Asking for amplitude calibration";
dev->SendCommandWithoutPayload(requestCommand());
}
void AmplitudeCalDialog::SaveToDevice()
{
for(unsigned int i=0;i<points.size();i++) {
auto p = points[i];
Protocol::PacketInfo info;
info.type = pointType();
info.amplitudePoint.freq = p.frequency / 10.0;
info.amplitudePoint.port1 = p.correctionPort1;
info.amplitudePoint.port2 = p.correctionPort2;
info.amplitudePoint.totalPoints = points.size();
info.amplitudePoint.pointNum = i;
dev->SendPacket(info);
}
}
void AmplitudeCalDialog::RemovePoint()
{
unsigned int row = ui->view->currentIndex().row();
if(row < points.size()) {
model.beginRemoveRows(QModelIndex(), row, row);
points.erase(points.begin() + row);
model.endInsertRows();
}
}
void AmplitudeCalDialog::AddPoint()
{
}
AmplitudeModel::AmplitudeModel(AmplitudeCalDialog *c) :
QAbstractTableModel(),
c(c)
{
}
int AmplitudeModel::rowCount(const QModelIndex &parent) const
{
return c->getPoints().size();
}
int AmplitudeModel::columnCount(const QModelIndex &parent) const
{
return ColIndexLast;
}
QVariant AmplitudeModel::data(const QModelIndex &index, int role) const
{
if(role == Qt::DisplayRole && index.row() < c->getPoints().size()) {
auto p = c->getPoints()[index.row()];
switch(index.column()) {
case ColIndexFreq:
return Unit::ToString(p.frequency, "Hz", " kMG", 6);
break;
case ColIndexCorrectionFactors:
return QString::number(p.correctionPort1) + ", " + QString::number(p.correctionPort2);
break;
case ColIndexPort1:
return Unit::ToString(p.amplitudePort1, "dbm", " ", 4);
break;
case ColIndexPort2:
return Unit::ToString(p.amplitudePort2, "dbm", " ", 4);
break;
}
}
return QVariant();
}
bool AmplitudeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if((unsigned int) index.row() >= c->getPoints().size()) {
return false;
}
switch(index.column()) {
case ColIndexPort1:
c->setAmplitude(value.toDouble(), index.row(), false);
return true;
case ColIndexPort2:
c->setAmplitude(value.toDouble(), index.row(), true);
return true;
}
return false;
}
QVariant AmplitudeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch(section) {
case ColIndexFreq: return "Frequency"; break;
case ColIndexCorrectionFactors: return "Correction Factors"; break;
case ColIndexPort1: return "Amplitude Port 1"; break;
case ColIndexPort2: return "Amplitude Port 2"; break;
default: return QVariant(); break;
}
} else {
return QVariant();
}
}
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;
}
return (Qt::ItemFlags) flags;
}

View File

@ -0,0 +1,77 @@
#ifndef AMPLITUDECALDIALOG_H
#define AMPLITUDECALDIALOG_H
#include <QDialog>
#include "mode.h"
#include "Device/device.h"
namespace Ui {
class AmplitudeCalDialog;
}
class AmplitudeCalDialog;
class AmplitudeModel : public QAbstractTableModel
{
friend AmplitudeCalDialog;
Q_OBJECT
public:
AmplitudeModel(AmplitudeCalDialog *c);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
private:
enum {
ColIndexFreq,
ColIndexCorrectionFactors,
ColIndexPort1,
ColIndexPort2,
ColIndexLast
};
AmplitudeCalDialog *c;
};
class AmplitudeCalDialog : public QDialog
{
Q_OBJECT
public:
explicit AmplitudeCalDialog(Device *dev, QWidget *parent = nullptr);
~AmplitudeCalDialog();
void reject() override;
class CorrectionPoint {
public:
double frequency;
double correctionPort1;
double correctionPort2;
double amplitudePort1;
double amplitudePort2;
};
std::vector<CorrectionPoint> getPoints() const;
void setAmplitude(double amplitude, unsigned int point, bool port2);
protected slots:
void ReceivedPoint(Protocol::AmplitudeCorrectionPoint p);
void LoadFromDevice();
void SaveToDevice();
void RemovePoint();
void AddPoint();
signals:
void pointsUpdated();
protected:
virtual Protocol::PacketType requestCommand() = 0;
virtual Protocol::PacketType pointType() = 0;
std::vector<CorrectionPoint> points;
Ui::AmplitudeCalDialog *ui;
Device *dev;
Mode *activeMode;
AmplitudeModel model;
};
#endif // SOURCECALDIALOG_H

View File

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AmplitudeCalDialog</class>
<widget class="QDialog" name="AmplitudeCalDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>766</width>
<height>599</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTableView" name="view">
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectItems</enum>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="add">
<property name="text">
<string>Add Point</string>
</property>
<property name="icon">
<iconset theme="list-add" resource="../icons.qrc">
<normaloff>:/icons/add.png</normaloff>:/icons/add.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="remove">
<property name="text">
<string>Delete Point</string>
</property>
<property name="icon">
<iconset theme="list-remove" resource="../icons.qrc">
<normaloff>:/icons/remove.png</normaloff>:/icons/remove.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="help">
<property name="text">
<string>Help</string>
</property>
<property name="icon">
<iconset theme="help"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="load">
<property name="text">
<string>Load from Device</string>
</property>
<property name="icon">
<iconset theme="document-open" resource="../icons.qrc">
<normaloff>:/icons/open.png</normaloff>:/icons/open.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="save">
<property name="text">
<string>Save to Device</string>
</property>
<property name="icon">
<iconset theme="document-save" resource="../icons.qrc">
<normaloff>:/icons/save.png</normaloff>:/icons/save.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../icons.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,8 @@
#include "sourcecaldialog.h"
SourceCalDialog::SourceCalDialog(Device *dev)
: AmplitudeCalDialog(dev)
{
setWindowTitle("Source Calibration Dialog");
LoadFromDevice();
}

View File

@ -0,0 +1,17 @@
#ifndef SOURCECALDIALOG_H
#define SOURCECALDIALOG_H
#include <QObject>
#include "amplitudecaldialog.h"
class SourceCalDialog : public AmplitudeCalDialog
{
Q_OBJECT
public:
SourceCalDialog(Device *dev);
protected:
Protocol::PacketType requestCommand() override { return Protocol::PacketType::RequestSourceCal; }
Protocol::PacketType pointType() override { return Protocol::PacketType::SourceCalPoint; }
};
#endif // SOURCECALDIALOG_H

View File

@ -434,6 +434,10 @@ void Device::ReceivedData()
case Protocol::PacketType::SpectrumAnalyzerResult:
emit SpectrumResultReceived(packet.spectrumResult);
break;
case Protocol::PacketType::SourceCalPoint:
case Protocol::PacketType::ReceiverCalPoint:
emit AmplitudeCorrectionPointReceived(packet.amplitudePoint);
break;
case Protocol::PacketType::DeviceInfo:
if(packet.info.ProtocolVersion != Protocol::Version) {
if(!lastInfoValid) {

View File

@ -13,8 +13,8 @@
Q_DECLARE_METATYPE(Protocol::Datapoint);
Q_DECLARE_METATYPE(Protocol::ManualStatus);
Q_DECLARE_METATYPE(Protocol::DeviceInfo);
Q_DECLARE_METATYPE(Protocol::SpectrumAnalyzerResult);
Q_DECLARE_METATYPE(Protocol::AmplitudeCorrectionPoint);
class USBInBuffer : public QObject {
Q_OBJECT;
@ -74,6 +74,7 @@ signals:
void DatapointReceived(Protocol::Datapoint);
void ManualStatusReceived(Protocol::ManualStatus);
void SpectrumResultReceived(Protocol::SpectrumAnalyzerResult);
void AmplitudeCorrectionPointReceived(Protocol::AmplitudeCorrectionPoint);
void DeviceInfoUpdated();
void ConnectionLost();
void AckReceived();

View File

@ -63,7 +63,7 @@ void FirmwareUpdateDialog::on_bStart_clicked()
addStatus("Erasing device memory...");
dev->SendCommandWithoutPayload(Protocol::PacketType::ClearFlash);
timer.setSingleShot(true);
timer.start(10000);
timer.start(20000);
}
void FirmwareUpdateDialog::addStatus(QString line)

View File

@ -144,7 +144,6 @@ ManualControlDialog::ManualControlDialog(Device &dev, QWidget *parent) :
MakeReadOnly(ui->refmag);
MakeReadOnly(ui->refphase);
qRegisterMetaType<Protocol::ManualStatus>("Status");
connect(&dev, &Device::ManualStatusReceived, this, &ManualControlDialog::NewStatus);
connect(ui->SourceCE, &QCheckBox::toggled, [=](bool) { UpdateDevice(); });

View File

@ -192,8 +192,6 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
window->addDockWidget(Qt::BottomDockWidgetArea, markerDock);
docks.insert(markerDock);
qRegisterMetaType<Protocol::SpectrumAnalyzerResult>("SpectrumResult");
// Set initial sweep settings
auto pref = Preferences::getInstance();
if(pref.Startup.RememberSweepSettings) {

View File

@ -360,8 +360,6 @@ VNA::VNA(AppWindow *window)
window->addDockWidget(Qt::BottomDockWidgetArea, markerDock);
docks.insert(markerDock);
qRegisterMetaType<Protocol::Datapoint>("Datapoint");
// Set initial sweep settings
auto pref = Preferences::getInstance();
if(pref.Acquisition.alwaysExciteBothPorts) {

View File

@ -44,6 +44,7 @@
#include "VNA/vna.h"
#include "Generator/generator.h"
#include "SpectrumAnalyzer/spectrumanalyzer.h"
#include "Calibration/sourcecaldialog.h"
using namespace std;
@ -100,6 +101,7 @@ AppWindow::AppWindow(QWidget *parent)
connect(ui->actionQuit, &QAction::triggered, this, &AppWindow::close);
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->actionPreferences, &QAction::triggered, [=](){
Preferences::getInstance().edit();
// settings might have changed, update necessary stuff
@ -129,6 +131,9 @@ AppWindow::AppWindow(QWidget *parent)
vna->activate();
qRegisterMetaType<Protocol::Datapoint>("Datapoint");
qRegisterMetaType<Protocol::ManualStatus>("Manual");
qRegisterMetaType<Protocol::SpectrumAnalyzerResult>("SpectrumAnalyzerResult");
qRegisterMetaType<Protocol::AmplitudeCorrectionPoint>("AmplitudeCorrection");
// List available devices
if(UpdateDeviceList() && Preferences::getInstance().Startup.ConnectToFirstDevice) {
@ -181,6 +186,7 @@ void AppWindow::ConnectToDevice(QString serial)
ui->actionDisconnect->setEnabled(true);
ui->actionManual_Control->setEnabled(true);
ui->actionFirmware_Update->setEnabled(true);
ui->actionSource_Calibration->setEnabled(true);
Mode::getActiveMode()->initializeDevice();
UpdateReference();
@ -207,6 +213,7 @@ void AppWindow::DisconnectDevice()
ui->actionDisconnect->setEnabled(false);
ui->actionManual_Control->setEnabled(false);
ui->actionFirmware_Update->setEnabled(false);
ui->actionSource_Calibration->setEnabled(false);
for(auto a : deviceActionGroup->actions()) {
a->setChecked(false);
}
@ -349,6 +356,12 @@ void AppWindow::DeviceNeedsUpdate(int reported, int expected)
}
}
void AppWindow::SourceCalibrationDialog()
{
auto d = new SourceCalDialog(device);
d->exec();
}
Device *AppWindow::getDevice() const
{
return device;

View File

@ -44,6 +44,7 @@ private slots:
void UpdateReference();
void StartFirmwareUpdateDialog();
void DeviceNeedsUpdate(int reported, int expected);
void SourceCalibrationDialog();
private:
void DeviceConnectionLost();
void CreateToolbars();

View File

@ -48,6 +48,8 @@
<addaction name="separator"/>
<addaction name="actionManual_Control"/>
<addaction name="actionFirmware_Update"/>
<addaction name="separator"/>
<addaction name="actionSource_Calibration"/>
</widget>
<widget class="QMenu" name="menuWindow">
<property name="title">
@ -151,6 +153,14 @@
<string>About</string>
</property>
</action>
<action name="actionSource_Calibration">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Source Calibration</string>
</property>
</action>
</widget>
<resources>
<include location="icons.qrc"/>

View File

@ -0,0 +1,136 @@
#include "AmplitudeCal.hpp"
#include <cstring>
#include "Communication.h"
#include "HW_HAL.hpp"
#define LOG_LEVEL LOG_LEVEL_INFO
#define LOG_MODULE "CAL"
#include "Log.h"
// increment when Calibration struct format changed. If a wrong version is found in flash, it will revert to default values
static constexpr uint16_t version = 0x0000;
using CorrectionTable = struct {
uint8_t usedPoints;
uint32_t freq[AmplitudeCal::maxPoints]; // LSB = 10Hz
int16_t port1Correction[AmplitudeCal::maxPoints]; // LSB = 0.01db
int16_t port2Correction[AmplitudeCal::maxPoints]; // LSB = 0.01db
};
using Calibration = struct _calibration {
uint16_t version;
CorrectionTable Source;
CorrectionTable Receiver;
};
static Calibration cal;
static_assert(sizeof(cal) <= AmplitudeCal::flash_size, "Reserved flash size is too small");
bool AmplitudeCal::Load() {
HWHAL::flash.read(flash_address, sizeof(cal), &cal);
if(cal.version != version) {
LOG_WARN("Invalid version in flash, expected %u, got %u", version, cal.version);
SetDefault();
return false;
} else {
LOG_INFO("Loaded from flash");
return true;
}
}
bool AmplitudeCal::Save() {
if(!HWHAL::flash.eraseRange(flash_address, flash_size)) {
return false;
}
return HWHAL::flash.write(flash_address, sizeof(cal), &cal);
}
void AmplitudeCal::SetDefault() {
memset(&cal, 0, sizeof(cal));
cal.version = version;
cal.Source.usedPoints = 1;
cal.Source.freq[0] = 100000000;
cal.Receiver.usedPoints = 1;
cal.Receiver.freq[0] = 100000000;
LOG_INFO("Set to default");
}
static AmplitudeCal::Correction InterpolateCorrection(const CorrectionTable& table, uint64_t freq) {
// adjust LSB to match table
freq /= 10;
AmplitudeCal::Correction ret;
// find first valid index that is higher than the given frequency
uint8_t i = 0;
for (; i < table.usedPoints; i++) {
if (table.freq[i] >= freq) {
break;
}
}
if (i == 0) {
// no previous index, nothing to interpolate
ret.port1 = table.port1Correction[0];
ret.port2 = table.port2Correction[0];
} else if (i >= table.usedPoints) {
// went beyond last point, nothing to interpolate
ret.port1 = table.port1Correction[table.usedPoints - 1];
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;
}
return ret;
}
AmplitudeCal::Correction AmplitudeCal::SourceCorrection(uint64_t freq) {
return InterpolateCorrection(cal.Source, freq);
}
AmplitudeCal::Correction AmplitudeCal::ReceiverCorrection(uint64_t freq) {
return InterpolateCorrection(cal.Receiver, freq);
}
static void SendCorrectionTable(const CorrectionTable& table, Protocol::PacketType type) {
for(uint8_t i=0;i<table.usedPoints;i++) {
// assemble packet
Protocol::PacketInfo p;
p.type = type;
p.amplitudePoint.totalPoints = table.usedPoints;
p.amplitudePoint.pointNum = i;
p.amplitudePoint.freq = table.freq[i];
p.amplitudePoint.port1 = table.port1Correction[i];
p.amplitudePoint.port2 = table.port2Correction[i];
Communication::Send(p);
}
}
void AmplitudeCal::SendSource() {
SendCorrectionTable(cal.Source, Protocol::PacketType::SourceCalPoint);
}
void AmplitudeCal::SendReceiver() {
SendCorrectionTable(cal.Receiver, Protocol::PacketType::ReceiverCalPoint);
}
static void addPoint(CorrectionTable& table, const Protocol::AmplitudeCorrectionPoint& p) {
table.freq[p.pointNum] = p.freq;
table.port1Correction[p.pointNum] = p.port1;
table.port2Correction[p.pointNum] = p.port2;
if(p.pointNum == p.totalPoints - 1) {
// this was the last point, update used points and save
table.usedPoints = p.totalPoints;
LOG_INFO("Last point received, saving to flash");
AmplitudeCal::Save();
}
}
void AmplitudeCal::AddSourcePoint(const Protocol::AmplitudeCorrectionPoint& p) {
addPoint(cal.Source, p);
}
void AmplitudeCal::AddReceiverPoint(const Protocol::AmplitudeCorrectionPoint& p) {
addPoint(cal.Receiver, p);
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <stdint.h>
#include "Flash.hpp"
#include "Firmware.hpp"
#include "Protocol.hpp"
namespace AmplitudeCal {
constexpr uint8_t maxPoints = 64;
constexpr uint32_t flash_address = Firmware::maxSize; // stored directly behind firmware in flash
constexpr uint32_t flash_size = 8192; // reserve two sectors for now
bool Load();
bool Save();
void SetDefault();
using Correction = struct _correction {
int16_t port1;
int16_t port2;
};
Correction SourceCorrection(uint64_t freq);
Correction ReceiverCorrection(uint64_t freq);
void SendSource();
void SendReceiver();
void AddSourcePoint(const Protocol::AmplitudeCorrectionPoint& p);
void AddReceiverPoint(const Protocol::AmplitudeCorrectionPoint& p);
}

View File

@ -17,6 +17,8 @@
#include "Manual.hpp"
#include "Generator.hpp"
#include "SpectrumAnalyzer.hpp"
#include "HW_HAL.hpp"
#include "AmplitudeCal.hpp"
#define LOG_LEVEL LOG_LEVEL_INFO
#define LOG_MODULE "App"
@ -32,8 +34,6 @@ static TaskHandle_t handle;
// has MCU controllable flash chip, firmware update supported
#define HAS_FLASH
#include "Firmware.hpp"
extern SPI_HandleTypeDef hspi1;
static Flash flash = Flash(&hspi1, FLASH_CS_GPIO_Port, FLASH_CS_Pin);
#endif
extern ADC_HandleTypeDef hadc1;
@ -72,17 +72,17 @@ void App_Start() {
LOG_INFO("Start");
Exti::Init();
#ifdef HAS_FLASH
if(!flash.isPresent()) {
if(!HWHAL::flash.isPresent()) {
LOG_CRIT("Failed to detect onboard FLASH");
LED::Error(1);
}
auto fw_info = Firmware::GetFlashContentInfo(&flash);
auto fw_info = Firmware::GetFlashContentInfo();
if(fw_info.valid) {
if(fw_info.CPU_need_update) {
// Function will not return, the device will reboot with the new firmware instead
// Firmware::PerformUpdate(&flash, fw_info);
}
if(!FPGA::Configure(&flash, fw_info.FPGA_bitstream_address, fw_info.FPGA_bitstream_size)) {
if(!FPGA::Configure(fw_info.FPGA_bitstream_address, fw_info.FPGA_bitstream_size)) {
LOG_CRIT("FPGA configuration failed");
LED::Error(3);
}
@ -99,6 +99,8 @@ void App_Start() {
EN_6V_GPIO_Port->BSRR = EN_6V_Pin;
#endif
AmplitudeCal::Load();
if (!HW::Init()) {
LOG_CRIT("Initialization failed, unable to start");
LED::Error(4);
@ -166,7 +168,7 @@ void App_Start() {
HW::SetMode(HW::Mode::Idle);
sweepActive = false;
LOG_DEBUG("Erasing FLASH in preparation for firmware update...");
if(flash.eraseChip()) {
if(HWHAL::flash.eraseRange(0, Firmware::maxSize)) {
LOG_DEBUG("...FLASH erased")
Communication::SendWithoutPayload(Protocol::PacketType::Ack);
} else {
@ -176,7 +178,7 @@ void App_Start() {
break;
case Protocol::PacketType::FirmwarePacket:
LOG_INFO("Writing firmware packet at address %u", recv_packet.firmware.address);
if(flash.write(recv_packet.firmware.address, sizeof(recv_packet.firmware.data), recv_packet.firmware.data)) {
if(HWHAL::flash.write(recv_packet.firmware.address, sizeof(recv_packet.firmware.data), recv_packet.firmware.data)) {
Communication::SendWithoutPayload(Protocol::PacketType::Ack);
} else {
LOG_ERR("Failed to write FLASH");
@ -185,18 +187,30 @@ void App_Start() {
break;
case Protocol::PacketType::PerformFirmwareUpdate: {
LOG_INFO("Firmware update process triggered");
auto fw_info = Firmware::GetFlashContentInfo(&flash);
auto fw_info = Firmware::GetFlashContentInfo();
if(fw_info.valid) {
Communication::SendWithoutPayload(Protocol::PacketType::Ack);
// Some delay to allow communication to finish
vTaskDelay(100);
Firmware::PerformUpdate(&flash, fw_info);
Firmware::PerformUpdate(fw_info);
// should never get here
Communication::SendWithoutPayload(Protocol::PacketType::Nack);
}
}
break;
#endif
case Protocol::PacketType::RequestSourceCal:
AmplitudeCal::SendSource();
break;
case Protocol::PacketType::RequestReceiverCal:
AmplitudeCal::SendReceiver();
break;
case Protocol::PacketType::SourceCalPoint:
AmplitudeCal::AddSourcePoint(recv_packet.amplitudePoint);
break;
case Protocol::PacketType::ReceiverCalPoint:
AmplitudeCal::AddReceiverPoint(recv_packet.amplitudePoint);
break;
default:
// this packet type is not supported
Communication::SendWithoutPayload(Protocol::PacketType::Nack);

View File

@ -457,6 +457,27 @@ static int16_t EncodeFirmwarePacket(const Protocol::FirmwarePacket &d, uint8_t *
return 4 + Protocol::FirmwareChunkSize;
}
static int16_t EncodeAmplitudeCorrectionPoint(
Protocol::AmplitudeCorrectionPoint d, uint8_t *buf, uint16_t bufSize) {
Encoder e(buf, bufSize);
e.add(d.totalPoints);
e.add(d.pointNum);
e.add(d.freq);
e.add(d.port1);
e.add(d.port2);
return e.getSize();
}
static Protocol::AmplitudeCorrectionPoint DecodeAmplitudeCorrectionPoint(uint8_t *buf) {
Protocol::AmplitudeCorrectionPoint d;
Decoder e(buf);
e.get(d.totalPoints);
e.get(d.pointNum);
e.get(d.freq);
e.get(d.port1);
e.get(d.port2);
return d;
}
uint16_t Protocol::DecodeBuffer(uint8_t *buf, uint16_t len, PacketInfo *info) {
if (!info || !len) {
info->type = PacketType::None;
@ -541,11 +562,17 @@ uint16_t Protocol::DecodeBuffer(uint8_t *buf, uint16_t len, PacketInfo *info) {
case PacketType::SpectrumAnalyzerResult:
info->spectrumResult = DecodeSpectrumAnalyzerResult(&data[4]);
break;
case PacketType::SourceCalPoint:
case PacketType::ReceiverCalPoint:
info->amplitudePoint = DecodeAmplitudeCorrectionPoint(&data[4]);
break;
case PacketType::Ack:
case PacketType::PerformFirmwareUpdate:
case PacketType::ClearFlash:
case PacketType::Nack:
case PacketType::RequestDeviceInfo:
case PacketType::RequestReceiverCal:
case PacketType::RequestSourceCal:
// no payload, nothing to do
break;
case PacketType::None:
@ -588,11 +615,17 @@ 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::SourceCalPoint:
case PacketType::ReceiverCalPoint:
payload_size = EncodeAmplitudeCorrectionPoint(packet.amplitudePoint, &dest[4], destsize - 8);
break;
case PacketType::Ack:
case PacketType::PerformFirmwareUpdate:
case PacketType::ClearFlash:
case PacketType::Nack:
case PacketType::RequestDeviceInfo:
case PacketType::RequestSourceCal:
case PacketType::RequestReceiverCal:
// no payload, nothing to do
break;
case PacketType::None:

View File

@ -134,6 +134,14 @@ using FirmwarePacket = struct _firmwarePacket {
uint8_t data[FirmwareChunkSize];
};
using AmplitudeCorrectionPoint = struct _amplitudecorrectionpoint {
uint8_t totalPoints;
uint8_t pointNum;
uint32_t freq;
int16_t port1;
int16_t port2;
};
enum class PacketType : uint8_t {
None = 0,
Datapoint = 1,
@ -151,6 +159,10 @@ enum class PacketType : uint8_t {
SpectrumAnalyzerSettings = 13,
SpectrumAnalyzerResult = 14,
RequestDeviceInfo = 15,
RequestSourceCal = 16,
RequestReceiverCal = 17,
SourceCalPoint = 18,
ReceiverCalPoint = 19,
};
using PacketInfo = struct _packetinfo {
@ -166,6 +178,7 @@ using PacketInfo = struct _packetinfo {
ManualStatus status;
SpectrumAnalyzerSettings spectrumSettings;
SpectrumAnalyzerResult spectrumResult;
AmplitudeCorrectionPoint amplitudePoint;
};
};

View File

@ -4,6 +4,7 @@
#include "main.h"
#include "FPGA_HAL.hpp"
#include <complex>
#include "HW_HAL.hpp"
#define LOG_LEVEL LOG_LEVEL_DEBUG
#define LOG_MODULE "FPGA"
@ -33,7 +34,7 @@ void FPGA::WriteRegister(FPGA::Reg reg, uint16_t value) {
}
}
bool FPGA::Configure(Flash *f, uint32_t start_address, uint32_t bitstream_size) {
bool FPGA::Configure(uint32_t start_address, uint32_t bitstream_size) {
if(!PROGRAM_B.gpio) {
LOG_WARN("PROGRAM_B not defined, assuming FPGA configures itself in master configuration");
// wait too allow enough time for FPGA configuration
@ -64,7 +65,7 @@ bool FPGA::Configure(Flash *f, uint32_t start_address, uint32_t bitstream_size)
}
// TODO this part might be doable with the DMA instead of the buffer
// get chunk of bitstream from flash...
f->read(start_address, size, buf);
HWHAL::flash.read(start_address, size, buf);
// ... and pass it on to FPGA
HAL_SPI_Transmit(&CONFIGURATION_SPI, buf, size, 100);
bitstream_size -= size;

View File

@ -105,7 +105,7 @@ enum class Window {
Flattop = 0x03,
};
bool Configure(Flash *f, uint32_t start_address, uint32_t bitstream_size);
bool Configure(uint32_t start_address, uint32_t bitstream_size);
using HaltedCallback = void(*)(void);
bool Init(HaltedCallback cb = nullptr);

View File

@ -29,7 +29,7 @@ void Flash::read(uint32_t address, uint16_t length, void *dest) {
CS(true);
}
bool Flash::write(uint32_t address, uint16_t length, uint8_t *src) {
bool Flash::write(uint32_t address, uint16_t length, void *src) {
if((address & 0xFF) != 0 || length%256 != 0) {
// only writes to complete pages allowed
LOG_ERR("Invalid write address/size: %lu/%u", address, length);
@ -49,7 +49,7 @@ bool Flash::write(uint32_t address, uint16_t length, uint8_t *src) {
// issue read command
HAL_SPI_Transmit(spi, cmd, 4, 100);
// write data
HAL_SPI_Transmit(spi, src, 256, 1000);
HAL_SPI_Transmit(spi, (uint8_t*) src, 256, 1000);
CS(true);
if(!WaitBusy(20)) {
LOG_ERR("Write timed out");
@ -78,16 +78,66 @@ void Flash::EnableWrite() {
}
bool Flash::eraseChip() {
LOG_INFO("Erasing...");
LOG_INFO("Erasing chip...");
EnableWrite();
CS(false);
// enable write latch
uint8_t chip_erase = 0x60;
HAL_SPI_Transmit(spi, &chip_erase, 1, 100);
CS(true);
return WaitBusy(25000);
}
bool Flash::eraseSector(uint32_t address) {
// align address with sector address
address -= address % SectorSize;
LOG_INFO("Erasing sector at %lu", address);
EnableWrite();
CS(false);
uint8_t cmd[4] = {
0x20,
(uint8_t) (address >> 16) & 0xFF,
(uint8_t) (address >> 8) & 0xFF,
(uint8_t) (address & 0xFF),
};
HAL_SPI_Transmit(spi, cmd, 4, 100);
CS(true);
return WaitBusy(25000);
}
bool Flash::erase32Block(uint32_t address) {
// align address with block address
address -= address % Block32Size;
LOG_INFO("Erasing 32kB block at %lu", address);
EnableWrite();
CS(false);
uint8_t cmd[4] = {
0x52,
(uint8_t) (address >> 16) & 0xFF,
(uint8_t) (address >> 8) & 0xFF,
(uint8_t) (address & 0xFF),
};
HAL_SPI_Transmit(spi, cmd, 4, 100);
CS(true);
return WaitBusy(25000);
}
bool Flash::erase64Block(uint32_t address) {
// align address with block address
address -= address % Block64Size;
LOG_INFO("Erasing 64kB block at %lu", address);
EnableWrite();
CS(false);
uint8_t cmd[4] = {
0xD8,
(uint8_t) (address >> 16) & 0xFF,
(uint8_t) (address >> 8) & 0xFF,
(uint8_t) (address & 0xFF),
};
HAL_SPI_Transmit(spi, cmd, 4, 100);
CS(true);
return WaitBusy(25000);
}
void Flash::initiateRead(uint32_t address) {
address &= 0x00FFFFFF;
CS(false);
@ -120,3 +170,38 @@ bool Flash::WaitBusy(uint32_t timeout) {
LOG_ERR("Timeout occured");
return false;
}
bool Flash::eraseRange(uint32_t start, uint32_t len) {
if(start % SectorSize != 0) {
LOG_ERR("Start address of range has to be sector aligned (is %lu)", start);
return false;
}
if(len % SectorSize != 0) {
LOG_ERR("Length of range has to be multiple of sector size (is %lu)", len);
return false;
}
uint32_t erased_len = 0;
while(erased_len < len) {
uint32_t remaining = len - erased_len;
if(remaining >= Block64Size && start % Block64Size == 0) {
erase64Block(start);
erased_len += Block64Size;
start += Block64Size;
continue;
}
if(remaining >= Block32Size && start % Block32Size == 0) {
erase32Block(start);
erased_len += Block32Size;
start += Block32Size;
continue;
}
if(remaining >= SectorSize && start % SectorSize == 0) {
eraseSector(start);
erased_len += SectorSize;
start += SectorSize;
continue;
}
// Should never get here
}
return true;
}

View File

@ -17,8 +17,12 @@ public:
bool isPresent();
void read(uint32_t address, uint16_t length, void *dest);
bool write(uint32_t address, uint16_t length, uint8_t *src);
bool write(uint32_t address, uint16_t length, void *src);
bool eraseChip();
bool eraseSector(uint32_t address);
bool erase32Block(uint32_t address);
bool erase64Block(uint32_t address);
bool eraseRange(uint32_t start, uint32_t len);
// Starts the reading process without actually reading any bytes
void initiateRead(uint32_t address);
const SPI_HandleTypeDef* const getSpi() const {
@ -26,6 +30,9 @@ public:
}
private:
static constexpr uint32_t SectorSize = 4096;
static constexpr uint32_t Block32Size = 32768;
static constexpr uint32_t Block64Size = 65536;
void CS(bool high) {
if(high) {
CS_gpio->BSRR = CS_pin;

View File

@ -2,6 +2,7 @@
#include "Protocol.hpp"
#include <cstring>
#include "HW_HAL.hpp"
#define LOG_LEVEL LOG_LEVEL_INFO
#define LOG_MODULE "FW"
@ -19,11 +20,11 @@ using Header = struct {
uint32_t crc;
} __attribute__((packed));
Firmware::Info Firmware::GetFlashContentInfo(Flash *f) {
Firmware::Info Firmware::GetFlashContentInfo() {
Info ret;
memset(&ret, 0, sizeof(ret));
Header h;
f->read(0, sizeof(h), &h);
HWHAL::flash.read(0, sizeof(h), &h);
// sanity check values
if (memcmp(&h.magic, "VNA!",
4) || h.FPGA_start == UINT32_MAX || h.FPGA_size > FPGA_MAXSIZE
@ -40,7 +41,7 @@ Firmware::Info Firmware::GetFlashContentInfo(Flash *f) {
if (h.FPGA_size + h.CPU_size - checked_size < read_size) {
read_size = h.FPGA_size + h.CPU_size - checked_size;
}
f->read(h.FPGA_start + checked_size, read_size, buf);
HWHAL::flash.read(h.FPGA_start + checked_size, read_size, buf);
crc = Protocol::CRC32(crc, buf, read_size);
checked_size += read_size;
}
@ -55,7 +56,7 @@ Firmware::Info Firmware::GetFlashContentInfo(Flash *f) {
if (h.CPU_size - checked_size < read_size) {
read_size = h.CPU_size - checked_size;
}
f->read(h.CPU_start + checked_size, read_size, buf);
HWHAL::flash.read(h.CPU_start + checked_size, read_size, buf);
if(memcmp(buf, (void*)(0x8000000+checked_size), read_size)) {
LOG_INFO("Difference to CPU firmware in external FLASH detected, update required");
ret.CPU_need_update = true;
@ -158,7 +159,7 @@ static void copy_flash(uint32_t size, SPI_TypeDef *spi) {
}
}
void Firmware::PerformUpdate(Flash *f, Info info) {
void Firmware::PerformUpdate(Info info) {
if(!info.valid) {
LOG_ERR("Invalid firmware data, not performing update");
return;
@ -174,8 +175,8 @@ void Firmware::PerformUpdate(Flash *f, Info info) {
FLASH_WaitForLastOperation(50000);
// Initiate readback from flash at CPU firmware start address
f->initiateRead(info.CPU_image_address);
HWHAL::flash.initiateRead(info.CPU_image_address);
copy_flash(info.CPU_image_size, f->getSpi()->Instance);
copy_flash(info.CPU_image_size, HWHAL::flash.getSpi()->Instance);
__builtin_unreachable();
}

View File

@ -12,6 +12,8 @@
namespace Firmware {
static constexpr uint32_t maxSize = 1048576;
using Info = struct info {
uint32_t FPGA_bitstream_address;
uint32_t FPGA_bitstream_size;
@ -21,8 +23,8 @@ using Info = struct info {
bool CPU_need_update;
};
Info GetFlashContentInfo(Flash *f);
void PerformUpdate(Flash *f, Info info);
Info GetFlashContentInfo();
void PerformUpdate(Info info);
}

View File

@ -3,3 +3,5 @@
Si5351C HWHAL::Si5351 = Si5351C(&hi2c2, 26000000);
MAX2871 HWHAL::Source = MAX2871(&hspi1, FPGA_CS_GPIO_Port, FPGA_CS_Pin, nullptr, 0, nullptr, 0, nullptr, 0, GPIOA, GPIO_PIN_6);
MAX2871 HWHAL::LO1 = MAX2871(&hspi1, FPGA_CS_GPIO_Port, FPGA_CS_Pin, nullptr, 0, nullptr, 0, nullptr, 0, GPIOA, GPIO_PIN_6);
extern SPI_HandleTypeDef hspi1;
Flash HWHAL::flash = Flash(&hspi1, FLASH_CS_GPIO_Port, FLASH_CS_Pin);

View File

@ -4,6 +4,7 @@
#include "Si5351C.hpp"
#include "max2871.hpp"
#include "main.h"
#include "Flash.hpp"
extern I2C_HandleTypeDef hi2c2;
extern SPI_HandleTypeDef hspi1;
@ -13,6 +14,7 @@ namespace HWHAL {
extern Si5351C Si5351;
extern MAX2871 Source;
extern MAX2871 LO1;
extern Flash flash;
// Mapping of the Si5351 channels to PLLs/Mixers
namespace SiChannel {