SA/GEN integration tests + bugfixes
This commit is contained in:
parent
ec6fae5822
commit
0c17288ece
@ -90,7 +90,7 @@ class libreVNA:
|
||||
return self.__read_response()
|
||||
|
||||
@staticmethod
|
||||
def parse_trace_data(data):
|
||||
def parse_VNA_trace_data(data):
|
||||
ret = []
|
||||
# Remove brackets (order of data implicitly known)
|
||||
data = data.replace(']','').replace('[','')
|
||||
@ -103,5 +103,20 @@ class libreVNA:
|
||||
real = float(values[i+1])
|
||||
imag = float(values[i+2])
|
||||
ret.append((freq, complex(real, imag)))
|
||||
return ret
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def parse_SA_trace_data(data):
|
||||
ret = []
|
||||
# Remove brackets (order of data implicitly known)
|
||||
data = data.replace(']','').replace('[','')
|
||||
values = data.split(',')
|
||||
if int(len(values) / 2) * 2 != len(values):
|
||||
# number of values must be a multiple of two (frequency, dBm)
|
||||
raise Exception("Invalid input data: expected tuples of two values each")
|
||||
for i in range(0, len(values), 2):
|
||||
freq = float(values[i])
|
||||
dBm = float(values[i+1])
|
||||
ret.append((freq, dBm))
|
||||
return ret
|
||||
|
||||
|
@ -42,7 +42,7 @@ data = vna.query(":VNA:TRACE:DATA? S11")
|
||||
|
||||
# Returned data is just a string containing all the measurement points.
|
||||
# Parsing the data returns a list containing frequency/complex tuples
|
||||
S11 = vna.parse_trace_data(data)
|
||||
S11 = vna.parse_VNA_trace_data(data)
|
||||
|
||||
for x in S11:
|
||||
print(x)
|
||||
|
@ -5,6 +5,8 @@ testmodules = [
|
||||
'tests.TestMode',
|
||||
'tests.TestVNASweep',
|
||||
'tests.TestCalibration',
|
||||
'tests.TestGenerator',
|
||||
'tests.TestSASweep',
|
||||
]
|
||||
|
||||
suite = unittest.TestSuite()
|
||||
|
@ -149,10 +149,10 @@ class TestCalibration(TestBase):
|
||||
cal.reset()
|
||||
|
||||
# grab trace data
|
||||
S11 = self.vna.parse_trace_data(self.vna.query(":VNA:TRACE:DATA? S11"))
|
||||
S12 = self.vna.parse_trace_data(self.vna.query(":VNA:TRACE:DATA? S12"))
|
||||
S21 = self.vna.parse_trace_data(self.vna.query(":VNA:TRACE:DATA? S21"))
|
||||
S22 = self.vna.parse_trace_data(self.vna.query(":VNA:TRACE:DATA? S22"))
|
||||
S11 = self.vna.parse_VNA_trace_data(self.vna.query(":VNA:TRACE:DATA? S11"))
|
||||
S12 = self.vna.parse_VNA_trace_data(self.vna.query(":VNA:TRACE:DATA? S12"))
|
||||
S21 = self.vna.parse_VNA_trace_data(self.vna.query(":VNA:TRACE:DATA? S21"))
|
||||
S22 = self.vna.parse_VNA_trace_data(self.vna.query(":VNA:TRACE:DATA? S22"))
|
||||
|
||||
# Attenuation is frequency dependent, use excessively large limits
|
||||
# TODO: use smaller limits based on frequency
|
||||
|
16
Software/Integrationtests/tests/TestGenerator.py
Normal file
16
Software/Integrationtests/tests/TestGenerator.py
Normal file
@ -0,0 +1,16 @@
|
||||
from tests.TestBase import TestBase
|
||||
|
||||
class TestGenerator(TestBase):
|
||||
def test_Generator(self):
|
||||
self.vna.cmd(":DEV:MODE GEN")
|
||||
self.assertEqual(self.vna.query(":DEV:MODE?"), "GEN")
|
||||
|
||||
self.vna.cmd(":GEN:FREQ 2000000000")
|
||||
self.assertEqual(float(self.vna.query(":GEN:FREQ?")), 2000000000)
|
||||
|
||||
self.vna.cmd(":GEN:LVL -12.34")
|
||||
self.assertEqual(float(self.vna.query(":GEN:LVL?")), -12.34)
|
||||
|
||||
self.assertEqual(self.vna.query(":GEN:PORT?"), "0")
|
||||
self.vna.cmd(":GEN:PORT 2")
|
||||
self.assertEqual(self.vna.query(":GEN:PORT?"), "2")
|
98
Software/Integrationtests/tests/TestSASweep.py
Normal file
98
Software/Integrationtests/tests/TestSASweep.py
Normal file
@ -0,0 +1,98 @@
|
||||
from tests.TestBase import TestBase
|
||||
import time
|
||||
|
||||
class TestSASweep(TestBase):
|
||||
def waitSweepTimeout(self, timeout = 1):
|
||||
self.assertEqual(self.vna.query(":SA:ACQ:FIN?"), "FALSE")
|
||||
stoptime = time.time() + timeout
|
||||
while self.vna.query(":SA:ACQ:FIN?") == "FALSE":
|
||||
if time.time() > stoptime:
|
||||
raise AssertionError("Sweep timed out")
|
||||
|
||||
def assertTrace_dB(self, trace, dB_nominal, dB_deviation):
|
||||
for S in trace:
|
||||
self.assertLessEqual(S[1], dB_nominal + dB_deviation)
|
||||
self.assertGreaterEqual(S[1], dB_nominal - dB_deviation)
|
||||
|
||||
def test_sweep_frequency(self):
|
||||
self.vna.cmd(":DEV:MODE SA")
|
||||
self.vna.cmd(":SA:FREQ:CENT 2000000000")
|
||||
self.vna.cmd(":SA:FREQ:SPAN 200000")
|
||||
|
||||
self.assertEqual(float(self.vna.query(":SA:FREQ:START?")), 1999900000)
|
||||
self.assertEqual(float(self.vna.query(":SA:FREQ:STOP?")), 2000100000)
|
||||
|
||||
self.vna.cmd(":SA:ACQ:RBW 10000")
|
||||
self.waitSweepTimeout(10)
|
||||
|
||||
port1 = self.vna.parse_SA_trace_data(self.vna.query(":SA:TRACE:DATA? PORT1"))
|
||||
port2 = self.vna.parse_SA_trace_data(self.vna.query(":SA:TRACE:DATA? PORT2"))
|
||||
|
||||
self.assertEqual(port1[0][0], 1999900000)
|
||||
self.assertEqual(port1[-1][0], 2000100000)
|
||||
self.assertEqual(port2[0][0], 1999900000)
|
||||
self.assertEqual(port2[-1][0], 2000100000)
|
||||
|
||||
# No signal present, signal level should be very low
|
||||
self.assertTrace_dB(port1, -140, 60)
|
||||
self.assertTrace_dB(port2, -140, 60)
|
||||
|
||||
def test_sweep_zerospan(self):
|
||||
self.vna.cmd(":DEV:MODE SA")
|
||||
self.vna.cmd(":SA:FREQ:CENT 2000000000")
|
||||
self.vna.cmd(":SA:FREQ:SPAN 0")
|
||||
|
||||
self.assertEqual(float(self.vna.query(":SA:FREQ:START?")), 2000000000)
|
||||
self.assertEqual(float(self.vna.query(":SA:FREQ:STOP?")), 2000000000)
|
||||
|
||||
self.vna.cmd(":SA:ACQ:RBW 10000")
|
||||
self.waitSweepTimeout(10)
|
||||
|
||||
port1 = self.vna.parse_SA_trace_data(self.vna.query(":SA:TRACE:DATA? PORT1"))
|
||||
port2 = self.vna.parse_SA_trace_data(self.vna.query(":SA:TRACE:DATA? PORT2"))
|
||||
|
||||
self.assertEqual(port1[0][0], 0.0)
|
||||
self.assertGreater(port1[-1][0], 4.5)
|
||||
self.assertLess(port1[-1][0], 5)
|
||||
self.assertEqual(port2[0][0], 0.0)
|
||||
self.assertGreater(port2[-1][0], 4.5)
|
||||
self.assertLess(port2[-1][0], 5)
|
||||
|
||||
# No signal present, signal level should be very low
|
||||
self.assertTrace_dB(port1, -140, 60)
|
||||
self.assertTrace_dB(port2, -140, 60)
|
||||
|
||||
def test_tracking_generator(self):
|
||||
self.vna.cmd(":DEV:MODE SA")
|
||||
self.vna.cmd(":SA:FREQ:CENT 2000000000")
|
||||
self.vna.cmd(":SA:FREQ:SPAN 200000")
|
||||
self.vna.cmd(":SA:ACQ:RBW 10000")
|
||||
self.vna.cmd(":SA:TRACK:PORT 1")
|
||||
self.assertEqual(self.vna.query(":SA:TRACK:PORT?"), "1")
|
||||
self.vna.cmd(":SA:TRACK:LVL -20")
|
||||
self.vna.cmd(":SA:TRACK:OFFSET 0")
|
||||
self.vna.cmd(":SA:TRACK:EN TRUE")
|
||||
self.waitSweepTimeout(10)
|
||||
|
||||
port1 = self.vna.parse_SA_trace_data(self.vna.query(":SA:TRACE:DATA? PORT1"))
|
||||
|
||||
level = port1[0][1]
|
||||
self.assertGreater(level, -30)
|
||||
self.assertLess(level, -10)
|
||||
# check tracking generator signal
|
||||
self.assertTrace_dB(port1, level, 5)
|
||||
|
||||
# Enable normalization
|
||||
self.vna.cmd(":SA:TRACK:NORM:LVL -10")
|
||||
self.vna.cmd(":SA:TRACK:NORM:EN TRUE")
|
||||
# wait for normalization to finish
|
||||
self.waitSweepTimeout(10)
|
||||
|
||||
# trigger the next sweep
|
||||
self.vna.cmd(":SA:ACQ:SINGLE TRUE")
|
||||
self.waitSweepTimeout(10)
|
||||
|
||||
# Reported level on port1 should match normalization very closely now
|
||||
port1 = self.vna.parse_SA_trace_data(self.vna.query(":SA:TRACE:DATA? PORT1"))
|
||||
self.assertTrace_dB(port1, -10, 1)
|
||||
|
@ -20,7 +20,7 @@ class TestVNASweep(TestBase):
|
||||
self.vna.cmd(":VNA:FREQuency:STOP 6000000000")
|
||||
self.waitSweepTimeout(2)
|
||||
|
||||
S11 = self.vna.parse_trace_data(self.vna.query(":VNA:TRACE:DATA? S11"))
|
||||
S11 = self.vna.parse_VNA_trace_data(self.vna.query(":VNA:TRACE:DATA? S11"))
|
||||
self.assertEqual(S11[0][0], 1000000)
|
||||
self.assertEqual(S11[-1][0], 6000000000)
|
||||
|
||||
@ -36,7 +36,7 @@ class TestVNASweep(TestBase):
|
||||
self.vna.cmd(":VNA:FREQuency:ZERO 1500000000")
|
||||
self.waitSweepTimeout(2)
|
||||
|
||||
S11 = self.vna.parse_trace_data(self.vna.query(":VNA:TRACE:DATA? S11"))
|
||||
S11 = self.vna.parse_VNA_trace_data(self.vna.query(":VNA:TRACE:DATA? S11"))
|
||||
self.assertEqual(S11[0][0], 0.0)
|
||||
# Sweep should take about 0.125 seconds
|
||||
self.assertGreater(S11[-1][0], 0.1)
|
||||
@ -53,7 +53,7 @@ class TestVNASweep(TestBase):
|
||||
self.vna.cmd(":VNA:POWER:STOP -10")
|
||||
self.waitSweepTimeout(2)
|
||||
|
||||
S11 = self.vna.parse_trace_data(self.vna.query(":VNA:TRACE:DATA? S11"))
|
||||
S11 = self.vna.parse_VNA_trace_data(self.vna.query(":VNA:TRACE:DATA? S11"))
|
||||
self.assertEqual(S11[0][0], -30)
|
||||
self.assertEqual(S11[-1][0], -10)
|
||||
|
||||
|
@ -132,7 +132,7 @@ public:
|
||||
Detector detector;
|
||||
bool signalID;
|
||||
bool trackingGenerator;
|
||||
int trackingPort;
|
||||
int trackingPort; // counting starts at zero
|
||||
double trackingOffset;
|
||||
double trackingPower;
|
||||
};
|
||||
|
@ -549,6 +549,7 @@ void SpectrumAnalyzer::NewDatapoint(VirtualDevice::SAMeasurement m)
|
||||
void SpectrumAnalyzer::SettingsChanged()
|
||||
{
|
||||
configurationTimer.start(100);
|
||||
ResetLiveTraces();
|
||||
}
|
||||
|
||||
void SpectrumAnalyzer::SetStartFreq(double freq)
|
||||
@ -909,6 +910,13 @@ void SpectrumAnalyzer::ConfigureDevice()
|
||||
}
|
||||
}
|
||||
|
||||
void SpectrumAnalyzer::ResetLiveTraces()
|
||||
{
|
||||
average.reset(settings.points);
|
||||
traceModel.clearLiveData();
|
||||
UpdateAverageCount();
|
||||
}
|
||||
|
||||
void SpectrumAnalyzer::SetupSCPI()
|
||||
{
|
||||
auto scpi_freq = new SCPINode("FREQuency");
|
||||
@ -1113,8 +1121,8 @@ void SpectrumAnalyzer::SetupSCPI()
|
||||
unsigned long long newval;
|
||||
if(!SCPI::paramToULongLong(params, 0, newval)) {
|
||||
return SCPI::getResultName(SCPI::Result::Error);
|
||||
} else if(newval <= VirtualDevice::getInfo(window->getDevice()).ports){
|
||||
SetTGPort(newval);
|
||||
} else if(newval > 0 && newval <= VirtualDevice::getInfo(window->getDevice()).ports){
|
||||
SetTGPort(newval-1);
|
||||
return SCPI::getResultName(SCPI::Result::Empty);
|
||||
} else {
|
||||
// invalid port number
|
||||
@ -1122,7 +1130,7 @@ void SpectrumAnalyzer::SetupSCPI()
|
||||
}
|
||||
return SCPI::getResultName(SCPI::Result::Empty);
|
||||
}, [=](QStringList) -> QString {
|
||||
return QString::number(settings.trackingPort);
|
||||
return QString::number(settings.trackingPort+1);
|
||||
}));
|
||||
scpi_tg->add(new SCPICommand("LVL", [=](QStringList params) -> QString {
|
||||
double newval;
|
||||
|
@ -74,6 +74,7 @@ private slots:
|
||||
void Run();
|
||||
void Stop();
|
||||
void ConfigureDevice();
|
||||
void ResetLiveTraces();
|
||||
|
||||
private:
|
||||
void SetupSCPI();
|
||||
|
@ -37,8 +37,11 @@ static uint8_t attenuator;
|
||||
static int64_t trackingFreq;
|
||||
static bool trackingLowband;
|
||||
|
||||
static bool firstSample;
|
||||
|
||||
// Zero span variables
|
||||
static uint64_t firstPointTime;
|
||||
static bool firstPoint;
|
||||
static bool firstPoint; // indicates the first point to be transmitted to the GUI, sets the firstPointTime
|
||||
static bool zerospan;
|
||||
|
||||
static void StartNextSample() {
|
||||
@ -175,6 +178,17 @@ static void StartNextSample() {
|
||||
trackingFreq, FPGA::SettlingTime::us60, FPGA::Samples::SPPRegister, 0,
|
||||
FPGA::LowpassFilter::Auto);
|
||||
|
||||
if(firstSample && (signalIDstep == 0)) {
|
||||
firstSample = false;
|
||||
// configure source/LO to allow for early settling
|
||||
FPGA::SetMode(FPGA::Mode::SourcePLL);
|
||||
Source.Update();
|
||||
FPGA::SetMode(FPGA::Mode::LOPLL);
|
||||
LO1.Update();
|
||||
FPGA::SetMode(FPGA::Mode::FPGA);
|
||||
HAL_Delay(20);
|
||||
}
|
||||
|
||||
FPGA::StartSweep();
|
||||
}
|
||||
|
||||
@ -214,6 +228,7 @@ void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) {
|
||||
zerospan = (s.f_start == s.f_stop);
|
||||
// set initial state
|
||||
pointCnt = 0;
|
||||
signalIDstep = 0;
|
||||
// enable the required hardware resources
|
||||
Si5351.Enable(SiChannel::Port1LO2);
|
||||
Si5351.Enable(SiChannel::Port2LO2);
|
||||
@ -265,6 +280,7 @@ void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) {
|
||||
|
||||
lastLO2 = 0;
|
||||
firstPoint = true;
|
||||
firstSample = true;
|
||||
active = true;
|
||||
StartNextSample();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user