# -*- 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, read_trace
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, idx=0):
    """
    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, idx)
    return header, read_trace(data, idx, header["Number of elements"])

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()
