HP3562A/read_trace.py

changeset 14
07e2cbf140df
parent 13
78e3e839658b
child 15
b930440af354
equal deleted inserted replaced
13:78e3e839658b 14:07e2cbf140df
1 # -*- coding: utf-8 -*-
2
3 import struct
4 import numpy
5
6
7 def decode_float(s):
8 assert len(s) in [4,8]
9 # exponential term
10 e = ord(s[-1])
11 if e & 0x80:
12 e = e - 256
13
14 # mantissa
15 m = [ord(x) for x in s[:-1]]
16 M = 0.
17 for i in range(len(s)-1):
18 #M += m[i]<<(i*8)
19 M += float(m[i])/2**((i+1)*8)
20 # XXX how do we deal negative numbers?
21 #if m[0] & 0x80:
22 # M = M - 2^(len(s))
23 return M * 2**(e+1)
24
25 def decode_string(s):
26 nb = ord(s[0])
27 s = s[1:nb+2]
28 r = ""
29 # XXX why do we need to do this? It's not described in the manual...
30 for c in s:
31 r += chr(ord(c) & 0x7F)
32 return r
33
34 EDSP = {0: "No data",
35 1: "Frequency response",
36 2: "Power spectrum 1",
37 3: "Power spectrum 2",
38 4: "Coherence",
39 5: "Cross spectrum",
40 6: "Input time 1",
41 7: "Input time 2",
42 8: "Input linear spectrum 1",
43 9: "Input linear spectrum 2",
44 10: "Impulse response",
45 11: "Cross correlation",
46 12: "Auto correlation 1",
47 13: "Auto correlation 2",
48 14: "Histogram 1",
49 15: "Histogram 2",
50 16: "Cumulative density function 1",
51 17: "Cumulative density function 2",
52 18: "Probability density function 1",
53 19: "Probability density function 2",
54 20: "Average linear spectrum 1",
55 21: "Average linear spectrum 2",
56 22: "Average time record 1",
57 23: "Average time record 2",
58 24: "Synthesis pole-zeros",
59 25: "Synthesis pole-residue",
60 26: "Synthesis polynomial",
61 27: "Synthesis constant",
62 28: "Windowed time record 1",
63 29: "Windowed time record 2",
64 30: "Windowed linear spectrum 1",
65 31: "Windowed linear spectrum 2",
66 32: "Filtered time record 1",
67 33: "Filtered time record 2",
68 34: "Filtered linear spectrum 1",
69 35: "Filtered linear spectrum 2",
70 36: "Time capture buffer",
71 37: "Captured linear spectrum",
72 38: "Captured time record",
73 39: "Throughput time record 1",
74 40: "Throughput time record 2",
75 41: "Curve fit",
76 42: "Weighted function",
77 43: "Not used",
78 44: "Orbits",
79 45: "Demodulation polar",
80 46: "Preview demod record 1",
81 47: "Preview demod record 2",
82 48: "Preview demod linear spectrum 1",
83 49: "Preview demod linear spectrum 2",
84 }
85
86 ECH = {0: "Channel 1",
87 1: "Channel 2",
88 2: "Channel 1&2",
89 3: "No channel",
90 }
91
92 EOVR = ECH
93
94 EDOM = {0: 'Time',
95 1: 'Frequency',
96 2: 'Voltage (amplitude)',
97 }
98
99 EVLT = {0: "Peak",
100 1: "RMS",
101 2: "Volt (indicates peak only)",
102 }
103
104 EAMP = {0: "Volts",
105 1: "Volts squared",
106 2: "PSD (V²/Hz)",
107 3: "ESD (V²s/Hz)",
108 4: "PSD¹² (V/Hz¹²)",
109 5: "No unit",
110 6: "Unit volts",
111 7: "Unit volts²",
112 }
113
114 EXAXIS= {0: "No units",
115 1: "Hertz",
116 2: "RPM",
117 3: "Orders",
118 4: "Seconds",
119 5: "Revs",
120 6: "Degrees",
121 7: "dB",
122 8: "dBV",
123 9: "Volts",
124 10: "V Hz¹²",
125 11: "Hz/s",
126 12: "V/EU",
127 13: "Vrms",
128 14: "V²/Hz",
129 15: "%",
130 16: "Points",
131 17: "Records",
132 18: "Ohms",
133 19: "Hertz/octave",
134 20: "Pulse/Rev",
135 21: "Decades",
136 22: "Minutes",
137 23: "V²s/Hz",
138 24: "Octave",
139 25: "Seconds/Decade",
140 26: "Seconds/Octave",
141 27: "Hz/Point",
142 28: "Points/Sweep",
143 29: "Points/Decade",
144 30: "Points/Octave",
145 31: "V/Vrms",
146 32: "V²",
147 33: "EU referenced to chan 1",
148 34: "EU referenced to chan 2",
149 35: "EU value",
150 }
151
152 EMEAS = {0: "Linear resolution",
153 1: "Log resolution",
154 2: "Swept sine",
155 3: "Time capture",
156 4: "Linear resolution throughput",
157 }
158
159 EDEMOD1 = {45: "AM",
160 46: "FM",
161 47: "PM",
162 }
163
164 EDEMOD2 = EDEMOD1
165
166 EAVG = {0: "No data",
167 1: "Not averaged",
168 2: "Averaged",}
169
170
171
172 EWIN = {0: "N/A",
173 1: "Hann",
174 2: "Flat top",
175 3: "Uniforme",
176 4: "Exponential",
177 5: "Force",
178 6: "Force chan 1/expon chan 2",
179 7: "Expon chan 1/force chan 2",
180 8: "User",
181 }
182
183 HEADER = [ ("Display function", EDSP, 'h', 2),
184 ('Number of elements', int, 'h', 2),
185 ('Displayed elements', int, 'h', 2),
186 ('Number of averages', int, 'h', 2),
187 ('Channel selection', ECH, 'h', 2),
188 ('Overflow status', EOVR, 'h', 2),
189 ('Overlap percentage', int, 'h', 2),
190 ('Domain', EDOM, 'h', 2),
191 ('Volts peak/rms', EVLT, 'h', 2),
192 ('Amplitude units', EAMP, 'h', 2),
193 ('X axis units', EXAXIS, 'h', 2),
194 ('Auto math label', str, 's', 14),
195 ('Trace label', str, 's', 22),
196 ('EU label 1', str, 's', 6),
197 ('EU label 2', str, 's', 6),
198 ('Float/Interger', bool, 'h', 2),
199 ('Complex/Real', bool, 'h', 2),
200 ('Live/Recalled', bool, 'h', 2),
201 ('Math result', bool, 'h', 2),
202 ('Real/Complex input', bool, 'h', 2),
203 ('Log/Linear data', bool, 'h', 2),
204 ('Auto math', bool, 'h', 2),
205 ('Real time status', bool, 'h', 2),
206 ('Measurement mode', EMEAS, 'h', 2),
207 ('Window', EWIN, 'h', 2),
208 ('Demod type channel 1', EDEMOD1, 'h', 2),
209 ('Demod type channel 2', EDEMOD2, 'h', 2),
210 ('Demod active channel 1', bool, 'h', 2),
211 ('Demod active channel 2', bool, 'h', 2),
212 ('Average status', EAVG, 'h', 2),
213 ('Not used', int, 'hh', 4),
214 ('Samp freq/2 (real)', decode_float, None, 4),
215 ('Samp freq/2 (imag)', decode_float, None, 4),
216 ('Not used', decode_float, None, 4),
217 ('Delta X-axis', decode_float, None, 4),
218 ('Max range', decode_float, None, 4),
219 ('Start time value', decode_float, None, 4),
220 ('Expon wind const 1', decode_float, None, 4),
221 ('Expon wind const 2', decode_float, None, 4),
222 ('EU value chan 1', decode_float, None, 4),
223 ('EU value chan 2', decode_float, None, 4),
224 ('Trig delay chan 1', decode_float, None, 4),
225 ('Trig delay chan 2', decode_float, None, 4),
226 ('Start freq value', decode_float, None, 8),
227 ('Start data value', decode_float, None, 8),
228 ]
229
230 def decode_trace(data):
231 d = data
232
233 typ = d[:2]
234 assert typ == "#A"
235
236 totlen = struct.unpack('>h', d[2:4])[0]
237 idx = 4
238 tt=0
239 header = {}
240 for i, (nam, dtype, fmt, nbytes) in enumerate(HEADER):
241 if dtype == str:
242 val = decode_string(d[idx:])
243 else:
244 if fmt:
245 v = struct.unpack('>'+fmt, d[idx: idx+nbytes])[0]
246 if isinstance(dtype, dict):
247 val = dtype.get(int(v), "N/A")
248 else:
249 val = dtype(v)
250 else:
251 val = dtype(d[idx: idx+nbytes])
252 header[nam] = val
253 idx += nbytes
254 resu = []
255 for i in range(header["Number of elements"]):
256 resu.append(decode_float(d[idx: idx+4]))
257 idx += 4
258 return header, numpy.array(resu, dtype=float)
259
260 def format_header(header, head_struct, columns=80):
261 todisp = []
262 for row in head_struct:
263 key = row[0]
264 val = header.get(key, "N/A")
265 if isinstance(val, basestring):
266 val = repr(val)
267 else:
268 val = str(val)
269 todisp.append((key+":", val))
270 maxk = max([len(k) for k, v in todisp])
271 maxv = max([len(v) for k, v in todisp])
272 fmt = "%%-%ds %%-%ds"%(maxk, maxv)
273 w = maxk+maxv+4
274 ncols = columns/w
275 nrows = len(todisp)/ncols
276 print "w=", w
277 print "ncols=", ncols
278 print "nrows=", nrows
279 res = ""
280 for i in range(nrows):
281 res += "| ".join([fmt%todisp[j*nrows+i] for j in range(ncols)]) + "\n"
282 return res
283
284
285 if __name__ == "__main__":
286 import sys
287 import optparse
288 opt = optparse.OptionParser("A simple tool for tracing a dumped trace")
289 opt.add_option('-f', '--filename', default=None,
290 dest='filename',
291 help='Output filename. If not set, read from stdin')
292 opt.add_option('-m', '--mode', default='binary',
293 dest='mode',
294 help='Dumping mode (may be "binary" [default], "ascii" or "ansi")',
295 )
296 opt.add_option('-d', '--display-header', default=False,
297 action="store_true",
298 dest="displayheader",
299 help="Display the trace header")
300 opt.add_option('-P', '--noplot-trace', default=True,
301 action="store_false",
302 dest="plot",
303 help="Do not display the plot of the trace")
304 opt.add_option('-x', '--xmode', default='lin',
305 dest='xmode',
306 help='X coordinate mode (may be "lin" [default] or "log")')
307 opt.add_option('-y', '--ymode', default='lin',
308 dest='ymode',
309 help='Y coordinate mode (may be "lin" [default], "log" or "db")')
310
311 options, argv = opt.parse_args(sys.argv)
312
313
314 if options.filename is None:
315 print "Can't deal stdin for now..."
316 sys.exit(1)
317 try:
318 header, data = decode_trace(open(options.filename, 'rb').read())
319 except Exception, e:
320 print "ERROR: can't read %s an interpret it as a HP3562 trace"%options.filename
321 print e
322 sys.exit(1)
323
324 if options.displayheader:
325 print format_header(header, HEADER, 100)
326 if options.plot:
327 f0 = header['Start freq value']
328 dx = header['Delta X-axis']
329 n = header['Number of elements']
330 x = numpy.linspace(f0, f0+dx*n, len(data))
331 y = data.copy()
332
333 import pylab
334 if options.ymode != "lin":
335 minv = min(y[y>0])
336 y[y==0] = minv
337 y = numpy.log10(y)
338 if options.ymode == "db":
339 y = y*10
340 pylab.ylabel('db')
341 pylab.grid()
342 pylab.plot(x, y)
343 pylab.xlabel('frequency')
344 pylab.show()
345
346

mercurial