diff --git a/.gitignore b/.gitignore index c18dd8d..64777da 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ __pycache__/ +.DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1c69b3d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ +v0.2 +- More PEP8 compliant: + - `func_gen()` -> `FuncGen()` + - `func_gen_channel()` -> `FuncGenChannel()` + - All lines < 100 characters (mostly < 80) +- No more `enable`/`disable_frequency_lock()`, now `set_frequency_lock()` +- Settings dictionary now contains tuples of value and unit, e.g. `settings = {"amplitude": (3, "Vpp"), ..}` +- Implemented `set_settings()` in both `FuncGen` and `FuncGenChannel` that takes a settings dictionary as input +- `get`/`set_output()` links to `get`/`set_output_state()` +- More examples +- Expanded README + +v0.1 +- First release diff --git a/README.md b/README.md index 128a46f..e705be1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Tektronix arbitrary function generator control through PyVISA -v0.1.0 // Dec 2019 +v0.2.0 // Dec 2019 API documentation can be found at [GitHub pages](https://asvela.github.io/tektronix-func-gen/) or in the repository [docs/index.html](docs/index.html). (To build the documentation yourself use [pdoc3](https://pdoc3.github.io/pdoc/) and run `$ pdoc --html tektronix_func_gen`.) @@ -10,18 +10,21 @@ API documentation can be found at [GitHub pages](https://asvela.github.io/tektro Put the module file in the folder wherein the file you will import it from resides. -## Usage +## Usage (through examples) -An example: +An example of basic control ```python import tektronix_func_gen as tfg -with tfg.func_gen('VISA ADDRESS OF YOUR INSTRUMENT') as fgen: +with tfg.FuncGen('VISA ADDRESS OF YOUR INSTRUMENT') as fgen: fgen.ch1.set_function("SIN") fgen.ch1.set_frequency(25, unit="Hz") + fgen.ch1.set_offset(50, unit="mV") + fgen.ch1.set_amplitude(0.002) fgen.ch1.set_output("ON") fgen.ch2.set_output("OFF") + # alternatively fgen.ch1.print_settings() to show from one channel only fgen.print_settings() ``` @@ -37,20 +40,42 @@ Current settings for TEKTRONIX AFG1022 XXXXX output ON OFF function SIN RAMP amplitude 0.002 1 Vpp - offset 0.0 -0.45 V + offset 0.05 -0.45 V frequency 25.0 10.0 Hz ``` -More examples are included at the end of the module. +Settings can also be stored and restored: -### Impedance - -Unfortunately the impedance (50Ω or high Z) cannot be controlled or read remotely. Which setting is in use affects the limits of the output voltage. Use the optional impedance keyword in the initialisation of the func_gen object to make the object aware what limits applies: `func_gen('VISA ADDRESS OF YOUR INSTRUMENT', impedance=("highZ", "50ohm"))`. +```python +"""Example showing how to connect, get the current settings of +the instrument, store them, change a setting and then restore the +initial settings""" +import tektronix_func_gen as tfg +with tfg.FuncGen('VISA ADDRESS OF YOUR INSTRUMENT') as fgen: + fgen.print_settings() + print("Saving these settings..") + settings = fgen.get_settings() + print("Change to 1Vpp amplitude for channel 1..") + fgen.ch1.set_amplitude(1) + fgen.print_settings() + print("Reset back to initial settings..") + fgen.set_settings(settings) + fgen.print_settings() +``` ### Syncronisation and frequency lock -The phase of the two channels can be syncronised with `syncronise_waveforms()`. Frequency lock can also be enabled/disabled with `enable_frequency_lock()`/`disable_frequency_lock()`. +The phase of the two channels can be syncronised with `syncronise_waveforms()`. Frequency lock can also be enabled/disabled with `set_frequency_lock()`: + +```python +"""Example showing the frequency being set to 10Hz and then the frequency +lock enabled, using the frequency at ch1 as the common frequency""" +import tektronix_func_gen as tfg +with tfg.FuncGen('VISA ADDRESS OF YOUR INSTRUMENT', verbose=False) as fgen: + fgen.ch1.set_frequency(10) + fgen.set_frequency_lock("ON", use_channel=1) +``` ### Arbitrary waveforms @@ -61,7 +86,7 @@ The length of the waveform must be between 2 and 8192 points. ```python import numpy as np import tektronix_func_gen as tfg -with tfg.func_gen('VISA ADDRESS OF YOUR INSTRUMENT') as fgen: +with tfg.FuncGen('VISA ADDRESS OF YOUR INSTRUMENT') as fgen: # create waveform x = np.linspace(0, 4*np.pi, 8000) waveform = np.sin(x)+x/5 @@ -82,7 +107,7 @@ with tfg.func_gen('VISA ADDRESS OF YOUR INSTRUMENT') as fgen: ### Set voltage and frequency limits -Limits for amplitude, voltage and frequency can be set by accessing the `func_gen_channel.channel_limits` dictionary or using the `set_stricter_limits()`. The dictionary has the following structure (these are the standard limits for AFG1022) +Limits for amplitude, voltage and frequency for each channel are kept in a dictionary `FuncGenChannel.channel_limits` (these are the standard limits for AFG1022) ```python channel_limits = { @@ -92,3 +117,27 @@ channel_limits = { "amplitude lims": ({"50ohm": {"min": 0.001, "max": 10}, "highZ": {"min": 0.002, "max": 20}}, "Vpp")} ``` + +They chan be changed by `FuncGenChannel.set_limit()`, or by using the `FuncGenChannel.set_stricter_limits()` for a series of prompts. + +```python +import tektronix_func_gen as tfg +"""Example showing how limits can be read and changed""" +with tfg.FuncGen('VISA ADDRESS OF YOUR INSTRUMENT') as fgen: + lims = fgen.ch1.get_frequency_lims() + print("Channel 1 frequency limits: {}".format(lims)) + print("Change the lower limit to 2Hz..") + fgen.ch1.set_limit("frequency lims", "min", 2) + lims = fgen.ch1.get_frequency_lims() + print("Channel 1 frequency limits: {}".format(lims)) + print("Try to set ch1 frequency to 1Hz..") + try: + fgen.ch1.set_frequency(1) + except NotSetError as err: + print(err) +``` + + +### Impedance + +Unfortunately the impedance (50Ω or high Z) cannot be controlled or read remotely. Which setting is in use affects the limits of the output voltage. Use the optional impedance keyword in the initialisation of the func_gen object to make the object aware what limits applies: `FuncGen('VISA ADDRESS OF YOUR INSTRUMENT', impedance=("highZ", "50ohm"))`. diff --git a/docs/index.html b/docs/tektronix_func_gen.html similarity index 52% rename from docs/index.html rename to docs/tektronix_func_gen.html index 984e773..3d1fe11 100644 --- a/docs/index.html +++ b/docs/tektronix_func_gen.html @@ -21,7 +21,8 @@
tektronix_func_gen
Control of Tektronix AFG1022 function generator through PyVISA
-To build the documentation use pdoc3 and run $ pdoc --html tektronix_func_gen
To build the documentation use pdoc3 and
+run $ pdoc --html tektronix_func_gen
Andreas Svela 2019
unit
: str
factor
: float
or None
None
if the unit string is emptyNone
+if the unit string is empty
-def example_lock_frequencies(address)
+
+def example_basic_control(address)
-Example showing the frequency being set to 10Hz and then the frequency
-lock enabled, using the frequency at ch1 as the common frequency
+Example showing how to connect, and the most basic control of the
+instrument parameteres
Expand source code
-def example_lock_frequencies(address):
- """Example showing the frequency being set to 10Hz and then the frequency
- lock enabled, using the frequency at ch1 as the common frequency"""
- with func_gen(address, verbose=False) as fgen:
- fgen.ch1.set_frequency(10)
- fgen.enable_frequency_lock(use_channel=1)
+def example_basic_control(address):
+ """Example showing how to connect, and the most basic control of the
+ instrument parameteres"""
+ print("\n\n", example_basic_control.__doc__)
+ with FuncGen(address) as fgen:
+ fgen.ch1.set_function("SIN")
+ fgen.ch1.set_frequency(25, unit="Hz")
+ fgen.ch1.set_offset(50, unit="mV")
+ fgen.ch1.set_amplitude(0.002)
+ fgen.ch1.set_output("ON")
+ fgen.ch2.set_output("OFF")
+ # Alternatively fgen.ch1.print_settings() to show from one channel only
+ fgen.print_settings()
-
-def example_print_current_settings(address)
+
+def example_change_settings(address)
-Example showing how to connect and get the current settings of the instrument
+Example showing how to get the current settings of the instrument,
+store them, change a setting and then restore the initial settings
Expand source code
-def example_print_current_settings(address):
- """Example showing how to connect and get the current settings of the instrument"""
- with func_gen(address) as fgen:
+def example_change_settings(address):
+ """Example showing how to get the current settings of the instrument,
+ store them, change a setting and then restore the initial settings"""
+ print("\n\n", example_change_settings.__doc__)
+ with FuncGen(address) as fgen:
+ fgen.print_settings()
+ print("Saving these settings..")
+ settings = fgen.get_settings()
+ print("Change to 1Vpp amplitude for channel 1..")
+ fgen.ch1.set_amplitude(1)
+ fgen.print_settings()
+ print("Reset back to initial settings..")
+ fgen.set_settings(settings)
fgen.print_settings()
+
+def example_changing_limits(address)
+
+
+Example showing how limits can be read and changed
+
+
+Expand source code
+
+def example_changing_limits(address):
+ """Example showing how limits can be read and changed"""
+ print("\n\n", example_changing_limits.__doc__)
+ with FuncGen(address) as fgen:
+ lims = fgen.ch1.get_frequency_lims()
+ print("Channel 1 frequency limits: {}".format(lims))
+ print("Change the lower limit to 2Hz..")
+ fgen.ch1.set_limit("frequency lims", "min", 2)
+ lims = fgen.ch1.get_frequency_lims()
+ print("Channel 1 frequency limits: {}".format(lims))
+ print("Try to set ch1 frequency to 1Hz..")
+ try:
+ fgen.ch1.set_frequency(1)
+ except NotSetError as err:
+ print(err)
+
+
+
+def example_lock_frequencies(address)
+
+
+Example showing the frequency being set to 10Hz and then the frequency
+lock enabled, using the frequency at ch1 as the common frequency
+
+
+Expand source code
+
+def example_lock_frequencies(address):
+ """Example showing the frequency being set to 10Hz and then the frequency
+ lock enabled, using the frequency at ch1 as the common frequency"""
+ print("\n\n", example_lock_frequencies.__doc__)
+ with FuncGen(address, verbose=False) as fgen:
+ fgen.ch1.set_frequency(10)
+ fgen.set_frequency_lock("ON", use_channel=1)
+
+
-def example_set_and_use_custom_waveform(fgen=None, channel=1, plot_signal=True)
+def example_set_and_use_custom_waveform(fgen=None, address=None, channel=1, plot_signal=True)
Example showing a waveform being created, transferred to the instrument,
@@ -1026,33 +1382,37 @@
Returns
Expand source code
-def example_set_and_use_custom_waveform(fgen=None, channel=1, plot_signal=True):
+def example_set_and_use_custom_waveform(fgen=None, address=None, channel=1,
+ plot_signal=True):
"""Example showing a waveform being created, transferred to the instrument,
and applied to a channel"""
- # create a signal
+ print("\n\n", example_set_and_use_custom_waveform.__doc__)
+ # Create a signal
x = np.linspace(0, 4*np.pi, 8000)
signal = np.sin(x)+x/5
if plot_signal: # plot the signal for visual control
import matplotlib.pyplot as plt
plt.plot(signal)
plt.show()
- # create initialise fgen if it was not supplied
+ # Create initialise fgen if it was not supplied
if fgen is None:
- fgen = func_gen()
+ fgen = FuncGen(address)
close_fgen = True # specify that it should be closed at end of function
else:
close_fgen = False # do not close the supplied fgen at end
print("Current waveform catalogue")
- for i, wav in enumerate(fgen.get_waveform_catalogue()): print(" {}: {}".format(i, wav))
- # transfer the waveform
+ for i, wav in enumerate(fgen.get_waveform_catalogue()):
+ print(" {}: {}".format(i, wav))
+ # Transfer the waveform
fgen.set_custom_waveform(signal, memory_num=5, verify=True)
print("New waveform catalogue:")
- for i, wav in enumerate(fgen.get_waveform_catalogue()): print(" {}: {}".format(i, wav))
+ for i, wav in enumerate(fgen.get_waveform_catalogue()):
+ print(" {}: {}".format(i, wav))
print("Set new wavefrom to channel {}..".format(channel), end=" ")
- fgen.channels[channel-1].set_output("OFF")
+ fgen.channels[channel-1].set_output_state("OFF")
fgen.channels[channel-1].set_function("USER5")
print("ok")
- # print current settings
+ # Print current settings
fgen.get_settings()
if close_fgen: fgen.close()
@@ -1062,48 +1422,8 @@ Returns
Classes
-
-class NotCompatibleError
-(*args, **kwargs)
-
--
-
Error for when the instrument is not compatible with this module
-
-
-Expand source code
-
-class NotCompatibleError(Exception):
- """Error for when the instrument is not compatible with this module"""
- pass
-
-Ancestors
-
-- builtins.Exception
-- builtins.BaseException
-
-
-
-class NotSetError
-(*args, **kwargs)
-
--
-
Error for when a value cannot be written to the instrument
-
-
-Expand source code
-
-class NotSetError(Exception):
- """Error for when a value cannot be written to the instrument"""
- pass
-
-Ancestors
-
-- builtins.Exception
-- builtins.BaseException
-
-
-
-class func_gen
+
+class FuncGen
(address, impedance=('highZ', 'highZ'), timeout=5000, verify_param_set=False, override_compatibility=False, verbose=True)
-
@@ -1134,28 +1454,32 @@
Attributes
address
: str
- The VISA address of the instrument
id
: str
-- Comma separated string with maker, model, serial and firmware of the instrument
+- Comma separated string with maker, model, serial and firmware of
+the instrument
inst
: pyvisa.resources.Resource
- The PyVISA resource
-channels
: tuple
of func_gen_channel
+channels
: tuple
of FuncGenChannel
- Objects to control the channels
-ch1
: func_gen_channel
+ch1
: FuncGenChannel
- Short hand for
channels[0]
Object to control channel 1
-ch2
: func_gen_channel
+ch2
: FuncGenChannel
- Short hand for
channels[1]
Object to control channel 2
instrument_limits
: dict
- Contains the following keys with subdictionaries
frequency lims
-Containing the frequency limits for the instrument where the keys "min"
-and "max" have values corresponding to minimum and maximum frequencies in Hz
+Containing the frequency limits for the instrument where the keys
+"min" and "max" have values corresponding to minimum and maximum
+frequencies in Hz
voltage lims
-Contains the maximum absolute voltage the instrument can output for the keys
-"50ohm" and "highZ" according to the impedance setting
+Contains the maximum absolute voltage the instrument can output
+for the keys "50ohm" and "highZ" according to the impedance setting
amplitude lims
-Contains the smallest and largest possible amplitudes where the keys
-"50ohm" and "highZ" will have subdictionaries with keys "min" and "max"
+Contains the smallest and largest possible amplitudes where the
+keys "50ohm" and "highZ" will have subdictionaries with keys
+"min" and "max"
arbitrary_waveform_length
: list
-The permitted minimum and maximum length of an arbitrary waveform, e.g. [2, 8192]
+The permitted minimum and maximum length of an arbitrary waveform,
+e.g. [2, 8192]
arbitrary_waveform_resolution
: int
The vertical resolution of the arbitrary waveform, for instance 14 bit
=> 2**14-1 = 16383
@@ -1165,13 +1489,15 @@ Raises
pyvisa.Error
If the supplied VISA address cannot be connected to
NotCompatibleError
-If the instrument limits for the model connected to are not known (Call the class with override_compatibility=True
to override and use AFG1022 limits)
+If the instrument limits for the model connected to are not known
+(Call the class with override_compatibility=True
to override and
+use AFG1022 limits)
Expand source code
-class func_gen():
+class FuncGen():
"""Class for interacting with Tektronix function generator
Parameters
@@ -1200,28 +1526,32 @@ Raises
address : str
The VISA address of the instrument
id : str
- Comma separated string with maker, model, serial and firmware of the instrument
+ Comma separated string with maker, model, serial and firmware of
+ the instrument
inst : `pyvisa.resources.Resource`
The PyVISA resource
- channels : tuple of func_gen_channel
+ channels : tuple of FuncGenChannel
Objects to control the channels
- ch1 : func_gen_channel
+ ch1 : FuncGenChannel
Short hand for `channels[0]` Object to control channel 1
- ch2 : func_gen_channel
+ ch2 : FuncGenChannel
Short hand for `channels[1]` Object to control channel 2
instrument_limits : dict
Contains the following keys with subdictionaries
`frequency lims`
- Containing the frequency limits for the instrument where the keys "min"
- and "max" have values corresponding to minimum and maximum frequencies in Hz
+ Containing the frequency limits for the instrument where the keys
+ "min" and "max" have values corresponding to minimum and maximum
+ frequencies in Hz
`voltage lims`
- Contains the maximum absolute voltage the instrument can output for the keys
- "50ohm" and "highZ" according to the impedance setting
+ Contains the maximum absolute voltage the instrument can output
+ for the keys "50ohm" and "highZ" according to the impedance setting
`amplitude lims`
- Contains the smallest and largest possible amplitudes where the keys
- "50ohm" and "highZ" will have subdictionaries with keys "min" and "max"
+ Contains the smallest and largest possible amplitudes where the
+ keys "50ohm" and "highZ" will have subdictionaries with keys
+ "min" and "max"
arbitrary_waveform_length : list
- The permitted minimum and maximum length of an arbitrary waveform, e.g. [2, 8192]
+ The permitted minimum and maximum length of an arbitrary waveform,
+ e.g. [2, 8192]
arbitrary_waveform_resolution : int
The vertical resolution of the arbitrary waveform, for instance 14 bit
=> 2**14-1 = 16383
@@ -1231,11 +1561,14 @@ Raises
pyvisa.Error
If the supplied VISA address cannot be connected to
NotCompatibleError
- If the instrument limits for the model connected to are not known (Call the class with `override_compatibility=True` to override and use AFG1022 limits)
+ If the instrument limits for the model connected to are not known
+ (Call the class with `override_compatibility=True` to override and
+ use AFG1022 limits)
"""
def __init__(self, address, impedance=("highZ",)*2, timeout=5000,
- verify_param_set=False, override_compatibility=False, verbose=True):
+ verify_param_set=False, override_compatibility=False,
+ verbose=True):
self.override_compatibility = override_compatibility
self.address = address
self.timeout = timeout
@@ -1248,24 +1581,26 @@ Raises
print("\nVisaError: Could not connect to \'{}\'".format(address))
raise
self.inst.timeout = self.timeout
- # Clear all the event registers and queues used in the instrument status and event reporting system
+ # Clear all the event registers and queues used in the instrument
+ # status and event reporting system
self.write("*CLS")
# Get information about the connected device
- self.id = self.query("*IDN?") # get the id of the connected device
- if self.id == '': # second query might be needed due to unknown reason (suspecting *CLS does something weird)
+ self.id = self.query("*IDN?")
+ # Second query might be needed due to unknown reason
+ # (suspecting *CLS does something weird)
+ if self.id == '':
self.id = self.query("*IDN?")
self.maker, self.model, self.serial = self.id.split(",")[:3]
- if self.verbose: print("Connected to {} model {}, serial {}".format(self.maker, self.model, self.serial))
+ if self.verbose:
+ print("Connected to {} model {}, serial"
+ " {}".format(self.maker, self.model, self.serial))
self.initialise_model_properties()
- self.channels = self.spawn_channel(1, impedance[0]), self.spawn_channel(2, impedance[1])
+ self.channels = (self.spawn_channel(1, impedance[0]),
+ self.spawn_channel(2, impedance[1]))
self.ch1, self.ch2 = self.channels
- # __enter__ and __exit__ functions defined to use the module with a 'with' statement
- # with func_gen(verify_param_set=True) as fgen:
- # fgen.get_settings()
- # This gives automatic handling of closing down the connection when fgen goes out of scope
- # The kwargs in __enter__ will be passed on to __init__
def __enter__(self, **kwargs):
+ # The kwargs will be passed on to __init__
return self
def __exit__(self, exc_type, exc_val, exc_tb):
@@ -1285,20 +1620,25 @@ Raises
Raises
------
NotCompatibleError
- If the connected model is not necessarily compatible with this package,
- limits are not known.
+ If the connected model is not necessarily compatible with this
+ package, slimits are not known.
"""
if self.model == 'AFG1022' or self.override_compatibility:
self.instrument_limits = {
- "frequency lims": ({"min": 1e-6, "max": 25e6}, "Hz"),
- "voltage lims": ({"50ohm": {"min": -5, "max": 5},
- "highZ": {"min": -10, "max": 10}}, "V"),
- "amplitude lims": ({"50ohm": {"min": 0.001, "max": 10},
- "highZ": {"min": 0.002, "max": 20}}, "Vpp")}
- self.arbitrary_waveform_length = [2, 8192] # min length, max length
- self.arbitrary_waveform_resolution = 16383 # 14 bit
+ "frequency lims": ({"min": 1e-6, "max": 25e6}, "Hz"),
+ "voltage lims": ({"50ohm": {"min": -5, "max": 5},
+ "highZ": {"min": -10, "max": 10}}, "V"),
+ "amplitude lims": ({"50ohm": {"min": 0.001, "max": 10},
+ "highZ": {"min": 0.002, "max": 20}}, "Vpp")}
+ self.arbitrary_waveform_length = [2, 8192] # min length, max length
+ self.arbitrary_waveform_resolution = 16383 # 14 bit
else:
- raise NotCompatibleError("Model {} not supported, no limits set!\n\tTo use the limits for AFG1022, call the class with 'override_compatibility=True'\n\tNote that this might lead to unexpected behaviour for custom waveforms and 'MIN'/'MAX' keywords.")
+ msg = ("Model {} not supported, no limits set!"
+ "\n\tTo use the limits for AFG1022, call the class with "
+ "'override_compatibility=True'"
+ "\n\tNote that this might lead to unexpected behaviour "
+ "for custom waveforms and 'MIN'/'MAX' keywords.")
+ raise NotCompatibleError(msg)
def write(self, command, custom_err_message=None):
"""Write a VISA command to the instrument
@@ -1308,8 +1648,10 @@ Raises
command : str
The VISA command to be written to the instrument
custom_err_message : str, default `None`
- When `None`, the RuntimeError message is "Writing command {command} failed: pyvisa returned StatusCode ..".
- Otherwise, if a message is supplied "Could not {message}: pyvisa returned StatusCode .."
+ When `None`, the RuntimeError message is "Writing command {command}
+ failed: pyvisa returned StatusCode ..".
+ Otherwise, if a message is supplied "Could not {message}:
+ pyvisa returned StatusCode .."
Returns
-------
@@ -1321,15 +1663,20 @@ Raises
Raises
------
RuntimeError
- If status returned by PyVISA write command is not `pyvisa.constants.StatusCode.success`
+ If status returned by PyVISA write command is not
+ `pyvisa.constants.StatusCode.success`
"""
- bytes, status = self.inst.write(command)
+ num_bytes, status = self.inst.write(command)
if not status == pyvisa.constants.StatusCode.success:
if custom_err_message is not None:
- raise RuntimeError("Could not {}: pyvisa returned StatusCode {} ({})".format(custom_err_message, status, str(status)))
+ msg = ("Could not {}: pyvisa returned StatusCode {} "
+ "({})".format(custom_err_message, status, str(status)))
+ raise RuntimeError(msg)
else:
- raise RuntimeError("Writing command {} failed: pyvisa returned StatusCode {} ({})".format(command, status, str(status)))
- return bytes, status
+ msg = ("Writing command {} failed: pyvisa returned StatusCode"
+ " {} ({})".format(command, status, str(status)))
+ raise RuntimeError(msg)
+ return num_bytes, status
def query(self, command):
"""Query the instrument
@@ -1357,18 +1704,13 @@ Raises
return self.query("SYSTEM:ERROR:NEXT?")
def spawn_channel(self, channel, impedance):
- """Wrapper function to create a `func_gen_channel` object for
+ """Wrapper function to create a `FuncGenChannel` object for
a channel -- see the class docstring"""
- return func_gen_channel(self, channel, impedance)
+ return FuncGenChannel(self, channel, impedance)
def get_settings(self):
"""Get dictionaries of the current settings of the two channels
- Parameters
- ----------
- print_settings : bool, default `True`
- `True` prints table of the current setting for both channels
-
Returns
-------
settings : list of dicts
@@ -1381,25 +1723,46 @@ Raises
def print_settings(self):
"""Prints table of the current setting for both channels"""
settings = self.get_settings()
- # find the necessary padding for the table columns
+ # Find the necessary padding for the table columns
# by evaluating the maximum length of the entries
key_padding = max([len(key) for key in settings[0].keys()])
- ch_paddings = [max([len(str(val[0])) for val in ch_settings.values()]) for ch_settings in settings]
+ ch_paddings = [max([len(str(val[0])) for val in ch_settings.values()])
+ for ch_settings in settings]
padding = [key_padding]+ch_paddings
- print("\nCurrent settings for {} {} {}\n".format(self.maker, self.model, self.serial))
-
+ print("\nCurrent settings for {} {} {}\n".format(self.maker,
+ self.model,
+ self.serial))
row_format = "{:>{padd[0]}s} {:{padd[1]}s} {:{padd[2]}s} {}"
- table_header = row_format.format("Setting", "Ch1", "Ch2", "Unit", padd=padding)
+ table_header = row_format.format("Setting", "Ch1", "Ch2",
+ "Unit", padd=padding)
print(table_header)
print("="*len(table_header))
- for (ch1key, (ch1val, unit)), (ch2key, (ch2val, _)) in zip(settings[0].items(), settings[1].items()):
- print(row_format.format(ch1key, str(ch1val), str(ch2val), unit, padd=padding))
+ for (ch1key, (ch1val, unit)), (_, (ch2val, _)) in zip(settings[0].items(),
+ settings[1].items()):
+ print(row_format.format(ch1key, str(ch1val), str(ch2val),
+ unit, padd=padding))
+
+ def set_settings(self, settings):
+ """Set the settings of both channels with settings dictionaries
+
+ (Each channel is turned off before applying the changes to avoid
+ potenitally harmful combinations)
+
+ Parameteres
+ -----------
+ settings : list of dicts
+ List of settings dictionaries as returned by `get_settings`, first
+ entry for channel 1, second for channel 2. The dictionaries should
+ have keys output, function, amplitude, offset, and frequency
+ """
+ for ch, s in zip(self.channels, settings):
+ ch.set_settings(s)
def syncronise_waveforms(self):
"""Syncronise waveforms of the two channels when using the same frequency
- Note: Does NOT enable the frequency lock that can be enabled on the user
- interface of the instrument)
+ Note: Does NOT enable the frequency lock that can be enabled on the
+ user interface of the instrument)
"""
self.write(":PHAS:INIT", custom_err_message="syncronise waveforms")
@@ -1411,39 +1774,44 @@ Raises
bool
`True` if frequency lock enabled
"""
- # if one is locked so is the other, so just need to check one
+ # If one is locked so is the other, so just need to check one
return int(self.query("SOURCE1:FREQuency:CONCurrent?")) == 1
- def enable_frequency_lock(self, use_channel=1):
- """Enable the frequency lock to make the two channels have the same frequency
- and phase of their signals, also after adjustments.
+ def set_frequency_lock(self, state, use_channel=1):
+ """Enable the frequency lock to make the two channels have the same
+ frequency and phase of their signals, also after adjustments.
- See also `func_gen.syncronise_waveforms` for one-time sync only.
+ See also `FuncGen.syncronise_waveforms` for one-time sync only.
Parameters
----------
+ state : {"ON", "OFF"}
+ ON to enable, OFF to disable the lock
use_channel : int, default 1
- The channel whose frequency shall be used as the common freqency
- """
- self.write("SOURCE{}:FREQuency:CONCurrent ON".format(use_channel),
- custom_err_message="enable frequency lock")
-
- def disable_frequency_lock(self):
- """Disable the frequency lock that makes the two channels have the same
- frequency and phase of their signals, also after adjustments
+ Only relevant if turning the lock ON: The channel whose frequency
+ shall be used as the common freqency
"""
- if self.get_frequency_lock():
- # sufficient to disable for only one of the channels
- self.write("SOURCE1:FREQuency:CONCurrent OFF")
- else:
- if self.verbose: print("(!) {}: Tried to disable frequency lock, but frequency lock was not enabled".format(self.model))
+ if self.verbose:
+ if state.lower() == "off" and not self.get_frequency_lock():
+ print("(!) {}: Tried to disable frequency lock, but "
+ "frequency lock was not enabled".format(self.model))
+ return
+ if state.lower() == "on" and self.get_frequency_lock():
+ print("(!) {}: Tried to enable frequency lock, but "
+ "frequency lock was already enabled".format(self.model))
+ return
+ # (Sufficient to disable for only one of the channels)
+ cmd = "SOURCE{}:FREQuency:CONCurrent {}".format(use_channel, state)
+ msg = "turn frequency lock {}".format(state)
+ self.write(cmd, custom_err_message=msg)
def software_trig(self):
- """NOT TESTED: sends a trigger signal to the device (for bursts or modulations)"""
+ """NOT TESTED: sends a trigger signal to the device
+ (for bursts or modulations)"""
self.write("*TRG", custom_err_message="send trigger signal")
- ## ~~~~~~~~~~~~~~~~~~~~~ CUSTOM WAVEFORM FUNCTIONS ~~~~~~~~~~~~~~~~~~~~~~ ##
+ ## ~~~~~~~~~~~~~~~~~~~~~ CUSTOM WAVEFORM FUNCTIONS ~~~~~~~~~~~~~~~~~~~~~ ##
def get_waveform_catalogue(self):
"""Get list of the waveforms that are in use (not empty)
@@ -1454,7 +1822,7 @@ Raises
Strings with the names of the user functions that are not empty
"""
catalogue = self.query("DATA:CATalog?").split(",")
- catalogue = [wf[1:-1] for wf in catalogue] # strip off extra quotes
+ catalogue = [wf[1:-1] for wf in catalogue] # strip off extra quotes
return catalogue
def get_custom_waveform(self, memory_num):
@@ -1471,33 +1839,40 @@ Raises
Waveform as ints spanning the resolution of the function gen or
and empty array if waveform not in use
"""
- # find the wavefroms in use
+ # Find the wavefroms in use
waveforms_in_use = self.get_waveform_catalogue()
if "USER{}".format(memory_num) in waveforms_in_use:
- # copy the waveform to edit memory
+ # Copy the waveform to edit memory
self.write("DATA:COPY EMEMory,USER{}".format(memory_num))
- # get the length of the waveform
+ # Get the length of the waveform
waveform_length = int(self.query("DATA:POINts? EMEMory"))
- # get the waveform (returns binary values)
- waveform = self.inst.query_binary_values("DATA:DATA? EMEMory", datatype='H', is_big_endian=True, container=np.ndarray)
- assert len(waveform) == waveform_length, "Waveform length from native length command (DATA:POINts?) and the processed binary values do not match, {} and {} respectively".format(waveform_length, len(waveform))
+ # Get the waveform (returns binary values)
+ waveform = self.inst.query_binary_values("DATA:DATA? EMEMory",
+ datatype='H',
+ is_big_endian=True,
+ container=np.ndarray)
+ msg = ("Waveform length from native length command (DATA:POINts?) "
+ "and the processed binary values do not match, {} and {} "
+ "respectively".format(waveform_length, len(waveform)))
+ assert len(waveform) == waveform_length, msg
return waveform
else:
print("Waveform USER{} is not in use".format(memory_num))
return np.array([])
- def set_custom_waveform(self, waveform, normalise=True, memory_num=0, verify=True, print_progress=True):
+ def set_custom_waveform(self, waveform, normalise=True, memory_num=0,
+ verify=True, print_progress=True):
"""Transfer waveform data to edit memory and then user memory.
NOTE: Will overwrite without warnings
Parameters
----------
waveform : ndarray
- Either unnormalised arbitrary waveform (then use `normalise=True`), or
- ints spanning the resolution of the function generator
+ Either unnormalised arbitrary waveform (then use `normalise=True`),
+ or ints spanning the resolution of the function generator
normalise : bool
- Choose whether to normalise the waveform to ints over the resolution
- span of the function generator
+ Choose whether to normalise the waveform to ints over the
+ resolution span of the function generator
memory_num : str or int {0,...,255}, default 0
Select which user memory to copy to
verify : bool, default `True`
@@ -1517,33 +1892,46 @@ Raises
If the waveform transferred to the instrument is of a different
length than the waveform supplied
"""
- # check if waveform data is suitable
- if print_progress: print("Check if waveform data is suitable..", end=" ")
+ # Check if waveform data is suitable
+ if print_progress:
+ print("Check if waveform data is suitable..", end=" ")
self.check_arb_waveform_length(waveform)
try:
self.check_arb_waveform_type_and_range(waveform)
except ValueError as e:
- if print_progress: print("\n "+str(e))
- if print_progress: print("Trying again normalising the waveform..", end=" ")
+ if print_progress:
+ print("\n "+str(e))
+ print("Trying again normalising the waveform..", end=" ")
waveform = self.normalise_to_waveform(waveform)
- if print_progress: print("ok")
- # transfer waveform
- if print_progress: print("Transfer waveform to function generator..", end=" ")
- self.inst.write_binary_values("DATA:DATA EMEMory,", waveform, datatype='H', is_big_endian=True)
- # The first query after the write_binary_values returns '', so here is a mock query
+ if print_progress:
+ print("ok")
+ print("Transfer waveform to function generator..", end=" ")
+ # Transfer waveform
+ self.inst.write_binary_values("DATA:DATA EMEMory,", waveform,
+ datatype='H', is_big_endian=True)
+ # The first query after the write_binary_values returns '',
+ # so here is a mock query
self.query("")
transfer_error = self.get_error()
emem_wf_length = self.query("DATA:POINts? EMEMory")
if emem_wf_length == '' or not int(emem_wf_length) == len(waveform):
- raise RuntimeError("Waveform in temporary EMEMory has a length of {}, not of the same length as the waveform ({}).\nError from the instrument: {}".format(emem_wf_length, len(waveform), transfer_error))
- if print_progress: print("ok")
- if print_progress: print("Copy waveform to USER{}..".format(memory_num), end=" ")
+ msg = ("Waveform in temporary EMEMory has a length of {}, not of "
+ "the same length as the waveform ({}).\nError from the "
+ "instrument: {}".format(emem_wf_length, len(waveform),
+ transfer_error))
+ raise RuntimeError(msg)
+ if print_progress:
+ print("ok")
+ print("Copy waveform to USER{}..".format(memory_num), end=" ")
self.write("DATA:COPY USER{},EMEMory".format(memory_num))
- if print_progress: print("ok")
+ if print_progress:
+ print("ok")
if verify:
- if print_progress: print("Verify waveform..".format(memory_num))
+ if print_progress:
+ print("Verify waveform USER{}..".format(memory_num))
if "USER{}".format(memory_num) in self.get_waveform_catalogue():
- self.verify_waveform(waveform, memory_num, normalise=normalise, print_result=print_progress)
+ self.verify_waveform(waveform, memory_num, normalise=normalise,
+ print_result=print_progress)
else:
print("(!) USER{} is empty".format(memory_num))
return waveform
@@ -1563,15 +1951,16 @@ Raises
waveform : ndarray
Waveform as ints spanning the resolution of the function gen
"""
- # check if waveform data is suitable
+ # Check if waveform data is suitable
self.check_arb_waveform_length(shape)
- # normalise
+ # Normalise
waveform = shape - np.min(shape)
normalisation_factor = np.max(waveform)
waveform = waveform/normalisation_factor*self.arbitrary_waveform_resolution
return waveform.astype(np.uint16)
- def verify_waveform(self, waveform, memory_num, normalise=True, print_result=True):
+ def verify_waveform(self, waveform, memory_num, normalise=True,
+ print_result=True):
"""Compare a waveform in user memory to argument waveform
Parameters
@@ -1593,25 +1982,32 @@ Raises
List of the indices where the waveforms are not equal or `None` if
the waveforms were of different lengths
"""
- if normalise:# make sure test waveform is normalised
+ if normalise: # make sure test waveform is normalised
waveform = self.normalise_to_waveform(waveform)
- # get the waveform on the instrument
+ # Get the waveform on the instrument
instrument_waveform = self.get_custom_waveform(memory_num)
- # compare lengths
+ # Compare lengths
len_inst_wav, len_wav = len(instrument_waveform), len(waveform)
if not len_inst_wav == len_wav:
- if print_result: print("The waveform in USER{} and the compared waveform are not of same length (instrument {} vs {})".format(memory_num, len_inst_wav, len_wav))
+ if print_result:
+ print("The waveform in USER{} and the compared waveform are "
+ "not of same length (instrument {} vs {})"
+ "".format(memory_num, len_inst_wav, len_wav))
return False, instrument_waveform, None
- # compare each element
+ # Compare each element
not_equal = []
for i in range(len_wav):
if not instrument_waveform[i] == waveform[i]:
not_equal.append(i)
- # return depending of whether list is empty or not
+ # Return depending of whether list is empty or not
if not not_equal: # if list is empty
- if print_result: print("The waveform in USER{} and the compared waveform are equal".format(memory_num))
+ if print_result:
+ print("The waveform in USER{} and the compared waveform "
+ "are equal".format(memory_num))
return True, instrument_waveform, not_equal
- if print_result: print("The waveform in USER{} and the compared waveform are NOT equal".format(memory_num))
+ if print_result:
+ print("The waveform in USER{} and the compared waveform are "
+ "NOT equal".format(memory_num))
return False, instrument_waveform, not_equal
def check_arb_waveform_length(self, waveform):
@@ -1627,8 +2023,12 @@ Raises
ValueError
If the waveform is not within the permitted length
"""
- if (len(waveform) < self.arbitrary_waveform_length[0]) or (len(waveform) > self.arbitrary_waveform_length[1]):
- raise ValueError("The waveform is of length {}, which is not within the acceptable length {} < len < {}".format(len(waveform), *self.arbitrary_waveform_length))
+ if ((len(waveform) < self.arbitrary_waveform_length[0]) or
+ (len(waveform) > self.arbitrary_waveform_length[1])):
+ msg = ("The waveform is of length {}, which is not within the "
+ "acceptable length {} < len < {}"
+ "".format(len(waveform), *self.arbitrary_waveform_length))
+ raise ValueError(msg)
def check_arb_waveform_type_and_range(self, waveform):
"""Checks if waveform is of int/np.int32 type and within the resolution
@@ -1647,13 +2047,17 @@ Raises
"""
for value in waveform:
if not isinstance(value, (int, np.uint16, np.int32)):
- raise ValueError("The waveform contains values that are not int, np.uint16 or np.int32")
+ raise ValueError("The waveform contains values that are not"
+ "int, np.uint16 or np.int32")
if (value < 0) or (value > self.arbitrary_waveform_resolution):
- raise ValueError("The waveform contains values out of range ({} is not within the resolution [0, {}])".format(value, self.arbitrary_waveform_resolution))
+ raise ValueError("The waveform contains values out of range "
+ "({} is not within the resolution "
+ "[0, {}])".format(value,
+ self.arbitrary_waveform_resolution))
Methods
-
+
def check_arb_waveform_length(self, waveform)
-
@@ -1685,11 +2089,15 @@
Raises
ValueError
If the waveform is not within the permitted length
"""
- if (len(waveform) < self.arbitrary_waveform_length[0]) or (len(waveform) > self.arbitrary_waveform_length[1]):
- raise ValueError("The waveform is of length {}, which is not within the acceptable length {} < len < {}".format(len(waveform), *self.arbitrary_waveform_length))
+ if ((len(waveform) < self.arbitrary_waveform_length[0]) or
+ (len(waveform) > self.arbitrary_waveform_length[1])):
+ msg = ("The waveform is of length {}, which is not within the "
+ "acceptable length {} < len < {}"
+ "".format(len(waveform), *self.arbitrary_waveform_length))
+ raise ValueError(msg)
-
+
def check_arb_waveform_type_and_range(self, waveform)
@@ -1727,12 +2135,16 @@ Raises
"""
for value in waveform:
if not isinstance(value, (int, np.uint16, np.int32)):
- raise ValueError("The waveform contains values that are not int, np.uint16 or np.int32")
+ raise ValueError("The waveform contains values that are not"
+ "int, np.uint16 or np.int32")
if (value < 0) or (value > self.arbitrary_waveform_resolution):
- raise ValueError("The waveform contains values out of range ({} is not within the resolution [0, {}])".format(value, self.arbitrary_waveform_resolution))
+ raise ValueError("The waveform contains values out of range "
+ "({} is not within the resolution "
+ "[0, {}])".format(value,
+ self.arbitrary_waveform_resolution))
-
+
def close(self)
@@ -1746,59 +2158,7 @@ Raises
self.inst.close()
-
-def disable_frequency_lock(self)
-
Disable the frequency lock that makes the two channels have the same -frequency and phase of their signals, also after adjustments
def disable_frequency_lock(self):
- """Disable the frequency lock that makes the two channels have the same
- frequency and phase of their signals, also after adjustments
- """
- if self.get_frequency_lock():
- # sufficient to disable for only one of the channels
- self.write("SOURCE1:FREQuency:CONCurrent OFF")
- else:
- if self.verbose: print("(!) {}: Tried to disable frequency lock, but frequency lock was not enabled".format(self.model))
-
-def enable_frequency_lock(self, use_channel=1)
-
Enable the frequency lock to make the two channels have the same frequency -and phase of their signals, also after adjustments.
-See also func_gen.syncronise_waveforms()
for one-time sync only.
use_channel
: int
, default 1
def enable_frequency_lock(self, use_channel=1):
- """Enable the frequency lock to make the two channels have the same frequency
- and phase of their signals, also after adjustments.
-
- See also `func_gen.syncronise_waveforms` for one-time sync only.
-
- Parameters
- ----------
- use_channel : int, default 1
- The channel whose frequency shall be used as the common freqency
- """
- self.write("SOURCE{}:FREQuency:CONCurrent ON".format(use_channel),
- custom_err_message="enable frequency lock")
-
+
def get_custom_waveform(self, memory_num)
@@ -1832,23 +2192,29 @@ Returns
Waveform as ints spanning the resolution of the function gen or
and empty array if waveform not in use
"""
- # find the wavefroms in use
+ # Find the wavefroms in use
waveforms_in_use = self.get_waveform_catalogue()
if "USER{}".format(memory_num) in waveforms_in_use:
- # copy the waveform to edit memory
+ # Copy the waveform to edit memory
self.write("DATA:COPY EMEMory,USER{}".format(memory_num))
- # get the length of the waveform
+ # Get the length of the waveform
waveform_length = int(self.query("DATA:POINts? EMEMory"))
- # get the waveform (returns binary values)
- waveform = self.inst.query_binary_values("DATA:DATA? EMEMory", datatype='H', is_big_endian=True, container=np.ndarray)
- assert len(waveform) == waveform_length, "Waveform length from native length command (DATA:POINts?) and the processed binary values do not match, {} and {} respectively".format(waveform_length, len(waveform))
+ # Get the waveform (returns binary values)
+ waveform = self.inst.query_binary_values("DATA:DATA? EMEMory",
+ datatype='H',
+ is_big_endian=True,
+ container=np.ndarray)
+ msg = ("Waveform length from native length command (DATA:POINts?) "
+ "and the processed binary values do not match, {} and {} "
+ "respectively".format(waveform_length, len(waveform)))
+ assert len(waveform) == waveform_length, msg
return waveform
else:
print("Waveform USER{} is not in use".format(memory_num))
return np.array([])
-
+
def get_error(self)
@@ -1873,7 +2239,7 @@ Returns
return self.query("SYSTEM:ERROR:NEXT?")
-
+
def get_frequency_lock(self)
@@ -1895,20 +2261,15 @@ Returns
bool
`True` if frequency lock enabled
"""
- # if one is locked so is the other, so just need to check one
+ # If one is locked so is the other, so just need to check one
return int(self.query("SOURCE1:FREQuency:CONCurrent?")) == 1
-
+
def get_settings(self)
Get dictionaries of the current settings of the two channels
-Parameters
-
-print_settings
: bool
, default True
-True
prints table of the current setting for both channels
-
Returns
settings
: list
of dicts
@@ -1923,11 +2284,6 @@ Returns
def get_settings(self):
"""Get dictionaries of the current settings of the two channels
- Parameters
- ----------
- print_settings : bool, default `True`
- `True` prints table of the current setting for both channels
-
Returns
-------
settings : list of dicts
@@ -1938,7 +2294,7 @@ Returns
return [ch.get_settings() for ch in self.channels]
-
+
def get_waveform_catalogue(self)
@@ -1961,11 +2317,11 @@ Returns
Strings with the names of the user functions that are not empty
"""
catalogue = self.query("DATA:CATalog?").split(",")
- catalogue = [wf[1:-1] for wf in catalogue] # strip off extra quotes
+ catalogue = [wf[1:-1] for wf in catalogue] # strip off extra quotes
return catalogue
-
+
def initialise_model_properties(self)
@@ -1974,8 +2330,8 @@ Returns
Raises
NotCompatibleError
-- If the connected model is not necessarily compatible with this package,
-limits are not known.
+- If the connected model is not necessarily compatible with this
+package, slimits are not known.
@@ -1988,23 +2344,28 @@ Raises
Raises
------
NotCompatibleError
- If the connected model is not necessarily compatible with this package,
- limits are not known.
+ If the connected model is not necessarily compatible with this
+ package, slimits are not known.
"""
if self.model == 'AFG1022' or self.override_compatibility:
self.instrument_limits = {
- "frequency lims": ({"min": 1e-6, "max": 25e6}, "Hz"),
- "voltage lims": ({"50ohm": {"min": -5, "max": 5},
- "highZ": {"min": -10, "max": 10}}, "V"),
- "amplitude lims": ({"50ohm": {"min": 0.001, "max": 10},
- "highZ": {"min": 0.002, "max": 20}}, "Vpp")}
- self.arbitrary_waveform_length = [2, 8192] # min length, max length
- self.arbitrary_waveform_resolution = 16383 # 14 bit
+ "frequency lims": ({"min": 1e-6, "max": 25e6}, "Hz"),
+ "voltage lims": ({"50ohm": {"min": -5, "max": 5},
+ "highZ": {"min": -10, "max": 10}}, "V"),
+ "amplitude lims": ({"50ohm": {"min": 0.001, "max": 10},
+ "highZ": {"min": 0.002, "max": 20}}, "Vpp")}
+ self.arbitrary_waveform_length = [2, 8192] # min length, max length
+ self.arbitrary_waveform_resolution = 16383 # 14 bit
else:
- raise NotCompatibleError("Model {} not supported, no limits set!\n\tTo use the limits for AFG1022, call the class with 'override_compatibility=True'\n\tNote that this might lead to unexpected behaviour for custom waveforms and 'MIN'/'MAX' keywords.")
+ msg = ("Model {} not supported, no limits set!"
+ "\n\tTo use the limits for AFG1022, call the class with "
+ "'override_compatibility=True'"
+ "\n\tNote that this might lead to unexpected behaviour "
+ "for custom waveforms and 'MIN'/'MAX' keywords.")
+ raise NotCompatibleError(msg)
-
+
def normalise_to_waveform(self, shape)
@@ -2040,16 +2401,16 @@ Returns
waveform : ndarray
Waveform as ints spanning the resolution of the function gen
"""
- # check if waveform data is suitable
+ # Check if waveform data is suitable
self.check_arb_waveform_length(shape)
- # normalise
+ # Normalise
waveform = shape - np.min(shape)
normalisation_factor = np.max(waveform)
waveform = waveform/normalisation_factor*self.arbitrary_waveform_resolution
return waveform.astype(np.uint16)
-
+
def print_settings(self)
@@ -2061,22 +2422,27 @@ Returns
def print_settings(self):
"""Prints table of the current setting for both channels"""
settings = self.get_settings()
- # find the necessary padding for the table columns
+ # Find the necessary padding for the table columns
# by evaluating the maximum length of the entries
key_padding = max([len(key) for key in settings[0].keys()])
- ch_paddings = [max([len(str(val[0])) for val in ch_settings.values()]) for ch_settings in settings]
+ ch_paddings = [max([len(str(val[0])) for val in ch_settings.values()])
+ for ch_settings in settings]
padding = [key_padding]+ch_paddings
- print("\nCurrent settings for {} {} {}\n".format(self.maker, self.model, self.serial))
-
+ print("\nCurrent settings for {} {} {}\n".format(self.maker,
+ self.model,
+ self.serial))
row_format = "{:>{padd[0]}s} {:{padd[1]}s} {:{padd[2]}s} {}"
- table_header = row_format.format("Setting", "Ch1", "Ch2", "Unit", padd=padding)
+ table_header = row_format.format("Setting", "Ch1", "Ch2",
+ "Unit", padd=padding)
print(table_header)
print("="*len(table_header))
- for (ch1key, (ch1val, unit)), (ch2key, (ch2val, _)) in zip(settings[0].items(), settings[1].items()):
- print(row_format.format(ch1key, str(ch1val), str(ch2val), unit, padd=padding))
+ for (ch1key, (ch1val, unit)), (_, (ch2val, _)) in zip(settings[0].items(),
+ settings[1].items()):
+ print(row_format.format(ch1key, str(ch1val), str(ch2val),
+ unit, padd=padding))
-
+
def query(self, command)
@@ -2111,7 +2477,7 @@ Returns
return self.inst.query(command).strip()
-
+
def set_custom_waveform(self, waveform, normalise=True, memory_num=0, verify=True, print_progress=True)
@@ -2120,11 +2486,11 @@ Returns
Parameters
waveform
: ndarray
-- Either unnormalised arbitrary waveform (then use
normalise=True
), or
-ints spanning the resolution of the function generator
+- Either unnormalised arbitrary waveform (then use
normalise=True
),
+or ints spanning the resolution of the function generator
normalise
: bool
-- Choose whether to normalise the waveform to ints over the resolution
-span of the function generator
+- Choose whether to normalise the waveform to ints over the
+resolution span of the function generator
memory_num
: str
or int
{0
,...
,255
}, default 0
- Select which user memory to copy to
verify
: bool
, default True
@@ -2149,18 +2515,19 @@ Raises
Expand source code
-def set_custom_waveform(self, waveform, normalise=True, memory_num=0, verify=True, print_progress=True):
+def set_custom_waveform(self, waveform, normalise=True, memory_num=0,
+ verify=True, print_progress=True):
"""Transfer waveform data to edit memory and then user memory.
NOTE: Will overwrite without warnings
Parameters
----------
waveform : ndarray
- Either unnormalised arbitrary waveform (then use `normalise=True`), or
- ints spanning the resolution of the function generator
+ Either unnormalised arbitrary waveform (then use `normalise=True`),
+ or ints spanning the resolution of the function generator
normalise : bool
- Choose whether to normalise the waveform to ints over the resolution
- span of the function generator
+ Choose whether to normalise the waveform to ints over the
+ resolution span of the function generator
memory_num : str or int {0,...,255}, default 0
Select which user memory to copy to
verify : bool, default `True`
@@ -2180,75 +2547,173 @@ Raises
If the waveform transferred to the instrument is of a different
length than the waveform supplied
"""
- # check if waveform data is suitable
- if print_progress: print("Check if waveform data is suitable..", end=" ")
+ # Check if waveform data is suitable
+ if print_progress:
+ print("Check if waveform data is suitable..", end=" ")
self.check_arb_waveform_length(waveform)
try:
self.check_arb_waveform_type_and_range(waveform)
except ValueError as e:
- if print_progress: print("\n "+str(e))
- if print_progress: print("Trying again normalising the waveform..", end=" ")
+ if print_progress:
+ print("\n "+str(e))
+ print("Trying again normalising the waveform..", end=" ")
waveform = self.normalise_to_waveform(waveform)
- if print_progress: print("ok")
- # transfer waveform
- if print_progress: print("Transfer waveform to function generator..", end=" ")
- self.inst.write_binary_values("DATA:DATA EMEMory,", waveform, datatype='H', is_big_endian=True)
- # The first query after the write_binary_values returns '', so here is a mock query
+ if print_progress:
+ print("ok")
+ print("Transfer waveform to function generator..", end=" ")
+ # Transfer waveform
+ self.inst.write_binary_values("DATA:DATA EMEMory,", waveform,
+ datatype='H', is_big_endian=True)
+ # The first query after the write_binary_values returns '',
+ # so here is a mock query
self.query("")
transfer_error = self.get_error()
emem_wf_length = self.query("DATA:POINts? EMEMory")
if emem_wf_length == '' or not int(emem_wf_length) == len(waveform):
- raise RuntimeError("Waveform in temporary EMEMory has a length of {}, not of the same length as the waveform ({}).\nError from the instrument: {}".format(emem_wf_length, len(waveform), transfer_error))
- if print_progress: print("ok")
- if print_progress: print("Copy waveform to USER{}..".format(memory_num), end=" ")
+ msg = ("Waveform in temporary EMEMory has a length of {}, not of "
+ "the same length as the waveform ({}).\nError from the "
+ "instrument: {}".format(emem_wf_length, len(waveform),
+ transfer_error))
+ raise RuntimeError(msg)
+ if print_progress:
+ print("ok")
+ print("Copy waveform to USER{}..".format(memory_num), end=" ")
self.write("DATA:COPY USER{},EMEMory".format(memory_num))
- if print_progress: print("ok")
+ if print_progress:
+ print("ok")
if verify:
- if print_progress: print("Verify waveform..".format(memory_num))
+ if print_progress:
+ print("Verify waveform USER{}..".format(memory_num))
if "USER{}".format(memory_num) in self.get_waveform_catalogue():
- self.verify_waveform(waveform, memory_num, normalise=normalise, print_result=print_progress)
+ self.verify_waveform(waveform, memory_num, normalise=normalise,
+ print_result=print_progress)
else:
print("(!) USER{} is empty".format(memory_num))
return waveform
-
+
+def set_frequency_lock(self, state, use_channel=1)
+
+
+Enable the frequency lock to make the two channels have the same
+frequency and phase of their signals, also after adjustments.
+See also FuncGen.syncronise_waveforms()
for one-time sync only.
+Parameters
+
+state
: {"ON"
, "OFF"
}
+- ON to enable, OFF to disable the lock
+use_channel
: int
, default 1
+- Only relevant if turning the lock ON: The channel whose frequency
+shall be used as the common freqency
+
+
+
+Expand source code
+
+def set_frequency_lock(self, state, use_channel=1):
+ """Enable the frequency lock to make the two channels have the same
+ frequency and phase of their signals, also after adjustments.
+
+ See also `FuncGen.syncronise_waveforms` for one-time sync only.
+
+ Parameters
+ ----------
+ state : {"ON", "OFF"}
+ ON to enable, OFF to disable the lock
+ use_channel : int, default 1
+ Only relevant if turning the lock ON: The channel whose frequency
+ shall be used as the common freqency
+ """
+ if self.verbose:
+ if state.lower() == "off" and not self.get_frequency_lock():
+ print("(!) {}: Tried to disable frequency lock, but "
+ "frequency lock was not enabled".format(self.model))
+ return
+ if state.lower() == "on" and self.get_frequency_lock():
+ print("(!) {}: Tried to enable frequency lock, but "
+ "frequency lock was already enabled".format(self.model))
+ return
+ # (Sufficient to disable for only one of the channels)
+ cmd = "SOURCE{}:FREQuency:CONCurrent {}".format(use_channel, state)
+ msg = "turn frequency lock {}".format(state)
+ self.write(cmd, custom_err_message=msg)
+
+
+
+def set_settings(self, settings)
+
+
+Set the settings of both channels with settings dictionaries
+(Each channel is turned off before applying the changes to avoid
+potenitally harmful combinations)
+Parameteres
+
+settings
: list
of dicts
+- List of settings dictionaries as returned by
get_settings
, first
+entry for channel 1, second for channel 2. The dictionaries should
+have keys output, function, amplitude, offset, and frequency
+
+
+
+Expand source code
+
+def set_settings(self, settings):
+ """Set the settings of both channels with settings dictionaries
+
+ (Each channel is turned off before applying the changes to avoid
+ potenitally harmful combinations)
+
+ Parameteres
+ -----------
+ settings : list of dicts
+ List of settings dictionaries as returned by `get_settings`, first
+ entry for channel 1, second for channel 2. The dictionaries should
+ have keys output, function, amplitude, offset, and frequency
+ """
+ for ch, s in zip(self.channels, settings):
+ ch.set_settings(s)
+
+
+
def software_trig(self)
-NOT TESTED: sends a trigger signal to the device (for bursts or modulations)
+NOT TESTED: sends a trigger signal to the device
+(for bursts or modulations)
Expand source code
def software_trig(self):
- """NOT TESTED: sends a trigger signal to the device (for bursts or modulations)"""
+ """NOT TESTED: sends a trigger signal to the device
+ (for bursts or modulations)"""
self.write("*TRG", custom_err_message="send trigger signal")
-
+
def spawn_channel(self, channel, impedance)
-Wrapper function to create a func_gen_channel
object for
+Wrapper function to create a FuncGenChannel
object for
a channel – see the class docstring
Expand source code
def spawn_channel(self, channel, impedance):
- """Wrapper function to create a `func_gen_channel` object for
+ """Wrapper function to create a `FuncGenChannel` object for
a channel -- see the class docstring"""
- return func_gen_channel(self, channel, impedance)
+ return FuncGenChannel(self, channel, impedance)
-
+
def syncronise_waveforms(self)
Syncronise waveforms of the two channels when using the same frequency
-Note: Does NOT enable the frequency lock that can be enabled on the user
-interface of the instrument)
+Note: Does NOT enable the frequency lock that can be enabled on the
+user interface of the instrument)
Expand source code
@@ -2256,13 +2721,13 @@ Raises
def syncronise_waveforms(self):
"""Syncronise waveforms of the two channels when using the same frequency
- Note: Does NOT enable the frequency lock that can be enabled on the user
- interface of the instrument)
+ Note: Does NOT enable the frequency lock that can be enabled on the
+ user interface of the instrument)
"""
self.write(":PHAS:INIT", custom_err_message="syncronise waveforms")
-
+
def verify_waveform(self, waveform, memory_num, normalise=True, print_result=True)
@@ -2290,7 +2755,8 @@ Returns
Expand source code
-def verify_waveform(self, waveform, memory_num, normalise=True, print_result=True):
+def verify_waveform(self, waveform, memory_num, normalise=True,
+ print_result=True):
"""Compare a waveform in user memory to argument waveform
Parameters
@@ -2312,29 +2778,36 @@ Returns
List of the indices where the waveforms are not equal or `None` if
the waveforms were of different lengths
"""
- if normalise:# make sure test waveform is normalised
+ if normalise: # make sure test waveform is normalised
waveform = self.normalise_to_waveform(waveform)
- # get the waveform on the instrument
+ # Get the waveform on the instrument
instrument_waveform = self.get_custom_waveform(memory_num)
- # compare lengths
+ # Compare lengths
len_inst_wav, len_wav = len(instrument_waveform), len(waveform)
if not len_inst_wav == len_wav:
- if print_result: print("The waveform in USER{} and the compared waveform are not of same length (instrument {} vs {})".format(memory_num, len_inst_wav, len_wav))
+ if print_result:
+ print("The waveform in USER{} and the compared waveform are "
+ "not of same length (instrument {} vs {})"
+ "".format(memory_num, len_inst_wav, len_wav))
return False, instrument_waveform, None
- # compare each element
+ # Compare each element
not_equal = []
for i in range(len_wav):
if not instrument_waveform[i] == waveform[i]:
not_equal.append(i)
- # return depending of whether list is empty or not
+ # Return depending of whether list is empty or not
if not not_equal: # if list is empty
- if print_result: print("The waveform in USER{} and the compared waveform are equal".format(memory_num))
+ if print_result:
+ print("The waveform in USER{} and the compared waveform "
+ "are equal".format(memory_num))
return True, instrument_waveform, not_equal
- if print_result: print("The waveform in USER{} and the compared waveform are NOT equal".format(memory_num))
+ if print_result:
+ print("The waveform in USER{} and the compared waveform are "
+ "NOT equal".format(memory_num))
return False, instrument_waveform, not_equal
-
+
def write(self, command, custom_err_message=None)
@@ -2344,8 +2817,10 @@ Parameters
command
: str
The VISA command to be written to the instrument
custom_err_message
: str
, default None
-When None
, the RuntimeError message is "Writing command {command} failed: pyvisa returned StatusCode ..".
-Otherwise, if a message is supplied "Could not {message}: pyvisa returned StatusCode .."
+When None
, the RuntimeError message is "Writing command {command}
+failed: pyvisa returned StatusCode ..".
+Otherwise, if a message is supplied "Could not {message}:
+pyvisa returned StatusCode .."
Returns
@@ -2357,7 +2832,8 @@ Returns
Raises
RuntimeError
-- If status returned by PyVISA write command is not
pyvisa.constants.StatusCode.success
+- If status returned by PyVISA write command is not
+
pyvisa.constants.StatusCode.success
@@ -2371,8 +2847,10 @@ Raises
command : str
The VISA command to be written to the instrument
custom_err_message : str, default `None`
- When `None`, the RuntimeError message is "Writing command {command} failed: pyvisa returned StatusCode ..".
- Otherwise, if a message is supplied "Could not {message}: pyvisa returned StatusCode .."
+ When `None`, the RuntimeError message is "Writing command {command}
+ failed: pyvisa returned StatusCode ..".
+ Otherwise, if a message is supplied "Could not {message}:
+ pyvisa returned StatusCode .."
Returns
-------
@@ -2384,28 +2862,33 @@ Raises
Raises
------
RuntimeError
- If status returned by PyVISA write command is not `pyvisa.constants.StatusCode.success`
+ If status returned by PyVISA write command is not
+ `pyvisa.constants.StatusCode.success`
"""
- bytes, status = self.inst.write(command)
+ num_bytes, status = self.inst.write(command)
if not status == pyvisa.constants.StatusCode.success:
if custom_err_message is not None:
- raise RuntimeError("Could not {}: pyvisa returned StatusCode {} ({})".format(custom_err_message, status, str(status)))
+ msg = ("Could not {}: pyvisa returned StatusCode {} "
+ "({})".format(custom_err_message, status, str(status)))
+ raise RuntimeError(msg)
else:
- raise RuntimeError("Writing command {} failed: pyvisa returned StatusCode {} ({})".format(command, status, str(status)))
- return bytes, status
+ msg = ("Writing command {} failed: pyvisa returned StatusCode"
+ " {} ({})".format(command, status, str(status)))
+ raise RuntimeError(msg)
+ return num_bytes, status
-
-class func_gen_channel
-(func_gen, channel, impedance)
+
+class FuncGenChannel
+(fgen, channel, impedance)
Class for controlling a channel on a function generator object
Parameters
-func_gen
: <a title="tektronix_func_gen.func_gen" href="#tektronix_func_gen.func_gen">
func_gen</a>
+fgen
: <a title="tektronix_func_gen.FuncGen" href="#tektronix_func_gen.FuncGen">
FuncGen</a>
- The function generator object
channel
: {1
, 2
}
- The channel to be controlled
@@ -2415,7 +2898,7 @@ Parameters
Attributes
-func_gen
: <a title="tektronix_func_gen.func_gen" href="#tektronix_func_gen.func_gen">
func_gen</a>
+fgen
: <a title="tektronix_func_gen.FuncGen" href="#tektronix_func_gen.FuncGen">
FuncGen</a>
- The function generator object for which the channel exists
channel
: {1
, 2
}
- The channel number
@@ -2425,7 +2908,7 @@ Attributes
source
: str
- "SOURce{}:" where {} is the channel number
settings_units
: list
-- The units for the settings produced by
func_gen_channel.get_settings
+- The units for the settings produced by
FuncGenChannel.get_settings
state_str
: dict
- For conversion from states 1 and 2 to "ON" and "OFF"
@@ -2433,12 +2916,12 @@ Attributes
Expand source code
-class func_gen_channel:
+class FuncGenChannel:
"""Class for controlling a channel on a function generator object
Parameters
----------
- func_gen : `func_gen`
+ fgen : `FuncGen`
The function generator object
channel : {1, 2}
The channel to be controlled
@@ -2448,7 +2931,7 @@ Attributes
Attributes
----------
- func_gen : `func_gen`
+ fgen : `FuncGen`
The function generator object for which the channel exists
channel : {1, 2}
The channel number
@@ -2458,7 +2941,7 @@ Attributes
source : str
"SOURce{}:" where {} is the channel number
settings_units : list
- The units for the settings produced by `func_gen_channel.get_settings`
+ The units for the settings produced by `FuncGenChannel.get_settings`
state_str : dict
For conversion from states 1 and 2 to "ON" and "OFF"
"""
@@ -2468,92 +2951,162 @@ Attributes
1 : "ON", 0 : "OFF"}
"""Dictionary for converting output states to "ON" and "OFF" """
- def __init__(self, func_gen, channel, impedance):
- self.func_gen = func_gen
+ def __init__(self, fgen, channel, impedance):
+ self.fgen = fgen
self.channel = channel
self.source = "SOURce{}:".format(channel)
self.impedance = impedance
- # adopt limits dictionary from instrument
- self.channel_limits = self.func_gen.instrument_limits
+ # Adopt limits dictionary from instrument
+ self.channel_limits = copy.deepcopy(self.fgen.instrument_limits)
+
+ def impedance_dependent_limit(self, limit_type):
+ """Check if the limit type is impedance dependent (voltages) or
+ not (frequency)
+
+ Returns
+ -------
+ bool
+ `True` if the limit is impedance dependent
+ """
+ try: # to access the key "min" to check if impedance must be selected
+ _ = self.channel_limits[limit_type][0]["min"]
+ return False
+ except KeyError: # if the key does not exist
+ # The impedance must be selected
+ return True
def set_stricter_limits(self):
"""Set limits for the voltage and frequency limits of the channel output
through a series of prompts"""
- print("Set stricter voltage and frequency limits for channel {}".format(self.channel))
+ print("Set stricter voltage and frequency limits "
+ "for channel {}".format(self.channel))
print("Use enter only to leave a limit unchanged.")
# Go through the different limits in the instrument_limits dict
- for limit_type, (inst_limit_dict, unit) in self.func_gen.instrument_limits.items():
- try: # to access the key "min" to check if impedance must be selected
- _ = inst_limit_dict["min"]
- use_impedance = False
- except KeyError: # if the key does not exist
- # the impedance must be selected
- use_impedance = True
- inst_limit_dict = inst_limit_dict[self.impedance]
+ for limit_type, (inst_limit_dict, unit) in self.fgen.instrument_limits.items():
+ use_impedance = self.impedance_dependent_limit(limit_type)
print("Set {} in {}".format(limit_type, unit), end=" ")
if use_impedance:
+ inst_limit_dict = inst_limit_dict[self.impedance]
print("[{} impedance limit]".format(self.impedance))
else:
print("") # get new line
# Go through the min and max for the limit type
for key, inst_value in inst_limit_dict.items():
# prompt for new value
- new_value = input(" {} (instrument limit {}{}): ".format(key, inst_value, unit))
+ new_value = input(" {} (instrument limit {}{}): "
+ "".format(key, inst_value, unit))
if new_value == "":
- # do not change if empty
+ # Do not change if empty
print("\tLimit not changed")
else:
try: # to convert to float
new_value = float(new_value)
except ValueError:
- print("\tLimit unchanged: Could not convert {} to float".format(new_value))
+ print("\tLimit unchanged: Could not convert \'{}\' "
+ "to float".format(new_value))
continue # to next item in dict
- # check that the new value is within the intrument limits
- acceptable_min = key == "min" and new_value > inst_value
- current_min = self.channel_limits[limit_type][0][self.impedance]["min"] if use_impedance else self.channel_limits[limit_type][0]["min"]
- larger_than_min = new_value > current_min
- acceptable_max = key == "max" and new_value < inst_value and larger_than_min
- if acceptable_min or acceptable_max: # within the limits
- # set the new channel_limit, using the impedance depending on the limit type
- if use_impedance:
- self.channel_limits[limit_type][0][self.impedance][key] = new_value
- else:
- self.channel_limits[limit_type][0][key] = new_value
- print("\tNew limit set {}{}".format(new_value, unit))
- else: # print description of why the limit was not set
- if larger_than_min:
- reason = "larger" if key == "max" else "smaller"
- print("\tNew limit NOT set: {}{unit} is {} than the instrument limit ({}{unit})".format(new_value, reason, inst_value, unit=unit))
- else:
- print("\tNew limit NOT set: {}{unit} is smaller than the current set minimum ({}{unit})".format(new_value, current_min, unit=unit))
-
- # get currently used parameters from function generator
+ # Set the new limit
+ self.set_limit(limit_type, key, new_value, verbose=True)
+
+ def set_limit(self, limit_type, bound, new_value, verbose=False):
+ """Set a limit if the new value is within the instrument limits and are
+ self consistent (max larger than min)
+
+ Parameterers
+ ------------
+ limit_type : str
+ The name of the limit in the channel_limits dictionary
+ bound : {"min", "max"}
+ Specifies if it is the max or the min limit that is to be set
+ new_value : float
+ The new value to be used for the limit
+ verbose : bool
+ Print confirmation that the limit was set or reason for why the
+ limit was not set
+
+ Returns
+ -------
+ bool
+ `True` if new limit set, `False` otherwise
+ """
+ # Short hand references
+ inst_limit_dict = self.fgen.instrument_limits[limit_type]
+ channel_limit_dict = self.channel_limits[limit_type]
+ # Find the instrument limit and unit
+ use_impedance = self.impedance_dependent_limit(limit_type)
+ if use_impedance:
+ inst_value = inst_limit_dict[0][self.impedance][bound]
+ else:
+ inst_value = inst_limit_dict[0][bound]
+ unit = inst_limit_dict[1]
+ # Check that the new value is within the intrument limits
+ acceptable_min = bound == "min" and new_value > inst_value
+ if use_impedance:
+ current_min = channel_limit_dict[0][self.impedance]["min"]
+ else:
+ current_min = channel_limit_dict[0]["min"]
+ larger_than_min = new_value > current_min
+ acceptable_max = bound == "max" and new_value < inst_value and larger_than_min
+ if acceptable_min or acceptable_max: # within the limits
+ # Set the new channel_limit, using the impedance depending on the
+ # limit type. Beware that the shorthand cannot be used, as this
+ # only changes the shorthand not the dictionary itself
+ if use_impedance:
+ self.channel_limits[limit_type][0][self.impedance][bound] = new_value
+ else:
+ self.channel_limits[limit_type][0][bound] = new_value
+ if verbose:
+ print("\tNew limit set {}{}".format(new_value, unit))
+ return True
+ elif verbose: # print description of why the limit was not set
+ if larger_than_min:
+ reason = "larger" if bound == "max" else "smaller"
+ print("\tNew limit NOT set: {}{unit} is {} than the instrument "
+ "limit ({}{unit})".format(new_value, reason, inst_value,
+ unit=unit))
+ else:
+ print("\tNew limit NOT set: {}{unit} is smaller than the "
+ "current set minimum ({}{unit})".format(new_value,
+ current_min,
+ unit=unit))
+ return False
+
+ # Get currently used parameters from function generator
def get_output_state(self):
"""Returns "0" for "OFF", "1" for "ON" """
- return self.func_gen.query("OUTPut{}:STATe?".format(self.channel))
+ return self.fgen.query("OUTPut{}:STATe?".format(self.channel))
+
def get_function(self):
"""Returns string of function name"""
- return self.func_gen.query("{}FUNCtion:SHAPe?".format(self.source))
+ return self.fgen.query("{}FUNCtion:SHAPe?".format(self.source))
+
def get_amplitude(self):
"""Returns peak-to-peak voltage in volts"""
- return float(self.func_gen.query("{}VOLTage:AMPLitude?".format(self.source)))
+ return float(self.fgen.query("{}VOLTage:AMPLitude?".format(self.source)))
+
def get_offset(self):
"""Returns offset voltage in volts"""
- return float(self.func_gen.query("{}VOLTage:OFFSet?".format(self.source)))
+ return float(self.fgen.query("{}VOLTage:OFFSet?".format(self.source)))
+
def get_frequency(self):
"""Returns frequency in Hertz"""
- return float(self.func_gen.query("{}FREQuency?".format(self.source)))
+ return float(self.fgen.query("{}FREQuency?".format(self.source)))
- # get limits set in the channel class
+ # Get limits set in the channel class
def get_frequency_lims(self):
"""Returns list of min and max frequency limits"""
- return [self.channel_limits["frequency lims"][0][key] for key in ["min", "max"]]
+ return [self.channel_limits["frequency lims"][0][key]
+ for key in ["min", "max"]]
+
def get_voltage_lims(self):
"""Returns list of min and max voltage limits for the current impedance"""
- return [self.channel_limits["voltage lims"][0][self.impedance][key] for key in ["min", "max"]]
+ return [self.channel_limits["voltage lims"][0][self.impedance][key]
+ for key in ["min", "max"]]
+
def get_amplitude_lims(self):
"""Returns list of min and max amplitude limits for the current impedance"""
- return [self.channel_limits["amplitude lims"][0][self.impedance][key] for key in ["min", "max"]]
+ return [self.channel_limits["amplitude lims"][0][self.impedance][key]
+ for key in ["min", "max"]]
def get_settings(self):
"""Get the settings for the channel
@@ -2561,9 +3114,9 @@ Attributes
Returns
-------
current_settings : dict
- Settings currently in use as a dictionary with keys output, function,
- amplitude, offset, and frequency and values tuples of the corresponding
- return and unit
+ Settings currently in use as a dictionary with keys output,
+ function, amplitude, offset, and frequency and values tuples of
+ the corresponding return and unit
"""
return {"output": (self.state_str[self.get_output_state()], ""),
"function": (self.get_function(), ""),
@@ -2572,17 +3125,39 @@ Attributes
"frequency": (self.get_frequency(), "Hz")}
def print_settings(self):
- """Print the settings currently in use for the channel
- (Recommended to use the `func_gen.print_settings` for printing both channels)
+ """Print the settings currently in use for the channel (Recommended
+ to use the `FuncGen.print_settings` for printing both channels)
"""
settings = self.get_settings()
longest_key = max([len(key) for key in settings.keys()])
print("\nCurrent settings for channel {}".format(self.channel))
print("==============================")
- for key, (val, unit) in zip(settings.items()):
- print("{:>{num_char}s} {} {}".format(key, val, unit, num_char=longest_key))
-
- def set_output(self, state):
+ for key, (val, unit) in settings.items():
+ print("{:>{num_char}s} {} {}".format(key, val, unit,
+ num_char=longest_key))
+
+ def set_settings(self, settings):
+ """Set the settings of the channel with a settings dictionary. Will
+ set the outout to OFF before applyign the settings (and turn the
+ channel ON or leave it OFF depending on the settings dict)
+
+ Parameteres
+ -----------
+ settings : dict
+ Settings dictionary as returned by `get_settings`: should have
+ keys output, function, amplitude, offset, and frequency
+ """
+ # First turn off to ensure no potentially harmful
+ # combination of settings
+ self.set_output_state("OFF")
+ # Set settings according to dictionary
+ self.set_function(settings['function'][0])
+ self.set_amplitude(settings['amplitude'][0])
+ self.set_offset(settings['offset'][0])
+ self.set_frequency(settings['frequency'][0])
+ self.set_output_state(settings['output'][0])
+
+ def set_output_state(self, state):
"""Enables or diables the output of the channel
Parameters
@@ -2594,38 +3169,65 @@ Attributes
Raises
------
NotSetError
- If `self.func_gen.verify_param_set` is `True` and the value after
+ If `self.fgen.verify_param_set` is `True` and the value after
applying the set function does not match the value returned by the
get function
"""
- self.func_gen.write("OUTPut{}:STATe {}".format(self.channel, state),
- custom_err_message="turn channel {} to state {}".format(self.channel, state))
- if self.func_gen.verify_param_set:
- actual_state = self.get_output()
+ err_msg = "turn channel {} to state {}".format(self.channel, state)
+ self.fgen.write("OUTPut{}:STATe {}".format(self.channel, state),
+ custom_err_message=err_msg)
+ if self.fgen.verify_param_set:
+ actual_state = self.get_output_state()
if not actual_state == state:
- raise NotSetError("Channel {} was not turned {}, it is {}.\nError from the instrument: {}".format(self.channel, state, self.state_str[actual_state], self.func_gen.get_error()))
+ msg = ("Channel {} was not turned {}, it is {}.\n"
+ "Error from the instrument: {}"
+ "".format(self.channel, state,
+ self.state_str[actual_state],
+ self.fgen.get_error()))
+ raise NotSetError(msg)
+
+ def get_output(self):
+ """Wrapper for get_output_state"""
+ return self.get_output_state()
+
+ def set_output(self, state):
+ """Wrapper for set_output_state"""
+ self.set_output_state(state)
def set_function(self, shape):
"""Set the function shape of the output
Parameters
----------
- shape : {SINusoid, SQUare, PULSe, RAMP, PRNoise, <Built_in>, USER[0], USER1, ..., USER255, EMEMory, EFILe}
- <Built_in>::={StairDown|StairUp|Stair Up&Dwn|Trapezoid|RoundHalf|AbsSine|AbsHalfSine|ClippedSine|ChoppedSine|NegRamp|OscRise|OscDecay|CodedPulse|PosPulse|NegPulse|ExpRise|ExpDecay|Sinc|Tan|Cotan|SquareRoot|X^2|HaverSine|Lorentz|Ln(x)|X^3|CauchyDistr|BesselJ|BesselY|ErrorFunc|Airy|Rectangle|Gauss|Hamming|Hanning|Bartlett|Blackman|Laylight|Triangle|DC|Heart|Round|Chirp|Rhombus|Cardiac}
+ shape : {SINusoid, SQUare, PULSe, RAMP, PRNoise, <Built_in>, USER[0],
+ USER1, ..., USER255, EMEMory, EFILe}
+ <Built_in>::={StairDown|StairUp|Stair Up&Dwn|Trapezoid|RoundHalf|
+ AbsSine|AbsHalfSine|ClippedSine|ChoppedSine|NegRamp|OscRise|
+ OscDecay|CodedPulse|PosPulse|NegPulse|ExpRise|ExpDecay|Sinc|
+ Tan|Cotan|SquareRoot|X^2|HaverSine|Lorentz|Ln(x)|X^3|CauchyDistr|
+ BesselJ|BesselY|ErrorFunc|Airy|Rectangle|Gauss|Hamming|Hanning|
+ Bartlett|Blackman|Laylight|Triangle|DC|Heart|Round|Chirp|Rhombus|
+ Cardiac}
Raises
------
NotSetError
- If `self.func_gen.verify_param_set` is `True` and the value after
+ If `self.fgen.verify_param_set` is `True` and the value after
applying the set function does not match the value returned by the
get function
"""
- self.func_gen.write("{}FUNCtion:SHAPe {}".format(self.source, shape),
- custom_err_message="set function {}".format(shape))
- if self.func_gen.verify_param_set:
+ cmd = "{}FUNCtion:SHAPe {}".format(self.source, shape)
+ self.fgen.write(cmd, custom_err_message="set function {}".format(shape))
+ if self.fgen.verify_param_set:
actual_shape = self.get_function()
if not actual_shape == shape:
- raise NotSetError("Function {} was not set on channel {}, it is {}. Check that the function name is correctly spelt. Run set_function.__doc__ to see available shapes.\nError from the instrument: {}".format(shape, self.channel, actual_shape, self.func_gen.get_error()))
+ msg = ("Function {} was not set on channel {}, it is {}. "
+ "Check that the function name is correctly spelt. "
+ "Run set_function.__doc__ to see available shapes.\n"
+ "Error from the instrument: {}"
+ "".format(shape, self.channel, actual_shape,
+ self.fgen.get_error()))
+ raise NotSetError(msg)
def set_amplitude(self, amplitude):
"""Set the peak-to-peak amplitude in volts
@@ -2633,43 +3235,60 @@ Attributes
Parameters
----------
amplitude : float or {"max", "min"}
- 0.1mV or four digits resolution, "max" or "min" will set the amplitude
- to the maximum or minimum limit given in `channel_limits`
+ 0.1mV or four digits resolution, "max" or "min" will set the
+ amplitude to the maximum or minimum limit given in `channel_limits`
Raises
------
NotSetError
- If `self.func_gen.verify_param_set` is `True` and the value after
+ If `self.fgen.verify_param_set` is `True` and the value after
applying the set function does not match the value returned by the
get function
"""
- # check if keyword min or max is given
+ # Check if keyword min or max is given
if str(amplitude).lower() in ["min", "max"]:
unit = "" # no unit for MIN/MAX
- # look up what the limit is for this keyword
+ # Look up what the limit is for this keyword
amplitude = self.channel_limits["amplitude lims"][0][self.impedance][str(amplitude).lower()]
else:
unit = "Vpp"
- # check if the given amplitude is within the current limits
+ # Check if the given amplitude is within the current limits
min_ampl, max_ampl = self.get_amplitude_lims()
if amplitude < min_ampl or amplitude > max_ampl:
- raise NotSetError("Could not set the amplitude {}{unit} as it is not within the amplitude limits set for the instrument [{}, {}]{unit}".format(amplitude, min_ampl, max_ampl, unit=unit))
- # check that the new amplitude will not violate voltage limits
+ msg = ("Could not set the amplitude {}{unit} as it is not "
+ "within the amplitude limits set for the instrument "
+ "[{}, {}]{unit}".format(amplitude, min_ampl, max_ampl,
+ unit=unit))
+ raise NotSetError(msg)
+ # Check that the new amplitude will not violate voltage limits
min_volt, max_volt = self.get_voltage_lims()
current_offset = self.get_offset()
- if amplitude/2-current_offset < min_volt or amplitude/2+current_offset > max_volt:
- raise NotSetError("Could not set the amplitude {}{unit} as the amplitude combined with the offset ({}V) will be outside the absolute voltage limits [{}, {}]{unit}".format(amplitude, current_offset, min_volt, max_volt, unit=unit))
- # set the amplitude
- self.func_gen.write("{}VOLTage:LEVel {}{}".format(self.source, amplitude, unit),
- custom_err_message="set amplitude {}{}".format(amplitude, unit))
- # verify that the amplitude has been set
- if self.func_gen.verify_param_set:
+ if (amplitude/2-current_offset < min_volt or
+ amplitude/2+current_offset > max_volt):
+ msg = ("Could not set the amplitude {}{unit} as the amplitude "
+ "combined with the offset ({}V) will be outside the "
+ "absolute voltage limits [{}, {}]{unit}"
+ "".format(amplitude, current_offset, min_volt, max_volt,
+ unit=unit))
+ raise NotSetError(msg)
+ # Set the amplitude
+ cmd = "{}VOLTage:LEVel {}{}".format(self.source, amplitude, unit)
+ err_msg = "set amplitude {}{}".format(amplitude, unit)
+ self.fgen.write(cmd, custom_err_message=err_msg)
+ # Verify that the amplitude has been set
+ if self.fgen.verify_param_set:
actual_amplitude = self.get_amplitude()
- # multiply with the appropriate factor according to SI prefix, or
+ # Multiply with the appropriate factor according to SI prefix, or
# if string is empty, use the value looked up from channel_limits earlier
check_amplitude = amplitude*SI_prefix_to_factor(unit) if not unit == "" else amplitude
if not actual_amplitude == check_amplitude:
- raise NotSetError("Amplitude {}{} was not set on channel {}, it is {}Vpp. Check that the number is within the possible range and in the correct format.\nError from the instrument: {}".format(amplitude, unit, self.channel, actual_amplitude, self.func_gen.get_error()))
+ msg = ("Amplitude {}{} was not set on channel {}, it is "
+ "{}Vpp. Check that the number is within the possible "
+ "range and in the correct format.\nError from the "
+ "instrument: {}"
+ "".format(amplitude, unit, self.channel,
+ actual_amplitude, self.fgen.get_error()))
+ raise NotSetError(msg)
def set_offset(self, offset, unit="V"):
"""Set offset in volts (or mV, see options)
@@ -2683,25 +3302,38 @@ Attributes
Raises
------
NotSetError
- If `self.func_gen.verify_param_set` is `True` and the value after
+ If `self.fgen.verify_param_set` is `True` and the value after
applying the set function does not match the value returned by the
get function
"""
- # check that the new offset will not violate voltage limits
+ # Check that the new offset will not violate voltage limits
min_volt, max_volt = self.get_voltage_lims()
current_amplitude = self.get_amplitude()
- if current_amplitude/2-offset < min_volt or current_amplitude/2+offset > max_volt:
- raise NotSetError("Could not set the offset {}{unit} as the offset combined with the amplitude ({}V) will be outside the absolute voltage limits [{}, {}]{unit}".format(offset, current_amplitude, min_volt, max_volt, unit=unit))
- # set the offset
- self.func_gen.write("{}VOLTage:LEVel:OFFSet {}{}".format(self.source, offset, unit),
- custom_err_message="set offset {}{}".format(offset, unit))
- # verify that the offset has been set
- if self.func_gen.verify_param_set:
+ if (current_amplitude/2-offset < min_volt or
+ current_amplitude/2+offset > max_volt):
+ msg = ("Could not set the offset {}{unit} as the offset combined"
+ "with the amplitude ({}V) will be outside the absolute "
+ "voltage limits [{}, {}]{unit}".format(offset,
+ current_amplitude,
+ min_volt, max_volt,
+ unit=unit))
+ raise NotSetError(msg)
+ # Set the offset
+ cmd = "{}VOLTage:LEVel:OFFSet {}{}".format(self.source, offset, unit)
+ err_msg = "set offset {}{}".format(offset, unit)
+ self.fgen.write(cmd, custom_err_message=err_msg)
+ # Verify that the offset has been set
+ if self.fgen.verify_param_set:
actual_offset = self.get_offset()
- # multiply with the appropriate factor according to SI prefix
+ # Multiply with the appropriate factor according to SI prefix
check_offset = offset*SI_prefix_to_factor(unit)
if not actual_offset == check_offset:
- raise NotSetError("Offset {}{} was not set on channel {}, it is {}V. Check that the number is within the possible range and in the correct format.\nError from the instrument: {}".format(offset, unit, self.channel, actual_offset, self.func_gen.get_error()))
+ msg = ("Offset {}{} was not set on channel {}, it is {}V. "
+ "Check that the number is within the possible range and "
+ "in the correct format.\nError from the instrument: {}"
+ "".format(offset, unit, self.channel, actual_offset,
+ self.fgen.get_error()))
+ raise NotSetError(msg)
def set_frequency(self, freq, unit="Hz"):
"""Set the frequency in Hertz (or kHz, MHz, see options)
@@ -2715,43 +3347,51 @@ Attributes
Raises
------
NotSetError
- If `self.func_gen.verify_param_set` is `True` and the value after
+ If `self.fgen.verify_param_set` is `True` and the value after
applying the set function does not match the value returned by the
get function
"""
if str(freq).lower() in ["min", "max"]: # handle min and max keywords
unit = "" # no unit for MIN/MAX
- # look up what the limit is for this keyword
+ # Look up what the limit is for this keyword
freq = self.channel_limits["frequency lims"][0][str(freq).lower()]
else:
- # check if the given frequency is within the current limits
+ # Check if the given frequency is within the current limits
min_freq, max_freq = self.get_frequency_lims()
if freq < min_freq or freq > max_freq:
- raise NotSetError("Could not set the frequency {}{} as it is not within the frequency limits set for the instrument [{}, {}]Hz".format(freq, unit, min_freq, max_freq))
- # check that the new amplitude will not violate voltage limits
+ msg = ("Could not set the frequency {}{} as it is not within "
+ "the frequency limits set for the instrument [{}, {}]"
+ "Hz".format(freq, unit, min_freq, max_freq))
+ raise NotSetError(msg)
+ # Check that the new amplitude will not violate voltage limits
min_volt, max_volt = self.get_voltage_lims()
- # set the frequency
- self.func_gen.write("{}FREQuency:FIXed {}{}".format(self.source, freq, unit),
+ # Set the frequency
+ self.fgen.write("{}FREQuency:FIXed {}{}".format(self.source, freq, unit),
custom_err_message="set frequency {}{}".format(freq, unit))
- # verify that the amplitude has been set
- if self.func_gen.verify_param_set:
+ # Verify that the amplitude has been set
+ if self.fgen.verify_param_set:
actual_freq = self.get_frequency()
- # multiply with the appropriate factor according to SI prefix, or
+ # Multiply with the appropriate factor according to SI prefix, or
# if string is empty, use the value looked up from channel_limits earlier
check_freq = freq*SI_prefix_to_factor(unit) if not unit == "" else freq
if not actual_freq == check_freq:
- raise NotSetError("Frequency {}{} was not set on channel {}, it is {}Hz. Check that the number is within the possible range and in the correct format.\nError from the instrument: {}".format(freq, unit, self.channel, actual_freq, self.func_gen.get_error()))
+ msg = ("Frequency {}{} was not set on channel {}, it is {}Hz. "
+ "Check that the number is within the possible range and in "
+ "the correct format.\nError from the instrument: {}"
+ "".format(freq, unit, self.channel, actual_freq,
+ self.fgen.get_error()))
+ raise NotSetError(msg)
Class variables
-var state_str
+var state_str
-
Dictionary for converting output states to "ON" and "OFF"
Methods
-
+
def get_amplitude(self)
-
@@ -2762,10 +3402,10 @@
Methods
def get_amplitude(self):
"""Returns peak-to-peak voltage in volts"""
- return float(self.func_gen.query("{}VOLTage:AMPLitude?".format(self.source)))
+ return float(self.fgen.query("{}VOLTage:AMPLitude?".format(self.source)))
-
+
def get_amplitude_lims(self)
@@ -2776,10 +3416,11 @@ Methods
def get_amplitude_lims(self):
"""Returns list of min and max amplitude limits for the current impedance"""
- return [self.channel_limits["amplitude lims"][0][self.impedance][key] for key in ["min", "max"]]
+ return [self.channel_limits["amplitude lims"][0][self.impedance][key]
+ for key in ["min", "max"]]
-
+
def get_frequency(self)
@@ -2790,10 +3431,10 @@ Methods
def get_frequency(self):
"""Returns frequency in Hertz"""
- return float(self.func_gen.query("{}FREQuency?".format(self.source)))
+ return float(self.fgen.query("{}FREQuency?".format(self.source)))
-
+
def get_frequency_lims(self)
@@ -2804,10 +3445,11 @@ Methods
def get_frequency_lims(self):
"""Returns list of min and max frequency limits"""
- return [self.channel_limits["frequency lims"][0][key] for key in ["min", "max"]]
+ return [self.channel_limits["frequency lims"][0][key]
+ for key in ["min", "max"]]
-
+
def get_function(self)
@@ -2818,10 +3460,10 @@ Methods
def get_function(self):
"""Returns string of function name"""
- return self.func_gen.query("{}FUNCtion:SHAPe?".format(self.source))
+ return self.fgen.query("{}FUNCtion:SHAPe?".format(self.source))
-
+
def get_offset(self)
@@ -2832,10 +3474,24 @@ Methods
def get_offset(self):
"""Returns offset voltage in volts"""
- return float(self.func_gen.query("{}VOLTage:OFFSet?".format(self.source)))
+ return float(self.fgen.query("{}VOLTage:OFFSet?".format(self.source)))
+
+
+
+def get_output(self)
+
+
+Wrapper for get_output_state
+
+
+Expand source code
+
+def get_output(self):
+ """Wrapper for get_output_state"""
+ return self.get_output_state()
-
+
def get_output_state(self)
@@ -2846,10 +3502,10 @@ Methods
def get_output_state(self):
"""Returns "0" for "OFF", "1" for "ON" """
- return self.func_gen.query("OUTPut{}:STATe?".format(self.channel))
+ return self.fgen.query("OUTPut{}:STATe?".format(self.channel))
-
+
def get_settings(self)
@@ -2857,9 +3513,9 @@ Methods
Returns
current_settings
: dict
-- Settings currently in use as a dictionary with keys output, function,
-amplitude, offset, and frequency and values tuples of the corresponding
-return and unit
+- Settings currently in use as a dictionary with keys output,
+function, amplitude, offset, and frequency and values tuples of
+the corresponding return and unit
@@ -2871,9 +3527,9 @@ Returns
Returns
-------
current_settings : dict
- Settings currently in use as a dictionary with keys output, function,
- amplitude, offset, and frequency and values tuples of the corresponding
- return and unit
+ Settings currently in use as a dictionary with keys output,
+ function, amplitude, offset, and frequency and values tuples of
+ the corresponding return and unit
"""
return {"output": (self.state_str[self.get_output_state()], ""),
"function": (self.get_function(), ""),
@@ -2882,7 +3538,7 @@ Returns
"frequency": (self.get_frequency(), "Hz")}
-
+
def get_voltage_lims(self)
@@ -2893,32 +3549,66 @@ Returns
def get_voltage_lims(self):
"""Returns list of min and max voltage limits for the current impedance"""
- return [self.channel_limits["voltage lims"][0][self.impedance][key] for key in ["min", "max"]]
+ return [self.channel_limits["voltage lims"][0][self.impedance][key]
+ for key in ["min", "max"]]
+
+
+
+def impedance_dependent_limit(self, limit_type)
+
+
+Check if the limit type is impedance dependent (voltages) or
+not (frequency)
+Returns
+
+bool
+True
if the limit is impedance dependent
+
+
+
+Expand source code
+
+def impedance_dependent_limit(self, limit_type):
+ """Check if the limit type is impedance dependent (voltages) or
+ not (frequency)
+
+ Returns
+ -------
+ bool
+ `True` if the limit is impedance dependent
+ """
+ try: # to access the key "min" to check if impedance must be selected
+ _ = self.channel_limits[limit_type][0]["min"]
+ return False
+ except KeyError: # if the key does not exist
+ # The impedance must be selected
+ return True
-
+
def print_settings(self)
-Print the settings currently in use for the channel
-(Recommended to use the func_gen.print_settings()
for printing both channels)
+Print the settings currently in use for the channel (Recommended
+to use the FuncGen.print_settings()
for printing both channels)
Expand source code
def print_settings(self):
- """Print the settings currently in use for the channel
- (Recommended to use the `func_gen.print_settings` for printing both channels)
+ """Print the settings currently in use for the channel (Recommended
+ to use the `FuncGen.print_settings` for printing both channels)
"""
settings = self.get_settings()
longest_key = max([len(key) for key in settings.keys()])
print("\nCurrent settings for channel {}".format(self.channel))
print("==============================")
- for key, (val, unit) in zip(settings.items()):
- print("{:>{num_char}s} {} {}".format(key, val, unit, num_char=longest_key))
+ for key, (val, unit) in settings.items():
+ print("{:>{num_char}s} {} {}".format(key, val, unit,
+ num_char=longest_key))
-
+
def set_amplitude(self, amplitude)
@@ -2926,13 +3616,13 @@ Returns
Parameters
amplitude
: float
or {"max"
, "min"
}
-- 0.1mV or four digits resolution, "max" or "min" will set the amplitude
-to the maximum or minimum limit given in
channel_limits
+- 0.1mV or four digits resolution, "max" or "min" will set the
+amplitude to the maximum or minimum limit given in
channel_limits
Raises
NotSetError
-- If
self.func_gen.verify_param_set
is True
and the value after
+- If
self.fgen.verify_param_set
is True
and the value after
applying the set function does not match the value returned by the
get function
@@ -2946,46 +3636,63 @@ Raises
Parameters
----------
amplitude : float or {"max", "min"}
- 0.1mV or four digits resolution, "max" or "min" will set the amplitude
- to the maximum or minimum limit given in `channel_limits`
+ 0.1mV or four digits resolution, "max" or "min" will set the
+ amplitude to the maximum or minimum limit given in `channel_limits`
Raises
------
NotSetError
- If `self.func_gen.verify_param_set` is `True` and the value after
+ If `self.fgen.verify_param_set` is `True` and the value after
applying the set function does not match the value returned by the
get function
"""
- # check if keyword min or max is given
+ # Check if keyword min or max is given
if str(amplitude).lower() in ["min", "max"]:
unit = "" # no unit for MIN/MAX
- # look up what the limit is for this keyword
+ # Look up what the limit is for this keyword
amplitude = self.channel_limits["amplitude lims"][0][self.impedance][str(amplitude).lower()]
else:
unit = "Vpp"
- # check if the given amplitude is within the current limits
+ # Check if the given amplitude is within the current limits
min_ampl, max_ampl = self.get_amplitude_lims()
if amplitude < min_ampl or amplitude > max_ampl:
- raise NotSetError("Could not set the amplitude {}{unit} as it is not within the amplitude limits set for the instrument [{}, {}]{unit}".format(amplitude, min_ampl, max_ampl, unit=unit))
- # check that the new amplitude will not violate voltage limits
+ msg = ("Could not set the amplitude {}{unit} as it is not "
+ "within the amplitude limits set for the instrument "
+ "[{}, {}]{unit}".format(amplitude, min_ampl, max_ampl,
+ unit=unit))
+ raise NotSetError(msg)
+ # Check that the new amplitude will not violate voltage limits
min_volt, max_volt = self.get_voltage_lims()
current_offset = self.get_offset()
- if amplitude/2-current_offset < min_volt or amplitude/2+current_offset > max_volt:
- raise NotSetError("Could not set the amplitude {}{unit} as the amplitude combined with the offset ({}V) will be outside the absolute voltage limits [{}, {}]{unit}".format(amplitude, current_offset, min_volt, max_volt, unit=unit))
- # set the amplitude
- self.func_gen.write("{}VOLTage:LEVel {}{}".format(self.source, amplitude, unit),
- custom_err_message="set amplitude {}{}".format(amplitude, unit))
- # verify that the amplitude has been set
- if self.func_gen.verify_param_set:
+ if (amplitude/2-current_offset < min_volt or
+ amplitude/2+current_offset > max_volt):
+ msg = ("Could not set the amplitude {}{unit} as the amplitude "
+ "combined with the offset ({}V) will be outside the "
+ "absolute voltage limits [{}, {}]{unit}"
+ "".format(amplitude, current_offset, min_volt, max_volt,
+ unit=unit))
+ raise NotSetError(msg)
+ # Set the amplitude
+ cmd = "{}VOLTage:LEVel {}{}".format(self.source, amplitude, unit)
+ err_msg = "set amplitude {}{}".format(amplitude, unit)
+ self.fgen.write(cmd, custom_err_message=err_msg)
+ # Verify that the amplitude has been set
+ if self.fgen.verify_param_set:
actual_amplitude = self.get_amplitude()
- # multiply with the appropriate factor according to SI prefix, or
+ # Multiply with the appropriate factor according to SI prefix, or
# if string is empty, use the value looked up from channel_limits earlier
check_amplitude = amplitude*SI_prefix_to_factor(unit) if not unit == "" else amplitude
if not actual_amplitude == check_amplitude:
- raise NotSetError("Amplitude {}{} was not set on channel {}, it is {}Vpp. Check that the number is within the possible range and in the correct format.\nError from the instrument: {}".format(amplitude, unit, self.channel, actual_amplitude, self.func_gen.get_error()))
+ msg = ("Amplitude {}{} was not set on channel {}, it is "
+ "{}Vpp. Check that the number is within the possible "
+ "range and in the correct format.\nError from the "
+ "instrument: {}"
+ "".format(amplitude, unit, self.channel,
+ actual_amplitude, self.fgen.get_error()))
+ raise NotSetError(msg)
-
+
def set_frequency(self, freq, unit='Hz')
@@ -3000,7 +3707,7 @@ Parameters
Raises
NotSetError
-- If
self.func_gen.verify_param_set
is True
and the value after
+- If
self.fgen.verify_param_set
is True
and the value after
applying the set function does not match the value returned by the
get function
@@ -3020,48 +3727,63 @@ Raises
Raises
------
NotSetError
- If `self.func_gen.verify_param_set` is `True` and the value after
+ If `self.fgen.verify_param_set` is `True` and the value after
applying the set function does not match the value returned by the
get function
"""
if str(freq).lower() in ["min", "max"]: # handle min and max keywords
unit = "" # no unit for MIN/MAX
- # look up what the limit is for this keyword
+ # Look up what the limit is for this keyword
freq = self.channel_limits["frequency lims"][0][str(freq).lower()]
else:
- # check if the given frequency is within the current limits
+ # Check if the given frequency is within the current limits
min_freq, max_freq = self.get_frequency_lims()
if freq < min_freq or freq > max_freq:
- raise NotSetError("Could not set the frequency {}{} as it is not within the frequency limits set for the instrument [{}, {}]Hz".format(freq, unit, min_freq, max_freq))
- # check that the new amplitude will not violate voltage limits
+ msg = ("Could not set the frequency {}{} as it is not within "
+ "the frequency limits set for the instrument [{}, {}]"
+ "Hz".format(freq, unit, min_freq, max_freq))
+ raise NotSetError(msg)
+ # Check that the new amplitude will not violate voltage limits
min_volt, max_volt = self.get_voltage_lims()
- # set the frequency
- self.func_gen.write("{}FREQuency:FIXed {}{}".format(self.source, freq, unit),
+ # Set the frequency
+ self.fgen.write("{}FREQuency:FIXed {}{}".format(self.source, freq, unit),
custom_err_message="set frequency {}{}".format(freq, unit))
- # verify that the amplitude has been set
- if self.func_gen.verify_param_set:
+ # Verify that the amplitude has been set
+ if self.fgen.verify_param_set:
actual_freq = self.get_frequency()
- # multiply with the appropriate factor according to SI prefix, or
+ # Multiply with the appropriate factor according to SI prefix, or
# if string is empty, use the value looked up from channel_limits earlier
check_freq = freq*SI_prefix_to_factor(unit) if not unit == "" else freq
if not actual_freq == check_freq:
- raise NotSetError("Frequency {}{} was not set on channel {}, it is {}Hz. Check that the number is within the possible range and in the correct format.\nError from the instrument: {}".format(freq, unit, self.channel, actual_freq, self.func_gen.get_error()))
+ msg = ("Frequency {}{} was not set on channel {}, it is {}Hz. "
+ "Check that the number is within the possible range and in "
+ "the correct format.\nError from the instrument: {}"
+ "".format(freq, unit, self.channel, actual_freq,
+ self.fgen.get_error()))
+ raise NotSetError(msg)
-
+
def set_function(self, shape)
Set the function shape of the output
Parameters
-shape
: {SINusoid
, SQUare
, PULSe
, RAMP
, PRNoise
, <Built_in
>, USER
[0
], USER1
, ...
, USER255
, EMEMory
, EFILe
}
-::={StairDown|StairUp|Stair Up&Dwn|Trapezoid|RoundHalf|AbsSine|AbsHalfSine|ClippedSine|ChoppedSine|NegRamp|OscRise|OscDecay|CodedPulse|PosPulse|NegPulse|ExpRise|ExpDecay|Sinc|Tan|Cotan|SquareRoot|X^2|HaverSine|Lorentz|Ln(x)|X^3|CauchyDistr|BesselJ|BesselY|ErrorFunc|Airy|Rectangle|Gauss|Hamming|Hanning|Bartlett|Blackman|Laylight|Triangle|DC|Heart|Round|Chirp|Rhombus|Cardiac}
+shape
: {SINusoid
, SQUare
, PULSe
, RAMP
, PRNoise
, <Built_in
>, USER
[0
],
+- USER1, …, USER255, EMEMory, EFILe}
+
::={StairDown|StairUp|Stair Up&Dwn|Trapezoid|RoundHalf|
+AbsSine|AbsHalfSine|ClippedSine|ChoppedSine|NegRamp|OscRise|
+OscDecay|CodedPulse|PosPulse|NegPulse|ExpRise|ExpDecay|Sinc|
+Tan|Cotan|SquareRoot|X^2|HaverSine|Lorentz|Ln(x)|X^3|CauchyDistr|
+BesselJ|BesselY|ErrorFunc|Airy|Rectangle|Gauss|Hamming|Hanning|
+Bartlett|Blackman|Laylight|Triangle|DC|Heart|Round|Chirp|Rhombus|
+Cardiac}
Raises
NotSetError
-- If
self.func_gen.verify_param_set
is True
and the value after
+- If
self.fgen.verify_param_set
is True
and the value after
applying the set function does not match the value returned by the
get function
@@ -3074,25 +3796,129 @@ Raises
Parameters
----------
- shape : {SINusoid, SQUare, PULSe, RAMP, PRNoise, <Built_in>, USER[0], USER1, ..., USER255, EMEMory, EFILe}
- <Built_in>::={StairDown|StairUp|Stair Up&Dwn|Trapezoid|RoundHalf|AbsSine|AbsHalfSine|ClippedSine|ChoppedSine|NegRamp|OscRise|OscDecay|CodedPulse|PosPulse|NegPulse|ExpRise|ExpDecay|Sinc|Tan|Cotan|SquareRoot|X^2|HaverSine|Lorentz|Ln(x)|X^3|CauchyDistr|BesselJ|BesselY|ErrorFunc|Airy|Rectangle|Gauss|Hamming|Hanning|Bartlett|Blackman|Laylight|Triangle|DC|Heart|Round|Chirp|Rhombus|Cardiac}
+ shape : {SINusoid, SQUare, PULSe, RAMP, PRNoise, <Built_in>, USER[0],
+ USER1, ..., USER255, EMEMory, EFILe}
+ <Built_in>::={StairDown|StairUp|Stair Up&Dwn|Trapezoid|RoundHalf|
+ AbsSine|AbsHalfSine|ClippedSine|ChoppedSine|NegRamp|OscRise|
+ OscDecay|CodedPulse|PosPulse|NegPulse|ExpRise|ExpDecay|Sinc|
+ Tan|Cotan|SquareRoot|X^2|HaverSine|Lorentz|Ln(x)|X^3|CauchyDistr|
+ BesselJ|BesselY|ErrorFunc|Airy|Rectangle|Gauss|Hamming|Hanning|
+ Bartlett|Blackman|Laylight|Triangle|DC|Heart|Round|Chirp|Rhombus|
+ Cardiac}
Raises
------
NotSetError
- If `self.func_gen.verify_param_set` is `True` and the value after
+ If `self.fgen.verify_param_set` is `True` and the value after
applying the set function does not match the value returned by the
get function
"""
- self.func_gen.write("{}FUNCtion:SHAPe {}".format(self.source, shape),
- custom_err_message="set function {}".format(shape))
- if self.func_gen.verify_param_set:
+ cmd = "{}FUNCtion:SHAPe {}".format(self.source, shape)
+ self.fgen.write(cmd, custom_err_message="set function {}".format(shape))
+ if self.fgen.verify_param_set:
actual_shape = self.get_function()
if not actual_shape == shape:
- raise NotSetError("Function {} was not set on channel {}, it is {}. Check that the function name is correctly spelt. Run set_function.__doc__ to see available shapes.\nError from the instrument: {}".format(shape, self.channel, actual_shape, self.func_gen.get_error()))
+ msg = ("Function {} was not set on channel {}, it is {}. "
+ "Check that the function name is correctly spelt. "
+ "Run set_function.__doc__ to see available shapes.\n"
+ "Error from the instrument: {}"
+ "".format(shape, self.channel, actual_shape,
+ self.fgen.get_error()))
+ raise NotSetError(msg)
-
+
+def set_limit(self, limit_type, bound, new_value, verbose=False)
+
+
+Set a limit if the new value is within the instrument limits and are
+self consistent (max larger than min)
+Parameterers
+
+limit_type
: str
+- The name of the limit in the channel_limits dictionary
+bound
: {"min"
, "max"
}
+- Specifies if it is the max or the min limit that is to be set
+new_value
: float
+- The new value to be used for the limit
+verbose
: bool
+- Print confirmation that the limit was set or reason for why the
+limit was not set
+
+Returns
+
+bool
+True
if new limit set, False
otherwise
+
+
+
+Expand source code
+
+def set_limit(self, limit_type, bound, new_value, verbose=False):
+ """Set a limit if the new value is within the instrument limits and are
+ self consistent (max larger than min)
+
+ Parameterers
+ ------------
+ limit_type : str
+ The name of the limit in the channel_limits dictionary
+ bound : {"min", "max"}
+ Specifies if it is the max or the min limit that is to be set
+ new_value : float
+ The new value to be used for the limit
+ verbose : bool
+ Print confirmation that the limit was set or reason for why the
+ limit was not set
+
+ Returns
+ -------
+ bool
+ `True` if new limit set, `False` otherwise
+ """
+ # Short hand references
+ inst_limit_dict = self.fgen.instrument_limits[limit_type]
+ channel_limit_dict = self.channel_limits[limit_type]
+ # Find the instrument limit and unit
+ use_impedance = self.impedance_dependent_limit(limit_type)
+ if use_impedance:
+ inst_value = inst_limit_dict[0][self.impedance][bound]
+ else:
+ inst_value = inst_limit_dict[0][bound]
+ unit = inst_limit_dict[1]
+ # Check that the new value is within the intrument limits
+ acceptable_min = bound == "min" and new_value > inst_value
+ if use_impedance:
+ current_min = channel_limit_dict[0][self.impedance]["min"]
+ else:
+ current_min = channel_limit_dict[0]["min"]
+ larger_than_min = new_value > current_min
+ acceptable_max = bound == "max" and new_value < inst_value and larger_than_min
+ if acceptable_min or acceptable_max: # within the limits
+ # Set the new channel_limit, using the impedance depending on the
+ # limit type. Beware that the shorthand cannot be used, as this
+ # only changes the shorthand not the dictionary itself
+ if use_impedance:
+ self.channel_limits[limit_type][0][self.impedance][bound] = new_value
+ else:
+ self.channel_limits[limit_type][0][bound] = new_value
+ if verbose:
+ print("\tNew limit set {}{}".format(new_value, unit))
+ return True
+ elif verbose: # print description of why the limit was not set
+ if larger_than_min:
+ reason = "larger" if bound == "max" else "smaller"
+ print("\tNew limit NOT set: {}{unit} is {} than the instrument "
+ "limit ({}{unit})".format(new_value, reason, inst_value,
+ unit=unit))
+ else:
+ print("\tNew limit NOT set: {}{unit} is smaller than the "
+ "current set minimum ({}{unit})".format(new_value,
+ current_min,
+ unit=unit))
+ return False
+
+
+
def set_offset(self, offset, unit='V')
@@ -3107,7 +3933,7 @@ Parameters
Raises
NotSetError
-- If
self.func_gen.verify_param_set
is True
and the value after
+- If
self.fgen.verify_param_set
is True
and the value after
applying the set function does not match the value returned by the
get function
@@ -3127,31 +3953,58 @@ Raises
Raises
------
NotSetError
- If `self.func_gen.verify_param_set` is `True` and the value after
+ If `self.fgen.verify_param_set` is `True` and the value after
applying the set function does not match the value returned by the
get function
"""
- # check that the new offset will not violate voltage limits
+ # Check that the new offset will not violate voltage limits
min_volt, max_volt = self.get_voltage_lims()
current_amplitude = self.get_amplitude()
- if current_amplitude/2-offset < min_volt or current_amplitude/2+offset > max_volt:
- raise NotSetError("Could not set the offset {}{unit} as the offset combined with the amplitude ({}V) will be outside the absolute voltage limits [{}, {}]{unit}".format(offset, current_amplitude, min_volt, max_volt, unit=unit))
- # set the offset
- self.func_gen.write("{}VOLTage:LEVel:OFFSet {}{}".format(self.source, offset, unit),
- custom_err_message="set offset {}{}".format(offset, unit))
- # verify that the offset has been set
- if self.func_gen.verify_param_set:
+ if (current_amplitude/2-offset < min_volt or
+ current_amplitude/2+offset > max_volt):
+ msg = ("Could not set the offset {}{unit} as the offset combined"
+ "with the amplitude ({}V) will be outside the absolute "
+ "voltage limits [{}, {}]{unit}".format(offset,
+ current_amplitude,
+ min_volt, max_volt,
+ unit=unit))
+ raise NotSetError(msg)
+ # Set the offset
+ cmd = "{}VOLTage:LEVel:OFFSet {}{}".format(self.source, offset, unit)
+ err_msg = "set offset {}{}".format(offset, unit)
+ self.fgen.write(cmd, custom_err_message=err_msg)
+ # Verify that the offset has been set
+ if self.fgen.verify_param_set:
actual_offset = self.get_offset()
- # multiply with the appropriate factor according to SI prefix
+ # Multiply with the appropriate factor according to SI prefix
check_offset = offset*SI_prefix_to_factor(unit)
if not actual_offset == check_offset:
- raise NotSetError("Offset {}{} was not set on channel {}, it is {}V. Check that the number is within the possible range and in the correct format.\nError from the instrument: {}".format(offset, unit, self.channel, actual_offset, self.func_gen.get_error()))
+ msg = ("Offset {}{} was not set on channel {}, it is {}V. "
+ "Check that the number is within the possible range and "
+ "in the correct format.\nError from the instrument: {}"
+ "".format(offset, unit, self.channel, actual_offset,
+ self.fgen.get_error()))
+ raise NotSetError(msg)
-
+
def set_output(self, state)
+Wrapper for set_output_state
+
+
+Expand source code
+
+def set_output(self, state):
+ """Wrapper for set_output_state"""
+ self.set_output_state(state)
+
+
+
+def set_output_state(self, state)
+
+
Enables or diables the output of the channel
Parameters
@@ -3162,7 +4015,7 @@ Parameters
Raises
NotSetError
-- If
self.func_gen.verify_param_set
is True
and the value after
+- If
self.fgen.verify_param_set
is True
and the value after
applying the set function does not match the value returned by the
get function
@@ -3170,7 +4023,7 @@ Raises
Expand source code
-def set_output(self, state):
+def set_output_state(self, state):
"""Enables or diables the output of the channel
Parameters
@@ -3182,19 +4035,64 @@ Raises
Raises
------
NotSetError
- If `self.func_gen.verify_param_set` is `True` and the value after
+ If `self.fgen.verify_param_set` is `True` and the value after
applying the set function does not match the value returned by the
get function
"""
- self.func_gen.write("OUTPut{}:STATe {}".format(self.channel, state),
- custom_err_message="turn channel {} to state {}".format(self.channel, state))
- if self.func_gen.verify_param_set:
- actual_state = self.get_output()
+ err_msg = "turn channel {} to state {}".format(self.channel, state)
+ self.fgen.write("OUTPut{}:STATe {}".format(self.channel, state),
+ custom_err_message=err_msg)
+ if self.fgen.verify_param_set:
+ actual_state = self.get_output_state()
if not actual_state == state:
- raise NotSetError("Channel {} was not turned {}, it is {}.\nError from the instrument: {}".format(self.channel, state, self.state_str[actual_state], self.func_gen.get_error()))
+ msg = ("Channel {} was not turned {}, it is {}.\n"
+ "Error from the instrument: {}"
+ "".format(self.channel, state,
+ self.state_str[actual_state],
+ self.fgen.get_error()))
+ raise NotSetError(msg)
+
+
+
+def set_settings(self, settings)
+
+
+Set the settings of the channel with a settings dictionary. Will
+set the outout to OFF before applyign the settings (and turn the
+channel ON or leave it OFF depending on the settings dict)
+Parameteres
+
+settings
: dict
+- Settings dictionary as returned by
get_settings
: should have
+keys output, function, amplitude, offset, and frequency
+
+
+
+Expand source code
+
+def set_settings(self, settings):
+ """Set the settings of the channel with a settings dictionary. Will
+ set the outout to OFF before applyign the settings (and turn the
+ channel ON or leave it OFF depending on the settings dict)
+
+ Parameteres
+ -----------
+ settings : dict
+ Settings dictionary as returned by `get_settings`: should have
+ keys output, function, amplitude, offset, and frequency
+ """
+ # First turn off to ensure no potentially harmful
+ # combination of settings
+ self.set_output_state("OFF")
+ # Set settings according to dictionary
+ self.set_function(settings['function'][0])
+ self.set_amplitude(settings['amplitude'][0])
+ self.set_offset(settings['offset'][0])
+ self.set_frequency(settings['frequency'][0])
+ self.set_output_state(settings['output'][0])
-
+
def set_stricter_limits(self)
@@ -3207,57 +4105,77 @@ Raises
def set_stricter_limits(self):
"""Set limits for the voltage and frequency limits of the channel output
through a series of prompts"""
- print("Set stricter voltage and frequency limits for channel {}".format(self.channel))
+ print("Set stricter voltage and frequency limits "
+ "for channel {}".format(self.channel))
print("Use enter only to leave a limit unchanged.")
# Go through the different limits in the instrument_limits dict
- for limit_type, (inst_limit_dict, unit) in self.func_gen.instrument_limits.items():
- try: # to access the key "min" to check if impedance must be selected
- _ = inst_limit_dict["min"]
- use_impedance = False
- except KeyError: # if the key does not exist
- # the impedance must be selected
- use_impedance = True
- inst_limit_dict = inst_limit_dict[self.impedance]
+ for limit_type, (inst_limit_dict, unit) in self.fgen.instrument_limits.items():
+ use_impedance = self.impedance_dependent_limit(limit_type)
print("Set {} in {}".format(limit_type, unit), end=" ")
if use_impedance:
+ inst_limit_dict = inst_limit_dict[self.impedance]
print("[{} impedance limit]".format(self.impedance))
else:
print("") # get new line
# Go through the min and max for the limit type
for key, inst_value in inst_limit_dict.items():
# prompt for new value
- new_value = input(" {} (instrument limit {}{}): ".format(key, inst_value, unit))
+ new_value = input(" {} (instrument limit {}{}): "
+ "".format(key, inst_value, unit))
if new_value == "":
- # do not change if empty
+ # Do not change if empty
print("\tLimit not changed")
else:
try: # to convert to float
new_value = float(new_value)
except ValueError:
- print("\tLimit unchanged: Could not convert {} to float".format(new_value))
+ print("\tLimit unchanged: Could not convert \'{}\' "
+ "to float".format(new_value))
continue # to next item in dict
- # check that the new value is within the intrument limits
- acceptable_min = key == "min" and new_value > inst_value
- current_min = self.channel_limits[limit_type][0][self.impedance]["min"] if use_impedance else self.channel_limits[limit_type][0]["min"]
- larger_than_min = new_value > current_min
- acceptable_max = key == "max" and new_value < inst_value and larger_than_min
- if acceptable_min or acceptable_max: # within the limits
- # set the new channel_limit, using the impedance depending on the limit type
- if use_impedance:
- self.channel_limits[limit_type][0][self.impedance][key] = new_value
- else:
- self.channel_limits[limit_type][0][key] = new_value
- print("\tNew limit set {}{}".format(new_value, unit))
- else: # print description of why the limit was not set
- if larger_than_min:
- reason = "larger" if key == "max" else "smaller"
- print("\tNew limit NOT set: {}{unit} is {} than the instrument limit ({}{unit})".format(new_value, reason, inst_value, unit=unit))
- else:
- print("\tNew limit NOT set: {}{unit} is smaller than the current set minimum ({}{unit})".format(new_value, current_min, unit=unit))
+ # Set the new limit
+ self.set_limit(limit_type, key, new_value, verbose=True)
+
+class NotCompatibleError
+(*args, **kwargs)
+
+
+Error for when the instrument is not compatible with this module
+
+
+Expand source code
+
+class NotCompatibleError(Exception):
+ """Error for when the instrument is not compatible with this module"""
+
+Ancestors
+
+- builtins.Exception
+- builtins.BaseException
+
+
+
+class NotSetError
+(*args, **kwargs)
+
+
+Error for when a value cannot be written to the instrument
+
+
+Expand source code
+
+class NotSetError(Exception):
+ """Error for when a value cannot be written to the instrument"""
+
+Ancestors
+
+- builtins.Exception
+- builtins.BaseException
+
+
@@ -3270,65 +4188,72 @@ Index
Functions
Classes
-
-
NotCompatibleError
-
--
-
NotSetError
+FuncGen
+
+check_arb_waveform_length
+check_arb_waveform_type_and_range
+close
+get_custom_waveform
+get_error
+get_frequency_lock
+get_settings
+get_waveform_catalogue
+initialise_model_properties
+normalise_to_waveform
+print_settings
+query
+set_custom_waveform
+set_frequency_lock
+set_settings
+software_trig
+spawn_channel
+syncronise_waveforms
+verify_waveform
+write
+
-
-
func_gen
+FuncGenChannel
-check_arb_waveform_length
-check_arb_waveform_type_and_range
-close
-disable_frequency_lock
-enable_frequency_lock
-get_custom_waveform
-get_error
-get_frequency_lock
-get_settings
-get_waveform_catalogue
-initialise_model_properties
-normalise_to_waveform
-print_settings
-query
-set_custom_waveform
-software_trig
-spawn_channel
-syncronise_waveforms
-verify_waveform
-write
+get_amplitude
+get_amplitude_lims
+get_frequency
+get_frequency_lims
+get_function
+get_offset
+get_output
+get_output_state
+get_settings
+get_voltage_lims
+impedance_dependent_limit
+print_settings
+set_amplitude
+set_frequency
+set_function
+set_limit
+set_offset
+set_output
+set_output_state
+set_settings
+set_stricter_limits
+state_str
-
-
func_gen_channel
-
-get_amplitude
-get_amplitude_lims
-get_frequency
-get_frequency_lims
-get_function
-get_offset
-get_output_state
-get_settings
-get_voltage_lims
-print_settings
-set_amplitude
-set_frequency
-set_function
-set_offset
-set_output
-set_stricter_limits
-state_str
-
+NotCompatibleError
+
+-
+
NotSetError
@@ -3341,4 +4266,4 @@