mirror of
https://github.com/jankae/LibreVNA.git
synced 2025-12-06 07:12:10 +01:00
example script for using the live streaming servers
This commit is contained in:
parent
ee1adc0f85
commit
253e2d3517
57
Documentation/UserManual/SCPI_Examples/capture_live_data.py
Normal file
57
Documentation/UserManual/SCPI_Examples/capture_live_data.py
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import time
|
||||||
|
from libreVNA import libreVNA
|
||||||
|
|
||||||
|
# Create the control instance
|
||||||
|
vna = libreVNA('localhost', 19542)
|
||||||
|
|
||||||
|
# Quick connection check (should print "LibreVNA-GUI")
|
||||||
|
print(vna.query("*IDN?"))
|
||||||
|
|
||||||
|
# Make sure we are connecting to a device (just to be sure, with default settings the LibreVNA-GUI auto-connects)
|
||||||
|
vna.cmd(":DEV:CONN")
|
||||||
|
dev = vna.query(":DEV:CONN?")
|
||||||
|
if dev == "Not connected":
|
||||||
|
print("Not connected to any device, aborting")
|
||||||
|
exit(-1)
|
||||||
|
else:
|
||||||
|
print("Connected to "+dev)
|
||||||
|
|
||||||
|
# Capture live data as it is coming in. Stop acquisition for now
|
||||||
|
vna.cmd(":VNA:ACQ:STOP")
|
||||||
|
|
||||||
|
# switch to VNA mode, set up the sweep parameters
|
||||||
|
print("Setting up the sweep...")
|
||||||
|
vna.cmd(":DEV:MODE VNA")
|
||||||
|
vna.cmd(":VNA:SWEEP FREQUENCY")
|
||||||
|
vna.cmd(":VNA:STIM:LVL -10")
|
||||||
|
vna.cmd(":VNA:ACQ:IFBW 100")
|
||||||
|
vna.cmd(":VNA:ACQ:AVG 1")
|
||||||
|
vna.cmd(":VNA:ACQ:POINTS 501")
|
||||||
|
vna.cmd(":VNA:FREQuency:START 10000000")
|
||||||
|
vna.cmd(":VNA:FREQuency:STOP 6000000000")
|
||||||
|
|
||||||
|
sweepComplete = False
|
||||||
|
|
||||||
|
|
||||||
|
def callback(data):
|
||||||
|
global sweepComplete
|
||||||
|
print(data)
|
||||||
|
if data["pointNum"] == 500:
|
||||||
|
# this was the last point
|
||||||
|
vna.remove_live_callback(19000, callback)
|
||||||
|
sweepComplete = True
|
||||||
|
|
||||||
|
|
||||||
|
# Set up the connection for the live data
|
||||||
|
vna.add_live_callback(19000, callback)
|
||||||
|
print("Starting the sweep...")
|
||||||
|
vna.cmd(":VNA:ACQ:RUN")
|
||||||
|
|
||||||
|
while not sweepComplete:
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
print("Sweep complete")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2,6 +2,8 @@ import re
|
||||||
import socket
|
import socket
|
||||||
from asyncio import IncompleteReadError # only import the exception class
|
from asyncio import IncompleteReadError # only import the exception class
|
||||||
import time
|
import time
|
||||||
|
import threading
|
||||||
|
import json
|
||||||
|
|
||||||
class SocketStreamReader:
|
class SocketStreamReader:
|
||||||
def __init__(self, sock: socket.socket, default_timeout=1):
|
def __init__(self, sock: socket.socket, default_timeout=1):
|
||||||
|
|
@ -72,6 +74,7 @@ class libreVNA:
|
||||||
def __init__(self, host='localhost', port=19542,
|
def __init__(self, host='localhost', port=19542,
|
||||||
check_cmds=True, timeout=1):
|
check_cmds=True, timeout=1):
|
||||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.host = host
|
||||||
try:
|
try:
|
||||||
self.sock.connect((host, port))
|
self.sock.connect((host, port))
|
||||||
except:
|
except:
|
||||||
|
|
@ -79,6 +82,8 @@ class libreVNA:
|
||||||
self.reader = SocketStreamReader(self.sock,
|
self.reader = SocketStreamReader(self.sock,
|
||||||
default_timeout=timeout)
|
default_timeout=timeout)
|
||||||
self.default_check_cmds = check_cmds
|
self.default_check_cmds = check_cmds
|
||||||
|
self.live_threads = {}
|
||||||
|
self.live_callbacks = {}
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.sock.close()
|
self.sock.close()
|
||||||
|
|
@ -117,6 +122,60 @@ class libreVNA:
|
||||||
if status < 0 or status > 255:
|
if status < 0 or status > 255:
|
||||||
raise Exception(f"*ESR? returned invalid value {status}.")
|
raise Exception(f"*ESR? returned invalid value {status}.")
|
||||||
return status
|
return status
|
||||||
|
|
||||||
|
def add_live_callback(self, port, callback):
|
||||||
|
# check if we already have a thread handling this connection
|
||||||
|
if not port in self.live_threads:
|
||||||
|
# needs to create the connection and thread first
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
try:
|
||||||
|
sock.connect((self.host, port))
|
||||||
|
except:
|
||||||
|
raise Exception("Unable to connect to streaming server at port {}. Make sure it is enabled.".format(port))
|
||||||
|
|
||||||
|
self.live_callbacks[port] = [callback]
|
||||||
|
self.live_threads[port] = threading.Thread(target=self.__live_thread, args=(sock, port))
|
||||||
|
self.live_threads[port].start()
|
||||||
|
else:
|
||||||
|
# thread already existed, simply add to list
|
||||||
|
self.live_callbacks[port].append(callback)
|
||||||
|
|
||||||
|
def remove_live_callback(self, port, callback):
|
||||||
|
if port in self.live_callbacks:
|
||||||
|
# remove all matching callbacks from the list
|
||||||
|
self.live_callbacks[port] = [cb for cb in self.live_callbacks[port] if cb != callback]
|
||||||
|
# if the list is now empty, the thread will exit
|
||||||
|
if len(self.live_callbacks) == 0:
|
||||||
|
self.live_threads[port].join()
|
||||||
|
del self.live_threads[port]
|
||||||
|
|
||||||
|
def __live_thread(self, sock, port):
|
||||||
|
reader = SocketStreamReader(sock, default_timeout=0.1)
|
||||||
|
while len(self.live_callbacks[port]) > 0:
|
||||||
|
try:
|
||||||
|
line = reader.readline().decode().rstrip()
|
||||||
|
# determine whether this is data from the VNA or spectrum analyzer
|
||||||
|
data = json.loads(line)
|
||||||
|
if "Z0" in data:
|
||||||
|
# This is VNA data which has the imag/real parts of the S-parameters split into two float values.
|
||||||
|
# This was necessary because json does not support complex number. But python does -> convert back
|
||||||
|
# to complex
|
||||||
|
measurements = {}
|
||||||
|
for meas in data["measurements"].keys():
|
||||||
|
if meas.endswith("_imag"):
|
||||||
|
# ignore
|
||||||
|
continue
|
||||||
|
name = meas.removesuffix("_real")
|
||||||
|
real = data["measurements"][meas]
|
||||||
|
imag = data["measurements"][name+"_imag"]
|
||||||
|
measurements[name] = complex(real, imag)
|
||||||
|
data["measurements"] = measurements
|
||||||
|
for cb in self.live_callbacks[port]:
|
||||||
|
cb(data)
|
||||||
|
except:
|
||||||
|
# ignore timeouts
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_VNA_trace_data(data):
|
def parse_VNA_trace_data(data):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue