diff --git a/.gitignore b/.gitignore index d8fcc30..dd882c6 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ *.xcf *.out *.toc +*.pyc Hardware/Renderings diff --git a/Hardware/Experiments/ADC_Noise/Scripts/libreVNA.py b/Hardware/Experiments/ADC_Noise/Scripts/libreVNA.py new file mode 100644 index 0000000..233c3e3 --- /dev/null +++ b/Hardware/Experiments/ADC_Noise/Scripts/libreVNA.py @@ -0,0 +1,82 @@ +import socket +from asyncio import IncompleteReadError # only import the exception class + +class SocketStreamReader: + def __init__(self, sock: socket.socket): + self._sock = sock + self._recv_buffer = bytearray() + + def read(self, num_bytes: int = -1) -> bytes: + raise NotImplementedError + + def readexactly(self, num_bytes: int) -> bytes: + buf = bytearray(num_bytes) + pos = 0 + while pos < num_bytes: + n = self._recv_into(memoryview(buf)[pos:]) + if n == 0: + raise IncompleteReadError(bytes(buf[:pos]), num_bytes) + pos += n + return bytes(buf) + + def readline(self) -> bytes: + return self.readuntil(b"\n") + + def readuntil(self, separator: bytes = b"\n") -> bytes: + if len(separator) != 1: + raise ValueError("Only separators of length 1 are supported.") + + chunk = bytearray(4096) + start = 0 + buf = bytearray(len(self._recv_buffer)) + bytes_read = self._recv_into(memoryview(buf)) + assert bytes_read == len(buf) + + while True: + idx = buf.find(separator, start) + if idx != -1: + break + + start = len(self._recv_buffer) + bytes_read = self._recv_into(memoryview(chunk)) + buf += memoryview(chunk)[:bytes_read] + + result = bytes(buf[: idx + 1]) + self._recv_buffer = b"".join( + (memoryview(buf)[idx + 1 :], self._recv_buffer) + ) + return result + + def _recv_into(self, view: memoryview) -> int: + bytes_read = min(len(view), len(self._recv_buffer)) + view[:bytes_read] = self._recv_buffer[:bytes_read] + self._recv_buffer = self._recv_buffer[bytes_read:] + if bytes_read == len(view): + return bytes_read + bytes_read += self._sock.recv_into(view[bytes_read:]) + return bytes_read + +class libreVNA: + def __init__(self, host='localhost', port=19542): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + self.sock.connect((host, port)) + except: + raise Exception("Unable to connect to LibreVNA-GUI. Make sure it is running and the TCP server is enabled.") + self.reader = SocketStreamReader(self.sock) + + def __del__(self): + self.sock.close() + + def __read_response(self): + return self.reader.readline().decode().rstrip() + + def cmd(self, cmd): + self.sock.sendall(cmd.encode()) + self.sock.send(b"\n") + self.__read_response() + + def query(self, query): + self.sock.sendall(query.encode()) + self.sock.send(b"\n") + return self.__read_response() diff --git a/Hardware/Experiments/ADC_Noise/Scripts/measure_ADC_noise.py b/Hardware/Experiments/ADC_Noise/Scripts/measure_ADC_noise.py new file mode 100644 index 0000000..b59ec77 --- /dev/null +++ b/Hardware/Experiments/ADC_Noise/Scripts/measure_ADC_noise.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +import time +from libreVNA import libreVNA + +def MeasureNoiseAndPrint(condition): + # Allow signal to settle + time.sleep(1) + + p1min = int(vna.query("PORT1_MIN?")) + p1max = int(vna.query("PORT1_MAX?")) + p2min = int(vna.query("PORT2_MIN?")) + p2max = int(vna.query("PORT2_MAX?")) + refmin = int(vna.query("REF_MIN?")) + refmax = int(vna.query("REF_MAX?")) + + p1pp = p1max - p1min + p2pp = p2max - p2min + refpp = refmax - refmin + print(condition + "&{:d}&{:d}&{:d}\\\\".format(p1pp, p2pp, refpp)) + +# Create the control instance +vna = libreVNA('localhost', 19542) + +# 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) + +# Enter manual control mode +vna.cmd(":MAN:STA") + +# By default everything is off, measure ADC noise +time.sleep(0.5) +vna.cmd("HSRC_FREQ 100000000") + +print("\\begin{longtable}{p{.5\\textwidth} | p{.1\\textwidth} | p{.1\\textwidth} | p{.1\\textwidth} }") +print("\\textbf{Condition} & \\textbf{Port 1} & \\textbf{Port 2} & \\textbf{Ref}\\\\") +print("\\hline") + +MeasureNoiseAndPrint("Everything off") + +vna.cmd("PORT1_EN TRUE") +vna.cmd("PORT2_EN TRUE") +vna.cmd("REF_EN TRUE") +MeasureNoiseAndPrint("Mixers and ADC driver enabled") + +vna.cmd("LO2_EN TRUE") +MeasureNoiseAndPrint("LO2 enabled") + +vna.cmd("LO1_CE TRUE") +vna.cmd("LO1_RFEN TRUE") +MeasureNoiseAndPrint("LO2 and LO1 enabled") + +vna.cmd("AMP_EN TRUE") +MeasureNoiseAndPrint("LO2, LO1 and amplifier enabled") + +vna.cmd("HSRC_CE TRUE") +vna.cmd("HSRC_RFEN TRUE") +MeasureNoiseAndPrint("LO2, LO1, amplifier and source at port 1 enabled") + +vna.cmd("PORT_SW 2") +MeasureNoiseAndPrint("LO2, LO1, amplifier and source at port 2 enabled") + +print("\\end{longtable}") + +vna.cmd("STOP") + +exit(0) +