Files
frappy/frappy_psi/network_analysers/mRS/miniVNAPRO2Driver.py

209 lines
7.1 KiB
Python

import serial
from serial.tools.list_ports import comports
import numpy as np
import time
import matplotlib.pyplot as plt
def get_comports():
return [c.name for c in comports()]
class miniVNA_PRO2():
'''All information here was ripped from the "open source" JAR that mRS provides'''
port = ''
data = np.array([])
freqs = np.array([])
infostr = 'uninitialised'
# calibration
e00 = 0
e11 = 0
d_e = 0
calibrated = False
properties = {
'max_freq': 220_000_000, #Hz
'min_freq': 10_000,
'min_samplerate': 0,
'max_samplerate': 4,
'generator_ports': 2, # num
'mult': 8.25955249230769,
}
def __init__(self, port=''):
if(port == ''):
cs = get_comports()
if(len(cs) == 1):
print('miniVNA PRO2: No COM port provided. Using the only one available, ' + cs[0])
port = cs[0]
else:
print('miniVNA PRO2: Please provide a COM port from the following:\n' + str(cs))
raise Exception
self.port = serial.Serial(port, 921600, timeout=1, parity=serial.PARITY_NONE)
self.reset()
print('miniVNA PRO2 connected with serial information:')
print(self.get_info())
def __del__(self):
self.port.close()
def calibrate(self, freqs):
#https://k6jca.blogspot.com/2019/12/vna-notes-on-12-term-error-model-and.html
self.calibrated_freqs = freqs
Gm1 = np.loadtxt('short.txt', dtype=np.complex128)
Gm2 = np.loadtxt('load.txt', dtype=np.complex128)
Gm3 = np.loadtxt('open.txt', dtype=np.complex128)
G1 = -1
G2 = 0.1
G3 = +1
self.e00 = (G1*G2*Gm1*Gm3 - G1*G3*Gm1*Gm2 - G1*G2*Gm2*Gm3 + G2*G3*Gm1*Gm2 + G1*G3*Gm2*Gm3 - G2*G3*Gm1*Gm3)/(G1*G2*Gm1 - G1*G2*Gm2 - G1*G3*Gm1 + G1*G3*Gm3 + G2*G3*Gm2 - G2*G3*Gm3)
self.e11 = -(G1*Gm2 - G2*Gm1 - G1*Gm3 + G3*Gm1 + G2*Gm3 - G3*Gm2)/(G1*G2*Gm1 - G1*G2*Gm2 - G1*G3*Gm1 + G1*G3*Gm3 + G2*G3*Gm2 - G2*G3*Gm3)
self.d_e = -(G1*Gm1*Gm2 - G1*Gm1*Gm3 - G2*Gm1*Gm2 + G2*Gm2*Gm3 + G3*Gm1*Gm3 - G3*Gm2*Gm3)/(G1*G2*Gm1 - G1*G2*Gm2 - G1*G3*Gm1 + G1*G3*Gm3 + G2*G3*Gm2 - G2*G3*Gm3)
self.calibrated = True
def write_cmd(self, args):
if not(isinstance(args, list)):
args = [args]
args = [ str(a) for a in args ]
c = '\r'.join(args) + '\r'
self.port.write(bytes(c, 'utf-8'))
def read_line(self):
return self.port.readline()[:-2].decode('utf-8') # last two bytes are always \r\n
def get_info(self):
self.write_cmd('9')
self.port.timeout = 3
self.infostr = self.read_line()
return self.infostr
def reset(self):
self.write_cmd([9,9])
time.sleep(3)
self.port.reset_input_buffer()
self.port.reset_output_buffer()
def sweep(self, f0: float, f1: float, N: int = 128, sample_rate: int = 0, mode = 1):
'''Mode 1 designates that we're reading the DET (reflection) port... I think'''
print('Starting sweep')
st = time.time()
d = (f1 - f0) / (N - 1)
f0 -= 4 * d
N += 4
assert(sample_rate <= self.properties['max_samplerate'] and sample_rate >= self.properties['min_samplerate'])
if(d > f1 - 10_000):
d = 0.0
if(d == 0.0):
i = N
else:
i = N+1
self.write_cmd(['10' + str(mode), (f0-d) * self.properties['mult'], sample_rate, N+1, (f1-f0+d)/N * self.properties['mult']])
self.port.set_buffer_size(rx_size = 12*(N+10))
while(self.port.in_waiting < 12*N): # prepare the first read. It takes a second, okay?
time.sleep(0.1)
percent = self.port.in_waiting/(12*N)
L = 50
print(f'\rminiVNA PRO2: Progress: {int(100*percent):>3d}% [{"".join(["="]*int(percent*L) + [" "]*(L-int(percent*L)))}]', end='')
raw_data = np.zeros((N, 12), dtype=np.uint8)
progress = 0
failures = 0
while(progress < N):
incoming = self.port.read(12)
conv = np.frombuffer(incoming, dtype=np.uint8)
raw_data[progress,:len(conv)] = conv
progress += 1
et = time.time()
print(f'\nSweep took {et-st:.2f}s')
self.data = np.zeros((N,), dtype=np.complex128)
# processing one at a time to avoid overflows
self.data += 1j/2 * raw_data[:,0]
self.data += 1j/2 * raw_data[:,1]*256
self.data += 1j/2 * raw_data[:,2]*65536
self.data += 1/2 * raw_data[:,3]
self.data += 1/2 * raw_data[:,4]*256
self.data += 1/2 * raw_data[:,5]*65536
self.data -= 1j/2 * raw_data[:,6]
self.data += 1j/2 * raw_data[:,7]*256
self.data += 1j/2 * raw_data[:,8]*65536
self.data -= 1/2 * raw_data[:,9]
self.data += 1/2 * raw_data[:,10]*256
self.data += 1/2 * raw_data[:,11]*65536
self.freq = np.linspace(f0, f1, N)[4:]
self.data = self.data[4:]# / 777_472_127_994 # normalize
#self.data = np.convolve(self.data, np.exp(-np.square(1/2 *
e00 = np.interp(self.freq, self.calibrated_freqs, self.e00) if self.calibrated else 0
e11 = np.interp(self.freq, self.calibrated_freqs, self.e11) if self.calibrated else 0
d_e = np.interp(self.freq, self.calibrated_freqs, self.d_e) if self.calibrated else -1
self.data = (self.data - e00) / (self.data * e11 - d_e)
# (f1 - f0) / (N-1) * i + f0
et = time.time()
print(f'Completed sweep. Full function took {et-st:.2f}s')
def start_generator(self, freq_I, freq_Q, att_I, att_Q, phase):
self.write_cmd([2, freq_I, freq_Q, phase, 3, att_Q, att_I])
time.sleep(3)
while(self.port.in_waiting > 0):
print(self.port.read(1), end='')
m = miniVNA_PRO2('COM9')
#plt.plot(m.freq, np.real(m.data), label='0')
#plt.plot(m.freq, np.imag(m.data), label='0')
#plt.plot(m.freq, np.abs(m.data), label='0', color='k')
#m.calibrate(np.linspace(40_000_000, 50_000_000, 512))
#m.start_generator(40_000_000, 50_000_000, 30, 30, 0)
#m.test_gen()
f0 = 15_000_000
f1 = 60_000_000
m.sweep(f0, f1, N=512, sample_rate=4)
if(False):
short = m.data
for i in range(10):
m.sweep(f0, f1, N=512, sample_rate=4)
short += m.data
short /= 11
if(True):
plt.plot(m.freq, np.imag(m.data))
plt.plot(m.freq, np.real(m.data))
plt.plot(m.freq, np.abs(m.data))
plt.show()
if(False):
input('attach device')
m.sweep(f0, f1, N=512, sample_rate=4)
full_data = m.data
plt.plot(m.freq, np.abs(full_data))
plt.plot(m.freq, np.abs(cord_data))
plt.plot(m.freq, np.abs(full_data - cord_data))
plt.legend()
plt.show()
inp = input('save?')
if(inp == 'y'):
np.savetxt(input('fn') + '.txt', m.data)