From f0a316852f7b73d3c1c14b74e56f7f8833444ee7 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Mon, 20 Jun 2016 16:29:44 -0700 Subject: [PATCH 01/79] initial commit of DPO2000 & MSO2000 support. --- ivi/tektronix/__init__.py | 22 +++++++ ivi/tektronix/tektronixBaseScope.py | 17 +++--- ivi/tektronix/tektronixDPO2000.py | 46 +++++++++++++++ ivi/tektronix/tektronixDPO2002B.py | 42 +++++++++++++ ivi/tektronix/tektronixDPO2004B.py | 42 +++++++++++++ ivi/tektronix/tektronixDPO2012.py | 42 +++++++++++++ ivi/tektronix/tektronixDPO2012B.py | 42 +++++++++++++ ivi/tektronix/tektronixDPO2014.py | 42 +++++++++++++ ivi/tektronix/tektronixDPO2014B.py | 42 +++++++++++++ ivi/tektronix/tektronixDPO2022B.py | 42 +++++++++++++ ivi/tektronix/tektronixDPO2024.py | 42 +++++++++++++ ivi/tektronix/tektronixDPO2024B.py | 42 +++++++++++++ ivi/tektronix/tektronixMSO2000.py | 92 +++++++++++++++++++++++++++++ ivi/tektronix/tektronixMSO2002B.py | 42 +++++++++++++ ivi/tektronix/tektronixMSO2004B.py | 43 ++++++++++++++ ivi/tektronix/tektronixMSO2012.py | 43 ++++++++++++++ ivi/tektronix/tektronixMSO2012B.py | 43 ++++++++++++++ ivi/tektronix/tektronixMSO2014.py | 43 ++++++++++++++ ivi/tektronix/tektronixMSO2014B.py | 43 ++++++++++++++ ivi/tektronix/tektronixMSO2022B.py | 43 ++++++++++++++ ivi/tektronix/tektronixMSO2024.py | 43 ++++++++++++++ ivi/tektronix/tektronixMSO2024B.py | 43 ++++++++++++++ 22 files changed, 934 insertions(+), 7 deletions(-) create mode 100644 ivi/tektronix/tektronixDPO2000.py create mode 100644 ivi/tektronix/tektronixDPO2002B.py create mode 100644 ivi/tektronix/tektronixDPO2004B.py create mode 100644 ivi/tektronix/tektronixDPO2012.py create mode 100644 ivi/tektronix/tektronixDPO2012B.py create mode 100644 ivi/tektronix/tektronixDPO2014.py create mode 100644 ivi/tektronix/tektronixDPO2014B.py create mode 100644 ivi/tektronix/tektronixDPO2022B.py create mode 100644 ivi/tektronix/tektronixDPO2024.py create mode 100644 ivi/tektronix/tektronixDPO2024B.py create mode 100644 ivi/tektronix/tektronixMSO2000.py create mode 100644 ivi/tektronix/tektronixMSO2002B.py create mode 100644 ivi/tektronix/tektronixMSO2004B.py create mode 100644 ivi/tektronix/tektronixMSO2012.py create mode 100644 ivi/tektronix/tektronixMSO2012B.py create mode 100644 ivi/tektronix/tektronixMSO2014.py create mode 100644 ivi/tektronix/tektronixMSO2014B.py create mode 100644 ivi/tektronix/tektronixMSO2022B.py create mode 100644 ivi/tektronix/tektronixMSO2024.py create mode 100644 ivi/tektronix/tektronixMSO2024B.py diff --git a/ivi/tektronix/__init__.py b/ivi/tektronix/__init__.py index adde0c94..808f013f 100644 --- a/ivi/tektronix/__init__.py +++ b/ivi/tektronix/__init__.py @@ -25,6 +25,16 @@ """ # Oscilloscopes +# DPO2000 +from .tektronixDPO2024 import tektronixDPO2024 +from .tektronixDPO2014 import tektronixDPO2014 +from .tektronixDPO2012 import tektronixDPO2012 +# DPO2000B +from .tektronixDPO2024B import tektronixDPO2024B +from .tektronixDPO2012B import tektronixDPO2012B +from .tektronixDPO2022B import tektronixDPO2022B +from .tektronixDPO2004B import tektronixDPO2004B +from .tektronixDPO2002B import tektronixDPO2002B # DPO4000 from .tektronixDPO4032 import tektronixDPO4032 from .tektronixDPO4034 import tektronixDPO4034 @@ -66,6 +76,18 @@ from .tektronixMDO3054 import tektronixMDO3054 from .tektronixMDO3102 import tektronixMDO3102 from .tektronixMDO3104 import tektronixMDO3104 +# MSO2000 +from .tektronixMSO2012 import tektronixMSO2012 +from .tektronixMSO2014 import tektronixMSO2014 +from .tektronixMSO2024 import tektronixMSO2024 +# MSO2000B +from .tektronixMSO2002B import tektronixMSO2002B +from .tektronixMSO2004B import tektronixMSO2004B +from .tektronixMSO2012B import tektronixMSO2012B +from .tektronixMSO2014B import tektronixMSO2014B +from .tektronixMSO2022B import tektronixMSO2022B +from .tektronixMSO2024B import tektronixMSO2024B + # Function Generators from .tektronixAWG2005 import tektronixAWG2005 diff --git a/ivi/tektronix/tektronixBaseScope.py b/ivi/tektronix/tektronixBaseScope.py index 7e64c98c..c4b490a2 100644 --- a/ivi/tektronix/tektronixBaseScope.py +++ b/ivi/tektronix/tektronixBaseScope.py @@ -224,13 +224,16 @@ def __init__(self, *args, **kwargs): self._identity_instrument_firmware_revision = "" self._identity_specification_major_version = 4 self._identity_specification_minor_version = 1 - self._identity_supported_instrument_models = ['DPO4032', 'DPO4034', 'DPO4054', - 'DPO4104', 'DPO4014B', 'DPO4034B', 'DPO4054B', 'DPO4102B', 'DPO4104B', - 'MSO4032', 'MSO4034', 'MSO4054', 'MSO4104', 'MSO4014B', 'MSO4034B', - 'MSO4054B', 'MSO4102B', 'MSO4104B', 'MDO4054', 'MDO4104', 'MDO4014B', - 'MDO4034B', 'MDO4054B', 'MDO4104B', 'MDO3012', 'MDO3014', 'MDO3022', - 'MDO3024', 'MDO3032', 'MDO3034', 'MDO3052', 'MDO3054', 'MDO3102', - 'MDO3104'] + self._identity_supported_instrument_models = ['DPO2002B', 'DPO2004B', 'DPO2012', + 'DPO2012B', 'DPO2014', 'DPO2014B', 'DPO2022B', 'DPO2024', + 'DPO2024B', 'DPO4032', 'DPO4034', 'DPO4054', 'DPO4104', 'DPO4014B', + 'DPO4034B', 'DPO4054B', 'DPO4102B', 'DPO4104B', 'MSO4032', 'MSO4034', + 'MSO4054', 'MSO4104', 'MSO4014B', 'MSO4034B','MSO4054B', 'MSO4102B', + 'MSO4104B', 'MDO4054', 'MDO4104', 'MDO4014B', 'MDO4034B', 'MDO4054B', + 'MDO4104B', 'MDO3012', 'MDO3014', 'MDO3022', 'MDO3024', 'MDO3032', + 'MDO3034', 'MDO3052', 'MDO3054', 'MDO3102', 'MDO3104', 'MSO2002B', + 'MSO2004B', 'MSO2012', 'MSO2012B', 'MSO2014''MSO2014B', 'MSO2022B', + 'MSO2024', 'MSO2024B'] self._add_property('channels[].invert', self._get_channel_invert, diff --git a/ivi/tektronix/tektronixDPO2000.py b/ivi/tektronix/tektronixDPO2000.py new file mode 100644 index 00000000..3a046b0c --- /dev/null +++ b/ivi/tektronix/tektronixDPO2000.py @@ -0,0 +1,46 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixBaseScope import * + +class tektronixDPO2000(tektronixBaseScope): + "Tektronix DPO2000 series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO2000') + + super(tektronixDPO2000, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 200e6 + + self._identity_description = "Tektronix DPO2000 series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DPO2024', 'DPO2014', 'DPO2012', + 'DPO2024B', 'DPO2014B', 'DPO2012B', 'DPO2022B', 'DPO2004B', 'DPO2002B'] + + self._init_channels() diff --git a/ivi/tektronix/tektronixDPO2002B.py b/ivi/tektronix/tektronixDPO2002B.py new file mode 100644 index 00000000..eaf5daef --- /dev/null +++ b/ivi/tektronix/tektronixDPO2002B.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixDPO2000 import * + +class tektronixDPO2002B(tektronixDPO2000): + "Tektronix DPO2002B IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO2002B') + + super(tektronixDPO2002B, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 70e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixDPO2004B.py b/ivi/tektronix/tektronixDPO2004B.py new file mode 100644 index 00000000..f3eb9a8d --- /dev/null +++ b/ivi/tektronix/tektronixDPO2004B.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixDPO2000 import * + +class tektronixDPO2004B(tektronixDPO2000): + "Tektronix DPO2004B IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO2004B') + + super(tektronixDPO2004B, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 70e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixDPO2012.py b/ivi/tektronix/tektronixDPO2012.py new file mode 100644 index 00000000..e641fa41 --- /dev/null +++ b/ivi/tektronix/tektronixDPO2012.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixDPO2000 import * + +class tektronixDPO2012(tektronixDPO2000): + "Tektronix DPO2012 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO2012') + + super(tektronixDPO2012, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixDPO2012B.py b/ivi/tektronix/tektronixDPO2012B.py new file mode 100644 index 00000000..609c79b6 --- /dev/null +++ b/ivi/tektronix/tektronixDPO2012B.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixDPO2000 import * + +class tektronixDPO2012B(tektronixDPO2000): + "Tektronix DPO2012B IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO2012B') + + super(tektronixDPO2012B, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixDPO2014.py b/ivi/tektronix/tektronixDPO2014.py new file mode 100644 index 00000000..22402206 --- /dev/null +++ b/ivi/tektronix/tektronixDPO2014.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixDPO2000 import * + +class tektronixDPO2014(tektronixDPO2000): + "Tektronix DPO2014 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO2014') + + super(tektronixDPO2014, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixDPO2014B.py b/ivi/tektronix/tektronixDPO2014B.py new file mode 100644 index 00000000..d37361ab --- /dev/null +++ b/ivi/tektronix/tektronixDPO2014B.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixDPO2000 import * + +class tektronixDPO2014B(tektronixDPO2000): + "Tektronix DPO2014B IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO2014B') + + super(tektronixDPO2014B, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixDPO2022B.py b/ivi/tektronix/tektronixDPO2022B.py new file mode 100644 index 00000000..b2e14959 --- /dev/null +++ b/ivi/tektronix/tektronixDPO2022B.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixDPO2000 import * + +class tektronixDPO2022B(tektronixDPO2000): + "Tektronix DPO2022B IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO2022B') + + super(tektronixDPO2022B, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 200e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixDPO2024.py b/ivi/tektronix/tektronixDPO2024.py new file mode 100644 index 00000000..ed42e462 --- /dev/null +++ b/ivi/tektronix/tektronixDPO2024.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixDPO2000 import * + +class tektronixDPO2024(tektronixDPO2000): + "Tektronix DPO2024 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO2024') + + super(tektronixDPO2024, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 200e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixDPO2024B.py b/ivi/tektronix/tektronixDPO2024B.py new file mode 100644 index 00000000..040d4547 --- /dev/null +++ b/ivi/tektronix/tektronixDPO2024B.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixDPO2000 import * + +class tektronixDPO2024B(tektronixDPO2000): + "Tektronix DPO2024B IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO2024B') + + super(tektronixDPO2024B, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 200e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixMSO2000.py b/ivi/tektronix/tektronixMSO2000.py new file mode 100644 index 00000000..cdd16dd3 --- /dev/null +++ b/ivi/tektronix/tektronixMSO2000.py @@ -0,0 +1,92 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixDPO2000 import * + +class tektronixMSO2000(tektronixDPO2000): + "Tektronix MSO2000 series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO2000') + + super(tektronixMSO2000, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 1e9 + + self._identity_description = "Tektronix MSO2000 series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['MSO2024', 'MSO2014', 'MSO2012', + 'MSO2024B', 'MSO2022B', 'MSO2014B', 'MSO2012B', 'MSO2004B', 'MSO2002B'] + self._init_channels() + + def _measurement_fetch_waveform(self, index): + index = ivi.get_index(self._channel_name, index) + + if self._driver_operation_simulate: + return list() + + self._write(":wfmoutpre:encdg binary") + if sys.byteorder == 'little': + self._write(":wfmoutpre:byt_or lsb") + else: + self._write(":wfmoutpre:byt_or msb") + self._write(":wfmoutpre:byt_nr 2") + self._write(":wfmoutpre:bn_fmt rp") + self._write(":wfmoutpre:pt_fmt y") + self._write(":wfmoutpre:domain time") + self._write(":data:source %s" % self._channel_name[index]) + self._write(":data:start 1") + self._write(":data:stop 1e10") + + # Read preamble + + pre = self._ask(":wfmoutpre?").split(';') + + format = pre[7].strip() + points = int(pre[6]) + xincr = float(pre[9]) + xzero = float(pre[10]) + ymult = float(pre[13]) + yoff = float(pre[14]) + yzero = int(float(pre[15])) + + if type == 1: + raise scope.InvalidAcquisitionTypeException() + + if format != 'Y': + raise UnexpectedResponseException() + + # Read waveform data + raw_data = self._ask_for_ieee_block(":curve?") + + # Split out points and convert to time and voltage pairs + y_data = array.array('H', raw_data) + + data = [((i * xincr) + xzero, ((y - yoff) * ymult) + yzero) for i, y in enumerate(y_data)] + + return data diff --git a/ivi/tektronix/tektronixMSO2002B.py b/ivi/tektronix/tektronixMSO2002B.py new file mode 100644 index 00000000..28bd0d4b --- /dev/null +++ b/ivi/tektronix/tektronixMSO2002B.py @@ -0,0 +1,42 @@ +1 +n Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + + +from .tektronixMSO2000 import * + +class tektronixMSO2002B(tektronixMSO2000): + "Tektronix MSO2002B IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO2002B') + + super(tektronixMSO2002B, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 70e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixMSO2004B.py b/ivi/tektronix/tektronixMSO2004B.py new file mode 100644 index 00000000..0f09b223 --- /dev/null +++ b/ivi/tektronix/tektronixMSO2004B.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + + +from .tektronixMSO2000 import * + +class tektronixMSO2004B(tektronixMSO2000): + "Tektronix MSO2004B IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO2004B') + + super(tektronixMSO2004B, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 70e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixMSO2012.py b/ivi/tektronix/tektronixMSO2012.py new file mode 100644 index 00000000..163db65b --- /dev/null +++ b/ivi/tektronix/tektronixMSO2012.py @@ -0,0 +1,43 @@ +from .tektronixMSO2024 import tektronixMSO2024 +n Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + + +from .tektronixMSO2000 import * + +class tektronixMSO2012(tektronixMSO2000): + "Tektronix MSO2012 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO2012') + + super(tektronixMSO2012, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + + self._init_channels() + diff --git a/ivi/tektronix/tektronixMSO2012B.py b/ivi/tektronix/tektronixMSO2012B.py new file mode 100644 index 00000000..05cb3348 --- /dev/null +++ b/ivi/tektronix/tektronixMSO2012B.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + + +from .tektronixMSO2000 import * + +class tektronixMSO2012B(tektronixMSO2000): + "Tektronix MSO2012B IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO2012B') + + super(tektronixMSO2012B, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixMSO2014.py b/ivi/tektronix/tektronixMSO2014.py new file mode 100644 index 00000000..985e9793 --- /dev/null +++ b/ivi/tektronix/tektronixMSO2014.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + + +from .tektronixMSO2000 import * + +class tektronixMSO2014(tektronixMSO2000): + "Tektronix MSO2014 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO2014') + + super(tektronixMSO2014, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixMSO2014B.py b/ivi/tektronix/tektronixMSO2014B.py new file mode 100644 index 00000000..19353f38 --- /dev/null +++ b/ivi/tektronix/tektronixMSO2014B.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + + +from .tektronixMSO2000 import * + +class tektronixMSO2014B(tektronixMSO2000): + "Tektronix MSO2014B IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO2014B') + + super(tektronixMSO2014B, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixMSO2022B.py b/ivi/tektronix/tektronixMSO2022B.py new file mode 100644 index 00000000..a58a998b --- /dev/null +++ b/ivi/tektronix/tektronixMSO2022B.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + + +from .tektronixMSO2000 import * + +class tektronixMSO2022B(tektronixMSO2000): + "Tektronix MSO2022B IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO2022B') + + super(tektronixMSO2022B, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 200e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixMSO2024.py b/ivi/tektronix/tektronixMSO2024.py new file mode 100644 index 00000000..c3c6bc0d --- /dev/null +++ b/ivi/tektronix/tektronixMSO2024.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + + +from .tektronixMSO2000 import * + +class tektronixMSO2024(tektronixMSO2000): + "Tektronix MSO2014 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO2024') + + super(tektronixMSO2024, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 200e6 + + self._init_channels() diff --git a/ivi/tektronix/tektronixMSO2024B.py b/ivi/tektronix/tektronixMSO2024B.py new file mode 100644 index 00000000..3bd855aa --- /dev/null +++ b/ivi/tektronix/tektronixMSO2024B.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + + +from .tektronixMSO2000 import * + +class tektronixMSO2024B(tektronixMSO2000): + "Tektronix MSO2024B IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO2024B') + + super(tektronixMSO2024B, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 200e6 + + self._init_channels() From 33724b258a6e30f56bbc4a747bbe0b9be287d97c Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Mon, 20 Jun 2016 16:33:58 -0700 Subject: [PATCH 02/79] Added MSO2014B --- ivi/tektronix/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi/tektronix/__init__.py b/ivi/tektronix/__init__.py index 808f013f..049d2e32 100644 --- a/ivi/tektronix/__init__.py +++ b/ivi/tektronix/__init__.py @@ -31,8 +31,8 @@ from .tektronixDPO2012 import tektronixDPO2012 # DPO2000B from .tektronixDPO2024B import tektronixDPO2024B +from .tektronixDPO2014B import tektronixDPO2014B from .tektronixDPO2012B import tektronixDPO2012B -from .tektronixDPO2022B import tektronixDPO2022B from .tektronixDPO2004B import tektronixDPO2004B from .tektronixDPO2002B import tektronixDPO2002B # DPO4000 From ab06942ab16131e9dfa7c18ebb5c9357c0b9904a Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Mon, 20 Jun 2016 16:38:23 -0700 Subject: [PATCH 03/79] Fixed minor syntax error --- ivi/tektronix/tektronixMSO2002B.py | 5 +++-- ivi/tektronix/tektronixMSO2012.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ivi/tektronix/tektronixMSO2002B.py b/ivi/tektronix/tektronixMSO2002B.py index 28bd0d4b..7e6c47eb 100644 --- a/ivi/tektronix/tektronixMSO2002B.py +++ b/ivi/tektronix/tektronixMSO2002B.py @@ -1,5 +1,6 @@ -1 -n Interchangeable Virtual Instrument Library +""" + +Python Interchangeable Virtual Instrument Library Copyright (c) 2016 Alex Forencich diff --git a/ivi/tektronix/tektronixMSO2012.py b/ivi/tektronix/tektronixMSO2012.py index 163db65b..f4705c56 100644 --- a/ivi/tektronix/tektronixMSO2012.py +++ b/ivi/tektronix/tektronixMSO2012.py @@ -1,5 +1,6 @@ -from .tektronixMSO2024 import tektronixMSO2024 -n Interchangeable Virtual Instrument Library +""" + +Python Interchangeable Virtual Instrument Library Copyright (c) 2016 Alex Forencich From ffe90633d3cd2a3ad77e9965cfe739bce304400c Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Tue, 28 Jun 2016 16:18:56 -0700 Subject: [PATCH 04/79] updated trigger source conditional to support 'edg' case. --- ivi/tektronix/tektronixBaseScope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi/tektronix/tektronixBaseScope.py b/ivi/tektronix/tektronixBaseScope.py index c4b490a2..12d30d1f 100644 --- a/ivi/tektronix/tektronixBaseScope.py +++ b/ivi/tektronix/tektronixBaseScope.py @@ -936,7 +936,7 @@ def _set_trigger_edge_slope(self, value): def _get_trigger_source(self): if not self._driver_operation_simulate and not self._get_cache_valid(): t = self._ask(":trigger:a:type?").lower() - if t == 'edge': + if t == 'edge' or t == 'edg': value = self._ask(":trigger:a:edge:source?").lower() elif t == 'logic': # TODO From f1bcff8ec45948185ace09e77bec7f8ec4a35fb7 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 29 Jun 2016 10:08:50 -0700 Subject: [PATCH 05/79] Updated MSO2000 to support model specific acquisition type and slope mappings. --- ivi/tektronix/tektronixMSO2000.py | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/ivi/tektronix/tektronixMSO2000.py b/ivi/tektronix/tektronixMSO2000.py index cdd16dd3..8a87976f 100644 --- a/ivi/tektronix/tektronixMSO2000.py +++ b/ivi/tektronix/tektronixMSO2000.py @@ -29,6 +29,16 @@ class tektronixMSO2000(tektronixDPO2000): "Tektronix MSO2000 series IVI oscilloscope driver" + AcquisitionTypeMapping = { + 'normal': 'sam', + 'average': 'ave', + } + + SlopeMapping = { + 'positive': 'ris', + 'negative': 'fall', + 'either': 'either'} + def __init__(self, *args, **kwargs): self.__dict__.setdefault('_instrument_id', 'MSO2000') @@ -44,6 +54,36 @@ def __init__(self, *args, **kwargs): 'MSO2024B', 'MSO2022B', 'MSO2014B', 'MSO2012B', 'MSO2004B', 'MSO2002B'] self._init_channels() + def _get_acquisition_type(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":acquire:mode?").lower() + self._acquisition_type = [k for k,v in AcquisitionTypeMapping.items() if v==value][0] + self._set_cache_valid() + return self._acquisition_type + + def _set_acquisition_type(self, value): + if value not in AcquisitionTypeMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":acquire:mode %s" % AcquisitionTypeMapping[value]) + self._acquisition_type = value + self._set_cache_valid() + + def _get_trigger_edge_slope(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:a:edge:slope?").lower() + self._trigger_edge_slope = [k for k,v in SlopeMapping.items() if v==value][0] + self._set_cache_valid() + return self._trigger_edge_slope + + def _set_trigger_edge_slope(self, value): + if value not in SlopeMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":trigger:a:edge:slope %s" % SlopeMapping[value]) + self._trigger_edge_slope = value + self._set_cache_valid() + def _measurement_fetch_waveform(self, index): index = ivi.get_index(self._channel_name, index) From 2bbd94bd78656a38536bc01e3baec48f3ee4dd1a Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 29 Jun 2016 10:27:33 -0700 Subject: [PATCH 06/79] Made acq type and slope map a class variable. --- ivi/tektronix/tektronixMSO2000.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/ivi/tektronix/tektronixMSO2000.py b/ivi/tektronix/tektronixMSO2000.py index 8a87976f..9aecdb3e 100644 --- a/ivi/tektronix/tektronixMSO2000.py +++ b/ivi/tektronix/tektronixMSO2000.py @@ -29,15 +29,7 @@ class tektronixMSO2000(tektronixDPO2000): "Tektronix MSO2000 series IVI oscilloscope driver" - AcquisitionTypeMapping = { - 'normal': 'sam', - 'average': 'ave', - } - SlopeMapping = { - 'positive': 'ris', - 'negative': 'fall', - 'either': 'either'} def __init__(self, *args, **kwargs): self.__dict__.setdefault('_instrument_id', 'MSO2000') @@ -49,6 +41,16 @@ def __init__(self, *args, **kwargs): self._channel_count = self._analog_channel_count + self._digital_channel_count self._bandwidth = 1e9 + self.AcquisitionTypeMapping = { + 'normal': 'sam', + 'average': 'ave', + } + + self.SlopeMapping = { + 'positive': 'ris', + 'negative': 'fall', + 'either': 'either'} + self._identity_description = "Tektronix MSO2000 series IVI oscilloscope driver" self._identity_supported_instrument_models = ['MSO2024', 'MSO2014', 'MSO2012', 'MSO2024B', 'MSO2022B', 'MSO2014B', 'MSO2012B', 'MSO2004B', 'MSO2002B'] From 88275a49158e2b7db53f86e3026bb21018d14c65 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 29 Jun 2016 10:37:54 -0700 Subject: [PATCH 07/79] updated variable --- ivi/tektronix/tektronixMSO2000.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/ivi/tektronix/tektronixMSO2000.py b/ivi/tektronix/tektronixMSO2000.py index 9aecdb3e..fd7e8294 100644 --- a/ivi/tektronix/tektronixMSO2000.py +++ b/ivi/tektronix/tektronixMSO2000.py @@ -26,6 +26,17 @@ from .tektronixDPO2000 import * +AcquisitionTypeMapping = { + 'normal': 'sam', + 'average': 'ave', + } + +SlopeMapping = { + 'positive': 'ris', + 'negative': 'fall', + 'either': 'either' + } + class tektronixMSO2000(tektronixDPO2000): "Tektronix MSO2000 series IVI oscilloscope driver" @@ -41,16 +52,6 @@ def __init__(self, *args, **kwargs): self._channel_count = self._analog_channel_count + self._digital_channel_count self._bandwidth = 1e9 - self.AcquisitionTypeMapping = { - 'normal': 'sam', - 'average': 'ave', - } - - self.SlopeMapping = { - 'positive': 'ris', - 'negative': 'fall', - 'either': 'either'} - self._identity_description = "Tektronix MSO2000 series IVI oscilloscope driver" self._identity_supported_instrument_models = ['MSO2024', 'MSO2014', 'MSO2012', 'MSO2024B', 'MSO2022B', 'MSO2014B', 'MSO2012B', 'MSO2004B', 'MSO2002B'] From 9e921cadef49099ac8b0add9b08c0f8effe706aa Mon Sep 17 00:00:00 2001 From: Tommy Slater Date: Wed, 13 Jul 2016 12:23:29 -0700 Subject: [PATCH 08/79] adds _trigger_value_map to class for mapping trigger values like 'rise' and 'ris' to 'positive' to fix issues with values coming from the instrument --- ivi/tektronix/tektronixMSO2000.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ivi/tektronix/tektronixMSO2000.py b/ivi/tektronix/tektronixMSO2000.py index fd7e8294..74162cb2 100644 --- a/ivi/tektronix/tektronixMSO2000.py +++ b/ivi/tektronix/tektronixMSO2000.py @@ -56,6 +56,12 @@ def __init__(self, *args, **kwargs): self._identity_supported_instrument_models = ['MSO2024', 'MSO2014', 'MSO2012', 'MSO2024B', 'MSO2022B', 'MSO2014B', 'MSO2012B', 'MSO2004B', 'MSO2002B'] self._init_channels() + self._trigger_value_map = { + 'rise': 'positive', + 'ris': 'positive', + 'fall': 'negative', + 'either': 'either' + } def _get_acquisition_type(self): if not self._driver_operation_simulate and not self._get_cache_valid(): @@ -75,7 +81,7 @@ def _set_acquisition_type(self, value): def _get_trigger_edge_slope(self): if not self._driver_operation_simulate and not self._get_cache_valid(): value = self._ask(":trigger:a:edge:slope?").lower() - self._trigger_edge_slope = [k for k,v in SlopeMapping.items() if v==value][0] + self._trigger_edge_slope = self._trigger_value_map[value] self._set_cache_valid() return self._trigger_edge_slope From 1d10e6b3493e3b587d7c83d79c52a3e261f94a9e Mon Sep 17 00:00:00 2001 From: Tommy Slater Date: Wed, 13 Jul 2016 14:53:44 -0700 Subject: [PATCH 09/79] adds acq mapping, changes naming convention of class set_maps and get_maps for trigger and acq, removes old global variables --- ivi/tektronix/tektronixMSO2000.py | 43 +++++++++++++++++-------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/ivi/tektronix/tektronixMSO2000.py b/ivi/tektronix/tektronixMSO2000.py index 74162cb2..71edab10 100644 --- a/ivi/tektronix/tektronixMSO2000.py +++ b/ivi/tektronix/tektronixMSO2000.py @@ -26,16 +26,6 @@ from .tektronixDPO2000 import * -AcquisitionTypeMapping = { - 'normal': 'sam', - 'average': 'ave', - } - -SlopeMapping = { - 'positive': 'ris', - 'negative': 'fall', - 'either': 'either' - } class tektronixMSO2000(tektronixDPO2000): "Tektronix MSO2000 series IVI oscilloscope driver" @@ -56,41 +46,56 @@ def __init__(self, *args, **kwargs): self._identity_supported_instrument_models = ['MSO2024', 'MSO2014', 'MSO2012', 'MSO2024B', 'MSO2022B', 'MSO2014B', 'MSO2012B', 'MSO2004B', 'MSO2002B'] self._init_channels() - self._trigger_value_map = { + self._trigger_set_map = { + 'positive': 'ris', + 'negative': 'fall', + 'either': 'either' + } + self._trigger_get_map = { 'rise': 'positive', 'ris': 'positive', 'fall': 'negative', 'either': 'either' } + self._acq_set_map = { + 'normal': 'sam', + 'average': 'ave', + } + self._acq_get_map = { + 'sam': 'normal', + 'sample': 'normal', + 'ave': 'average', + 'average': 'average', + } def _get_acquisition_type(self): if not self._driver_operation_simulate and not self._get_cache_valid(): value = self._ask(":acquire:mode?").lower() - self._acquisition_type = [k for k,v in AcquisitionTypeMapping.items() if v==value][0] + self._acquisition_type = self._acq_get_map[value] self._set_cache_valid() return self._acquisition_type def _set_acquisition_type(self, value): - if value not in AcquisitionTypeMapping: + if value not in self._acq_set_map: raise ivi.ValueNotSupportedException() if not self._driver_operation_simulate: - self._write(":acquire:mode %s" % AcquisitionTypeMapping[value]) + self._write(":acquire:mode %s" % self._acq_set_map[value]) self._acquisition_type = value self._set_cache_valid() def _get_trigger_edge_slope(self): if not self._driver_operation_simulate and not self._get_cache_valid(): value = self._ask(":trigger:a:edge:slope?").lower() - self._trigger_edge_slope = self._trigger_value_map[value] + self._trigger_edge_slope = self._trigger_get_map[value] self._set_cache_valid() return self._trigger_edge_slope - def _set_trigger_edge_slope(self, value): - if value not in SlopeMapping: + def _set_trigger_edge_slope(self, slope): + if slope not in self._trigger_set_map: raise ivi.ValueNotSupportedException() if not self._driver_operation_simulate: - self._write(":trigger:a:edge:slope %s" % SlopeMapping[value]) - self._trigger_edge_slope = value + self._write(":trigger:a:edge:slope %s" % self._trigger_set_map[slope]) + self._trigger_edge_slope = slope self._set_cache_valid() def _measurement_fetch_waveform(self, index): From fb0ac0db9e44cbcb05691ef484fbef6bf565345f Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 17 Aug 2016 17:19:53 +0200 Subject: [PATCH 10/79] Updated vertical divisions to 8 --- ivi/tektronix/tektronixMSO2024.py | 1 + ivi/tektronix/tektronixMSO2024B.py | 1 + 2 files changed, 2 insertions(+) diff --git a/ivi/tektronix/tektronixMSO2024.py b/ivi/tektronix/tektronixMSO2024.py index c3c6bc0d..dc8b29fc 100644 --- a/ivi/tektronix/tektronixMSO2024.py +++ b/ivi/tektronix/tektronixMSO2024.py @@ -39,5 +39,6 @@ def __init__(self, *args, **kwargs): self._digital_channel_count = 16 self._channel_count = self._analog_channel_count + self._digital_channel_count self._bandwidth = 200e6 + self._vertical_divisions = 8 self._init_channels() diff --git a/ivi/tektronix/tektronixMSO2024B.py b/ivi/tektronix/tektronixMSO2024B.py index 3bd855aa..7128dce6 100644 --- a/ivi/tektronix/tektronixMSO2024B.py +++ b/ivi/tektronix/tektronixMSO2024B.py @@ -39,5 +39,6 @@ def __init__(self, *args, **kwargs): self._digital_channel_count = 16 self._channel_count = self._analog_channel_count + self._digital_channel_count self._bandwidth = 200e6 + self._vertical_divisions = 8 self._init_channels() From 9688ae820fd30b07d6386b8f1adc419e097958ae Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Mon, 7 Nov 2016 12:12:18 -0800 Subject: [PATCH 11/79] Initial commit of Tektronix DPO7034C support --- ivi/tektronix/__init__.py | 2 ++ ivi/tektronix/tektronixBaseScope.py | 2 +- ivi/tektronix/tektronixDPO7000.py | 45 +++++++++++++++++++++++++++++ ivi/tektronix/tektronixDPO7034C.py | 42 +++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 ivi/tektronix/tektronixDPO7000.py create mode 100644 ivi/tektronix/tektronixDPO7034C.py diff --git a/ivi/tektronix/__init__.py b/ivi/tektronix/__init__.py index 049d2e32..f5d069ec 100644 --- a/ivi/tektronix/__init__.py +++ b/ivi/tektronix/__init__.py @@ -51,6 +51,8 @@ from .tektronixDPO4054B import tektronixDPO4054B from .tektronixDPO4102B import tektronixDPO4102B from .tektronixDPO4104B import tektronixDPO4104B +# DPO7000C +from .tektronixDPO7354C import tektronixDPO7354C # MSO4000B from .tektronixMSO4014B import tektronixMSO4014B from .tektronixMSO4034B import tektronixMSO4034B diff --git a/ivi/tektronix/tektronixBaseScope.py b/ivi/tektronix/tektronixBaseScope.py index 0b4491c9..e7381a79 100644 --- a/ivi/tektronix/tektronixBaseScope.py +++ b/ivi/tektronix/tektronixBaseScope.py @@ -233,7 +233,7 @@ def __init__(self, *args, **kwargs): 'MDO4104B', 'MDO3012', 'MDO3014', 'MDO3022', 'MDO3024', 'MDO3032', 'MDO3034', 'MDO3052', 'MDO3054', 'MDO3102', 'MDO3104', 'MSO2002B', 'MSO2004B', 'MSO2012', 'MSO2012B', 'MSO2014''MSO2014B', 'MSO2022B', - 'MSO2024', 'MSO2024B'] + 'MSO2024', 'MSO2024B', 'DPO7354C'] self._add_property('channels[].invert', self._get_channel_invert, diff --git a/ivi/tektronix/tektronixDPO7000.py b/ivi/tektronix/tektronixDPO7000.py new file mode 100644 index 00000000..36c42b4c --- /dev/null +++ b/ivi/tektronix/tektronixDPO7000.py @@ -0,0 +1,45 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixBaseScope import * + +class tektronixDPO7000(tektronixBaseScope): + "Tektronix DPO7000 series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO7000') + + super(tektronixDPO7000, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 3.5e9 + + self._identity_description = "Tektronix DPO7000 series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DPO7354C'] + + self._init_channels() diff --git a/ivi/tektronix/tektronixDPO7034C.py b/ivi/tektronix/tektronixDPO7034C.py new file mode 100644 index 00000000..9b1e1115 --- /dev/null +++ b/ivi/tektronix/tektronixDPO7034C.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixDPO7000 import * + +class tektronixDPO7034C(tektronixDPO7000): + "Tektronix DPO7034C IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO7034C') + + super(tektronixDPO4032, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 3.5e9 + + self._init_channels() From 46702abad965c1c845377460e30dd9f22f2a8745 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Mon, 7 Nov 2016 12:17:24 -0800 Subject: [PATCH 12/79] Fixed typo --- .../{tektronixDPO7034C.py => tektronixDPO7354C.py} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename ivi/tektronix/{tektronixDPO7034C.py => tektronixDPO7354C.py} (90%) diff --git a/ivi/tektronix/tektronixDPO7034C.py b/ivi/tektronix/tektronixDPO7354C.py similarity index 90% rename from ivi/tektronix/tektronixDPO7034C.py rename to ivi/tektronix/tektronixDPO7354C.py index 9b1e1115..acccd615 100644 --- a/ivi/tektronix/tektronixDPO7034C.py +++ b/ivi/tektronix/tektronixDPO7354C.py @@ -26,11 +26,11 @@ from .tektronixDPO7000 import * -class tektronixDPO7034C(tektronixDPO7000): - "Tektronix DPO7034C IVI oscilloscope driver" +class tektronixDPO7354C(tektronixDPO7000): + "Tektronix DPO7354C IVI oscilloscope driver" def __init__(self, *args, **kwargs): - self.__dict__.setdefault('_instrument_id', 'DPO7034C') + self.__dict__.setdefault('_instrument_id', 'DPO7354C') super(tektronixDPO4032, self).__init__(*args, **kwargs) From 84623ed843efa205c099ef6636234040885d9453 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 9 Nov 2016 14:15:57 -0800 Subject: [PATCH 13/79] Initial commit for DSOS804A --- ivi/agilent/__init__.py | 4 +- ivi/agilent/agilentDSOS804A.py | 44 ++++ ivi/agilent/agilents.py | 379 +++++++++++++++++++++++++++++++++ 3 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 ivi/agilent/agilentDSOS804A.py create mode 100644 ivi/agilent/agilents.py diff --git a/ivi/agilent/__init__.py b/ivi/agilent/__init__.py index 83039b19..19c14e7a 100644 --- a/ivi/agilent/__init__.py +++ b/ivi/agilent/__init__.py @@ -153,7 +153,9 @@ from .agilentMSOX92504A import agilentMSOX92504A from .agilentMSOX92804A import agilentMSOX92804A from .agilentMSOX93204A import agilentMSOX93204A - +# Infinium S series +from .agilentDSOS204A import agilentDSOS204A +from .agilentDSOS804A import agilentDSOS804A # Spectrum Analyzers # 859xA series from .agilent8590A import agilent8590A diff --git a/ivi/agilent/agilentDSOS804A.py b/ivi/agilent/agilentDSOS804A.py new file mode 100644 index 00000000..38984995 --- /dev/null +++ b/ivi/agilent/agilentDSOS804A.py @@ -0,0 +1,44 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2012-2014 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .agilents import * + +class agilentDSOS804A(agilents): + "KeySight InfiniiVision DSOS804A IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DSOS804A') + + super(agilentDSOS804A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 8e9 + + self._init_channels() + + \ No newline at end of file diff --git a/ivi/agilent/agilents.py b/ivi/agilent/agilents.py new file mode 100644 index 00000000..619b1bd4 --- /dev/null +++ b/ivi/agilent/agilents.py @@ -0,0 +1,379 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2012-2014 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .agilentBaseInfiniium import * + +AcquisitionModeMapping = { + 'etim': ('normal', 'equivalent_time'), + 'rtim': ('normal', 'real_time'), + 'pdet': ('peak_detect', 'real_time'), + 'hres': ('high_resolution', 'real_time'), + 'segm': ('normal', 'segmented'), + 'segp': ('peak_detect', 'segmented'), + 'segh': ('high_resolution', 'segmented') +} +AcquisitionType = set(['normal', 'peak_detect', 'high_resolution']) +VerticalCoupling = set(['dc']) +ScreenshotImageFormatMapping = { + 'tif': 'tif', + 'tiff': 'tif', + 'bmp': 'bmp', + 'bmp24': 'bmp', + 'png': 'png', + 'png24': 'png', + 'jpg': 'jpg', + 'jpeg': 'jpg', + 'gif': 'gif'} +SampleMode = set(['real_time', 'equivalent_time', 'segmented']) + +class agilents(agilentBaseInfiniium): + "Agilent Infiniium S series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + self._analog_channel_name = list() + self._analog_channel_count = 4 + self._digital_channel_name = list() + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._channel_common_mode = list() + self._channel_differential = list() + self._channel_differential_skew = list() + self._channel_display_auto = list() + self._channel_display_offset = list() + self._channel_display_range = list() + self._channel_display_scale = list() + + super(agilents, self).__init__(*args, **kwargs) + + self._analog_channel_name = list() + self._analog_channel_count = 4 + self._digital_channel_name = list() + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 0.5e9 + + self._horizontal_divisions = 10 + self._vertical_divisions = 8 + + self._display_color_grade = False + + self._identity_description = "KeySight Infiniium S series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DSOS054A','DSOS104A','DSOS204A','DSOS254A','DSOS404A','DSOS604A', + 'DSOS804A','MSOS054A','MSOS104A','MSOS204A','MSOS254A','MSOS404A','MSOS604A','MSOS804A'] + + self._add_property('channels[].common_mode', + self._get_channel_common_mode, + self._set_channel_common_mode, + None, + ivi.Doc(""" + Turns on/off common mode for the channel. Channels 2 and 4 may form a + common mode channel and channels 1 and 3 may form a common mode channel. + """)) + self._add_property('channels[].differential', + self._get_channel_differential, + self._set_channel_differential, + None, + ivi.Doc(""" + Turns on/off differential mode for the channel. Channels 2 and 4 may form + a differential channel and channels 1 and 3 may form a differential + channel. + """)) + self._add_property('channels[].differential_skew', + self._get_channel_differential_skew, + self._set_channel_differential_skew, + None, + ivi.Doc(""" + Specifies the skew that is applied to the differential or common mode pair + of channels. Units are seconds. + """)) + self._add_property('channels[].display_auto', + self._get_channel_display_auto, + self._set_channel_display_auto, + None, + ivi.Doc(""" + Sets the differential and common mode display scale and offset to track + the acquisition scale and offset. + """)) + self._add_property('channels[].display_offset', + self._get_channel_display_offset, + self._set_channel_display_offset, + None, + ivi.Doc(""" + Sets the displayed offset of the selected channel. Setting this parameter + disables display_auto. Units are volts. + """)) + self._add_property('channels[].display_range', + self._get_channel_display_range, + self._set_channel_display_range, + None, + ivi.Doc(""" + Sets the full scale vertical range of the selected channel. Setting this + parameter disables display_auto. Units are volts. + """)) + self._add_property('channels[].display_scale', + self._get_channel_display_scale, + self._set_channel_display_scale, + None, + ivi.Doc(""" + Sets the displayed scale of the selected channel per division. Setting + this parameter disables display_auto. Units are volts. + """)) + + self._init_channels() + + + def _utility_error_query(self): + error_code = 0 + error_message = "No error" + if not self._driver_operation_simulate: + error_code = self._ask(":system:error?") + error_code = int(error_code) + if error_code != 0: + error_message = "Unknown" + return (error_code, error_message) + + def _init_channels(self): + try: + super(agilents, self)._init_channels() + except AttributeError: + pass + + self._channel_common_mode = list() + self._channel_differential = list() + self._channel_differential_skew = list() + self._channel_display_auto = list() + self._channel_display_offset = list() + self._channel_display_range = list() + self._channel_display_scale = list() + + for i in range(self._analog_channel_count): + self._channel_common_mode.append(False) + self._channel_differential.append(False) + self._channel_differential_skew.append(0) + self._channel_display_auto.append(True) + self._channel_display_offset.append(0.0) + self._channel_display_range.append(1.0) + self._channel_display_scale.append(0.1) + + + def _display_fetch_screenshot(self, format='png', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in ScreenshotImageFormatMapping: + raise ivi.ValueNotSupportedException() + + format = ScreenshotImageFormatMapping[format] + + self._write(":display:data? %s, screen, on, %s" % (format, 'invert' if invert else 'normal')) + + return self._read_ieee_block() + + def _get_channel_common_mode(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_common_mode[index] = bool(int(self._ask(":%s:commonmode?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_common_mode[index] + + def _set_channel_common_mode(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:commonmode %d" % (self._channel_name[index], int(value))) + self._channel_common_mode[index] = value + self._set_cache_valid(index=index) + + def _get_channel_differential(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_differential[index] = bool(int(self._ask(":%s:differential?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_differential[index] + + def _set_channel_differential(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:differential %d" % (self._channel_name[index], int(value))) + self._channel_differential[index] = value + self._set_cache_valid(index=index) + + def _get_channel_differential_skew(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_differential_skew[index] = float(self._ask(":%s:differential:skew?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_differential_skew[index] + + def _set_channel_differential_skew(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:differential:skew %e" % (self._channel_name[index], value)) + self._channel_differential_skew[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_auto(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_auto[index] = bool(int(self._ask(":%s:display:auto?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_display_auto[index] + + def _set_channel_display_auto(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:display:auto %d" % (self._channel_name[index], int(value))) + self._channel_display_auto[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_offset(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_offset[index] = float(self._ask(":%s:display:offset?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_display_offset[index] + + def _set_channel_display_offset(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:display:offset %e" % (self._channel_name[index], value)) + self._channel_display_offset[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_range(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_range[index] = float(self._ask(":%s:display:range?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_display_range[index] + + def _set_channel_display_range(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:display:range %e" % (self._channel_name[index], value)) + self._channel_display_range[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_scale(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_scale[index] = float(self._ask(":%s:display:scale?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_display_scale[index] + + def _set_channel_display_scale(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:display:scale %e" % (self._channel_name[index], value)) + self._channel_display_scale[index] = value + self._set_cache_valid(index=index) + + def _measurement_fetch_waveform(self, index): + index = ivi.get_index(self._channel_name, index) + + if self._driver_operation_simulate: + return list() + + self._write(":waveform:byteorder msbfirst") + self._write(":waveform:format word") + self._write(":waveform:streaming on") + self._write(":waveform:source %s" % self._channel_name[index]) + + # Read preamble + + pre = self._ask(":waveform:preamble?").split(',') + + format = int(pre[0]) + type = int(pre[1]) + points = int(pre[2]) + count = int(pre[3]) + xincrement = float(pre[4]) + xorigin = float(pre[5]) + xreference = int(float(pre[6])) + yincrement = float(pre[7]) + yorigin = float(pre[8]) + yreference = int(float(pre[9])) + + if type == 1: + raise scope.InvalidAcquisitionTypeException() + + if format != 2: + raise UnexpectedResponseException() + + self._write(":waveform:data?") + + # Read waveform data + raw_data = self._read_ieee_block() + + # Split out points and convert to time and voltage pairs + + data = list() + for i in range(points): + x = ((i - xreference) * xincrement) + xorigin + + yval = struct.unpack(">h", raw_data[i*2:i*2+2])[0] + + if yval == 31232: + # hole value + y = float('nan') + else: + y = ((yval - yreference) * yincrement) + yorigin + + data.append((x, y)) + + return data + + def _measurement_read_waveform(self, index, maximum_time): + return self._measurement_fetch_waveform(index) + + def _measurement_initiate(self): + if not self._driver_operation_simulate: + self._write(":acquire:complete 100") + self._write(":digitize") + self._set_cache_valid(False, 'trigger_continuous') + + def _set_working_directory(self,value): + if not self._driver_operation_simulate: + self._write(":DISK:CDIRECTORY %s" % '\"'+value+'\"') + + def _get_pwd(self): + if not self._driver_operation_simulate: + return self._ask(":DISK:PWD?") + + def _save_waveform(self,filename,source,filtype='BIN',header="ON"): + if not self._driver_operation_simulate: + self._write(":DISK:SAVE:WAVEFORM %s" % 'CHANnel'+str(source)+',\"'+filename+'\",'+filtype+','+header) + + def _set_save_waveform_all(self): + if not self._driver_operation_simulate: + self._write(":DISK:SEGMented ALL") From c47d69d640180d0e56a47138da3990a335e76ef7 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Mon, 5 Dec 2016 12:42:28 -0800 Subject: [PATCH 14/79] New DPO features to support GRL --- ivi/tektronix/tektronixDPO7000.py | 104 ++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/ivi/tektronix/tektronixDPO7000.py b/ivi/tektronix/tektronixDPO7000.py index 36c42b4c..7d5f5353 100644 --- a/ivi/tektronix/tektronixDPO7000.py +++ b/ivi/tektronix/tektronixDPO7000.py @@ -43,3 +43,107 @@ def __init__(self, *args, **kwargs): self._identity_supported_instrument_models = ['DPO7354C'] self._init_channels() + + self._add_property('acquisition.horizontal_mode', + self._get_acquisition_horizontal_mode, + self._set_acquisition_horizontal_mode, + None, + ivi.Doc(""" + This command set or queries the horizontal mode. Auto mode is the factory + default. There are three horizontal modes: + * 'auto': AUTO selects the automatic horizontal model. Auto mode attempts to keep record + length constant as you change the time per division setting. Record length is + read only. + * 'constant' selects the constant horizontal model. Constant mode attempts to keep + sample rate constant as you change the time per division setting. Record length + is read only. + * 'manual' selects the manual horizontal model. Manual mode lets you change + sample mode and record length. Time per division or Horizontal scale is read only. + """)) + + self._add_property('acquisition.sample_rate', + self._get_acquisition_sample_rate, + self._set_acquisition_sample_rate, + None, + None, + ivi.Doc(""" + Returns the effective sample rate of the acquired waveform using the + current configuration. The units are samples per second. + """, cls, grp, '4.2.10')) + + self._add_property('acquisition.horizontal_roll', + self._get_acquisition_horizontal_roll, + self._set_acquisition_horizontal_roll, + None, + ivi.Doc(""" + This command sets or queries the Roll Mode status. Use Roll Mode when you + want to view data at very slow sweep speeds. It is useful for observing data + samples on the screen as they occur. There are three modes: + * 'auto': AUTO enables Roll Mode, if the time/division is set appropriately. + * 'off': OFF disables Roll Mode. + * 'on': ON enables Roll Mode, if the time/division is set approprately. + """)) + + def _get_acquisition_horizontal_mode(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition_horizontal_mode = self._ask(":horizontal:mode?") + self._set_cache_valid() + return self._aquisition_horizontal_mode + + def _set_acquisition_horizontal_mode(self, value): + if not self._driver_operation_simulate: + self._write(":horizontal:mode %e" % value) + self._acquisition_horizontal_mode = value + self._set_cache_valid() + + def _get_acquisition_sample_rate(self): + return self._get_acquisition_record_length() / self._get_acquisition_time_per_record() + + def _set_acquisition_sample_rate(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":horizontal:mode:samplerate %e" % value) + self._acquisition_sample_rate = value + self._set_cache_valid() + + def _get_acquisition_horizontal_roll(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._horizontal_roll = self._ask(":horizontal:roll?") + self._set_cache_valid() + return self._acquisition_horizontal_roll + + def _set_acquisition_horizontal_roll(self, value): + if not self._driver_operation_simulate: + self._write(":horizontal:roll %e" % value) + self._acquisition_horizontal_roll = value + self._set_cache_valid() + + def _get_timebase_scale(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._timebase_scale = float(self._ask(":horizontal:mode:scale?")) + self._timebase_range = self._timebase_scale * self._horizontal_divisions + self._set_cache_valid() + return self._timebase_scale + + def _set_timebase_scale(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":horizontal:mode:scale %e" % value) + self._timebase_scale = value + self._timebase_range = value * self._horizontal_divisions + self._set_cache_valid() + self._set_cache_valid(False, 'timebase_window_range') + + def _get_acquisition_number_of_points_minimum(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition_number_of_points_minimum = int(self._ask(":horizontal:mode:recordlength?")) + self._set_cache_valid() + return self._acquisition_number_of_points_minimum + + def _set_acquisition_number_of_points_minimum(self, value): + value = int(value) + # coerce value? + if not self._driver_operation_simulate: + self._write(":horizontal:mode:recordlength %d" % value) + self._acquisition_number_of_points_minimum = value + self._set_cache_valid() From 31f6b43b1b6a8fa66aad4972d7d56290112fb0b6 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 7 Dec 2016 10:15:53 -0800 Subject: [PATCH 15/79] updated comment for DSO of agilent --- ivi/agilent/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ivi/agilent/__init__.py b/ivi/agilent/__init__.py index 19c14e7a..3ccb0bb9 100644 --- a/ivi/agilent/__init__.py +++ b/ivi/agilent/__init__.py @@ -153,9 +153,9 @@ from .agilentMSOX92504A import agilentMSOX92504A from .agilentMSOX92804A import agilentMSOX92804A from .agilentMSOX93204A import agilentMSOX93204A -# Infinium S series -from .agilentDSOS204A import agilentDSOS204A -from .agilentDSOS804A import agilentDSOS804A +# # Infinium S series +# from .agilentDSOS204A import agilentDSOS204A +# from .agilentDSOS804A import agilentDSOS804A # Spectrum Analyzers # 859xA series from .agilent8590A import agilent8590A From 2c76682adc84a4d912269c43b0958427835de4db Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 21 Dec 2016 17:48:21 -0800 Subject: [PATCH 16/79] Incorporating updates to endianness --- ivi/tektronix/tektronixBaseScope.py | 34 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/ivi/tektronix/tektronixBaseScope.py b/ivi/tektronix/tektronixBaseScope.py index e7381a79..92b1a886 100644 --- a/ivi/tektronix/tektronixBaseScope.py +++ b/ivi/tektronix/tektronixBaseScope.py @@ -224,16 +224,13 @@ def __init__(self, *args, **kwargs): self._identity_instrument_firmware_revision = "" self._identity_specification_major_version = 4 self._identity_specification_minor_version = 1 - self._identity_supported_instrument_models = ['DPO2002B', 'DPO2004B', 'DPO2012', - 'DPO2012B', 'DPO2014', 'DPO2014B', 'DPO2022B', 'DPO2024', - 'DPO2024B', 'DPO4032', 'DPO4034', 'DPO4054', 'DPO4104', 'DPO4014B', - 'DPO4034B', 'DPO4054B', 'DPO4102B', 'DPO4104B', 'MSO4032', 'MSO4034', - 'MSO4054', 'MSO4104', 'MSO4014B', 'MSO4034B','MSO4054B', 'MSO4102B', - 'MSO4104B', 'MDO4054', 'MDO4104', 'MDO4014B', 'MDO4034B', 'MDO4054B', - 'MDO4104B', 'MDO3012', 'MDO3014', 'MDO3022', 'MDO3024', 'MDO3032', - 'MDO3034', 'MDO3052', 'MDO3054', 'MDO3102', 'MDO3104', 'MSO2002B', - 'MSO2004B', 'MSO2012', 'MSO2012B', 'MSO2014''MSO2014B', 'MSO2022B', - 'MSO2024', 'MSO2024B', 'DPO7354C'] + self._identity_supported_instrument_models = ['DPO4032', 'DPO4034', 'DPO4054', + 'DPO4104', 'DPO4014B', 'DPO4034B', 'DPO4054B', 'DPO4102B', 'DPO4104B', + 'MSO4032', 'MSO4034', 'MSO4054', 'MSO4104', 'MSO4014B', 'MSO4034B', + 'MSO4054B', 'MSO4102B', 'MSO4104B', 'MDO4054', 'MDO4104', 'MDO4014B', + 'MDO4034B', 'MDO4054B', 'MDO4104B', 'MDO3012', 'MDO3014', 'MDO3022', + 'MDO3024', 'MDO3032', 'MDO3034', 'MDO3052', 'MDO3054', 'MDO3102', + 'MDO3104'] self._add_property('channels[].invert', self._get_channel_invert, @@ -936,7 +933,7 @@ def _set_trigger_edge_slope(self, value): def _get_trigger_source(self): if not self._driver_operation_simulate and not self._get_cache_valid(): t = self._ask(":trigger:a:type?").lower() - if t == 'edge' or t == 'edg': + if t == 'edge': value = self._ask(":trigger:a:edge:source?").lower() elif t == 'logic': # TODO @@ -1281,16 +1278,18 @@ def _measurement_fetch_waveform(self, index): # Read preamble pre = self._ask(":wfmoutpre?").split(';') - acq_format = pre[7].strip() + acq_format = pre[7].strip().upper() points = int(pre[6]) point_size = int(pre[0]) - point_enc = pre[2] - point_fmt = pre[3] + point_enc = pre[2].strip().upper() + point_fmt = pre[3].strip().upper() + byte_order = pre[4].strip().upper() trace.x_increment = float(pre[10]) trace.x_origin = float(pre[11]) + trace.x_reference = int(float(pre[12])) trace.y_increment = float(pre[14]) - trace.y_reference = float(pre[15]) - trace.y_origin = int(float(pre[16])) + trace.y_reference = int(float(pre[15])) + trace.y_origin = float(pre[16]) if acq_format != 'Y': raise UnexpectedResponseException() @@ -1319,7 +1318,7 @@ def _measurement_fetch_waveform(self, index): else: raise UnexpectedResponseException() - if sys.byteorder == 'little': + if (byte_order == 'LSB') != (sys.byteorder == 'little'): trace.y_raw.byteswap() return trace @@ -1469,4 +1468,3 @@ def _set_trigger_modifier(self, value): def _measurement_auto_setup(self): if not self._driver_operation_simulate: self._write(":autoset execute") - From d1aac14eca18ab3a7110a2e731e369a07b996361 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 21 Dec 2016 17:49:47 -0800 Subject: [PATCH 17/79] Fixed class for super --- ivi/tektronix/tektronixDPO7354C.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi/tektronix/tektronixDPO7354C.py b/ivi/tektronix/tektronixDPO7354C.py index acccd615..11f9a1e9 100644 --- a/ivi/tektronix/tektronixDPO7354C.py +++ b/ivi/tektronix/tektronixDPO7354C.py @@ -32,7 +32,7 @@ class tektronixDPO7354C(tektronixDPO7000): def __init__(self, *args, **kwargs): self.__dict__.setdefault('_instrument_id', 'DPO7354C') - super(tektronixDPO4032, self).__init__(*args, **kwargs) + super(tektronixDPO7354C, self).__init__(*args, **kwargs) self._analog_channel_count = 4 self._digital_channel_count = 0 From 16d2b8a0687a77ad332cb545be2c4f67a3f36abf Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Sun, 29 Jan 2017 17:45:16 -0800 Subject: [PATCH 18/79] Initial checkin of MSO5204B support. --- ivi/tektronix/tektronixBaseScope.py | 25 ++++++--- ivi/tektronix/tektronixDPO5000.py | 46 +++++++++++++++++ ivi/tektronix/tektronixDPO7000.py | 78 +++++++++++++++++++++++++++++ ivi/tektronix/tektronixMSO5000.py | 45 +++++++++++++++++ ivi/tektronix/tektronixMSO5204B.py | 42 ++++++++++++++++ 5 files changed, 228 insertions(+), 8 deletions(-) create mode 100644 ivi/tektronix/tektronixDPO5000.py create mode 100644 ivi/tektronix/tektronixMSO5000.py create mode 100644 ivi/tektronix/tektronixMSO5204B.py diff --git a/ivi/tektronix/tektronixBaseScope.py b/ivi/tektronix/tektronixBaseScope.py index 92b1a886..c923212a 100644 --- a/ivi/tektronix/tektronixBaseScope.py +++ b/ivi/tektronix/tektronixBaseScope.py @@ -224,13 +224,22 @@ def __init__(self, *args, **kwargs): self._identity_instrument_firmware_revision = "" self._identity_specification_major_version = 4 self._identity_specification_minor_version = 1 - self._identity_supported_instrument_models = ['DPO4032', 'DPO4034', 'DPO4054', - 'DPO4104', 'DPO4014B', 'DPO4034B', 'DPO4054B', 'DPO4102B', 'DPO4104B', - 'MSO4032', 'MSO4034', 'MSO4054', 'MSO4104', 'MSO4014B', 'MSO4034B', - 'MSO4054B', 'MSO4102B', 'MSO4104B', 'MDO4054', 'MDO4104', 'MDO4014B', - 'MDO4034B', 'MDO4054B', 'MDO4104B', 'MDO3012', 'MDO3014', 'MDO3022', - 'MDO3024', 'MDO3032', 'MDO3034', 'MDO3052', 'MDO3054', 'MDO3102', - 'MDO3104'] + self._identity_supported_instrument_models = ['DPO2002B', 'DPO2004B', 'DPO2012', + 'DPO2012B', 'DPO2014', 'DPO2014B', 'DPO2022B', 'DPO2024', + 'DPO2024B', 'DPO4032', 'DPO4034', 'DPO4054', 'DPO4104', 'DPO4014B', + 'DPO4034B', 'DPO4054B', 'DPO4102B', 'DPO4104B', 'MSO4032', 'MSO4034', + 'MSO4054', 'MSO4104', 'MSO4014B', 'MSO4034B','MSO4054B', 'MSO4102B', + 'MSO4104B', 'MDO4054', 'MDO4104', 'MDO4014B', 'MDO4034B', 'MDO4054B', + 'MDO4104B', 'MDO3012', 'MDO3014', 'MDO3022', 'MDO3024', 'MDO3032', + 'MDO3034', 'MDO3052', 'MDO3054', 'MDO3102', 'MDO3104', 'MSO2002B', + 'MSO2004B', 'MSO2012', 'MSO2012B', 'MSO2014''MSO2014B', 'MSO2022B', + 'MSO2024', 'MSO2024B', 'MSO71254C', 'MSO71604C', 'MSO72004C', 'DPO71254C', + 'DPO71604C', 'DPO72004C', 'MSO5034', 'MSO5054', 'MSO5104', 'MSO5204', + 'DPO5054', 'DPO5104', 'DPO5204', 'DPO70804C', 'MSO70804C', 'DPO7254C', + 'DPO7354C', 'DPO70404C', 'DPO70604C', 'MSO70404C', 'MSO70604C', 'DPO7054C', + 'DPO7104C', 'MSO72304DX', 'MSO72504DX', 'MSO73304DX', 'DPO72304DX', + 'DPO72504DX', 'DPO73304DX', 'DPO5034B', 'DPO5054B', 'DPO5104B', 'DPO5204B', + 'MSO5034B', 'MSO5054B', 'MSO5104B', 'MSO5204B'] self._add_property('channels[].invert', self._get_channel_invert, @@ -933,7 +942,7 @@ def _set_trigger_edge_slope(self, value): def _get_trigger_source(self): if not self._driver_operation_simulate and not self._get_cache_valid(): t = self._ask(":trigger:a:type?").lower() - if t == 'edge': + if t == 'edge' or t == 'edg': value = self._ask(":trigger:a:edge:source?").lower() elif t == 'logic': # TODO diff --git a/ivi/tektronix/tektronixDPO5000.py b/ivi/tektronix/tektronixDPO5000.py new file mode 100644 index 00000000..d8a6cc3f --- /dev/null +++ b/ivi/tektronix/tektronixDPO5000.py @@ -0,0 +1,46 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixBaseScope import * + +class tektronixDPO5000(tektronixBaseScope): + "Tektronix DPO4000 series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO4000') + + super(tektronixDPO5000, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 1e9 + + self._identity_description = "Tektronix DPO4000 series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DPO4032', 'DPO4034', 'DPO4054', + 'DPO4104', 'DPO4014B', 'DPO4034B', 'DPO4054B', 'DPO4102B', 'DPO4104B'] + + self._init_channels() \ No newline at end of file diff --git a/ivi/tektronix/tektronixDPO7000.py b/ivi/tektronix/tektronixDPO7000.py index 7d5f5353..f3add345 100644 --- a/ivi/tektronix/tektronixDPO7000.py +++ b/ivi/tektronix/tektronixDPO7000.py @@ -147,3 +147,81 @@ def _set_acquisition_number_of_points_minimum(self, value): self._write(":horizontal:mode:recordlength %d" % value) self._acquisition_number_of_points_minimum = value self._set_cache_valid() + + def _measurement_fetch_waveform(self, index): + index = ivi.get_index(self._channel_name, index) + + if self._driver_operation_simulate: + return ivi.TraceYT() + + self._write(":data:source %s" % self._channel_name[index]) + self._write(":data:encdg fastest") + self._write(":data:width 2") + self._write(":data:start 1") + self._write(":data:stop 1e10") + + trace = ivi.TraceYT() + + # Read preamble + pre = self._ask(":wfmoutpre?").split(';') + print "pre = ",self._ask("WFMOutpre?") + print "byt_nr", self._ask("WFMOUTpre:BYT_NR?") + print "bit_nr", self._ask("WFMOUTpre:BIT_NR?") + print "ENCDG", self._ask("WFMOUTpre:ENCDG?") + print "BNFMT", self._ask("WFMOUTpre:BN_FMT?") + print "BYTOR", self._ask("WFMOUTpre:BYT_OR?") + print "NRFMT", self._ask("WFMOUTpre:NR_PT?") + print "PTFMT", self._ask("WFMOUTpre:PT_FMT?") + print "XINC", self._ask("WFMOUTpre:XINCR?") + print "XZERO", self._ask("WFMOUTpre:XZERO?") + print "PTOFF", self._ask("WFMOUTpre:PT_OFF?") + print "YMULT", self._ask("WFMOUTpre:YMULT?") + print "YOFOF", self._ask("WFMOUTpre:YOFF?") + print "YZERO", self._ask("WFMOUTpre:YZERO?") + print "NR_nr", self._ask("WFMOUTpre:NR_FR?") + acq_format = pre[7].strip().upper() + points = int(pre[6]) + point_size = int(pre[0]) + point_enc = pre[2].strip().upper() + point_fmt = pre[3].strip().upper() + byte_order = pre[4].strip().upper() + trace.x_reference = float(pre[11]) #pt_off + trace.x_increment = float(pre[9]) #xincr + trace.x_origin = float(pre[10]) #xzero + trace.y_increment = float(pre[13]) #ymult + trace.y_reference = int(float(pre[14])) #yoff + trace.y_origin = (float(pre[15])) #yzero + + if acq_format != 'Y': + raise UnexpectedResponseException() + + if point_enc != 'BINARY': + raise UnexpectedResponseException() + + # Read waveform data + raw_data = self._ask_for_ieee_block(":curve?") + self._read_raw() # flush buffer + + # Store in trace object + if point_fmt == 'RP' and point_size == 1: + trace.y_raw = array.array('B', raw_data[0:points*2]) + elif point_fmt == 'RP' and point_size == 2: + trace.y_raw = array.array('H', raw_data[0:points*2]) + elif point_fmt == 'RI' and point_size == 1: + trace.y_raw = array.array('b', raw_data[0:points*2]) + elif point_fmt == 'RI' and point_size == 2: + trace.y_raw = array.array('h', raw_data[0:points*2]) + elif point_fmt == 'FP' and point_size == 4: + trace.y_increment = 1 + trace.y_reference = 0 + trace.y_origin = 0 + trace.y_raw = array.array('f', raw_data[0:points*4]) + else: + raise UnexpectedResponseException() + + if (byte_order == 'LSB') != sys.byteorder == 'little': + trace.y_raw.byteswap() + + trace.y_raw.byteswap() + + return trace diff --git a/ivi/tektronix/tektronixMSO5000.py b/ivi/tektronix/tektronixMSO5000.py new file mode 100644 index 00000000..eb9c2c24 --- /dev/null +++ b/ivi/tektronix/tektronixMSO5000.py @@ -0,0 +1,45 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixDPO5000 import * + +class tektronixMSO5000(tektronixDPO5000): + "Tektronix MSO4000 series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO5000') + + super(tektronixMSO5000, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._bandwidth = 1e9 + + self._identity_description = "Tektronix MSO5000 series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['MSO5034', 'MSO5054', 'MSO5104', + 'MSO5204', 'MSO5034B', 'MSO5054B', 'MSO5104B', 'MSO5204B'] + + self._init_channels() diff --git a/ivi/tektronix/tektronixMSO5204B.py b/ivi/tektronix/tektronixMSO5204B.py new file mode 100644 index 00000000..1e547408 --- /dev/null +++ b/ivi/tektronix/tektronixMSO5204B.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixMSO5000 import * + +class tektronixMSO5204B(tektronixMSO5000): + "Tektronix MSO5204B IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO5204B') + + super(tektronixMSO5204B, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 2e9 + + self._init_channels() From 50588bd13fb578038e22fecf88544efcc6c1c834 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Sun, 29 Jan 2017 19:37:45 -0800 Subject: [PATCH 19/79] Adding updates to init file for new scopes --- ivi/tektronix/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ivi/tektronix/__init__.py b/ivi/tektronix/__init__.py index f5d069ec..041e073b 100644 --- a/ivi/tektronix/__init__.py +++ b/ivi/tektronix/__init__.py @@ -51,6 +51,12 @@ from .tektronixDPO4054B import tektronixDPO4054B from .tektronixDPO4102B import tektronixDPO4102B from .tektronixDPO4104B import tektronixDPO4104B +# DPO5000 +from .tektronixDPO5000 import tektronixDPO5000 +# MSO5000 +from .tektronixMSO5000 import tektronixMSO5000 +# MSO5204B +from .tektronixMSO50204B import tektronixMSO5204B # DPO7000C from .tektronixDPO7354C import tektronixDPO7354C # MSO4000B From e6037f3ea1cf9874719f0154e7856548ab7ba0d9 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Mon, 30 Jan 2017 06:00:22 -0800 Subject: [PATCH 20/79] update --- ivi/tektronix/tektronixDPO5000.py | 190 +++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 4 deletions(-) diff --git a/ivi/tektronix/tektronixDPO5000.py b/ivi/tektronix/tektronixDPO5000.py index d8a6cc3f..525b3d3c 100644 --- a/ivi/tektronix/tektronixDPO5000.py +++ b/ivi/tektronix/tektronixDPO5000.py @@ -27,10 +27,10 @@ from .tektronixBaseScope import * class tektronixDPO5000(tektronixBaseScope): - "Tektronix DPO4000 series IVI oscilloscope driver" + "Tektronix DPO5000 series IVI oscilloscope driver" def __init__(self, *args, **kwargs): - self.__dict__.setdefault('_instrument_id', 'DPO4000') + self.__dict__.setdefault('_instrument_id', 'DPO5000') super(tektronixDPO5000, self).__init__(*args, **kwargs) @@ -39,8 +39,190 @@ def __init__(self, *args, **kwargs): self._channel_count = self._analog_channel_count + self._digital_channel_count self._bandwidth = 1e9 - self._identity_description = "Tektronix DPO4000 series IVI oscilloscope driver" + self._identity_description = "Tektronix DPO5000 series IVI oscilloscope driver" self._identity_supported_instrument_models = ['DPO4032', 'DPO4034', 'DPO4054', 'DPO4104', 'DPO4014B', 'DPO4034B', 'DPO4054B', 'DPO4102B', 'DPO4104B'] - self._init_channels() \ No newline at end of file + self._init_channels() + + self._add_property('acquisition.horizontal_mode', + self._get_acquisition_horizontal_mode, + self._set_acquisition_horizontal_mode, + None, + ivi.Doc(""" + This command set or queries the horizontal mode. Auto mode is the factory + default. There are three horizontal modes: + * 'auto': AUTO selects the automatic horizontal model. Auto mode attempts to keep record + length constant as you change the time per division setting. Record length is + read only. + * 'constant' selects the constant horizontal model. Constant mode attempts to keep + sample rate constant as you change the time per division setting. Record length + is read only. + * 'manual' selects the manual horizontal model. Manual mode lets you change + sample mode and record length. Time per division or Horizontal scale is read only. + """)) + + self._add_property('acquisition.sample_rate', + self._get_acquisition_sample_rate, + self._set_acquisition_sample_rate, + None, + None, + ivi.Doc(""" + Returns the effective sample rate of the acquired waveform using the + current configuration. The units are samples per second. + """, cls, grp, '4.2.10')) + + self._add_property('acquisition.horizontal_roll', + self._get_acquisition_horizontal_roll, + self._set_acquisition_horizontal_roll, + None, + ivi.Doc(""" + This command sets or queries the Roll Mode status. Use Roll Mode when you + want to view data at very slow sweep speeds. It is useful for observing data + samples on the screen as they occur. There are three modes: + * 'auto': AUTO enables Roll Mode, if the time/division is set appropriately. + * 'off': OFF disables Roll Mode. + * 'on': ON enables Roll Mode, if the time/division is set approprately. + """)) + + def _get_acquisition_horizontal_mode(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition_horizontal_mode = self._ask(":horizontal:mode?") + self._set_cache_valid() + return self._aquisition_horizontal_mode + + def _set_acquisition_horizontal_mode(self, value): + if not self._driver_operation_simulate: + self._write(":horizontal:mode %e" % value) + self._acquisition_horizontal_mode = value + self._set_cache_valid() + + def _get_acquisition_sample_rate(self): + return self._get_acquisition_record_length() / self._get_acquisition_time_per_record() + + def _set_acquisition_sample_rate(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":horizontal:mode:samplerate %e" % value) + self._acquisition_sample_rate = value + self._set_cache_valid() + + def _get_acquisition_horizontal_roll(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._horizontal_roll = self._ask(":horizontal:roll?") + self._set_cache_valid() + return self._acquisition_horizontal_roll + + def _set_acquisition_horizontal_roll(self, value): + if not self._driver_operation_simulate: + self._write(":horizontal:roll %e" % value) + self._acquisition_horizontal_roll = value + self._set_cache_valid() + + def _get_timebase_scale(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._timebase_scale = float(self._ask(":horizontal:mode:scale?")) + self._timebase_range = self._timebase_scale * self._horizontal_divisions + self._set_cache_valid() + return self._timebase_scale + + def _set_timebase_scale(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":horizontal:mode:scale %e" % value) + self._timebase_scale = value + self._timebase_range = value * self._horizontal_divisions + self._set_cache_valid() + self._set_cache_valid(False, 'timebase_window_range') + + def _get_acquisition_number_of_points_minimum(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition_number_of_points_minimum = int(self._ask(":horizontal:mode:recordlength?")) + self._set_cache_valid() + return self._acquisition_number_of_points_minimum + + def _set_acquisition_number_of_points_minimum(self, value): + value = int(value) + # coerce value? + if not self._driver_operation_simulate: + self._write(":horizontal:mode:recordlength %d" % value) + self._acquisition_number_of_points_minimum = value + self._set_cache_valid() + + def _measurement_fetch_waveform(self, index): + index = ivi.get_index(self._channel_name, index) + + if self._driver_operation_simulate: + return ivi.TraceYT() + + self._write(":data:source %s" % self._channel_name[index]) + self._write(":data:encdg fastest") + self._write(":data:width 2") + self._write(":data:start 1") + self._write(":data:stop 1e10") + + trace = ivi.TraceYT() + + # Read preamble + pre = self._ask(":wfmoutpre?").split(';') + print "pre = ",self._ask("WFMOutpre?") + print "byt_nr", self._ask("WFMOUTpre:BYT_NR?") + print "bit_nr", self._ask("WFMOUTpre:BIT_NR?") + print "ENCDG", self._ask("WFMOUTpre:ENCDG?") + print "BNFMT", self._ask("WFMOUTpre:BN_FMT?") + print "BYTOR", self._ask("WFMOUTpre:BYT_OR?") + print "NRFMT", self._ask("WFMOUTpre:NR_PT?") + print "PTFMT", self._ask("WFMOUTpre:PT_FMT?") + print "XINC", self._ask("WFMOUTpre:XINCR?") + print "XZERO", self._ask("WFMOUTpre:XZERO?") + print "PTOFF", self._ask("WFMOUTpre:PT_OFF?") + print "YMULT", self._ask("WFMOUTpre:YMULT?") + print "YOFOF", self._ask("WFMOUTpre:YOFF?") + print "YZERO", self._ask("WFMOUTpre:YZERO?") + print "NR_nr", self._ask("WFMOUTpre:NR_FR?") + acq_format = pre[7].strip().upper() + points = int(pre[6]) + point_size = int(pre[0]) + point_enc = pre[2].strip().upper() + point_fmt = pre[3].strip().upper() + byte_order = pre[4].strip().upper() + trace.x_reference = float(pre[11]) #pt_off + trace.x_increment = float(pre[9]) #xincr + trace.x_origin = float(pre[10]) #xzero + trace.y_increment = float(pre[13]) #ymult + trace.y_reference = int(float(pre[14])) #yoff + trace.y_origin = (float(pre[15])) #yzero + + if acq_format != 'Y': + raise UnexpectedResponseException() + + if point_enc != 'BINARY': + raise UnexpectedResponseException() + + # Read waveform data + raw_data = self._ask_for_ieee_block(":curve?") + self._read_raw() # flush buffer + + # Store in trace object + if point_fmt == 'RP' and point_size == 1: + trace.y_raw = array.array('B', raw_data[0:points*2]) + elif point_fmt == 'RP' and point_size == 2: + trace.y_raw = array.array('H', raw_data[0:points*2]) + elif point_fmt == 'RI' and point_size == 1: + trace.y_raw = array.array('b', raw_data[0:points*2]) + elif point_fmt == 'RI' and point_size == 2: + trace.y_raw = array.array('h', raw_data[0:points*2]) + elif point_fmt == 'FP' and point_size == 4: + trace.y_increment = 1 + trace.y_reference = 0 + trace.y_origin = 0 + trace.y_raw = array.array('f', raw_data[0:points*4]) + else: + raise UnexpectedResponseException() + + if (byte_order == 'LSB') != sys.byteorder == 'little': + trace.y_raw.byteswap() + + trace.y_raw.byteswap() + + return trace \ No newline at end of file From fb47e2d0a3b3f5a4b2654008eb28260a6175df02 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Mon, 30 Jan 2017 06:05:51 -0800 Subject: [PATCH 21/79] fixed typo --- ivi/tektronix/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi/tektronix/__init__.py b/ivi/tektronix/__init__.py index 041e073b..53b89a19 100644 --- a/ivi/tektronix/__init__.py +++ b/ivi/tektronix/__init__.py @@ -56,7 +56,7 @@ # MSO5000 from .tektronixMSO5000 import tektronixMSO5000 # MSO5204B -from .tektronixMSO50204B import tektronixMSO5204B +from .tektronixMSO5204B import tektronixMSO5204B # DPO7000C from .tektronixDPO7354C import tektronixDPO7354C # MSO4000B From 274ea5a7b885148d49c0402f2fb944a2af2703c2 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Mon, 30 Jan 2017 08:54:44 -0800 Subject: [PATCH 22/79] Fixed indentation --- ivi/tektronix/tektronixDPO5000.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi/tektronix/tektronixDPO5000.py b/ivi/tektronix/tektronixDPO5000.py index 525b3d3c..860f64f7 100644 --- a/ivi/tektronix/tektronixDPO5000.py +++ b/ivi/tektronix/tektronixDPO5000.py @@ -45,7 +45,7 @@ def __init__(self, *args, **kwargs): self._init_channels() - self._add_property('acquisition.horizontal_mode', + self._add_property('acquisition.horizontal_mode', self._get_acquisition_horizontal_mode, self._set_acquisition_horizontal_mode, None, From f2366fdc2f40631989552737602f59660592f5b7 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Mon, 30 Jan 2017 09:18:09 -0800 Subject: [PATCH 23/79] fixed add_property bug --- ivi/tektronix/tektronixDPO5000.py | 2 +- ivi/tektronix/tektronixDPO7000.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ivi/tektronix/tektronixDPO5000.py b/ivi/tektronix/tektronixDPO5000.py index 860f64f7..f994036e 100644 --- a/ivi/tektronix/tektronixDPO5000.py +++ b/ivi/tektronix/tektronixDPO5000.py @@ -70,7 +70,7 @@ def __init__(self, *args, **kwargs): ivi.Doc(""" Returns the effective sample rate of the acquired waveform using the current configuration. The units are samples per second. - """, cls, grp, '4.2.10')) + """)) self._add_property('acquisition.horizontal_roll', self._get_acquisition_horizontal_roll, diff --git a/ivi/tektronix/tektronixDPO7000.py b/ivi/tektronix/tektronixDPO7000.py index f3add345..7858c241 100644 --- a/ivi/tektronix/tektronixDPO7000.py +++ b/ivi/tektronix/tektronixDPO7000.py @@ -69,7 +69,7 @@ def __init__(self, *args, **kwargs): ivi.Doc(""" Returns the effective sample rate of the acquired waveform using the current configuration. The units are samples per second. - """, cls, grp, '4.2.10')) + """)) self._add_property('acquisition.horizontal_roll', self._get_acquisition_horizontal_roll, From 226f52b7b0e895c762162e80ca6fb8bf67c257ed Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Mon, 30 Jan 2017 09:21:31 -0800 Subject: [PATCH 24/79] fix --- ivi/tektronix/tektronixDPO5000.py | 1 - ivi/tektronix/tektronixDPO7000.py | 1 - 2 files changed, 2 deletions(-) diff --git a/ivi/tektronix/tektronixDPO5000.py b/ivi/tektronix/tektronixDPO5000.py index f994036e..88db0f89 100644 --- a/ivi/tektronix/tektronixDPO5000.py +++ b/ivi/tektronix/tektronixDPO5000.py @@ -66,7 +66,6 @@ def __init__(self, *args, **kwargs): self._get_acquisition_sample_rate, self._set_acquisition_sample_rate, None, - None, ivi.Doc(""" Returns the effective sample rate of the acquired waveform using the current configuration. The units are samples per second. diff --git a/ivi/tektronix/tektronixDPO7000.py b/ivi/tektronix/tektronixDPO7000.py index 7858c241..8e628197 100644 --- a/ivi/tektronix/tektronixDPO7000.py +++ b/ivi/tektronix/tektronixDPO7000.py @@ -65,7 +65,6 @@ def __init__(self, *args, **kwargs): self._get_acquisition_sample_rate, self._set_acquisition_sample_rate, None, - None, ivi.Doc(""" Returns the effective sample rate of the acquired waveform using the current configuration. The units are samples per second. From c804dbce199ac6ccf422208eb4807dba938cf9e8 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Tue, 31 Jan 2017 09:14:58 -0800 Subject: [PATCH 25/79] Added screenshot fetch --- ivi/tektronix/tektronixDPO5000.py | 27 ++++++++++++++++++++++++++- ivi/tektronix/tektronixDPO7000.py | 23 +++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/ivi/tektronix/tektronixDPO5000.py b/ivi/tektronix/tektronixDPO5000.py index 88db0f89..40ae1dc2 100644 --- a/ivi/tektronix/tektronixDPO5000.py +++ b/ivi/tektronix/tektronixDPO5000.py @@ -224,4 +224,29 @@ def _measurement_fetch_waveform(self, index): trace.y_raw.byteswap() - return trace \ No newline at end of file + return trace + + + def _display_fetch_screenshot(self, format='png', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + format = self._display_screenshot_image_format_mapping[format] + + self._write("HARDCopy:PORT FILE;") + self._write("EXPort:FORMat PNG") + + self._write("HARDCopy:FILEName \"C:\\Temp.png\"") + self._write("HARDCopy STARt") + + self._write("FILESystem:READFile \"C:\\Temp.png\"") + + screenshot = self._read_raw() + + self._write("FILESystem:DELEte \"C:\\Temp.png\"") + + return screenshot + diff --git a/ivi/tektronix/tektronixDPO7000.py b/ivi/tektronix/tektronixDPO7000.py index 8e628197..9ef5adf7 100644 --- a/ivi/tektronix/tektronixDPO7000.py +++ b/ivi/tektronix/tektronixDPO7000.py @@ -224,3 +224,26 @@ def _measurement_fetch_waveform(self, index): trace.y_raw.byteswap() return trace + + def _display_fetch_screenshot(self, format='png', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + format = self._display_screenshot_image_format_mapping[format] + + self._write("HARDCopy:PORT FILE;") + self._write("EXPort:FORMat PNG") + + self._write("HARDCopy:FILEName \"C:\\Temp.png\"") + self._write("HARDCopy STARt") + + self._write("FILESystem:READFile \"C:\\Temp.png\"") + + screenshot = self._read_raw() + + self._write("FILESystem:DELEte \"C:\\Temp.png\"") + + return screenshot From 7fea817dc99797d2074bd8103f616ff072ab7f25 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Fri, 24 Feb 2017 13:22:39 -0800 Subject: [PATCH 26/79] Updated measurement mapping to support negative and positive overshoot --- ivi/tektronix/tektronixMSO5000.py | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/ivi/tektronix/tektronixMSO5000.py b/ivi/tektronix/tektronixMSO5000.py index eb9c2c24..9108b55a 100644 --- a/ivi/tektronix/tektronixMSO5000.py +++ b/ivi/tektronix/tektronixMSO5000.py @@ -26,6 +26,49 @@ from .tektronixDPO5000 import * +MeasurementFunctionMapping = { + 'rise_time': 'rise', + 'fall_time': 'fall', + 'frequency': 'frequency', + 'period': 'period', + 'voltage_rms': 'rms', + 'voltage_peak_to_peak': 'pk2pk', + 'voltage_max': 'maximum', + 'voltage_min': 'minimum', + 'voltage_high': 'high', + 'voltage_low': 'low', + 'voltage_average': 'mean', + 'width_negative': 'nwidth', + 'width_positive': 'pwidth', + 'duty_cycle_negative': 'nduty', + 'duty_cycle_positive': 'pduty', + 'amplitude': 'amplitude', + 'voltage_cycle_rms': 'crms', + 'voltage_cycle_average': 'cmean', + 'overshoot': 'tovershoot', + + 'area': 'area', + 'burst': 'burst', + 'cycle_area': 'carea', + 'overshoot_negative': 'novershoot', + 'overshoot_positive': 'povershoot', + 'edgecount_negative': 'nedgecount', + 'edgecount_positive': 'pedgecount', + 'pulsecount_negative': 'npulsecount', + 'pulsecount_positive': 'ppulsecount', + + 'histogram_hits': 'hits', + 'histogram_peak_hits': 'peakhits', + 'histogram_median': 'median', + 'histogram_sigma1': 'sigma1', + 'histogram_sigma2': 'sigma2', + 'histogram_sigma3': 'sigma3', + 'histogram_stdev': 'stdev', + 'histogram_waveforms': 'waveforms', + + 'phase': 'phase', + 'delay': 'delay'} + class tektronixMSO5000(tektronixDPO5000): "Tektronix MSO4000 series IVI oscilloscope driver" From 87a6458f80f03fe94f1c09e1223eac34bbdd046f Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 19 Apr 2017 09:07:15 -0700 Subject: [PATCH 27/79] First test checkin of new rigol --- ivi/rigol/__init__.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/ivi/rigol/__init__.py b/ivi/rigol/__init__.py index 3ea5b2a7..ec486b6a 100644 --- a/ivi/rigol/__init__.py +++ b/ivi/rigol/__init__.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -24,6 +24,39 @@ """ +# Oscilloscopes +# DS1000Z +from .rigolDS1074Z import rigolDS1074Z +from .rigolDS1104Z import rigolDS1104Z +from .rigolMSO1074Z import rigolMSO1074Z +from .rigolMSO1104Z import rigolMSO1104Z +# DS2000A +from .rigolDS2072A import rigolDS2072A +from .rigolDS2102A import rigolDS2102A +from .rigolDS2202A import rigolDS2202A +from .rigolDS2302A import rigolDS2302A +from .rigolMSO2072A import rigolMSO2072A +from .rigolMSO2102A import rigolMSO2102A +from .rigolMSO2202A import rigolMSO2202A +from .rigolMSO2302A import rigolMSO2302A +# DS4000 +from .rigolDS4012 import rigolDS4012 +from .rigolDS4014 import rigolDS4014 +from .rigolDS4022 import rigolDS4022 +from .rigolDS4024 import rigolDS4024 +from .rigolDS4032 import rigolDS4032 +from .rigolDS4034 import rigolDS4034 +from .rigolDS4052 import rigolDS4052 +from .rigolDS4054 import rigolDS4054 +from .rigolMSO4012 import rigolMSO4012 +from .rigolMSO4014 import rigolMSO4014 +from .rigolMSO4022 import rigolMSO4022 +from .rigolMSO4024 import rigolMSO4024 +from .rigolMSO4032 import rigolMSO4032 +from .rigolMSO4034 import rigolMSO4034 +from .rigolMSO4052 import rigolMSO4052 +from .rigolMSO4054 import rigolMSO4054 + # DC Power Supplies # DP800 from .rigolDP831A import rigolDP831A @@ -35,4 +68,4 @@ # Digital Multimeters #DM3068 -from .rigolDM3068Agilent import rigolDM3068Agilent +from .rigolDM3068Agilent import rigolDM3068Agilent \ No newline at end of file From 7c4949b811ff9eb5404ddcfa33dd67eabd2684aa Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 19 Apr 2017 09:17:32 -0700 Subject: [PATCH 28/79] Happy new year --- ivi/rigol/rigolBaseDCPwr.py | 2 +- ivi/rigol/rigolDM3068Agilent.py | 6 +++--- ivi/rigol/rigolDP1000.py | 3 ++- ivi/rigol/rigolDP1116A.py | 6 ++---- ivi/rigol/rigolDP1308A.py | 4 ++-- ivi/rigol/rigolDP800.py | 2 +- ivi/rigol/rigolDP831A.py | 4 ++-- ivi/rigol/rigolDP832.py | 4 ++-- ivi/rigol/rigolDP832A.py | 4 ++-- 9 files changed, 17 insertions(+), 18 deletions(-) diff --git a/ivi/rigol/rigolBaseDCPwr.py b/ivi/rigol/rigolBaseDCPwr.py index fe600eb2..421f9785 100644 --- a/ivi/rigol/rigolBaseDCPwr.py +++ b/ivi/rigol/rigolBaseDCPwr.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/ivi/rigol/rigolDM3068Agilent.py b/ivi/rigol/rigolDM3068Agilent.py index 01a7df28..29d515b1 100644 --- a/ivi/rigol/rigolDM3068Agilent.py +++ b/ivi/rigol/rigolDM3068Agilent.py @@ -2,8 +2,8 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2012-2016 Alex Forencich -Copyright (c) 2015-2016 Rikard Lindstrom +Copyright (c) 2012-2017 Alex Forencich +Copyright (c) 2015-2017 Rikard Lindstrom Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -318,4 +318,4 @@ def _set_thermistor_resistance(self, value): self._write("temp:tran:ther:type %g" % value) value = float(value) - self._thermistor_resistance = value + self._thermistor_resistance = value \ No newline at end of file diff --git a/ivi/rigol/rigolDP1000.py b/ivi/rigol/rigolDP1000.py index 83a2f352..5cd0e305 100644 --- a/ivi/rigol/rigolDP1000.py +++ b/ivi/rigol/rigolDP1000.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -66,3 +66,4 @@ def __init__(self, *args, **kwargs): + diff --git a/ivi/rigol/rigolDP1116A.py b/ivi/rigol/rigolDP1116A.py index 8491ea11..de3c174b 100644 --- a/ivi/rigol/rigolDP1116A.py +++ b/ivi/rigol/rigolDP1116A.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -49,6 +49,4 @@ def __init__(self, *args, **kwargs): } ] - self._init_outputs() - - + self._init_outputs() \ No newline at end of file diff --git a/ivi/rigol/rigolDP1308A.py b/ivi/rigol/rigolDP1308A.py index 6cab72e9..f68cd095 100644 --- a/ivi/rigol/rigolDP1308A.py +++ b/ivi/rigol/rigolDP1308A.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -68,4 +68,4 @@ def __init__(self, *args, **kwargs): self._init_outputs() - + \ No newline at end of file diff --git a/ivi/rigol/rigolDP800.py b/ivi/rigol/rigolDP800.py index cfa33a6e..f95a6ffb 100644 --- a/ivi/rigol/rigolDP800.py +++ b/ivi/rigol/rigolDP800.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/ivi/rigol/rigolDP831A.py b/ivi/rigol/rigolDP831A.py index 839663ad..c2d11209 100644 --- a/ivi/rigol/rigolDP831A.py +++ b/ivi/rigol/rigolDP831A.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -68,4 +68,4 @@ def __init__(self, *args, **kwargs): self._init_outputs() - + \ No newline at end of file diff --git a/ivi/rigol/rigolDP832.py b/ivi/rigol/rigolDP832.py index 7e68b690..4638443c 100644 --- a/ivi/rigol/rigolDP832.py +++ b/ivi/rigol/rigolDP832.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -68,4 +68,4 @@ def __init__(self, *args, **kwargs): self._init_outputs() - + \ No newline at end of file diff --git a/ivi/rigol/rigolDP832A.py b/ivi/rigol/rigolDP832A.py index b043d2c1..9380500a 100644 --- a/ivi/rigol/rigolDP832A.py +++ b/ivi/rigol/rigolDP832A.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -68,4 +68,4 @@ def __init__(self, *args, **kwargs): self._init_outputs() - + \ No newline at end of file From affad6f635befc0bbd8b20eb6cd130023529ff50 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 19 Apr 2017 10:22:14 -0700 Subject: [PATCH 29/79] Initial checkin of Rigol driver support --- ivi/rigol/rigolBaseScope.py | 1242 +++++++++++++++++++++++++++++++++++ ivi/rigol/rigolDS1000Z.py | 54 ++ ivi/rigol/rigolDS1074Z.py | 42 ++ ivi/rigol/rigolDS1104Z.py | 44 ++ ivi/rigol/rigolDS2000.py | 91 +++ ivi/rigol/rigolDS2072A.py | 43 ++ ivi/rigol/rigolDS2102A.py | 43 ++ ivi/rigol/rigolDS2202A.py | 43 ++ ivi/rigol/rigolDS2302A.py | 43 ++ ivi/rigol/rigolDS4000.py | 107 +++ ivi/rigol/rigolDS4012.py | 43 ++ ivi/rigol/rigolDS4014.py | 43 ++ ivi/rigol/rigolDS4022.py | 43 ++ ivi/rigol/rigolDS4024.py | 43 ++ ivi/rigol/rigolDS4032.py | 43 ++ ivi/rigol/rigolDS4034.py | 43 ++ ivi/rigol/rigolDS4052.py | 43 ++ ivi/rigol/rigolDS4054.py | 43 ++ ivi/rigol/rigolDSSource.py | 435 ++++++++++++ ivi/rigol/rigolMSO1074Z.py | 42 ++ ivi/rigol/rigolMSO1104Z.py | 42 ++ ivi/rigol/rigolMSO2072A.py | 43 ++ ivi/rigol/rigolMSO2102A.py | 43 ++ ivi/rigol/rigolMSO2202A.py | 43 ++ ivi/rigol/rigolMSO2302A.py | 43 ++ ivi/rigol/rigolMSO4012.py | 43 ++ ivi/rigol/rigolMSO4014.py | 43 ++ ivi/rigol/rigolMSO4022.py | 43 ++ ivi/rigol/rigolMSO4024.py | 43 ++ ivi/rigol/rigolMSO4032.py | 43 ++ ivi/rigol/rigolMSO4034.py | 43 ++ ivi/rigol/rigolMSO4052.py | 43 ++ ivi/rigol/rigolMSO4054.py | 43 ++ 33 files changed, 3131 insertions(+) create mode 100644 ivi/rigol/rigolBaseScope.py create mode 100644 ivi/rigol/rigolDS1000Z.py create mode 100644 ivi/rigol/rigolDS1074Z.py create mode 100644 ivi/rigol/rigolDS1104Z.py create mode 100644 ivi/rigol/rigolDS2000.py create mode 100644 ivi/rigol/rigolDS2072A.py create mode 100644 ivi/rigol/rigolDS2102A.py create mode 100644 ivi/rigol/rigolDS2202A.py create mode 100644 ivi/rigol/rigolDS2302A.py create mode 100644 ivi/rigol/rigolDS4000.py create mode 100644 ivi/rigol/rigolDS4012.py create mode 100644 ivi/rigol/rigolDS4014.py create mode 100644 ivi/rigol/rigolDS4022.py create mode 100644 ivi/rigol/rigolDS4024.py create mode 100644 ivi/rigol/rigolDS4032.py create mode 100644 ivi/rigol/rigolDS4034.py create mode 100644 ivi/rigol/rigolDS4052.py create mode 100644 ivi/rigol/rigolDS4054.py create mode 100644 ivi/rigol/rigolDSSource.py create mode 100644 ivi/rigol/rigolMSO1074Z.py create mode 100644 ivi/rigol/rigolMSO1104Z.py create mode 100644 ivi/rigol/rigolMSO2072A.py create mode 100644 ivi/rigol/rigolMSO2102A.py create mode 100644 ivi/rigol/rigolMSO2202A.py create mode 100644 ivi/rigol/rigolMSO2302A.py create mode 100644 ivi/rigol/rigolMSO4012.py create mode 100644 ivi/rigol/rigolMSO4014.py create mode 100644 ivi/rigol/rigolMSO4022.py create mode 100644 ivi/rigol/rigolMSO4024.py create mode 100644 ivi/rigol/rigolMSO4032.py create mode 100644 ivi/rigol/rigolMSO4034.py create mode 100644 ivi/rigol/rigolMSO4052.py create mode 100644 ivi/rigol/rigolMSO4054.py diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py new file mode 100644 index 00000000..256227b0 --- /dev/null +++ b/ivi/rigol/rigolBaseScope.py @@ -0,0 +1,1242 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +import array +import math +import time + +from .. import ivi +from .. import scope +from .. import scpi +from .. import extra + +AcquisitionTypeMapping = { + 'normal': 'norm', + 'peak_detect': 'peak', + 'high_resolution': 'hres', + 'average': 'aver'} +VerticalCoupling = set(['ac', 'dc', 'gnd']) +TriggerTypeMapping = { + 'edge': 'edge', + 'tv': 'video', + 'width': 'puls', + 'glitch': 'puls', + 'runt': 'runt', + #'immediate': '', + 'ac_line': 'edge', + 'window': 'wind', + 'nth_edge': 'nedg', + 'pattern': 'patt', + 'delay': 'del', + 'timeout': 'tim', + 'duration': 'dur', + 'setup_hold': 'shold', + 'rs232': 'rs232', + 'i2c': 'iic', + 'spi': 'spi'} +TriggerCouplingMapping = { + 'ac': ('ac', 0), + 'dc': ('dc', 0), + 'hf_reject': ('hfr', 0), + 'lf_reject': ('lfr', 0), + 'noise_reject': ('dc', 1), + 'ac_noise_reject': ('ac', 1), + 'hf_noise_reject': ('hfr', 1), + 'lf_noise_reject': ('lfr', 1)} +TVTriggerEventMapping = { + 'field1': 'oddf', + 'field2': 'even', + 'any_line': 'alin', + 'line_number': 'line'} +TVTriggerFormatMapping = { + 'ntsc': 'ntsc', + 'pal': 'pals', + 'secam': 'pals', + 'p480': '480p', + 'p576': '576p'} +PolarityMapping = {'positive': 'pos', + 'negative': 'neg'} +GlitchConditionMapping = {'less_than': 'less', + 'greater_than': 'gre'} +WidthConditionMapping = {'within': ''} +SlopeMapping = { + 'positive': 'pos', + 'negative': 'neg', + 'either': 'rfal'} +MeasurementFunctionMapping = { + 'rise_time': 'risetime', + 'fall_time': 'falltime', + 'frequency': 'frequency', + 'period': 'period', + 'voltage_rms': 'vrms display', + 'voltage_peak_to_peak': 'vpp', + 'voltage_max': 'vmax', + 'voltage_min': 'vmin', + 'voltage_high': 'vtop', + 'voltage_low': 'vbase', + 'voltage_average': 'vaverage display', + 'width_negative': 'nwidth', + 'width_positive': 'pwidth', + 'duty_cycle_positive': 'dutycycle', + 'amplitude': 'vamplitude', + 'voltage_cycle_rms': 'vrms cycle', + 'voltage_cycle_average': 'vaverage cycle', + 'overshoot': 'overshoot', + 'preshoot': 'preshoot', + 'ratio': 'vratio', + 'phase': 'phase', + 'delay': 'delay'} +MeasurementFunctionMappingDigital = { + 'rise_time': 'risetime', + 'fall_time': 'falltime', + 'frequency': 'frequency', + 'period': 'period', + 'width_negative': 'nwidth', + 'width_positive': 'pwidth', + 'duty_cycle_positive': 'dutycycle'} +ScreenshotImageFormatMapping = { + 'tif': 'tiff', + 'tiff': 'tiff', + 'bmp': 'bmp24', + 'bmp24': 'bmp24', + 'bmp8': 'bmp8', + 'png': 'png', + 'png24': 'png', + 'jpg': 'jpeg', + 'jpeg': 'jpeg'} +TimebaseModeMapping = { + 'main': 'main', + 'window': 'window', + 'xy': 'xy', + 'roll': 'roll'} +TriggerModifierMapping = {'none': 'norm', 'auto': 'auto'} + +class rigolBaseScope(scpi.common.IdnCommand, scpi.common.ErrorQuery, scpi.common.Reset, + scpi.common.SelfTest, scpi.common.Memory, + scope.Base, scope.TVTrigger, scope.GlitchTrigger, scope.WidthTrigger, + scope.AcLineTrigger, scope.WaveformMeasurement, + scope.ContinuousAcquisition, scope.AverageAcquisition, + scope.TriggerModifier, scope.AutoSetup, + extra.common.SystemSetup, extra.common.Screenshot, + ivi.Driver): + "Rigol generic IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + self._analog_channel_name = list() + self._analog_channel_count = 4 + self._digital_channel_name = list() + self._digital_channel_count = 16 + self._channel_invert = list() + self._channel_bw_limit = list() + + super(rigolBaseScope, self).__init__(*args, **kwargs) + + self._self_test_delay = 0 + self._memory_size = 10 + + self._analog_channel_name = list() + self._analog_channel_count = 4 + self._digital_channel_name = list() + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 1e9 + self._bandwidth_limit = {'20M': 20e6} + self._max_averages = 1024 + + self._horizontal_divisions = 12 + self._vertical_divisions = 8 + + self._timebase_mode = 'main' + self._timebase_position = 0.0 + self._timebase_range = 1e-3 + self._timebase_scale = 100e-6 + self._timebase_window_position = 0.0 + self._timebase_window_range = 5e-6 + self._timebase_window_scale = 500e-9 + self._display_screenshot_image_format_mapping = ScreenshotImageFormatMapping + self._display_vectors = True + + self._identity_description = "Rigol generic IVI oscilloscope driver" + self._identity_identifier = "" + self._identity_revision = "" + self._identity_vendor = "" + self._identity_instrument_manufacturer = "Rigol Technologies" + self._identity_instrument_model = "" + self._identity_instrument_firmware_revision = "" + self._identity_specification_major_version = 4 + self._identity_specification_minor_version = 1 + self._identity_supported_instrument_models = ['DS1074Z', 'DS1104Z', 'MSO1074Z', + 'MSO1104Z', 'DS2074A', 'DS2104A', 'DS2204A', 'DS2304A', 'MSO2074A', + 'MSO2104A', 'MSO2204A', 'MSO2304A'] + + self._add_property('channels[].invert', + self._get_channel_invert, + self._set_channel_invert, + None, + ivi.Doc(""" + Selects whether or not to invert the channel. + """)) + self._add_property('channels[].label', + self._get_channel_label, + self._set_channel_label, + None, + ivi.Doc(""" + Sets the channel label. Setting a channel label also adds the label to + the nonvolatile label list. + """)) + self._add_property('channels[].probe_skew', + self._get_channel_probe_skew, + self._set_channel_probe_skew, + None, + ivi.Doc(""" + Specifies the channel-to-channel skew factor for the channel. Each analog + channel can be adjusted + or - 100 ns for a total of 200 ns difference + between channels. This can be used to compensate for differences in cable + delay. Units are seconds. + """)) + self._add_property('channels[].scale', + self._get_channel_scale, + self._set_channel_scale, + None, + ivi.Doc(""" + Specifies the vertical scale, or units per division, of the channel. Units + are volts. + """)) + self._add_property('timebase.mode', + self._get_timebase_mode, + self._set_timebase_mode, + None, + ivi.Doc(""" + Sets the current time base. There are four time base modes: + + * 'main': normal timebase + * 'window': zoomed or delayed timebase + * 'xy': channels are plotted against each other, no timebase + * 'roll': data moves continuously from left to right + """)) + self._add_property('timebase.position', + self._get_timebase_position, + self._set_timebase_position, + None, + ivi.Doc(""" + Sets the time interval between the trigger event and the display reference + point on the screen. The display reference point is either left, right, or + center and is set with the timebase.reference property. The maximum + position value depends on the time/division settings. + """)) + self._add_property('timebase.range', + self._get_timebase_range, + self._set_timebase_range, + None, + ivi.Doc(""" + Sets the full-scale horizontal time in seconds for the main window. The + range is 10 times the current time-per-division setting. + """)) + self._add_property('timebase.scale', + self._get_timebase_scale, + self._set_timebase_scale, + None, + ivi.Doc(""" + Sets the horizontal scale or units per division for the main window. + """)) + self._add_property('timebase.window.position', + self._get_timebase_window_position, + self._set_timebase_window_position, + None, + ivi.Doc(""" + Sets the horizontal position in the zoomed (delayed) view of the main + sweep. The main sweep range and the main sweep horizontal position + determine the range for this command. The value for this command must + keep the zoomed view window within the main sweep range. + """)) + self._add_property('timebase.window.range', + self._get_timebase_window_range, + self._set_timebase_window_range, + None, + ivi.Doc(""" + Sets the fullscale horizontal time in seconds for the zoomed (delayed) + window. The range is 10 times the current zoomed view window seconds per + division setting. The main sweep range determines the range for this + command. The maximum value is one half of the timebase.range value. + """)) + self._add_property('timebase.window.scale', + self._get_timebase_window_scale, + self._set_timebase_window_scale, + None, + ivi.Doc(""" + Sets the zoomed (delayed) window horizontal scale (seconds/division). The + main sweep scale determines the range for this command. The maximum value + is one half of the timebase.scale value. + """)) + self._add_property('display.vectors', + self._get_display_vectors, + self._set_display_vectors, + None, + ivi.Doc(""" + When enabled, draws a line between consecutive waveform data points. + """)) + self._add_method('display.clear', + self._display_clear, + ivi.Doc(""" + Clears the display and resets all associated measurements. If the + oscilloscope is stopped, all currently displayed data is erased. If the + oscilloscope is running, all the data in active channels and functions is + erased; however, new data is displayed on the next acquisition. + """)) + + self._init_channels() + + def _initialize(self, resource = None, id_query = False, reset = False, **keywargs): + "Opens an I/O session to the instrument." + + self._channel_count = self._analog_channel_count + self._digital_channel_count + + super(rigolBaseScope, self)._initialize(resource, id_query, reset, **keywargs) + + # interface clear + if not self._driver_operation_simulate: + self._clear() + + # check ID + if id_query and not self._driver_operation_simulate: + id = self.identity.instrument_model + id_check = self._instrument_id + id_short = id[:len(id_check)] + if id_short != id_check: + raise Exception("Instrument ID mismatch, expecting %s, got %s", id_check, id_short) + + # reset + if reset: + self.utility.reset() + + def _utility_disable(self): + pass + + def _utility_lock_object(self): + pass + + def _utility_unlock_object(self): + pass + + def _init_channels(self): + super(rigolBaseScope, self)._init_channels() + + self._channel_name = list() + self._channel_label = list() + self._channel_probe_skew = list() + self._channel_invert = list() + self._channel_scale = list() + self._channel_bw_limit = list() + + self._analog_channel_name = list() + for i in range(self._analog_channel_count): + self._channel_name.append("channel%d" % (i+1)) + self._channel_label.append("%d" % (i+1)) + self._analog_channel_name.append("channel%d" % (i+1)) + self._channel_probe_skew.append(0) + self._channel_invert.append(False) + self._channel_scale.append(1.0) + self._channel_bw_limit.append(False) + + # digital channels + self._digital_channel_name = list() + if (self._digital_channel_count > 0): + for i in range(self._digital_channel_count): + self._channel_name.append("digital%d" % i) + self._channel_label.append("D%d" % i) + self._digital_channel_name.append("digital%d" % i) + + for i in range(self._analog_channel_count, self._channel_count): + self._channel_input_impedance[i] = 100000 + self._channel_input_frequency_max[i] = 1e9 + self._channel_probe_attenuation[i] = 1 + self._channel_coupling[i] = 'dc' + self._channel_offset[i] = 0 + self._channel_range[i] = 1 + + self._channel_count = self._analog_channel_count + self._digital_channel_count + self.channels._set_list(self._channel_name) + + def _system_fetch_setup(self): + if self._driver_operation_simulate: + return b'' + + self._write(":system:setup?") + + data = ivi.decode_ieee_block(self._read_raw()) + + return data + + def _system_load_setup(self, data): + if self._driver_operation_simulate: + return + + self._write_ieee_block(data, ':system:setup ') + + self.driver_operation.invalidate_all_attributes() + + def _display_fetch_screenshot(self, format='png', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + format = self._display_screenshot_image_format_mapping[format] + + self._write(":display:data? on, %d, %s" % (int(invert), format)) + + data = self._read_raw() + + return ivi.decode_ieee_block(data) + + def _get_timebase_mode(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + if int(self._ask(":timebase:delay:enable?")): + self._timebase_mode = "window" + else: + value = self._ask(":timebase:mode?").lower() + self._timebase_mode = [k for k,v in TimebaseModeMapping.items() if v==value][0] + self._set_cache_valid() + return self._timebase_mode + + def _set_timebase_mode(self, value): + if value not in TimebaseModeMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + if value == 'window': + self._write("timebase:mode main") + self._write("timebase:delay:enable 1") + else: + self._write("timebase:delay:enable 0") + self._write(":timebase:mode %s" % TimebaseModeMapping[value]) + self._timebase_mode = value + self._set_cache_valid() + + def _get_timebase_position(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._timebase_position = float(self._ask(":timebase:offset?")) + self._set_cache_valid() + return self._timebase_position + + def _set_timebase_position(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":timebase:offset %e" % value) + self._timebase_position = value + self._set_cache_valid() + self._set_cache_valid(False, 'timebase_window_position') + + def _get_timebase_range(self): + return self._get_timebase_scale() * self._horizontal_divisions + + def _set_timebase_range(self, value): + value = float(value) + self._set_timebase_scale(value / self._horizontal_divisions) + + def _get_timebase_scale(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._timebase_scale = float(self._ask(":timebase:scale?")) + self._timebase_range = self._timebase_scale * self._horizontal_divisions + self._set_cache_valid() + return self._timebase_scale + + def _set_timebase_scale(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":timebase:scale %e" % value) + self._timebase_scale = value + self._timebase_range = value * self._horizontal_divisions + self._set_cache_valid() + self._set_cache_valid(False, 'timebase_window_scale') + + def _get_timebase_window_position(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._timebase_window_position = float(self._ask(":timebase:delay:offset?")) + self._set_cache_valid() + return self._timebase_window_position + + def _set_timebase_window_position(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":timebase:delay:offset %e" % value) + self._timebase_window_position = value + self._set_cache_valid() + + def _get_timebase_window_range(self): + return self._get_timebase_window_scale() * self._horizontal_divisions + + def _set_timebase_window_range(self, value): + value = float(value) + self._set_timebase_window_scale(value / self._horizontal_divisions) + + def _get_timebase_window_scale(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._timebase_window_scale = float(self._ask(":timebase:delay:scale?")) + self._timebase_window_range = self._timebase_window_scale * self._horizontal_divisions + self._set_cache_valid() + self._set_cache_valid(True, 'timebase_window_range') + return self._timebase_window_scale + + def _set_timebase_window_scale(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":timebase:delay:scale %e" % value) + self._timebase_window_scale = value + self._timebase_window_range = value * self._horizontal_divisions + self._set_cache_valid() + self._set_cache_valid(True, 'timebase_window_range') + + def _get_display_vectors(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._display_vectors = self._ask(":display:type?").lower() == 'vect' + self._set_cache_valid() + return self._display_vectors + + def _set_display_vectors(self, value): + value = bool(value) + if not self._driver_operation_simulate: + self._write(":display:type %s" % ('vectors' if value else 'dots')) + self._display_vectors = value + self._set_cache_valid() + + def _display_clear(self): + if not self._driver_operation_simulate: + self._write(":display:clear") + + def _get_acquisition_start_time(self): + pos = 0 + if not self._driver_operation_simulate and not self._get_cache_valid(): + pos = float(self._ask(":timebase:offset?")) + self._set_cache_valid() + self._acquisition_start_time = pos - self._get_acquisition_time_per_record() / 2 + return self._acquisition_start_time + + def _set_acquisition_start_time(self, value): + value = float(value) + value = value + self._get_acquisition_time_per_record() / 2 + if not self._driver_operation_simulate: + self._write(":timebase:offset %e" % value) + self._acquisition_start_time = value + self._set_cache_valid() + + def _get_acquisition_type(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":acquire:type?").lower() + self._acquisition_type = [k for k,v in AcquisitionTypeMapping.items() if v==value][0] + self._set_cache_valid() + return self._acquisition_type + + def _set_acquisition_type(self, value): + if value not in AcquisitionTypeMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":acquire:type %s" % AcquisitionTypeMapping[value]) + self._acquisition_type = value + self._set_cache_valid() + + def _get_acquisition_number_of_points_minimum(self): + return self._acquisition_number_of_points_minimum + + def _set_acquisition_number_of_points_minimum(self, value): + value = int(value) + self._acquisition_number_of_points_minimum = value + + def _get_acquisition_record_length(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition_record_length = int(self._ask(":waveform:points?")) + self._set_cache_valid() + return self._acquisition_record_length + + def _get_acquisition_time_per_record(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition_time_per_record = float(self._ask(":timebase:scale?")) * self._horizontal_divisions + self._set_cache_valid() + return self._acquisition_time_per_record + + def _set_acquisition_time_per_record(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":timebase:scale %e" % (value / self._horizontal_divisions)) + self._acquisition_time_per_record = value + self._set_cache_valid() + self._set_cache_valid(False, 'acquisition_start_time') + + def _get_channel_label(self, index): + index = ivi.get_index(self._channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_label[index] = self._ask(":%s:label?" % self._channel_name[index]).strip('"') + self._set_cache_valid(index=index) + return self._channel_label[index] + + def _set_channel_label(self, index, value): + value = str(value) + index = ivi.get_index(self._channel_name, index) + if not self._driver_operation_simulate: + self._write(":%s:label \"%s\"" % (self._channel_name[index], value)) + self._channel_label[index] = value + self._set_cache_valid(index=index) + + def _get_channel_enabled(self, index): + index = ivi.get_index(self._channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_enabled[index] = bool(int(self._ask(":%s:display?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_enabled[index] + + def _set_channel_enabled(self, index, value): + value = bool(value) + index = ivi.get_index(self._channel_name, index) + if not self._driver_operation_simulate: + self._write(":%s:display %d" % (self._channel_name[index], int(value))) + self._channel_enabled[index] = value + self._set_cache_valid(index=index) + + def _get_channel_input_impedance(self, index): + index = ivi.get_index(self._analog_channel_name, index) + return self._channel_input_impedance[index] + + def _set_channel_input_impedance(self, index, value): + value = float(value) + index = ivi.get_index(self._analog_channel_name, index) + if value != 1000000: + raise Exception('Invalid impedance selection') + self._channel_input_impedance[index] = value + + def _get_channel_input_frequency_max(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + value = self._ask(":%s:bwlimit?" % self._channel_name[index]).upper() + if value == 'OFF': + self._channel_input_frequency_max[index] = self._bandwidth + else: + self._channel_input_frequency_max[index] = self._bandwidth_limit[value] + return self._channel_input_frequency_max[index] + + def _set_channel_input_frequency_max(self, index, value): + value = float(value) + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate: + limit = 'OFF' + bw = self._bandwidth + + for l, b in self._bandwidth_limit.items(): + if b < bw and b > value: + limit, bw = l, b + + value = bw + self._write(":%s:bwlimit %s" % (self._channel_name[index], limit)) + self._channel_input_frequency_max[index] = value + self._set_cache_valid(index=index) + + def _get_channel_probe_attenuation(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_attenuation[index] = float(self._ask(":%s:probe?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_attenuation[index] + + def _set_channel_probe_attenuation(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:probe %e" % (self._channel_name[index], value)) + self._channel_probe_attenuation[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'channel_offset', index) + self._set_cache_valid(False, 'channel_scale', index) + self._set_cache_valid(False, 'channel_range', index) + self._set_cache_valid(False, 'trigger_level') + + def _get_channel_probe_skew(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_skew[index] = float(self._ask(":%s:tcal?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_skew[index] + + def _set_channel_probe_skew(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:tcal %e" % (self._channel_name[index], value)) + self._channel_probe_skew[index] = value + self._set_cache_valid(index=index) + + def _get_channel_invert(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_invert[index] = bool(int(self._ask(":%s:invert?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_invert[index] + + def _set_channel_invert(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:invert %d" % (self._channel_name[index], value)) + self._channel_invert[index] = value + self._set_cache_valid(index=index) + + def _get_channel_coupling(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_enabled[index] = self._ask(":%s:coupling?" % self._channel_name[index]).lower() + self._set_cache_valid(index=index) + return self._channel_coupling[index] + + def _set_channel_coupling(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + if value not in VerticalCoupling: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":%s:coupling %s" % (self._channel_name[index], value)) + self._channel_coupling[index] = value + self._set_cache_valid(index=index) + + def _get_channel_offset(self, index): + index = ivi.get_index(self._channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_offset[index] = float(self._ask(":%s:offset?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_offset[index] + + def _set_channel_offset(self, index, value): + index = ivi.get_index(self._channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:offset %e" % (self._channel_name[index], value)) + self._channel_offset[index] = value + self._set_cache_valid(index=index) + + def _get_channel_range(self, index): + index = ivi.get_index(self._channel_name, index) + return self._get_channel_scale(index) * self._vertical_divisions + + def _set_channel_range(self, index, value): + index = ivi.get_index(self._channel_name, index) + value = float(value) + self._set_channel_range(index, value / self._vertical_divisions) + + def _get_channel_scale(self, index): + index = ivi.get_index(self._channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_scale[index] = float(self._ask(":%s:scale?" % self._channel_name[index])) + self._channel_range[index] = self._channel_scale[index] * self._vertical_divisions + self._set_cache_valid(index=index) + self._set_cache_valid(True, "channel_range", index) + return self._channel_scale[index] + + def _set_channel_scale(self, index, value): + index = ivi.get_index(self._channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:scale %e" % (self._channel_name[index], value)) + self._channel_scale[index] = value + self._channel_range[index] = value * self._vertical_divisions + self._set_cache_valid(index=index) + self._set_cache_valid(True, "channel_range", index) + self._set_cache_valid(False, "channel_offset", index) + + def _get_measurement_status(self): + return self._measurement_status + + def _get_trigger_coupling(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + cpl = self._ask(":trigger:coupling?").lower() + noise = int(self._ask(":trigger:nreject?")) + for k in TriggerCouplingMapping: + if (cpl, noise) == TriggerCouplingMapping[k]: + self._trigger_coupling = k + return self._trigger_coupling + + def _set_trigger_coupling(self, value): + if value not in TriggerCouplingMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + cpl, noise = TriggerCouplingMapping[value] + self._write(":trigger:coupling %s" % cpl) + self._write(":trigger:nreject %d" % noise) + self._trigger_coupling = value + self._set_cache_valid() + + def _get_trigger_holdoff(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._trigger_holdoff = float(self._ask(":trigger:holdoff?")) + self._set_cache_valid() + return self._trigger_holdoff + + def _set_trigger_holdoff(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":trigger:holdoff %e" % value) + self._trigger_holdoff = value + self._set_cache_valid() + + def _get_trigger_level(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._trigger_level = float(self._ask(":trigger:edge:level?")) + self._set_cache_valid() + return self._trigger_level + + def _set_trigger_level(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":trigger:edge:level %e" % value) + self._trigger_level = value + self._set_cache_valid() + + def _get_trigger_edge_slope(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:edge:slope?").lower() + self._trigger_edge_slope = [k for k,v in SlopeMapping.items() if v==value][0] + self._set_cache_valid() + return self._trigger_edge_slope + + def _set_trigger_edge_slope(self, value): + if value not in SlopeMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":trigger:edge:slope %s" % SlopeMapping[value]) + self._trigger_edge_slope = value + self._set_cache_valid() + + def _get_trigger_source(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:edge:source?").lower() + # TODO process value + self._trigger_source = value + self._set_cache_valid() + return self._trigger_source + + def _set_trigger_source(self, value): + if hasattr(value, 'name'): + value = value.name + value = str(value) + if value not in self._channel_name: + raise ivi.UnknownPhysicalNameException() + if not self._driver_operation_simulate: + self._write(":trigger:edge:source %s" % value) + self._trigger_source = value + self._set_cache_valid() + + def _get_trigger_type(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:mode?").lower() + if value == 'edge': + src = self._ask(":trigger:edge:source?").lower() + if src == 'ac': + value = 'ac_line' + elif value == 'puls': + qual = self._ask(":trigger:pulse:when?").lower() + if qual in ('pgl', 'ngl'): + value = 'width' + else: + value = 'glitch' + else: + value = [k for k,v in TriggerTypeMapping.items() if v==value][0] + self._trigger_type = value + self._set_cache_valid() + return self._trigger_type + + def _set_trigger_type(self, value): + if value not in TriggerTypeMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":trigger:mode %s" % TriggerTypeMapping[value]) + if value == 'ac_line': + self._write(":trigger:edge:source ac") + if value == 'glitch': + if self._get_trigger_glitch_condition() == 'greater_than': + if self._get_trigger_width_polarity() == 'positive': + self._write(":trigger:pulse:when pgr") + else: + self._write(":trigger:pulse:when ngr") + else: + if self._get_trigger_width_polarity() == 'positive': + self._write(":trigger:pulse:when ples") + else: + self._write(":trigger:pulse:when nles") + if value == 'width': + if self._get_trigger_width_polarity() == 'positive': + self._write(":trigger:pulse:when pgl") + else: + self._write(":trigger:pulse:when ngl") + self._trigger_type = value + self._set_cache_valid() + + def _measurement_abort(self): + pass + + def _get_trigger_tv_trigger_event(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:video:mode?").lower() + # may need processing + self._trigger_tv_trigger_event = [k for k,v in TVTriggerEventMapping.items() if v==value][0] + self._set_cache_valid() + return self._trigger_tv_trigger_event + + def _set_trigger_tv_trigger_event(self, value): + if value not in TVTriggerEvent: + raise ivi.ValueNotSupportedException() + # may need processing + if not self._driver_operation_simulate: + self._write(":trigger:video:mode %s" % TVTriggerEventMapping[value]) + self._trigger_tv_trigger_event = value + self._set_cache_valid() + + def _get_trigger_tv_line_number(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = int(self._ask(":trigger:video:line?")) + # may need processing + self._trigger_tv_line_number = value + self._set_cache_valid() + return self._trigger_tv_line_number + + def _set_trigger_tv_line_number(self, value): + value = int(value) + # may need processing + if not self._driver_operation_simulate: + self._write(":trigger:video:line %e" % value) + self._trigger_tv_line_number = value + self._set_cache_valid() + + def _get_trigger_tv_polarity(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:video:polarity?").lower() + self._trigger_tv_polarity = [k for k,v in PolarityMapping.items() if v==value][0] + self._set_cache_valid() + return self._trigger_tv_polarity + + def _set_trigger_tv_polarity(self, value): + if value not in PolarityMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":trigger:video:polarity %s" % PolarityMapping[value]) + self._trigger_tv_polarity = value + self._set_cache_valid() + + def _get_trigger_tv_signal_format(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:video:standard?").lower() + self._trigger_tv_signal_format = [k for k,v in TVTriggerFormatMapping.items() if v==value][0] + self._set_cache_valid() + return self._trigger_tv_signal_format + + def _set_trigger_tv_signal_format(self, value): + if value not in TVTriggerFormatMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":trigger:video:standard %s" % TVTriggerFormatMapping[value]) + self._trigger_tv_signal_format = value + self._set_cache_valid() + + def _get_trigger_glitch_condition(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:pulse:when?").lower() + if value in ('pgr', 'ngr'): + self._trigger_glitch_condition = 'greater_than' + self._set_cache_valid() + elif value in ('ples', 'nles'): + self._trigger_glitch_condition = 'less_than' + self._set_cache_valid() + return self._trigger_glitch_condition + + def _set_trigger_glitch_condition(self, value): + if value not in GlitchConditionMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + if self._get_trigger_glitch_polarity() == 'positive': + self._write(":trigger:pulse:when %s" % ('pgr' if value == 'greater_than' else 'ples')) + else: + self._write(":trigger:pulse:when %s" % ('ngr' if value == 'greater_than' else 'nles')) + self._trigger_glitch_condition = value + self._set_cache_valid() + + def _get_trigger_glitch_polarity(self): + return self._get_trigger_width_polarity() + + def _set_trigger_glitch_polarity(self, value): + self._set_trigger_width_polarity(value) + + def _get_trigger_glitch_width(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._trigger_glitch_width = float(self._ask(":trigger:pulse:width?")) + self._set_cache_valid() + return self._trigger_glitch_width + + def _set_trigger_glitch_width(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":trigger:pulse:width %e" % value) + self._trigger_glitch_width = value + self._set_cache_valid() + + def _get_trigger_width_condition(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = 'within' + return self._trigger_width_condition + + def _set_trigger_width_condition(self, value): + if value not in WidthConditionMapping: + raise ivi.ValueNotSupportedException() + self._trigger_width_condition = value + self._set_cache_valid() + + def _get_trigger_width_threshold_high(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._trigger_width_threshold_high = float(self._ask(":trigger:pulse:uwidth?")) + self._set_cache_valid() + return self._trigger_width_threshold_high + + def _set_trigger_width_threshold_high(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":trigger:pulse:uwidth %e" % value) + self._trigger_width_threshold_high = value + self._set_cache_valid() + + def _get_trigger_width_threshold_low(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._trigger_width_threshold_low = float(self._ask(":trigger:pulse:lwidth?")) + self._set_cache_valid() + return self._trigger_width_threshold_low + + def _set_trigger_width_threshold_low(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":trigger:pulse:lwidth %e" % value) + self._trigger_width_threshold_low = value + self._set_cache_valid() + + def _get_trigger_width_polarity(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:pulse:when?").lower() + self._trigger_width_polarity = 'positive' if value.lower() in ('pgr', 'ples', 'pgl') else 'negative' + self._set_cache_valid() + return self._trigger_width_polarity + + def _set_trigger_width_polarity(self, value): + if value not in PolarityMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + if self._get_trigger_type() == 'glitch': + if self._get_trigger_glitch_condition() == 'greater_than': + if value == 'positive': + self._write(":trigger:pulse:when pgr") + else: + self._write(":trigger:pulse:when ngr") + else: + if value == 'positive': + self._write(":trigger:pulse:when ples") + else: + self._write(":trigger:pulse:when nles") + if self._get_trigger_type() == 'width': + if value == 'positive': + self._write(":trigger:pulse:when pgl") + else: + self._write(":trigger:pulse:when ngl") + self._trigger_width_polarity = value + self._set_cache_valid() + + def _get_trigger_ac_line_slope(self): + return self._get_trigger_edge_slope() + + def _set_trigger_ac_line_slope(self, value): + self._set_trigger_edge_slope(value) + + def _measurement_fetch_waveform(self, index): + index = ivi.get_index(self._channel_name, index) + + if self._driver_operation_simulate: + return ivi.TraceYT() + + self._write(":waveform:source %s" % self._channel_name[index]) + self._write(":waveform:format byte") + + trace = ivi.TraceYT() + + # Read preamble + pre = self._ask(":waveform:preamble?").split(',') + + acq_format = int(pre[0]) + acq_type = int(pre[1]) + points = int(pre[2]) + trace.average_count = int(pre[3]) + trace.x_increment = float(pre[4]) + trace.x_origin = float(pre[5]) + trace.x_reference = int(float(pre[6])) + trace.y_increment = float(pre[7]) + trace.y_origin = 0.0 + trace.y_reference = int(float(pre[9]) + float(pre[8])) + + if acq_format != 0: + raise UnexpectedResponseException() + + # Read waveform data + data = bytearray() + + for offset in range(1, points+1, 250000): + self._write(":waveform:start %d" % offset) + self._write(":waveform:stop %d" % min(points, offset+249999)) + self._write(":waveform:data?") + raw_data = self._read_raw() + data.extend(ivi.decode_ieee_block(raw_data)) + + # Store in trace object + trace.y_raw = array.array('B', data[0:points]) + + return trace + + def _measurement_read_waveform(self, index, maximum_time): + return self._measurement_fetch_waveform(index) + + def _measurement_initiate(self): + if not self._driver_operation_simulate: + self._write(":single") + self._set_cache_valid(False, 'trigger_continuous') + + def _get_reference_level_high(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._reference_level_high = float(self._ask(":measure:setup:max?")) + self._set_cache_valid() + return self._reference_level_high + + def _set_reference_level_high(self, value): + value = float(value) + if value < 7: value = 7 + if value > 95: value = 95 + if not self._driver_operation_simulate: + self._write(":measure:setup:max %e" % value) + self._reference_level_high = value + self._set_cache_valid() + + def _get_reference_level_low(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._reference_level_low = float(self._ask(":measure:setup:min?")) + self._set_cache_valid() + return self._reference_level_low + + def _set_reference_level_low(self, value): + value = float(value) + if value < 5: value = 5 + if value > 93: value = 93 + if not self._driver_operation_simulate: + self._write(":measure:setup:min %e" % value) + self._reference_level_low = value + self._set_cache_valid() + + def _get_reference_level_middle(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._reference_level_middle = float(self._ask(":measure:setup:mid?")) + self._set_cache_valid() + return self._reference_level_middle + + def _set_reference_level_middle(self, value): + value = float(value) + if value < 6: value = 6 + if value > 94: value = 94 + if not self._driver_operation_simulate: + self._write(":measure:setup:mid %e" % value) + self._reference_level_middle = value + self._set_cache_valid() + + def _measurement_fetch_waveform_measurement(self, index, measurement_function, ref_channel = None): + index = ivi.get_index(self._channel_name, index) + if index < self._analog_channel_count: + if measurement_function not in MeasurementFunctionMapping: + raise ivi.ValueNotSupportedException() + func = MeasurementFunctionMapping[measurement_function] + else: + if measurement_function not in MeasurementFunctionMappingDigital: + raise ivi.ValueNotSupportedException() + func = MeasurementFunctionMappingDigital[measurement_function] + if not self._driver_operation_simulate: + l = func.split(' ') + l[0] = l[0] + '?' + if len(l) > 1: + l[-1] = l[-1] + ',' + func = ' '.join(l) + query = ":measure:item? %s, %s" % (func, self._channel_name[index]) + if measurement_function in ['phase', 'delay']: + ref_index = ivi.get_index(self._channel_name, ref_channel) + query += ", %s" % self._channel_name[ref_index] + return float(self._ask(query)) + return 0 + + def _measurement_read_waveform_measurement(self, index, measurement_function, maximum_time): + return self._measurement_fetch_waveform_measurement(index, measurement_function) + + def _get_trigger_continuous(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._trigger_continuous = self._ask(":trigger:sweep?").lower() != 'sing' + self._set_cache_valid() + return self._trigger_continuous + + def _set_trigger_continuous(self, value): + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s" % ('run' if value else 'stop')) + self._trigger_continuous = value + self._set_cache_valid() + + def _get_acquisition_number_of_averages(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition_number_of_averages = int(self._ask(":acquire:averages?")) + self._set_cache_valid() + return self._acquisition_number_of_averages + + def _set_acquisition_number_of_averages(self, value): + if value < 2 or value > self._max_averages: + raise ivi.OutOfRangeException() + value = 2**round(math.log(value, 2)) + if not self._driver_operation_simulate: + self._write(":acquire:averages %d" % value) + self._acquisition_number_of_averages = value + self._set_cache_valid() + + def _get_trigger_modifier(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:sweep?").lower() + if value == 'sing': + self._trigger_modifier = 'none' + else: + self._trigger_modifier = [k for k,v in TriggerModifierMapping.items() if v==value][0] + self._set_cache_valid() + return self._trigger_modifier + + def _set_trigger_modifier(self, value): + if value not in TriggerModifierMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":trigger:sweep %s" % TriggerModifierMapping[value]) + self._trigger_modifier = value + self._set_cache_valid() + + def _measurement_auto_setup(self): + if not self._driver_operation_simulate: + self._write(":autoscale") diff --git a/ivi/rigol/rigolDS1000Z.py b/ivi/rigol/rigolDS1000Z.py new file mode 100644 index 00000000..40b1850a --- /dev/null +++ b/ivi/rigol/rigolDS1000Z.py @@ -0,0 +1,54 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolBaseScope import * +from .rigolDSSource import * + +class rigolDS1000Z(rigolBaseScope, rigolDSSource): + "Rigol DS1000Z series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + + super(rigolDS1000Z, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._bandwidth = 100e6 + self._bandwidth_limit = {'20M': 20e6} + self._max_averages = 1024 + + self._horizontal_divisions = 12 + self._vertical_divisions = 8 + + # Internal source + self._output_count = 2 + + self._identity_description = "Rigol DS1000Z series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DS1074Z', 'DS1104Z', 'MSO1074Z', 'MSO1104Z'] + + self._init_channels() + self._init_outputs() diff --git a/ivi/rigol/rigolDS1074Z.py b/ivi/rigol/rigolDS1074Z.py new file mode 100644 index 00000000..34a286e4 --- /dev/null +++ b/ivi/rigol/rigolDS1074Z.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS1000Z import * + +class rigolDS1074Z(rigolDS1000Z): + "Rigol DS1074Z IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS1074Z') + + super(rigolDS1074Z, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 70e6 + + self._init_channels() diff --git a/ivi/rigol/rigolDS1104Z.py b/ivi/rigol/rigolDS1104Z.py new file mode 100644 index 00000000..bfc07376 --- /dev/null +++ b/ivi/rigol/rigolDS1104Z.py @@ -0,0 +1,44 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS1000Z import * + +class rigolDS1104Z(rigolDS1000Z): + "Rigol DS1104Z IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS1104Z') + + super(rigolDS1104Z, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + + self._init_channels() + + \ No newline at end of file diff --git a/ivi/rigol/rigolDS2000.py b/ivi/rigol/rigolDS2000.py new file mode 100644 index 00000000..3d9644b2 --- /dev/null +++ b/ivi/rigol/rigolDS2000.py @@ -0,0 +1,91 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolBaseScope import * +from .rigolDSSource import * + +ScreenshotImageFormatMapping = { + 'bmp': 'bmp', + 'bmp24': 'bmp24'} + +class rigolDS2000A(rigolBaseScope, rigolDSSource): + "Rigol DS2000A series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + super(rigolDS2000A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 16 + self._bandwidth = 300e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + self._max_averages = 8192 + + self._horizontal_divisions = 14 + self._vertical_divisions = 8 + + # Internal source + self._output_count = 2 + + self._display_screenshot_image_format_mapping = ScreenshotImageFormatMapping + + self._identity_description = "Rigol DS2000A series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DS2074A', 'DS2104A', 'DS2204A', + 'DS2304A', 'MSO2074A', 'MSO2104A', 'MSO2204A', 'MSO2304A'] + + self._init_channels() + self._init_outputs() + + def _display_fetch_screenshot(self, format='bmp', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + self._write(":display:data?") + + data = self._read_raw() + + return ivi.decode_ieee_block(data) + + def _get_channel_probe_attenuation(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_attenuation[index] = float(self._ask(":%s:probe?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_attenuation[index] + + def _set_channel_probe_attenuation(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:probe %s" % (self._channel_name[index], ("%f" %value).rstrip('0').rstrip('.'))) + self._channel_probe_attenuation[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'channel_offset', index) + self._set_cache_valid(False, 'channel_scale', index) + self._set_cache_valid(False, 'channel_range', index) + self._set_cache_valid(False, 'trigger_level') diff --git a/ivi/rigol/rigolDS2072A.py b/ivi/rigol/rigolDS2072A.py new file mode 100644 index 00000000..0dc000c3 --- /dev/null +++ b/ivi/rigol/rigolDS2072A.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS2000A import * + +class rigolDS2072A(rigolDS2000A): + "Rigol DS2072A IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS2072A') + + super(rigolDS2072A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 70e6 + self._bandwidth_limit = {'20M': 20e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS2102A.py b/ivi/rigol/rigolDS2102A.py new file mode 100644 index 00000000..9a0519e8 --- /dev/null +++ b/ivi/rigol/rigolDS2102A.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS2000A import * + +class rigolDS2102A(rigolDS2000A): + "Rigol DS2102A IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS2102A') + + super(rigolDS2102A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + self._bandwidth_limit = {'20M': 20e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS2202A.py b/ivi/rigol/rigolDS2202A.py new file mode 100644 index 00000000..e1d72644 --- /dev/null +++ b/ivi/rigol/rigolDS2202A.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS2000A import * + +class rigolDS2202A(rigolDS2000A): + "Rigol DS2202A IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS2202A') + + super(rigolDS2202A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 200e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS2302A.py b/ivi/rigol/rigolDS2302A.py new file mode 100644 index 00000000..3b3567fd --- /dev/null +++ b/ivi/rigol/rigolDS2302A.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS2000A import * + +class rigolDS2302A(rigolDS2000A): + "Rigol DS2302A IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS2302A') + + super(rigolDS2302A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 300e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4000.py b/ivi/rigol/rigolDS4000.py new file mode 100644 index 00000000..ff9f7f9d --- /dev/null +++ b/ivi/rigol/rigolDS4000.py @@ -0,0 +1,107 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolBaseScope import * + +class rigolDS4000(rigolBaseScope): + "Rigol DS4000 series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + + super(rigolDS4000, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._bandwidth = 500e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + self._max_averages = 8192 + + self._horizontal_divisions = 12 + self._vertical_divisions = 8 + + self._identity_description = "Rigol DS4000 series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DS4012', 'DS4014', 'DS4022', + 'DS4024', 'DS4032', 'DS4034', 'DS4052', 'DS4054', 'MSO4012', 'MSO4014', + 'MSO4022', 'MSO4024', 'MSO4032', 'MSO4034', 'MSO4052', 'MSO4054'] + + self._init_channels() + + def _display_fetch_screenshot(self, format='bmp', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + self._write(":display:data?") + + data = self._read_raw() + + return ivi.decode_ieee_block(data) + + def _get_channel_input_impedance(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + val = self._ask(":%s:impedance?" % self._channel_name[index]) + if val == 'OMEG': + self._channel_input_impedance[index] = 1000000 + elif val == 'FIFT': + self._channel_input_impedance[index] = 50 + self._set_cache_valid(index=index) + return self._channel_input_impedance[index] + + def _set_channel_input_impedance(self, index, value): + value = float(value) + index = ivi.get_index(self._analog_channel_name, index) + if value != 50 and value != 1000000: + raise Exception('Invalid impedance selection') + if not self._driver_operation_simulate: + if value == 1000000: + self._write(":%s:impedance omeg" % self._channel_name[index]) + elif value == 50: + self._write(":%s:impedance fifty" % self._channel_name[index]) + self._channel_input_impedance[index] = value + self._set_cache_valid(index=index) + + def _get_channel_probe_attenuation(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_attenuation[index] = float(self._ask(":%s:probe?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_attenuation[index] + + def _set_channel_probe_attenuation(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:probe %s" % (self._channel_name[index], ("%f" %value).rstrip('0').rstrip('.'))) + self._channel_probe_attenuation[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'channel_offset', index) + self._set_cache_valid(False, 'channel_scale', index) + self._set_cache_valid(False, 'channel_range', index) + self._set_cache_valid(False, 'trigger_level') diff --git a/ivi/rigol/rigolDS4012.py b/ivi/rigol/rigolDS4012.py new file mode 100644 index 00000000..be3cbfe7 --- /dev/null +++ b/ivi/rigol/rigolDS4012.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS4000 import * + +class rigolDS4012(rigolDS4000): + "Rigol DS4012 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4012') + + super(rigolDS4012, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + self._bandwidth_limit = {'20M': 20e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4014.py b/ivi/rigol/rigolDS4014.py new file mode 100644 index 00000000..f22e94c6 --- /dev/null +++ b/ivi/rigol/rigolDS4014.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS4000 import * + +class rigolDS4014(rigolDS4000): + "Rigol DS4014 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4014') + + super(rigolDS4014, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + self._bandwidth_limit = {'20M': 20e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4022.py b/ivi/rigol/rigolDS4022.py new file mode 100644 index 00000000..0a3a86f4 --- /dev/null +++ b/ivi/rigol/rigolDS4022.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS4000 import * + +class rigolDS4022(rigolDS4000): + "Rigol DS4022 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4022') + + super(rigolDS4022, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 200e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4024.py b/ivi/rigol/rigolDS4024.py new file mode 100644 index 00000000..72065b85 --- /dev/null +++ b/ivi/rigol/rigolDS4024.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS4000 import * + +class rigolDS4024(rigolDS4000): + "Rigol DS4024 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4024') + + super(rigolDS4024, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 200e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4032.py b/ivi/rigol/rigolDS4032.py new file mode 100644 index 00000000..ef26bb49 --- /dev/null +++ b/ivi/rigol/rigolDS4032.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS4000 import * + +class rigolDS4032(rigolDS4000): + "Rigol DS4032 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4032') + + super(rigolDS4032, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 300e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4034.py b/ivi/rigol/rigolDS4034.py new file mode 100644 index 00000000..1e2ac60f --- /dev/null +++ b/ivi/rigol/rigolDS4034.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS4000 import * + +class rigolDS4034(rigolDS4000): + "Rigol DS4034 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4034') + + super(rigolDS4034, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 300e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4052.py b/ivi/rigol/rigolDS4052.py new file mode 100644 index 00000000..9ede3e23 --- /dev/null +++ b/ivi/rigol/rigolDS4052.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS4000 import * + +class rigolDS4052(rigolDS4000): + "Rigol DS4052 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4052') + + super(rigolDS4052, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 500e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4054.py b/ivi/rigol/rigolDS4054.py new file mode 100644 index 00000000..b20bfc95 --- /dev/null +++ b/ivi/rigol/rigolDS4054.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS4000 import * + +class rigolDS4054(rigolDS4000): + "Rigol DS4054 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4054') + + super(rigolDS4054, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 500e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDSSource.py b/ivi/rigol/rigolDSSource.py new file mode 100644 index 00000000..aac0c549 --- /dev/null +++ b/ivi/rigol/rigolDSSource.py @@ -0,0 +1,435 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +import numpy as np +import struct + +from .. import ivi +from .. import fgen + +OutputMode = set(['function', 'arbitrary']) +OperationMode = set(['continuous']) +StandardWaveformMapping = { + 'sine': 'sin', + 'square': 'squ', + 'triangle': 'ramp', + 'ramp_up': 'ramp', + 'ramp_down': 'ramp', + 'dc': 'dc', + 'pulse': 'puls', + 'noise': 'nois', + 'sinc': 'sinc', + 'exprise': 'expr', + 'expfall': 'expf', + 'cardiac': 'ecg', + 'gaussian': 'gaus', + 'lorentz': 'lor', + 'haversine': 'hav' + } + +class rigolDSSource(fgen.Base, fgen.StdFunc, fgen.ArbWfm, fgen.ArbFrequency, + fgen.ArbChannelWfm): + "Rigol DSO internal source IVI function generator driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + + self._output_standard_waveform_symmetry = list() + + super(rigolDSSource, self).__init__(*args, **kwargs) + + # Internal source + self._output_count = 2 + self._arbitrary_sample_rate = 0 + self._arbitrary_waveform_number_waveforms_max = 0 + self._arbitrary_waveform_size_max = 16384 + self._arbitrary_waveform_size_min = 2 + self._arbitrary_waveform_quantum = 1 + + self._add_property('outputs[].standard_waveform.symmetry', + self._get_output_standard_waveform_symmetry, + self._set_output_standard_waveform_symmetry, + None, + """ + Specifies the symmetry for a ramp or triangle waveform. This attribute + affects function generator behavior only when the Waveform attribute is + set to Waveform Triangle, Ramp Up, or Ramp Down. The value is expressed + as a percentage. + """) + + self._identity_description = "Rigol DSO internal source IVI function generator driver" + self._identity_supported_instrument_models = [] + + self._init_outputs() + + def _init_outputs(self): + try: + super(rigolDSSource, self)._init_outputs() + except AttributeError: + pass + self._output_name = list() + self._output_operation_mode = list() + self._output_enabled = list() + self._output_impedance = list() + self._output_mode = list() + self._output_reference_clock_source = list() + self._output_standard_waveform_ramp_symmetry = list() + for i in range(self._output_count): + self._output_name.append("source%d" % (i+1)) + self._output_operation_mode.append('continuous') + self._output_enabled.append(False) + self._output_impedance.append(50) + self._output_mode.append('function') + self._output_reference_clock_source.append('internal') + self._output_standard_waveform_symmetry.append(50.0) + + self.outputs._set_list(self._output_name) + + # AFG option + def _get_output_operation_mode(self, index): + index = ivi.get_index(self._output_name, index) + return self._output_operation_mode[index] + + def _set_output_operation_mode(self, index, value): + index = ivi.get_index(self._output_name, index) + if value not in OperationMode: + raise ivi.ValueNotSupportedException() + self._output_operation_mode[index] = value + + def _get_output_enabled(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:output:state?" % self._output_name[index]) + self._output_enabled[index] = resp == 'ON' + self._set_cache_valid(index=index) + return self._output_enabled[index] + + def _set_output_enabled(self, index, value): + index = ivi.get_index(self._output_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:output:state %d" % (self._output_name[index], value)) + self._output_enabled[index] = value + self._set_cache_valid(index=index) + + def _get_output_impedance(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + val = self._ask(":%s:output:impedance?" % self._output_name[index]) + if val == 'HIGHZ': + self._output_impedance[index] = 1000000 + elif val == 'FIF': + self._output_impedance[index] = 50 + self._set_cache_valid(index=index) + return self._output_impedance[index] + + def _set_output_impedance(self, index, value): + value = float(value) + index = ivi.get_index(self._analog_channel_name, index) + if value != 50 and value != 1000000: + raise Exception('Invalid impedance selection') + if not self._driver_operation_simulate: + if value == 1000000: + self._write(":%s:output:impedance highz" % self._output_name[index]) + elif value == 50: + self._write(":%s:output:impedance fifty" % self._output_name[index]) + self._output_impedance[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'output_standard_waveform_amplitude', index) + + def _get_output_mode(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:function?" % self._output_name[index]).lower() + if resp == 'ext': + self._output_mode[index] = 'arbitrary' + else: + self._output_mode[index] = 'function' + self._set_cache_valid(index=index) + return self._output_mode[index] + + def _set_output_mode(self, index, value): + index = ivi.get_index(self._output_name, index) + if value not in OutputMode: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + if value == 'arbitrary': + self._write(":%s:function ext" % self._output_name[index]) + else: + if self._get_cache_valid('output_standard_waveform_waveform', index=index): + self._set_output_standard_waveform_waveform(index, self._output_standard_waveform_waveform[index]) + else: + self._set_output_standard_waveform_waveform(index, 'sine') + self._output_mode[index] = value + self._set_cache_valid(index=index) + + def _get_output_reference_clock_source(self, index): + index = ivi.get_index(self._output_name, index) + return self._output_reference_clock_source[index] + + def _set_output_reference_clock_source(self, index, value): + index = ivi.get_index(self._output_name, index) + value = 'internal' + self._output_reference_clock_source[index] = value + + def abort_generation(self): + pass + + def initiate_generation(self): + pass + + def _get_output_standard_waveform_amplitude(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:voltage:amplitude?" % self._output_name[index]) + self._output_standard_waveform_amplitude[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_amplitude[index] + + def _set_output_standard_waveform_amplitude(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < 0.01 or value > 5.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:voltage:amplitude %e" % (self._output_name[index], value)) + self._output_standard_waveform_amplitude[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_dc_offset(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:voltage:offset?" % self._output_name[index]) + self._output_standard_waveform_dc_offset[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_dc_offset[index] + + def _set_output_standard_waveform_dc_offset(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:voltage:offset %e" % (self._output_name[index], value)) + self._output_standard_waveform_dc_offset[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_duty_cycle_high(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:pulse:dcycle?" % self._output_name[index]) + self._output_standard_waveform_duty_cycle_high[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_duty_cycle_high[index] + + def _set_output_standard_waveform_duty_cycle_high(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < 10.0 or value > 90.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:pulse:dcycle %e" % (self._output_name[index], value)) + self._output_standard_waveform_duty_cycle_high[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_symmetry(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:function:ramp:symmetry?" % self._output_name[index]) + self._output_standard_waveform_symmetry[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_symmetry[index] + + def _set_output_standard_waveform_symmetry(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < 0.0 or value > 100.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:function:ramp:symmetry %e" % (self._output_name[index], value)) + self._output_standard_waveform_symmetry[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_start_phase(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:phase?" % self._output_name[index]) + self._output_standard_waveform_start_phase[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_start_phase[index] + + def _set_output_standard_waveform_start_phase(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) % 360 + if value < 0 or value > 360.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:phase %e" % (self._output_name[index], value)) + self._output_standard_waveform_start_phase[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_frequency(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:frequency?" % self._output_name[index]) + self._output_standard_waveform_frequency[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_frequency[index] + + def _set_output_standard_waveform_frequency(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < 0.1 or value > 25e6: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:frequency %e" % (self._output_name[index], value)) + self._output_standard_waveform_frequency[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_waveform(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:function?" % self._output_name[index]).lower() + if resp == 'arbitrary': + resp = 'sine' + resp = [k for k,v in StandardWaveformMapping.items() if v==resp][0] + if resp == 'ramp_up': + if self._get_output_standard_waveform_symmetry(index) <= 10.0: + resp = 'ramp_down' + elif self._get_output_standard_waveform_symmetry(index) >= 90.0: + resp = 'ramp_up' + else: + resp = 'triangle' + self._output_standard_waveform_waveform[index] = resp + self._set_cache_valid(index=index) + return self._output_standard_waveform_waveform[index] + + def _set_output_standard_waveform_waveform(self, index, value): + index = ivi.get_index(self._output_name, index) + if value not in StandardWaveformMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":%s:function %s" % (self._output_name[index], StandardWaveformMapping[value])) + if value == 'triangle': + if self._get_output_standard_waveform_symmetry(index) <= 10.0 or self._get_output_standard_waveform_symmetry(index) >= 90: + self._set_output_standard_waveform_symmetry(index, 50.0) + elif value == 'ramp_up': + self._set_output_standard_waveform_symmetry(index, 100.0) + elif value == 'ramp_down': + self._set_output_standard_waveform_symmetry(index, 0.0) + self._output_standard_waveform_waveform[index] = value + self._set_cache_valid(index=index) + self._output_mode[index] = 'function' + self._set_cache_valid(True, 'output_mode', index=index) + + def _get_output_arbitrary_gain(self, index): + return self._get_output_standard_waveform_amplitude(index) + + def _set_output_arbitrary_gain(self, index, value): + self._set_output_standard_waveform_amplitude(index, value) + + def _get_output_arbitrary_offset(self, index): + return self._get_output_standard_waveform_dc_offset(index) + + def _set_output_arbitrary_offset(self, index, value): + self._set_output_standard_waveform_dc_offset(index, value) + + def _get_output_arbitrary_waveform(self, index): + index = ivi.get_index(self._output_name, index) + return self._output_arbitrary_waveform[index] + + def _set_output_arbitrary_waveform(self, index, value): + index = ivi.get_index(self._output_name, index) + value = str(value) + self._output_arbitrary_waveform[index] = value + + def _get_arbitrary_sample_rate(self): + return self._arbitrary_sample_rate + + def _set_arbitrary_sample_rate(self, value): + value = float(value) + self._arbitrary_sample_rate = value + + def _get_arbitrary_waveform_number_waveforms_max(self): + return self._arbitrary_waveform_number_waveforms_max + + def _get_arbitrary_waveform_size_max(self): + return self._arbitrary_waveform_size_max + + def _get_arbitrary_waveform_size_min(self): + return self._arbitrary_waveform_size_min + + def _get_arbitrary_waveform_quantum(self): + return self._arbitrary_waveform_quantum + + def _arbitrary_waveform_clear(self, handle): + pass + + def _arbitrary_waveform_configure(self, index, handle, gain, offset): + self._set_output_arbitrary_waveform(index, handle) + self._set_output_arbitrary_gain(index, gain) + self._set_output_arbitrary_offset(index, offset) + + def _arbitrary_waveform_create(self, data): + return "handle" + + def _get_output_arbitrary_frequency(self, index): + return self._get_output_standard_waveform_frequency(index) + + def _set_output_arbitrary_frequency(self, index, value): + self._set_output_standard_waveform_frequency(index, value) + + def _arbitrary_waveform_create_channel_waveform(self, index, data): + y = None + x = None + if type(data) == list and type(data[0]) == float: + # list + y = array(data) + elif type(data) == np.ndarray and len(data.shape) == 1: + # 1D array + y = data + elif type(data) == np.ndarray and len(data.shape) == 2 and data.shape[0] == 1: + # 2D array, hieght 1 + y = data[0] + elif type(data) == np.ndarray and len(data.shape) == 2 and data.shape[1] == 1: + # 2D array, width 1 + y = data[:,0] + else: + x, y = ivi.get_sig(data) + + if len(y) % self._arbitrary_waveform_quantum != 0: + raise ivi.ValueNotSupportedException() + + # clip on [-1,1] and rescale to [0,1] + yc = (y.clip(-1, 1)+1)/2 + + # scale to 14 bits + yb = np.rint(yc * ((1 << 14)-1)).astype(int) & 0x00003fff + + raw_data = yb.astype(' Date: Thu, 20 Apr 2017 08:26:03 -0700 Subject: [PATCH 30/79] Adding DS2000A --- ivi/rigol/rigolDS2000A.py | 91 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 ivi/rigol/rigolDS2000A.py diff --git a/ivi/rigol/rigolDS2000A.py b/ivi/rigol/rigolDS2000A.py new file mode 100644 index 00000000..3d9644b2 --- /dev/null +++ b/ivi/rigol/rigolDS2000A.py @@ -0,0 +1,91 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolBaseScope import * +from .rigolDSSource import * + +ScreenshotImageFormatMapping = { + 'bmp': 'bmp', + 'bmp24': 'bmp24'} + +class rigolDS2000A(rigolBaseScope, rigolDSSource): + "Rigol DS2000A series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + super(rigolDS2000A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 16 + self._bandwidth = 300e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + self._max_averages = 8192 + + self._horizontal_divisions = 14 + self._vertical_divisions = 8 + + # Internal source + self._output_count = 2 + + self._display_screenshot_image_format_mapping = ScreenshotImageFormatMapping + + self._identity_description = "Rigol DS2000A series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DS2074A', 'DS2104A', 'DS2204A', + 'DS2304A', 'MSO2074A', 'MSO2104A', 'MSO2204A', 'MSO2304A'] + + self._init_channels() + self._init_outputs() + + def _display_fetch_screenshot(self, format='bmp', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + self._write(":display:data?") + + data = self._read_raw() + + return ivi.decode_ieee_block(data) + + def _get_channel_probe_attenuation(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_attenuation[index] = float(self._ask(":%s:probe?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_attenuation[index] + + def _set_channel_probe_attenuation(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:probe %s" % (self._channel_name[index], ("%f" %value).rstrip('0').rstrip('.'))) + self._channel_probe_attenuation[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'channel_offset', index) + self._set_cache_valid(False, 'channel_scale', index) + self._set_cache_valid(False, 'channel_range', index) + self._set_cache_valid(False, 'trigger_level') From 96d87d4e38defd9f5b3a01607484b1af3e19c334 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Thu, 11 May 2017 22:48:56 -0700 Subject: [PATCH 31/79] updated channel naming --- ivi/rigol/rigolBaseScope.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index 256227b0..a6d8401a 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -354,9 +354,9 @@ def _init_channels(self): self._analog_channel_name = list() for i in range(self._analog_channel_count): - self._channel_name.append("channel%d" % (i+1)) + self._channel_name.append("chan%d" % (i+1)) self._channel_label.append("%d" % (i+1)) - self._analog_channel_name.append("channel%d" % (i+1)) + self._analog_channel_name.append("chan%d" % (i+1)) self._channel_probe_skew.append(0) self._channel_invert.append(False) self._channel_scale.append(1.0) @@ -366,9 +366,9 @@ def _init_channels(self): self._digital_channel_name = list() if (self._digital_channel_count > 0): for i in range(self._digital_channel_count): - self._channel_name.append("digital%d" % i) + self._channel_name.append("d%d" % i) self._channel_label.append("D%d" % i) - self._digital_channel_name.append("digital%d" % i) + self._digital_channel_name.append("d%d" % i) for i in range(self._analog_channel_count, self._channel_count): self._channel_input_impedance[i] = 100000 From 176793590fc4bde4170f9345f8239350446546aa Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Fri, 12 May 2017 12:02:44 -0700 Subject: [PATCH 32/79] Updated measurement syntax --- ivi/rigol/rigolBaseScope.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index a6d8401a..3999788f 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -87,28 +87,28 @@ 'negative': 'neg', 'either': 'rfal'} MeasurementFunctionMapping = { - 'rise_time': 'risetime', - 'fall_time': 'falltime', + 'rise_time': 'rtime', + 'fall_time': 'ftime', 'frequency': 'frequency', 'period': 'period', - 'voltage_rms': 'vrms display', + 'voltage_rms': 'vrms', 'voltage_peak_to_peak': 'vpp', 'voltage_max': 'vmax', 'voltage_min': 'vmin', 'voltage_high': 'vtop', 'voltage_low': 'vbase', - 'voltage_average': 'vaverage display', + 'voltage_average': 'vavg', 'width_negative': 'nwidth', 'width_positive': 'pwidth', - 'duty_cycle_positive': 'dutycycle', - 'amplitude': 'vamplitude', - 'voltage_cycle_rms': 'vrms cycle', - 'voltage_cycle_average': 'vaverage cycle', + 'duty_cycle_positive': 'pduty', + 'amplitude': 'vamp', + 'voltage_cycle_rms': 'pvrms', + 'voltage_cycle_average': 'vbase', 'overshoot': 'overshoot', - 'preshoot': 'preshoot', - 'ratio': 'vratio', - 'phase': 'phase', - 'delay': 'delay'} + 'preshoot': 'preshoot'} + #'ratio': 'vratio', + #'phase': 'rphase', + #'delay': 'rdelay'} MeasurementFunctionMappingDigital = { 'rise_time': 'risetime', 'fall_time': 'falltime', @@ -1181,7 +1181,7 @@ def _measurement_fetch_waveform_measurement(self, index, measurement_function, r if len(l) > 1: l[-1] = l[-1] + ',' func = ' '.join(l) - query = ":measure:item? %s, %s" % (func, self._channel_name[index]) + query = ":measure:%s? %s" % (func, self._channel_name[index]) if measurement_function in ['phase', 'delay']: ref_index = ivi.get_index(self._channel_name, ref_channel) query += ", %s" % self._channel_name[ref_index] From 4563512c8633e04231a470f8230951919c4421f0 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Sat, 13 May 2017 09:06:01 -0700 Subject: [PATCH 33/79] Minor tweak to measurement function --- ivi/rigol/rigolBaseScope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index 3999788f..715eb34b 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -1181,7 +1181,7 @@ def _measurement_fetch_waveform_measurement(self, index, measurement_function, r if len(l) > 1: l[-1] = l[-1] + ',' func = ' '.join(l) - query = ":measure:%s? %s" % (func, self._channel_name[index]) + query = ":measure:%s %s" % (func, self._channel_name[index]) if measurement_function in ['phase', 'delay']: ref_index = ivi.get_index(self._channel_name, ref_channel) query += ", %s" % self._channel_name[ref_index] From ef936fb72b7585d12ac7cfc9d2f517fbd5649a68 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Thu, 1 Jun 2017 07:36:33 -0700 Subject: [PATCH 34/79] Updated channel offset sign --- ivi/rigol/rigolBaseScope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index 715eb34b..c7896125 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -721,7 +721,7 @@ def _set_channel_coupling(self, index, value): def _get_channel_offset(self, index): index = ivi.get_index(self._channel_name, index) if not self._driver_operation_simulate and not self._get_cache_valid(index=index): - self._channel_offset[index] = float(self._ask(":%s:offset?" % self._channel_name[index])) + self._channel_offset[index] = -float(self._ask(":%s:offset?" % self._channel_name[index])) self._set_cache_valid(index=index) return self._channel_offset[index] From 5610184ce20162f75c5decc34ea5231f6b4a88cf Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Thu, 15 Jun 2017 21:42:52 -0700 Subject: [PATCH 35/79] Updating vertical divisions number --- ivi/tektronix/tektronixMDO3012.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ivi/tektronix/tektronixMDO3012.py b/ivi/tektronix/tektronixMDO3012.py index 6f98fb96..efc3695b 100644 --- a/ivi/tektronix/tektronixMDO3012.py +++ b/ivi/tektronix/tektronixMDO3012.py @@ -37,6 +37,7 @@ def __init__(self, *args, **kwargs): self._analog_channel_count = 2 self._digital_channel_count = 16 self._bandwidth = 100e6 + self._vertical_divisions = 8 # AFG option self._output_count = 1 From fd9a1db9864534717859ef2194c75e474725ac58 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Fri, 16 Jun 2017 13:54:54 -0700 Subject: [PATCH 36/79] adding support for channel position --- ivi/tektronix/tektronixBaseScope.py | 34 ++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/ivi/tektronix/tektronixBaseScope.py b/ivi/tektronix/tektronixBaseScope.py index c923212a..f8456d03 100644 --- a/ivi/tektronix/tektronixBaseScope.py +++ b/ivi/tektronix/tektronixBaseScope.py @@ -281,6 +281,13 @@ def __init__(self, *args, **kwargs): Specifies the vertical scale, or units per division, of the channel. Units are volts. """)) + self._add_property('channels[].position', + self._get_channel_position, + self._set_channel_position, + None, + ivi.Doc(""" + Specifies the vertical position of the channel. Units are volts. + """)) self._add_property('channels[].trigger_level', self._get_channel_trigger_level, self._set_channel_trigger_level, @@ -424,6 +431,7 @@ def _init_channels(self): self._channel_scale = list() self._channel_trigger_level = list() self._channel_bw_limit = list() + self._channel_position = list() self._analog_channel_name = list() for i in range(self._analog_channel_count): @@ -436,6 +444,7 @@ def _init_channels(self): self._channel_invert.append(False) self._channel_probe_id.append("NONE") self._channel_bw_limit.append(False) + self._channel_position.append(0) # digital channels self._digital_channel_name = list() @@ -452,6 +461,7 @@ def _init_channels(self): self._channel_coupling[i] = 'dc' self._channel_offset[i] = 0 self._channel_range[i] = 1 + #self._channel_position[i] = 0 self._channel_count = self._analog_channel_count + self._digital_channel_count self.channels._set_list(self._channel_name) @@ -817,18 +827,29 @@ def _set_channel_coupling(self, index, value): def _get_channel_offset(self, index): index = ivi.get_index(self._channel_name, index) - if not self._driver_operation_simulate and not self._get_cache_valid(index=index): - self._channel_offset[index] = -float(self._ask(":%s:position?" % self._channel_name[index])) * self._get_channel_scale(index) - self._set_cache_valid(index=index) + if not self._driver_operation_simulate: + self._channel_offset[index] = float(self._ask(":%s:offset?" % self._channel_name[index])) return self._channel_offset[index] def _set_channel_offset(self, index, value): index = ivi.get_index(self._channel_name, index) value = float(value) if not self._driver_operation_simulate: - self._write(":%s:position %e" % (self._channel_name[index], -value / self._get_channel_scale(index))) + self._write(":%s:offset %e" % (self._channel_name[index], value)) self._channel_offset[index] = value - self._set_cache_valid(index=index) + + def _get_channel_position(self, index): + index = ivi.get_index(self._channel_name, index) + if not self._driver_operation_simulate: + self._channel_position[index] = float(self._ask(":%s:position?" % self._channel_name[index])) + return self._channel_position[index] + + def _set_channel_position(self, index, value): + index = ivi.get_index(self._channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:position %e" % (self._channel_name[index], value)) + self._channel_position[index] = value def _get_channel_range(self, index): return self._get_channel_scale(index) * self._vertical_divisions @@ -1043,7 +1064,6 @@ def _set_trigger_type(self, value): self._write(":trigger:a:edge:source line") elif value in ['runt', 'width', 'glitch', 'transition', 'timeout']: self._write(":trigger:a:pulse:class %s" % value) - print(value) if value == 'glitch': t = self._ask(":trigger:a:pulsewidth:when?").lower() if t not in GlitchConditionMapping.values(): @@ -1282,11 +1302,13 @@ def _measurement_fetch_waveform(self, index): self._write(":data:start 1") self._write(":data:stop 1e10") + trace = ivi.TraceYT() # Read preamble pre = self._ask(":wfmoutpre?").split(';') + acq_format = pre[7].strip().upper() points = int(pre[6]) point_size = int(pre[0]) From 1fc9041fa6fdf5140ea93f93b05c42a6c2933c29 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Tue, 20 Jun 2017 10:54:37 -0700 Subject: [PATCH 37/79] Decached timebase values --- ivi/tektronix/tektronixBaseScope.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ivi/tektronix/tektronixBaseScope.py b/ivi/tektronix/tektronixBaseScope.py index f8456d03..7ee74f4f 100644 --- a/ivi/tektronix/tektronixBaseScope.py +++ b/ivi/tektronix/tektronixBaseScope.py @@ -547,9 +547,8 @@ def _set_timebase_mode(self, value): self._set_cache_valid() def _get_timebase_position(self): - if not self._driver_operation_simulate and not self._get_cache_valid(): + if not self._driver_operation_simulate: self._timebase_position = float(self._ask(":horizontal:delay:time?")) - self._set_cache_valid() return self._timebase_position def _set_timebase_position(self, value): @@ -558,7 +557,6 @@ def _set_timebase_position(self, value): self._write(":horizontal:delay:mode 1") self._write(":horizontal:delay:time %e" % value) self._timebase_position = value - self._set_cache_valid() self._set_cache_valid(False, 'acquisition_start_time') self._set_cache_valid(False, 'timebase_window_position') From 7e161cdd1fe860b937eab74d5ddf37aadb8ee911 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 5 Jul 2017 20:23:41 -0700 Subject: [PATCH 38/79] added negative duty cycle measurement and fixed bug in channel coupling assignment --- ivi/rigol/rigolBaseScope.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index c7896125..90fc2f75 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -100,7 +100,8 @@ 'voltage_average': 'vavg', 'width_negative': 'nwidth', 'width_positive': 'pwidth', - 'duty_cycle_positive': 'pduty', + 'duty_cycle_positive': 'pduty', + 'duty_cycle_negative': 'nduty', 'amplitude': 'vamp', 'voltage_cycle_rms': 'pvrms', 'voltage_cycle_average': 'vbase', @@ -705,7 +706,7 @@ def _set_channel_invert(self, index, value): def _get_channel_coupling(self, index): index = ivi.get_index(self._analog_channel_name, index) if not self._driver_operation_simulate and not self._get_cache_valid(index=index): - self._channel_enabled[index] = self._ask(":%s:coupling?" % self._channel_name[index]).lower() + self._channel_coupling[index] = self._ask(":%s:coupling?" % self._channel_name[index]).lower() self._set_cache_valid(index=index) return self._channel_coupling[index] From e704932844ca3a0ec4ad082a400da02906317bae Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 5 Jul 2017 20:47:27 -0700 Subject: [PATCH 39/79] fixed channel offset sign and fixed channel_range --- ivi/rigol/rigolBaseScope.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index 90fc2f75..43730d15 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -722,7 +722,7 @@ def _set_channel_coupling(self, index, value): def _get_channel_offset(self, index): index = ivi.get_index(self._channel_name, index) if not self._driver_operation_simulate and not self._get_cache_valid(index=index): - self._channel_offset[index] = -float(self._ask(":%s:offset?" % self._channel_name[index])) + self._channel_offset[index] = float(self._ask(":%s:offset?" % self._channel_name[index])) self._set_cache_valid(index=index) return self._channel_offset[index] @@ -741,7 +741,7 @@ def _get_channel_range(self, index): def _set_channel_range(self, index, value): index = ivi.get_index(self._channel_name, index) value = float(value) - self._set_channel_range(index, value / self._vertical_divisions) + self._set_channel_scale(index, value / self._vertical_divisions) def _get_channel_scale(self, index): index = ivi.get_index(self._channel_name, index) From 417ed35496abda2bdcca062b93fe6e0ffd823c4b Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Thu, 6 Jul 2017 08:46:11 -0700 Subject: [PATCH 40/79] Fixed memory depth bugs --- ivi/rigol/rigolBaseScope.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index 43730d15..5556ee72 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -91,25 +91,25 @@ 'fall_time': 'ftime', 'frequency': 'frequency', 'period': 'period', - 'voltage_rms': 'vrms', + 'voltage_rms': 'vrms', 'voltage_peak_to_peak': 'vpp', 'voltage_max': 'vmax', 'voltage_min': 'vmin', 'voltage_high': 'vtop', 'voltage_low': 'vbase', - 'voltage_average': 'vavg', + 'voltage_average': 'vavg', 'width_negative': 'nwidth', 'width_positive': 'pwidth', - 'duty_cycle_positive': 'pduty', - 'duty_cycle_negative': 'nduty', - 'amplitude': 'vamp', - 'voltage_cycle_rms': 'pvrms', - 'voltage_cycle_average': 'vbase', + 'duty_cycle_positive': 'pduty', + 'duty_cycle_negative': 'nduty', + 'amplitude': 'vamp', + 'voltage_cycle_rms': 'pvrms', + 'voltage_cycle_average': 'vbase', 'overshoot': 'overshoot', 'preshoot': 'preshoot'} #'ratio': 'vratio', - #'phase': 'rphase', - #'delay': 'rdelay'} + #'phase': 'rphase', + #'delay': 'rdelay'} MeasurementFunctionMappingDigital = { 'rise_time': 'risetime', 'fall_time': 'falltime', @@ -561,15 +561,21 @@ def _set_acquisition_type(self, value): self._set_cache_valid() def _get_acquisition_number_of_points_minimum(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition_number_of_points_minimum = int(self._ask(":acquire:mdepth?")) + self._set_cache_valid() return self._acquisition_number_of_points_minimum def _set_acquisition_number_of_points_minimum(self, value): value = int(value) + if not self._driver_operation_simulate: + self._write(":acquire:mdepth %d" % value) self._acquisition_number_of_points_minimum = value + self._set_cache_valid() def _get_acquisition_record_length(self): if not self._driver_operation_simulate and not self._get_cache_valid(): - self._acquisition_record_length = int(self._ask(":waveform:points?")) + self._acquisition_record_length = int(self._ask(":acquire:mdepth?")) self._set_cache_valid() return self._acquisition_record_length From 61f4c514905cf4d63fa5043f9f5035e2d960bc53 Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Thu, 6 Jul 2017 08:52:13 -0700 Subject: [PATCH 41/79] fixed caches --- ivi/rigol/rigolBaseScope.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index 5556ee72..008a731e 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -561,9 +561,8 @@ def _set_acquisition_type(self, value): self._set_cache_valid() def _get_acquisition_number_of_points_minimum(self): - if not self._driver_operation_simulate and not self._get_cache_valid(): + if not self._driver_operation_simulate: self._acquisition_number_of_points_minimum = int(self._ask(":acquire:mdepth?")) - self._set_cache_valid() return self._acquisition_number_of_points_minimum def _set_acquisition_number_of_points_minimum(self, value): @@ -571,12 +570,10 @@ def _set_acquisition_number_of_points_minimum(self, value): if not self._driver_operation_simulate: self._write(":acquire:mdepth %d" % value) self._acquisition_number_of_points_minimum = value - self._set_cache_valid() def _get_acquisition_record_length(self): - if not self._driver_operation_simulate and not self._get_cache_valid(): + if not self._driver_operation_simulate: self._acquisition_record_length = int(self._ask(":acquire:mdepth?")) - self._set_cache_valid() return self._acquisition_record_length def _get_acquisition_time_per_record(self): From d25fdd087278bf7c296e211d59e8ac00a5f1db70 Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Thu, 6 Jul 2017 13:29:52 -0700 Subject: [PATCH 42/79] Updated cache for offset --- ivi/rigol/rigolBaseScope.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index 008a731e..81a42cf6 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -724,9 +724,8 @@ def _set_channel_coupling(self, index, value): def _get_channel_offset(self, index): index = ivi.get_index(self._channel_name, index) - if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + if not self._driver_operation_simulate: self._channel_offset[index] = float(self._ask(":%s:offset?" % self._channel_name[index])) - self._set_cache_valid(index=index) return self._channel_offset[index] def _set_channel_offset(self, index, value): @@ -735,7 +734,6 @@ def _set_channel_offset(self, index, value): if not self._driver_operation_simulate: self._write(":%s:offset %e" % (self._channel_name[index], value)) self._channel_offset[index] = value - self._set_cache_valid(index=index) def _get_channel_range(self, index): index = ivi.get_index(self._channel_name, index) From 1a4cb983210480a1ac4da2cd07d844092a43873f Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Mon, 24 Jul 2017 14:27:48 -0700 Subject: [PATCH 43/79] Updated vertical divisions to 8 --- ivi/tektronix/tektronixDPO2000.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ivi/tektronix/tektronixDPO2000.py b/ivi/tektronix/tektronixDPO2000.py index 3a046b0c..0e7d496f 100644 --- a/ivi/tektronix/tektronixDPO2000.py +++ b/ivi/tektronix/tektronixDPO2000.py @@ -38,6 +38,8 @@ def __init__(self, *args, **kwargs): self._digital_channel_count = 0 self._channel_count = self._analog_channel_count + self._digital_channel_count self._bandwidth = 200e6 + self._horizontal_divisions = 10 + self._vertical_divisions = 8 self._identity_description = "Tektronix DPO2000 series IVI oscilloscope driver" self._identity_supported_instrument_models = ['DPO2024', 'DPO2014', 'DPO2012', From 898dda6ed796334249c6aff5a2d60637b14bab54 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Thu, 27 Jul 2017 07:43:48 -0700 Subject: [PATCH 44/79] Support for RigolDS1054Z --- ivi/rigol/__init__.py | 3 ++- ivi/rigol/rigolBaseScope.py | 2 +- ivi/rigol/rigolDS1000Z.py | 2 +- ivi/rigol/rigolDS1054Z.py | 43 +++++++++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 ivi/rigol/rigolDS1054Z.py diff --git a/ivi/rigol/__init__.py b/ivi/rigol/__init__.py index ec486b6a..2c65eabb 100644 --- a/ivi/rigol/__init__.py +++ b/ivi/rigol/__init__.py @@ -26,6 +26,7 @@ # Oscilloscopes # DS1000Z +from .rigolDS1054Z import rigolDS1054Z from .rigolDS1074Z import rigolDS1074Z from .rigolDS1104Z import rigolDS1104Z from .rigolMSO1074Z import rigolMSO1074Z @@ -68,4 +69,4 @@ # Digital Multimeters #DM3068 -from .rigolDM3068Agilent import rigolDM3068Agilent \ No newline at end of file +from .rigolDM3068Agilent import rigolDM3068Agilent diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index 81a42cf6..c1c5f01b 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -190,7 +190,7 @@ def __init__(self, *args, **kwargs): self._identity_instrument_firmware_revision = "" self._identity_specification_major_version = 4 self._identity_specification_minor_version = 1 - self._identity_supported_instrument_models = ['DS1074Z', 'DS1104Z', 'MSO1074Z', + self._identity_supported_instrument_models = ['DS1054Z', 'DS1074Z', 'DS1104Z', 'MSO1074Z', 'MSO1104Z', 'DS2074A', 'DS2104A', 'DS2204A', 'DS2304A', 'MSO2074A', 'MSO2104A', 'MSO2204A', 'MSO2304A'] diff --git a/ivi/rigol/rigolDS1000Z.py b/ivi/rigol/rigolDS1000Z.py index 40b1850a..f394af78 100644 --- a/ivi/rigol/rigolDS1000Z.py +++ b/ivi/rigol/rigolDS1000Z.py @@ -48,7 +48,7 @@ def __init__(self, *args, **kwargs): self._output_count = 2 self._identity_description = "Rigol DS1000Z series IVI oscilloscope driver" - self._identity_supported_instrument_models = ['DS1074Z', 'DS1104Z', 'MSO1074Z', 'MSO1104Z'] + self._identity_supported_instrument_models = ['DS1054Z', 'DS1074Z', 'DS1104Z', 'MSO1074Z', 'MSO1104Z'] self._init_channels() self._init_outputs() diff --git a/ivi/rigol/rigolDS1054Z.py b/ivi/rigol/rigolDS1054Z.py new file mode 100644 index 00000000..4fa19637 --- /dev/null +++ b/ivi/rigol/rigolDS1054Z.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS1000Z import * + +class rigolDS1054Z(rigolDS1000Z): + "Rigol DS1074Z IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS1054Z') + + super(rigolDS1074Z, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 50e6 + + self._init_channels() + From fe28b8b4c354b05be157db6024f5d3fb9d6ba096 Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Thu, 27 Jul 2017 14:58:36 -0700 Subject: [PATCH 45/79] fixed minor bug --- ivi/rigol/rigolDS1054Z.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ivi/rigol/rigolDS1054Z.py b/ivi/rigol/rigolDS1054Z.py index 4fa19637..46bd379b 100644 --- a/ivi/rigol/rigolDS1054Z.py +++ b/ivi/rigol/rigolDS1054Z.py @@ -27,12 +27,12 @@ from .rigolDS1000Z import * class rigolDS1054Z(rigolDS1000Z): - "Rigol DS1074Z IVI oscilloscope driver" + "Rigol DS1054Z IVI oscilloscope driver" def __init__(self, *args, **kwargs): self.__dict__.setdefault('_instrument_id', 'DS1054Z') - super(rigolDS1074Z, self).__init__(*args, **kwargs) + super(rigolDS1054Z, self).__init__(*args, **kwargs) self._analog_channel_count = 4 self._digital_channel_count = 0 From 3d194b051dd3696038e2966fdddae355a879efa4 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Fri, 28 Jul 2017 07:48:03 -0700 Subject: [PATCH 46/79] updated divisions --- ivi/rigol/rigolDS1054Z.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ivi/rigol/rigolDS1054Z.py b/ivi/rigol/rigolDS1054Z.py index 46bd379b..bb227a2d 100644 --- a/ivi/rigol/rigolDS1054Z.py +++ b/ivi/rigol/rigolDS1054Z.py @@ -38,6 +38,8 @@ def __init__(self, *args, **kwargs): self._digital_channel_count = 0 self._channel_count = self._analog_channel_count + self._digital_channel_count self._bandwidth = 50e6 + self._horizontal_divisions = 12 + self._vertical_divisions = 10 self._init_channels() From 54756d541da56df3ef6aa4ce28ec39244b0fa44d Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Mon, 11 Sep 2017 15:59:20 -0700 Subject: [PATCH 47/79] Initial checkin of DPO3014 support. --- ivi/tektronix/tektronixDPO3000.py | 45 +++++++++++++++++++++++++++++++ ivi/tektronix/tektronixDPO3014.py | 42 +++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 ivi/tektronix/tektronixDPO3000.py create mode 100644 ivi/tektronix/tektronixDPO3014.py diff --git a/ivi/tektronix/tektronixDPO3000.py b/ivi/tektronix/tektronixDPO3000.py new file mode 100644 index 00000000..b0899907 --- /dev/null +++ b/ivi/tektronix/tektronixDPO3000.py @@ -0,0 +1,45 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixBaseScope import * + +class tektronixDPO3000(tektronixBaseScope): + "Tektronix DPO3000 series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO3000') + + super(tektronixDPO3000, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 1e9 + + self._identity_description = "Tektronix DPO3000 series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DPO3034', 'DPO3014'] + + self._init_channels() diff --git a/ivi/tektronix/tektronixDPO3014.py b/ivi/tektronix/tektronixDPO3014.py new file mode 100644 index 00000000..fdd07d38 --- /dev/null +++ b/ivi/tektronix/tektronixDPO3014.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixDPO3000 import * + +class tektronixDPO3014(tektronixDPO3000): + "Tektronix DPO3014 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO3014') + + super(tektronixDPO3014, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._bandwidth = 100e6 + self._vertical_divisions = 8 + + From 84b64b0973531f4271405dcb1505c733d070d76a Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 13 Sep 2017 09:28:48 -0700 Subject: [PATCH 48/79] Update for DPO3014 --- ivi/tektronix/__init__.py | 3 +++ ivi/tektronix/tektronixBaseScope.py | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/ivi/tektronix/__init__.py b/ivi/tektronix/__init__.py index 53b89a19..88598feb 100644 --- a/ivi/tektronix/__init__.py +++ b/ivi/tektronix/__init__.py @@ -35,6 +35,9 @@ from .tektronixDPO2012B import tektronixDPO2012B from .tektronixDPO2004B import tektronixDPO2004B from .tektronixDPO2002B import tektronixDPO2002B +# DPO3000 +from .tektronixDPO3014 import tektronixDPO3014 +from .tektronixDPO3034 import tektronixDPO3034 # DPO4000 from .tektronixDPO4032 import tektronixDPO4032 from .tektronixDPO4034 import tektronixDPO4034 diff --git a/ivi/tektronix/tektronixBaseScope.py b/ivi/tektronix/tektronixBaseScope.py index 7ee74f4f..31db4b1a 100644 --- a/ivi/tektronix/tektronixBaseScope.py +++ b/ivi/tektronix/tektronixBaseScope.py @@ -226,19 +226,19 @@ def __init__(self, *args, **kwargs): self._identity_specification_minor_version = 1 self._identity_supported_instrument_models = ['DPO2002B', 'DPO2004B', 'DPO2012', 'DPO2012B', 'DPO2014', 'DPO2014B', 'DPO2022B', 'DPO2024', - 'DPO2024B', 'DPO4032', 'DPO4034', 'DPO4054', 'DPO4104', 'DPO4014B', - 'DPO4034B', 'DPO4054B', 'DPO4102B', 'DPO4104B', 'MSO4032', 'MSO4034', - 'MSO4054', 'MSO4104', 'MSO4014B', 'MSO4034B','MSO4054B', 'MSO4102B', - 'MSO4104B', 'MDO4054', 'MDO4104', 'MDO4014B', 'MDO4034B', 'MDO4054B', - 'MDO4104B', 'MDO3012', 'MDO3014', 'MDO3022', 'MDO3024', 'MDO3032', - 'MDO3034', 'MDO3052', 'MDO3054', 'MDO3102', 'MDO3104', 'MSO2002B', - 'MSO2004B', 'MSO2012', 'MSO2012B', 'MSO2014''MSO2014B', 'MSO2022B', + 'DPO2024B', 'DPO3014', 'DPO3034','DPO4032', 'DPO4034', 'DPO4054', + 'DPO4104', 'DPO4014B','DPO4034B', 'DPO4054B', 'DPO4102B', 'DPO4104B', 'MSO4032', + 'MSO4034', 'MSO4054', 'MSO4104', 'MSO4014B', 'MSO4034B','MSO4054B', 'MSO4102B', + 'MSO4104B', 'MDO4054', 'MDO4104', 'MDO4014B', 'MDO4034B', 'MDO4054B', + 'MDO4104B', 'MDO3012', 'MDO3014', 'MDO3022', 'MDO3024', 'MDO3032', + 'MDO3034', 'MDO3052', 'MDO3054', 'MDO3102', 'MDO3104', 'MSO2002B', + 'MSO2004B', 'MSO2012', 'MSO2012B', 'MSO2014''MSO2014B', 'MSO2022B', 'MSO2024', 'MSO2024B', 'MSO71254C', 'MSO71604C', 'MSO72004C', 'DPO71254C', - 'DPO71604C', 'DPO72004C', 'MSO5034', 'MSO5054', 'MSO5104', 'MSO5204', - 'DPO5054', 'DPO5104', 'DPO5204', 'DPO70804C', 'MSO70804C', 'DPO7254C', + 'DPO71604C', 'DPO72004C', 'MSO5034', 'MSO5054', 'MSO5104', 'MSO5204', + 'DPO5054', 'DPO5104', 'DPO5204', 'DPO70804C', 'MSO70804C', 'DPO7254C', 'DPO7354C', 'DPO70404C', 'DPO70604C', 'MSO70404C', 'MSO70604C', 'DPO7054C', - 'DPO7104C', 'MSO72304DX', 'MSO72504DX', 'MSO73304DX', 'DPO72304DX', - 'DPO72504DX', 'DPO73304DX', 'DPO5034B', 'DPO5054B', 'DPO5104B', 'DPO5204B', + 'DPO7104C', 'MSO72304DX', 'MSO72504DX', 'MSO73304DX', 'DPO72304DX', + 'DPO72504DX', 'DPO73304DX', 'DPO5034B', 'DPO5054B', 'DPO5104B', 'DPO5204B', 'MSO5034B', 'MSO5054B', 'MSO5104B', 'MSO5204B'] self._add_property('channels[].invert', @@ -871,7 +871,7 @@ def _set_channel_scale(self, index, value): self._channel_scale[index] = value self._set_cache_valid(index=index) self._set_cache_valid(False, "channel_offset", index) - + def _get_channel_trigger_level(self, index): index = ivi.get_index(self._channel_name, index) if not self._driver_operation_simulate and not self._get_cache_valid(index=index): @@ -1478,7 +1478,7 @@ def _set_acquisition_number_of_averages(self, value): self._write(":acquire:numavg %d" % value) self._acquisition_number_of_averages = value self._set_cache_valid() - + def _get_trigger_modifier(self): if not self._driver_operation_simulate and not self._get_cache_valid(): value = self._ask(":trigger:a:mode?").lower() From e8f09b5d50ddce0665d6af9f8e86c1157c7d826c Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 13 Sep 2017 09:32:06 -0700 Subject: [PATCH 49/79] added DPO3034 --- ivi/tektronix/tektronixDPO3034.py | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 ivi/tektronix/tektronixDPO3034.py diff --git a/ivi/tektronix/tektronixDPO3034.py b/ivi/tektronix/tektronixDPO3034.py new file mode 100644 index 00000000..35450f9b --- /dev/null +++ b/ivi/tektronix/tektronixDPO3034.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .tektronixDPO3000 import * + +class tektronixDPO3034(tektronixDPO3000): + "Tektronix DPO3034 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DPO3034') + + super(tektronixDPO3014, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._bandwidth = 300e6 + self._vertical_divisions = 8 + + + From 709064c40a84ead5b18ad786c33767e265fde105 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Wed, 13 Sep 2017 10:46:27 -0700 Subject: [PATCH 50/79] fixed super class --- ivi/tektronix/tektronixDPO3034.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi/tektronix/tektronixDPO3034.py b/ivi/tektronix/tektronixDPO3034.py index 35450f9b..4b79cb1c 100644 --- a/ivi/tektronix/tektronixDPO3034.py +++ b/ivi/tektronix/tektronixDPO3034.py @@ -32,7 +32,7 @@ class tektronixDPO3034(tektronixDPO3000): def __init__(self, *args, **kwargs): self.__dict__.setdefault('_instrument_id', 'DPO3034') - super(tektronixDPO3014, self).__init__(*args, **kwargs) + super(tektronixDPO3034, self).__init__(*args, **kwargs) self._analog_channel_count = 4 self._digital_channel_count = 0 From c4b0d3afbca7d9a6e01ef54327f21bbb3f81fb09 Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Wed, 8 Nov 2017 10:20:18 -0800 Subject: [PATCH 51/79] updated vertical divisions --- ivi/rigol/rigolDS1054Z.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi/rigol/rigolDS1054Z.py b/ivi/rigol/rigolDS1054Z.py index bb227a2d..629f6655 100644 --- a/ivi/rigol/rigolDS1054Z.py +++ b/ivi/rigol/rigolDS1054Z.py @@ -39,7 +39,7 @@ def __init__(self, *args, **kwargs): self._channel_count = self._analog_channel_count + self._digital_channel_count self._bandwidth = 50e6 self._horizontal_divisions = 12 - self._vertical_divisions = 10 + self._vertical_divisions = 8 self._init_channels() From 189820123b62825194aff43dd976261bbf0b28cb Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Thu, 9 Aug 2018 20:39:09 -0700 Subject: [PATCH 52/79] Initial commit of MSO9104A and MSO9604A support. --- ivi/agilent/__init__.py | 3 + ivi/agilent/agilent9000.py | 353 ++++++++++++++++++++++++++++ ivi/agilent/agilentBaseInfiniium.py | 100 ++++---- ivi/agilent/agilentMSO9064A.py | 44 ++++ ivi/agilent/agilentMSO9104A.py | 44 ++++ 5 files changed, 494 insertions(+), 50 deletions(-) create mode 100644 ivi/agilent/agilent9000.py create mode 100644 ivi/agilent/agilentMSO9064A.py create mode 100644 ivi/agilent/agilentMSO9104A.py diff --git a/ivi/agilent/__init__.py b/ivi/agilent/__init__.py index 3ccb0bb9..44a9d769 100644 --- a/ivi/agilent/__init__.py +++ b/ivi/agilent/__init__.py @@ -121,6 +121,9 @@ from .agilentMSO7052B import agilentMSO7052B from .agilentMSO7054B import agilentMSO7054B from .agilentMSO7104B import agilentMSO7104B +# Infiniium 9000A +from .agilentMSO9104A import agilentMSO9104A +from .agilentMSO9064A import agilentMSO9064A # Infiniium 90000A from .agilentDSO90254A import agilentDSO90254A from .agilentDSO90404A import agilentDSO90404A diff --git a/ivi/agilent/agilent9000.py b/ivi/agilent/agilent9000.py new file mode 100644 index 00000000..88b6e109 --- /dev/null +++ b/ivi/agilent/agilent9000.py @@ -0,0 +1,353 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2012-2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .agilentBaseInfiniium import * + +AcquisitionModeMapping = { + 'etim': ('normal', 'equivalent_time'), + 'rtim': ('normal', 'real_time'), + 'pdet': ('peak_detect', 'real_time'), + 'hres': ('high_resolution', 'real_time'), + 'segm': ('normal', 'segmented'), + 'segp': ('peak_detect', 'segmented'), + 'segh': ('high_resolution', 'segmented') +} +AcquisitionType = set(['normal', 'peak_detect', 'high_resolution']) +VerticalCoupling = set(['dc']) +ScreenshotImageFormatMapping = { + 'tif': 'tif', + 'tiff': 'tif', + 'bmp': 'bmp', + 'bmp24': 'bmp', + 'png': 'png', + 'png24': 'png', + 'jpg': 'jpg', + 'jpeg': 'jpg', + 'gif': 'gif'} +SampleMode = set(['real_time', 'equivalent_time', 'segmented']) + +class agilent9000(agilentBaseInfiniium): + "Agilent Infiniium 9000A series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + self._analog_channel_name = list() + self._analog_channel_count = 4 + self._digital_channel_name = list() + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._channel_common_mode = list() + self._channel_differential = list() + self._channel_differential_skew = list() + self._channel_display_auto = list() + self._channel_display_offset = list() + self._channel_display_range = list() + self._channel_display_scale = list() + + super(agilent9000, self).__init__(*args, **kwargs) + + self._analog_channel_name = list() + self._analog_channel_count = 4 + self._digital_channel_name = list() + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 4e9 + + self._horizontal_divisions = 10 + self._vertical_divisions = 8 + + self._display_color_grade = False + + self._identity_description = "Agilent Infiniium 9000A series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['MSO9104A', 'MSO9064A'] + + self._add_property('channels[].common_mode', + self._get_channel_common_mode, + self._set_channel_common_mode, + None, + ivi.Doc(""" + Turns on/off common mode for the channel. Channels 2 and 4 may form a + common mode channel and channels 1 and 3 may form a common mode channel. + """)) + self._add_property('channels[].differential', + self._get_channel_differential, + self._set_channel_differential, + None, + ivi.Doc(""" + Turns on/off differential mode for the channel. Channels 2 and 4 may form + a differential channel and channels 1 and 3 may form a differential + channel. + """)) + self._add_property('channels[].differential_skew', + self._get_channel_differential_skew, + self._set_channel_differential_skew, + None, + ivi.Doc(""" + Specifies the skew that is applied to the differential or common mode pair + of channels. Units are seconds. + """)) + self._add_property('channels[].display_auto', + self._get_channel_display_auto, + self._set_channel_display_auto, + None, + ivi.Doc(""" + Sets the differential and common mode display scale and offset to track + the acquisition scale and offset. + """)) + self._add_property('channels[].display_offset', + self._get_channel_display_offset, + self._set_channel_display_offset, + None, + ivi.Doc(""" + Sets the displayed offset of the selected channel. Setting this parameter + disables display_auto. Units are volts. + """)) + self._add_property('channels[].display_range', + self._get_channel_display_range, + self._set_channel_display_range, + None, + ivi.Doc(""" + Sets the full scale vertical range of the selected channel. Setting this + parameter disables display_auto. Units are volts. + """)) + self._add_property('channels[].display_scale', + self._get_channel_display_scale, + self._set_channel_display_scale, + None, + ivi.Doc(""" + Sets the displayed scale of the selected channel per division. Setting + this parameter disables display_auto. Units are volts. + """)) + + self._init_channels() + + + def _utility_error_query(self): + error_code = 0 + error_message = "No error" + if not self._driver_operation_simulate: + error_code = self._ask(":system:error?") + error_code = int(error_code) + if error_code != 0: + error_message = "Unknown" + return (error_code, error_message) + + def _init_channels(self): + try: + super(agilent9000, self)._init_channels() + except AttributeError: + pass + + self._channel_common_mode = list() + self._channel_differential = list() + self._channel_differential_skew = list() + self._channel_display_auto = list() + self._channel_display_offset = list() + self._channel_display_range = list() + self._channel_display_scale = list() + + for i in range(self._analog_channel_count): + self._channel_common_mode.append(False) + self._channel_differential.append(False) + self._channel_differential_skew.append(0) + self._channel_display_auto.append(True) + self._channel_display_offset.append(0.0) + self._channel_display_range.append(1.0) + self._channel_display_scale.append(0.1) + + + def _display_fetch_screenshot(self, format='png', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in ScreenshotImageFormatMapping: + raise ivi.ValueNotSupportedException() + + format = ScreenshotImageFormatMapping[format] + + self._write(":display:data? %s, screen, on, %s" % (format, 'invert' if invert else 'normal')) + + return self._read_ieee_block() + + def _get_channel_common_mode(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_common_mode[index] = bool(int(self._ask(":%s:commonmode?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_common_mode[index] + + def _set_channel_common_mode(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:commonmode %d" % (self._channel_name[index], int(value))) + self._channel_common_mode[index] = value + self._set_cache_valid(index=index) + + def _get_channel_differential(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_differential[index] = bool(int(self._ask(":%s:differential?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_differential[index] + + def _set_channel_differential(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:differential %d" % (self._channel_name[index], int(value))) + self._channel_differential[index] = value + self._set_cache_valid(index=index) + + def _get_channel_differential_skew(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_differential_skew[index] = float(self._ask(":%s:differential:skew?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_differential_skew[index] + + def _set_channel_differential_skew(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:differential:skew %e" % (self._channel_name[index], value)) + self._channel_differential_skew[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_auto(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_auto[index] = bool(int(self._ask(":%s:display:auto?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_display_auto[index] + + def _set_channel_display_auto(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:display:auto %d" % (self._channel_name[index], int(value))) + self._channel_display_auto[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_offset(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_offset[index] = float(self._ask(":%s:display:offset?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_display_offset[index] + + def _set_channel_display_offset(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:display:offset %e" % (self._channel_name[index], value)) + self._channel_display_offset[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_range(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_range[index] = float(self._ask(":%s:display:range?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_display_range[index] + + def _set_channel_display_range(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:display:range %e" % (self._channel_name[index], value)) + self._channel_display_range[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_scale(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_scale[index] = float(self._ask(":%s:display:scale?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_display_scale[index] + + def _set_channel_display_scale(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:display:scale %e" % (self._channel_name[index], value)) + self._channel_display_scale[index] = value + self._set_cache_valid(index=index) + + def _measurement_fetch_waveform(self, index): + index = ivi.get_index(self._channel_name, index) + + if self._driver_operation_simulate: + return list() + + if sys.byteorder == 'little': + self._write(":waveform:byteorder lsbfirst") + else: + self._write(":waveform:byteorder msbfirst") + self._write(":waveform:format word") + self._write(":waveform:streaming on") + self._write(":waveform:source %s" % self._channel_name[index]) + + # Read preamble + + pre = self._ask(":waveform:preamble?").split(',') + + format = int(pre[0]) + type = int(pre[1]) + points = int(pre[2]) + count = int(pre[3]) + xincrement = float(pre[4]) + xorigin = float(pre[5]) + xreference = int(float(pre[6])) + yincrement = float(pre[7]) + yorigin = float(pre[8]) + yreference = int(float(pre[9])) + + if type == 1: + raise scope.InvalidAcquisitionTypeException() + + if format != 2: + raise UnexpectedResponseException() + + # Read waveform data + raw_data = self._ask_for_ieee_block(":waveform:data?") + + # Split out points and convert to time and voltage pairs + y_data = array.array('h', raw_data[0:points*2]) + + data = [(((i - xreference) * xincrement) + xorigin, float('nan') if y == 31232 else ((y - yreference) * yincrement) + yorigin) for i, y in enumerate(y_data)] + + return data + + def _measurement_read_waveform(self, index, maximum_time): + return self._measurement_fetch_waveform(index) + + def _measurement_initiate(self): + if not self._driver_operation_simulate: + self._write(":acquire:complete 100") + self._write(":digitize") + self._set_cache_valid(False, 'trigger_continuous') + diff --git a/ivi/agilent/agilentBaseInfiniium.py b/ivi/agilent/agilentBaseInfiniium.py index 1dcf6c6f..13f5bd4c 100644 --- a/ivi/agilent/agilentBaseInfiniium.py +++ b/ivi/agilent/agilentBaseInfiniium.py @@ -51,7 +51,7 @@ class agilentBaseInfiniium(agilentBaseScope): "Agilent Infiniium series IVI oscilloscope driver" - + def __init__(self, *args, **kwargs): self.__dict__.setdefault('_instrument_id', '') self._analog_channel_name = list() @@ -66,37 +66,37 @@ def __init__(self, *args, **kwargs): self._channel_display_offset = list() self._channel_display_range = list() self._channel_display_scale = list() - + super(agilentBaseInfiniium, self).__init__(*args, **kwargs) - + self._analog_channel_name = list() self._analog_channel_count = 4 self._digital_channel_name = list() self._digital_channel_count = 16 self._channel_count = self._analog_channel_count + self._digital_channel_count self._bandwidth = 13e9 - + self._horizontal_divisions = 10 self._vertical_divisions = 8 self._display_screenshot_image_format_mapping = ScreenshotImageFormatMapping self._display_color_grade = False - + self._identity_description = "Agilent Infiniium series IVI oscilloscope driver" - self._identity_supported_instrument_models = ['DSO90254A','DSO90404A','DSO90604A', - 'DSO90804A','DSO91204A','DSO91304A','DSOX91304A','DSOX91604A','DSOX92004A', - 'DSOX92504A','DSOX92804A','DSOX93204A','DSA90254A','DSA90404A','DSA90604A', - 'DSA90804A','DSA91204A','DSA91304A','DSAX91304A','DSAX91604A','DSAX92004A', - 'DSAX92504A','DSAX92804A','DSAX93204A','MSOX91304A','MSOX91604A','MSOX92004A', - 'MSOX92504A','MSOX92804A','MSOX93204A'] - + self._identity_supported_instrument_models = ['MSO9104A', 'MSO9064A','DSO90254A', + 'DSO90404A','DSO90604A', 'DSO90804A','DSO91204A','DSO91304A','DSOX91304A', + 'DSOX91604A','DSOX92004A', 'DSOX92504A','DSOX92804A','DSOX93204A','DSA90254A', + 'DSA90404A','DSA90604A', 'DSA90804A','DSA91204A','DSA91304A','DSAX91304A', + 'DSAX91604A','DSAX92004A', 'DSAX92504A','DSAX92804A','DSAX93204A','MSOX91304A', + 'MSOX91604A','MSOX92004A', 'MSOX92504A','MSOX92804A','MSOX93204A'] + self._add_property('display.color_grade', self._get_display_color_grade, self._set_display_color_grade, None, ivi.Doc(""" Controls color grade persistance. - + When in the color grade persistance mode, all waveforms are mapped into a database and shown with different colors representing varying number of hits in a pixel. Vector display mode is disabled when color grade is @@ -108,7 +108,7 @@ def __init__(self, *args, **kwargs): Returns the range of hits represented by each color. Fourteen values are returned, representing the minimum and maximum count for each of seven colors. The values are returned in the following order: - + * White minimum value * White maximum value * Yellow minimum value @@ -124,10 +124,10 @@ def __init__(self, *args, **kwargs): * Green minimum value * Green maximum value """)) - + self._init_channels() - - + + def _utility_error_query(self): error_code = 0 error_message = "No error" @@ -137,68 +137,68 @@ def _utility_error_query(self): if error_code != 0: error_message = "Unknown" return (error_code, error_message) - + def _init_channels(self): try: super(agilentBaseInfiniium, self)._init_channels() except AttributeError: pass - + # currently no additional parameters - - + + def _display_fetch_screenshot(self, format='png', invert=False): if self._driver_operation_simulate: return b'' - + if format not in self._display_screenshot_image_format_mapping: raise ivi.ValueNotSupportedException() - + format = self._display_screenshot_image_format_mapping[format] - + self._write(":display:data? %s, screen, on, %s" % (format, 'invert' if invert else 'normal')) - + return self._read_ieee_block() - + def _get_display_vectors(self): if not self._driver_operation_simulate and not self._get_cache_valid(): self._display_vectors = bool(int(self._ask(":display:connect?"))) self._set_cache_valid() return self._display_vectors - + def _set_display_vectors(self, value): value = bool(value) if not self._driver_operation_simulate: self._write(":display:connect %d" % int(value)) self._display_vectors = value self._set_cache_valid() - + def _get_display_color_grade(self): if not self._driver_operation_simulate and not self._get_cache_valid(): self._display_color_grade = bool(int(self._ask(":display:cgrade?"))) self._set_cache_valid() return self._display_color_grade - + def _set_display_color_grade(self, value): value = bool(value) if not self._driver_operation_simulate: self._write(":display:cgrade %d" % int(value)) self._display_color_grade = value self._set_cache_valid() - + def _fetch_display_color_grade_levels(self): if self._driver_operation_simulate(): return [0]*14 - + lst = self._ask(":display:cgrade:levels?").split(',') return [int(x) for x in lst] - + def _get_channel_input_impedance(self, index): index = ivi.get_index(self._analog_channel_name, index) # fixed self._channel_input_impedance[index] = 50 return self._channel_input_impedance[index] - + def _set_channel_input_impedance(self, index, value): value = float(value) index = ivi.get_index(self._analog_channel_name, index) @@ -206,24 +206,24 @@ def _set_channel_input_impedance(self, index, value): raise Exception('Invalid impedance selection') self._channel_input_impedance[index] = value self._set_cache_valid(index=index) - + def _measurement_fetch_waveform(self, index): index = ivi.get_index(self._channel_name, index) - + if self._driver_operation_simulate: return list() - + if sys.byteorder == 'little': self._write(":waveform:byteorder lsbfirst") else: self._write(":waveform:byteorder msbfirst") self._write(":waveform:format word") self._write(":waveform:source %s" % self._channel_name[index]) - + # Read preamble - + pre = self._ask(":waveform:preamble?").split(',') - + format = int(pre[0]) type = int(pre[1]) points = int(pre[2]) @@ -234,26 +234,26 @@ def _measurement_fetch_waveform(self, index): yincrement = float(pre[7]) yorigin = float(pre[8]) yreference = int(float(pre[9])) - + #if type == 1: # raise scope.InvalidAcquisitionTypeException() - + if format != 2: raise UnexpectedResponseException() - + # Read waveform data raw_data = self._ask_for_ieee_block(":waveform:data?") - + # Split out points and convert to time and voltage pairs y_data = array.array('h', raw_data[0:points*2]) - + data = [(((i - xreference) * xincrement) + xorigin, float('nan') if y == 31232 else ((y - yreference) * yincrement) + yorigin) for i, y in enumerate(y_data)] - + return data - + def _measurement_read_waveform(self, index, maximum_time): return self._measurement_fetch_waveform(index) - + def _measurement_initiate(self): if not self._driver_operation_simulate: self._write(":acquire:complete 100") @@ -297,16 +297,16 @@ def _set_acquisition_mode(self, t, value): def _get_acquisition_type(self): self._get_acquisition_mode() return self._acquisition_type - + def _set_acquisition_type(self, value): if value not in AcquisitionType: raise ivi.ValueNotSupportedException() self._set_acquisition_mode('type', value) - + def _get_acquisition_sample_mode(self): self._get_acquisition_mode() return self._acquisition_sample_mode - + def _set_acquisition_sample_mode(self, value): if value not in SampleMode: raise ivi.ValueNotSupportedException() diff --git a/ivi/agilent/agilentMSO9064A.py b/ivi/agilent/agilentMSO9064A.py new file mode 100644 index 00000000..7711244e --- /dev/null +++ b/ivi/agilent/agilentMSO9064A.py @@ -0,0 +1,44 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2012-2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .agilent9000 import * + +class agilentMSO9064A(agilent9000): + "Agilent Infiniium MSO9064A IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO9064A') + + super(agilentMSO9064A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 6e8 + + self._init_channels() + + diff --git a/ivi/agilent/agilentMSO9104A.py b/ivi/agilent/agilentMSO9104A.py new file mode 100644 index 00000000..371ad6e1 --- /dev/null +++ b/ivi/agilent/agilentMSO9104A.py @@ -0,0 +1,44 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2012-2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .agilent9000 import * + +class agilentMSO9104A(agilent9000): + "Agilent Infiniium MSO9104A IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO9104A') + + super(agilentMSO9104A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 1e9 + + self._init_channels() + + From 34e7221179f3156a8011e09b5a0cfe269d9ea0c5 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Sat, 8 Sep 2018 12:39:37 -0700 Subject: [PATCH 53/79] added timebase reference to infiniuum base --- ivi/agilent/agilentBaseInfiniium.py | 114 +++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) diff --git a/ivi/agilent/agilentBaseInfiniium.py b/ivi/agilent/agilentBaseInfiniium.py index 13f5bd4c..80f8cb89 100644 --- a/ivi/agilent/agilentBaseInfiniium.py +++ b/ivi/agilent/agilentBaseInfiniium.py @@ -48,6 +48,11 @@ 'jpeg': 'jpg', 'gif': 'gif'} SampleMode = set(['real_time', 'equivalent_time', 'segmented']) +TimebaseReferenceMapping = { + 'left': 'left', + 'center': 'cent', + 'right': 'righ', + 'percent': 'perc'} class agilentBaseInfiniium(agilentBaseScope): "Agilent Infiniium series IVI oscilloscope driver" @@ -193,10 +198,34 @@ def _fetch_display_color_grade_levels(self): lst = self._ask(":display:cgrade:levels?").split(',') return [int(x) for x in lst] + def _get_channel_coupling(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_coupling[index] = self._ask(":%s:input?" % self._channel_name[index]).lower() + self._set_cache_valid(index=index) + return self._channel_coupling[index] + + def _set_channel_coupling(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + if value not in VerticalCoupling: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":%s:input %s" % (self._channel_name[index], value)) + self._channel_coupling[index] = value + self._set_cache_valid(index=index) + def _get_channel_input_impedance(self, index): index = ivi.get_index(self._analog_channel_name, index) - # fixed - self._channel_input_impedance[index] = 50 + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + val = self._ask(":%s:input?" % self._channel_name[index]).lower() + if val == 'dc': + val == '1M' + elif val == 'DC50': + val == '50' + elif val == 'AC': + val == '1M' + self._channel_coupling[index] = val + self._set_cache_valid(index=index) return self._channel_input_impedance[index] def _set_channel_input_impedance(self, index, value): @@ -207,6 +236,87 @@ def _set_channel_input_impedance(self, index, value): self._channel_input_impedance[index] = value self._set_cache_valid(index=index) + def _get_timebase_reference(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":timebase:reference?").lower() + self._timebase_reference = [k for k,v in TimebaseReferenceMapping.items() if v==value][0] + if self._timebase_reference == 'percent': + self._timebase_reference = float(self._ask(":timebase:reference:percent?")) + self._set_cache_valid() + return self._timebase_reference + + def _get_trigger_coupling(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._trigger_coupling = self._ask(":trigger:edge:coupling?") + return self._trigger_coupling + + def _set_trigger_coupling(self, value): + if value not in TriggerCouplingMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + cpl, noise, hf = TriggerCouplingMapping[value] + self._write(":trigger:coupling %s" % cpl) + self._write(":trigger:nreject %d" % noise) + self._write(":trigger:hfreject %d" % hf) + self._trigger_coupling = value + self._set_cache_valid() + + def _get_trigger_source(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:edge:source?").lower() + # TODO process value + self._trigger_source = value + self._set_cache_valid() + return self._trigger_source + + def _set_trigger_source(self, value): + if hasattr(value, 'name'): + value = value.name + value = str(value) + if value not in self._channel_name: + raise ivi.UnknownPhysicalNameException() + if not self._driver_operation_simulate: + self._write(":trigger:edge:source %s" % value) + self._trigger_source = value + self._set_cache_valid() + + def _get_trigger_type(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:mode?").lower() + self._trigger_type = value + self._set_cache_valid() + return self._trigger_type + + def _set_trigger_type(self, value): + if value not in TriggerTypeMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":trigger:mode %s" % TriggerTypeMapping[value]) + if value == 'ac_line': + self._write(":trigger:source line") + if value == 'glitch': + qual = self._ask(":trigger:glitch:qualifier?").lower() + if qual not in GlitchConditionMapping.values(): + self._write(":trigger:glitch:qualifier %s" % GlitchConditionMapping[self._trigger_glitch_condition]) + if value == 'width': + self._write(":trigger:glitch:qualifier range") + self._trigger_type = value + self._set_cache_valid() + + def _get_acquisition_number_of_averages(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition_number_of_averages = int(self._ask(":acquire:average:count?")) + self._set_cache_valid() + return self._acquisition_number_of_averages + + def _set_acquisition_number_of_averages(self, value): + if value < 1 or value > 65536: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":acquire:average:count %d" % value) + self._acquisition_number_of_averages = value + self._set_cache_valid() + def _measurement_fetch_waveform(self, index): index = ivi.get_index(self._channel_name, index) From 409c142a762452a3b2b9b408942324e94878a5ae Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Tue, 11 Sep 2018 13:01:12 -0700 Subject: [PATCH 54/79] digital waveform fetch --- ivi/agilent/agilentMSO9064A.py | 39 +++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/ivi/agilent/agilentMSO9064A.py b/ivi/agilent/agilentMSO9064A.py index 7711244e..1e26bcd8 100644 --- a/ivi/agilent/agilentMSO9064A.py +++ b/ivi/agilent/agilentMSO9064A.py @@ -30,7 +30,8 @@ class agilentMSO9064A(agilent9000): "Agilent Infiniium MSO9064A IVI oscilloscope driver" def __init__(self, *args, **kwargs): - self.__dict__.setdefault('_instrument_id', 'MSO9064A') + cls = 'IviScope' + grp = 'Base' super(agilentMSO9064A, self).__init__(*args, **kwargs) @@ -40,5 +41,41 @@ def __init__(self, *args, **kwargs): self._bandwidth = 6e8 self._init_channels() + self._add_method('measurement.fetch_waveform_digital', self._measurement_fetch_waveform_digital, ivi.Doc("""description goes here""", cls, grp, '4.3.13')) + + + def _measurement_fetch_waveform_digital(self, index): + # tbdecided how to specify # of channels for the server and JSON + raw_data = [] + #index = TODO + + + if self._driver_operation_simulate: + return list() + + self._write(":waveform:byteorder msbfirst") + self._write(":waveform:format ascii") + self._write(":waveform:source %s" % index) + + # Read preamble + pre = self._ask(":waveform:preamble?").split(',') + + xinc = float(pre[4]) + xorg = float(pre[5]) + xref = int(float(pre[6])) + +# if format != 0: +# raise UnexpectedResponseException() + + # Read waveform data + raw_data.append(self._ask(':WAVeform:DATA?')) + + # convert string of hex values to list of hex strings + data_list = raw_data[0].split(",") + + # convert to times + data = [((((k-xref)*xinc) + xorg), e) for k,e in enumerate(data_list)] + + return data From c13777d4099739800babd426815aa6a1b030d502 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Tue, 11 Sep 2018 13:02:01 -0700 Subject: [PATCH 55/79] cleaned up text --- ivi/agilent/agilentMSO9064A.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/ivi/agilent/agilentMSO9064A.py b/ivi/agilent/agilentMSO9064A.py index 1e26bcd8..bf8ce9ec 100644 --- a/ivi/agilent/agilentMSO9064A.py +++ b/ivi/agilent/agilentMSO9064A.py @@ -45,10 +45,7 @@ def __init__(self, *args, **kwargs): def _measurement_fetch_waveform_digital(self, index): - # tbdecided how to specify # of channels for the server and JSON raw_data = [] - #index = TODO - if self._driver_operation_simulate: return list() From 0272e3ef1da904c5f40e2c9f0441c1dd6131b126 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Tue, 18 Sep 2018 12:41:08 -0700 Subject: [PATCH 56/79] support AUTO in mdepth --- ivi/rigol/rigolBaseScope.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index c1c5f01b..b67384ca 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -562,7 +562,11 @@ def _set_acquisition_type(self, value): def _get_acquisition_number_of_points_minimum(self): if not self._driver_operation_simulate: - self._acquisition_number_of_points_minimum = int(self._ask(":acquire:mdepth?")) + value = self._ask(":acquire:mdepth?") + if value == 'AUTO': + self._acquisition_number_of_points_minimum = value + else: + self._acquisition_number_of_points_minimum = int(value) return self._acquisition_number_of_points_minimum def _set_acquisition_number_of_points_minimum(self, value): From f9ece1f7e7f8a9f52bc66298fa939450c249ddc3 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Tue, 25 Sep 2018 12:50:35 -0700 Subject: [PATCH 57/79] added analog sample rate --- ivi/agilent/agilentMSO9064A.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ivi/agilent/agilentMSO9064A.py b/ivi/agilent/agilentMSO9064A.py index bf8ce9ec..385e7109 100644 --- a/ivi/agilent/agilentMSO9064A.py +++ b/ivi/agilent/agilentMSO9064A.py @@ -42,7 +42,25 @@ def __init__(self, *args, **kwargs): self._init_channels() self._add_method('measurement.fetch_waveform_digital', self._measurement_fetch_waveform_digital, ivi.Doc("""description goes here""", cls, grp, '4.3.13')) - + self._add_property('acquisition.analog.sample_rate', + self._get_acquisition_analog_sample_rate, + self._set_acquisition_analog_sample_rate, + None, + None, + ivi.Doc(""" + Returns or sets the effective sample rate of the acquired analog waveform using the + current configuration. The units are samples per second. + """, cls, grp, '4.2.10')) + + def _get_acquisition_analog_sample_rate(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition__analog_sample_rate = int(self._ask(":acquire:srate:analog?")) + self._set_cache_valid() + return self._acquisition__analog_sample_rate + + def _set_acquisition_analog_sample_rate(self, value): + value = float(value) + self._acquisition_analog_sample_rate = value def _measurement_fetch_waveform_digital(self, index): raw_data = [] From f92b05e9c32d1fb09d632900e85c69475debec1b Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Mon, 1 Oct 2018 13:03:57 +0800 Subject: [PATCH 58/79] 'getpointsmin' --- ivi/agilent/agilentBaseInfiniium.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ivi/agilent/agilentBaseInfiniium.py b/ivi/agilent/agilentBaseInfiniium.py index 80f8cb89..a24045b3 100644 --- a/ivi/agilent/agilentBaseInfiniium.py +++ b/ivi/agilent/agilentBaseInfiniium.py @@ -273,8 +273,8 @@ def _set_trigger_source(self, value): if hasattr(value, 'name'): value = value.name value = str(value) - if value not in self._channel_name: - raise ivi.UnknownPhysicalNameException() + # if value not in self._channel_name: + # raise ivi.UnknownPhysicalNameException() if not self._driver_operation_simulate: self._write(":trigger:edge:source %s" % value) self._trigger_source = value @@ -422,3 +422,13 @@ def _set_acquisition_sample_mode(self, value): raise ivi.ValueNotSupportedException() self._set_acquisition_mode('mode', value) + def _get_acquisition_number_of_points_minimum(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition_number_of_points_minimum = int(self._ask(":ACQuire:POINts:ANALog?")) + return self._acquisition_number_of_points_minimum + + def _set_acquisition_number_of_points_minimum(self, value): + if not self._driver_operation_simulate: + self._write(":ACQuire:POINts:ANALog %s" % value) + self._acquisition_number_of_points_minimum = value + self._set_cache_valid() \ No newline at end of file From 99e4c476907dd4c05a000f6defc942918d48fb00 Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Mon, 1 Oct 2018 16:05:49 +0800 Subject: [PATCH 59/79] 'coupling' --- ivi/agilent/agilentBaseInfiniium.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ivi/agilent/agilentBaseInfiniium.py b/ivi/agilent/agilentBaseInfiniium.py index a24045b3..bce08126 100644 --- a/ivi/agilent/agilentBaseInfiniium.py +++ b/ivi/agilent/agilentBaseInfiniium.py @@ -254,10 +254,7 @@ def _set_trigger_coupling(self, value): if value not in TriggerCouplingMapping: raise ivi.ValueNotSupportedException() if not self._driver_operation_simulate: - cpl, noise, hf = TriggerCouplingMapping[value] - self._write(":trigger:coupling %s" % cpl) - self._write(":trigger:nreject %d" % noise) - self._write(":trigger:hfreject %d" % hf) + self._write(":trigger:edge:coupling %s" % value) self._trigger_coupling = value self._set_cache_valid() From 23ffff69e61691d707fc8903dec97d54424932e7 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Tue, 2 Oct 2018 08:12:37 -0700 Subject: [PATCH 60/79] wip --- ivi/agilent/agilentBaseInfiniium.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ivi/agilent/agilentBaseInfiniium.py b/ivi/agilent/agilentBaseInfiniium.py index bce08126..5344fcfe 100644 --- a/ivi/agilent/agilentBaseInfiniium.py +++ b/ivi/agilent/agilentBaseInfiniium.py @@ -300,6 +300,19 @@ def _set_trigger_type(self, value): self._trigger_type = value self._set_cache_valid() + def _get_averaging_enabled(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":acquire:average?") + self._averaging_enabled = value + self._set_cache_valid() + return self._averaging_enabled + + def _set_averaging_enabled(self, value): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._write(":acquire:average %s" % value) + self._averaging_enabled = value + self._set_cache_valid() + def _get_acquisition_number_of_averages(self): if not self._driver_operation_simulate and not self._get_cache_valid(): self._acquisition_number_of_averages = int(self._ask(":acquire:average:count?")) @@ -423,7 +436,7 @@ def _get_acquisition_number_of_points_minimum(self): if not self._driver_operation_simulate and not self._get_cache_valid(): self._acquisition_number_of_points_minimum = int(self._ask(":ACQuire:POINts:ANALog?")) return self._acquisition_number_of_points_minimum - + def _set_acquisition_number_of_points_minimum(self, value): if not self._driver_operation_simulate: self._write(":ACQuire:POINts:ANALog %s" % value) From a19b282422f199bfc4f261d4180add95cb0270b1 Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Tue, 2 Oct 2018 15:17:44 +0800 Subject: [PATCH 61/79] 'updates_for_9064' --- ivi/agilent/agilent9000.py | 4 ++-- ivi/agilent/agilentBaseInfiniium.py | 26 ++++++++++++++++---------- ivi/agilent/agilentMSO9064A.py | 5 ++--- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/ivi/agilent/agilent9000.py b/ivi/agilent/agilent9000.py index 88b6e109..a455d951 100644 --- a/ivi/agilent/agilent9000.py +++ b/ivi/agilent/agilent9000.py @@ -326,8 +326,8 @@ def _measurement_fetch_waveform(self, index): yorigin = float(pre[8]) yreference = int(float(pre[9])) - if type == 1: - raise scope.InvalidAcquisitionTypeException() + # if type == 1: + # raise scope.InvalidAcquisitionTypeException() if format != 2: raise UnexpectedResponseException() diff --git a/ivi/agilent/agilentBaseInfiniium.py b/ivi/agilent/agilentBaseInfiniium.py index 5344fcfe..ba3b503e 100644 --- a/ivi/agilent/agilentBaseInfiniium.py +++ b/ivi/agilent/agilentBaseInfiniium.py @@ -129,7 +129,15 @@ def __init__(self, *args, **kwargs): * Green minimum value * Green maximum value """)) - + self._add_property('acquisition.averaging_enabled', + self._get_acquisition_averaging_enabled, + self._set_acquisition_averaging_enabled, + None, + ivi.Doc(""" + The acquisition averaging control allows Infiniium to acquire + waveform data from several acquisitions and then average them all together. + When enabled, the channel data is averaged before being displayed. + """)) self._init_channels() @@ -300,18 +308,16 @@ def _set_trigger_type(self, value): self._trigger_type = value self._set_cache_valid() - def _get_averaging_enabled(self): - if not self._driver_operation_simulate and not self._get_cache_valid(): + def _get_acquisition_averaging_enabled(self): + if not self._driver_operation_simulate: value = self._ask(":acquire:average?") - self._averaging_enabled = value - self._set_cache_valid() - return self._averaging_enabled + self._acquisition_averaging_enabled = value + return self._acquisition_averaging_enabled - def _set_averaging_enabled(self, value): - if not self._driver_operation_simulate and not self._get_cache_valid(): + def _set_acquisition_averaging_enabled(self, value): + if not self._driver_operation_simulate: self._write(":acquire:average %s" % value) - self._averaging_enabled = value - self._set_cache_valid() + self._acquisition_averaging_enabled = value def _get_acquisition_number_of_averages(self): if not self._driver_operation_simulate and not self._get_cache_valid(): diff --git a/ivi/agilent/agilentMSO9064A.py b/ivi/agilent/agilentMSO9064A.py index 385e7109..9efc63c5 100644 --- a/ivi/agilent/agilentMSO9064A.py +++ b/ivi/agilent/agilentMSO9064A.py @@ -42,11 +42,10 @@ def __init__(self, *args, **kwargs): self._init_channels() self._add_method('measurement.fetch_waveform_digital', self._measurement_fetch_waveform_digital, ivi.Doc("""description goes here""", cls, grp, '4.3.13')) - self._add_property('acquisition.analog.sample_rate', + self._add_property('acquisition.analog_sample_rate', self._get_acquisition_analog_sample_rate, self._set_acquisition_analog_sample_rate, None, - None, ivi.Doc(""" Returns or sets the effective sample rate of the acquired analog waveform using the current configuration. The units are samples per second. @@ -54,7 +53,7 @@ def __init__(self, *args, **kwargs): def _get_acquisition_analog_sample_rate(self): if not self._driver_operation_simulate and not self._get_cache_valid(): - self._acquisition__analog_sample_rate = int(self._ask(":acquire:srate:analog?")) + self._acquisition__analog_sample_rate = self._ask(":acquire:srate:analog?") self._set_cache_valid() return self._acquisition__analog_sample_rate From 8105d8064503725dde781f0378d75db58defaecb Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Thu, 4 Oct 2018 06:34:38 -0600 Subject: [PATCH 62/79] updated_mso9104A --- ivi/agilent/agilentMSO9104A.py | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/ivi/agilent/agilentMSO9104A.py b/ivi/agilent/agilentMSO9104A.py index 371ad6e1..69e609df 100644 --- a/ivi/agilent/agilentMSO9104A.py +++ b/ivi/agilent/agilentMSO9104A.py @@ -40,5 +40,55 @@ def __init__(self, *args, **kwargs): self._bandwidth = 1e9 self._init_channels() + self._add_method('measurement.fetch_waveform_digital', self._measurement_fetch_waveform_digital, ivi.Doc("""description goes here""", cls, grp, '4.3.13')) + self._add_property('acquisition.analog_sample_rate', + self._get_acquisition_analog_sample_rate, + self._set_acquisition_analog_sample_rate, + None, + ivi.Doc(""" + Returns or sets the effective sample rate of the acquired analog waveform using the + current configuration. The units are samples per second. + """, cls, grp, '4.2.10')) + + def _get_acquisition_analog_sample_rate(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition__analog_sample_rate = self._ask(":acquire:srate:analog?") + self._set_cache_valid() + return self._acquisition__analog_sample_rate + + def _set_acquisition_analog_sample_rate(self, value): + value = float(value) + self._acquisition_analog_sample_rate = value + + def _measurement_fetch_waveform_digital(self, index): + raw_data = [] + + if self._driver_operation_simulate: + return list() + + self._write(":waveform:byteorder msbfirst") + self._write(":waveform:format ascii") + self._write(":waveform:source %s" % index) + + # Read preamble + pre = self._ask(":waveform:preamble?").split(',') + + xinc = float(pre[4]) + xorg = float(pre[5]) + xref = int(float(pre[6])) + +# if format != 0: +# raise UnexpectedResponseException() + + # Read waveform data + raw_data.append(self._ask(':WAVeform:DATA?')) + + # convert string of hex values to list of hex strings + data_list = raw_data[0].split(",") + + # convert to times + data = [((((k-xref)*xinc) + xorg), e) for k,e in enumerate(data_list)] + + return data From ddcafadd03144ac442f8e62b902967730350f025 Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Mon, 15 Oct 2018 09:14:29 -0700 Subject: [PATCH 63/79] samplerate --- ivi/rigol/rigolBaseScope.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index b67384ca..9796960e 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -308,6 +308,13 @@ def __init__(self, *args, **kwargs): oscilloscope is running, all the data in active channels and functions is erased; however, new data is displayed on the next acquisition. """)) + self._add_property('acquisition.analog_sample_rate', + self._get_acquisition_analog_sample_rate, + None, + ivi.Doc(""" + Returns or sets the effective sample rate of the acquired analog waveform using the + current configuration. The units are samples per second. + """,)) self._init_channels() @@ -594,6 +601,12 @@ def _set_acquisition_time_per_record(self, value): self._set_cache_valid() self._set_cache_valid(False, 'acquisition_start_time') + def _get_acquisition_analog_sample_rate(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition__analog_sample_rate = self._ask(":acquire:srate?") + self._set_cache_valid() + return self._acquisition__analog_sample_rate + def _get_channel_label(self, index): index = ivi.get_index(self._channel_name, index) if not self._driver_operation_simulate and not self._get_cache_valid(index=index): From 1396b3633f681f61da7c931ee43252ba6897d4dd Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Wed, 17 Oct 2018 10:26:22 -0700 Subject: [PATCH 64/79] updated start time and scale --- ivi/rigol/rigolBaseScope.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index 9796960e..fd75470f 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -468,7 +468,7 @@ def _set_timebase_range(self, value): def _get_timebase_scale(self): if not self._driver_operation_simulate and not self._get_cache_valid(): - self._timebase_scale = float(self._ask(":timebase:scale?")) + self._timebase_scale = float(self._ask(":timebase:main:scale?")) self._timebase_range = self._timebase_scale * self._horizontal_divisions self._set_cache_valid() return self._timebase_scale @@ -476,7 +476,7 @@ def _get_timebase_scale(self): def _set_timebase_scale(self, value): value = float(value) if not self._driver_operation_simulate: - self._write(":timebase:scale %e" % value) + self._write(":timebase:main:scale %e" % value) self._timebase_scale = value self._timebase_range = value * self._horizontal_divisions self._set_cache_valid() @@ -539,7 +539,7 @@ def _display_clear(self): def _get_acquisition_start_time(self): pos = 0 if not self._driver_operation_simulate and not self._get_cache_valid(): - pos = float(self._ask(":timebase:offset?")) + pos = float(self._ask(":timebase:main:offset?")) self._set_cache_valid() self._acquisition_start_time = pos - self._get_acquisition_time_per_record() / 2 return self._acquisition_start_time @@ -548,7 +548,7 @@ def _set_acquisition_start_time(self, value): value = float(value) value = value + self._get_acquisition_time_per_record() / 2 if not self._driver_operation_simulate: - self._write(":timebase:offset %e" % value) + self._write(":timebase:main:offset %e" % value) self._acquisition_start_time = value self._set_cache_valid() From fbc740819cb7ea25023becaf7e08963c31a10cbd Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Fri, 19 Oct 2018 08:16:47 -0700 Subject: [PATCH 65/79] timebase position fix --- ivi/rigol/rigolBaseScope.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index fd75470f..078b9298 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -87,9 +87,9 @@ 'negative': 'neg', 'either': 'rfal'} MeasurementFunctionMapping = { - 'rise_time': 'rtime', - 'fall_time': 'ftime', - 'frequency': 'frequency', + 'rise_time': 'RTIME', + 'fall_time': 'FTIME', + 'frequency': 'FREQUENCY', 'period': 'period', 'voltage_rms': 'vrms', 'voltage_peak_to_peak': 'vpp', @@ -447,14 +447,14 @@ def _set_timebase_mode(self, value): def _get_timebase_position(self): if not self._driver_operation_simulate and not self._get_cache_valid(): - self._timebase_position = float(self._ask(":timebase:offset?")) + self._timebase_position = float(self._ask(":timebase:main:offset?")) self._set_cache_valid() return self._timebase_position def _set_timebase_position(self, value): value = float(value) if not self._driver_operation_simulate: - self._write(":timebase:offset %e" % value) + self._write(":timebase:main:offset %e" % value) self._timebase_position = value self._set_cache_valid() self._set_cache_valid(False, 'timebase_window_position') @@ -1186,25 +1186,32 @@ def _set_reference_level_middle(self, value): def _measurement_fetch_waveform_measurement(self, index, measurement_function, ref_channel = None): index = ivi.get_index(self._channel_name, index) + print 'measurement_function ', measurement_function + print 'index is ', index if index < self._analog_channel_count: if measurement_function not in MeasurementFunctionMapping: raise ivi.ValueNotSupportedException() func = MeasurementFunctionMapping[measurement_function] + print 'func is ', func else: if measurement_function not in MeasurementFunctionMappingDigital: raise ivi.ValueNotSupportedException() func = MeasurementFunctionMappingDigital[measurement_function] if not self._driver_operation_simulate: - l = func.split(' ') - l[0] = l[0] + '?' - if len(l) > 1: - l[-1] = l[-1] + ',' - func = ' '.join(l) - query = ":measure:%s %s" % (func, self._channel_name[index]) + # l = func.split(' ') + # l[0] = l[0] + '?' + # if len(l) > 1: + # l[-1] = l[-1] + ',' + # func = ' '.join(l) + query = ":measure:item? %s, %s" % (func, self._channel_name[index]) if measurement_function in ['phase', 'delay']: ref_index = ivi.get_index(self._channel_name, ref_channel) query += ", %s" % self._channel_name[ref_index] - return float(self._ask(query)) + meas = self._ask(query) + if meas == 'measure error!': + return meas + else: + return float(meas) return 0 def _measurement_read_waveform_measurement(self, index, measurement_function, maximum_time): From 302b5a8efdf6d1349e22a162070ea727875ddfe5 Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Thu, 10 Jan 2019 11:08:46 -0800 Subject: [PATCH 66/79] updated init_channels for MDO3000 --- ivi/tektronix/tektronixMDO3000.py | 9 +++++++++ ivi/version.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ivi/tektronix/tektronixMDO3000.py b/ivi/tektronix/tektronixMDO3000.py index acfabb83..255f009b 100644 --- a/ivi/tektronix/tektronixMDO3000.py +++ b/ivi/tektronix/tektronixMDO3000.py @@ -48,3 +48,12 @@ def __init__(self, *args, **kwargs): self._init_channels() self._init_outputs() + + def _init_channels(self): + self._channel_count = self._analog_channel_count + self._digital_channel_count + try: + super(tektronixMDO4000, self)._init_channels() + except AttributeError: + pass + self.channels._set_list(self._channel_name) + self._channel_name_dict = ivi.get_index_dict(self._channel_name) diff --git a/ivi/version.py b/ivi/version.py index 452bb987..db7a4160 100644 --- a/ivi/version.py +++ b/ivi/version.py @@ -1 +1 @@ -__version__ = '0.14.9' +__version__ = '0.19.1' From 2a9aeb8e3b5b98051997b8a3d753f66ff36db82f Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Wed, 23 Jan 2019 16:33:32 -0800 Subject: [PATCH 67/79] updated setting of acq points --- ivi/rigol/rigolBaseScope.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index 078b9298..af2b4f7f 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -577,9 +577,13 @@ def _get_acquisition_number_of_points_minimum(self): return self._acquisition_number_of_points_minimum def _set_acquisition_number_of_points_minimum(self, value): - value = int(value) - if not self._driver_operation_simulate: - self._write(":acquire:mdepth %d" % value) + if value == 'AUTO': + if not self._driver_operation_simulate: + self._write(":acquire:mdepth %s" % value) + else: + value = int(value) + if not self._driver_operation_simulate: + self._write(":acquire:mdepth %d" % value) self._acquisition_number_of_points_minimum = value def _get_acquisition_record_length(self): From ba245716aad7994ded622f9de4f56d823afe5547 Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Wed, 23 Jan 2019 16:35:44 -0800 Subject: [PATCH 68/79] updated version --- ivi/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi/version.py b/ivi/version.py index db7a4160..5daae67f 100644 --- a/ivi/version.py +++ b/ivi/version.py @@ -1 +1 @@ -__version__ = '0.19.1' +__version__ = '0.19.2' From 21e1a8e1d3328951c782e310565f2d070b4772da Mon Sep 17 00:00:00 2001 From: Nick Edwards Date: Thu, 21 Feb 2019 16:57:22 -0800 Subject: [PATCH 69/79] Updated to always return get cache valid false --- ivi/ivi.py | 467 +++++++++++++++++++++++++------------------------ ivi/version.py | 2 +- 2 files changed, 235 insertions(+), 234 deletions(-) diff --git a/ivi/ivi.py b/ivi/ivi.py index e4a8756b..f2fea3b8 100644 --- a/ivi/ivi.py +++ b/ivi/ivi.py @@ -144,36 +144,36 @@ def __init__(self): d.setdefault('_props', dict()) d.setdefault('_docs', dict()) d.setdefault('_locked', False) - + def _add_property(self, name, fget=None, fset=None, fdel=None, doc=None): "Add a managed property" d = object.__getattribute__(self, '__dict__') d['_props'][name] = (fget, fset, fdel) d['_docs'][name] = doc d[name] = None - + def _add_method(self, name, f=None, doc=None): "Add a managed method" d = object.__getattribute__(self, '__dict__') d['_docs'][name] = doc d[name] = f - + def _del_property(self, name): "Remove managed property or method" d = object.__getattribute__(self, '__dict__') del d['_props'][name] del d['_docs'][name] del d[name] - + def _lock(self, lock=True): "Set lock state to prevent creation or deletion of unmanaged members" d = object.__getattribute__(self, '__dict__') d['_locked'] = lock - + def _unlock(self): "Unlock object to allow creation or deletion of unmanaged members, equivalent to _lock(False)" self._lock(False) - + def __getattribute__(self, name): if name == '__dict__': return object.__getattribute__(self, name) @@ -186,7 +186,7 @@ def __getattribute__(self, name): raise AttributeError("unreadable attribute") return f() return object.__getattribute__(self, name) - + def __setattr__(self, name, value): d = object.__getattribute__(self, '__dict__') d.setdefault('_props', dict()) @@ -200,7 +200,7 @@ def __setattr__(self, name, value): if name not in d and self._locked: raise AttributeError("locked") object.__setattr__(self, name, value) - + def __delattr__(self, name): d = object.__getattribute__(self, '__dict__') d.setdefault('_props', dict()) @@ -214,7 +214,7 @@ def __delattr__(self, name): if name not in d and self._locked: raise AttributeError("locked") object.__delattr__(self, name) - + class IndexedPropertyCollection(object): "A building block to create hierarchical trees of methods and properties with an index that is converted to a parameter" @@ -224,7 +224,7 @@ def __init__(self): self._indicies = list() self._indicies_dict = dict() self._objs = list() - + def _add_property(self, name, fget=None, fset=None, fdel=None, doc=None, props = None, docs = None): "Add a managed property" if props is None: @@ -245,7 +245,7 @@ def _add_property(self, name, fget=None, fset=None, fdel=None, doc=None, props = else: props[n] = (fget, fset, fdel) docs[n] = doc - + def _add_method(self, name, f=None, doc=None, props = None, docs = None): "Add a managed method" if props is None: @@ -266,15 +266,15 @@ def _add_method(self, name, f=None, doc=None, props = None, docs = None): else: props[n] = f docs[n] = doc - + def _add_sub_property(self, sub, name, fget=None, fset=None, fdel=None, doc=None): "Add a sub-property (equivalent to _add_property('sub.name', ...))" self._add_property(sub+'.'+name, fget, fset, fdel, doc) - + def _add_sub_method(self, sub, name, f=None, doc=None): "Add a sub-method (equivalent to _add_method('sub.name', ...))" self._add_method(sub+'.'+name, f, doc) - + def _del_property(self, name): "Delete property" l = name.split('.',1) @@ -286,7 +286,7 @@ def _del_property(self, name): else: del self._props[name] del self._docs[name] - + def _build_obj(self, props, docs, i): "Build a tree of PropertyCollection objects with the proper index associations" obj = PropertyCollection() @@ -307,7 +307,7 @@ def _build_obj(self, props, docs, i): obj._add_method(n, partial(itm, i), doc) obj._lock() return obj - + def _set_list(self, l): "Set a list of allowable indicies as an associative array" self._indicies = list(l) @@ -315,7 +315,7 @@ def _set_list(self, l): self._objs = list() for i in range(len(self._indicies)): self._objs.append(self._build_obj(self._props, self._docs, i)) - + def __getitem__(self, key): if type(key) is slice: return self._objs[key] @@ -324,10 +324,10 @@ def __getitem__(self, key): def __iter__(self): return self._objs.__iter__() - + def __len__(self): return len(self._indicies) - + def count(self): return len(self._indicies) @@ -396,7 +396,7 @@ def __init__(self, doc = '', cls = '', grp = '', section = '', name = ''): self.cls = cls self.grp = grp self.section = section - + def render(self): txt = '.. attribute:: ' + self.name + '\n\n' if self.cls != '': @@ -406,7 +406,7 @@ def render(self): txt += '\n'.join(' ' + x for x in self.doc.splitlines()) txt += '\n' return txt - + def __str__(self): return self.doc @@ -496,7 +496,7 @@ def build_ieee_block(data): # ex: #800002000 prefixes 2000 data bytes return str('#8%08d' % len(data)).encode('utf-8') + data - + def decode_ieee_block(data): "Decode IEEE block" # IEEE block binary data is prefixed with #lnnnnnnnn @@ -505,20 +505,20 @@ def decode_ieee_block(data): # ex: #800002000 prefixes 2000 data bytes if len(data) == 0: return b'' - + ind = 0 c = '#'.encode('utf-8') while data[ind:ind+1] != c: ind += 1 - + ind += 1 l = int(data[ind:ind+1]) ind += 1 - + if (l > 0): num = int(data[ind:ind+l].decode('utf-8')) ind += l - + return data[ind:ind+num] else: return data[ind:] @@ -546,10 +546,10 @@ def get_sig(sig): y = np.array(sig[:,1]) else: raise Exception('Unknown argument') - + if len(x) != len(y): raise Exception('Signals must be the same length!') - + return x, y @@ -587,13 +587,13 @@ def trim_doc(docstring): def doc(obj=None, itm=None, docs=None, prefix=None): """Python IVI documentation generator""" st = "" - + # add a dot to prefix when needed if prefix is None or len(prefix) == 0: prefix = '' elif not prefix[-1] == '.': prefix += '.' - + # if something passed in docs, iterate over it if docs is not None: for n in sorted(docs.keys()): @@ -604,44 +604,44 @@ def doc(obj=None, itm=None, docs=None, prefix=None): else: # print leaf (method or property) st += prefix + n + "\n" - + return st - + if itm is not None: # split off first component before the dot l = itm.split('.',1) n = l[0] r = '' - + # remove brackets k = n.find('[') if k > 0: n = n[:k] - + # if there is more left, need to recurse if len(l) > 1: r = l[1] - + # hand off to parent if type(obj) == dict and n in obj: return doc(obj[n], r, prefix=prefix+n) - + elif n in obj.__dict__: return doc(obj.__dict__[n], r, prefix=prefix+n) - + elif hasattr(obj, '_docs') and n in obj._docs: d = obj._docs[n] if type(d) == dict: return doc(d, r, prefix=prefix+n) - + else: - + d = None - + # return documentation if present if type(obj) == dict and n in obj: d = obj[n] - + elif hasattr(obj, '_docs') and n in obj._docs: d = obj._docs[n] @@ -652,31 +652,31 @@ def doc(obj=None, itm=None, docs=None, prefix=None): return d elif type(d) == str: return trim_doc(d) - + return "error" - - + + if hasattr(obj, '__dict__'): # if obj has __dict__, iterate over it for n in sorted(obj.__dict__.keys()): o = obj.__dict__[n] - + # add brackets for indexed property collections extra = '' if type(o) == IndexedPropertyCollection: extra = '[]' - + if n == '_docs': # process documentation dict st += doc(docs=o, prefix=prefix) elif hasattr(o, '_docs'): # process object that contains a documentation dict st += doc(o, prefix=prefix+n) - + # if we got something, return it if len(st) > 0: return st - + return "error" def help(obj=None, itm=None, complete=False, indent=0): @@ -686,7 +686,7 @@ def help(obj=None, itm=None, complete=False, indent=0): l = sorted(filter(None, l)) for m in l: d = doc(obj, m) - + if type(d) == Doc: print(d.render()) if type(d) == str: @@ -704,7 +704,7 @@ def help(obj=None, itm=None, complete=False, indent=0): print(trim_doc(""" Using Python IVI help --------------------- - + Use the help method to get documentation on IVI methods and properties. The IVI help system is a little different from the built-in Python help system. Here are some examples on how to use it correctly: @@ -755,10 +755,10 @@ def help(obj=None, itm=None, complete=False, indent=0): class DriverOperation(IviContainer): "Inherent IVI methods for driver operation" - + def __init__(self, *args, **kwargs): super(DriverOperation, self).__init__(*args, **kwargs) - + self._driver_operation_cache = True self._driver_operation_driver_setup = "" self._driver_operation_interchange_check = False @@ -768,10 +768,10 @@ def __init__(self, *args, **kwargs): self._driver_operation_record_coercions = False self._driver_operation_io_resource_descriptor = "" self._driver_operation_simulate = False - + self._driver_operation_interchange_warnings = list() self._driver_operation_coercion_records = list() - + self._add_property('driver_operation.cache', self._get_driver_operation_cache, self._set_driver_operation_cache, @@ -781,7 +781,7 @@ def __init__(self, *args, **kwargs): specific driver keeps track of the current instrument settings so that it can avoid sending redundant commands to the instrument. If False, the specific driver does not cache the value of attributes. - + The default value is True. When the user opens an instrument session through an IVI class driver or uses a logical name to initialize a specific driver, the user can override this value by specifying a value in @@ -799,7 +799,7 @@ def __init__(self, *args, **kwargs): passes in the OptionString parameter of the Initialize function. Refer to Section 6.14, Initialize, for the restrictions on the format of the driver setup string. - + The string that this attribute returns does not have a predefined maximum length. """) @@ -816,7 +816,7 @@ def __init__(self, *args, **kwargs): Get Next Interchange Warning, Section 6.2, Clear Interchange Warnings, and Section 6.18, Reset Interchange Check, for more information. If False, the specific driver does not perform interchangeability checking. - + If the user opens an instrument session through an IVI class driver and the Interchange Check attribute is enabled, the IVI class driver may perform additional interchangeability checking. The IVI class driver @@ -824,7 +824,7 @@ def __init__(self, *args, **kwargs): The user can retrieve both class driver interchangeability warnings and specific driver interchangeability warnings by calling the Get Next Interchange Warning function on the class driver session. - + If the IVI specific driver does not implement interchangeability checking, the specific driver returns the Value Not Supported error when the user attempts to set the Interchange Check attribute to True. If the specific @@ -833,7 +833,7 @@ def __init__(self, *args, **kwargs): accepts True as a valid value for the Interchange Check attribute even if the class driver does not implement interchangeability checking capabilities of its own. - + The default value is False. If the user opens an instrument session through an IVI class driver or initializes an IVI specific driver with a logical name, the user can override this value in the IVI configuration @@ -851,7 +851,7 @@ def __init__(self, *args, **kwargs): not pass a logical name, then this attribute returns an empty string. Refer to IVI-3.5: Configuration Server Specification for restrictions on the format of IVI logical names. - + The string that this attribute returns contains a maximum of 256 characters including the NULL character. """) @@ -867,7 +867,7 @@ def __init__(self, *args, **kwargs): program, the user can set this attribute to False to disable status checking and maximize performance. The user specifies this value for the entire IVI driver session. - + The default value is False. When the user opens an instrument session through an IVI class driver or uses a logical name to initialize an IVI specific driver, the user can override this value by specifying a value in @@ -883,7 +883,7 @@ def __init__(self, *args, **kwargs): If True, the IVI specific driver validates attribute values and function parameters. If False, the IVI specific driver does not validate attribute values and function parameters. - + If range check is enabled, the specific driver validates the parameter values that users pass to driver functions. Validating attribute values and function parameters is useful for debugging. After validating the @@ -904,17 +904,17 @@ def __init__(self, *args, **kwargs): makes for ViInt32 and ViReal64 attributes. If False, the IVI specific driver does not keep a list of the value coercions it makes for ViInt32 and ViReal64 attributes. - + If the Record Value Coercions attribute is enabled, the specific driver maintains a record of each coercion. The user calls the Get Next Coercion Record function to extract and delete the oldest coercion record from the list. Refer to Section 6.10, Get Next Coercion Record, for more information. - + If the IVI specific driver does not implement coercion recording, the specific driver returns the Value Not Supported error when the user attempts to set the Record Value Coercions attribute to True. - + The default value is False. When the user opens an instrument session through an IVI class driver or uses a logical name to initialize a IVI specific driver, the user can override this value by specifying a value in @@ -932,7 +932,7 @@ def __init__(self, *args, **kwargs): configuration store or by passing a resource descriptor to the Initialize function of the specific driver. Refer to Section 6.14, Initialize, for the restrictions on the contents of the resource descriptor string. - + The string that this attribute returns contains a maximum of 256 characters including the NULL character. """) @@ -944,11 +944,11 @@ def __init__(self, *args, **kwargs): If True, the IVI specific driver simulates instrument driver I/O operations. If False, the IVI specific driver communicates directly with the instrument. - + If simulation is enabled, the specific driver functions do not perform instrument I/O. For output parameters that represent instrument data, the specific driver functions return simulated values. - + The default value is False. When the user opens an instrument session through an IVI class driver or uses a logical name to initialize an IVI specific driver, the user can override this value by specifying a value in @@ -961,11 +961,11 @@ def __init__(self, *args, **kwargs): """ This function clears the list of interchangeability warnings that the IVI specific driver maintains. - + When this function is called on an IVI class driver session, the function clears the list of interchangeability warnings that the class driver and the specific driver maintain. - + Refer to the Interchange Check attribute for more information on interchangeability checking. """) @@ -978,32 +978,32 @@ def __init__(self, *args, **kwargs): associated with the IVI session. It retrieves and clears the oldest instance in which the specific driver coerced a value the user specified to another value. - + The function returns an empty string in the CoercionRecord parameter if no coercion records remain for the session. - + The coercion record string shall contain the following information: - + * The name of the attribute that was coerced. This can be the generic name, the COM property name, or the C defined constant. * If the attribute applies to a repeated capability, the name of the virtual or physical repeated capability identifier. * The value that the user specified for the attribute. * The value to which the attribute was coerced. - + A recommended format for the coercion record string is as follows:: - + " Attribute " + + [" on " + ] + " was coerced from " + + " to " + - + . - + And example coercion record string is as follows:: - + Attribute TKTDS500_ATTR_VERTICAL_RANGE on channel ch1 was coerced from 9.0 to 10.0. - + """) self._add_method('driver_operation.get_next_interchange_warning', self._driver_operation_get_next_interchange_warning, @@ -1014,16 +1014,16 @@ def __init__(self, *args, **kwargs): session. It retrieves and clears the oldest interchangeability warning from the list. Interchangeability warnings indicate that using the application with a different instrument might cause different behavior. - + When this function is called on an IVI class driver session, it may return interchangeability warnings generated by the IVI class driver as well as interchangeability warnings generated by the IVI specific driver. The IVI class driver determines the relative order in which the IVI class driver warnings are returned in relation to the IVI specific driver warnings. - + The function returns an empty string in the InterchangeWarning parameter if no interchangeability warnings remain for the session. - + Refer to the Interchange Check attribute for more information on interchangeability checking. """) @@ -1040,7 +1040,7 @@ class driver determines the relative order in which the IVI class driver specific driver so that specific driver functions that execute prior to calling this function have no effect on whether future calls to the specific driver generate interchangeability warnings. - + When developing a complex test system that consists of multiple test modules, it is generally a good idea to design the test modules so that they can run in any order. To do so requires ensuring that each test @@ -1051,7 +1051,7 @@ class driver determines the relative order in which the IVI class driver different order, the behavior of the instrument and therefore the entire test module is likely to change. This change in behavior is generally instrument specific and represents an interchangeability problem. - + Users can use this function to test for such cases. By calling this function at the beginning of a test module, users can determine whether the test module has dependencies on the operation of previously executed @@ -1061,80 +1061,80 @@ class driver determines the relative order in which the IVI class driver does not completely configure the instrument and that the user is likely to experience different behavior if the user changes the execution order of the test modules or if the user changes instruments. - + Note: This function does not clear interchangeability warnings from the list of interchangeability warnings. To guarantee that the Get Next Interchange Warning function returns interchangeability warnings that occur only after the program calls function, the user must clear the list of interchangeability warnings by calling the Clear Interchange Warnings function. - + Refer to the Interchange Check attribute for more information on interchangeability checking. """) - - + + def _get_driver_operation_cache(self): return self._driver_operation_cache - + def _set_driver_operation_cache(self, value): self._driver_operation_cache = bool(value) - + def _get_driver_operation_driver_setup(self): return self._driver_operation_driver_setup - + def _get_driver_operation_interchange_check(self): return self._driver_operation_interchange_check - + def _set_driver_operation_interchange_check(self, value): self._driver_operation_interchange_check = bool(value) - + def _get_driver_operation_logical_name(self): return self._driver_operation_logical_name - + def _get_driver_operation_query_instrument_status(self): return self._driver_operation_query_instrument_status - + def _set_driver_operation_query_instrument_status(self, value): self._driver_operation_query_instrument_status = bool(value) - + def _get_driver_operation_range_check(self): return self._driver_operation_range_check - + def _set_driver_operation_range_check(self, value): self._driver_operation_range_check = bool(value) - + def _get_driver_operation_record_coercions(self): return self._driver_operation_record_coercions - + def _set_driver_operation_record_coercions(self, value): self._driver_operation_record_coercions = bool(value) - + def _get_driver_operation_io_resource_descriptor(self): return self._driver_operation_io_resource_descriptor - + def _get_driver_operation_simulate(self): return self._driver_operation_simulate - + def _set_driver_operation_simulate(self, value): value = bool(value) if self._driver_operation_simulate and not value: raise SimulationStateException() self._driver_operation_simulate = value - + def _driver_operation_clear_interchange_warnings(self): self._driver_operation_interchange_warnings = list() - + def _driver_operation_get_next_coercion_record(self): if len(self._driver_operation_coercion_records) > 0: return self._driver_operation_coercion_records.pop() return "" - + def _driver_operation_get_next_interchange_warning(self): if len(self._driver_operation_interchange_warnings) > 0: return self._driver_operation_interchange_warnings.pop() return "" - + def _driver_operation_invalidate_all_attributes(self): pass @@ -1147,7 +1147,7 @@ class DriverIdentity(IviContainer): def __init__(self, *args, **kwargs): super(DriverIdentity, self).__init__(*args, **kwargs) - + self._identity_description = "Base IVI Driver" self._identity_identifier = "" self._identity_revision = "" @@ -1159,14 +1159,14 @@ def __init__(self, *args, **kwargs): self._identity_specification_minor_version = 0 self._identity_supported_instrument_models = list() self.__dict__.setdefault('_identity_group_capabilities', list()) - + self._add_property('identity.description', self._get_identity_description, None, None, """ Returns a brief description of the IVI software component. - + The string that this attribute returns has no maximum size. """) self._add_property('identity.identifier', @@ -1186,7 +1186,7 @@ def __init__(self, *args, **kwargs): Returns version information about the IVI software component. Refer to Section 3.1.2.2, Additional Compliance Rules for Revision String Attributes, for additional rules regarding this attribute. - + The string that this attribute returns has no maximum size. """) self._add_property('identity.vendor', @@ -1195,7 +1195,7 @@ def __init__(self, *args, **kwargs): None, """ Returns the name of the vendor that supplies the IVI software component. - + The string that this attribute returns has no maximum size. """) self._add_property('identity.instrument_manufacturer', @@ -1207,7 +1207,7 @@ def __init__(self, *args, **kwargs): driver returns the value it queries from the instrument as the value of this attribute or a string indicating that it cannot query the instrument identity. - + In some cases, it is not possible for the specific driver to query the manufacturer of the instrument. This can occur when the Simulate attribute is set to True or if the instrument is not capable of returning the @@ -1217,7 +1217,7 @@ def __init__(self, *args, **kwargs): attribute. If the instrument is not capable of returning the manufacturer and the Simulate attribute is set to False, the specific driver returns "Cannot query from instrument" as the value of this attribute. - + The string that this attribute returns does not have a predefined maximum length. """) @@ -1229,7 +1229,7 @@ def __init__(self, *args, **kwargs): Returns the model number or name of the physical instrument. The IVI specific driver returns the value it queries from the instrument or a string indicating that it cannot query the instrument identity. - + In some cases, it is not possible for the specific driver to query the model of the instrument. This can occur when the Simulate attribute is set to True or if the instrument is not capable of returning the model. @@ -1239,7 +1239,7 @@ def __init__(self, *args, **kwargs): If the instrument is not capable of returning the model and the Simulate attribute is set to False, the specific driver returns "Cannot query from instrument" as the value of this attribute. - + The string that this attribute returns does not have a predefined maximum length. """) @@ -1253,7 +1253,7 @@ def __init__(self, *args, **kwargs): returns the value it queries from the instrument as the value of this attribute or a string indicating that it cannot query the instrument identity. - + In some cases, it is not possible for the specific driver to query the firmware revision of the instrument. This can occur when the Simulate attribute is set to True or if the instrument is not capable of returning @@ -1264,7 +1264,7 @@ def __init__(self, *args, **kwargs): firmware version and the Simulate attribute is set to False, the specific driver returns "Cannot query from instrument" as the value of this attribute. - + The string that this attribute returns does not have a predefined maximum length. """) @@ -1276,7 +1276,7 @@ def __init__(self, *args, **kwargs): Returns the major version number of the class specification in accordance with which the IVI software component was developed. The value is a positive integer value. - + If the software component is not compliant with a class specification, the software component returns zero as the value of this attribute. """) @@ -1288,7 +1288,7 @@ def __init__(self, *args, **kwargs): Returns the minor version number of the class specification in accordance with which the IVI software component was developed. The value is a positive integer value. - + If the software component is not compliant with a class specification, the software component returns zero as the value of this attribute. """) @@ -1301,11 +1301,11 @@ def __init__(self, *args, **kwargs): the IVI specific driver is compatible. The string has no white space except possibly embedded in the instrument model names. An example of a string that this attribute might return is "TKTDS3012,TKTDS3014,TKTDS3016". - + It is not necessary for the string to include the abbreviation for the manufacturer if it is the same for all models. In the example above, it is valid for the attribute to return the string "TDS3012,TDS3014,TDS3016". - + The string that this attribute returns does not have a predefined maximum length. """) @@ -1319,10 +1319,10 @@ def __init__(self, *args, **kwargs): capability group names that the IVI class specifications define. The string has no white space except for white space that might be embedded in a capability group name. - + If the IVI specific driver does not comply with an IVI class specification, the specific driver returns an empty string as the value of this attribute. - + The string that this attribute returns does not have a predefined maximum length. """) @@ -1333,7 +1333,7 @@ def __init__(self, *args, **kwargs): driver implements. The items in the list are capability group names that the IVI class specifications define. The list is returned as a list of strings. - + If the IVI specific driver does not comply with an IVI class specification, the specific driver returns an array with zero elements. """) @@ -1344,54 +1344,54 @@ def __init__(self, *args, **kwargs): driver is compatible. The list is returned as a list of strings. For example, this attribute might return the strings "TKTDS3012", "TKTDS3014", and "TKTDS3016" . - + It is not necessary for the string to include the abbreviation for the manufacturer if it is the same for all models. In the example above, it is valid for the attribute to return the strings "TDS3012", "TDS3014", and "TDS3016". """) - - + + def _add_group_capability(self, name): self.__dict__.setdefault('_identity_group_capabilities', list()) self._identity_group_capabilities.insert(0, name) - + def _get_identity_description(self): return self._identity_description - + def _get_identity_identifier(self): return self._identity_identifier - + def _get_identity_revision(self): return self._identity_revision - + def _get_identity_vendor(self): return self._identity_vendor - + def _get_identity_instrument_manufacturer(self): return self._identity_instrument_manufacturer - + def _get_identity_instrument_model(self): return self._identity_instrument_model - + def _get_identity_instrument_firmware_revision(self): return self._identity_instrument_firmware_revision - + def _get_identity_specification_major_version(self): return self._identity_specification_major_version - + def _get_identity_specification_minor_version(self): return self._identity_specification_minor_version - + def _get_identity_supported_instrument_models(self): return ",".join(self._identity_supported_instrument_models) - + def _get_identity_group_capabilities(self): return ",".join(self._identity_group_capabilities) - + def _identity_get_group_capabilities(self): return self._identity_group_capabilities - + def _identity_get_supported_instrument_models(self): return self._identity_supported_instrument_models @@ -1401,7 +1401,7 @@ class DriverUtility(IviContainer): def __init__(self, *args, **kwargs): super(DriverUtility, self).__init__(*args, **kwargs) - + self._add_method('utility.disable', self._utility_disable, """ @@ -1413,7 +1413,7 @@ def __init__(self, *args, **kwargs): perform the other operations that the Reset operation performs such as configuring the instrument options on which the IVI specific driver depends. For some instruments, the disable function may do nothing. - + The IVI class specifications define the exact behavior of this function for each instrument class. Refer to the IVI class specifications for more information on the behavior of this function. @@ -1422,16 +1422,16 @@ def __init__(self, *args, **kwargs): self._utility_error_query, """ Queries the instrument and returns instrument specific error information. - + Generally, the user calls this function after another function in the IVI driver returns the Instrument Status error. The IVI specific driver returns the Instrument Status error when the instrument indicates that it encountered an error and its error queue is not empty. Error Query extracts an error out of the instrument's error queue. - + For instruments that have status registers but no error queue, the IVI specific driver emulates an error queue in software. - + The method returns a tuple containing the error code and error message. """) self._add_method('utility.lock_object', @@ -1442,7 +1442,7 @@ def __init__(self, *args, **kwargs): have released their locks or for the length of time specified by the maximum time parameter, whichever come first. The type of lock obtained depends upon the parameters passed to the specific driver constructor. - + The user can use Lock Session with IVI specific drivers to protect a section of code that requires exclusive access to the instrument. This occurs when the user takes multiple actions that affect the instrument @@ -1450,11 +1450,11 @@ def __init__(self, *args, **kwargs): the instrument state until all the actions execute. For example, if the user sets various instrument attributes and then triggers a measurement, the user must ensure no other execution thread modifies the attribute - values until the user finishes taking the measurement. - + values until the user finishes taking the measurement. + It is important to note that this lock is not related to I/O locks such as the VISA resource locking mechanism. - + The user can safely make nested calls to Lock Session within the same thread. To completely unlock the session, the user must balance each call to Lock Session with a call to Unlock Session. Calls to Lock Session must @@ -1465,13 +1465,13 @@ def __init__(self, *args, **kwargs): self._utility_reset, """ This function performs the following actions: - + * Places the instrument in a known state. In an IEEE 488.2 instrument, the Reset function sends the command string ``*RST`` to the instrument. * Configures instrument options on which the IVI specific driver depends. A specific driver might enable or disable headers or enable binary mode for waveform transfers. - + The user can either call the Reset function separately or specify that it be called from the Initialize function. The Initialize function performs additional operations after performing the reset operation to place the @@ -1485,7 +1485,7 @@ def __init__(self, *args, **kwargs): The Reset With Defaults function performs the same operations that the Reset function performs and then performs the following additional operations in the specified order: - + * Disables the class extension capability groups that the IVI specific driver implements. * If the class specification with which the IVI specific driver is @@ -1495,7 +1495,7 @@ def __init__(self, *args, **kwargs): * Configures the initial settings for the specific driver and instrument based on the information retrieved from the IVI configuration store when the instrument driver session was initialized. - + Notice that the Initialize function also performs these functions. To place the instrument and the IVI specific driver in the exact same state that they attain when the user calls the Initialize function, the user @@ -1507,44 +1507,44 @@ def __init__(self, *args, **kwargs): Causes the instrument to perform a self test. Self Test waits for the instrument to complete the test. It then queries the instrument for the results of the self test and returns the results to the user. - + If the instrument passes the self test, this function returns the tuple:: - + (0, 'Self test passed') - + Otherwise, the function returns a tuple of the result code and message. """) self._add_method('utility.unlock_object', self._utility_unlock_object, """ This function releases a lock that the Lock Session function acquires. - + Refer to Lock Session for additional information on IVI session locks. """) - - + + def _utility_disable(self): pass - + def _utility_error_query(self): error_code = 0 error_message = "No error" return (error_code, error_message) - + def _utility_lock_object(self): pass - + def _utility_reset(self): pass - + def _utility_reset_with_defaults(self): self.utility_reset() - + def _utility_self_test(self): code = 0 message = "Self test passed" return (code, message) - + def _utility_unlock_object(self): pass @@ -1559,25 +1559,25 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw 'interchange_check', 'driver_setup', 'prefer_pyvisa'): if k in kwargs: kw[k] = kwargs.pop(k) - + self._interface = None self._initialized = False self.__dict__.setdefault('_instrument_id', '') self._cache_valid = dict() - + super(Driver, self).__init__(*args, **kwargs) - + self._add_method('initialize', self._initialize, """ The user must call the Initialize function prior to calling other IVI driver functions that access the instrument. The Initialize function is called automatically by the constructor if a resource string is passed as - the first argument to the constructor. - + the first argument to the constructor. + If simulation is disabled when the user calls the Initialize function, the function performs the following actions: - + * Opens and configures an I/O session to the instrument. * If the user passes True for the IdQuery parameter, the function queries the instrument for its ID and verifies that the IVI specific driver @@ -1588,7 +1588,7 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw instrument in a known state. In an IEEE 488.2 instrument, the function sends the command string "*RST" to the instrument. If the instrument cannot perform a reset, the IVI specific driver returns the Reset Not - Supported warning. + Supported warning. * Configures instrument options on which the IVI specific driver depends. For example, a specific driver might enable or disable headers or enable binary mode for waveform transfers. @@ -1600,12 +1600,12 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw the attributes to the values that the class specification defines. 3. If the ResourceName parameter is a logical name, the IVI specific driver configures the initial settings for the specific driver and - instrument based on the configuration of the logical name in the IVI + instrument based on the configuration of the logical name in the IVI configuration store. - + If simulation is enabled when the user calls the Initialize function, the function performs the following actions: - + * If the user passes True for the IdQuery parameter and the instrument cannot return its ID, the IVI specific driver returns the ID Query Not Supported warning. @@ -1615,7 +1615,7 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw * If the ResourceName parameter is a logical name, the IVI specific driver configures the initial settings for the specific driver based on the configuration of the logical name in the IVI configuration store. - + Some instrument driver operations require or take into account information from the IVI configuration store. Examples of such information are virtual repeated capability name mappings and the value of certain inherent @@ -1626,17 +1626,17 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw Section 3.2.3, Instantiating the Right Configuration Store From Software Modules, of IVI-3.5: Configuration Server Specification for details on how to correctly instantiate the configuration store. - + The ResourceName parameter must contain either a logical name that is defined in the IVI configuration store or an instrument specific string that identifies the I/O address of the instrument, such as a VISA resource descriptor string. Refer to IVI-3.5: Configuration Server Specification for restrictions on the format of IVI logical names. Refer to the VXIplug&play specifications for the grammar of VISA resource descriptor - strings. - + strings. + Example resource strings:: - + 'TCPIP::10.0.0.1::INSTR' 'TCPIP0::10.0.0.1::INSTR' 'TCPIP::10.0.0.1::gpib,5::INSTR' @@ -1653,7 +1653,7 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw 'ASRL::COM1,9600,8n1::INSTR' 'ASRL::/dev/ttyUSB0,9600::INSTR' 'ASRL::/dev/ttyUSB0,9600,8n1::INSTR' - + The user can use additional parameters to specify the initial values of certain IVI inherent attributes for the session. The following table lists the inherent attributes that the user can set through these named @@ -1661,7 +1661,7 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw attributes. If the user does not specify the initial value of an inherent attribute, the initial value of the attribute depends on the value of the ResourceName parameter: - + * If the ResourceName parameter contains an IVI logical name, the IVI specific driver configures the initial settings based on the configuration of the logical name in the IVI configuration store. @@ -1669,12 +1669,12 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw identifies the I/O address of the instrument, the IVI specific driver sets inherent attributes to their default initial values. The following table shows the default initial value for each attribute. - + The following table lists the IVI inherent attributes that the user can set, their default initial values, and the name that represents each attribute. These options are passed to the initialize function or the - constructor as key-value pairs. - + constructor as key-value pairs. + +-------------------------+----------------------+---------------------+ | Attribute | Default Inital Value | Options String Name | +=========================+======================+=====================+ @@ -1694,7 +1694,7 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw +-------------------------+----------------------+---------------------+ | Prefer PyVISA | False | prefer_pyvisa | +-------------------------+----------------------+---------------------+ - + Each IVI specific driver defines it own meaning and valid values for the Driver Setup attribute. Many specific drivers ignore the value of the Driver Setup attribute. Other specific drivers use the Driver Setup string @@ -1702,7 +1702,7 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw if a specific driver supports a family of instrument models, the driver can use the Driver Setup attribute to allow the user to specify a particular instrument model to simulate. - + If the user attempts to initialize the instrument a second time without first calling the Close function, the Initialize function returns the Already Initialized error. @@ -1717,13 +1717,13 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw the Initialize function successfully executes, this attribute returns False. After the Initialize function successfully executes and prior to the execution of the Close function, this attribute returns True. After - the Close function executes, this attribute returns False. - + the Close function executes, this attribute returns False. + The Initialized attribute is one of the few IVI specific driver attributes that can be accessed while the specific driver is not in the initialized state. All the attributes of an IVI specific driver that can be accessed while the specific driver is not in the initialized state are listed below. - + * Component Class Spec Major Version * Component Class Spec Minor Version * Component Description @@ -1739,10 +1739,10 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw """ When the user finishes using a Python IVI driver, the user should call either the Close method or __del__. Note that __del__ will call close - automatically. - + automatically. + This function also does the following: - + * Prevents the user from calling other functions in the driver that access the instrument until the user calls the Initialize function again. @@ -1917,32 +1917,33 @@ def _close(self): def _get_initialized(self): "Returnes initialization state of driver" return self._initialized - + def _get_cache_tag(self, tag=None, skip=1): if tag is None: stack = inspect.stack() start = 0 + skip if len(stack) < start + 1: return '' - tag = stack[start][3] - + tag = stack[start][3] + if tag[0:4] == "_get": tag = tag[4:] if tag[0:4] == "_set": tag = tag[4:] if tag[0] == "_": tag = tag[1:] - + return tag def _get_cache_valid(self, tag=None, index=-1, skip_disable=False): - if not skip_disable and not self._driver_operation_cache: - return False - tag = self._get_cache_tag(tag, 2) - if index >= 0: - tag = tag + '_%d' % index - try: - return self._cache_valid[tag] - except KeyError: - self._cache_valid[tag] = False - return False + # if not skip_disable and not self._driver_operation_cache: + # return False + # tag = self._get_cache_tag(tag, 2) + # if index >= 0: + # tag = tag + '_%d' % index + # try: + # return self._cache_valid[tag] + # except KeyError: + # self._cache_valid[tag] = False + # return False + return False #always return false to ensure physical instr is queried def _set_cache_valid(self, valid=True, tag=None, index=-1): tag = self._get_cache_tag(tag, 2) @@ -1961,7 +1962,7 @@ def _write_raw(self, data): if not self._initialized or self._interface is None: raise NotInitializedException() self._interface.write_raw(data) - + def _read_raw(self, num=-1): "Read binary data from instrument" if self._driver_operation_simulate: @@ -1970,7 +1971,7 @@ def _read_raw(self, num=-1): if not self._initialized or self._interface is None: raise NotInitializedException() return self._interface.read_raw(num) - + def _ask_raw(self, data, num=-1): "Write then read binary data" if self._driver_operation_simulate: @@ -1984,7 +1985,7 @@ def _ask_raw(self, data, num=-1): # if interface does not implement ask_raw, emulate it self._write_raw(data) return self._read_raw(num) - + def _write(self, data, encoding = 'utf-8'): "Write string to instrument" if self._driver_operation_simulate: @@ -2002,7 +2003,7 @@ def _write(self, data, encoding = 'utf-8'): return self._write_raw(str(data).encode(encoding)) - + def _read(self, num=-1, encoding = 'utf-8'): "Read string from instrument" if self._driver_operation_simulate: @@ -2014,7 +2015,7 @@ def _read(self, num=-1, encoding = 'utf-8'): return self._interface.read(num, encoding) except AttributeError: return self._read_raw(num).decode(encoding).rstrip('\r\n') - + def _ask(self, data, num=-1, encoding = 'utf-8'): "Write then read string" if self._driver_operation_simulate: @@ -2035,11 +2036,11 @@ def _ask(self, data, num=-1, encoding = 'utf-8'): self._write(data, encoding) return self._read(num, encoding) - + def _ask_for_values(self, msg, delim=',', converter=float, array=True): ''' write then read a list or array of data - + Parameters -------------- msg : str @@ -2049,8 +2050,8 @@ def _ask_for_values(self, msg, delim=',', converter=float, array=True): converter : type a datatype used to typecase the elements in the returned list array: bool - convert the output to a numpy array - + convert the output to a numpy array + ''' s = self._ask(msg) s_split = s.split(delim) @@ -2058,7 +2059,7 @@ def _ask_for_values(self, msg, delim=',', converter=float, array=True): if array: out = np.array(out) return out - + def _read_stb(self): "Read status byte" if self._driver_operation_simulate: @@ -2070,7 +2071,7 @@ def _read_stb(self): return self._interface.read_stb() except (AttributeError, NotImplementedError): return int(self._ask("*STB?")) - + def _trigger(self): "Device trigger" if self._driver_operation_simulate: @@ -2081,7 +2082,7 @@ def _trigger(self): self._interface.trigger() except (AttributeError, NotImplementedError): self._write("*TRG") - + def _clear(self): "Device clear" if self._driver_operation_simulate: @@ -2092,7 +2093,7 @@ def _clear(self): return self._interface.clear() except (AttributeError, NotImplementedError): self._write("*CLS") - + def _remote(self): "Device set remote" if self._driver_operation_simulate: @@ -2100,7 +2101,7 @@ def _remote(self): if not self._initialized or self._interface is None: raise NotInitializedException() return self._interface.remote() - + def _local(self): "Device set local" if self._driver_operation_simulate: @@ -2108,7 +2109,7 @@ def _local(self): if not self._initialized or self._interface is None: raise NotInitializedException() return self._interface.local() - + def _read_ieee_block(self): "Read IEEE block" # IEEE block binary data is prefixed with #lnnnnnnnn @@ -2132,7 +2133,7 @@ def _read_ieee_block(self): raw_data = self._read_raw() return raw_data - + def _ask_for_ieee_block(self, data, encoding = 'utf-8'): "Write string then read IEEE block" self._write(data, encoding) @@ -2144,33 +2145,33 @@ def _write_ieee_block(self, data, prefix = None, encoding = 'utf-8'): # where l is length of n and n is the # length of the data # ex: #800002000 prefixes 2000 data bytes - + block = b'' - + if type(prefix) == str: block = prefix.encode(encoding) elif type(prefix) == bytes: block = prefix - + block = block + build_ieee_block(data) - + self._write_raw(block) - + def doc(self, obj=None, itm=None, docs=None, prefix=None): """Python IVI documentation generator""" - + # need an obj, if none specified, use self if obj is None: obj = self - + # if first arg is a string, put in itm and use self for obj if type(obj) == str: itm = obj obj = self - + return doc(obj, itm, docs, prefix) - + def help(self, itm=None, complete=False, indent=0): """Python IVI help system""" return help(self, itm, complete, indent) - + diff --git a/ivi/version.py b/ivi/version.py index 5daae67f..b6991384 100644 --- a/ivi/version.py +++ b/ivi/version.py @@ -1 +1 @@ -__version__ = '0.19.2' +__version__ = '0.19.3' From 3f02fbcb38e58124c6a7c3d408530f17fe4a0d67 Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Thu, 4 Apr 2019 15:19:37 -0700 Subject: [PATCH 70/79] added support for new Rigol scope models --- ivi/rigol/__init__.py | 11 ++++ ivi/rigol/rigolBaseScope.py | 6 +- ivi/rigol/rigolDS1000Z.py | 2 +- ivi/rigol/rigolDS1074ZPlus.py | 42 ++++++++++++++ ivi/rigol/rigolDS5000.py | 106 ++++++++++++++++++++++++++++++++++ ivi/rigol/rigolDS7000.py | 105 +++++++++++++++++++++++++++++++++ ivi/rigol/rigolDS7014.py | 43 ++++++++++++++ ivi/rigol/rigolDS8000.py | 105 +++++++++++++++++++++++++++++++++ ivi/rigol/rigolMSO5072.py | 43 ++++++++++++++ ivi/rigol/rigolMSO5074.py | 43 ++++++++++++++ ivi/rigol/rigolMSO7014.py | 43 ++++++++++++++ ivi/rigol/rigolMSO8064.py | 43 ++++++++++++++ ivi/version.py | 2 +- 13 files changed, 589 insertions(+), 5 deletions(-) create mode 100644 ivi/rigol/rigolDS1074ZPlus.py create mode 100644 ivi/rigol/rigolDS5000.py create mode 100644 ivi/rigol/rigolDS7000.py create mode 100644 ivi/rigol/rigolDS7014.py create mode 100644 ivi/rigol/rigolDS8000.py create mode 100644 ivi/rigol/rigolMSO5072.py create mode 100644 ivi/rigol/rigolMSO5074.py create mode 100644 ivi/rigol/rigolMSO7014.py create mode 100644 ivi/rigol/rigolMSO8064.py diff --git a/ivi/rigol/__init__.py b/ivi/rigol/__init__.py index 2c65eabb..72df6af0 100644 --- a/ivi/rigol/__init__.py +++ b/ivi/rigol/__init__.py @@ -28,6 +28,7 @@ # DS1000Z from .rigolDS1054Z import rigolDS1054Z from .rigolDS1074Z import rigolDS1074Z +from .rigolDS1074ZPlus import rigolDS1074ZPlus from .rigolDS1104Z import rigolDS1104Z from .rigolMSO1074Z import rigolMSO1074Z from .rigolMSO1104Z import rigolMSO1104Z @@ -57,6 +58,16 @@ from .rigolMSO4034 import rigolMSO4034 from .rigolMSO4052 import rigolMSO4052 from .rigolMSO4054 import rigolMSO4054 +# MSO5000 +from .rigolMSO5072 import rigolMSO5072 +from .rigolMSO5074 import rigolMSO5074 +# MSO7000 +from .rigolDS7014 import rigolDS7014 +from .rigolMSO7014 import rigolMSO7014 +# MSO8000 +from .rigolMSO8064 import rigolMSO8064 + + # DC Power Supplies # DP800 diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index af2b4f7f..f0b60842 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -190,9 +190,9 @@ def __init__(self, *args, **kwargs): self._identity_instrument_firmware_revision = "" self._identity_specification_major_version = 4 self._identity_specification_minor_version = 1 - self._identity_supported_instrument_models = ['DS1054Z', 'DS1074Z', 'DS1104Z', 'MSO1074Z', - 'MSO1104Z', 'DS2074A', 'DS2104A', 'DS2204A', 'DS2304A', 'MSO2074A', - 'MSO2104A', 'MSO2204A', 'MSO2304A'] + self._identity_supported_instrument_models = ['DS1054Z', 'DS1074ZPlus', 'DS1104Z', 'DS1074Plus', 'MSO1074Z', + 'MSO1104Z', 'DS2072A', 'DS2074A', 'DS2104A', 'DS2204A', 'DS2304A', 'MSO2072A', 'MSO2074A', + 'MSO2104A', 'MSO2204A', 'MSO2304A', 'MSO5072', 'MSO5074', 'DS7014', 'MSO7014', 'MSO8064'] self._add_property('channels[].invert', self._get_channel_invert, diff --git a/ivi/rigol/rigolDS1000Z.py b/ivi/rigol/rigolDS1000Z.py index f394af78..7312b9e5 100644 --- a/ivi/rigol/rigolDS1000Z.py +++ b/ivi/rigol/rigolDS1000Z.py @@ -48,7 +48,7 @@ def __init__(self, *args, **kwargs): self._output_count = 2 self._identity_description = "Rigol DS1000Z series IVI oscilloscope driver" - self._identity_supported_instrument_models = ['DS1054Z', 'DS1074Z', 'DS1104Z', 'MSO1074Z', 'MSO1104Z'] + self._identity_supported_instrument_models = ['DS1054Z', 'DS1074Z', 'DS1074ZPlus', 'DS1104Z', 'MSO1074Z', 'MSO1104Z'] self._init_channels() self._init_outputs() diff --git a/ivi/rigol/rigolDS1074ZPlus.py b/ivi/rigol/rigolDS1074ZPlus.py new file mode 100644 index 00000000..b35e00de --- /dev/null +++ b/ivi/rigol/rigolDS1074ZPlus.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS1000Z import * + +class rigolDS1074ZPlus(rigolDS1000Z): + "Rigol DS1074ZPlus IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS1074ZPlus') + + super(rigolDS1074ZPlus, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 70e6 + + self._init_channels() diff --git a/ivi/rigol/rigolDS5000.py b/ivi/rigol/rigolDS5000.py new file mode 100644 index 00000000..4895c391 --- /dev/null +++ b/ivi/rigol/rigolDS5000.py @@ -0,0 +1,106 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolBaseScope import * + +class rigolDS5000(rigolBaseScope): + "Rigol DS5000 series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + + super(rigolDS5000, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._bandwidth = 500e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + self._max_averages = 8192 + + self._horizontal_divisions = 12 + self._vertical_divisions = 8 + + self._identity_description = "Rigol DS5000 series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['MSO5072', 'MSO5074', 'MSO5102', + 'MSO5104', 'MSO5204', 'MSO5354'] + + self._init_channels() + + def _display_fetch_screenshot(self, format='bmp', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + self._write(":display:data?") + + data = self._read_raw() + + return ivi.decode_ieee_block(data) + + def _get_channel_input_impedance(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + val = self._ask(":%s:impedance?" % self._channel_name[index]) + if val == 'OMEG': + self._channel_input_impedance[index] = 1000000 + elif val == 'FIFT': + self._channel_input_impedance[index] = 50 + self._set_cache_valid(index=index) + return self._channel_input_impedance[index] + + def _set_channel_input_impedance(self, index, value): + value = float(value) + index = ivi.get_index(self._analog_channel_name, index) + if value != 50 and value != 1000000: + raise Exception('Invalid impedance selection') + if not self._driver_operation_simulate: + if value == 1000000: + self._write(":%s:impedance omeg" % self._channel_name[index]) + elif value == 50: + self._write(":%s:impedance fifty" % self._channel_name[index]) + self._channel_input_impedance[index] = value + self._set_cache_valid(index=index) + + def _get_channel_probe_attenuation(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_attenuation[index] = float(self._ask(":%s:probe?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_attenuation[index] + + def _set_channel_probe_attenuation(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:probe %s" % (self._channel_name[index], ("%f" %value).rstrip('0').rstrip('.'))) + self._channel_probe_attenuation[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'channel_offset', index) + self._set_cache_valid(False, 'channel_scale', index) + self._set_cache_valid(False, 'channel_range', index) + self._set_cache_valid(False, 'trigger_level') diff --git a/ivi/rigol/rigolDS7000.py b/ivi/rigol/rigolDS7000.py new file mode 100644 index 00000000..128ae611 --- /dev/null +++ b/ivi/rigol/rigolDS7000.py @@ -0,0 +1,105 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolBaseScope import * + +class rigolDS7000(rigolBaseScope): + "Rigol DS7000 series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + + super(rigolDS7000, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._bandwidth = 500e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + self._max_averages = 8192 + + self._horizontal_divisions = 12 + self._vertical_divisions = 8 + + self._identity_description = "Rigol DS7000 series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DS7014', 'MSO7014'] + + self._init_channels() + + def _display_fetch_screenshot(self, format='bmp', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + self._write(":display:data?") + + data = self._read_raw() + + return ivi.decode_ieee_block(data) + + def _get_channel_input_impedance(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + val = self._ask(":%s:impedance?" % self._channel_name[index]) + if val == 'OMEG': + self._channel_input_impedance[index] = 1000000 + elif val == 'FIFT': + self._channel_input_impedance[index] = 50 + self._set_cache_valid(index=index) + return self._channel_input_impedance[index] + + def _set_channel_input_impedance(self, index, value): + value = float(value) + index = ivi.get_index(self._analog_channel_name, index) + if value != 50 and value != 1000000: + raise Exception('Invalid impedance selection') + if not self._driver_operation_simulate: + if value == 1000000: + self._write(":%s:impedance omeg" % self._channel_name[index]) + elif value == 50: + self._write(":%s:impedance fifty" % self._channel_name[index]) + self._channel_input_impedance[index] = value + self._set_cache_valid(index=index) + + def _get_channel_probe_attenuation(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_attenuation[index] = float(self._ask(":%s:probe?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_attenuation[index] + + def _set_channel_probe_attenuation(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:probe %s" % (self._channel_name[index], ("%f" %value).rstrip('0').rstrip('.'))) + self._channel_probe_attenuation[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'channel_offset', index) + self._set_cache_valid(False, 'channel_scale', index) + self._set_cache_valid(False, 'channel_range', index) + self._set_cache_valid(False, 'trigger_level') diff --git a/ivi/rigol/rigolDS7014.py b/ivi/rigol/rigolDS7014.py new file mode 100644 index 00000000..5db1cad7 --- /dev/null +++ b/ivi/rigol/rigolDS7014.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS7000 import * + +class rigolDS7014(rigolDS7000): + "Rigol DS7014 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS7014') + + super(rigolDS7014, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + self._bandwidth_limit = {'20M': 20e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS8000.py b/ivi/rigol/rigolDS8000.py new file mode 100644 index 00000000..03e39acb --- /dev/null +++ b/ivi/rigol/rigolDS8000.py @@ -0,0 +1,105 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolBaseScope import * + +class rigolDS8000(rigolBaseScope): + "Rigol DS8000 series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + + super(rigolDS8000, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._bandwidth = 500e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + self._max_averages = 8192 + + self._horizontal_divisions = 12 + self._vertical_divisions = 8 + + self._identity_description = "Rigol DS8000 series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['MSO8064'] + + self._init_channels() + + def _display_fetch_screenshot(self, format='bmp', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + self._write(":display:data?") + + data = self._read_raw() + + return ivi.decode_ieee_block(data) + + def _get_channel_input_impedance(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + val = self._ask(":%s:impedance?" % self._channel_name[index]) + if val == 'OMEG': + self._channel_input_impedance[index] = 1000000 + elif val == 'FIFT': + self._channel_input_impedance[index] = 50 + self._set_cache_valid(index=index) + return self._channel_input_impedance[index] + + def _set_channel_input_impedance(self, index, value): + value = float(value) + index = ivi.get_index(self._analog_channel_name, index) + if value != 50 and value != 1000000: + raise Exception('Invalid impedance selection') + if not self._driver_operation_simulate: + if value == 1000000: + self._write(":%s:impedance omeg" % self._channel_name[index]) + elif value == 50: + self._write(":%s:impedance fifty" % self._channel_name[index]) + self._channel_input_impedance[index] = value + self._set_cache_valid(index=index) + + def _get_channel_probe_attenuation(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_attenuation[index] = float(self._ask(":%s:probe?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_attenuation[index] + + def _set_channel_probe_attenuation(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:probe %s" % (self._channel_name[index], ("%f" %value).rstrip('0').rstrip('.'))) + self._channel_probe_attenuation[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'channel_offset', index) + self._set_cache_valid(False, 'channel_scale', index) + self._set_cache_valid(False, 'channel_range', index) + self._set_cache_valid(False, 'trigger_level') diff --git a/ivi/rigol/rigolMSO5072.py b/ivi/rigol/rigolMSO5072.py new file mode 100644 index 00000000..9f4ac990 --- /dev/null +++ b/ivi/rigol/rigolMSO5072.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS5000 import * + +class rigolMSO5072(rigolDS5000): + "Rigol MSO5072 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO5072') + + super(rigolMSO5072, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + self._bandwidth_limit = {'20M': 20e6} + + self._init_channels() diff --git a/ivi/rigol/rigolMSO5074.py b/ivi/rigol/rigolMSO5074.py new file mode 100644 index 00000000..b9e3e5b4 --- /dev/null +++ b/ivi/rigol/rigolMSO5074.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS5000 import * + +class rigolMSO5074(rigolDS5000): + "Rigol MSO5074 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO5074') + + super(rigolMSO5074, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + self._bandwidth_limit = {'20M': 20e6} + + self._init_channels() diff --git a/ivi/rigol/rigolMSO7014.py b/ivi/rigol/rigolMSO7014.py new file mode 100644 index 00000000..0b5b9667 --- /dev/null +++ b/ivi/rigol/rigolMSO7014.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS7000 import * + +class rigolMSO7014(rigolDS7000): + "Rigol MSO7014 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO7014') + + super(rigolMSO7014, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + self._bandwidth_limit = {'20M': 20e6} + + self._init_channels() diff --git a/ivi/rigol/rigolMSO8064.py b/ivi/rigol/rigolMSO8064.py new file mode 100644 index 00000000..94a14b24 --- /dev/null +++ b/ivi/rigol/rigolMSO8064.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from .rigolDS8000 import * + +class rigolMSO8064(rigolDS8000): + "Rigol MSO8064 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO8064') + + super(rigolMSO8064, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + self._bandwidth_limit = {'20M': 20e6} + + self._init_channels() diff --git a/ivi/version.py b/ivi/version.py index b6991384..2c49b72b 100644 --- a/ivi/version.py +++ b/ivi/version.py @@ -1 +1 @@ -__version__ = '0.19.3' +__version__ = '0.19.4' From 0ccf6f082db8948456c5884a0661df858048a079 Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Thu, 4 Apr 2019 19:02:29 -0700 Subject: [PATCH 71/79] updated fetch waveform measurement function --- ivi/rigol/rigolDS2000.py | 29 +++++++++++++++++++++++++++++ ivi/rigol/rigolDS2000A.py | 27 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/ivi/rigol/rigolDS2000.py b/ivi/rigol/rigolDS2000.py index 3d9644b2..fcef2a5f 100644 --- a/ivi/rigol/rigolDS2000.py +++ b/ivi/rigol/rigolDS2000.py @@ -89,3 +89,32 @@ def _set_channel_probe_attenuation(self, index, value): self._set_cache_valid(False, 'channel_scale', index) self._set_cache_valid(False, 'channel_range', index) self._set_cache_valid(False, 'trigger_level') + + def _measurement_fetch_waveform_measurement(self, index, measurement_function, ref_channel = None): + print 'index is ', index + index = ivi.get_index(self._channel_name, index) + if index < self._analog_channel_count: + if measurement_function not in MeasurementFunctionMapping: + raise ivi.ValueNotSupportedException() + func = MeasurementFunctionMapping[measurement_function] + else: + if measurement_function not in MeasurementFunctionMappingDigital: + raise ivi.ValueNotSupportedException() + func = MeasurementFunctionMappingDigital[measurement_function] + if not self._driver_operation_simulate: + # l = func.split(' ') + # l[0] = l[0] + '?' + # if len(l) > 1: + # l[-1] = l[-1] + ',' + # func = ' '.join(l) + query = ":measure:%s? %s" % (func, self._channel_name[index]) + print 'query is ', query + if measurement_function in ['phase', 'delay']: + ref_index = ivi.get_index(self._channel_name, ref_channel) + query += ", %s" % self._channel_name[ref_index] + meas = self._ask(query) + if meas == 'measure error!': + return meas + else: + return float(meas) + return 0 \ No newline at end of file diff --git a/ivi/rigol/rigolDS2000A.py b/ivi/rigol/rigolDS2000A.py index 3d9644b2..19cc900e 100644 --- a/ivi/rigol/rigolDS2000A.py +++ b/ivi/rigol/rigolDS2000A.py @@ -89,3 +89,30 @@ def _set_channel_probe_attenuation(self, index, value): self._set_cache_valid(False, 'channel_scale', index) self._set_cache_valid(False, 'channel_range', index) self._set_cache_valid(False, 'trigger_level') + + def _measurement_fetch_waveform_measurement(self, index, measurement_function, ref_channel = None): + index = ivi.get_index(self._channel_name, index) + if index < self._analog_channel_count: + if measurement_function not in MeasurementFunctionMapping: + raise ivi.ValueNotSupportedException() + func = MeasurementFunctionMapping[measurement_function] + else: + if measurement_function not in MeasurementFunctionMappingDigital: + raise ivi.ValueNotSupportedException() + func = MeasurementFunctionMappingDigital[measurement_function] + if not self._driver_operation_simulate: + # l = func.split(' ') + # l[0] = l[0] + '?' + # if len(l) > 1: + # l[-1] = l[-1] + ',' + # func = ' '.join(l) + query = ":measure:%s? %s" % (func, self._channel_name[index]) + if measurement_function in ['phase', 'delay']: + ref_index = ivi.get_index(self._channel_name, ref_channel) + query += ", %s" % self._channel_name[ref_index] + meas = self._ask(query) + if meas == 'measure error!': + return meas + else: + return float(meas) + return 0 \ No newline at end of file From e7c98a5de3752fcd98655f8823b2cbbc2a06bc40 Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Thu, 4 Apr 2019 19:03:39 -0700 Subject: [PATCH 72/79] version 19.5 --- ivi/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi/version.py b/ivi/version.py index 2c49b72b..cd5705a4 100644 --- a/ivi/version.py +++ b/ivi/version.py @@ -1 +1 @@ -__version__ = '0.19.4' +__version__ = '0.19.5' From 7e0aabb6f23e7dd5a5e54d55f9ddd0f7707aff85 Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Fri, 12 Apr 2019 11:36:04 -0700 Subject: [PATCH 73/79] updated tektronix driver support --- ivi/tektronix/__init__.py | 1 + ivi/version.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ivi/tektronix/__init__.py b/ivi/tektronix/__init__.py index 88598feb..63f21ead 100644 --- a/ivi/tektronix/__init__.py +++ b/ivi/tektronix/__init__.py @@ -31,6 +31,7 @@ from .tektronixDPO2012 import tektronixDPO2012 # DPO2000B from .tektronixDPO2024B import tektronixDPO2024B +from .tektronixDPO2022B import tektronixDPO2022B from .tektronixDPO2014B import tektronixDPO2014B from .tektronixDPO2012B import tektronixDPO2012B from .tektronixDPO2004B import tektronixDPO2004B diff --git a/ivi/version.py b/ivi/version.py index cd5705a4..bd5657d5 100644 --- a/ivi/version.py +++ b/ivi/version.py @@ -1 +1 @@ -__version__ = '0.19.5' +__version__ = '0.19.6' From e6b9e1272e79e3fa982f1c083065fd679055b2bb Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Tue, 7 May 2019 12:05:51 -0700 Subject: [PATCH 74/79] updates for Rigol --- ivi/rigol/rigolBaseScope.py | 14 +- ivi/rigol/rigolBaseWG.py | 343 ++++++++++++++++++++++++++++++++++++ ivi/rigol/rigolMSO5072.py | 37 +++- ivi/version.py | 2 +- 4 files changed, 386 insertions(+), 10 deletions(-) create mode 100644 ivi/rigol/rigolBaseWG.py diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index f0b60842..87dc8fad 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -111,13 +111,8 @@ #'phase': 'rphase', #'delay': 'rdelay'} MeasurementFunctionMappingDigital = { - 'rise_time': 'risetime', - 'fall_time': 'falltime', 'frequency': 'frequency', - 'period': 'period', - 'width_negative': 'nwidth', - 'width_positive': 'pwidth', - 'duty_cycle_positive': 'dutycycle'} + 'period': 'period',} ScreenshotImageFormatMapping = { 'tif': 'tiff', 'tiff': 'tiff', @@ -146,6 +141,8 @@ class rigolBaseScope(scpi.common.IdnCommand, scpi.common.ErrorQuery, scpi.common "Rigol generic IVI oscilloscope driver" def __init__(self, *args, **kwargs): + cls = 'IviScope' + grp = 'Base' self.__dict__.setdefault('_instrument_id', '') self._analog_channel_name = list() self._analog_channel_count = 4 @@ -573,7 +570,7 @@ def _get_acquisition_number_of_points_minimum(self): if value == 'AUTO': self._acquisition_number_of_points_minimum = value else: - self._acquisition_number_of_points_minimum = int(value) + self._acquisition_number_of_points_minimum = float(value) return self._acquisition_number_of_points_minimum def _set_acquisition_number_of_points_minimum(self, value): @@ -588,7 +585,7 @@ def _set_acquisition_number_of_points_minimum(self, value): def _get_acquisition_record_length(self): if not self._driver_operation_simulate: - self._acquisition_record_length = int(self._ask(":acquire:mdepth?")) + self._acquisition_record_length = float(self._ask(":acquire:mdepth?")) return self._acquisition_record_length def _get_acquisition_time_per_record(self): @@ -1270,3 +1267,4 @@ def _set_trigger_modifier(self, value): def _measurement_auto_setup(self): if not self._driver_operation_simulate: self._write(":autoscale") + diff --git a/ivi/rigol/rigolBaseWG.py b/ivi/rigol/rigolBaseWG.py new file mode 100644 index 00000000..1d7f5ed8 --- /dev/null +++ b/ivi/rigol/rigolBaseWG.py @@ -0,0 +1,343 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +import numpy as np +import struct + +from .. import ivi +from .. import fgen + +OutputMode = set(['function', 'arbitrary']) +OperationMode = set(['continuous']) +StandardWaveformMapping = { + 'sin': 'sin', + 'squ': 'squ', + 'ramp': 'ramp', + 'dc': 'dc', + 'puls': 'puls', + 'nois': 'nois', + 'sinc': 'sinc', + 'exprise': 'exprise', + 'expfall': 'expfall', + 'ecg': 'ecg', + 'gaussian': 'gaussian', + 'lorentz': 'lorentz', + 'haversine': 'haversine', + } + +class rigolBaseWG(fgen.Base, fgen.StdFunc, fgen.ArbWfm, fgen.ArbFrequency, + fgen.ArbChannelWfm): + "Rigol Oscilloscope WG option IVI waveform generator driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS1074Z') + + self._output_standard_waveform_pulse_width = list() + self._output_standard_waveform_symmetry = list() + self._output_noise_enabled = list() + self._output_noise_percent = list() + + super(rigolBaseWG, self).__init__(*args, **kwargs) + + # WG option + self._output_count = 2 + self._arbitrary_sample_rate = 0 + self._arbitrary_waveform_number_waveforms_max = 0 + self._arbitrary_waveform_size_max = 131072 + self._arbitrary_waveform_size_min = 2 + self._arbitrary_waveform_quantum = 1 + + self._add_property('outputs[].standard_waveform.symmetry', + self._get_output_standard_waveform_symmetry, + self._set_output_standard_waveform_symmetry, + None, + """ + Specifies the symmetry for a ramp or triangle waveform. This attribute + affects function generator behavior only when the Waveform attribute is + set to Waveform Triangle, Ramp Up, or Ramp Down. The value is expressed + as a percentage. + """) + + + self._identity_description = "Rigol Oscilloscope WG option IVI function generator driver" + self._identity_supported_instrument_models = ['DS1074Z', 'DS1104Z', 'DS1074ZPlus', + 'DS1104ZPlus', 'MSO1074Z', 'MSO1104Z', 'DS2072A', 'MSO2072A', 'MSO5072', 'MSO5074', + 'DS7014', 'MSO7014', 'MSO8064'] + + self._init_outputs() + + def _init_outputs(self): + try: + super(rigolBaseWG, self)._init_outputs() + except AttributeError: + pass + self._output_name = list() + self._source_name = list() + self._output_operation_mode = list() + self._output_enabled = list() + self._output_impedance = list() + self._output_mode = list() + self._output_reference_clock_source = list() + self._output_standard_waveform_pulse_width = list() + self._output_standard_waveform_ramp_symmetry = list() + self._output_noise_enabled = list() + self._output_noise_percent = list() + for i in range(self._output_count): + if self._output_count == 1: + self._output_name.append("output") + else: + self._output_name.append("output%d" % (i+1)) + self._output_operation_mode.append('continuous') + self._output_enabled.append(False) + self._output_impedance.append(50) + self._output_mode.append('function') + self._output_reference_clock_source.append('internal') + self._output_standard_waveform_pulse_width.append(100e-6) + self._output_standard_waveform_symmetry.append(50.0) + self._output_noise_enabled.append(False) + self._output_noise_percent.append(10.0) + + #create source channels + for i in range(self._output_count): + if self._output_count == 1: + self._source_name.append("source") + else: + self._source_name.append("source%d" % (i+1)) + + self.outputs._set_list(self._output_name) + + + # AFG option + def _get_output_enabled(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s?" % (self._output_name[index])) + self._output_enabled[index] = bool(int(resp)) + self._set_cache_valid(index=index) + return self._output_enabled[index] + + def _set_output_enabled(self, index, value): + index = ivi.get_index(self._output_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s %d" % (self._output_name[index], value)) + self._output_enabled[index] = value + self._set_cache_valid(index=index) + + + def _get_output_impedance(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + val = self._ask(":%s:impedance?" % self._output_name[index]) + if val == 'OMEG': + self._output_impedance[index] = "HighZ" + elif val == 'FIFT': + self._output_impedance[index] = "50Ohms" + self._set_cache_valid(index=index) + return self._output_impedance[index] + + def _set_output_impedance(self, index, value): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + if value == "HighZ": + self._write(":%s:impedance OMEG" % self._output_name[index]) + elif value == "50Ohms": + self._write(":%s:impedance FIFTY" % self._output_name[index]) + self._output_impedance[index] = value + self._set_cache_valid(index=index) + + def _get_output_settings(self, index): + index = ivi.get_index(self._output_name, index) + self._output_settings = {} + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:apply?" % self._source_name[index]) + settings = resp.split(',') + wtype = settings[0].lower() + wtype = [k for k,v in StandardWaveformMapping.items() if v==wtype][0] + self._output_settings["wtype"] = wtype + self._output_settings["amplitude"] = settings[2] + self._output_settings["offset"] = settings[3] + if wtype != 'noise': + self._output_settings["freq"] = settings[1] + self._output_settings["startphase"] = settings[4] + self._set_cache_valid(index=index) + return self._output_settings + + def _set_output_settings(self, index, wtype, amplitude, offset, freq = "", startphase = ""): + #fix source index + index = ivi.get_index(self._output_name, index) + wtype = StandardWaveformMapping[wtype] + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + if wtype == "NOISE": + self._write(":%s:APPLY:NOISE %s, %s" % (self._source_name[index], amplitude, offset)) + else: + self._write(":%s:APPLY:%s %s, %s, %s, %s" % (self._source_name[index], wtype, freq, amplitude, offset, startphase)) + self._set_cache_valid(index=index) + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_frequency(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:frequency?" % self._source_name[index]) + self._output_standard_waveform_frequency[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_frequency[index] + + def _set_output_standard_waveform_frequency(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < 0.1 or value > 50e6: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:frequency %e" % (self._source_name[index], value)) + self._output_standard_waveform_frequency[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_start_phase(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:phase?" % self._source_name[index]) + self._output_standard_waveform_start_phase[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_start_phase[index] + + def _set_output_standard_waveform_start_phase(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < -180.0 or value > 180.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:phase %e" % (self._source_name[index], value)) + self._output_standard_waveform_start_phase[index] = value + self._set_cache_valid(index=index) + + + def _get_output_standard_waveform_waveform(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:function?" % self._source_name[index]).lower() + if resp == 'arbitrary': + resp = 'sine' + resp = [k for k,v in StandardWaveformMapping.items() if v==resp][0] + if resp == 'ramp_up': + if self._get_output_standard_waveform_symmetry(index) <= 10.0: + resp = 'ramp_down' + elif self._get_output_standard_waveform_symmetry(index) >= 90.0: + resp = 'ramp_up' + else: + resp = 'triangle' + self._output_standard_waveform_waveform[index] = resp + self._set_cache_valid(index=index) + return self._output_standard_waveform_waveform[index] + + def _set_output_standard_waveform_waveform(self, index, value): + index = ivi.get_index(self._output_name, index) + if value not in StandardWaveformMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":%s:function %s" % (self._source_name[index], StandardWaveformMapping[value])) + if value == 'triangle': + if self._get_output_standard_waveform_symmetry(index) <= 10.0 or self._get_output_standard_waveform_symmetry(index) >= 90: + self._set_output_standard_waveform_symmetry(index, 50.0) + elif value == 'ramp_up': + self._set_output_standard_waveform_symmetry(index, 100.0) + elif value == 'ramp_down': + self._set_output_standard_waveform_symmetry(index, 0.0) + self._output_standard_waveform_waveform[index] = value + self._set_cache_valid(index=index) + self._output_mode[index] = 'function' + self._set_cache_valid(True, 'output_mode', index=index) + + def _get_output_standard_waveform_symmetry(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:function:ramp:symmetry?" % self._source_name[index]) + self._output_standard_waveform_symmetry[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_symmetry[index] + + def _set_output_standard_waveform_symmetry(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < 0.0 or value > 100.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:function:ramp:symmetry %e" % (self._source_name[index], value)) + self._output_standard_waveform_symmetry[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_amplitude(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:voltage?" % self._source_name[index]) + self._output_standard_waveform_amplitude[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_amplitude[index] + + def _set_output_standard_waveform_amplitude(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < 0.01 or value > 5.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:voltage %e" % (self._source_name[index], value)) + self._output_standard_waveform_amplitude[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_dc_offset(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:voltage:offset?" % self._source_name[index]) + self._output_standard_waveform_dc_offset[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_dc_offset[index] + + def _set_output_standard_waveform_dc_offset(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:voltage:offset %e" % (self._source_name[index], value)) + self._output_standard_waveform_dc_offset[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_duty_cycle_high(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:pulse:dcycle?" % self._source_name[index]) + self._output_standard_waveform_duty_cycle_high[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_duty_cycle_high[index] + + def _set_output_standard_waveform_duty_cycle_high(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + print value + if value < 10.0 or value > 90.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:pulse:dcycle %e" % (self._source_name[index], value)) + self._output_standard_waveform_duty_cycle_high[index] = value + self._set_cache_valid(index=index) diff --git a/ivi/rigol/rigolMSO5072.py b/ivi/rigol/rigolMSO5072.py index 9f4ac990..7c036390 100644 --- a/ivi/rigol/rigolMSO5072.py +++ b/ivi/rigol/rigolMSO5072.py @@ -26,10 +26,16 @@ from .rigolDS5000 import * +MeasurementFunctionMappingDigital = { + 'frequency': 'frequency', + 'period': 'period',} + class rigolMSO5072(rigolDS5000): "Rigol MSO5072 IVI oscilloscope driver" def __init__(self, *args, **kwargs): + cls = 'IviScope' + grp = 'Base' self.__dict__.setdefault('_instrument_id', 'MSO5072') super(rigolMSO5072, self).__init__(*args, **kwargs) @@ -39,5 +45,34 @@ def __init__(self, *args, **kwargs): self._channel_count = self._analog_channel_count + self._digital_channel_count self._bandwidth = 100e6 self._bandwidth_limit = {'20M': 20e6} - + self._output_count = 2 + self._add_method('measurement.fetch_waveform_digital', self._measurement_fetch_waveform_digital, ivi.Doc("""description goes here""", cls, grp, '4.3.13')) self._init_channels() + self._init_outputs() + + def _measurement_fetch_waveform_digital(self, index): + raw_data = [] + + if self._driver_operation_simulate: + return list() + self._write(":waveform:source %s" % index) + # Read preamble + pre = self._ask(":waveform:preamble?").split(',') + + xinc = float(pre[4]) + xorg = float(pre[5]) + xref = int(float(pre[6])) + +# if format != 0: +# raise UnexpectedResponseException() + + # Read waveform data + raw_data.append(self._ask_for_ieee_block(':WAVeform:DATA?')) + self._read_raw() # flush buffer + + # convert string of hex values to list of hex strings + data_list = list(raw_data[0]) + + # convert to times + data = [((((k-xref)*xinc) + xorg), e) for k,e in enumerate(data_list)] + return data diff --git a/ivi/version.py b/ivi/version.py index bd5657d5..418b7752 100644 --- a/ivi/version.py +++ b/ivi/version.py @@ -1 +1 @@ -__version__ = '0.19.6' +__version__ = '0.19.7' From 90595c16a24937dbba03311ba3c3cadf03640d23 Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Tue, 7 May 2019 12:18:37 -0700 Subject: [PATCH 75/79] update to inits --- ivi/rigol/rigolDS5000.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ivi/rigol/rigolDS5000.py b/ivi/rigol/rigolDS5000.py index 4895c391..0cebdc24 100644 --- a/ivi/rigol/rigolDS5000.py +++ b/ivi/rigol/rigolDS5000.py @@ -25,8 +25,10 @@ """ from .rigolBaseScope import * +from .rigolBaseWG import * -class rigolDS5000(rigolBaseScope): + +class rigolDS5000(rigolBaseScope, rigolBaseWG): "Rigol DS5000 series IVI oscilloscope driver" def __init__(self, *args, **kwargs): @@ -48,6 +50,8 @@ def __init__(self, *args, **kwargs): 'MSO5104', 'MSO5204', 'MSO5354'] self._init_channels() + self._init_outputs() + def _display_fetch_screenshot(self, format='bmp', invert=False): if self._driver_operation_simulate: From 587f427927607e4ef667bc0e7e9975a4a988c95a Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Tue, 7 May 2019 12:19:22 -0700 Subject: [PATCH 76/79] rev version --- ivi/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi/version.py b/ivi/version.py index 418b7752..bc978220 100644 --- a/ivi/version.py +++ b/ivi/version.py @@ -1 +1 @@ -__version__ = '0.19.7' +__version__ = '0.19.8' From 77e545b18b8bf76f7fbc510c8f352e38682a8feb Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Wed, 15 May 2019 13:33:15 -0700 Subject: [PATCH 77/79] changed autoscale to autoset --- ivi/rigol/rigolBaseScope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index 87dc8fad..34b7210f 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -1266,5 +1266,5 @@ def _set_trigger_modifier(self, value): def _measurement_auto_setup(self): if not self._driver_operation_simulate: - self._write(":autoscale") + self._write(":autoset") From 985c8ef24fa9ab92e9c9f7976c680f62ad62c81a Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Wed, 15 May 2019 13:34:14 -0700 Subject: [PATCH 78/79] rev'd version --- ivi/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi/version.py b/ivi/version.py index bc978220..8481d431 100644 --- a/ivi/version.py +++ b/ivi/version.py @@ -1 +1 @@ -__version__ = '0.19.8' +__version__ = '0.19.9' From 169e6657cd04e2a6a81f864511bdf92c00396dfb Mon Sep 17 00:00:00 2001 From: nicedwarf Date: Fri, 24 May 2019 15:25:40 -0700 Subject: [PATCH 79/79] updated acquisition length --- ivi/rigol/rigolBaseScope.py | 6 +++++- ivi/version.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py index 34b7210f..4a631fc1 100644 --- a/ivi/rigol/rigolBaseScope.py +++ b/ivi/rigol/rigolBaseScope.py @@ -585,7 +585,11 @@ def _set_acquisition_number_of_points_minimum(self, value): def _get_acquisition_record_length(self): if not self._driver_operation_simulate: - self._acquisition_record_length = float(self._ask(":acquire:mdepth?")) + val = self._ask(":acquire:mdepth?") + if val == "AUTO": + self._acquisition_record_length = val + else: + self._acquisition_record_length =float(val) return self._acquisition_record_length def _get_acquisition_time_per_record(self): diff --git a/ivi/version.py b/ivi/version.py index 8481d431..f759c3de 100644 --- a/ivi/version.py +++ b/ivi/version.py @@ -1 +1 @@ -__version__ = '0.19.9' +__version__ = '0.19.10'