-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patheelib.py
executable file
·157 lines (139 loc) · 4.17 KB
/
eelib.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/python3
import time
import sys
import math
import re
import struct
import enum
import pyvisa
def errprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def norm_angle(x: float) -> float:
"""Normalizes angle to value between [-180,180[."""
x = x % 360
if x >= 180:
x -= 360
elif x < -180:
x += 360
return x
def vunit(v):
"""Returns voltage with proper unit."""
units = ("V", "mV", "uV")
factors = (1, 1e-3, 1e-6)
for i in range(0, len(units)):
if abs(v) >= factors[i]: return f"{v / factors[i]}{units[i]}"
return "1uV"
def active_channels():
"""Returns active DSO channels."""
channels = []
for ch in ['C1', 'C2', 'C3', 'C4']:
s = dso.query(f"{ch}:TRA?").strip()
match = re.search("^(C\d):TRA ON$", s)
if match: channels.append(match.group(1))
return channels
def vautoscale(ch, iterations = 2, vdiv = 7.5):
"""Automatic vertical offset and scale adjustment."""
# Measures Vmin and Vmax, then adjusts vertical offset and scale
for i in range(0, iterations):
if i > 0: time.sleep(0.1)
s = dso.query(f"{ch}:PAVA? MIN").strip()
match1 = re.search("^C\d:PAVA MIN,(.*)V$", s)
s = dso.query(f"{ch}:PAVA? MAX").strip()
match2 = re.search("^C\d:PAVA MAX,(.*)V$", s)
if match1 and match2 and not match1.group(1).startswith("*") and not match2.group(1).startswith("*"):
vmin = float(match1.group(1))
vmax = float(match2.group(1))
vpp = vmax - vmin
v0 = vmin + vpp / 2
dso.write(f"{ch}:OFST {-v0:.5f}V")
dso.write(f"{ch}:VDIV {vpp / vdiv:.5f}V")
else:
errprint(f"Error parsing for vautoscale: {s}")
sys.exit(1)
def hscale(f):
"""Computes optimal horizontal scale for given frequency."""
t = 1 / (f * 4)
units = ("ns", "us", "ms", "s")
factors = (1e-9, 1e-6, 1e-3, 1)
for i in range(0, len(units)):
for j in (1, 2, 5, 10, 20, 50, 100, 200, 500):
if j * factors[i] > t: return f"{j}{units[i]}"
return "1s"
def dBV(v):
return 20 * math.log10(v)
def measure_hscale():
"""Returns horizontal scale per division."""
s = dso.query(f"TDIV?").strip()
match = re.search("^TDIV ([-+0-9.E]+)S$", s)
if match:
t = float(match.group(1))
else:
errprint(f"Error parsing: {s}")
sys.exit(2)
return t
def measure_vpp(ch):
"""Returns Vpp of given DSO channel."""
# Assumes optimal vertical scale
s = dso.query(f"{ch}:PAVA? PKPK").strip()
match = re.search("^C\d:PAVA PKPK,(.*)V$", s)
if match and not match.group(1).startswith("*"):
vpp = float(match.group(1))
else:
errprint(f"Error parsing Vpp: {s}")
sys.exit(2)
return vpp
def measure_mean(ch):
"""Returns mean of given DSO channel."""
# Assumes optimal vertical scale
s = dso.query(f"{ch}:PAVA? MEAN").strip()
match = re.search("^C\d:PAVA MEAN,(.*)V$", s)
if match and not match.group(1).startswith("*"):
v = float(match.group(1))
else:
errprint(f"Error parsing V: {s}")
sys.exit(2)
return v
def measure_level(ch):
"""Returns V of given DSO channel at the trigger point."""
# Assumes optimal vertical scale
s = dso.query(f"{ch}:PAVA? LEVELX").strip()
match = re.search("^C\d:PAVA LEVELX,(.*)V$", s)
if match:
sample = float(match.group(1))
else:
errprint(f"Error parsing: {s}")
sys.exit(2)
return sample
def measure_phase(ch1, ch2):
"""Returns phase difference between two DSO channels."""
# Assumes optimal horizontal and vertical scales
dso.write(f"{ch2}-{ch1}:MEAD? PHA") # Query returns extra \xa1\xe3 data, so using read_raw
s = dso.read_raw()[:-2].decode()
match = re.search("^C\d-C\d:MEAD PHA,(.*)$", s)
if match:
if match.group(1).startswith("*"):
phase = 0
else:
phase = norm_angle(float(match.group(1)))
else:
errprint(f"Error parsing phase: {s}")
sys.exit(4)
return phase
def close_resources():
for instr in resources:
resources[instr].close()
rm = pyvisa.ResourceManager()
resources = {}
for instr in rm.list_resources():
match = re.search("::SDS.*::INSTR$", instr)
if match:
resources['dso'] = rm.open_resource(instr)
match = re.search("::SDG.*::INSTR$", instr)
if match:
resources['awg'] = rm.open_resource(instr)
dso = resources['dso'] if 'dso' in resources else None
awg = resources['awg'] if 'awg' in resources else None
# Module Tests
if __name__ == "__main__":
assert norm_angle(240) == -120
assert vunit(2.5e-3) == "2.5mV"