mirror of
https://github.com/jankae/LibreVNA.git
synced 2026-03-06 13:23:49 +01:00
Preserve phase information in spectrum analyzer mode
This commit is contained in:
parent
787f0d0223
commit
963578cc9f
|
|
@ -425,7 +425,7 @@ void AmplitudeCalDialog::AutomaticMeasurementDialog()
|
|||
|
||||
void AmplitudeCalDialog::ReceivedMeasurement(Protocol::SpectrumAnalyzerResult res)
|
||||
{
|
||||
MeasurementResult m = {.port1 = Util::SparamTodB(res.port1), .port2 = Util::SparamTodB(res.port2)};
|
||||
MeasurementResult m = {.port1 = Util::SparamTodB(complex<double>(res.real_port1, res.imag_port1)), .port2 = Util::SparamTodB(complex<double>(res.real_port2, res.imag_port2))};
|
||||
sweepMeasurements.push_back(m);
|
||||
if(res.pointNum == automaticSweepPoints - 1) {
|
||||
// sweep finished, find maximum for each port
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ HEADERS += \
|
|||
Device/manualcontroldialog.h \
|
||||
Generator/generator.h \
|
||||
Generator/signalgenwidget.h \
|
||||
SpectrumAnalyzer/sadata.h \
|
||||
SpectrumAnalyzer/spectrumanalyzer.h \
|
||||
SpectrumAnalyzer/tracewidgetsa.h \
|
||||
Tools/eseries.h \
|
||||
|
|
|
|||
24
Software/PC_Application/SpectrumAnalyzer/sadata.h
Normal file
24
Software/PC_Application/SpectrumAnalyzer/sadata.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef SADATA_H
|
||||
#define SADATA_H
|
||||
|
||||
#include "Tools/parameters.h"
|
||||
|
||||
#include <complex>
|
||||
|
||||
class SAData {
|
||||
public:
|
||||
SAData() = default;
|
||||
SAData(const Protocol::SpectrumAnalyzerResult &d) {
|
||||
port1 = std::complex<double>(d.real_port1, d.imag_port1);
|
||||
port2 = std::complex<double>(d.real_port2, d.imag_port2);
|
||||
frequency = d.frequency;
|
||||
time = (double) d.us / 1000000.0;
|
||||
pointNum = d.pointNum;
|
||||
}
|
||||
double frequency;
|
||||
double time;
|
||||
std::complex<double> port1, port2;
|
||||
unsigned int pointNum;
|
||||
};
|
||||
|
||||
#endif // SADATA_H
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
#include "Device/firmwareupdatedialog.h"
|
||||
#include "preferences.h"
|
||||
#include "Generator/signalgenwidget.h"
|
||||
#include "sadata.h"
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QDesktopWidget>
|
||||
|
|
@ -330,6 +331,7 @@ nlohmann::json SpectrumAnalyzer::toJSON()
|
|||
acq["detector"] = DetectorToString((Detector) settings.Detector).toStdString();
|
||||
acq["signal ID"] = settings.SignalID ? true : false;
|
||||
sweep["acquisition"] = acq;
|
||||
sweep["averages"] = averages;
|
||||
nlohmann::json tracking;
|
||||
tracking["enabled"] = settings.trackingGenerator ? true : false;
|
||||
tracking["port"] = settings.trackingGeneratorPort ? 2 : 1;
|
||||
|
|
@ -425,6 +427,7 @@ void SpectrumAnalyzer::fromJSON(nlohmann::json j)
|
|||
EnableNormalization(false);
|
||||
}
|
||||
}
|
||||
SetAveraging(sweep.value("averages", 0));
|
||||
SetSingleSweep(sweep.value("single", singleSweep));
|
||||
}
|
||||
}
|
||||
|
|
@ -455,15 +458,17 @@ void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d)
|
|||
return;
|
||||
}
|
||||
|
||||
d = average.process(d);
|
||||
auto sd = SAData(d);
|
||||
|
||||
sd = average.process(sd);
|
||||
|
||||
if(settings.f_start == settings.f_stop) {
|
||||
// keep track of first point time
|
||||
if(d.pointNum == 0) {
|
||||
firstPointTime = d.us;
|
||||
d.us = 0;
|
||||
if(sd.pointNum == 0) {
|
||||
firstPointTime = sd.time;
|
||||
sd.time = 0;
|
||||
} else {
|
||||
d.us -= firstPointTime;
|
||||
sd.time -= firstPointTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -472,8 +477,8 @@ void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d)
|
|||
// this is the last averaging sweep, use values for normalization
|
||||
if(normalize.port1Correction.size() > 0 || d.pointNum == 0) {
|
||||
// add measurement
|
||||
normalize.port1Correction.push_back(d.port1);
|
||||
normalize.port2Correction.push_back(d.port2);
|
||||
normalize.port1Correction.push_back(abs(sd.port1));
|
||||
normalize.port2Correction.push_back(abs(sd.port2));
|
||||
if(d.pointNum == settings.pointNum - 1) {
|
||||
// this was the last point
|
||||
normalize.measuring = false;
|
||||
|
|
@ -490,14 +495,14 @@ void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d)
|
|||
}
|
||||
|
||||
if(normalize.active) {
|
||||
d.port1 /= normalize.port1Correction[d.pointNum];
|
||||
d.port2 /= normalize.port2Correction[d.pointNum];
|
||||
sd.port1 /= normalize.port1Correction[d.pointNum];
|
||||
sd.port2 /= normalize.port2Correction[d.pointNum];
|
||||
double corr = pow(10.0, normalize.Level->value() / 20.0);
|
||||
d.port1 *= corr;
|
||||
d.port2 *= corr;
|
||||
sd.port1 *= corr;
|
||||
sd.port2 *= corr;
|
||||
}
|
||||
|
||||
traceModel.addSAData(d, settings);
|
||||
traceModel.addSAData(sd, settings);
|
||||
emit dataChanged();
|
||||
if(d.pointNum == settings.pointNum - 1) {
|
||||
UpdateAverageCount();
|
||||
|
|
|
|||
|
|
@ -354,6 +354,7 @@ std::set<YAxis::Type> YAxis::getSupported(XAxis::Type type, TraceModel::DataSour
|
|||
case XAxis::Type::Frequency:
|
||||
ret.insert(YAxis::Type::Magnitude);
|
||||
ret.insert(YAxis::Type::MagnitudedBuV);
|
||||
ret.insert(YAxis::Type::Phase);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ void TraceModel::addVNAData(const VNAData& d, TraceMath::DataType datatype)
|
|||
}
|
||||
}
|
||||
|
||||
void TraceModel::addSAData(const Protocol::SpectrumAnalyzerResult& d, const Protocol::SpectrumAnalyzerSettings& settings)
|
||||
void TraceModel::addSAData(const SAData &d, const Protocol::SpectrumAnalyzerSettings& settings)
|
||||
{
|
||||
source = DataSource::SA;
|
||||
for(auto t : traces) {
|
||||
|
|
@ -260,13 +260,13 @@ void TraceModel::addSAData(const Protocol::SpectrumAnalyzerResult& d, const Prot
|
|||
if(settings.f_start == settings.f_stop) {
|
||||
// in zerospan mode, insert data by index
|
||||
index = d.pointNum;
|
||||
td.x = (double) d.us / 1000000.0;
|
||||
td.x = (double) d.time / 1000000.0;
|
||||
} else {
|
||||
td.x = d.frequency;
|
||||
}
|
||||
switch(t->liveParameter()) {
|
||||
case Trace::LiveParameter::Port1: td.y = complex<double>(d.port1, 0); break;
|
||||
case Trace::LiveParameter::Port2: td.y = complex<double>(d.port2, 0); break;
|
||||
case Trace::LiveParameter::Port1: td.y = d.port1; break;
|
||||
case Trace::LiveParameter::Port2: td.y = d.port2; break;
|
||||
default:
|
||||
// not a SA trace, skip
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "savable.h"
|
||||
#include "trace.h"
|
||||
#include "VNA/vnadata.h"
|
||||
#include "SpectrumAnalyzer/sadata.h"
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <vector>
|
||||
|
|
@ -66,7 +67,7 @@ signals:
|
|||
public slots:
|
||||
void clearLiveData();
|
||||
void addVNAData(const VNAData& d, TraceMath::DataType datatype);
|
||||
void addSAData(const Protocol::SpectrumAnalyzerResult& d, const Protocol::SpectrumAnalyzerSettings& settings);
|
||||
void addSAData(const SAData& d, const Protocol::SpectrumAnalyzerSettings& settings);
|
||||
|
||||
private:
|
||||
DataSource source;
|
||||
|
|
|
|||
|
|
@ -737,6 +737,7 @@ nlohmann::json VNA::toJSON()
|
|||
sweep["power"] = power;
|
||||
sweep["points"] = settings.npoints;
|
||||
sweep["IFBW"] = settings.bandwidth;
|
||||
sweep["averages"] = averages;
|
||||
j["sweep"] = sweep;
|
||||
|
||||
j["traces"] = traceModel.toJSON();
|
||||
|
|
@ -793,6 +794,7 @@ void VNA::fromJSON(nlohmann::json j)
|
|||
type = SweepType::Frequency;
|
||||
}
|
||||
SetSweepType(type);
|
||||
SetAveraging(sweep.value("averages", 0));
|
||||
SetSingleSweep(sweep.value("single", singleSweep));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ VNAData Averaging::process(VNAData d)
|
|||
return d;
|
||||
}
|
||||
|
||||
Protocol::SpectrumAnalyzerResult Averaging::process(Protocol::SpectrumAnalyzerResult d)
|
||||
SAData Averaging::process(SAData d)
|
||||
{
|
||||
if (d.pointNum == avg.size()) {
|
||||
// add moving average entry
|
||||
|
|
@ -123,16 +123,21 @@ Protocol::SpectrumAnalyzerResult Averaging::process(Protocol::SpectrumAnalyzerRe
|
|||
deque->pop_front();
|
||||
}
|
||||
|
||||
if(averages == 1) {
|
||||
// no need for any calculation, no averaging set
|
||||
return d;
|
||||
}
|
||||
|
||||
switch(mode) {
|
||||
case Mode::Mean: {
|
||||
// calculate average
|
||||
complex<double> sum[2];
|
||||
for(auto s : *deque) {
|
||||
sum[0] += s[0];
|
||||
sum[1] += s[1];
|
||||
sum[0] += abs(s[0]);
|
||||
sum[1] += abs(s[1]);
|
||||
}
|
||||
d.port1 = abs(sum[0] / (double) (deque->size()));
|
||||
d.port2 = abs(sum[1] / (double) (deque->size()));
|
||||
d.port1 = sum[0] / (double) (deque->size());
|
||||
d.port2 = sum[1] / (double) (deque->size());
|
||||
}
|
||||
break;
|
||||
case Mode::Median: {
|
||||
|
|
@ -143,12 +148,12 @@ Protocol::SpectrumAnalyzerResult Averaging::process(Protocol::SpectrumAnalyzerRe
|
|||
port2.reserve(size);
|
||||
for(auto d : *deque) {
|
||||
port1.insert(upper_bound(port1.begin(), port1.end(), abs(d[0])), abs(d[0]));
|
||||
port2.insert(upper_bound(port2.begin(), port2.end(), abs(d[0])), abs(d[0]));
|
||||
port2.insert(upper_bound(port2.begin(), port2.end(), abs(d[1])), abs(d[1]));
|
||||
}
|
||||
if(size & 0x01) {
|
||||
// odd number of samples
|
||||
d.port1 = port1[size / 2];
|
||||
d.port2 = port1[size / 2];
|
||||
d.port2 = port2[size / 2];
|
||||
} else {
|
||||
// even number, use average of middle samples
|
||||
d.port1 = (port1[size / 2 - 1] + port1[size / 2]) / 2;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "Device/device.h"
|
||||
#include "VNA/vnadata.h"
|
||||
#include "SpectrumAnalyzer/sadata.h"
|
||||
|
||||
#include <array>
|
||||
#include <deque>
|
||||
|
|
@ -20,7 +21,7 @@ public:
|
|||
void reset(unsigned int points);
|
||||
void setAverages(unsigned int a);
|
||||
VNAData process(VNAData d);
|
||||
Protocol::SpectrumAnalyzerResult process(Protocol::SpectrumAnalyzerResult d);
|
||||
SAData process(SAData d);
|
||||
// Returns the number of averaged sweeps. Value is incremented whenever the last point of the sweep is added.
|
||||
// Returned values are in range 0 to averages
|
||||
unsigned int getLevel();
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
namespace Protocol {
|
||||
|
||||
static constexpr uint16_t Version = 11;
|
||||
static constexpr uint16_t Version = 12;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
|
|
@ -150,8 +150,8 @@ using SpectrumAnalyzerSettings = struct _spectrumAnalyzerSettings {
|
|||
};
|
||||
|
||||
using SpectrumAnalyzerResult = struct _spectrumAnalyzerResult {
|
||||
float port1;
|
||||
float port2;
|
||||
float real_port1, imag_port1;
|
||||
float real_port2, imag_port2;
|
||||
union {
|
||||
struct {
|
||||
// for non-zero span
|
||||
|
|
|
|||
|
|
@ -452,11 +452,9 @@ FPGA::DFTResult FPGA::ReadDFTResult() {
|
|||
int64_t p1real = assembleSampleResultValue(&recv[18]);
|
||||
// LOG_INFO("DFT raw: %ld, %ld, %ld, %ld", (int32_t) p1real, (int32_t) p1imag, (int32_t) p2real, (int32_t) p2imag);
|
||||
// Log_Flush();
|
||||
auto p1 = std::complex<float>(p1real, p1imag);
|
||||
auto p2 = std::complex<float>(p2real, p2imag);
|
||||
DFTResult res;
|
||||
res.P1 = std::complex<float>(p1real, p1imag);
|
||||
res.P2 = std::complex<float>(p2real, p2imag);
|
||||
// LOG_INFO("DFT: %ld, %ld, %ld, %ld", (int32_t) p1.real(), (int32_t) p1.imag(), (int32_t) p2.real(), (int32_t) p2.imag());
|
||||
res.P1 = std::abs(p1);
|
||||
res.P2 = std::abs(p2);
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <complex>
|
||||
#include "Flash.hpp"
|
||||
#include "max2871.hpp"
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ using SamplingResult = struct _samplingresult {
|
|||
};
|
||||
|
||||
using DFTResult = struct _dftresult {
|
||||
float P1, P2;
|
||||
std::complex<float> P1, P2;
|
||||
};
|
||||
|
||||
using ADCLimits = struct _adclimits {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ static uint8_t *USBD_Class_GetDeviceQualifierDescriptor (uint16_t *length);
|
|||
|
||||
static usbd_recv_callback_t cb;
|
||||
static uint8_t usb_receive_buffer[1024];
|
||||
static uint8_t usb_transmit_fifo[8192];
|
||||
static uint8_t usb_transmit_fifo[6144];
|
||||
static uint16_t usb_transmit_read_index = 0;
|
||||
static uint16_t usb_transmit_fifo_level = 0;
|
||||
static bool data_transmission_active = false;
|
||||
|
|
|
|||
|
|
@ -22,13 +22,17 @@ static uint32_t binSize;
|
|||
static uint8_t signalIDstep;
|
||||
static uint32_t sampleNum;
|
||||
static Protocol::PacketInfo p;
|
||||
|
||||
// Result values
|
||||
static std::complex<double> res_port1, res_port2;
|
||||
|
||||
static bool active = false;
|
||||
static uint32_t lastLO2;
|
||||
static uint32_t actualRBW;
|
||||
static uint16_t DFTpoints;
|
||||
static bool negativeDFT; // if true, a positive frequency shift at input results in a negative shift at the 2.IF. Handle DFT accordingly
|
||||
|
||||
static float port1Measurement[FPGA::DFTbins], port2Measurement[FPGA::DFTbins];
|
||||
static std::complex<float> port1Measurement[FPGA::DFTbins], port2Measurement[FPGA::DFTbins];
|
||||
|
||||
static uint8_t signalIDsteps;
|
||||
static std::array<uint8_t, 4> signalIDprescalers;
|
||||
|
|
@ -274,15 +278,15 @@ bool SA::MeasurementDone(const FPGA::SamplingResult &result) {
|
|||
FPGA::AbortSweep();
|
||||
|
||||
for(uint16_t i=0;i<DFTpoints;i++) {
|
||||
float port1, port2;
|
||||
std::complex<double> port1, port2;
|
||||
if (s.UseDFT) {
|
||||
// use DFT result
|
||||
auto dft = FPGA::ReadDFTResult();
|
||||
port1 = dft.P1;
|
||||
port2 = dft.P2;
|
||||
} else {
|
||||
port1 = fabs(std::complex<float>(result.P1I, result.P1Q));
|
||||
port2 = fabs(std::complex<float>(result.P2I, result.P2Q));
|
||||
port1 = std::complex<float>(result.P1I, result.P1Q);
|
||||
port2 = std::complex<float>(result.P2I, result.P2Q);
|
||||
}
|
||||
port1 /= sampleNum;
|
||||
port2 /= sampleNum;
|
||||
|
|
@ -293,10 +297,11 @@ bool SA::MeasurementDone(const FPGA::SamplingResult &result) {
|
|||
index = DFTpoints - i - 1;
|
||||
}
|
||||
|
||||
if(port1 < port1Measurement[index]) {
|
||||
// Signal ID: only keep the lowest measurement
|
||||
if(abs(port1) < abs(port1Measurement[index])) {
|
||||
port1Measurement[index] = port1;
|
||||
}
|
||||
if(port2 < port2Measurement[index]) {
|
||||
if(abs(port2) < abs(port2Measurement[index])) {
|
||||
port2Measurement[index] = port2;
|
||||
}
|
||||
}
|
||||
|
|
@ -332,46 +337,46 @@ void SA::Work() {
|
|||
switch(det) {
|
||||
case Detector::PosPeak:
|
||||
if(pointInBin == 0) {
|
||||
p.spectrumResult.port1 = std::numeric_limits<float>::min();
|
||||
p.spectrumResult.port2 = std::numeric_limits<float>::min();
|
||||
res_port1 = std::numeric_limits<std::complex<float>>::min();
|
||||
res_port2 = std::numeric_limits<std::complex<float>>::min();
|
||||
}
|
||||
if(port1Measurement[i] > p.spectrumResult.port1) {
|
||||
p.spectrumResult.port1 = port1Measurement[i];
|
||||
if(abs(port1Measurement[i]) > abs(res_port1)) {
|
||||
res_port1 = port1Measurement[i];
|
||||
}
|
||||
if(port2Measurement[i] > p.spectrumResult.port2) {
|
||||
p.spectrumResult.port2 = port2Measurement[i];
|
||||
if(abs(port2Measurement[i]) > abs(res_port2)) {
|
||||
res_port2 = port2Measurement[i];
|
||||
}
|
||||
break;
|
||||
case Detector::NegPeak:
|
||||
if(pointInBin == 0) {
|
||||
p.spectrumResult.port1 = std::numeric_limits<float>::max();
|
||||
p.spectrumResult.port2 = std::numeric_limits<float>::max();
|
||||
res_port1 = std::complex<float>(std::numeric_limits<float>::max());
|
||||
res_port2 = std::complex<float>(std::numeric_limits<float>::max());
|
||||
}
|
||||
if(port1Measurement[i] < p.spectrumResult.port1) {
|
||||
p.spectrumResult.port1 = port1Measurement[i];
|
||||
if(abs(port1Measurement[i]) < abs(res_port1)) {
|
||||
res_port1 = port1Measurement[i];
|
||||
}
|
||||
if(port2Measurement[i] < p.spectrumResult.port2) {
|
||||
p.spectrumResult.port2 = port2Measurement[i];
|
||||
if(abs(port2Measurement[i]) < abs(res_port2)) {
|
||||
res_port2 = port2Measurement[i];
|
||||
}
|
||||
break;
|
||||
case Detector::Sample:
|
||||
if(pointInBin <= binSize / 2) {
|
||||
// still in first half of bin, simply overwrite
|
||||
p.spectrumResult.port1 = port1Measurement[i];
|
||||
p.spectrumResult.port2 = port2Measurement[i];
|
||||
res_port1 = port1Measurement[i];
|
||||
res_port2 = port2Measurement[i];
|
||||
}
|
||||
break;
|
||||
case Detector::Average:
|
||||
if(pointInBin == 0) {
|
||||
p.spectrumResult.port1 = 0;
|
||||
p.spectrumResult.port2 = 0;
|
||||
res_port1 = 0;
|
||||
res_port2 = 0;
|
||||
}
|
||||
p.spectrumResult.port1 += port1Measurement[i];
|
||||
p.spectrumResult.port2 += port2Measurement[i];
|
||||
res_port1 += port1Measurement[i];
|
||||
res_port2 += port2Measurement[i];
|
||||
if(lastPointInBin) {
|
||||
// calculate average
|
||||
p.spectrumResult.port1 /= binSize;
|
||||
p.spectrumResult.port2 /= binSize;
|
||||
res_port1 /= binSize;
|
||||
res_port2 /= binSize;
|
||||
}
|
||||
break;
|
||||
case Detector::Normal:
|
||||
|
|
@ -396,13 +401,17 @@ void SA::Work() {
|
|||
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;
|
||||
res_port1 /= 253000000.0;
|
||||
res_port2 /= 253000000.0;
|
||||
if (s.applyReceiverCorrection) {
|
||||
auto correction = Cal::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);
|
||||
res_port1 *= powf(10.0f, (float) correction.port1 / 100.0f / 20.0f);
|
||||
res_port2 *= powf(10.0f, (float) correction.port2 / 100.0f / 20.0f);
|
||||
}
|
||||
p.spectrumResult.real_port1 = real(res_port1);
|
||||
p.spectrumResult.imag_port1 = imag(res_port1);
|
||||
p.spectrumResult.real_port2 = real(res_port2);
|
||||
p.spectrumResult.imag_port2 = imag(res_port2);
|
||||
p.spectrumResult.pointNum = binIndex;
|
||||
Communication::Send(p);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue