tp/scripts/owon_hds25s_bode/bode_scpi_data.py (7617B)
1 import pyvisa 2 import time 3 import numpy as np 4 5 # List of all valid timebases supported by OWON HDS2* series 6 TIMEBASES = [ 7 (2e-9, "2.0ns"), (5e-9, "5.0ns"), (10e-9, "10.0ns"), (20e-9, "20.0ns"), 8 (50e-9, "50.0ns"), (100e-9, "100ns"), (200e-9, "200ns"), (500e-9, "500ns"), 9 (1e-6, "1.0us"), (2e-6, "2.0us"), (5e-6, "5.0us"), (10e-6, "10us"), 10 (20e-6, "20us"), (50e-6, "50us"), (100e-6, "100us"), (200e-6, "200us"), 11 (500e-6, "500us"), (1e-3, "1.0ms"), (2e-3, "2.0ms"), (5e-3, "5.0ms"), 12 (10e-3, "10ms"), (20e-3, "20ms"), (50e-3, "50ms"), (100e-3, "100ms"), 13 (200e-3, "200ms"), (500e-3, "500ms"), (1.0, "1.0s"), (2.0, "2.0s"), 14 (5.0, "5.0s"), (10.0, "10s"), (20.0, "20s"), (50.0, "50s"), 15 (100.0, "100s"), (200.0, "200s"), (500.0, "500s"), (1000.0, "1000s") 16 ] 17 18 # Supported scale tables according to probe attenuation 19 VOLT_SCALES = { 20 "1X": [ 21 (0.010, "10.0mV"), (0.020, "20.0mV"), (0.050, "50.0mV"), 22 (0.100, "100mV"), (0.200, "200mV"), (0.500, "500mV"), 23 (1.000, "1.00V"), (2.000, "2.00V"), (5.000, "5.00V"), 24 (10.00, "10.0V") 25 ], 26 "10X": [ 27 (0.100, "100mV"), (0.200, "200mV"), (0.500, "500mV"), 28 (1.000, "1.00V"), (2.000, "2.00V"), (5.000, "5.00V"), 29 (10.00, "10.0V"), (20.00, "20.0V"), (50.00, "50.0V"), 30 (100.0, "100V") 31 ], 32 "100X": [ 33 (1.000, "1.00V"), (2.000, "2.00V"), (5.000, "5.00V"), 34 (10.00, "10.0V"), (20.00, "20.0V"), (50.00, "50.0V"), 35 (100.0, "100V"), (200.0, "200V"), (500.0, "500V"), 36 (1000.0, "1.00kV") 37 ], 38 "1000X": [ 39 (10.00, "10.0V"), (20.00, "20.0V"), (50.00, "50.0V"), 40 (100.0, "100V"), (200.0, "200V"), (500.0, "500V"), 41 (1000.0, "1.00kV"),(2000.0, "2.00kV"),(5000.0, "5.00kV"), 42 (10000.0, "10.0kV") 43 ] 44 } 45 46 CSV_FILEPATH = "csv/bode_data.csv" 47 LOG_ENABLE = False 48 PROBE = "1X" 49 GEN_PEAK = 0.10 # 2*GEN_PEAK peak to peak voltage 50 FREQS_COUNT = 10 51 MEAN_SAMPLE_COUNT = 3 52 curr_scale_idx = 2 # 500 mV startup vertical height 53 scales = VOLT_SCALES.get(PROBE) 54 55 def LOG(msg): 56 if LOG_ENABLE: 57 print(f"[LOG] {msg}") 58 59 def LOG_LOC(loc, msg): 60 if LOG_ENABLE: 61 print(f"[LOG]{loc} {msg}") 62 63 def set_best_timebase(frequency): 64 period = 1.0 / frequency 65 target_scale = period / 2.0 # Display width is roughly 10 divisions 66 67 # Pick the closest supported value in the list 68 best_match = min(TIMEBASES, key=lambda x: abs(x[0] - target_scale)) 69 LOG(f"HORizontal:SCALe {best_match[1]}") 70 time.sleep(0.1) 71 scope.write(f":HORizontal:SCALe {best_match[1]}") 72 73 def set_best_vert_scale(): 74 global curr_scale_idx 75 76 read_val_str = scope.query(":MEASurement:CH1:MAX?").strip() 77 LOG_LOC("[set_best_vert_scale]", f"read_val_str == {read_val_str}") 78 79 while read_val_str.startswith(">"): 80 LOG_LOC("[set_best_vert_scale]['>']", f"startswith('>')") 81 if curr_scale_idx <= 9: 82 curr_scale_idx = curr_scale_idx + 1 83 new_scale = scales[curr_scale_idx][1] 84 LOG_LOC("[set_best_vert_scale]['>']", f"Changing vertical scale to {new_scale}") 85 scope.write(f":CH1:SCALe {new_scale}") 86 87 if freq < 10: 88 # Roll mode 89 time.sleep(10) 90 else: 91 time.sleep(1) 92 93 # Read again with new scale 94 read_val_str = scope.query(":MEASurement:CH1:MAX?").strip() 95 LOG_LOC("[set_best_vert_scale]['>'][read again]", f"read_val_str == {read_val_str}") 96 else: 97 read_val_str.lstrip(">") 98 99 while read_val_str.startswith("<"): 100 LOG_LOC("[set_best_vert_scale]['<']", f"startswith('<')") 101 if curr_scale_idx > 0: 102 curr_scale_idx = curr_scale_idx - 1 103 new_scale = scales[curr_scale_idx][1] 104 # print(f"Changing vertical scale to {new_scale}") 105 scope.write(f":CH1:SCALe {new_scale}") 106 107 if freq < 10: 108 # Roll mode 109 time.sleep(10) 110 else: 111 time.sleep(1) 112 113 # Read again with new scale 114 read_val_str = scope.query(":MEASurement:CH1:MAX?").strip() 115 LOG_LOC("[set_best_vert_scale]['<']", f"read_val_str == {read_val_str}") 116 else: 117 read_val_str.lstrip("<") 118 119 read_val = float(read_val_str) 120 LOG_LOC("[set_best_vert_scale]", f"read_val == {read_val_str}") 121 122 if curr_scale_idx > 0 and read_val < 2*scales[curr_scale_idx - 1][0]: 123 curr_scale_idx = curr_scale_idx - 1 124 new_scale = scales[curr_scale_idx][1] 125 # print(f"Changing vertical scale to {new_scale}") 126 scope.write(f":CH1:SCALe {new_scale}") 127 128 if freq < 10: 129 # Roll mode 130 time.sleep(10) 131 132 # Read again with new scale 133 read_val_str = scope.query(":MEASurement:CH1:MAX?").strip() 134 LOG_LOC("[set_best_vert_scale][read_val < 2*next_scale", f"read_val_str == {read_val_str}") 135 136 while read_val_str.startswith(">") or read_val_str.startswith("<"): 137 read_val_str = scope.query(":MEASurement:CH1:MAX?").strip() 138 139 read_val = float(read_val_str) 140 141 # example: 5 decades (10^0 to 10^5) at 5 points/decade -> 5 * 5 + 1 = 26 points 142 # freqs = np.concatenate((np.array([1]), np.logspace(4, 5, num=FREQS_COUNT))) 143 freqs = np.logspace(0, 5, num=FREQS_COUNT) 144 freqs = np.unique([int(f) for f in freqs]) 145 print(freqs) 146 amps = [] 147 148 rm = pyvisa.ResourceManager('@py') 149 150 try: 151 scope = rm.open_resource("ASRL/dev/ttyUSB0::INSTR") 152 except: 153 print("[ERROR] Device not found") 154 exit(0) 155 156 try: 157 data = scope.read_raw() 158 print("Pending:", data) 159 except pyvisa.errors.VisaIOError as e: 160 print("No pending SCPI data") 161 162 print("Identification: ", scope.query("*IDN?"), end="") 163 164 # Configure scope 165 scope.write(f":CH1:SCALe {scales[curr_scale_idx][1]}") 166 scope.write(":CH1:COUPling AC") 167 scope.write(f":CH1:PROBE {PROBE}") 168 scope.write(f":FUNCtion:AMPLitude {GEN_PEAK*2}") 169 scope.write(":FUNCtion SINE") 170 171 print(f"Generator set to function SINE") 172 # print(" freq, amp/peak") 173 174 for freq in freqs: 175 # print(f"Probing {freq:2.2f} Hz") 176 scope.write(f":FUNCtion:FREQuency {freq:2.2f}") 177 178 # print(scope.query(":FUNCtion:FREQuency?")) 179 set_best_timebase(freq) 180 181 # Roll mode 182 if freq < 10: 183 scope.write(":CH1:COUPling DC") 184 time.sleep(10) 185 else: 186 scope.write(":CH1:COUPling AC") 187 time.sleep(0.5) 188 189 amp_arr = [] 190 for i in range(MEAN_SAMPLE_COUNT): 191 set_best_vert_scale() 192 read_val_str = scope.query(":MEASurement:CH1:MAX?").strip() 193 LOG_LOC(f"[main_loop]", f"read_val_str == {read_val_str}") 194 195 while read_val_str.startswith(">") or read_val_str.startswith("<"): 196 set_best_vert_scale() 197 read_val_str = scope.query(":MEASurement:CH1:MAX?").strip() 198 LOG_LOC(f"[main_loop]", f"read_val_str == {read_val_str}") 199 200 read_val = float(read_val_str) 201 202 # print(f"{read_val:<3.3f}") 203 amp_arr.append(read_val) 204 205 time.sleep(0.5) # time between mean samples 206 207 amp_mean = (sum(amp_arr) / MEAN_SAMPLE_COUNT) 208 amp_mean = 0.001 if amp_mean < 0.001 else amp_mean 209 210 amp_norm = amp_mean / GEN_PEAK 211 amps.append(amp_norm) 212 213 print(f"{freq:>8}, {amp_norm:<3.3f}") 214 215 scope.close() 216 217 # print(f"freqs = [{' '.join(map(str, freqs))}]") 218 # print(f"amps = [{' '.join(map(str, amps))}]") 219 220 data = np.column_stack((freqs, amps)) 221 np.savetxt(f"{CSV_FILEPATH}", 222 data, 223 delimiter=",", 224 header="Frequency,Amplitude", 225 comments="", 226 fmt=["%d", "%.3f"])
