[py3k] beginning to port to py3k

Tue, 01 May 2018 00:10:23 +0200

author
David Douard <david.douard@logilab.fr>
date
Tue, 01 May 2018 00:10:23 +0200
changeset 86
96e30b092f70
parent 85
b55977dcc311
child 87
59a0946aa3d1

[py3k] beginning to port to py3k

also write a proper setuptools based setup.py and convert some bin/* scripts
as entry_points.

README file | annotate | diff | comparison | revisions
bin/dump_datablock file | annotate | diff | comparison | revisions
bin/gpib_detect file | annotate | diff | comparison | revisions
bin/read_coord file | annotate | diff | comparison | revisions
bin/read_state file | annotate | diff | comparison | revisions
bin/read_trace file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3456.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3562A/HP356X.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3562A/__init__.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3562A/coord_decoder.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3562A/dump_datablock.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3562A/dump_datablock_mockup.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3562A/enum_types.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3562A/mathtools.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3562A/state_decoder.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3562A/test/test_HP3562A.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP3562A/trace_decoder.py file | annotate | diff | comparison | revisions
pygpibtoolkit/HP8904.py file | annotate | diff | comparison | revisions
pygpibtoolkit/__init__.py file | annotate | diff | comparison | revisions
pygpibtoolkit/detect.py file | annotate | diff | comparison | revisions
pygpibtoolkit/gpib_utils.py file | annotate | diff | comparison | revisions
pygpibtoolkit/gpibcontroller.py file | annotate | diff | comparison | revisions
pygpibtoolkit/prologix.py file | annotate | diff | comparison | revisions
pygpibtoolkit/pygpib.py file | annotate | diff | comparison | revisions
pygpibtoolkit/tools.py file | annotate | diff | comparison | revisions
setup.py file | annotate | diff | comparison | revisions
--- a/README	Thu Apr 02 16:58:24 2009 +0200
+++ b/README	Tue May 01 00:10:23 2018 +0200
@@ -1,4 +1,4 @@
-pygpibtoolkit is (c) 2007-2009 David Douard 
+pygpibtoolkit is (c) 2007-2018 David Douard
 and is available under the GNU General Public Licence v2.
 
 You can get information on pygpibtoolkit on
@@ -6,8 +6,4 @@
 
 See `doc/introduction.rst` for a quick overview.
 
-If you have any questions, please mail david.douard@logilab.fr
-for support.
-
-David Douard
-
+If you have any questions, please mail david.douard@sdfa3.org for support.
--- a/bin/dump_datablock	Thu Apr 02 16:58:24 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-#!/usr/bin/python
-
-import sys, os
-try:
-    from pygpibtoolkit.HP3562A.dump_datablock import main
-except ImportError:
-    sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
-    from pygpibtoolkit.HP3562A.dump_datablock import main
-main()
--- a/bin/gpib_detect	Thu Apr 02 16:58:24 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-#!/usr/bin/python
-import sys
-import os
-import signal
-import time
-
-try:
-    from pygpibtoolkit.gpibcontroller import GPIBController
-except ImportError:
-    sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
-    from pygpibtoolkit.gpibcontroller import GPIBController
-import pygpibtoolkit.HP3562A
-from pygpibtoolkit.prologix import GPIB
-def main():
-    import optparse
-    opt = optparse.OptionParser("A simple tool for detecting connected GPIB devices")
-    opt.add_option('-d', '--device', default="/dev/ttyUSB0",
-                   dest="device",
-                   help="Device of connected Prologix GPIB bundle [/dev/ttyUSB0]",)
-    options, argv = opt.parse_args(sys.argv)
-
-    print "Detecting GPIB devices on the bus. Please wait until completion."
-    cnx = GPIB(device=options.device)
-    c = GPIBController(cnx)
-    
-    signal.signal(signal.SIGINT, c.stop)
-    signal.signal(signal.SIGQUIT, c.stop)
-    
-    time.sleep(1)
-    devices = c.detect_devices()
-    c.stop()
-    
-    print "GPIB devices:"
-    for k in sorted(devices.keys()):
-        print "%-3d: %s"%(k, devices[k])
-    return c, devices
-if __name__ == "__main__":
-    c, dev = main()
-    
--- a/bin/read_coord	Thu Apr 02 16:58:24 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-#!/usr/bin/python
-
-import sys, os
-try:
-    from pygpibtoolkit.HP3562A.coord_decoder import main
-except ImportError:
-    sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
-    from pygpibtoolkit.HP3562A.coord_decoder import main
-main()
--- a/bin/read_state	Thu Apr 02 16:58:24 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-#!/usr/bin/python
-
-import sys, os
-try:
-    from pygpibtoolkit.HP3562A.state_decoder import main
-except ImportError:
-    sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
-    from pygpibtoolkit.HP3562A.state_decoder import main
-main()
--- a/bin/read_trace	Thu Apr 02 16:58:24 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-#!/usr/bin/python
-import sys, os
-try:
-    from pygpibtoolkit.HP3562A.trace_decoder import main
-except ImportError:
-    sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
-    from pygpibtoolkit.HP3562A.trace_decoder import main
-main()
--- a/pygpibtoolkit/HP3456.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/HP3456.py	Tue May 01 00:10:23 2018 +0200
@@ -11,14 +11,14 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2009 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+""" Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
 
 """
 import mx.DateTime as dt
 
-from pygpibtoolkit.pygpib import Constants, Command
-from pygpibtoolkit.pygpib import Mode, ModeCommand
+from pygpibtoolkit.pygpib import Command
+from pygpibtoolkit.pygpib import ModeCommand
 from pygpibtoolkit.pygpib import WriteOnlyMode
 from pygpibtoolkit.pygpib import BoolValue, FloatValue
 from pygpibtoolkit.gpibcontroller import AbstractGPIBDevice, deviceRegister
@@ -27,10 +27,10 @@
 class Function(WriteOnlyMode):
     "Function"
     _init_value = "S0F1"
-    
-    #S0 = ModeCommand("Unshift", "S0")
-    #S1 = ModeCommand("Shift", "S1")
-    
+
+    # S0 = ModeCommand("Unshift", "S0")
+    # S1 = ModeCommand("Shift", "S1")
+
     S0F1 = ModeCommand("DC Voltage", "DCV")
     S0F2 = ModeCommand("AC Voltage", "ACV")
     S0F3 = ModeCommand("AC + DC Voltage", "ACV+DCV")
@@ -42,12 +42,12 @@
     S1F3 = ModeCommand("(AC + DC)/DC Voltage Ratio", "ACV+DCV/DCV")
     S1F4 = ModeCommand("O.C. 2 wire Ohm", "OCO2")
     S1F5 = ModeCommand("O.C. 4 wire Ohm", "OCO4")
-    
+
 
 class Range(WriteOnlyMode):
     "Range"
     _init_value = "R1"
-    
+
     R1 = ModeCommand("Auto", "AUTO")
     R2 = ModeCommand("0.1", "0.1")
     R3 = ModeCommand("1", "1")
@@ -58,15 +58,17 @@
     R8 = ModeCommand("100E3", "100E3")
     R9 = ModeCommand("1E6", "1E6")
 
+
 class Trigger(WriteOnlyMode):
     "Trigger"
     _init_value = "T1"
-    
+
     T1 = ModeCommand("Internat", "INT")
     T2 = ModeCommand("External", "EXT")
     T3 = ModeCommand("Single", "SGL")
     T4 = ModeCommand("Hold", "HLD")
 
+
 class Math(WriteOnlyMode):
     "Math"
     _init_value = "M0"
@@ -81,7 +83,8 @@
     M7 = ModeCommand("Scale", "")
     M8 = ModeCommand("% Error", "")
     M9 = ModeCommand("dB", "")
-    
+
+
 class AutoZero(WriteOnlyMode):
     "Auto Zero"
     _init_value = "Z1"
@@ -89,6 +92,7 @@
     Z0 = ModeCommand("Off", "OFF")
     Z1 = ModeCommand("On", "ON")
 
+
 class Filter(WriteOnlyMode):
     "Filter"
     _init_value = "FL0"
@@ -96,6 +100,7 @@
     FL0 = ModeCommand("Off", "OFF")
     FL1 = ModeCommand("On", "ON")
 
+
 class Test(WriteOnlyMode):
     "Test"
     _init_value = "TE0"
@@ -103,13 +108,15 @@
     TE0 = ModeCommand("Off", "OFF")
     TE1 = ModeCommand("On", "ON")
 
+
 class ReadingStorage(WriteOnlyMode):
     "Reading Storage"
     _init_value = "RS0"
 
     RS0 = ModeCommand("Off", "OFF")
     RS1 = ModeCommand("On", "ON")
-    
+
+
 class SystemOutputMode(WriteOnlyMode):
     "System Output Mode"
     _init_value = "SO0"
@@ -117,6 +124,7 @@
     SO0 = ModeCommand("Off", "OFF")
     SO1 = ModeCommand("On", "ON")
 
+
 class Display(WriteOnlyMode):
     "Display"
     _init_value = "D1"
@@ -124,6 +132,7 @@
     D0 = ModeCommand("Off", "OFF")
     D1 = ModeCommand("On", "ON")
 
+
 class OutputFormat(WriteOnlyMode):
     "Output Format"
     _init_value = "P0"
@@ -131,81 +140,99 @@
     P0 = ModeCommand("ASCII", "ASC")
     P1 = ModeCommand("Packed Format", "PFOR")
 
+
 class EOI(WriteOnlyMode):
     "EOI"
     _init_value = "O1"
-    
+
     O0 = ModeCommand("Disable", "OFF")
     O1 = ModeCommand("Enable", "ON")
 
+
 class SW1(BoolValue):
     _readonly = True
+
     def convert_from(self, *value):
         if value:
-            return  int(value[0]) and "Front" or "Rear"
+            return int(value[0]) and "Front" or "Rear"
         return "?"
 
+
 class H(Command):
     "HOME - Software Reset"
 
+
 class CL1(Command):
     "CLEAR CONTINUE - Active"
 
+
 class _Register(FloatValue):
     _readonly = False
     _cached = False
+
     def build_get_cmd(self):
-        return "RE"+self.__class__.__name__
+        return "RE" + self.__class__.__name__
 
     def build_set_cmd(self, *value):
         value = self.convert_to(*value)
-        cmd = "%sST%s"%(value, self.__class__.__name__)
+        cmd = "%sST%s" % (value, self.__class__.__name__)
         return cmd, value
 
+
 class N(_Register):
     "Number of readings"
 
+
 class G(_Register):
     "Number of displayed digits"
 
+
 class I(_Register):
     "Number of power line cycles int."
 
+
 class D(_Register):
     "Delay"
 
+
 class M(_Register):
     "Mean"
     _readonly = True
-    
+
+
 class V(_Register):
     "Variance"
     _readonly = True
 
+
 class C(_Register):
     "Count"
     _readonly = True
-    
+
+
 class L(_Register):
     "Lower"
-    
+
+
 class R(_Register):
     "R"
 
+
 class U(_Register):
     "Upper"
-    
+
+
 class Y(_Register):
     "Y"
-    
+
+
 class Z(_Register):
     "Z"
 
-    
-    
+
 class HP3456Device(AbstractGPIBDevice):
-    _accepts = [r"^(?P<model>HP3456A)$",]
-    _idn = None # cannot ask device its model number or version
+    _accepts = [r"^(?P<model>HP3456A)$", ]
+    _idn = None  # cannot ask device its model number or version
 
     def __init__(self, idn, address, controller):
         super(HP3456Device, self).__init__(idn, address, controller)
@@ -213,7 +240,7 @@
         self._data_cb = None
 
     def manage_srq(self, statusbyte):
-        #print "Managing SRQ", statusbyte
+        # print "Managing SRQ", statusbyte
         if statusbyte & 0x04:
             # data ready, read the value on the GPIB bus
             self._controller.send_command(self._address, '', cb=self.data_cb)
@@ -234,5 +261,5 @@
     def register_data_cb(self, cb):
         self._data_cb = cb
 
-        
+
 deviceRegister.register_manager(HP3456Device)
--- a/pygpibtoolkit/HP3562A/HP356X.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/HP3562A/HP356X.py	Tue May 01 00:10:23 2018 +0200
@@ -10,14 +10,14 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2009 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+""" Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
 
 """
 
 from pygpibtoolkit.pygpib import Constants, Command
 from pygpibtoolkit.pygpib import Condition
-from pygpibtoolkit.pygpib import BoolValue, IntValue, FloatValue
+from pygpibtoolkit.pygpib import BoolValue, IntValue
 from pygpibtoolkit.pygpib import PercentageValue, FrequencyValue, DurationValue
 from pygpibtoolkit.pygpib import VoltageValue
 from pygpibtoolkit.pygpib import EnumValue, StringValue
@@ -25,6 +25,7 @@
 
 from pygpibtoolkit.gpibcontroller import AbstractGPIBDevice, deviceRegister
 
+
 #####################
 # HP3562A constants and command set
 # **VERY INCOMPLETE**
@@ -32,20 +33,22 @@
 class LinRes(Condition):
     def __call__(self, device):
         return device.MeasMode == "LINEAR RES"
-class LogRes(Condition):
+class LogRes(Condition):  # noqa
     def __call__(self, device):
         return device.MeasMode == "LOG RES"
-class SweptSine(Condition):
+class SweptSine(Condition):  # noqa
     def __call__(self, device):
         return device.MeasMode == "SWEPT SINE"
-class TimeCaptur(Condition):
+class TimeCaptur(Condition):  # noqa
     def __call__(self, device):
         return device.MeasMode == "TIME CAPTUR"
 
+
 # GPIB buffer size is 3x80 characters lines
 class STATUS_BYTE(Constants):
     # HP3562A Status Byte, as returned by a serial poll
-    _constants = [(0x40, "RQS", "Request Service"), # when sent in response to a serial poll
+    _constants = [(0x40, "RQS", "Request Service"),
+                  # when sent in response to a serial poll
                   (0x20, "ERR", "GPIB error"),
                   (0x10, "RDY", "ready to accept GPIB commands"),
                   ]
@@ -61,23 +64,25 @@
                   (8, "USRQ1", "User SRQ #8"),
                   (9, "EOD", "End of disk action"),
                   (10, "EOP", "End of plot action"),
-                  (11, "STCH", "Instrument status changed"), # any change in 
-                  # the status register sets this condition
+                  (11, "STCH", "Instrument status changed"),
+                  # STCH: any change in the status register sets this condition
                   (12, "PWR", "Power on"),
                   (13, "KEY", "Key pressed"),
                   (14, "DCP", "Device clear plotter (listen)"),
                   # ...
                   ]
+
     def __init__(self):
         super(STATUS_BYTE, self).__init__()
         self._conditions = dict([(x[0], x[1]) for x in self.conditions])
         self._rev_conditions = dict([(x[1], x[0]) for x in self.conditions])
         self._long_conditions = dict([(x[0], x[2]) for x in self.conditions])
-               
+
     def byte_condition(self, byte):
         byte = byte & 0x8F
         return self._conditions.get(byte, "N/A")
 
+
 class IS(Flag):
     "INTERNAL STATUS - Internal Status Register"
     _constants = [(0x01, "MEASP", "measeurement pause"),
@@ -96,7 +101,8 @@
                   (0x2000, "ASRC", "Asctive Status Register changed"),
                   (0x4000, "PWRF", "Power-on test failed"),
                   ]
-    
+
+
 class STA(Flag):
     "STATUS QUERY - Status/event query"
     _constants = [(0x01, "N/A", "Not used"),
@@ -116,6 +122,8 @@
                   (0x4000, "CH2OV", "Channel 2 overrange"),
                   (0x8000, "MAOV", "Math overflow"),
                   ]
+
+
 class AS(Flag):
     "ACTIVITY STATUS - Activity Status Register"
     _constants = [(0x01, "CKFL", "Check fault log"),
@@ -125,7 +133,7 @@
                   (0x10, "MSSM", "Missed sample"),
                   (0x20, "TMPR", "Timed preview"),
                   (0x40, "ACDA", "Accept data"),
-                  #...
+                  # ...
                   ]
 
 
@@ -137,40 +145,49 @@
 
     def get_mode(self, device):
         # XXX Can I get this from the device?
-        #mode = device.DSBN. # HERE
+        # mode = device.DSBN. # HERE
         return None
-    
+
+
 class ARM(Command):
     "ARM - Arm"
+
+
 class NAVG(IntValue):
     "Number of averages"
 
+
 class AverageMode(Mode):
     "Average mode"
     _condition = LinRes() | LogRes() | TimeCaptur()
     _key = "EAVGTYP"
     _DATA_BLOCK = "DSBN"
-    
+
     STBL = ModeCommand(6, "STABLE (MEAN)")
-    EXP  = ModeCommand(7, "EXPON")
+    EXP = ModeCommand(7, "EXPON")
     PHLD = ModeCommand(8, "PEAK HOLD")
     CNPK = ModeCommand(9, "CONT PEAK")
     AVOF = ModeCommand(10, "AVG OFF")
 
+
 class TIAV(BoolValue):
     "TIM AV ON OFF - Time average"
     _condition = LinRes() | TimeCaptur() | SweptSine()
 
+
 class OVLP(PercentageValue):
     "OVRLP% - Overlap (%)"
     _condition = LinRes() | TimeCaptur() | SweptSine()
-    
+
+
 class AUTO(BoolValue):
     "AUTO ON OFF - Auto calibration"
 
+
 class SNGC(Command):
     "SINGLE CAL - Single calibration"
 
+
 class CoordMode(Mode):
     "Coord mode"
     _key = "EYCOORD"
@@ -179,60 +196,73 @@
     MGDB = ModeCommand(5, "MAG (dB)")
     MDBM = ModeCommand(10, "MAG (dBm)")
     MGLG = ModeCommand(4, "MAG (LOG)")
-    MAG  = ModeCommand(3, "MAG (LIN)")
+    MAG = ModeCommand(3, "MAG (LIN)")
     PHSE = ModeCommand(8, "PHASE")
     REAL = ModeCommand(1, "REAL")
     IMAG = ModeCommand(2, "IMAG")
     NYQT = ModeCommand(6, "NYQUIST")
     NICL = ModeCommand(9, "NICHOL")
 
+
 # FREQ menu
 class FRS(FrequencyValue):
     "FREQ SPAN - Freq. span"
 
+
 class SF(FrequencyValue):
     "START FREQ - Start freq."
 
+
 class CF(FrequencyValue):
     "CENTER FREQ - Center freq."
-    _units = "hz","khz","mhz","ord","rmp"
+    _units = "hz", "khz", "mhz", "ord", "rmp"
     _condition = LinRes() | TimeCaptur() | SweptSine()
-    
+
+
 class ZST(Command):
     "ZERO START - Zero start"
     _condition = LinRes() | TimeCaptur() | SweptSine()
-    
+
+
 class MAXS(FrequencyValue):
     "MAX SPAN - Max span"
     _condition = LinRes() | TimeCaptur() | SweptSine()
-    
+
+
 class TLN(DurationValue):
     "TIME LENGTH - Time len."
     _condition = LinRes() | TimeCaptur() | SweptSine()
-    
+
+
 class ESMP(BoolValue):
     "E SMPL ON OFF - E sample"
     _condition = LinRes() | TimeCaptur() | SweptSine()
-    
+
+
 class SMPF(FrequencyValue):
     "SAMPLE FREQ - Sample freq."
     _condition = LinRes() | TimeCaptur() | SweptSine()
-    
+
+
 class SPF(FrequencyValue):
     "STOP FREQ - Stop freq."
     _condition = SweptSine
-    
+
+
 class SWRT(FrequencyValue):
     "SWEEP RATE - Sweep rate"
 
+
 class ABIB(Command):
     "ABORT HP-IB - Abort HPIB"
 
+
 # INPUT COUPLING
 class C1AC(EnumValue):
     "CHAN1 AC DC - Channel 1 AC/DC"
     _values = ["DC", "AC"]
-    
+
+
 class Chan1Coupling(Mode):
     "Channel 1 coupling"
     _key = "FLT1"
@@ -240,10 +270,12 @@
     FLT1 = ModeCommand(1, "FLOAT CHAN1")
     GND1 = ModeCommand(0, "GROUND CHAN1")
 
+
 class C2AC(EnumValue):
     "CHAN2 AC DC - Channel 2 AC/DC"
     _values = ["AC", "DC"]
-    
+
+
 class Chan2Coupling(Mode):
     "Channel 2 coupling"
     _key = "FLT1"
@@ -251,16 +283,20 @@
     FLT2 = ModeCommand("Float", "FLOAT CHAN2")
     GND2 = ModeCommand("Ground", "GROUND CHAN2")
 
+
 class LCL(Command):
     "LOCAL - Local"
 
+
 class C1RG(VoltageValue):
     "CHAN 1 RANGE - Channel 1 range"
-    _units = "V","mV","Vrms","mVrms","dBV", "EU" 
+    _units = "V", "mV", "Vrms", "mVrms", "dBV", "EU"
+
 
 class C2RG(VoltageValue):
     "CHAN 2 RANGE - Channel 2 range"
-    _units = "V","mV","Vrms","mVrms","dBV", "EU" 
+    _units = "V", "mV", "Vrms", "mVrms", "dBV", "EU"
+
 
 # MEAS MODE
 class MeasMode(Mode):
@@ -271,20 +307,38 @@
     LGRS = ModeCommand(1, "LOG RES")
     SSIN = ModeCommand(2, "SWEPT SINE")
     CPTR = ModeCommand(3, "TIME CAPTUR")
-    
+
+
 # MEAS DISP
 class FreqResp(Condition):
     def __call__(self, device):
         return device.SelectMeas == "FREQ RESP"
 
+
 class MeasDisp(Mode):
     # TODO
-    FRQR = ModeCommand(0, "FREQ RESP", condition=(LinRes() | LogRes()) & FreqResp() | SweptSine())
-    COHR = ModeCommand(0, "COHER", condition=(LinRes() | LogRes()) & FreqResp() | SweptSine())
-    PSP1 = ModeCommand(0, "POWER SPEC1", condition=(LinRes() | LogRes()) & FreqResp() | SweptSine())
-    PSP2 = ModeCommand(0, "POWER SPEC2", condition=(LinRes() | LogRes()) & FreqResp() | SweptSine())
-    CSPS = ModeCommand(0, "CROSS SPEC", condition=(LinRes() | LogRes()) & FreqResp() | SweptSine())
-    IRSP = ModeCommand(0, "IMPLS RESP", condition=LinRes() & FreqResp())
+    FRQR = ModeCommand(0, "FREQ RESP",
+                       condition=((LinRes() | LogRes())
+                                  & FreqResp()
+                                  | SweptSine()))
+    COHR = ModeCommand(0, "COHER",
+                       condition=((LinRes() | LogRes())
+                                  & FreqResp()
+                                  | SweptSine()))
+    PSP1 = ModeCommand(0, "POWER SPEC1",
+                       condition=((LinRes() | LogRes())
+                                  & FreqResp()
+                                  | SweptSine()))
+    PSP2 = ModeCommand(0, "POWER SPEC2",
+                       condition=((LinRes() | LogRes())
+                                  & FreqResp()
+                                  | SweptSine()))
+    CSPS = ModeCommand(0, "CROSS SPEC",
+                       condition=((LinRes() | LogRes())
+                                  & FreqResp()
+                                  | SweptSine()))
+    IRSP = ModeCommand(0, "IMPLS RESP",
+                       condition=LinRes() & FreqResp())
     AUMT = ModeCommand(0, "AUTO MATH", )
     FILT = ModeCommand(0, "FILTRD INPUT")
 
@@ -292,26 +346,28 @@
     TMR2 = ModeCommand(0, "TIME REC2", condition=TimeCaptur)
     LSP1 = ModeCommand(0, "LINEAR SPEC1", condition=TimeCaptur)
     LSP2 = ModeCommand(0, "LINEAR SPEC2", condition=TimeCaptur)
-    
+
 
 class PowerSpec(Condition):
     def __call__(self, device):
         return device.SelectMeas == "POWER SPEC"
 
+
 class AutoCorr(Condition):
     def __call__(self, device):
         return device.SelectMeas == "AUTO CORR"
 
+
 class CrossCorr(Condition):
     def __call__(self, device):
         return device.SelectMeas == "CROSS CORR"
 
+
 class Hist(Condition):
     def __call__(self, device):
         return device.SelectMeas == "HIST"
 
 
-
 # SELECT MEAS
 class SelectMeas(Mode):
     "Select measurement"
@@ -319,16 +375,22 @@
     _DATA_BLOCK = "DSBN"
     _condition = LinRes() | TimeCaptur() | SweptSine()
 
-    FRSP = ModeCommand(0, "FREQ RESP", condition=LinRes() | LogRes() | SweptSine())
-    PSPC = ModeCommand(2, "POWER SPEC", condition=LinRes() | LogRes() | TimeCaptur())
-    AUCR = ModeCommand(3, "AUTO CORR", condition=LinRes() | TimeCaptur())
-    CCOR = ModeCommand(1, "CROSS CORR", condition=LinRes())
-    HIST = ModeCommand(4, "HIST", condition=LinRes() | TimeCaptur())
+    FRSP = ModeCommand(
+        0, "FREQ RESP", condition=LinRes() | LogRes() | SweptSine())
+    PSPC = ModeCommand(
+        2, "POWER SPEC", condition=LinRes() | LogRes() | TimeCaptur())
+    AUCR = ModeCommand(
+        3, "AUTO CORR", condition=LinRes() | TimeCaptur())
+    CCOR = ModeCommand(
+        1, "CROSS CORR", condition=LinRes())
+    HIST = ModeCommand(
+        4, "HIST", condition=LinRes() | TimeCaptur())
+
 
 class Channel(Mode):
     ""
     _key = "ECH"
-    _DATA_BLOCK = "DDBN" # XXX sure?
+    _DATA_BLOCK = "DDBN"  # XXX sure?
     CH12 = ModeCommand("Channel 1&2 active", "CH 1&2 ACTIVE",
                        condition=LinRes() | LogRes())
     CH1 = ModeCommand("Channel 1 active", "CH 1 ACTIVE",
@@ -336,17 +398,22 @@
     CH2 = ModeCommand("Channel 2 active", "CH 2 ACTIVE",
                       condition=LinRes() | LogRes() | TimeCaptur())
 
+
 class REV(StringValue):
     _readonly = True
     "REVISION - Revision"
 
+
 class SACR(Command):
     "SACR - Send Auto Carrier"
-    
+
+
 class HP356XDevice(AbstractGPIBDevice):
-    _accepts = [ r"^(?P<model>HP *356[23][ABCD]).*$",]
+    _accepts = [r"^(?P<model>HP *356[23][ABCD]).*$", ]
     _idn = "ID?"
+
     def manage_srq(self, statusbyte):
-        print "Managing SRQ", statusbyte
-        
+        print("Managing SRQ: %s" % statusbyte)
+
+
 deviceRegister.register_manager(HP356XDevice)
--- a/pygpibtoolkit/HP3562A/__init__.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/HP3562A/__init__.py	Tue May 01 00:10:23 2018 +0200
@@ -10,15 +10,15 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+"""Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
 
 
 HP356X
 ======
 
-Module for communicating with the HP 3562A and derivated Digital
-Signal Analyzers.
+Module for communicating with the HP 3562A and derivated Digital Signal
+Analyzers.
 
 Subpackages
 -----------
@@ -28,7 +28,4 @@
 ---------
 
 """
-import HP356X
-
-
-    
+from . import HP356X  # noqa
--- a/pygpibtoolkit/HP3562A/coord_decoder.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/HP3562A/coord_decoder.py	Tue May 01 00:10:23 2018 +0200
@@ -11,8 +11,8 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+"""Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
 
 state_decoder
 =============
@@ -20,22 +20,25 @@
 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. 
+This file can be exectued as a python script. Use '-h' for more informations.
+
 """
 
-from pygpibtoolkit.gpib_utils import format_datablock_header, decode_datablock_header, read_datablock_trace
-from pygpibtoolkit.gpib_utils import decode_float, decode_string
-from pygpibtoolkit.HP3562A.trace_decoder import decode_trace, HEADER as TRACE_HEADER
-from pygpibtoolkit.HP3562A.enum_types import *
+from pygpibtoolkit.gpib_utils import (
+    decode_datablock_header, read_datablock_trace,
+    decode_float, )
+from pygpibtoolkit.HP3562A.trace_decoder import HEADER as TRACE_HEADER
+from pygpibtoolkit.HP3562A.enum_types import *  # noqa
+
 
 HEADER = [("EYCOORD", "Y coordinates", EYCOORD, 'h', 2),
-          ("", "# of disp elements", int, "h", 2),
-          ("", "First element", int, "h", 2),
+          ("", "# of disp elements", int, 'h', 2),
+          ("", "First element", int, 'h', 2),
           ("", "Total elements", int, 'h', 2),
           ("", "Display sampling", EDISPSMP, 'h', 2),
           ("", "Scaling", ESCAL, 'h', 2),
-          ("", "Data pointer", long, 'l', 4),
-          ("", "In data", long, 'l', 4),
+          ("", "Data pointer", int, 'l', 4),
+          ("", "In data", int, 'l', 4),
           ("", "Log/linear x-axis", bool, 'h', 2),
           ("", "Sampled display data", bool, 'h', 2),
           ("", "Plot/Graph mode", bool, 'h', 2),
@@ -55,8 +58,9 @@
           ("", "Left grid", decode_float, None, 8),
           ("", "Right grid", decode_float, None, 8),
           ("", "Left data", decode_float, None, 8),
-          ("", "Right data", decode_float, None, 8),          
-    ]
+          ("", "Right data", decode_float, None, 8),
+          ]
+
 
 def decode_coord(data):
     """
@@ -69,73 +73,75 @@
     trace_header, idx = decode_datablock_header(data, TRACE_HEADER, idx)
     trace = read_datablock_trace(data, idx, trace_header["Number of elements"])
     return header, trace_header, trace
-    
+
 
 def main():
     import sys
-    import optparse
+    import argparse
     import numpy
-    
-    opt = optparse.OptionParser("A simple tool for displaying dumped coord data block")
-    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)
 
+    opt = argparse.ArgumentParser(
+        "A simple tool for displaying dumped coord data block")
+    opt.add_argument(
+        '-f', '--filename', default=None, dest='filename',
+        help='Input filename. If not set, read from stdin')
+    opt.add_argument(
+        '-m', '--mode', default='binary', dest='mode',
+        help='Dumping mode (may be "binary" [default], "ascii" or "ansi")')
+    opt.add_argument(
+        '-d', '--display-header', default=False, action="store_true",
+        dest="displayheader", help="Display the trace header")
+    opt.add_argument(
+        '-P', '--noplot-trace', default=True, action="store_false",
+        dest="plot", help="Do not display the plot of the trace")
+    opt.add_argument(
+        '-x', '--xmode', default='lin', dest='xmode',
+        help='X coordinate mode (may be "lin" [default] or "log")')
+    opt.add_argument(
+        '-y', '--ymode', default='lin', dest='ymode',
+        help='Y coordinate mode (may be "lin" [default], "log" or "db")')
+
+    options = opt.parse_args()
 
     if options.filename is None:
-        print "Can't deal stdin for now..."
+        print("Can't deal stdin for now...")
         sys.exit(1)
     try:
-        coord_header, header, data = decode_coord(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
+        with open(options.filename, 'rb') as f:
+            coord_header, header, data = decode_coord(f.read())
+    except Exception as e:
+        print("ERROR: can't read %s or interpret it as a HP3562 trace"
+              % options.filename)
+        print(e)
+        raise
         sys.exit(1)
 
     if options.displayheader:
-        print format_header(coord_header, HEADER, 100)
-        print format_header(header, TRACE_HEADER, 100)
+        print(format_header(coord_header, HEADER, 100))
+        print(format_header(header, TRACE_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)) 
+        x = numpy.linspace(f0, f0+dx*n, len(data))
         y = data.copy()
 
         ym = coord_header['Min value of data']
         yM = coord_header['Max value of data']
         ys = coord_header['Y scale factor']
-        
-        y[y<ym] = ym
+
+        y[y < ym] = ym
         y *= ys
-        
+
         import pylab
         pylab.ylabel(header['Amplitude units'])
         pylab.grid()
         pylab.plot(x, y)
-        pylab.xlabel('frequency (%s)'%header["X axis units"])
-        pylab.ylabel("%s (%s)"%(coord_header['Y coordinates'], header['Amplitude units']) )
+        pylab.xlabel('frequency (%s)' % header["X axis units"])
+        pylab.ylabel("%s (%s)" % (
+            coord_header['Y coordinates'], header['Amplitude units']))
         pylab.show()
-    
+
+
 if __name__ == "__main__":
     main()
--- a/pygpibtoolkit/HP3562A/dump_datablock.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/HP3562A/dump_datablock.py	Tue May 01 00:10:23 2018 +0200
@@ -10,13 +10,14 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+"""Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
+
 """
 
 import sys
 import time
-import pygpibtoolkit.pygpib
+import pygpibtoolkit.pygpib  # noqa
 from pygpibtoolkit import prologix
 
 
@@ -25,20 +26,23 @@
     Class dedicated to dump data blocks from HP3562A DSA (trace,
     internal state or coordinate).
     """
-    MODES = {'trace': 'DD',
-             'state': 'DS',
-             'coord': 'DC',
-             }
-    
-    FORMATS = {'binary': 'BN',
-               'ascii': 'AS',
-               'ansi': 'AN'}
-            
+    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)
+        super().__init__(device, baudrate, timeout, address, mode=1)
 
-    def dump(self, mode='trace', format="binary"): 
+    def dump(self, mode='trace', format="binary"):
         """
         Dump the required data block and return it as a raw string buffer.
 
@@ -52,53 +56,52 @@
         cmd = self.MODES[mode] + self.FORMATS[format]
 
         res = ""
-        print "command = ", cmd
+        print("command = %s" % cmd)
         # warning, this use direct low level serial communications...
-        self._cnx.write('%s\r'%cmd)
+        self._cnx.write('%s\r' % cmd)
         i = 0
-        while i<self._retries:
+        while i < self._retries:
             l = self._cnx.readline()
             if l.strip() == "":
                 i += 1
                 time.sleep(self._timeout)
                 continue
-            res += l 
+            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)
+    import argparse
+    opt = argparse.ArgumentParser(
+        "A simple tool for dumping the current trace")
+    opt.add_argument(
+        '-f', '--filename', default=None, dest='filename',
+        help='Output filename. If not set, write to stdout')
+    opt.add_argument(
+        '-d', '--device', default='/dev/ttyUSB0', dest='device',
+        help='Device of the RS232 connection (default: /dev/ttyUSB0)')
+    opt.add_argument(
+        '-a', '--address', default=0, dest='address',
+        help='GPIB address of the device')
+    opt.add_argument(
+        '-b', '--block', default='trace', dest='block',
+        help=('Data block to dump (may be "trace" [default], '
+              '"state" or "coord")'))
+    opt.add_argument(
+        '-m', '--mode', default='binary', dest='mode',
+        help='Dumping mode (may be "binary" [default], "ascii" or "ansi")')
+
+    options = opt.parse_args()
 
     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)))
+    sys.stderr.write("read %s bytes\n" % (len(res)))
     if options.filename:
         open(options.filename, 'w').write(res)
     else:
-        print res
-    
-if __name__=='__main__':
+        print(res)
+
+
+if __name__ == '__main__':
     main()
--- a/pygpibtoolkit/HP3562A/dump_datablock_mockup.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/HP3562A/dump_datablock_mockup.py	Tue May 01 00:10:23 2018 +0200
@@ -10,18 +10,20 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+"""Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
+
 """
 
 import glob
-import os
 import time
 import random
 
+
 class HP3562dumper:
-    """
-    A mockup thet will find in a directory some HPGL files and feed them randomly
+    """A mockup that will find in a directory some HPGL files and feed them
+    randomly
+
     """
     def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1,
                  address=5):
@@ -33,12 +35,12 @@
 
     def dump(self, mode='trace', format="binary"):
         fn = random.choice(self.filenames[mode])
-        ret = open(fn, 'rb').read()
-        if len(ret)>0:
-            time.sleep(random.randint(1,3))
+        with open(fn, 'rb') as f:
+            ret = f.read()
+        if len(ret) > 0:
+            time.sleep(random.randint(1, 3))
             return ret
         return None
 
     def send_command(self, *args):
         pass
-    
--- a/pygpibtoolkit/HP3562A/enum_types.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/HP3562A/enum_types.py	Tue May 01 00:10:23 2018 +0200
@@ -11,233 +11,258 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+"""Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
 
 Constants used for HP3562A data block interpretation.
+
 """
 
-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",
-        }
+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",
-       }
+ECH = {
+    0: "Channel 1",
+    1: "Channel 2",
+    2: "Channel 1&2",
+    3: "No channel",
+}
 
 EOVR = ECH
 
-EDOM = {0: 'Time',
-        1: 'Frequency',
-        2: 'Voltage (amplitude)',
-        }
+EDOM = {
+    0: 'Time',
+    1: 'Frequency',
+    2: 'Voltage (amplitude)',
+}
 
-EVLT = {0: "Peak",
-        1: "RMS",
-        2: "Volt (indicates peak only)",
-        }
+EVLT = {
+    0: "Peak",
+    1: "RMS",
+    2: "Volt (indicates peak only)",
+}
+
+EAMP = {
+    0: "Volts",
+    1: "Volts²",
+    2: "PSD (V²/Hz)",
+    3: "ESD (V²s/Hz)",
+    4: "PSD¹² (V/Hz¹²)",
+    5: "No unit",
+    6: "Unit volts",
+    7: "Unit volts²",
+}
 
-EAMP = {0: u"Volts",
-        1: u"Volts²",
-        2: u"PSD (V²/Hz)",
-        3: u"ESD (V²s/Hz)",
-        4: u"PSD¹² (V/Hz¹²)",
-        5: u"No unit",
-        6: u"Unit volts",
-        7: u"Unit volts²",
-        }
-        
-EXAXIS= {0: u"No units",
-         1: u"Hertz",
-         2: u"RPM",
-         3: u"Orders",
-         4: u"Seconds",
-         5: u"Revs",
-         6: u"Degrees",
-         7: u"dB",
-         8: u"dBV",
-         9: u"Volts",
-         10: u"V\u221AHz",
-         11: u"Hz/s",
-         12: u"V/EU",
-         13: u"Vrms",
-         14: u"V²/Hz",
-         15: u"%",
-         16: u"Points",
-         17: u"Records",
-         18: u"Ohms",
-         19: u"Hertz/octave",
-         20: u"Pulse/Rev",
-         21: u"Decades",
-         22: u"Minutes",
-         23: u"V²s/Hz",
-         24: u"Octave",
-         25: u"Seconds/Decade",
-         26: u"Seconds/Octave",
-         27: u"Hz/Point",
-         28: u"Points/Sweep",
-         29: u"Points/Decade",
-         30: u"Points/Octave",
-         31: u"V/Vrms",
-         32: u"V²",
-         33: u"EU referenced to chan 1",
-         34: u"EU referenced to chan 2",
-         35: u"EU value",
-         }
+EXAXIS= {
+    0: "No units",
+    1: "Hertz",
+    2: "RPM",
+    3: "Orders",
+    4: "Seconds",
+    5: "Revs",
+    6: "Degrees",
+    7: "dB",
+    8: "dBV",
+    9: "Volts",
+    10: "V\u221AHz",
+    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",
-         }
+EMEAS = {
+    0: "Linear resolution",
+    1: "Log resolution",
+    2: "Swept sine",
+    3: "Time capture",
+    4: "Linear resolution throughput",
+}
 
-EDEMOD = {45: "AM",
-          46: "FM",
-          47: "PM",
-          }
+EDEMOD = {
+    45: "AM",
+    46: "FM",
+    47: "PM",
+}
 
-EAVG = {0: "No data",
-        1: "Not averaged",
-        2: "Averaged",}
+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",
-        }
+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",
-         }
+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",
-            }
+EWINTYP = {
+    11: "Hanning",
+    12: "Flat top",
+    13: "Uniform",
+    14: "User window",
+    15: "Force/Exponential",
+}
 
-EFEXPW = { 0: "Force",
-           1: "Exponential",
-           }
+EFEXPW = {
+    0: "Force",
+    1: "Exponential",
+}
 
-EAVGTYP = { 6: "Stable",
-            7: "Exponential",
-            8: "Peak",
-            9: "Continuous peak",
-            10: "Averaging off",
-            }
+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",
-            }
+ETRGTYP = {
+    18: "Free run",
+    19: "Channel 1",
+    20: "Channel 2",
+    21: "External",
+    22: "Source trigger",
+    23: "HP-IB trigger",
+}
 
-ETRGSLP = { 16: "Positive",
-            17: "Negative",
-            }
+ETRGSLP = {
+    16: "Positive",
+    17: "Negative",
+}
 
-EPRVTYP = { 0: "Manual preview",
-            1: "Timed preview",
-            2: "Preview off",
-            }
+EPRVTYP = {
+    0: "Manual preview",
+    1: "Timed preview",
+    2: "Preview off",
+}
 
-ESMPTYP = { 24: "Internal sample",
-            25: "External sample",
-            }
+ESMPTYP = {
+    24: "Internal sample",
+    25: "External sample",
+}
 
 ERNGUNT = EXAXIS
 
-ERNGTYP = { 26: "Auto range on",
-            27: "Auto range off",
-            28: "Auto range set",
-            }
+ERNGTYP = {
+    26: "Auto range on",
+    27: "Auto range off",
+    28: "Auto range set",
+}
 
-EINCPL = { 29: "AC",
-           30: "DC",
-           }
+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",
-            }
+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",
-            }
+ESWPDIR = {
+    41: "Up",
+    42: "Sweep hold",
+    43: "Manual sweep",
+    44: "Down",
+}
 
-ESWPMOD = { 39: "Linear sweep",
-            40: "Log sweep",
-            }
+ESWPMOD = {
+    39: "Linear sweep",
+    40: "Log sweep",
+}
 
 EEXTSMPFREQUNT = EXAXIS
 
@@ -245,16 +270,19 @@
 
 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",
-              }
+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
 
@@ -264,24 +292,28 @@
 
 ECPTLGHUNT = EXAXIS
 
-EYCOORD = { 1: "Real",
-            2: "Imaginary",
-            3: "Linear magnitude",
-            4: "Log magnitude",
-            5: "dB",
-            6: "Nyquist",
-            7: "Not used",
-            8: "Phase",
-            9: "Nichols",
-            10: "dBm",
-            }
+EYCOORD = {
+    1: "Real",
+    2: "Imaginary",
+    3: "Linear magnitude",
+    4: "Log magnitude",
+    5: "dB",
+    6: "Nyquist",
+    7: "Not used",
+    8: "Phase",
+    9: "Nichols",
+    10: "dBm",
+}
 
-EDISPSMP = { 0: "Not sampled", # #displayed elements = total elements
-             1: "Half sampled", # #displayed elements = total elements/2
-             2: "Sampled", # #displayed elements < total elements
-            }
-ESCAL = { 0: "X and Y auto scale",
-          1: "X fixed, Y auto scale",
-          2: "X auto scale, Y fixed",
-          3: "X and Y fixed",
-          }
+EDISPSMP = {
+    0: "Not sampled",  # displayed elements = total elements
+    1: "Half sampled",  # displayed elements = total elements/2
+    2: "Sampled",  # displayed elements < total elements
+}
+
+ESCAL = {
+    0: "X and Y auto scale",
+    1: "X fixed, Y auto scale",
+    2: "X auto scale, Y fixed",
+    3: "X and Y fixed",
+}
--- a/pygpibtoolkit/HP3562A/mathtools.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/HP3562A/mathtools.py	Tue May 01 00:10:23 2018 +0200
@@ -10,12 +10,14 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+"""Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
+
 """
 
 import numpy
 
+
 def thd(spectrum, squared=True, db=True):
     """
     compute THD from a spectrum data, given by a simple 1D data
@@ -23,7 +25,7 @@
 
     The result is the couple (fmax, thd), this latter being expressed
     in 'db' if db is True; if not, in percentage.
-    
+
     """
     n = len(spectrum)
     # first, find the fundamental frequency
@@ -50,6 +52,7 @@
         thd = 100.0*numpy.sqrt(thd)
     return h_freqs2.mean(), thd
 
+
 def thd_n(spectrum, squared=True, db=True):
     """
     compute THD+N from a spectrum data, given by a simple 1D data
@@ -57,12 +60,12 @@
 
     The result is the thdN, expressed in 'db' if db is True; if not,
     in percentage.
-    
+
     """
     if not squared:
         spectrum = spectrum**2
     fmax = spectrum.argmax()
-    w = int(fmax/10) # argh
+    w = int(fmax/10)  # argh
     vmax = spectrum[fmax-w:fmax+w].sum()
     thd = (spectrum.sum()-vmax)/(vmax)
     if db:
@@ -70,7 +73,3 @@
     else:
         thd = 100.0*numpy.sqrt(thd)
     return thd
-
-
-    
-    
--- a/pygpibtoolkit/HP3562A/state_decoder.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/HP3562A/state_decoder.py	Tue May 01 00:10:23 2018 +0200
@@ -11,8 +11,8 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+"""Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
 
 state_decoder
 =============
@@ -20,12 +20,16 @@
 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. 
+This file can be exectued as a python script. Use '-h' for more informations.
+
 """
-from pygpibtoolkit.gpib_utils import format_datablock_header, decode_datablock_header
-from pygpibtoolkit.gpib_utils import decode_float, decode_string
 
-from pygpibtoolkit.HP3562A.enum_types import *
+from pygpibtoolkit.gpib_utils import (
+    format_datablock_header, decode_datablock_header,
+    decode_float, decode_string)
+
+from pygpibtoolkit.HP3562A.enum_types import *  # noqa
+
 
 HEADER = [("EMEAS", "Measurement mode", EMEAS, 'h', 2),
           ("MTYP1", "Measurement 1", EMTYP, 'h', 2),
@@ -120,7 +124,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
@@ -130,35 +135,36 @@
     """
     header, idx = decode_datablock_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)
+    import argparse
+    opt = argparse.ArgumentParser("A simple tool for tracing a dumped trace")
+    opt.add_argument(
+        '-f', '--filename', default=None, dest='filename',
+        help='Input filename. If not set, read from stdin')
+    opt.add_argument(
+        '-m', '--mode', default='binary', dest='mode',
+        help='Dumping mode (may be "binary" [default], "ascii" or "ansi")')
 
+    options = opt.parse_args()
 
     if options.filename is None:
-        print "Can't deal stdin for now..."
+        print("Can't deal stdin for now...")
         sys.exit(1)
     try:
-        header = decode_state(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
+        with open(options.filename, 'rb') as f:
+            header = decode_state(f.read())
+    except Exception as e:
+        print("ERROR: can't read %s or interpret it as a HP3562 trace"
+              % options.filename)
+        print(e)
+        raise
         sys.exit(1)
 
-    print format_datablock_header(header, HEADER, 100)
-    
+    print(format_datablock_header(header, HEADER, 100))
+
+
 if __name__ == "__main__":
     main()
-          
--- a/pygpibtoolkit/HP3562A/test/test_HP3562A.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/HP3562A/test/test_HP3562A.py	Tue May 01 00:10:23 2018 +0200
@@ -1,11 +1,17 @@
 #
-from  logilab.common.testlib import TestCase, InnerTest, unittest_main
+from unittest import TestCase
 
 from pygpibtoolkit.HP3562A.dump_datablock_mockup import HP3562dumper
 from pygpibtoolkit.gpibcontroller import GPIBController
 from pygpibtoolkit.gpibmockup import GPIB
 
+
 class TestHP3562ADeviceManager(TestCase):
     def test_device(self):
         c = GPIBController(GPIB())
-        self.failUnless(c.detect_devices() == {})
+        self.assertEqual(c.detect_devices(), {})
+
+
+if __name__ == '__main__':
+    from unittest import main
+    main()
--- a/pygpibtoolkit/HP3562A/trace_decoder.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/HP3562A/trace_decoder.py	Tue May 01 00:10:23 2018 +0200
@@ -11,8 +11,8 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+"""Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
 
 trace_decoder
 =============
@@ -20,62 +20,65 @@
 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. 
+This file can be exectued as a python script. Use '-h' for more informations.
+
 """
-import struct
+
 import numpy
-from pygpibtoolkit.gpib_utils import format_datablock_header, decode_datablock_header, read_datablock_trace
-from pygpibtoolkit.gpib_utils import decode_float, decode_string
+from pygpibtoolkit.gpib_utils import (
+    format_datablock_header, decode_datablock_header, read_datablock_trace,
+    decode_float)
 
-from pygpibtoolkit.HP3562A.enum_types import *
+from pygpibtoolkit.HP3562A.enum_types import *  # noqa
 
 
-HEADER = [ ("DSPFCT", "Display function", EDSP, 'h', 2),
-           ("NELTS", 'Number of elements', int, 'h', 2),
-           ("DSPELTS", 'Displayed elements', int, 'h', 2),
-           ("NAVG", 'Number of averages', int, 'h', 2),
-           ("CHSEL", 'Channel selection', ECH, 'h', 2),
-           ("OVST", 'Overflow status', EOVR, 'h', 2),
-           ("OVLP", 'Overlap percentage', int, 'h', 2),
-           ("DOM", 'Domain', EDOM, 'h', 2),
-           ("VOLTMEAS", 'Volts peak/rms', EVLT, 'h', 2),
-           ("AMPLUNT", 'Amplitude units', EAMP, 'h', 2),
-           ("XUNT", 'X axis units', EXAXIS, 'h', 2),
-           ("AMTHLBL", 'Auto math label', str, 's', 14),
-           ("TRLBL", 'Trace label', str, 's', 22),
-           ("EULB1", 'EU label 1', str, 's', 6),
-           ("EULB2", 'EU label 2', str, 's', 6),
-           ("VALTYPE", 'Float/Interger', bool, 'h', 2),
-           ("RVALTYPE", 'Complex/Real', bool, 'h', 2),
-           ("LIVE", 'Live/Recalled', bool, 'h', 2),
-           ("MATHRESU", 'Math result', bool, 'h', 2),
-           ("RVALINPUTTYPE", 'Real/Complex input', bool, 'h', 2),
-           ("LOGDATA", 'Log/Linear data', bool, 'h', 2),
-           ("AMTH", 'Auto math', bool, 'h', 2),
-           ("RTST", 'Real time status', bool, 'h', 2),
-           ("EMEAS", '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),
-           ]
+HEADER = [("DSPFCT", "Display function", EDSP, 'h', 2),
+          ("NELTS", 'Number of elements', int, 'h', 2),
+          ("DSPELTS", 'Displayed elements', int, 'h', 2),
+          ("NAVG", 'Number of averages', int, 'h', 2),
+          ("CHSEL", 'Channel selection', ECH, 'h', 2),
+          ("OVST", 'Overflow status', EOVR, 'h', 2),
+          ("OVLP", 'Overlap percentage', int, 'h', 2),
+          ("DOM", 'Domain', EDOM, 'h', 2),
+          ("VOLTMEAS", 'Volts peak/rms', EVLT, 'h', 2),
+          ("AMPLUNT", 'Amplitude units', EAMP, 'h', 2),
+          ("XUNT", 'X axis units', EXAXIS, 'h', 2),
+          ("AMTHLBL", 'Auto math label', str, 's', 14),
+          ("TRLBL", 'Trace label', str, 's', 22),
+          ("EULB1", 'EU label 1', str, 's', 6),
+          ("EULB2", 'EU label 2', str, 's', 6),
+          ("VALTYPE", 'Float/Interger', bool, 'h', 2),
+          ("RVALTYPE", 'Complex/Real', bool, 'h', 2),
+          ("LIVE", 'Live/Recalled', bool, 'h', 2),
+          ("MATHRESU", 'Math result', bool, 'h', 2),
+          ("RVALINPUTTYPE", 'Real/Complex input', bool, 'h', 2),
+          ("LOGDATA", 'Log/Linear data', bool, 'h', 2),
+          ("AMTH", 'Auto math', bool, 'h', 2),
+          ("RTST", 'Real time status', bool, 'h', 2),
+          ("EMEAS", '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):
     """
@@ -87,68 +90,70 @@
     complex values).
     """
     header, idx = decode_datablock_header(data, HEADER, idx)
-    return header, read_datablock_trace(data, idx, header["Number of elements"])
+    return header, read_datablock_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)
+    import argparse
+    opt = argparse.ArgumentParser("A simple tool for tracing a dumped trace")
+    opt.add_argument(
+        '-f', '--filename', default=None, dest='filename',
+        help='Output filename. If not set, read from stdin')
+    opt.add_argument(
+        '-m', '--mode', default='binary',dest='mode',
+        help='Dumping mode (may be "binary" [default], "ascii" or "ansi")')
+    opt.add_argument(
+        '-d', '--display-header', default=False, action="store_true",
+        dest="displayheader", help="Display the trace header")
+    opt.add_argument(
+        '-P', '--noplot-trace', default=True, action="store_false",
+        dest="plot", help="Do not display the plot of the trace")
+    opt.add_argument(
+        '-x', '--xmode', default='lin', dest='xmode',
+        help='X coordinate mode (may be "lin" [default] or "log")')
+    opt.add_argument(
+        '-y', '--ymode', default='lin', dest='ymode',
+        help='Y coordinate mode (may be "lin" [default], "log" or "db")')
 
+    options = opt.parse_args()
 
     if options.filename is None:
-        print "Can't deal stdin for now..."
+        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
+        with open(options.filename, 'rb') as f:
+            header, data = decode_trace(f.read())
+    except Exception as 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_datablock_header(header, HEADER, 100)
+        print(format_datablock_header(header, HEADER, 100))
+
     f0 = header['Start freq value']
     dx = header['Delta X-axis']
     n = header['Number of elements']
-    x = numpy.linspace(f0, f0+dx*n, len(data)) 
+    x = numpy.linspace(f0, f0+dx*n, len(data))
     y = data.copy()
     if options.plot:
         import pylab
         if options.ymode != "lin":
-            minv = min(y[y>0])
-            y[y==0] = minv
+            minv = min(y[y > 0])
+            y[y == 0] = minv
             y = numpy.log10(y)
         if options.ymode == "db":
-            y = y*10
+            y = y * 10
             pylab.ylabel('db')
         pylab.grid()
         pylab.plot(x, y)
         pylab.xlabel('frequency')
-        pylab.show()    
+        pylab.show()
     return header, x, y
 
+
 if __name__ == "__main__":
     h, x, y = main()
--- a/pygpibtoolkit/HP8904.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/HP8904.py	Tue May 01 00:10:23 2018 +0200
@@ -10,33 +10,32 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2009 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+""" Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
 
 """
-import mx.DateTime as dt
-
-from pygpibtoolkit.pygpib import Constants, Command
-from pygpibtoolkit.pygpib import Mode, ModeCommand
+from pygpibtoolkit.pygpib import ModeCommand
 from pygpibtoolkit.pygpib import VoltageValue, FrequencyValue
 from pygpibtoolkit.pygpib import FloatUnitValue
-
 from pygpibtoolkit.pygpib import SimpleMode
 
 from pygpibtoolkit.gpibcontroller import AbstractGPIBDevice, deviceRegister
 
+
 class _VoltageValue(VoltageValue):
-    _units = "VL","MV","UV"
+    _units = "VL", "MV", "UV"
+
 
 class APA(_VoltageValue):
     "CHAN A AMP - Channel A amplitude"
-class APB(_VoltageValue):
+class APB(_VoltageValue):  # noqa
     "CHAN B AMP - Channel B amplitude"
-class APC(_VoltageValue):
+class APC(_VoltageValue):  # noqa
     "CHAN C AMP - Channel C amplitude"
-class APD(_VoltageValue):
+class APD(_VoltageValue):  # noqa
     "CHAN D AMP - Channel D amplitude"
 
+
 class Backlight(SimpleMode):
     "Backlight"
 
@@ -45,7 +44,7 @@
 
 
 class _PerOutputMode(SimpleMode):
-    
+
     def __init__(self):
         self._cmds = []
         for cmdname, cmd in self.__class__.__dict__.items():
@@ -58,7 +57,7 @@
         assert self._prefix+value in self._cmds
         return self._prefix+value, value
 
-    
+
 class Filter1(_PerOutputMode):
     "Filter for output 1"
     _prefix = "FS1"
@@ -67,10 +66,11 @@
     LO = ModeCommand("Gaussian (low overshoot)", "GAUSS.")
     AU = ModeCommand("Automatic", "AUTO")
 
+
 class Filter2(Filter1):
     "Filter for output 2"
     _prefix = "FS2"
-    
+
 
 class Float1(_PerOutputMode):
     "Float Control for output 1"
@@ -78,45 +78,53 @@
     ON = ModeCommand("On", "ON")
     OF = ModeCommand("Off", "OFF")
 
+
 class Float2(Float1):
     "Float Control for output 2"
     _prefix = "FC2"
 
+
 class _FrequencyValue(FrequencyValue):
-    _units = ['HZ','KZ']
-    
+    _units = ['HZ', 'KZ']
+
+
 class FRA(_FrequencyValue):
     "CHAN A FRQ - Channel A frequency"
-class FRB(_FrequencyValue):
+class FRB(_FrequencyValue):  # noqa
     "CHAN B FRQ - Channel B frequency"
-class FRC(_FrequencyValue):
+class FRC(_FrequencyValue):  # noqa
     "CHAN C FRQ - Channel C frequency"
-class FRD(_FrequencyValue):
+class FRD(_FrequencyValue):  # noqa
     "CHAN D FRQ - Channel D frequency"
 
+
 class OutputControl1(_PerOutputMode):
     "Output 1"
     _prefix = "OO1"
     ON = ModeCommand("On", "ON")
     OF = ModeCommand("Off", "OFF")
 
+
 class OutputControl2(OutputControl1):
     "Output 2"
     _prefix = "OO2"
 
+
 class _PhaseValue(FloatUnitValue):
     _units = ['DG', 'RD']
     _name = "phase"
 
+
 class PHA(_PhaseValue):
     "CHAN A PHA - Channel A phase"
-class PHB(_PhaseValue):
+class PHB(_PhaseValue):  # noqa
     "CHAN B PHA - Channel B phase"
-class PHC(_PhaseValue):
+class PHC(_PhaseValue):  # noqa
     "CHAN C PHA - Channel C phase"
-class PHD(_PhaseValue):
+class PHD(_PhaseValue):  # noqa
     "CHAN D PHA - Channel D phase"
 
+
 class _Waveform(_PerOutputMode):
     "waveform"
     SI = ModeCommand('Sine', 'SINE')
@@ -126,19 +134,20 @@
     NS = ModeCommand('Noise', 'NOIS')
     DC = ModeCommand('DC', 'DC')
 
+
 class WaveformA(_Waveform):
     "CHAN A FORM - Channel A waveform"
     _prefix = "WFA"
-class WaveformB(_Waveform):
+class WaveformB(_Waveform):  # noqa
     "CHAN B FORM - Channel B waveform"
     _prefix = "WFB"
-class WaveformC(_Waveform):
+class WaveformC(_Waveform):  # noqa
     "CHAN C FORM - Channel C waveform"
     _prefix = "WFC"
-class WaveformD(_Waveform):
+class WaveformD(_Waveform):  # noqa
     "CHAN D FORM - Channel D waveform"
     _prefix = "WFD"
-    
+
 
 class _Destination(_PerOutputMode):
     "Destination"
@@ -150,13 +159,14 @@
     OC1 = ModeCommand('OC1', 'OC1')
     OC2 = ModeCommand('OC2', 'OC2')
     OF = ModeCommand('OF', 'OF')
-    
+
+
 class HP8904Device(AbstractGPIBDevice):
-    _accepts = ["^(?P<model>HP *8904A).*$",]
+    _accepts = ["^(?P<model>HP *8904A).*$"]
     _idn = "ID"
 
     def __init__(self, idn, address, controller):
         super(HP8904Device, self).__init__(idn, address, controller)
 
-        
+
 deviceRegister.register_manager(HP8904Device)
--- a/pygpibtoolkit/__init__.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/__init__.py	Tue May 01 00:10:23 2018 +0200
@@ -10,6 +10,6 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+""" Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
 """
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pygpibtoolkit/detect.py	Tue May 01 00:10:23 2018 +0200
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+""" Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
+"""
+import sys
+import signal
+import time
+
+from pygpibtoolkit.gpibcontroller import GPIBController
+from pygpibtoolkit.prologix import GPIB
+
+
+def main():
+    import argparse
+    opt = argparse.ArgumentParser(
+        "A simple tool for detecting connected GPIB devices")
+    opt.add_argument(
+        '-d', '--device', default="/dev/ttyUSB0",
+        dest="device",
+        help="Device of connected Prologix GPIB bundle [/dev/ttyUSB0]",)
+    options = opt.parse_args(sys.argv)
+
+    print("Detecting GPIB devices on the bus. Please wait until completion.")
+    cnx = GPIB(device=options.device)
+    c = GPIBController(cnx)
+
+    signal.signal(signal.SIGINT, c.stop)
+    signal.signal(signal.SIGQUIT, c.stop)
+
+    time.sleep(1)
+    devices = c.detect_devices()
+    c.stop()
+
+    print("GPIB devices:")
+    for k in sorted(devices.keys()):
+        print("%-3d: %s" % (k, devices[k]))
+    return c, devices
+
+
+if __name__ == "__main__":
+    main()
--- a/pygpibtoolkit/gpib_utils.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/gpib_utils.py	Tue May 01 00:10:23 2018 +0200
@@ -11,8 +11,8 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+""" Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
 
 Several utility functions for GPIB data conversions
 """
@@ -20,6 +20,7 @@
 import re
 import numpy
 
+
 ########################################
 # internal binary types decoders
 
@@ -43,27 +44,30 @@
             i3 = i3*2**(-15)
         i4 = i4 * 2**(-38)
         return (i1+i2+i3+i4)*2**(e-15)
-        
+
+
 def decode_string(s):
     """
     Decode a string from the HP binay representation.
     """
-    nb = ord(s[0])
+    nb = s[0]
     s = s[1:nb+2]
-    r = ""
+    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
+        r.append(chr(c & 0x7F))
+    return ''.join(r)
+
 
 ###
 # Datablock useful functions
 def format_datablock_header(header, head_struct, columns=80):
     """
-    Pretty print a data block (trace, state or coord) 
+    Pretty print a data block (trace, state or coord)
     """
-    bool_re = re.compile(r'((?P<before>.*) )?(?P<flag>\w+/\w+)( (?P<after>.*))?')
-    
+    bool_re = re.compile(
+        r'((?P<before>.*) )?(?P<flag>\w+/\w+)( (?P<after>.*))?')
+
     todisp = []
     for row in head_struct:
         pname = row[0]
@@ -72,7 +76,7 @@
         if typ is None:
             continue
         val = header.get(key, "N/A")
-        if isinstance(val, basestring):
+        if isinstance(val, str):
             val = repr(val)
         elif typ is bool and isinstance(val, typ):
             m = bool_re.match(key)
@@ -87,34 +91,37 @@
                 val = d['flag'].split('/')[not val]
             else:
                 val = str(val)
-        else:            
+        else:
             val = str(val)
-        todisp.append((key+":", 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
+    fmt = "%%-%ds %%-%ds" % (maxk, maxv)
+    w = maxk + maxv + 4
+    ncols = columns // w
     if ncols:
-        nrows = len(todisp)/ncols
+        nrows = len(todisp) // ncols
     else:
         nrows = len(todisp)
         ncols = 1
-    res = ""
+    res = []
     for i in range(nrows):
-        res += "| ".join([fmt%todisp[j*nrows+i] for j in range(ncols)]) + "\n"
-    return res
+        res.append(("| ".join(
+            fmt % todisp[j * nrows + i] for j in range(ncols))).rstrip())
+    return '\n'.join(res)
+
 
-def decode_datablock_header(data, header_struct, idx=0):    
+def decode_datablock_header(data, header_struct, idx=0):
     d = data
-    if d[idx:].startswith('#'):
+    if d[idx:idx + 1] == b'#':
         # we have a preliminary header here...
-        typ = d[idx:idx+2]
-        assert typ == "#A"
+        typ = d[idx:idx + 2]
+        assert typ == b"#A"
         idx += 2
         totlen = struct.unpack('>h', d[idx:idx+2])[0]
         idx += 2
-    tt=0
+        print('  header at %s for %s' % (idx, totlen))
+    tt = 0
     header = {}
     for i, (cmd, nam, dtype, fmt, nbytes) in enumerate(header_struct):
         if dtype is None:
@@ -135,10 +142,11 @@
         idx += nbytes
     return header, idx
 
+
 def read_datablock_trace(data, idx, nelts):
-    assert len(data[idx:]) >= (nelts*4), "data[idx:] is too small (%s for %s)"%(len(data[idx:]), (nelts*4))
+    assert len(data[idx:]) >= (nelts*4), "data[idx:] is too small (%s for %s)" % (len(data[idx:]), (nelts*4))
     resu = []
     for i in range(nelts):
-        resu.append(decode_float(data[idx: idx+4]))
+        resu.append(decode_float(data[idx: idx + 4]))
         idx += 4
     return numpy.array(resu, dtype=float)
--- a/pygpibtoolkit/gpibcontroller.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/gpibcontroller.py	Tue May 01 00:10:23 2018 +0200
@@ -11,8 +11,8 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+""" Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
 
 A general purpose GPIB controller based on prologix GPIB device
 """
@@ -24,9 +24,10 @@
 from inspect import isclass
 
 from serial.serialutil import SerialException
-from prologix import GPIB, GPIB_CONTROLLER, GPIB_DEVICE
+from pygpibtoolkit.prologix import GPIB, GPIB_CONTROLLER, GPIB_DEVICE
 from pygpibtoolkit.pygpib import AbstractCommand
 
+
 class AbstractGPIBDeviceMetaclass(type):
     def __new__(mcs, name, bases, classdict):
         # Automatically add commands to Device classes.
@@ -45,15 +46,14 @@
                 setattr(new_cls, pname, p)
                 for subattr in p._cmds:
                     def getter(self, cmd=subattr):
-                        print "self=",self
                         ret = self.send_command(cmd)
                         if not ret:
                             ret = ""
                         return ret
                     setattr(new_cls, subattr, property(getter))
-                    
         return new_cls
 
+
 class AbstractGPIBDevice(object):
     __metaclass__ = AbstractGPIBDeviceMetaclass
     _accepts = []
@@ -65,42 +65,44 @@
             if m:
                 return m.group('model')
         return False
-        #return idn in cls._accepts
-    
+        # return idn in cls._accepts
+
     def __init__(self, idn, address, controller):
         self._idn = idn
         self._address = address
         self._controller = controller
         self._use_cache = True
-        
+
         self._cache = {}
         for pname, param in self.__class__.__dict__.items():
-            if isinstance(param, AbstractCommand) and not param._readonly and param._cached:
+            if (isinstance(param, AbstractCommand)
+                    and not param._readonly
+                    and param._cached):
                 self._cache[pname] = param._init_value
 
     def use_cache(self, use=None):
         if use is None:
             return self._use_cache
         self._use_cache = bool(use)
-        
+
     def _get(self, name):
         if self._use_cache and name in self._cache and \
                 self._cache[name] is not None:
             return self._cache[name]
-        
-        param = getattr(self.__class__, name) 
+
+        param = getattr(self.__class__, name)
         value = param.get_value_from_device(self)
         if name in self._cache:
             self._cache[name] = value
         return value
-            
+
     def _set(self, name, value):
-        param = getattr(self.__class__, name) 
+        param = getattr(self.__class__, name)
         res, value = param.send_value_to_device(self, value)
         if name in self._cache:
             self._cache[name] = value
         return res
-    
+
     def manage_srq(self, statusbyte):
         pass
 
@@ -115,8 +117,8 @@
         Read currently available DATA in buffer
         """
         return self.send_command('')
-    
-    
+
+
 class GPIBDeviceRegister(object):
     def __init__(self):
         self._registry = []
@@ -134,19 +136,23 @@
         return [mgr._idn for mgr in self._registry if mgr._idn]
 
     def __str__(self):
-        msg = "<GPIBDeviceRegister: %s managers\n"%len(self._registry)
+        msg = "<GPIBDeviceRegister: %s managers\n" % len(self._registry)
         for mgr in self._registry:
-            msg += "  %s: %s\n"%(mgr.__name__, ", ".join(mgr._accepts))
+            msg += "  %s: %s\n" % (mgr.__name__, ", ".join(mgr._accepts))
         msg += ">"
         return msg
-    
+
+
 deviceRegister = GPIBDeviceRegister()
 
+
 class GenericGPIBDevice(AbstractGPIBDevice):
     _idn = "*IDN?"
-    
+
+
 deviceRegister.register_manager(GenericGPIBDevice)
 
+
 class GPIBController(object):
     """
     The main GPIB Controller In Charge.
@@ -162,7 +168,7 @@
     with GPIB and device commands, which are executed ASAP (ie. as
     soon as the GPIB bus if free and the device states itself as
     ready.)
-    
+
     """
     def __init__(self, gpibcnx=None, controller_address=21):
         self._address = controller_address
@@ -170,11 +176,11 @@
         self._devices = {}
         self._debug = False
         self._setup_threading_system()
-        
+
     def __del__(self):
-        print "deleting controller"
+        # print "deleting controller"
         self._stop.set()
-        
+
     def _setup_threading_system(self):
         self._n_cmds = 0
         self._n_cmds_lock = threading.RLock()
@@ -189,23 +195,23 @@
         self._cnx_thread.start()
         self._suspended = False
         self._loop_interrupter.set()
-        
+
     def _check_srq(self):
         if self._cnx and self._cnx.check_srq():
             addrs = sorted(self._devices.keys())
             polled = self._cnx.poll(addrs)
             for add in addrs:
                 if polled[add] & 0x40:
-                    #print "device %s (#%s) requested attention"%(self._devices[add]._idn, add)
+                    # print "device %s (#%s) requested attention"%(self._devices[add]._idn, add)
                     # TODO: check what to do...
                     self._devices[add].manage_srq(polled[add])
-        
+
     def _cnx_loop(self):
         while 1:
             if self._debug:
                 sys.stderr.write('.')
             if self._stop.isSet():
-                print "_stop set, exiting"
+                print("_stop set, exiting")
                 return
             while not self._loop_interrupter.isSet():
                 if self._debug:
@@ -249,25 +255,25 @@
     def suspend(self, *args):
         while not self._loop_interrupter.isSet():
             self._loop_interrupter.wait()
-        print "Suspending main connection loop"
+        print("Suspending main connection loop")
         self._suspended = True
         self._loop_interrupter.clear()
 
     def resume(self, *args):
         if not self._suspended:
             return
-        print "Resuming main connection loop"
+        print("Resuming main connection loop")
         self._suspended = False
         self._loop_interrupter.set()
-    
+
     def send_command(self, addr, cmd, sync=True, cb=None):
-        #print "SEND CMD %s"%repr(cmd)
+        # print "SEND CMD %s"%repr(cmd)
         if cb is not None and callable(cb):
             sync = False
         if sync and self._loop_interrupter.isSet():
-            print "WARNING: send synchronized command when cnx is locked"
-            #sync = False
-        
+            print("WARNING: send synchronized command when cnx is locked")
+            # sync = False
+
         self._n_cmds_lock.acquire()
         self._n_cmds += 1
         n_cmd = self._n_cmds
@@ -276,7 +282,7 @@
         cond = self._cmd_condition
         cond.acquire()
         if sync:
-            cb = threading.Event()            
+            cb = threading.Event()
         self._cmd_queue[n_cmd] = (addr, cmd, cb)
         cond.notify()
         cond.release()
@@ -289,7 +295,7 @@
             resu = self._results_queue.pop(n_cmd)
             cond.release()
             return resu
-    
+
     def detect_devices(self):
         """
         Perform a Serial Poll on all addresses to detect alive devices
@@ -309,21 +315,26 @@
                         if idn:
                             self.register_device(address, idn)
                             devices[address] = str(idn)
-                            print "registered device", idn
+                            print("registered device %s" % idn)
                             break
                         else:
                             # redo a spoll, it should have set the err bit
                             poll = self._cnx.poll(address)
-                            if (isinstance(poll, dict) and address not in poll) or (not poll & 0x20): # 0x20 == ERR bit
-                                print "WARNING: device %d did not answer to a %s command by setting its ERR bit"%(address, id_str)
+                            if ((isinstance(poll, dict)
+                                    and address not in poll)
+                                    or (not poll & 0x20)):  # 0x20 == ERR bit
+                                print("WARNING: device %d did not answer to a "
+                                      "%s command by setting its ERR bit" %
+                                      (address, id_str))
                             devices[address] = "UNKNOWN"
                     else:
-                        print "WARNING: Can't retrieve IDN of device at address ", address
+                        print("WARNING: Can't retrieve IDN of device at "
+                              "address %s" % address)
                         devices[address] = "UNKNOWN"
         finally:
             self._loop_interrupter.set()
         return devices
-    
+
     def register_device(self, address, idn):
         """
         Register a device manager for device at given GPIB
@@ -335,15 +346,16 @@
             self._devices[address] = devicemgr
             return devicemgr
         else:
-            print "WARNING: can't find a manager for", repr(idn)
-            #print deviceRegister._registry
+            print("WARNING: can't find a manager for %r" % idn)
+            # print deviceRegister._registry
 
     def idn(self, address):
         """
         Query identity of the addressed device.
         """
-        return self.send_command(address, self._devices[address].__class__._idn).strip()
-    
+        return self.send_command(
+            address, self._devices[address].__class__._idn).strip()
+
     def status(self, address):
         """
         Query status byte for device at given address
@@ -355,7 +367,7 @@
             return self._cnx.poll(address)
         finally:
             self._loop_interrupter.set()
-            
+
     def get_devicemanager(self, address):
         return self._devices.get(address)
 
--- a/pygpibtoolkit/prologix.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/prologix.py	Tue May 01 00:10:23 2018 +0200
@@ -10,51 +10,53 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+""" Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
 
 prologix
 ========
 
 Module defining a communication object to talk to Prologix USB-GPIB controler.
+"""
 
-"""
 import sys
 import serial
 import time
 
-from pygpibtoolkit import pygpib
 from pygpibtoolkit.pygpib import ConnectionError
 
 GPIB_CONTROLLER = 1
 GPIB_DEVICE = 0
 
-class GPIB(object):
+
+class GPIB:
     _retries = 15
+
     def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1,
-                 address=0, mode=1):        
+                 address=0, mode=1):
         """
         Create a new GPIB controller for the Prologix USB-GPIB device
-        located on serial device 'device'.        
+        located on serial device 'device'.
         """
-        self._cnx = serial.Serial(port=device, baudrate=baudrate, timeout=timeout)
+        self._cnx = serial.Serial(port=device, baudrate=baudrate,
+                                  timeout=timeout)
         self._timeout = timeout
 
         try:
             res = self._cnx.readlines()
-            if res: # empty evetual read buffer
-                print "there where pending stuffs in buffer", repr(res)
+            if res:  # empty evetual read buffer
+                print("there where pending stuffs in buffer %r" % res)
             self.set_mode(1)
             self.set_address(address)
             self._set_cmd('auto', 0)
             self.set_mode(mode)
-        except Exception, e:
-            print "Humm, something went wrong", e
-            
+        except Exception as e:
+            print("Humm, something went wrong: %s" % e)
+
     def set_address(self, address, check=True):
         """
         Set the address of the GPIB device:
-        
+
         - if the device is the Controller In Charge, this is the
           address of the device commands are sent to,
 
@@ -62,12 +64,12 @@
         """
         self._set_cmd('addr', address, check)
         self._address = address
-        #self._set_cmd('auto', 0)
-        
+        # self._set_cmd('auto', 0)
+
     def set_mode(self, mode):
         """
         Set GPIB device mode to 'mode':
-        
+
         - GPIB_CONTROLLER: set the device as the Controller In Charge
           on the GPIB bus
 
@@ -87,7 +89,7 @@
 
     def set_device(self):
         """
-        Set the GPIB device as a simple device on the GPIB bus.        
+        Set the GPIB device as a simple device on the GPIB bus.
         """
         self.set_mode(0)
 
@@ -101,24 +103,24 @@
         if address is not None:
             self.set_address(address)
         self._cnx.write(cmd+';\r')
-        time.sleep(self._timeout) # required?
+        time.sleep(self._timeout)  # required?
         return self.read_eoi()
-        
+
     def read_eoi(self, address=None):
         """
         Read the HPIB buffer from device, till EOI is performed, or timeout.
         """
         if address is not None:
             self.set_address(address, check=False)
-        self._cnx.write('++read eoi\r') # idem
+        self._cnx.write('++read eoi\r')  # idem
         ret = ""
         i = 0
         while not ret.endswith('\r\n') and i<3:
             ret += ''.join(self._cnx.readlines())
-            time.sleep(self._timeout) # required?
+            time.sleep(self._timeout)  # required?
             i += 1
         return ''.join(ret)
-            
+
     def check_srq(self):
         """
         Check the SRQ line
@@ -126,10 +128,10 @@
         assert self._mode == 1, "must be the Controller In Charge"
         ret = self._cnx.readline().strip()
         if ret:
-            print "garbage:", ret
+            print("garbage: %s" % ret)
         self._cnx.write('++srq\r')
         ret = self._cnx.readline().strip()
-        if ret in ["0","1"]:
+        if ret in "01":
             return bool(int(ret))
         return None
 
@@ -139,28 +141,28 @@
         """
         if address is not None:
             self.set_address(address, check=False)
-        self._cnx.write('++trg\r') # idem
-        
+        self._cnx.write('++trg\r')  # idem
+
     def poll(self, addresses=None, verbose=False):
         """
         Poll every address, and return a dictionnary
-         {add: status, ...}        
-        """        
-        assert self._mode == 1, "must be the Controller In Charge"        
+         {add: status, ...}
+        """
+        assert self._mode == 1, "must be the Controller In Charge"
         only_one = False
         if addresses is None:
             addresses = range(31)
         if not isinstance(addresses, (list, tuple)):
             addresses = [addresses]
             only_one = True
-        if len(addresses)==0:
+        if not addresses:
             return None
 
         if verbose:
             sys.stderr.write('polling ')
         dico = {}
         for add in addresses:
-            self._cnx.write('++spoll %d\r'%add)
+            self._cnx.write('++spoll %d\r' % add)
             time.sleep(0.1)
             ret = self._cnx.readline().strip()
             if ret:
@@ -170,15 +172,17 @@
             else:
                 if verbose:
                     sys.stderr.write('.')
-                
-            time.sleep(0.30) # need to wait at least 150ms (not enough on prologix)
+
+            # need to wait at least 150ms (not enough on prologix)
+            time.sleep(0.30)
+
         if verbose:
             sys.stderr.write('\n')
         self.set_address(self._address)
         if only_one and dico:
             return dico.values()[0]
         return dico
-    
+
     def _read(self):
         for i in range(self._retries):
             rdata = self._cnx.readline()
@@ -188,21 +192,20 @@
         return rdata
 
     def _set_cmd(self, cmd, value, check=True):
-        self._cnx.write('++%s %d\r'%(cmd, value))
+        self._cnx.write('++%s %d\r' % (cmd, value))
         if check:
-            self._cnx.write('++%s\r'%(cmd))
+            self._cnx.write('++%s\r' % (cmd))
             rval = self._read().strip()
             if not rval.isdigit() or int(rval) != value:
-                raise ConnectionError("Can't set GPIB %s to %s [ret=%s]"%(cmd, value, repr(rval)))
+                raise ConnectionError("Can't set GPIB %s to %s [ret=%s]" % (
+                    cmd, value, repr(rval)))
 
     def reset(self):
         """
         Perform a reset of the USB device
-        
+
         """
-        print "Resetting GPIB controller"
+        print("Resetting GPIB controller")
         self._cnx.write('++rst\r')
-        print "Must wait for 5 seconds"
+        print("Must wait for 5 seconds")
         time.sleep(5)
-        
-        
--- a/pygpibtoolkit/pygpib.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/pygpib.py	Tue May 01 00:10:23 2018 +0200
@@ -10,45 +10,50 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+"""Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
 
 gpib: create serial connection to GPIB-USB device (Prologix is the
 only supported device for now).
+
 """
 import re
-import serial
-from serial.serialutil import SerialException
-import time
-from pygpibtoolkit.tools import AbstractRegister 
+from pygpibtoolkit.tools import AbstractRegister  # noqa
 import operator
 
+
 class ConnectionError(Exception):
     pass
 
-class Condition(object):
+
+class Condition:
     def __call__(self, device):
         return True
+
     def __and__(self, cond):
         assert isinstance(cond, Condition)
         return _ComposedCondition(operator.__and__, self, cond)
+
     def __or__(self, cond):
         assert isinstance(cond, Condition)
         return _ComposedCondition(operator.__or__, self, cond)
-    
+
+
 class _ComposedCondition(Condition):
     def __init__(self, op, *args):
         self._conditions = list(args[:])
-        self._reduc_op = op 
+        self._reduc_op = op
         for cond in args:
             assert isinstance(cond, Condition)
             if isinstance(cond, _ComposedCondition) and cond._reduc_op == op:
                 i = self._conditions.index(cond)
                 self._conditions[i:i+1] = cond._conditions
+
     def __call__(self, device):
         return reduce(self._reduc_op, [f(device) for f in self._conditions])
-    
-class Constants(object):
+
+
+class Constants:
     def __init__(self):
         self.constants = {}
         self.descriptions = {}
@@ -58,7 +63,7 @@
             self.constants[v] = k
             self.rev_constants[k] = v
             self.descriptions[v] = m
-            
+
     def __getitem__(self, k):
         if isinstance(k, basestring):
             return self.rev_constants[k]
@@ -70,18 +75,20 @@
             k = self.rev_constants[k]
         return self.descriptions[k]
 
-class MODE(Constants):    
+
+class MODE(Constants):
     _constants = [(1, "CONTROLLER", "Set device as Controller in Charge"),
                   (0, "DEVICE", "Set device as simple listener"),
                   ]
 
-class ModeCommand(object):
+class ModeCommand:
     def __init__(self, description, name, condition=None):
         self.name = name
         self.description = description
         self.condition = condition
-    
-class AbstractCommand(object):
+
+
+class AbstractCommand:
     """
     Base class for HPIB command description.
 
@@ -92,7 +99,7 @@
     _init_value = None
     _cached = True
     _cmds = []
-    
+
     def __get__(self, instance, owner):
         if instance is None:
             return self
@@ -102,7 +109,7 @@
         if instance is None:
             return self
         return instance._set(self.__class__.__name__, value)
-    
+
     def get_value_from_device(self, device):
         cmd = self.build_get_cmd()
         value = device.send_command(cmd)
@@ -113,24 +120,25 @@
         cmd, value = self.build_set_cmd(value)
         res = device.send_command(cmd)
         return res, self.convert_to(value)
-    
+
     def build_get_cmd(self):
         return self.__class__.__name__
 
     def build_set_cmd(self, *value):
-        raise ValueError, "Can't set value for command '%s'"%self.__class__.__name__
-    
+        raise ValueError(
+            "Can't set value for command '%s'" % self.__class__.__name__)
+
     def convert_from(self, *value):
         return None
 
     def convert_to(self, *value):
         return None
 
-    
+
 class Command(AbstractCommand):
     """pure command
     """
-    
+
 
 class AbstractValue(Command):
     _readonly = False
@@ -140,39 +148,44 @@
 
     def build_set_cmd(self, *value):
         value = self.convert_to(*value)
-        cmd = "%s %s"%(self.__class__.__name__, value)
+        cmd = "%s %s" % (self.__class__.__name__, value)
         return cmd, value
-    
+
     def convert_to(self, *value):
         if value:
             return str(value[0])
         return ""
-    
+
     def convert_from(self, *value):
         if value:
             return self._type(value[0].strip())
         return None
-    
+
+
 class BoolValue(AbstractValue):
     _type = bool
+
     def convert_from(self, *value):
         if value:
-            return value[0] and value[0].lower() in ['1','true','yes','on']
+            return value[0] and value[0].lower() in ['1', 'true', 'yes', 'on']
         return False
-    
+
     def convert_to(self, *value):
         if value:
-            return  value[0] and "1" or "0"
-        return "" # XXX is it correct?
-    
+            return value[0] and "1" or "0"
+        return ""  # XXX is it correct?
+
+
 class IntValue(AbstractValue):
     _type = int
+
     def convert_from(self, *value):
         if value:
-            # int is resturned as a string representing a float 
+            # int is resturned as a string representing a float
             return self._type(float(value[0].strip()))
         return None
 
+
 class flag(int):
     def __new__(cls, val, constants):
         val = int.__new__(cls, val)
@@ -181,40 +194,50 @@
             if name not in ['', 'N/A']:
                 setattr(val, name, v)
         return val
-    
+
     def __str__(self):
-        return '%s <' + '|'.join([x[1] for x in self._constants if x[0]&self]) + ">"
+        return ('%s <'
+                + '|'.join(x[1] for x in self._constants if x[0] & self)
+                + ">")
+
     def flags(self):
-        return [x[1] for x in self._constants if x[0] & (self and x[1]) not in ['','N/A']]
-        
+        return [x[1] for x in self._constants
+                if x[0] & (self and x[1]) not in ['', 'N/A']]
+
     def descriptions(self):
-        return [x[2] for x in self._constants if (x[0] & self) and x[1] not in ['','N/A']]
+        return [x[2] for x in self._constants
+                if (x[0] & self) and x[1] not in ['', 'N/A']]
+
 
 class Flag(IntValue):
     _readonly = True
     _constants = []
     _type = flag
+
     def convert_from(self, *value):
         if value:
             return self._type(float(value[0].strip()), _constants)
         return None
-    
+
+
 class FloatValue(AbstractValue):
     _type = float
 
+
 class PercentageValue(FloatValue):
     pass #TODO
 
+
 class FloatUnitValue(FloatValue):
     """
     A Float value with unit (frequency, etc.)
     """
     def convert_to(self, *value):
         if value:
-            if len(value)==1:
-                if isinstance(value[0], basestring):
+            if len(value) == 1:
+                if isinstance(value[0], str):
                     value = value[0].strip()
-                    reunits = '(?P<unit>'+'|'.join(self._units)+')'
+                    reunits = '(?P<unit>' + '|'.join(self._units) + ')'
                     reexpr = r'(?P<value>[-]?[0-9]+([.][0-9]+)?) *' + reunits
                     m = re.match(reexpr, value)
                     try:
@@ -224,8 +247,8 @@
                         else:
                             unit = self._units[0]
                         freq = float(value)
-                    except Exception, e:
-                        raise ValueError, "Can't interpret %s as a %s"%(repr(value), self._name)
+                    except Exception as e:
+                        raise ValueError("Can't interpret %r as a %s" % (value, self._name))
                 else:
                     freq = float(value[0])
                     unit = self._units[0]
@@ -233,37 +256,45 @@
                 freq = float(value[0])
                 unit = value[1]
             else:
-                raise ValueError, "Can't interpret %s as a %s"%(repr(value), self._name)
-            assert unit in self._units, "Unit is not correct (%s)"%repr(unit)
-            return "%s%s"%(freq, unit)
-        return "" # XXX is it correct?
+                raise ValueError("Can't interpret %r as a %s" % (value, self._name))
+            assert unit in self._units, "Unit is not correct (%r)" % unit
+            return "%s%s" % (freq, unit)
+        return ""  # XXX is it correct?
+
 
 class FrequencyValue(FloatUnitValue):
-    _units = ['Hz','kHz','mHz',]
+    _units = ['Hz', 'kHz', 'mHz',]
     _name = "frequency"
 
+
 class VoltageValue(FloatUnitValue):
-    _units = ['V','mV',]
+    _units = ['V', 'mV',]
     _name = "voltage"
-    
+
+
 class DurationValue(FloatUnitValue):
-    _units = ['sec','msec','usec','min']
+    _units = ['sec', 'msec', 'usec', 'min']
     _name = "duration"
 
+
 class StringValue(AbstractValue):
     _type = str
+
     def convert_to(self, *value):
         if value:
-            return "'%s'"%str(value[0])
+            return "'%s'" % str(value[0])
         return "''"
 
+
 class EnumValue(StringValue):
-    _values = []
+    _values = ()
+
     def convert_to(self, *value):
         if value:
             assert value[0] in self._values
-            return "%s"%self._values.index(value[0])
-        return "" # XXX
+            return "%s" % self._values.index(value[0])
+        return ""  # XXX
+
 
 class SimpleMode(AbstractCommand):
     """
@@ -286,7 +317,7 @@
 class WriteOnlyMode(SimpleMode):
     def get_value_from_device(self, device):
         raise ValueError("can't retrieve mode value from device")
-        
+
     def __set__(self, instance, value):
         if instance is None:
             return self
@@ -307,7 +338,8 @@
     def get_value_from_device(self, device):
         value = self.get_mode(device)
         return value
-        
+
+
 # TODO
 # class STATUS_BYTE(Constants):
 #     # IEEE 488.2 Status Byte constants
@@ -333,4 +365,3 @@
 #     OPC = 0x01 # Operation Complete: device has completed any pending
 #                # operation (ready to accept new commands). This bit is
 #                # generated in response to a OPC command.
-    
--- a/pygpibtoolkit/tools.py	Thu Apr 02 16:58:24 2009 +0200
+++ b/pygpibtoolkit/tools.py	Tue May 01 00:10:23 2018 +0200
@@ -10,62 +10,68 @@
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE).
-http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr
+""" Copyright (c) 2007-2018 David Douard (Paris, FRANCE).
+http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org
 
 Helper functions and classes
 """
 import re
 
-def str_num_cmp(s1,s2):
+
+def cmp(a, b):
+    return (a > b) - (a < b)
+
+
+def str_num_cmp(s1, s2):
     """
     string comparator function that will put 'toto_10' after 'toto_2'
     Also works for strings like 'toto_1_et_23er25'.
     """
     r = re.compile(r'((?<=\d)\D+|(?<=\D)\d+)')
-    r1= r.split(s1)
-    r2= r.split(s2)
-    r1=[not x.isdigit() and x or int(x) for x in r1 if x!='']
-    r2=[not x.isdigit() and x or int(x) for x in r2 if x!='']
-    return cmp(r1,r2)
+    r1 = r.split(s1)
+    r2 = r.split(s2)
+    r1 = [not x.isdigit() and x or int(x) for x in r1 if x]
+    r2 = [not x.isdigit() and x or int(x) for x in r2 if x]
+    return cmp(r1, r2)
 
 
-class AbstractRegister(object):
+class AbstractRegister:
     _instance = None
     _registered_type = None
+
     def __new__(cls):
         # implements a singleton
         if cls._instance is None:
-            #debug('Instanciating %s', cls.__name__)
+            # debug('Instanciating %s', cls.__name__)
             cls._instance = super(AbstractRegister, cls).__new__(cls)
             cls._instance.registered = {}
             cls._instance.accepts = set()
         return cls._instance
-    
+
     def add(self, cls):
         assert issubclass(cls, self._registered_type)
         if cls is self._registered_type:
             return
         if cls._accepts is None:
             return
-        #assert isinstance(cls._accepts, (basestring, tuple))
-        
-        #debug("Registerered %s for %s", cls.__name__)
+        # assert isinstance(cls._accepts, (basestring, tuple))
+
+        # debug("Registerered %s for %s", cls.__name__)
         if not isinstance(cls._accepts, tuple):
             cls._accepts = (cls._accepts,)
         for key in cls._accepts:
             key = self._get_typ(key)
-            #debug("  new key = %s", key)            
+            # debug("  new key = %s", key)
             self.registered.setdefault(key, []).append(cls)
             self.accepts.add(key)
 
     def _get_typ(self, typ):
-        if not isinstance(typ, basestring):
+        if not isinstance(typ, str):
             if not isinstance(typ, type):
                 return None
             typ = typ.__name__
         return typ
-    
+
     def __contains__(self, val):
         val = self._get_typ(val)
         return val in self.accepts
@@ -75,14 +81,14 @@
         if not isinstance(typ, type):
             typ = typ.__class__
         name = typ.__name__
-        #debug("Looking a widget for %s", typ.__name__)
+        # debug("Looking a widget for %s", typ.__name__)
         orig_typ = typ
         while typ is not None:
             if typ.__name__ in self.registered:
                 for w in self.registered[typ.__name__]:
                     if w._filter is None or w._filter(item):
-                        #debug("Widget for %s is %s", typ.__name__, w)
-                        return  w#self.registered[typ.__name__]
+                        # debug("Widget for %s is %s", typ.__name__, w)
+                        return w  # self.registered[typ.__name__]
             if typ.__bases__:
                 typ = typ.__bases__[0]
                 if typ == object:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/setup.py	Tue May 01 00:10:23 2018 +0200
@@ -0,0 +1,17 @@
+#
+from setuptools import setup, find_packages
+
+setup(name="pygpibtoolkit",
+      author='David Douard',
+      packages=find_packages(),
+      version='0.1.0',
+      install_requires=[
+          'pyserial', 'numpy', 'matplotlib'],
+      entry_points={'console_scripts': [
+          'pygpib-detect=pygpibtoolkit.detect:main',
+          'hp3562-coord=pygpibtoolkit.HP3562A.coord_decoder:main',
+          'hp3562-state=pygpibtoolkit.HP3562A.state_decoder:main',
+          'hp3562-trace=pygpibtoolkit.HP3562A.trace_decoder:main',
+          'hp3562-dump=pygpibtoolkit.HP3562A.dump_datablock:main',
+          ]}
+      )

mercurial