Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signal Generation Restructure and addition of high level methods #183

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
8 changes: 3 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,15 @@ repos:
args: [--number, --end-of-line, keep]
additional_dependencies:
- setuptools # This is required since Python 3.12 no longer installs setuptools by default in virtual environments
- mdformat-myst
- mdformat-toc
- mdformat-frontmatter
- black==23.12.1 # This may need to be updated/removed in the future once ruff supports formatting python code blocks in markdown
- mdformat-beautysh
- mdformat-black
- black==23.12.1 # This may need to be updated/removed in the future once ruff supports formatting python code blocks in markdown
- mdformat-config
- mdformat-shfmt
- mdformat-web
- mdformat-gfm
- mdformat-footnote
- mdformat-myst
- mdformat-toc
# Currently rstcheck doesn't work with the templates
# - repo: https://github.com/rstcheck/rstcheck
# rev: v6.1.2
Expand Down
24 changes: 18 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,27 @@ Things to be included in the next release go here.

### Added

- Added TekVISA as one of the VISA backends supported.
- Added the constraint ranges for all signal generators
- Added drivers for AWG and AFG channels
- Added a property named `source_channel` in AWG's and AFG's.
- Added drivers for internal AFG in TekScopes.
- Added a property named `internal_afg` in TekScope.
- Added implementation of `generate_function` for all AWG models.
- Added two burst functions to SignalGeneratorMixin: one to set up burst and one to generate the burst by forcing trigger.
- NOTE: Only the AFG's and internal AFG have these functions implemented.
- Added `OutputSignalPath` enum attribute in AWG's representing output signal path options.
- Added two functions for loading waveform set files in the AWG70k's and AWG5200: one for loading a waveform set file and another for loading a specific waveform from a waveform set file.
- Added `sample_waveform_set_file` attribute in AWG70k's and AWG5200 to define the default waveform set file.

### Changed
qthompso marked this conversation as resolved.
Show resolved Hide resolved

- Updated the `get_model_series()` function to only warn the user if the model is not found in the `SupportedModels` enumeration. This also eliminates false warnings during unit tests.

### Fixed

- Updated the measurement source selection command for the MDO3K, MDO4K, MSO4K and DPO4K models to work properly.
- <span style="color:red">BREAKING CHANGE</span>. Changed the term "signal source" to "signal generator".
- All uses of this term are changed. Import paths now use signal_generator instead of signal_source.
- <span style="color:red">BREAKING CHANGE</span>. Changed the function name of `generate_waveform()` to `generate_function()`.
- `generate_waveform()` only exists on AWGs now, however the functionality is entirely changed.
- <span style="color:red">BREAKING CHANGE</span>. Changed the `generate_function()` function by removing burst functionality.
- Any use of burst now must use `setup_burst()` and `generate_burst()` instead.
- Updated AWG's such that the `family_base_class` is at the series level.

______________________________________________________________________

Expand Down
1 change: 1 addition & 0 deletions docs/advanced/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Advanced information about the `tm_devices` package.
maxdepth: 2
---
architecture.md
signal_generators.md
```

## Advanced Usage and Examples
Expand Down
638 changes: 638 additions & 0 deletions docs/advanced/signal_generators.md
qthompso marked this conversation as resolved.
Show resolved Hide resolved

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions docs/basic_usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ language: python

## Disable command checking

## Disable command checking

This removes an extra query that verifies the property was set to the expected
value. This can be disabled at the device level or disabled for all devices by
disabling verification via the device manager.
Expand All @@ -89,6 +91,27 @@ language: python
---
```

## Generate a function using an AFG

Call `generate_function()` to generate a 0.5 V, 10 MHz RAMP wave on SOURCE1 of the AFG.

```{literalinclude} ../examples/signal_generators/generate_function.py
---
language: python
---
```

## Getting the parameter constraints of a signal generator

Call `get_waveform_constraints()` and check if the frequency,
amplitude, and sample rate provided are possible to generate.

```{literalinclude} ../examples/signal_generators/waveform_constraints.py
---
language: python
---
```

## Curve query saved to csv

Perform a curve query and save the results to a csv file.
Expand Down
1 change: 1 addition & 0 deletions docs/contributing/add_new_driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ the filepath would be `tm_devices/drivers/pi/power_supplies/psu2200/new_psu.py`

