# NWA Mode # INST:SEL NWA|SAN"... #NEED to enable LXI on net analyzer. DHCP needs to be on import vxi11 import numpy as np import traceback import time class ZVLNetAnalyzer(): def __init__(self, ip=None): if(ip is None or ip == ''): if(len(vxi11.list_devices()) == 1): ip = vxi11.list_devices()[0] print('ZVL NA: Selecting ip='+ip) else: print('ZVL NA: Please provide an ip from the following:') print(vxi11.list_devices()) raise Exception self.instrument = vxi11.Instrument(ip) self.instrument.open() idn = self.instrument.ask('*IDN?') print(f'Identity: {idn}') self.reset() self.base_data = np.array([]) self.base_data = self.get_data()[1] def reset(self): #self.instrument.write('*RST') #self.instrument.write('SYST:PRES') # reloads current setup. self.instrument.write('*CLS') self.instrument.write('INST:NSEL 2') self.instrument.write('DISPlay:WINDow1:STATe ON') self.instrument.write(":CALC:PAR:MEAS 'Trc1', 'S11'") self.instrument.write('CALC:FORM MLOG') self.instrument.write('INIT:CONT OFF') self.instrument.write("SYST:USER:DISP:TITL 'Frappy connection'") #self.instrument.write('INIT:SCOP OFF') #self.instrument.write('DISPlay:WINDow2:STATe ON') def load_calibration(self, f): self.instrument.write(f":MMEMORY:STORE:CORR 1, 'OSM1 {f}'") # put calibration in pool self.instrument.write(f":MMEMORY:LOAD:CORR 1, 'OSM1 {f}'") # load from pool def set_freq_range(self, start, stop): '''In Hz''' self.instrument.write(f'SENS1:FREQ:STAR {start}') self.instrument.write(f'SENS1:FREQ:STOP {stop}') def set_freq_span(self, center, span): '''In Hz''' self.instrument.write(f'SENS1:FREQ:CENT {center}') self.instrument.write(f'SENS1:FREQ:SPAN {span}') def set_averaging_passes(self,avgs): ''' Parameters ''' assert(avgs <= 1000) assert(avgs >= 1) self.instrument.write('AVER:CLE') self.instrument.write(f'AVER:COUN {avgs}') self.instrument.write('AVER ON') time.sleep(1) def clear(self): self.instrument.write('AVER:CLE') self.instrument.write('TRAC:CLE') self.instrument.write('CLE') def format_data(self, returned_from_device, complex=True): d = returned_from_device.split(',') if(complex): d = [ float(r) + 1j*float(i) for r, i in zip(d[::2], d[1::2]) ] else: d = [ float(i) for i in d ] return np.array(d) def get_data(self, N=1000, units='dB', averaging_passes=1): '''Returns the frequencies, in Hz, and the magnitudes of S11, in units (see `units` parameter). Note: Testing shows that acquisition time is approx. 0.7ms (mostly linear) per datapoint. Max 1000*averaging_passes datapoints (700ms per averaging pass, really). Valid units are: dB: deciBels (power) unitless: simply magnitudes of S11 Parameters N: int, between 2 and 1000 inc., the number of points to be returned. (default 1000) units: str, see above. Units/format of returned data. averaging_passes: int, describes the number of scans that will be taken and averaged. values can be from 1-999 inclusive (default 1) ''' assert(N>=2) assert(N<=1000) assert(averaging_passes>=1) assert(averaging_passes<=999) assert(units in ['dB', 'unitless']) self.instrument.write(f'SWE:POIN {N}') self.instrument.write(f'SWE:COUN {averaging_passes}') if(N != self.base_data.shape[0]): data = np.zeros(N, dtype=np.complex128) else: data = np.copy(self.base_data) freqs = np.zeros_like(data) self.instrument.write('INIT') while(np.sum(data) == np.sum(self.base_data) or np.all(data == 0)): total_data = np.zeros_like(data, dtype=np.complex128) for i in range(1, averaging_passes+1): data = self.instrument.ask(f'CALC:DATA:NSW? SDAT, {i}') freqs = self.instrument.ask('CALC:DATA:STIM?') data = self.format_data(data) freqs = self.format_data(freqs, complex=False) try: total_data += data except: total_data = data total_data /= averaging_passes self.base_data = data if(units == 'dB'): # |total_data| is amplitudes. dB should be in terms of power total_data = np.log10(np.square(np.abs(total_data)))*10.0 elif(units == 'unitless'): total_data = np.abs(total_data) return freqs, total_data def find_peak(self, range_start=None, range_end=None, scan_width=100_000): if(range_start is None): range_start = 9_000 if(range_end is None): range_end = 13_600_000_000 min_mag = 0 min_mag_index = 0 N=1000 num_scans = (range_end - range_start)//scan_width + 1 full_record = np.zeros(N*num_scans) full_record_fq = np.zeros(N*num_scans) i = 1 s = range_start e = s + scan_width while(i <= num_scans): s = range_start + i*scan_width e = s + scan_width self.set_freq_range(s, e) freqs, data = self.get_data() mn = np.min(data) if(mn < min_mag): min_mag = mn min_mag_index = np.argmin(data) + (i-1)*N full_record[(i-1)*N:i*N] = data full_record_fq[(i-1)*N:i*N] = freqs i += 1 return min_mag, min_mag_index, full_record, full_record_fq # example code. profiles the per-point delay for reading data and #ip = '169.254.150.182' #import matplotlib.pyplot as plt #print('start') #z = ZVLNetAnalyzer(ip) #mm, mmi, fr, frq = z.find_peak(50_000_000, 350_000_000, 20_000_000) #plt.plot(frq, fr) #plt.axvline(frq[mmi]) #plt.axhline(mm) #plt.show() #z.reset() ##z.set_freq_range(1_000_000, 2_000_000.5) ##z.set_freq_span(1_000_000, 10_000) #z.set_freq_span(220_000_000, 50_000_000) #Ns = np.linspace(3, 1000, 100).astype(int) #ts = [] #for N in Ns: # st = time.time() # freqs, data = z.get_data(N) # et = time.time() # dt = (et-st) # print(f'got data, {dt/N} ({dt})') # ts += [dt] # #plt.scatter(Ns, ts) #plt.show() #plt.scatter(Ns, np.array(ts)/np.array(Ns)) #plt.show() #input() #plt.plot(*z.get_data(averaging_passes=1), alpha=0.3) #plt.plot(*z.get_data(averaging_passes=64), alpha=0.3) #plt.show()