-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update factory coefficient script with SNA5000A example
- Loading branch information
Showing
5 changed files
with
186 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
119 changes: 119 additions & 0 deletions
119
Software/Scripts/FactoryCoefficients/VNA_Example_SNA5000A/SNA5000A.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import pyvisa | ||
from enum import Enum | ||
|
||
class SNA5000A: | ||
# Class to control a siglent SNA5000A vector network analyzer | ||
|
||
def __init__(self, ip="192.168.22.8"): | ||
rm = pyvisa.ResourceManager('@py') | ||
dev_addr = "TCPIP0::"+ip+"::INSTR" | ||
self.inst = rm.open_resource(dev_addr) | ||
# check ID | ||
id = self.inst.query('*IDN?') | ||
if not id.startswith('Siglent Technologies,SNA50'): | ||
raise RuntimeError("Wrong device ID received:"+id) | ||
self.num_ports = int(self.read(":SERVICE:PORT:COUNT?")) | ||
self.excited_ports = self.num_ports | ||
|
||
def set_excited_ports(self, portlist): | ||
if len(portlist) == 0: | ||
# disabling all ports is not allowed | ||
return | ||
# enable traces for the specified ports | ||
for i in range(1, self.num_ports+1): | ||
if i in portlist: | ||
# enable the trace | ||
self.write(":DISP:WIND:TRAC" + str(i) + " 1") | ||
# set the parameter | ||
self.write(":CALC:PAR" + str(i) + ":DEF S" + str(i) + str(i)) | ||
else: | ||
# disable the trace | ||
self.write(":DISP:WIND:TRAC" + str(i) + " 0") | ||
self.excited_ports = len(portlist) | ||
|
||
def set_start_freq(self, freq): | ||
self.write(":SENS:FREQ:STAR "+str(freq)) | ||
|
||
def set_stop_freq(self, freq): | ||
self.write(":SENS:FREQ:STOP "+str(freq)) | ||
|
||
def set_IF_bandwidth(self, bandwidth): | ||
self.write(":SENS:BWID "+str(bandwidth)) | ||
|
||
def set_source_power(self, power): | ||
self.write(":SOUR:POW "+str(power)) | ||
|
||
def set_points(self, points): | ||
self.write(":SENS:SWEEP:POINTS "+str(points)) | ||
|
||
def get_trace_data(self, parameters: set): | ||
# example call: get_trace_data({"S11", "S21"}) | ||
xdata = self.read(":CALC:DATA:XAXIS?") | ||
rawdata = {} | ||
for p in parameters: | ||
rawdata[p] = self.read(":SENS:DATA:CORR? "+p) | ||
# parse received data | ||
freqList = [] | ||
for val in xdata.split(","): | ||
freqList.append(float(val)) | ||
parsedData = {} | ||
for param in rawdata.keys(): | ||
real = [] | ||
imag = [] | ||
doubleValues = rawdata[param].split(",") | ||
for i in range(0, len(doubleValues), 2): | ||
real.append(float(doubleValues[i])) | ||
imag.append(float(doubleValues[i+1])) | ||
if len(real) != len(freqList): | ||
raise Exception("X/Y data vectors with different lengths, unable to parse data") | ||
parsedData[param+"_real"] = real | ||
parsedData[param+"_imag"] = imag | ||
ret = {} | ||
for p in parameters: | ||
trace = [] | ||
for i in range(len(freqList)): | ||
freq = freqList[i] | ||
real = parsedData[p + "_real"][i] | ||
imag = parsedData[p + "_imag"][i] | ||
trace.append((freq, complex(real, imag))) | ||
ret[p] = trace | ||
return ret | ||
|
||
def start_single_sweep(self): | ||
self.start_continuous_sweep() | ||
self.write(":INIT:IMM") | ||
|
||
def blocking_single_sweep(self, data=set()): | ||
self.write(":TRIG:SOUR MAN") | ||
self.start_continuous_sweep() | ||
|
||
# Sweep may take a long time, so we need to adjust the timeout of the visa connection | ||
sweeptime = float(self.read(":SENS:SWE:TIME?")) | ||
timeout = self.excited_ports * sweeptime + 10 | ||
self.write("TRIG:SING") | ||
old_timeout = self.inst.timeout | ||
self.inst.timeout = timeout * 1000 | ||
self.read("*OPC?") | ||
self.inst.timeout = old_timeout | ||
ret = self.get_trace_data(data) | ||
self.write(":TRIG:SOUR INT") | ||
return ret | ||
|
||
def start_continuous_sweep(self): | ||
self.write(":INIT:CONT 1") | ||
|
||
def stop_sweep(self): | ||
self.write(":INIT:CONT 0") | ||
|
||
def sweep_complete(self) -> bool: | ||
print(self.read(":SENS:AVER:COUN?")) | ||
return True | ||
|
||
def write(self, command): | ||
self.inst.write(command) | ||
|
||
def read(self, command): | ||
return self.inst.query(command) | ||
|
||
def reset(self): | ||
self.inst.write("*RST") |
59 changes: 59 additions & 0 deletions
59
Software/Scripts/FactoryCoefficients/VNA_Example_SNA5000A/VNA.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# A VNA is needed to measure and create the factory coefficients. | ||
# This file contains the necessary function to extract data from this VNA. | ||
# Please implement all functions according to the VNA you are using. | ||
|
||
from VNA_Example_SNA5000A.SNA5000A import SNA5000A | ||
import time | ||
|
||
vna = None | ||
|
||
def checkIfReady() -> bool: | ||
""" | ||
checkIfReady checks if the VNA is connected and ready to be used | ||
This function can also be used for initiliazing the VNA. It is called only | ||
once at the beginning | ||
:return: True if VNA is ready. False otherwise | ||
""" | ||
global vna | ||
try: | ||
vna = SNA5000A() | ||
except: | ||
print("Failed to detect VNA") | ||
return False | ||
|
||
vna.stop_sweep() | ||
vna.set_start_freq(9000) | ||
vna.set_stop_freq(8500000000) | ||
vna.set_points(801) | ||
vna.set_source_power(0) | ||
vna.set_IF_bandwidth(10000) | ||
|
||
return True | ||
|
||
def getPorts() -> int: | ||
""" | ||
getPorts returns the number of ports the VNA has | ||
Creation of factory coefficients if faster if more ports are available. | ||
The VNA needs at least 2 ports and at most 4 ports can be utilized. | ||
:return: Number of ports on the VNA | ||
""" | ||
return vna.num_ports | ||
|
||
def measure(): | ||
""" | ||
measure starts a measurement and returns the S parameter data | ||
This function should start a new measurement and block until the data | ||
is available. No previous measurements must be returned. | ||
Measurements are returned as a dictionary: | ||
Key: S parameter name (e.g. "S11") | ||
Value: List of tuples: (frequency, complex) | ||
:return: Measurements | ||
""" | ||
return vna.blocking_single_sweep({"S11","S12","S13","S14","S21","S22","S23","S24","S31","S32","S33","S34","S41","S42","S43","S44"}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters