mirror of
https://github.com/jankae/LibreVNA.git
synced 2026-04-21 06:13:41 +00:00
proof-of-concept spectrum analyzer mode
This commit is contained in:
parent
76875c2316
commit
38e73365df
33 changed files with 942 additions and 82 deletions
|
|
@ -16,6 +16,7 @@
|
|||
#include "Hardware.hpp"
|
||||
#include "Manual.hpp"
|
||||
#include "Generator.hpp"
|
||||
#include "SpectrumAnalyzer.hpp"
|
||||
|
||||
#define LOG_LEVEL LOG_LEVEL_INFO
|
||||
#define LOG_MODULE "App"
|
||||
|
|
@ -37,6 +38,7 @@ static Flash flash = Flash(&hspi1, FLASH_CS_GPIO_Port, FLASH_CS_Pin);
|
|||
|
||||
#define FLAG_USB_PACKET 0x01
|
||||
#define FLAG_DATAPOINT 0x02
|
||||
#define FLAG_WORK_REQUIRED 0x04
|
||||
|
||||
static void VNACallback(Protocol::Datapoint res) {
|
||||
result = res;
|
||||
|
|
@ -50,6 +52,11 @@ static void USBPacketReceived(Protocol::PacketInfo p) {
|
|||
xTaskNotifyFromISR(handle, FLAG_USB_PACKET, eSetBits, &woken);
|
||||
portYIELD_FROM_ISR(woken);
|
||||
}
|
||||
static void HardwareWorkRequired() {
|
||||
BaseType_t woken = false;
|
||||
xTaskNotifyFromISR(handle, FLAG_WORK_REQUIRED, eSetBits, &woken);
|
||||
portYIELD_FROM_ISR(woken);
|
||||
}
|
||||
|
||||
void App_Start() {
|
||||
handle = xTaskGetCurrentTaskHandle();
|
||||
|
|
@ -90,7 +97,7 @@ void App_Start() {
|
|||
EN_6V_GPIO_Port->BSRR = EN_6V_Pin;
|
||||
#endif
|
||||
|
||||
if (!HW::Init()) {
|
||||
if (!HW::Init(HardwareWorkRequired)) {
|
||||
LOG_CRIT("Initialization failed, unable to start");
|
||||
LED::Error(4);
|
||||
}
|
||||
|
|
@ -108,6 +115,9 @@ void App_Start() {
|
|||
uint32_t notification;
|
||||
if(xTaskNotifyWait(0x00, UINT32_MAX, ¬ification, 100) == pdPASS) {
|
||||
// something happened
|
||||
if(notification & FLAG_WORK_REQUIRED) {
|
||||
HW::Work();
|
||||
}
|
||||
if(notification & FLAG_DATAPOINT) {
|
||||
Protocol::PacketInfo packet;
|
||||
packet.type = Protocol::PacketType::Datapoint;
|
||||
|
|
@ -143,6 +153,12 @@ void App_Start() {
|
|||
Generator::Setup(packet.generator);
|
||||
Communication::SendWithoutPayload(Protocol::PacketType::Ack);
|
||||
break;
|
||||
case Protocol::PacketType::SpectrumAnalyzerSettings:
|
||||
sweepActive = false;
|
||||
LOG_INFO("Updating spectrum analyzer settings");
|
||||
SA::Setup(packet.spectrumSettings);
|
||||
Communication::SendWithoutPayload(Protocol::PacketType::Ack);
|
||||
break;
|
||||
#ifdef HAS_FLASH
|
||||
case Protocol::PacketType::ClearFlash:
|
||||
HW::SetMode(HW::Mode::Idle);
|
||||
|
|
@ -191,7 +207,7 @@ void App_Start() {
|
|||
LOG_WARN("Timed out waiting for point, last received point was %d (Status 0x%04x)", result.pointNum, FPGA::GetStatus());
|
||||
FPGA::AbortSweep();
|
||||
// restart the current sweep
|
||||
HW::Init();
|
||||
HW::Init(HardwareWorkRequired);
|
||||
HW::Ref::update();
|
||||
VNA::Setup(settings, VNACallback);
|
||||
sweepActive = true;
|
||||
|
|
|
|||
|
|
@ -358,6 +358,50 @@ static int16_t EncodeManualControl(Protocol::ManualControl d, uint8_t *buf,
|
|||
return e.getSize();
|
||||
}
|
||||
|
||||
static Protocol::SpectrumAnalyzerSettings DecodeSpectrumAnalyzerSettings(uint8_t *buf) {
|
||||
Protocol::SpectrumAnalyzerSettings d;
|
||||
Decoder e(buf);
|
||||
e.get<uint64_t>(d.f_start);
|
||||
e.get<uint64_t>(d.f_stop);
|
||||
e.get<uint32_t>(d.RBW);
|
||||
e.get<uint16_t>(d.pointNum);
|
||||
d.WindowType = e.getBits(2);
|
||||
d.SignalID = e.getBits(1);
|
||||
d.Detector = e.getBits(3);
|
||||
return d;
|
||||
}
|
||||
static int16_t EncodeSpectrumAnalyzerSettings(Protocol::SpectrumAnalyzerSettings d, uint8_t *buf,
|
||||
uint16_t bufSize) {
|
||||
Encoder e(buf, bufSize);
|
||||
e.add<uint64_t>(d.f_start);
|
||||
e.add<uint64_t>(d.f_stop);
|
||||
e.add<uint32_t>(d.RBW);
|
||||
e.add<uint16_t>(d.pointNum);
|
||||
e.addBits(d.WindowType, 2);
|
||||
e.addBits(d.SignalID, 1);
|
||||
e.addBits(d.Detector, 3);
|
||||
return e.getSize();
|
||||
}
|
||||
|
||||
static Protocol::SpectrumAnalyzerResult DecodeSpectrumAnalyzerResult(uint8_t *buf) {
|
||||
Protocol::SpectrumAnalyzerResult d;
|
||||
Decoder e(buf);
|
||||
e.get<float>(d.port1);
|
||||
e.get<float>(d.port2);
|
||||
e.get<uint64_t>(d.frequency);
|
||||
e.get<uint16_t>(d.pointNum);
|
||||
return d;
|
||||
}
|
||||
static int16_t EncodeSpectrumAnalyzerResult(Protocol::SpectrumAnalyzerResult d, uint8_t *buf,
|
||||
uint16_t bufSize) {
|
||||
Encoder e(buf, bufSize);
|
||||
e.add<float>(d.port1);
|
||||
e.add<float>(d.port2);
|
||||
e.add<uint64_t>(d.frequency);
|
||||
e.add<uint16_t>(d.pointNum);
|
||||
return e.getSize();
|
||||
}
|
||||
|
||||
static Protocol::FirmwarePacket DecodeFirmwarePacket(uint8_t *buf) {
|
||||
Protocol::FirmwarePacket d;
|
||||
// simple packet format, memcpy is faster than using the decoder
|
||||
|
|
@ -446,6 +490,12 @@ uint16_t Protocol::DecodeBuffer(uint8_t *buf, uint16_t len, PacketInfo *info) {
|
|||
case PacketType::Generator:
|
||||
info->generator = DecodeGeneratorSettings(&data[4]);
|
||||
break;
|
||||
case PacketType::SpectrumAnalyzerSettings:
|
||||
info->spectrumSettings = DecodeSpectrumAnalyzerSettings(&data[4]);
|
||||
break;
|
||||
case PacketType::SpectrumAnalyzerResult:
|
||||
info->spectrumResult = DecodeSpectrumAnalyzerResult(&data[4]);
|
||||
break;
|
||||
case PacketType::Ack:
|
||||
case PacketType::PerformFirmwareUpdate:
|
||||
case PacketType::ClearFlash:
|
||||
|
|
@ -486,6 +536,12 @@ uint16_t Protocol::EncodePacket(PacketInfo packet, uint8_t *dest, uint16_t dests
|
|||
case PacketType::Generator:
|
||||
payload_size = EncodeGeneratorSettings(packet.generator, &dest[4], destsize - 8);
|
||||
break;
|
||||
case PacketType::SpectrumAnalyzerSettings:
|
||||
payload_size = EncodeSpectrumAnalyzerSettings(packet.spectrumSettings, &dest[4], destsize - 8);
|
||||
break;
|
||||
case PacketType::SpectrumAnalyzerResult:
|
||||
payload_size = EncodeSpectrumAnalyzerResult(packet.spectrumResult, &dest[4], destsize - 8);
|
||||
break;
|
||||
case PacketType::Ack:
|
||||
case PacketType::PerformFirmwareUpdate:
|
||||
case PacketType::ClearFlash:
|
||||
|
|
|
|||
|
|
@ -102,10 +102,18 @@ using SpectrumAnalyzerSettings = struct _spectrumAnalyzerSettings {
|
|||
uint64_t f_start;
|
||||
uint64_t f_stop;
|
||||
uint32_t RBW;
|
||||
uint16_t pointNum;
|
||||
uint8_t WindowType :2;
|
||||
uint8_t SignalID :1;
|
||||
uint8_t Detector :3;
|
||||
};
|
||||
|
||||
using SpectrumAnalyzerResult = struct _spectrumAnalyzerResult {
|
||||
float port1;
|
||||
float port2;
|
||||
uint64_t frequency;
|
||||
uint16_t pointNum;
|
||||
};
|
||||
|
||||
static constexpr uint16_t FirmwareChunkSize = 256;
|
||||
using FirmwarePacket = struct _firmwarePacket {
|
||||
|
|
@ -127,6 +135,8 @@ enum class PacketType : uint8_t {
|
|||
Nack = 10,
|
||||
Reference = 11,
|
||||
Generator = 12,
|
||||
SpectrumAnalyzerSettings = 13,
|
||||
SpectrumAnalyzerResult = 14,
|
||||
};
|
||||
|
||||
using PacketInfo = struct _packetinfo {
|
||||
|
|
@ -138,8 +148,10 @@ using PacketInfo = struct _packetinfo {
|
|||
GeneratorSettings generator;
|
||||
DeviceInfo info;
|
||||
ManualControl manual;
|
||||
ManualStatus status;
|
||||
FirmwarePacket firmware;
|
||||
ManualStatus status;
|
||||
SpectrumAnalyzerSettings spectrumSettings;
|
||||
SpectrumAnalyzerResult spectrumResult;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include "delay.hpp"
|
||||
#include <cmath>
|
||||
#define LOG_LEVEL LOG_LEVEL_WARN
|
||||
#define LOG_LEVEL LOG_LEVEL_ERR
|
||||
#define LOG_MODULE "MAX2871"
|
||||
#include "Log.h"
|
||||
|
||||
|
|
@ -198,7 +198,7 @@ bool MAX2871::SetFrequency(uint64_t f) {
|
|||
approx.num, approx.denom, abs(rem_f - rem_approx));
|
||||
}
|
||||
|
||||
uint64_t f_set = (uint64_t) N * f_PFD + (f_PFD * approx.num) / approx.denom;
|
||||
uint64_t f_set = (uint64_t) N * f_PFD + rem_approx;
|
||||
f_set /= (1UL << div);
|
||||
|
||||
// write values to registers
|
||||
|
|
|
|||
|
|
@ -57,6 +57,9 @@ public:
|
|||
uint32_t* GetRegisters() {
|
||||
return regs;
|
||||
}
|
||||
uint64_t GetActualFrequency() {
|
||||
return outputFrequency;
|
||||
}
|
||||
private:
|
||||
static constexpr uint64_t MaxFreq = 6100000000; // 6GHz according to datasheet, but slight overclocking is possible
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "Exti.hpp"
|
||||
#include "VNA.hpp"
|
||||
#include "Manual.hpp"
|
||||
#include "SpectrumAnalyzer.hpp"
|
||||
|
||||
#define LOG_LEVEL LOG_LEVEL_INFO
|
||||
#define LOG_MODULE "HW"
|
||||
|
|
@ -15,8 +16,6 @@ static uint32_t extOutFreq = 0;
|
|||
static bool extRefInUse = false;
|
||||
HW::Mode activeMode;
|
||||
|
||||
static constexpr uint32_t IF1 = 60100000;
|
||||
static constexpr uint32_t IF2 = 250000;
|
||||
static Protocol::ReferenceSettings ref;
|
||||
|
||||
using namespace HWHAL;
|
||||
|
|
@ -30,6 +29,9 @@ static void HaltedCallback() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static HW::WorkRequest requestWork;
|
||||
|
||||
static void ReadComplete(FPGA::SamplingResult result) {
|
||||
bool needs_work = false;
|
||||
switch(activeMode) {
|
||||
|
|
@ -39,11 +41,14 @@ static void ReadComplete(FPGA::SamplingResult result) {
|
|||
case HW::Mode::Manual:
|
||||
needs_work = Manual::MeasurementDone(result);
|
||||
break;
|
||||
case HW::Mode::SA:
|
||||
needs_work = SA::MeasurementDone(result);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(needs_work) {
|
||||
HAL_NVIC_SetPendingIRQ(COMP4_IRQn);
|
||||
if(needs_work && requestWork) {
|
||||
requestWork();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -51,9 +56,7 @@ static void FPGA_Interrupt(void*) {
|
|||
FPGA::InitiateSampleRead(ReadComplete);
|
||||
}
|
||||
|
||||
/* low priority interrupt to handle additional workload after FPGA interrupt */
|
||||
extern "C" {
|
||||
void COMP4_IRQHandler(void) {
|
||||
void HW::Work() {
|
||||
switch(activeMode) {
|
||||
case HW::Mode::VNA:
|
||||
VNA::Work();
|
||||
|
|
@ -61,16 +64,17 @@ void COMP4_IRQHandler(void) {
|
|||
case HW::Mode::Manual:
|
||||
Manual::Work();
|
||||
break;
|
||||
case HW::Mode::SA:
|
||||
SA::Work();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HW::Init() {
|
||||
bool HW::Init(WorkRequest wr) {
|
||||
requestWork = wr;
|
||||
LOG_DEBUG("Initializing...");
|
||||
HAL_NVIC_SetPriority(COMP4_IRQn, 15, 0);
|
||||
HAL_NVIC_EnableIRQ(COMP4_IRQn);
|
||||
|
||||
activeMode = Mode::Idle;
|
||||
|
||||
|
|
@ -179,10 +183,9 @@ void HW::SetMode(Mode mode) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
if(activeMode == Mode::Manual && mode != Mode::Idle) {
|
||||
// do a full initialization when switching from manual mode to anything else than idle
|
||||
// (making sure that any changes made in manual mode are reverted)
|
||||
HW::Init();
|
||||
if(mode != Mode::Idle && activeMode != Mode::Idle) {
|
||||
// do a full initialization when switching directly between modes
|
||||
HW::Init(requestWork);
|
||||
}
|
||||
SetIdle();
|
||||
activeMode = mode;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@
|
|||
|
||||
namespace HW {
|
||||
|
||||
static constexpr uint32_t ADCSamplerate = 914000;
|
||||
static constexpr uint32_t IF1 = 60100000;
|
||||
static constexpr uint32_t IF2 = 250000;
|
||||
|
||||
enum class Mode {
|
||||
Idle,
|
||||
Manual,
|
||||
|
|
@ -12,9 +16,12 @@ enum class Mode {
|
|||
SA,
|
||||
};
|
||||
|
||||
bool Init();
|
||||
using WorkRequest = void (*)(void);
|
||||
|
||||
bool Init(WorkRequest wr);
|
||||
void SetMode(Mode mode);
|
||||
void SetIdle();
|
||||
void Work();
|
||||
|
||||
bool GetTemps(uint8_t *source, uint8_t *lo);
|
||||
void fillDeviceInfo(Protocol::DeviceInfo *info);
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ void Manual::Work() {
|
|||
}
|
||||
Protocol::PacketInfo p;
|
||||
p.type = Protocol::PacketType::Status;
|
||||
p.status = status;
|
||||
uint16_t isr_flags = FPGA::GetStatus();
|
||||
if (!(isr_flags & 0x0002)) {
|
||||
p.status.source_locked = 1;
|
||||
|
|
|
|||
207
Software/VNA_embedded/Application/SpectrumAnalyzer.cpp
Normal file
207
Software/VNA_embedded/Application/SpectrumAnalyzer.cpp
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
#include "SpectrumAnalyzer.hpp"
|
||||
#include "Hardware.hpp"
|
||||
#include "HW_HAL.hpp"
|
||||
#include <complex.h>
|
||||
#include <limits>
|
||||
#include "Communication.h"
|
||||
|
||||
#define LOG_LEVEL LOG_LEVEL_DEBUG
|
||||
#define LOG_MODULE "SA"
|
||||
#include "Log.h"
|
||||
|
||||
static Protocol::SpectrumAnalyzerSettings s;
|
||||
static uint32_t pointCnt;
|
||||
static uint32_t points;
|
||||
static uint32_t binSize;
|
||||
static uint8_t signalIDstep;
|
||||
static uint32_t sampleNum;
|
||||
static Protocol::PacketInfo p;
|
||||
static bool active = false;
|
||||
|
||||
static float port1Measurement, port2Measurement;
|
||||
|
||||
using namespace HWHAL;
|
||||
|
||||
static void StartNextSample() {
|
||||
uint64_t freq = s.f_start + (s.f_stop - s.f_start) * pointCnt / (points - 1);
|
||||
uint64_t LO1freq;
|
||||
uint32_t LO2freq;
|
||||
switch(signalIDstep) {
|
||||
case 0:
|
||||
default:
|
||||
// reset minimum amplitudes in first signal ID step
|
||||
port1Measurement = std::numeric_limits<float>::max();
|
||||
port2Measurement = std::numeric_limits<float>::max();
|
||||
LO1freq = freq + HW::IF1;
|
||||
LO2freq = HW::IF1 - HW::IF2;
|
||||
break;
|
||||
case 1:
|
||||
LO1freq = freq - HW::IF1;
|
||||
LO2freq = HW::IF1 - HW::IF2;
|
||||
break;
|
||||
case 2:
|
||||
LO1freq = freq + HW::IF1;
|
||||
LO2freq = HW::IF1 + HW::IF2;
|
||||
break;
|
||||
case 3:
|
||||
LO1freq = freq - HW::IF1;
|
||||
LO2freq = HW::IF1 + HW::IF2;
|
||||
break;
|
||||
}
|
||||
LO1.SetFrequency(LO1freq);
|
||||
// LO1 is not able to reach all frequencies with the required precision, adjust LO2 to account for deviation
|
||||
int32_t LO1deviation = (int64_t) LO1.GetActualFrequency() - LO1freq;
|
||||
LO2freq += LO1deviation;
|
||||
// Adjust LO2 PLL
|
||||
// Generate second LO with Si5351
|
||||
Si5351.SetCLK(SiChannel::Port1LO2, LO2freq, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
|
||||
Si5351.SetCLK(SiChannel::Port2LO2, LO2freq, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
|
||||
// Configure the sampling in the FPGA
|
||||
FPGA::WriteSweepConfig(0, 0, Source.GetRegisters(), LO1.GetRegisters(), 0,
|
||||
0, FPGA::SettlingTime::us20, FPGA::Samples::SPPRegister, 0,
|
||||
FPGA::LowpassFilter::M947);
|
||||
|
||||
FPGA::StartSweep();
|
||||
}
|
||||
|
||||
void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) {
|
||||
LOG_DEBUG("Setting up...");
|
||||
s = settings;
|
||||
HW::SetMode(HW::Mode::SA);
|
||||
FPGA::AbortSweep();
|
||||
FPGA::SetMode(FPGA::Mode::FPGA);
|
||||
// in almost all cases a full sweep requires more points than the FPGA can handle at a time
|
||||
// individually start each point and do the sweep in the uC
|
||||
FPGA::SetNumberOfPoints(1);
|
||||
// calculate amount of required points
|
||||
points = (s.f_stop - s.f_start) / s.RBW;
|
||||
// adjust to integer multiple of requested result points (in order to have the same amount of measurements in each bin)
|
||||
points += s.pointNum - points % s.pointNum;
|
||||
binSize = points / s.pointNum;
|
||||
LOG_DEBUG("%u displayed points, resulting in %lu points and bins of size %u", s.pointNum, points, binSize);
|
||||
// calculate required samples per measurement for requested RBW
|
||||
// see https://www.tek.com/blog/window-functions-spectrum-analyzers for window factors
|
||||
constexpr float window_factors[4] = {0.89f, 2.23f, 1.44f, 3.77f};
|
||||
sampleNum = HW::ADCSamplerate * window_factors[s.WindowType] / s.RBW;
|
||||
// round up to next multiple of 128
|
||||
sampleNum += 128 - sampleNum%128;
|
||||
FPGA::SetSamplesPerPoint(sampleNum);
|
||||
// set initial state
|
||||
pointCnt = 0;
|
||||
// enable the required hardware resources
|
||||
Si5351.Enable(SiChannel::Port1LO2);
|
||||
Si5351.Enable(SiChannel::Port2LO2);
|
||||
FPGA::SetWindow((FPGA::Window) s.WindowType);
|
||||
FPGA::Enable(FPGA::Periphery::LO1Chip);
|
||||
FPGA::Enable(FPGA::Periphery::LO1RF);
|
||||
FPGA::Enable(FPGA::Periphery::ExcitePort1);
|
||||
FPGA::Enable(FPGA::Periphery::Port1Mixer);
|
||||
FPGA::Enable(FPGA::Periphery::Port2Mixer);
|
||||
active = true;
|
||||
StartNextSample();
|
||||
}
|
||||
|
||||
bool SA::MeasurementDone(FPGA::SamplingResult result) {
|
||||
if(!active) {
|
||||
return false;
|
||||
}
|
||||
float port1 = abs(std::complex<float>(result.P1I, result.P1Q))/sampleNum;
|
||||
float port2 = abs(std::complex<float>(result.P2I, result.P2Q))/sampleNum;
|
||||
if(port1 < port1Measurement) {
|
||||
port1Measurement = port1;
|
||||
}
|
||||
if(port2 < port2Measurement) {
|
||||
port2Measurement = port2;
|
||||
}
|
||||
// trigger work function
|
||||
return true;
|
||||
}
|
||||
|
||||
void SA::Work() {
|
||||
if(!active) {
|
||||
return;
|
||||
}
|
||||
if(!s.SignalID || signalIDstep >= 3) {
|
||||
// this measurement point is done, handle result according to detector
|
||||
uint16_t binIndex = pointCnt / binSize;
|
||||
uint32_t pointInBin = pointCnt % binSize;
|
||||
bool lastPointInBin = pointInBin >= binSize - 1;
|
||||
auto det = (Detector) s.Detector;
|
||||
if(det == Detector::Normal) {
|
||||
det = binIndex & 0x01 ? Detector::PosPeak : Detector::NegPeak;
|
||||
}
|
||||
switch(det) {
|
||||
case Detector::PosPeak:
|
||||
if(pointInBin == 0) {
|
||||
p.spectrumResult.port1 = std::numeric_limits<float>::min();
|
||||
p.spectrumResult.port2 = std::numeric_limits<float>::min();
|
||||
}
|
||||
if(port1Measurement > p.spectrumResult.port1) {
|
||||
p.spectrumResult.port1 = port1Measurement;
|
||||
}
|
||||
if(port2Measurement > p.spectrumResult.port2) {
|
||||
p.spectrumResult.port2 = port2Measurement;
|
||||
}
|
||||
break;
|
||||
case Detector::NegPeak:
|
||||
if(pointInBin == 0) {
|
||||
p.spectrumResult.port1 = std::numeric_limits<float>::max();
|
||||
p.spectrumResult.port2 = std::numeric_limits<float>::max();
|
||||
}
|
||||
if(port1Measurement < p.spectrumResult.port1) {
|
||||
p.spectrumResult.port1 = port1Measurement;
|
||||
}
|
||||
if(port2Measurement < p.spectrumResult.port2) {
|
||||
p.spectrumResult.port2 = port2Measurement;
|
||||
}
|
||||
break;
|
||||
case Detector::Sample:
|
||||
if(pointInBin <= binSize / 2) {
|
||||
// still in first half of bin, simply overwrite
|
||||
p.spectrumResult.port1 = port1Measurement;
|
||||
p.spectrumResult.port2 = port2Measurement;
|
||||
}
|
||||
break;
|
||||
case Detector::Average:
|
||||
if(pointInBin == 0) {
|
||||
p.spectrumResult.port1 = 0;
|
||||
p.spectrumResult.port2 = 0;
|
||||
}
|
||||
p.spectrumResult.port1 += port1Measurement;
|
||||
p.spectrumResult.port2 += port2Measurement;
|
||||
if(lastPointInBin) {
|
||||
// calculate average
|
||||
p.spectrumResult.port1 /= binSize;
|
||||
p.spectrumResult.port2 /= binSize;
|
||||
}
|
||||
break;
|
||||
case Detector::Normal:
|
||||
// nothing to do, normal detector handled by PosPeak or NegPeak in each sample
|
||||
break;
|
||||
}
|
||||
if(lastPointInBin) {
|
||||
// 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);
|
||||
Communication::Send(p);
|
||||
}
|
||||
// setup for next step
|
||||
signalIDstep = 0;
|
||||
if(pointCnt < points - 1) {
|
||||
pointCnt++;
|
||||
} else {
|
||||
pointCnt = 0;
|
||||
}
|
||||
} else {
|
||||
// more measurements required for signal ID
|
||||
signalIDstep++;
|
||||
}
|
||||
StartNextSample();
|
||||
}
|
||||
|
||||
void SA::Stop() {
|
||||
active = false;
|
||||
FPGA::AbortSweep();
|
||||
}
|
||||
21
Software/VNA_embedded/Application/SpectrumAnalyzer.hpp
Normal file
21
Software/VNA_embedded/Application/SpectrumAnalyzer.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "Protocol.hpp"
|
||||
#include "FPGA/FPGA.hpp"
|
||||
|
||||
namespace SA {
|
||||
|
||||
enum class Detector {
|
||||
PosPeak = 0x00,
|
||||
NegPeak = 0x01,
|
||||
Sample = 0x02,
|
||||
Normal = 0x03,
|
||||
Average = 0x04,
|
||||
};
|
||||
|
||||
void Setup(Protocol::SpectrumAnalyzerSettings settings);
|
||||
bool MeasurementDone(FPGA::SamplingResult result);
|
||||
void Work();
|
||||
void Stop();
|
||||
|
||||
}
|
||||
|
|
@ -55,7 +55,7 @@ bool VNA::Setup(Protocol::SweepSettings s, SweepCallback cb) {
|
|||
uint16_t points = settings.points <= FPGA::MaxPoints ? settings.points : FPGA::MaxPoints;
|
||||
// Configure sweep
|
||||
FPGA::SetNumberOfPoints(points);
|
||||
uint32_t samplesPerPoint = (1000000 / s.if_bandwidth);
|
||||
uint32_t samplesPerPoint = (HW::ADCSamplerate / s.if_bandwidth);
|
||||
// round up to next multiple of 128 (128 samples are spread across 35 IF2 periods)
|
||||
samplesPerPoint = ((uint32_t) ((samplesPerPoint + 127) / 128)) * 128;
|
||||
// has to be one less than actual number of samples
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue