Tue, 18 Dec 2007 00:23:10 +0100
several improvements; add an internal state reader
HP3562A/__init__.py | file | annotate | diff | comparison | revisions | |
HP3562A/dump_datablock.py | file | annotate | diff | comparison | revisions | |
HP3562A/dump_trace.py | file | annotate | diff | comparison | revisions | |
HP3562A/enum_types.py | file | annotate | diff | comparison | revisions | |
HP3562A/read_trace.py | file | annotate | diff | comparison | revisions | |
HP3562A/state_decoder.py | file | annotate | diff | comparison | revisions | |
HP3562A/trace_decoder.py | file | annotate | diff | comparison | revisions | |
bin/dump_datablock | file | annotate | diff | comparison | revisions | |
bin/read_state | file | annotate | diff | comparison | revisions | |
bin/read_trace | file | annotate | diff | comparison | revisions | |
prologix.py | file | annotate | diff | comparison | revisions |
--- a/HP3562A/__init__.py Mon Dec 17 18:59:45 2007 +0100 +++ b/HP3562A/__init__.py Tue Dec 18 00:23:10 2007 +0100 @@ -12,8 +12,90 @@ --------- """ +import struct +import gpib -from gpib import gpib +######################################## +# HP3562A internal binary types decoders + +def decode_float(s): + assert len(s) in [4,8] + # exponential term + e = ord(s[-1]) + if e & 0x80: + e = e - 256 + + # mantissa + m = [ord(x) for x in s[:-1]] + M = 0. + for i in range(len(s)-1): + #M += m[i]<<(i*8) + M += float(m[i])/2**((i+1)*8) + # XXX how do we deal negative numbers? + #if m[0] & 0x80: + # M = M - 2^(len(s)) + return M * 2**(e+1) + +def decode_string(s): + nb = ord(s[0]) + s = s[1:nb+2] + r = "" + # XXX why do we need to do this? It's not described in the manual... + for c in s: + r += chr(ord(c) & 0x7F) + return r + + +### +# Some useful functions +def format_header(header, head_struct, columns=80): + """ + Pretty print a data block (trace, state or coord) + """ + todisp = [] + for row in head_struct: + key = row[0] + val = header.get(key, "N/A") + if isinstance(val, basestring): + val = repr(val) + else: + val = str(val) + todisp.append((key+":", val)) + maxk = max([len(k) for k, v in todisp]) + maxv = max([len(v) for k, v in todisp]) + fmt = "%%-%ds %%-%ds"%(maxk, maxv) + w = maxk+maxv+4 + ncols = columns/w + nrows = len(todisp)/ncols + res = "" + for i in range(nrows): + res += "| ".join([fmt%todisp[j*nrows+i] for j in range(ncols)]) + "\n" + return res + +def decode_header(data, header_struct): + d = data + typ = d[:2] + assert typ == "#A" + + totlen = struct.unpack('>h', d[2:4])[0] + idx = 4 + tt=0 + header = {} + for i, (nam, dtype, fmt, nbytes) in enumerate(header_struct): + if dtype == str: + val = decode_string(d[idx:]) + else: + if fmt: + v = struct.unpack('>'+fmt, d[idx: idx+nbytes])[0] + if isinstance(dtype, dict): + val = dtype.get(int(v), "N/A") + else: + val = dtype(v) + else: + val = dtype(d[idx: idx+nbytes]) + header[nam] = val + idx += nbytes + return header, idx ##################### # HP3562A constants @@ -102,3 +184,5 @@ (0x40, "ACDA", "Accept date"), #... ] + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HP3562A/dump_datablock.py Tue Dec 18 00:23:10 2007 +0100 @@ -0,0 +1,78 @@ +import sys +import time +import gpib +import prologix + + +class HP3562dumper(prologix.GPIB): + + MODES = {'trace': 'DD', + 'state': 'DS', + 'coord': 'DC', + } + + FORMATS = {'binary': 'BN', + 'ascii': 'AS', + 'ansi': 'AN'} + + def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1, + address=0): + super(HP3562dumper, self).__init__(device, baudrate, timeout, address, mode=1) + + def dump(self, mode='trace', format="binary"): + format = format.lower() + mode = mode.lower() + assert mode in self.MODES + assert format in self.FORMATS + cmd = self.MODES[mode] + self.FORMATS[format] + + res = "" + print "command = ", cmd + self._cnx.write('%s\r'%cmd) + i = 0 + while i<self._retries: + l = self._cnx.readline() + if l.strip() == "": + i += 1 + time.sleep(self._timeout) + continue + res += l + i = 0 + return res + + + +def main(): + import optparse + opt = optparse.OptionParser("A simple tool for dumping the current trace") + opt.add_option('-f', '--filename', default=None, + dest='filename', + help='Output filename. If not set, write to stdout') + opt.add_option('-d', '--device', default='/dev/ttyUSB0', + dest='device', + help='Device of the RS232 connection (default: /dev/ttyUSB0)', + ) + opt.add_option('-a', '--address', default=0, + dest='address', + help='GPIB address of the device', + ) + opt.add_option('-b', '--block', default='trace', + dest='block', + help='Data block to dump (may be "trace" [default], "state" or "coord")', + ) + opt.add_option('-m', '--mode', default='binary', + dest='mode', + help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', + ) + options, argv = opt.parse_args(sys.argv) + + cnx = HP3562dumper(device=options.device, address=int(options.address)) + res = cnx.dump(mode=options.block, format=options.mode) + sys.stderr.write("read %s bytes\n"%(len(res))) + if options.filename: + open(options.filename, 'w').write(res) + else: + print res + +if __name__=='__main__': + main()
--- a/HP3562A/dump_trace.py Mon Dec 17 18:59:45 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -import sys -import time -import gpib - -class HP3562dumper(gpib.GPIB): - - MODES = {'trace': 'DD', - 'state': 'DS', - 'coord': 'DC', - } - - FORMATS = {'binary': 'BN', - 'ascii': 'AS', - 'ansi': 'AN'} - - def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1, - address=0): - super(HP3562dumper, self).__init__(device, baudrate, timeout, address, mode=1) - - def dump(self, mode='trace', format="binary"): - format = format.lower() - mode = mode.lower() - assert mode in self.MODES - assert format in self.FORMATS - cmd = self.MODES[mode] + self.FORMATS[format] - - res = "" - print "command = ", cmd - self._cnx.write('%s\r'%cmd) - i = 0 - while i<self._retries: - l = self._cnx.readline() - if l.strip() == "": - i += 1 - time.sleep(self._timeout) - continue - res += l - i = 0 - return res - - - -if __name__=='__main__': - import optparse - opt = optparse.OptionParser("A simple tool for dumping the current trace") - opt.add_option('-f', '--filename', default=None, - dest='filename', - help='Output filename. If not set, write to stdout') - opt.add_option('-d', '--device', default='/dev/ttyUSB0', - dest='device', - help='Device of the RS232 connection (default: /dev/ttyUSB0)', - ) - opt.add_option('-a', '--address', default=0, - dest='address', - help='GPIB address of the device', - ) - opt.add_option('-b', '--block', default='trace', - dest='block', - help='Data block to dump (may be "trace" [default], "state" or "coord")', - ) - opt.add_option('-m', '--mode', default='binary', - dest='mode', - help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', - ) - options, argv = opt.parse_args(sys.argv) - - cnx = HP3562dumper(device=options.device, address=int(options.address)) - res = cnx.dump(mode=options.block, format=options.mode) - sys.stderr.write("read %s bytes\n"%(len(res))) - if options.filename: - open(options.filename, 'w').write(res) - else: - print res -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HP3562A/enum_types.py Tue Dec 18 00:23:10 2007 +0100 @@ -0,0 +1,248 @@ +# -*- coding: utf-8 -*- + +EDSP = {0: "No data", + 1: "Frequency response", + 2: "Power spectrum 1", + 3: "Power spectrum 2", + 4: "Coherence", + 5: "Cross spectrum", + 6: "Input time 1", + 7: "Input time 2", + 8: "Input linear spectrum 1", + 9: "Input linear spectrum 2", + 10: "Impulse response", + 11: "Cross correlation", + 12: "Auto correlation 1", + 13: "Auto correlation 2", + 14: "Histogram 1", + 15: "Histogram 2", + 16: "Cumulative density function 1", + 17: "Cumulative density function 2", + 18: "Probability density function 1", + 19: "Probability density function 2", + 20: "Average linear spectrum 1", + 21: "Average linear spectrum 2", + 22: "Average time record 1", + 23: "Average time record 2", + 24: "Synthesis pole-zeros", + 25: "Synthesis pole-residue", + 26: "Synthesis polynomial", + 27: "Synthesis constant", + 28: "Windowed time record 1", + 29: "Windowed time record 2", + 30: "Windowed linear spectrum 1", + 31: "Windowed linear spectrum 2", + 32: "Filtered time record 1", + 33: "Filtered time record 2", + 34: "Filtered linear spectrum 1", + 35: "Filtered linear spectrum 2", + 36: "Time capture buffer", + 37: "Captured linear spectrum", + 38: "Captured time record", + 39: "Throughput time record 1", + 40: "Throughput time record 2", + 41: "Curve fit", + 42: "Weighted function", + 43: "Not used", + 44: "Orbits", + 45: "Demodulation polar", + 46: "Preview demod record 1", + 47: "Preview demod record 2", + 48: "Preview demod linear spectrum 1", + 49: "Preview demod linear spectrum 2", + } + +ECH = {0: "Channel 1", + 1: "Channel 2", + 2: "Channel 1&2", + 3: "No channel", + } + +EOVR = ECH + +EDOM = {0: 'Time', + 1: 'Frequency', + 2: 'Voltage (amplitude)', + } + +EVLT = {0: "Peak", + 1: "RMS", + 2: "Volt (indicates peak only)", + } + +EAMP = {0: "Volts", + 1: "Volts squared", + 2: "PSD (V²/Hz)", + 3: "ESD (V²s/Hz)", + 4: "PSD¹² (V/Hz¹²)", + 5: "No unit", + 6: "Unit volts", + 7: "Unit volts²", + } + +EXAXIS= {0: "No units", + 1: "Hertz", + 2: "RPM", + 3: "Orders", + 4: "Seconds", + 5: "Revs", + 6: "Degrees", + 7: "dB", + 8: "dBV", + 9: "Volts", + 10: "V Hz¹²", + 11: "Hz/s", + 12: "V/EU", + 13: "Vrms", + 14: "V²/Hz", + 15: "%", + 16: "Points", + 17: "Records", + 18: "Ohms", + 19: "Hertz/octave", + 20: "Pulse/Rev", + 21: "Decades", + 22: "Minutes", + 23: "V²s/Hz", + 24: "Octave", + 25: "Seconds/Decade", + 26: "Seconds/Octave", + 27: "Hz/Point", + 28: "Points/Sweep", + 29: "Points/Decade", + 30: "Points/Octave", + 31: "V/Vrms", + 32: "V²", + 33: "EU referenced to chan 1", + 34: "EU referenced to chan 2", + 35: "EU value", + } + +EMEAS = {0: "Linear resolution", + 1: "Log resolution", + 2: "Swept sine", + 3: "Time capture", + 4: "Linear resolution throughput", + } + +EDEMOD = {45: "AM", + 46: "FM", + 47: "PM", + } + +EAVG = {0: "No data", + 1: "Not averaged", + 2: "Averaged",} + +EWIN = {0: "N/A", + 1: "Hann", + 2: "Flat top", + 3: "Uniforme", + 4: "Exponential", + 5: "Force", + 6: "Force chan 1/expon chan 2", + 7: "Expon chan 1/force chan 2", + 8: "User", + } + +EMTYP = {0: "Frequency responce", + 1: "Cross correlation", + 2: "Power spectrum", + 3: "Auto correlation", + 4: "Histogram", + 5: "No measurement", + } + +EWINTYP = { 11: "Hanning", + 12: "Flat top", + 13: "Uniform", + 14: "User window", + 15: "Force/Exponential", + } + +EFEXPW = { 0: "Force", + 1: "Exponential", + } + +EAVGTYP = { 6: "Stable", + 7: "Exponential", + 8: "Peak", + 9: "Continuous peak", + 10: "Averaging off", + } + +ETRGTYP = { 18: "Free run", + 19: "Channel 1", + 20: "Channel 2", + 21: "External", + 22: "Source trigger", + 23: "HP-IB trigger", + } + +ETRGSLP = { 16: "Positive", + 17: "Negative", + } + +EPRVTYP = { 0: "Manual preview", + 1: "Timed preview", + 2: "Preview off", + } + +ESMPTYP = { 24: "Internal sample", + 25: "External sample", + } + +ERNGUNT = EXAXIS + +ERNGTYP = { 26: "Auto range on", + 27: "Auto range off", + 28: "Auto range set", + } + +EINCPL = { 29: "AC", + 30: "DC", + } + +ESRCTYP = { 31: "Source off", + 32: "Random noise", + 33: "Burst random", + 34: "Periodic chirp", + 35: "Burst chirp", + 36: "Swept sine", + 37: "Fixed sine", + } + +ESWPDIR = { 41: "Up", + 42: "Sweep hold", + 43: "Manual sweep", + 44: "Down", + } + +ESWPMOD = { 39: "Linear sweep", + 40: "Log sweep", + } + +EEXTSMPFREQUNT = EXAXIS + +EBNDUNT = EXAXIS + +ESWPRATEUNT = EXAXIS + +EAUGAINREFCH = { 0: "Channel 1", + 1: "Channel 2", + 2: "Not used", + 3: "No channel", + } +EDEMODCH = { 0: "Channel 1", + 1: "Channel 2", + 2: "Both channels", + 3: "No channel", + } + +ESRCLVLUNT = EXAXIS + +ESRCDCOFFST = EXAXIS + +ETRGLVLUNT = EXAXIS + +ECPTLGHUNT = EXAXIS
--- a/HP3562A/read_trace.py Mon Dec 17 18:59:45 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,346 +0,0 @@ -# -*- coding: utf-8 -*- - -import struct -import numpy - - -def decode_float(s): - assert len(s) in [4,8] - # exponential term - e = ord(s[-1]) - if e & 0x80: - e = e - 256 - - # mantissa - m = [ord(x) for x in s[:-1]] - M = 0. - for i in range(len(s)-1): - #M += m[i]<<(i*8) - M += float(m[i])/2**((i+1)*8) - # XXX how do we deal negative numbers? - #if m[0] & 0x80: - # M = M - 2^(len(s)) - return M * 2**(e+1) - -def decode_string(s): - nb = ord(s[0]) - s = s[1:nb+2] - r = "" - # XXX why do we need to do this? It's not described in the manual... - for c in s: - r += chr(ord(c) & 0x7F) - return r - -EDSP = {0: "No data", - 1: "Frequency response", - 2: "Power spectrum 1", - 3: "Power spectrum 2", - 4: "Coherence", - 5: "Cross spectrum", - 6: "Input time 1", - 7: "Input time 2", - 8: "Input linear spectrum 1", - 9: "Input linear spectrum 2", - 10: "Impulse response", - 11: "Cross correlation", - 12: "Auto correlation 1", - 13: "Auto correlation 2", - 14: "Histogram 1", - 15: "Histogram 2", - 16: "Cumulative density function 1", - 17: "Cumulative density function 2", - 18: "Probability density function 1", - 19: "Probability density function 2", - 20: "Average linear spectrum 1", - 21: "Average linear spectrum 2", - 22: "Average time record 1", - 23: "Average time record 2", - 24: "Synthesis pole-zeros", - 25: "Synthesis pole-residue", - 26: "Synthesis polynomial", - 27: "Synthesis constant", - 28: "Windowed time record 1", - 29: "Windowed time record 2", - 30: "Windowed linear spectrum 1", - 31: "Windowed linear spectrum 2", - 32: "Filtered time record 1", - 33: "Filtered time record 2", - 34: "Filtered linear spectrum 1", - 35: "Filtered linear spectrum 2", - 36: "Time capture buffer", - 37: "Captured linear spectrum", - 38: "Captured time record", - 39: "Throughput time record 1", - 40: "Throughput time record 2", - 41: "Curve fit", - 42: "Weighted function", - 43: "Not used", - 44: "Orbits", - 45: "Demodulation polar", - 46: "Preview demod record 1", - 47: "Preview demod record 2", - 48: "Preview demod linear spectrum 1", - 49: "Preview demod linear spectrum 2", - } - -ECH = {0: "Channel 1", - 1: "Channel 2", - 2: "Channel 1&2", - 3: "No channel", - } - -EOVR = ECH - -EDOM = {0: 'Time', - 1: 'Frequency', - 2: 'Voltage (amplitude)', - } - -EVLT = {0: "Peak", - 1: "RMS", - 2: "Volt (indicates peak only)", - } - -EAMP = {0: "Volts", - 1: "Volts squared", - 2: "PSD (V²/Hz)", - 3: "ESD (V²s/Hz)", - 4: "PSD¹² (V/Hz¹²)", - 5: "No unit", - 6: "Unit volts", - 7: "Unit volts²", - } - -EXAXIS= {0: "No units", - 1: "Hertz", - 2: "RPM", - 3: "Orders", - 4: "Seconds", - 5: "Revs", - 6: "Degrees", - 7: "dB", - 8: "dBV", - 9: "Volts", - 10: "V Hz¹²", - 11: "Hz/s", - 12: "V/EU", - 13: "Vrms", - 14: "V²/Hz", - 15: "%", - 16: "Points", - 17: "Records", - 18: "Ohms", - 19: "Hertz/octave", - 20: "Pulse/Rev", - 21: "Decades", - 22: "Minutes", - 23: "V²s/Hz", - 24: "Octave", - 25: "Seconds/Decade", - 26: "Seconds/Octave", - 27: "Hz/Point", - 28: "Points/Sweep", - 29: "Points/Decade", - 30: "Points/Octave", - 31: "V/Vrms", - 32: "V²", - 33: "EU referenced to chan 1", - 34: "EU referenced to chan 2", - 35: "EU value", - } - -EMEAS = {0: "Linear resolution", - 1: "Log resolution", - 2: "Swept sine", - 3: "Time capture", - 4: "Linear resolution throughput", - } - -EDEMOD1 = {45: "AM", - 46: "FM", - 47: "PM", - } - -EDEMOD2 = EDEMOD1 - -EAVG = {0: "No data", - 1: "Not averaged", - 2: "Averaged",} - - - -EWIN = {0: "N/A", - 1: "Hann", - 2: "Flat top", - 3: "Uniforme", - 4: "Exponential", - 5: "Force", - 6: "Force chan 1/expon chan 2", - 7: "Expon chan 1/force chan 2", - 8: "User", - } - -HEADER = [ ("Display function", EDSP, 'h', 2), - ('Number of elements', int, 'h', 2), - ('Displayed elements', int, 'h', 2), - ('Number of averages', int, 'h', 2), - ('Channel selection', ECH, 'h', 2), - ('Overflow status', EOVR, 'h', 2), - ('Overlap percentage', int, 'h', 2), - ('Domain', EDOM, 'h', 2), - ('Volts peak/rms', EVLT, 'h', 2), - ('Amplitude units', EAMP, 'h', 2), - ('X axis units', EXAXIS, 'h', 2), - ('Auto math label', str, 's', 14), - ('Trace label', str, 's', 22), - ('EU label 1', str, 's', 6), - ('EU label 2', str, 's', 6), - ('Float/Interger', bool, 'h', 2), - ('Complex/Real', bool, 'h', 2), - ('Live/Recalled', bool, 'h', 2), - ('Math result', bool, 'h', 2), - ('Real/Complex input', bool, 'h', 2), - ('Log/Linear data', bool, 'h', 2), - ('Auto math', bool, 'h', 2), - ('Real time status', bool, 'h', 2), - ('Measurement mode', EMEAS, 'h', 2), - ('Window', EWIN, 'h', 2), - ('Demod type channel 1', EDEMOD1, 'h', 2), - ('Demod type channel 2', EDEMOD2, 'h', 2), - ('Demod active channel 1', bool, 'h', 2), - ('Demod active channel 2', bool, 'h', 2), - ('Average status', EAVG, 'h', 2), - ('Not used', int, 'hh', 4), - ('Samp freq/2 (real)', decode_float, None, 4), - ('Samp freq/2 (imag)', decode_float, None, 4), - ('Not used', decode_float, None, 4), - ('Delta X-axis', decode_float, None, 4), - ('Max range', decode_float, None, 4), - ('Start time value', decode_float, None, 4), - ('Expon wind const 1', decode_float, None, 4), - ('Expon wind const 2', decode_float, None, 4), - ('EU value chan 1', decode_float, None, 4), - ('EU value chan 2', decode_float, None, 4), - ('Trig delay chan 1', decode_float, None, 4), - ('Trig delay chan 2', decode_float, None, 4), - ('Start freq value', decode_float, None, 8), - ('Start data value', decode_float, None, 8), - ] - -def decode_trace(data): - d = data - - typ = d[:2] - assert typ == "#A" - - totlen = struct.unpack('>h', d[2:4])[0] - idx = 4 - tt=0 - header = {} - for i, (nam, dtype, fmt, nbytes) in enumerate(HEADER): - if dtype == str: - val = decode_string(d[idx:]) - else: - if fmt: - v = struct.unpack('>'+fmt, d[idx: idx+nbytes])[0] - if isinstance(dtype, dict): - val = dtype.get(int(v), "N/A") - else: - val = dtype(v) - else: - val = dtype(d[idx: idx+nbytes]) - header[nam] = val - idx += nbytes - resu = [] - for i in range(header["Number of elements"]): - resu.append(decode_float(d[idx: idx+4])) - idx += 4 - return header, numpy.array(resu, dtype=float) - -def format_header(header, head_struct, columns=80): - todisp = [] - for row in head_struct: - key = row[0] - val = header.get(key, "N/A") - if isinstance(val, basestring): - val = repr(val) - else: - val = str(val) - todisp.append((key+":", val)) - maxk = max([len(k) for k, v in todisp]) - maxv = max([len(v) for k, v in todisp]) - fmt = "%%-%ds %%-%ds"%(maxk, maxv) - w = maxk+maxv+4 - ncols = columns/w - nrows = len(todisp)/ncols - print "w=", w - print "ncols=", ncols - print "nrows=", nrows - res = "" - for i in range(nrows): - res += "| ".join([fmt%todisp[j*nrows+i] for j in range(ncols)]) + "\n" - return res - - -if __name__ == "__main__": - import sys - import optparse - opt = optparse.OptionParser("A simple tool for tracing a dumped trace") - opt.add_option('-f', '--filename', default=None, - dest='filename', - help='Output filename. If not set, read from stdin') - opt.add_option('-m', '--mode', default='binary', - dest='mode', - help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', - ) - opt.add_option('-d', '--display-header', default=False, - action="store_true", - dest="displayheader", - help="Display the trace header") - opt.add_option('-P', '--noplot-trace', default=True, - action="store_false", - dest="plot", - help="Do not display the plot of the trace") - opt.add_option('-x', '--xmode', default='lin', - dest='xmode', - help='X coordinate mode (may be "lin" [default] or "log")') - opt.add_option('-y', '--ymode', default='lin', - dest='ymode', - help='Y coordinate mode (may be "lin" [default], "log" or "db")') - - options, argv = opt.parse_args(sys.argv) - - - if options.filename is None: - print "Can't deal stdin for now..." - sys.exit(1) - try: - header, data = decode_trace(open(options.filename, 'rb').read()) - except Exception, e: - print "ERROR: can't read %s an interpret it as a HP3562 trace"%options.filename - print e - sys.exit(1) - - if options.displayheader: - print format_header(header, HEADER, 100) - if options.plot: - f0 = header['Start freq value'] - dx = header['Delta X-axis'] - n = header['Number of elements'] - x = numpy.linspace(f0, f0+dx*n, len(data)) - y = data.copy() - - import pylab - if options.ymode != "lin": - minv = min(y[y>0]) - y[y==0] = minv - y = numpy.log10(y) - if options.ymode == "db": - y = y*10 - pylab.ylabel('db') - pylab.grid() - pylab.plot(x, y) - pylab.xlabel('frequency') - pylab.show() - -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HP3562A/state_decoder.py Tue Dec 18 00:23:10 2007 +0100 @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- +""" +state_decoder +============= + +Module for decoding the internal state of the HP3562A DSA, using the +GPIB command "DSBN" (Dump State BiNary). + +This file can be exectued as a python script. Use '-h' for more informations. +""" +from HP3562A import format_header, decode_float, decode_string, decode_header + +from HP3562A.enum_types import * + +HEADER = [("Measurement mode", EMEAS, 'h', 2), + ("Measurement 1", EMTYP, 'h', 2), + ("Measurement 2", EMTYP, 'h', 2), + ("Window type", EWINTYP, 'h', 2), + ("Force/Expon window 1", EFEXPW, 'h', 2), + ("Force/Expon window 2", EFEXPW, 'h', 2), + ("Average type", EAVGTYP, 'h', 2), + ("Overlap percentage", int, 'h', 2), + ("Number of averages", int, 'h', 2), + ("Sweep # of averages", int, 'h', 2), + ("Trigger type", ETRGTYP, 'h', 2), + ("Trigger slope", ETRGSLP, 'h', 2), + ("Preview type", EPRVTYP, 'h', 2), + ("Sample type", ESMPTYP, 'h', 2), + ("Range units chan 1", ERNGUNT, 'h', 2), + ("Range units chan 2", ERNGUNT, 'h', 2), + ("Range type 1", ERNGTYP, 'h', 2), + ("Range type 2", ERNGTYP, 'h', 2), + ("Input coupling 1", EINCPL, 'h', 2), + ("Input coupling 2", EINCPL, 'h', 2), + ("Source type", ESRCTYP, 'h', 2), + ("Chirp percent", int, 'h', 2), + ("Burst percent", int, 'h', 2), + ("Sweep direction", ESWPDIR, 'h', 2), + ("Sweep mode", ESWPMOD, 'h', 2), + ("Ext sample freq untis", EXAXIS, 'h', 2), + ("Bandwidth units", EXAXIS, 'h', 2), + ("Log span index", int, 'h', 2), + ("Log start index", int, 'h', 2), + ("Sweep rate units", EXAXIS, 'h', 2), + ("Auto gain ref chan", EDEMODCH, 'h', 2), + ("Demod channels", EDEMODCH, 'h', 2), + ("Demod type chan 1", EDEMOD, 'h', 2), + ("Demod type chan 2", EDEMOD, 'h', 2), + ("Source level units", EXAXIS, 'h', 2), + ("Source offset units", EXAXIS, 'h', 2), + ("Trigger level units", EXAXIS, 'h', 2), + ("Capt/thru length units", EXAXIS, 'h', 2), + ("EU label 1", str, 's', 6), + ("EU label 2", str, 's', 6), + ("Auto carrier on/off", bool, 'h', 2), + ("Time average on/off", bool, 'h', 2), + ("Auto/fixed resolution", bool, 'h', 2), + ("Auto gain on/off", bool, 'h', 2), + ("Auto/fixed integrate",bool, 'h', 2), + ("Fast average on/off", bool, 'h', 2), + ("Overload reject on/off", bool, 'h', 2), + ("Chan 1 float/ground", bool, 'h', 2), + ("Chan 2 float/ground", bool, 'h', 2), + ("Time throughput on/off", bool, 'h', 2), + ("Demodulation on/off", bool, 'h', 2), + ("EU/volts chan 1", bool, 'h', 2), + ("EU/volts chan 2", bool, 'h', 2), + ("Manual/auto arm", bool, 'h', 2), + ("Demod preview on/off", bool, 'h', 2), + ("Delete freq on/off", bool, 'h', 2), + ("Lin res Fstart pegged", bool, 'h', 2), + ("Swept Fstart pegged", bool, 'h', 2), + ("Force length chan 1", decode_float, None, 4), + ("Force length chan 2", decode_float, None, 4), + ("Expon time constant 1", decode_float, None, 4), + ("Expon time constant 2", decode_float, None, 4), + ("Sweep time", decode_float, None, 4), + ("Sweep rate", decode_float, None, 4), + ("Sweep resolution", decode_float, None, 4), + ("Sweep integrate time", decode_float, None, 4), + ("Auto gain level", decode_float, None, 4), + ("Auto gain limit", decode_float, None, 4), + ("Source level", decode_float, None, 4), + ("EU value chan 1", decode_float, None, 4), + ("EU value chan 2", decode_float, None, 4), + ("Trigger delay chan 1", decode_float, None, 4), + ("Trigger delay chan 2", decode_float, None, 4), + ("Integrate var thresh", decode_float, None, 4), + ("Capt/thru length", decode_float, None, 4), + ("Frequency span", decode_float, None, 4), + ("Time record length", decode_float, None, 4), + ("Frequency resolution", decode_float, None, 4), + ("Time resolution", decode_float, None, 4), + ("External sample rate", decode_float, None, 4), + ("Sample rate (actual)", decode_float, None, 4), + ("Range channel 1", decode_float, None, 4), + ("Range channel 2", decode_float, None, 4), + ("Preview time", decode_float, None, 4), + ("Trigger level", decode_float, None, 4), + ("Source dc offset", decode_float, None, 4), + ("Fixed sine frequency", decode_float, None, 8), + ("Start frequency", decode_float, None, 8), + ("Center frequency", decode_float, None, 8), + ("Sweep start", decode_float, None, 8), + ("Sweep end", decode_float, None, 8), + ("Carrier frequency", decode_float, None, 8), + ] + +def decode_state(data): + """ + Decode the data (as generated by the HP3562A DSA in response to a + "DSBN" command), and returns a dict (header) + + header is the dictionnary of the header of the dumped data block, + """ + header, idx = decode_header(data, HEADER) + return header + + +def main(): + import sys + import optparse + opt = optparse.OptionParser("A simple tool for tracing a dumped trace") + opt.add_option('-f', '--filename', default=None, + dest='filename', + help='Output filename. If not set, read from stdin') + opt.add_option('-m', '--mode', default='binary', + dest='mode', + help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', + ) + + options, argv = opt.parse_args(sys.argv) + + + if options.filename is None: + print "Can't deal stdin for now..." + sys.exit(1) + #try: + if 1: + header = decode_state(open(options.filename, 'rb').read()) + else: + #except Exception, e: + print "ERROR: can't read %s an interpret it as a HP3562 trace"%options.filename + print e + sys.exit(1) + + print format_header(header, HEADER, 100) + +if __name__ == "__main__": + main() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HP3562A/trace_decoder.py Tue Dec 18 00:23:10 2007 +0100 @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- +""" +trace_decoder +============= + +Module for decoding a trace generated by the HP3562A DSA, using the +GPIB command "DDBN" (Dump Data BiNary). + +This file can be exectued as a python script. Use '-h' for more informations. +""" +import struct +import numpy +from HP3562A import format_header, decode_float, decode_string, decode_header +from HP3562A.enum_types import * + + +HEADER = [ ("Display function", EDSP, 'h', 2), + ('Number of elements', int, 'h', 2), + ('Displayed elements', int, 'h', 2), + ('Number of averages', int, 'h', 2), + ('Channel selection', ECH, 'h', 2), + ('Overflow status', EOVR, 'h', 2), + ('Overlap percentage', int, 'h', 2), + ('Domain', EDOM, 'h', 2), + ('Volts peak/rms', EVLT, 'h', 2), + ('Amplitude units', EAMP, 'h', 2), + ('X axis units', EXAXIS, 'h', 2), + ('Auto math label', str, 's', 14), + ('Trace label', str, 's', 22), + ('EU label 1', str, 's', 6), + ('EU label 2', str, 's', 6), + ('Float/Interger', bool, 'h', 2), + ('Complex/Real', bool, 'h', 2), + ('Live/Recalled', bool, 'h', 2), + ('Math result', bool, 'h', 2), + ('Real/Complex input', bool, 'h', 2), + ('Log/Linear data', bool, 'h', 2), + ('Auto math', bool, 'h', 2), + ('Real time status', bool, 'h', 2), + ('Measurement mode', EMEAS, 'h', 2), + ('Window', EWIN, 'h', 2), + ('Demod type channel 1', EDEMOD, 'h', 2), + ('Demod type channel 2', EDEMOD, 'h', 2), + ('Demod active channel 1', bool, 'h', 2), + ('Demod active channel 2', bool, 'h', 2), + ('Average status', EAVG, 'h', 2), + ('Not used', int, 'hh', 4), + ('Samp freq/2 (real)', decode_float, None, 4), + ('Samp freq/2 (imag)', decode_float, None, 4), + ('Not used', decode_float, None, 4), + ('Delta X-axis', decode_float, None, 4), + ('Max range', decode_float, None, 4), + ('Start time value', decode_float, None, 4), + ('Expon wind const 1', decode_float, None, 4), + ('Expon wind const 2', decode_float, None, 4), + ('EU value chan 1', decode_float, None, 4), + ('EU value chan 2', decode_float, None, 4), + ('Trig delay chan 1', decode_float, None, 4), + ('Trig delay chan 2', decode_float, None, 4), + ('Start freq value', decode_float, None, 8), + ('Start data value', decode_float, None, 8), + ] + +def decode_trace(data): + """ + Decode the data (as generated by the HP3562A DSA in response to a + "DDBN" command), and returns a couple (header, values). + + header is the dictionnary of the header of the dumped data block, + value is a numpy array holding the trace (vector of float or + complex values). + """ + header, idx = decode_header(data, HEADER) + resu = [] + for i in range(header["Number of elements"]): + resu.append(decode_float(data[idx: idx+4])) + idx += 4 + return header, numpy.array(resu, dtype=float) + + + +def main(): + import sys + import optparse + opt = optparse.OptionParser("A simple tool for tracing a dumped trace") + opt.add_option('-f', '--filename', default=None, + dest='filename', + help='Output filename. If not set, read from stdin') + opt.add_option('-m', '--mode', default='binary', + dest='mode', + help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', + ) + opt.add_option('-d', '--display-header', default=False, + action="store_true", + dest="displayheader", + help="Display the trace header") + opt.add_option('-P', '--noplot-trace', default=True, + action="store_false", + dest="plot", + help="Do not display the plot of the trace") + opt.add_option('-x', '--xmode', default='lin', + dest='xmode', + help='X coordinate mode (may be "lin" [default] or "log")') + opt.add_option('-y', '--ymode', default='lin', + dest='ymode', + help='Y coordinate mode (may be "lin" [default], "log" or "db")') + + options, argv = opt.parse_args(sys.argv) + + + if options.filename is None: + print "Can't deal stdin for now..." + sys.exit(1) + try: + header, data = decode_trace(open(options.filename, 'rb').read()) + except Exception, e: + print "ERROR: can't read %s an interpret it as a HP3562 trace"%options.filename + print e + sys.exit(1) + + if options.displayheader: + print format_header(header, HEADER, 100) + if options.plot: + f0 = header['Start freq value'] + dx = header['Delta X-axis'] + n = header['Number of elements'] + x = numpy.linspace(f0, f0+dx*n, len(data)) + y = data.copy() + + import pylab + if options.ymode != "lin": + minv = min(y[y>0]) + y[y==0] = minv + y = numpy.log10(y) + if options.ymode == "db": + y = y*10 + pylab.ylabel('db') + pylab.grid() + pylab.plot(x, y) + pylab.xlabel('frequency') + pylab.show() + + +if __name__ == "__main__": + main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/dump_datablock Tue Dec 18 00:23:10 2007 +0100 @@ -0,0 +1,4 @@ +#!/usr/bin/python + +from HP3562A.dump_datablock import main +main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/read_state Tue Dec 18 00:23:10 2007 +0100 @@ -0,0 +1,4 @@ +#!/usr/bin/python + +from HP3562A.state_decoder import main +main()