|
1 # -*- coding: utf-8 -*- |
|
2 """ |
|
3 trace_decoder |
|
4 ============= |
|
5 |
|
6 Module for decoding a trace generated by the HP3562A DSA, using the |
|
7 GPIB command "DDBN" (Dump Data BiNary). |
|
8 |
|
9 This file can be exectued as a python script. Use '-h' for more informations. |
|
10 """ |
|
11 import struct |
|
12 import numpy |
|
13 from HP3562A import format_header, decode_float, decode_string, decode_header |
|
14 from HP3562A.enum_types import * |
|
15 |
|
16 |
|
17 HEADER = [ ("Display function", EDSP, 'h', 2), |
|
18 ('Number of elements', int, 'h', 2), |
|
19 ('Displayed elements', int, 'h', 2), |
|
20 ('Number of averages', int, 'h', 2), |
|
21 ('Channel selection', ECH, 'h', 2), |
|
22 ('Overflow status', EOVR, 'h', 2), |
|
23 ('Overlap percentage', int, 'h', 2), |
|
24 ('Domain', EDOM, 'h', 2), |
|
25 ('Volts peak/rms', EVLT, 'h', 2), |
|
26 ('Amplitude units', EAMP, 'h', 2), |
|
27 ('X axis units', EXAXIS, 'h', 2), |
|
28 ('Auto math label', str, 's', 14), |
|
29 ('Trace label', str, 's', 22), |
|
30 ('EU label 1', str, 's', 6), |
|
31 ('EU label 2', str, 's', 6), |
|
32 ('Float/Interger', bool, 'h', 2), |
|
33 ('Complex/Real', bool, 'h', 2), |
|
34 ('Live/Recalled', bool, 'h', 2), |
|
35 ('Math result', bool, 'h', 2), |
|
36 ('Real/Complex input', bool, 'h', 2), |
|
37 ('Log/Linear data', bool, 'h', 2), |
|
38 ('Auto math', bool, 'h', 2), |
|
39 ('Real time status', bool, 'h', 2), |
|
40 ('Measurement mode', EMEAS, 'h', 2), |
|
41 ('Window', EWIN, 'h', 2), |
|
42 ('Demod type channel 1', EDEMOD, 'h', 2), |
|
43 ('Demod type channel 2', EDEMOD, 'h', 2), |
|
44 ('Demod active channel 1', bool, 'h', 2), |
|
45 ('Demod active channel 2', bool, 'h', 2), |
|
46 ('Average status', EAVG, 'h', 2), |
|
47 ('Not used', int, 'hh', 4), |
|
48 ('Samp freq/2 (real)', decode_float, None, 4), |
|
49 ('Samp freq/2 (imag)', decode_float, None, 4), |
|
50 ('Not used', decode_float, None, 4), |
|
51 ('Delta X-axis', decode_float, None, 4), |
|
52 ('Max range', decode_float, None, 4), |
|
53 ('Start time value', decode_float, None, 4), |
|
54 ('Expon wind const 1', decode_float, None, 4), |
|
55 ('Expon wind const 2', decode_float, None, 4), |
|
56 ('EU value chan 1', decode_float, None, 4), |
|
57 ('EU value chan 2', decode_float, None, 4), |
|
58 ('Trig delay chan 1', decode_float, None, 4), |
|
59 ('Trig delay chan 2', decode_float, None, 4), |
|
60 ('Start freq value', decode_float, None, 8), |
|
61 ('Start data value', decode_float, None, 8), |
|
62 ] |
|
63 |
|
64 def decode_trace(data): |
|
65 """ |
|
66 Decode the data (as generated by the HP3562A DSA in response to a |
|
67 "DDBN" command), and returns a couple (header, values). |
|
68 |
|
69 header is the dictionnary of the header of the dumped data block, |
|
70 value is a numpy array holding the trace (vector of float or |
|
71 complex values). |
|
72 """ |
|
73 header, idx = decode_header(data, HEADER) |
|
74 resu = [] |
|
75 for i in range(header["Number of elements"]): |
|
76 resu.append(decode_float(data[idx: idx+4])) |
|
77 idx += 4 |
|
78 return header, numpy.array(resu, dtype=float) |
|
79 |
|
80 |
|
81 |
|
82 def main(): |
|
83 import sys |
|
84 import optparse |
|
85 opt = optparse.OptionParser("A simple tool for tracing a dumped trace") |
|
86 opt.add_option('-f', '--filename', default=None, |
|
87 dest='filename', |
|
88 help='Output filename. If not set, read from stdin') |
|
89 opt.add_option('-m', '--mode', default='binary', |
|
90 dest='mode', |
|
91 help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', |
|
92 ) |
|
93 opt.add_option('-d', '--display-header', default=False, |
|
94 action="store_true", |
|
95 dest="displayheader", |
|
96 help="Display the trace header") |
|
97 opt.add_option('-P', '--noplot-trace', default=True, |
|
98 action="store_false", |
|
99 dest="plot", |
|
100 help="Do not display the plot of the trace") |
|
101 opt.add_option('-x', '--xmode', default='lin', |
|
102 dest='xmode', |
|
103 help='X coordinate mode (may be "lin" [default] or "log")') |
|
104 opt.add_option('-y', '--ymode', default='lin', |
|
105 dest='ymode', |
|
106 help='Y coordinate mode (may be "lin" [default], "log" or "db")') |
|
107 |
|
108 options, argv = opt.parse_args(sys.argv) |
|
109 |
|
110 |
|
111 if options.filename is None: |
|
112 print "Can't deal stdin for now..." |
|
113 sys.exit(1) |
|
114 try: |
|
115 header, data = decode_trace(open(options.filename, 'rb').read()) |
|
116 except Exception, e: |
|
117 print "ERROR: can't read %s an interpret it as a HP3562 trace"%options.filename |
|
118 print e |
|
119 sys.exit(1) |
|
120 |
|
121 if options.displayheader: |
|
122 print format_header(header, HEADER, 100) |
|
123 if options.plot: |
|
124 f0 = header['Start freq value'] |
|
125 dx = header['Delta X-axis'] |
|
126 n = header['Number of elements'] |
|
127 x = numpy.linspace(f0, f0+dx*n, len(data)) |
|
128 y = data.copy() |
|
129 |
|
130 import pylab |
|
131 if options.ymode != "lin": |
|
132 minv = min(y[y>0]) |
|
133 y[y==0] = minv |
|
134 y = numpy.log10(y) |
|
135 if options.ymode == "db": |
|
136 y = y*10 |
|
137 pylab.ylabel('db') |
|
138 pylab.grid() |
|
139 pylab.plot(x, y) |
|
140 pylab.xlabel('frequency') |
|
141 pylab.show() |
|
142 |
|
143 |
|
144 if __name__ == "__main__": |
|
145 main() |