mirror of
https://github.com/jankae/LibreVNA.git
synced 2026-04-04 22:17:31 +00:00
Working source and receiver calibration
This commit is contained in:
parent
875f3b0170
commit
026fffd588
23 changed files with 722 additions and 68 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -223,7 +223,8 @@ static Protocol::GeneratorSettings DecodeGeneratorSettings(uint8_t *buf) {
|
|||
Decoder e(buf);
|
||||
e.get<uint64_t>(d.frequency);
|
||||
e.get<int16_t>(d.cdbm_level);
|
||||
e.get<uint8_t>(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<uint64_t>(d.frequency);
|
||||
e.add<int16_t>(d.cdbm_level);
|
||||
e.add<uint8_t>(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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <cstdint>
|
||||
#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 {
|
||||
|
|
|
|||
|
|
@ -219,8 +219,8 @@ bool SA::MeasurementDone(const FPGA::SamplingResult &result) {
|
|||
port1 = dft.P1;
|
||||
port2 = dft.P2;
|
||||
} else {
|
||||
port1 = abs(std::complex<float>(result.P1I, result.P1Q));
|
||||
port2 = abs(std::complex<float>(result.P2I, result.P2Q));
|
||||
port1 = fabs(std::complex<float>(result.P1I, result.P1Q));
|
||||
port2 = fabs(std::complex<float>(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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue