Working source and receiver calibration

This commit is contained in:
Jan Käberich 2020-11-17 23:03:13 +01:00
parent 875f3b0170
commit 026fffd588
23 changed files with 722 additions and 68 deletions

View file

@ -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;

View file

@ -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();
}

View file

@ -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 {

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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 {

View file

@ -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);
}
}