```python
"""NewPSU device driver."""

from tm_devices.drivers.pi.power_supplies.psu2200.psu2200 import PSU2200


Expand Down
39 changes: 39 additions & 0 deletions docs/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,27 @@ API
AWG
: Arbitrary Waveform Generator

DAC
: Digital to Analog Converter

DAQ
: Digital Acquisition System

DCA
: Direct-Coupled Amplifier

DMM
: Digital Multimeter

DPOJET
: A jitter, noise, timing, and eye diagram analysis tool for Tektronix Performance Digital Oscilloscopes

GPIB
: General Purpose Interface Bus

IAFG
: Internal Arbitrary Function Generator.

LP
: Low Profile

Expand All @@ -36,6 +48,12 @@ MSO
MT
: Margin Tester

OCR
: Operation Condition Register

OPC
: Operation Complete

PI
: Programmable Interface

Expand All @@ -45,11 +63,32 @@ PSU
Scope
: Oscilloscope

SCPI
: Standard Commands for Programmable Instruments

SESR
: Standard Event Status Register

SMU
: Source Measure Unit

SRQ
: Service Request

SS
: System Switch

TCPIP
: Transmission Control Protocol/Internet Protocol

TSP
: Test Script Processor

USB
: Universal Serial Bus

VISA
: Virtual Instrument Software Architecture

WAI
: Wait
14 changes: 13 additions & 1 deletion examples/scopes/tekscope/generate_internal_afg_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# Create a connection to the scope and indicate that it is a MSO5 scope for type hinting
scope: MSO5 = dm.add_scope("192.168.1.102")

# Generate the signal
# Generate the signal using individual PI commands.
scope.commands.afg.frequency.write(10e6) # set frequency
scope.commands.afg.offset.write(0.2) # set offset
scope.commands.afg.square.duty.write(50) # set duty cycle
Expand All @@ -18,3 +18,15 @@
scope.commands.esr.query() # check for any errors

scope.commands.acquire.stopafter.write("SEQUENCE") # perform a single sequence

# Generate the same signal using a single method call.
scope.generate_function(
frequency=10e6,
offset=0.2,
amplitude=0.5,
duty_cycle=50,
function=scope.source_device_constants.functions.SQUARE,
termination="FIFTY",
)
scope.commands.ch[1].scale.write(0.5, verify=True) # set and check vertical scale on CH1
scope.commands.acquire.stopafter.write("SEQUENCE") # perform a single sequence
18 changes: 18 additions & 0 deletions examples/signal_generators/generate_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""An example showing how to generate a function using an AFG."""

from tm_devices import DeviceManager
from tm_devices.drivers import AFG3K

with DeviceManager(verbose=True) as dm:
# Create a connection to the AFG and indicate that it is an AFG3K for type hinting
afg3k: AFG3K = dm.add_afg("192.168.0.1")

# Generate a RAMP waveform on SOURCE1 of the AFG3K with the provided properties.
afg3k.generate_function(
function=afg3k.source_device_constants.functions.RAMP,
channel="SOURCE1",
frequency=10e6,
amplitude=0.5,
offset=0,
symmetry=50.0,
)
17 changes: 17 additions & 0 deletions examples/signal_generators/set_source_channel_properties.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""An example showing the use of an AWG source channel."""

from tm_devices import DeviceManager
from tm_devices.drivers import AWG5K

with DeviceManager(verbose=True) as dm:
# Create a connection to the scope and indicate that it is an AWG5K for type hinting.
awg5k: AWG5K = dm.add_awg("192.168.0.1")

# Set the offset to 0.5 on SOURCE1 of the AWG5K
awg5k.source_channel["SOURCE1"].set_offset(0.5)

# Set the amplitude to 0.2 on SOURCE1 of the AWG5K
awg5k.source_channel["SOURCE1"].set_amplitude(0.2)

# Turn on SOURCE1 of the AWG5K
awg5k.source_channel["SOURCE1"].set_state(1)
110 changes: 110 additions & 0 deletions examples/signal_generators/waveform_constraints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""An example showing the use of waveform constraints for an AWG."""

from tm_devices import DeviceManager
from tm_devices.drivers import AWG5K
from tm_devices.helpers.enums import SignalGeneratorFunctionsAWG

# The desired frequency.
DESIRED_FREQUENCY = 10e18

# The desired amplitude.
DESIRED_AMPLITUDE = 5.0

# The desired sample rate.
DESIRED_SAMPLE_RATE = 1

# The desired function to generate.
DESIRED_FUNCTION = SignalGeneratorFunctionsAWG.SIN

# The desired waveform length to generate.
DESIRED_WAVEFORM_LENGTH = 1000000

# Format error message for function constraints.
FORMAT_ERROR_MESSAGE_FUNCTION = (
"The desired {desired_prop_name} ({desired_val}) is not within the device's "
"{desired_prop_name} range for generating a {function_name} waveform. "
"Must be in the range of [{lower}, {upper}]."
)

# Format error message for waveform length constraints.
FORMAT_ERROR_MESSAGE_WAVEFORM_LENGTH = (
"The desired {desired_prop_name} ({desired_val}) is not within the device's "
"{desired_prop_name} range for generating a waveform with a length of {waveform_length}. "
"Must be in the range of [{lower}, {upper}]."
)


with DeviceManager(verbose=True) as dm:
# Create a connection to the scope and indicate that it is an AWG5K for type hinting.
awg5k: AWG5K = dm.add_awg("192.168.0.1")

# Get the device constraints for generating the desired function on an AWG5K.
awg5k_constraints_function = awg5k.get_waveform_constraints(function=DESIRED_FUNCTION)

# Get the frequency constraints.
frequency_range = awg5k_constraints_function.frequency_range

# Print a message if the desired frequency is not within the frequency constraints.
if not frequency_range.lower <= DESIRED_FREQUENCY <= frequency_range.upper:
print(
FORMAT_ERROR_MESSAGE_FUNCTION.format(
desired_prop_name="frequency",
desired_val=DESIRED_FREQUENCY,
function_name=DESIRED_FUNCTION.name,
lower=frequency_range.lower,
upper=frequency_range.upper,
)
)
else:
# generate a function as the signal source should be able to handle it
awg5k.generate_function(
function=awg5k.source_device_constants.functions.RAMP,
channel="SOURCE1",
frequency=DESIRED_FREQUENCY,
amplitude=0.5,
offset=0,
)

# Get the amplitude constraints.
amplitude_range = awg5k_constraints_function.amplitude_range

# Print a message if the desired amplitude is not within the amplitude constraints.
if not amplitude_range.lower <= DESIRED_AMPLITUDE <= amplitude_range.upper:
print(
FORMAT_ERROR_MESSAGE_FUNCTION.format(
desired_prop_name="amplitude",
desired_val=DESIRED_AMPLITUDE,
function_name=DESIRED_FUNCTION.name,
lower=amplitude_range.lower,
upper=amplitude_range.upper,
)
)
else:
# generate a function as the signal source should be able to handle it
awg5k.generate_function(
function=awg5k.source_device_constants.functions.RAMP,
channel="SOURCE1",
frequency=500.0e3,
amplitude=DESIRED_AMPLITUDE,
offset=0,
)

# Get the device constraints for generating the desired waveform length on an AWG5K.
awg5k_constraints_waveform_length = awg5k.get_waveform_constraints(
waveform_length=DESIRED_WAVEFORM_LENGTH
)

# Get the frequency constraints.
sample_rate_range = awg5k_constraints_waveform_length.sample_rate_range

# Print a message if the desired sample rate is not within the sample rate constraints.
if not sample_rate_range.lower <= DESIRED_SAMPLE_RATE <= sample_rate_range.upper:
print(
FORMAT_ERROR_MESSAGE_WAVEFORM_LENGTH.format(
desired_prop_name="sample rate",
desired_val=DESIRED_SAMPLE_RATE,
waveform_length=DESIRED_WAVEFORM_LENGTH,
lower=sample_rate_range.lower,
upper=sample_rate_range.upper,
)
)
4 changes: 2 additions & 2 deletions src/tm_devices/device_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
from tm_devices.drivers.pi.pi_device import PIDevice
from tm_devices.drivers.pi.power_supplies.power_supply import PowerSupplyUnit
from tm_devices.drivers.pi.scopes.scope import Scope
from tm_devices.drivers.pi.signal_sources.afgs.afg import AFG
from tm_devices.drivers.pi.signal_sources.awgs.awg import AWG
from tm_devices.drivers.pi.signal_generators.afgs.afg import AFG
from tm_devices.drivers.pi.signal_generators.awgs.awg import AWG
from tm_devices.drivers.pi.source_measure_units.source_measure_unit import SourceMeasureUnit
from tm_devices.drivers.pi.systems_switches.systems_switch import SystemsSwitch
from tm_devices.helpers import (
Expand Down