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, read_trace |
|
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, idx=0): |
|
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, idx) |
|
74 return header, read_trace(data, idx, header["Number of elements"]) |
|
75 |
|
76 def main(): |
|
77 import sys |
|
78 import optparse |
|
79 opt = optparse.OptionParser("A simple tool for tracing a dumped trace") |
|
80 opt.add_option('-f', '--filename', default=None, |
|
81 dest='filename', |
|
82 help='Output filename. If not set, read from stdin') |
|
83 opt.add_option('-m', '--mode', default='binary', |
|
84 dest='mode', |
|
85 help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', |
|
86 ) |
|
87 opt.add_option('-d', '--display-header', default=False, |
|
88 action="store_true", |
|
89 dest="displayheader", |
|
90 help="Display the trace header") |
|
91 opt.add_option('-P', '--noplot-trace', default=True, |
|
92 action="store_false", |
|
93 dest="plot", |
|
94 help="Do not display the plot of the trace") |
|
95 opt.add_option('-x', '--xmode', default='lin', |
|
96 dest='xmode', |
|
97 help='X coordinate mode (may be "lin" [default] or "log")') |
|
98 opt.add_option('-y', '--ymode', default='lin', |
|
99 dest='ymode', |
|
100 help='Y coordinate mode (may be "lin" [default], "log" or "db")') |
|
101 |
|
102 options, argv = opt.parse_args(sys.argv) |
|
103 |
|
104 |
|
105 if options.filename is None: |
|
106 print "Can't deal stdin for now..." |
|
107 sys.exit(1) |
|
108 try: |
|
109 header, data = decode_trace(open(options.filename, 'rb').read()) |
|
110 except Exception, e: |
|
111 print "ERROR: can't read %s an interpret it as a HP3562 trace"%options.filename |
|
112 print e |
|
113 sys.exit(1) |
|
114 |
|
115 if options.displayheader: |
|
116 print format_header(header, HEADER, 100) |
|
117 if options.plot: |
|
118 f0 = header['Start freq value'] |
|
119 dx = header['Delta X-axis'] |
|
120 n = header['Number of elements'] |
|
121 x = numpy.linspace(f0, f0+dx*n, len(data)) |
|
122 y = data.copy() |
|
123 |
|
124 import pylab |
|
125 if options.ymode != "lin": |
|
126 minv = min(y[y>0]) |
|
127 y[y==0] = minv |
|
128 y = numpy.log10(y) |
|
129 if options.ymode == "db": |
|
130 y = y*10 |
|
131 pylab.ylabel('db') |
|
132 pylab.grid() |
|
133 pylab.plot(x, y) |
|
134 pylab.xlabel('frequency') |
|
135 pylab.show() |
|
136 |
|
137 |
|
138 if __name__ == "__main__": |
|
139 main() |
|