Repository: tektronix/tm_devices (language: python)

Issue:
Workflow Updates
Update GitHub Workflows to run properly.


Discussion / hints:

Fix patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index dc39f683..4f6f6939 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -79,6 +79,7 @@ repos:
         args: [--number, --end-of-line, keep, --ignore-missing-references]
         additional_dependencies:
           - setuptools  # This is required since Python 3.12 no longer installs setuptools by default in virtual environments
+          - mdformat-admon
           - mdformat-beautysh
           - mdformat-config
           - mdformat-footnote
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b185b28..5233949c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,30 @@ Valid subsections within a version are:
 
 Things to be included in the next release go here.
 
+### Added
+
+- 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 the 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 AFGs and Internal AFGs have these functions implemented.
+- Added `OutputSignalPath` enum attribute in AWGs representing output signal path options.
+- Added two functions for loading waveform set files in the AWG70k and AWG5200 drivers: 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 the AWG70k and AWG5200 drivers to define the default waveform set file.
+
+### Changed
+
+- <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 AWGs such that the `family_base_class` is at the series level.
+
 ---
 
 ## v1.5.0 (2024-06-10)
diff --git a/docs/advanced/signal_generators.md b/docs/advanced/signal_generators.md
new file mode 100644
index 00000000..8411e1e4
--- /dev/null
+++ b/docs/advanced/signal_generators.md
@@ -0,0 +1,620 @@
+# Signal Generation
+
+An overview of the signal generation functionality available in this package, along with information about Tektronix signal generators.
+
+---
+
+## Function Generation
+
+A function is a limited set of common waveforms that are provided by default through the instrument.
+The simplicity of these waveforms allows for output parameters like waveform length and sample rate to be
+abstracted away. Frequency replaces these in order to provide signals that are easy to quantify and manipulate.
+
+Arbitrary Function Generators ([AFGs](default:AFG)) utilize a phase increment process and a data lookup table to provide variable frequencies.
+The phase increment calculated is dependent on the waveform length and frequency requested. This has
+a side effect where the phase increment can be larger than one index in the lookup table. Functions bypass this
+issue by being simplistic enough that waveform length reduction does not have a detrimental effect on the end output.
+
+Arbitrary Waveform Generators ([AWGs](default:AWG)) enforce one cycle per sample, allowing the output to be the same shape regardless of clock rate.
+The number of samples that occur during a second is referred to as a sample per second (S/s), a unit that determines the frequency of the waveform.
+With low frequency functions, [AWGs](default:AWG) are functionally identical to [AFGs](default:AFG), besides offering more constrained amplitudes and offsets.
+
+---
+
+### Class Structure
+
+```mermaid
+classDiagram
+    direction LR
+
+    SignalGeneratorMixin <|-- Tekscope
+    SignalGeneratorMixin <|-- AFG
+    SignalGeneratorMixin <|-- AWG
+
+```
+
+The [`SignalGenerator`][tm_devices.drivers.pi.signal_generators.signal_generator.SignalGenerator] class is responsible
+for most waveform generators, including the [`AFG`][tm_devices.drivers.pi.signal_generators.afgs.afg.AFG] and
+[`AWG`][tm_devices.drivers.pi.signal_generators.awgs.awg.AWG].
+Similarly, [`TekScope`][tm_devices.drivers.pi.scopes.tekscope.tekscope.TekScope] is responsible for the
+[AFG](default:AFG) internal to the scopes themselves, commonly referred to as
+an [IAFG](default:IAFG). All of these classes inherit
+[`SignalGeneratorMixin`][tm_devices.driver_mixins.signal_generator_mixin.SignalGeneratorMixin],
+which includes a list of methods that share
+functionality throughout all signal generators.
+
+!!! note
+    [`SignalGeneratorMixin`][tm_devices.driver_mixins.signal_generator_mixin.SignalGeneratorMixin]
+    only contains abstract methods; defining the class by itself and calling
+    methods in it will only raise `NotImplemented` errors.
+
+```mermaid
+classDiagram
+    direction LR
+
+    SignalGeneratorMixin <|-- Tekscope
+    SignalGeneratorMixin <|-- AFG
+    SignalGeneratorMixin <|-- AWG
+
+    Tekscope "1..n" *-- InternalAFGChannel
+    AWG "1..n" *-- AWGSourceChannel
+    AWGSourceChannel <|-- AWG5200SourceChannel
+    AWGSourceChannel <|-- AWG5KSourceChannel
+    AWG5KSourceChannel <|-- AWG7KSourceChannel
+    AWGSourceChannel <|-- AWG70KSourceChannel
+    AFG "1..n" *-- AFGSourceChannel
+```
+
+Each [`SignalGenerator`][tm_devices.drivers.pi.signal_generators.signal_generator.SignalGenerator] class(
+[`AFG`][tm_devices.drivers.pi.signal_generators.afgs.afg.AFG],
+[`AWG`][tm_devices.drivers.pi.signal_generators.awgs.awg.AWG]) and
+[`TekScope`][tm_devices.drivers.pi.scopes.tekscope.tekscope.TekScope]
+(if the [AFG](default:AFG) license is installed) will contain a dictionary of source channel classes,
+which are defined on first access.
+Each of these source channel classes (
+[`AFGSourceChannel`][tm_devices.drivers.pi.signal_generators.afgs.afg.AFGSourceChannel],
+[`AWGSourceChannel`][tm_devices.drivers.pi.signal_generators.awgs.awg.AWGSourceChannel], and
+[`InternalAFGChannel`][tm_devices.drivers.pi.scopes.tekscope.tekscope.InternalAFGChannel]
+) represents an output on the signal generator
+(or the [IAFG](default:IAFG) in the case of an oscilloscope).
+
+These source channel classes contain methods and properties that pertain to [PI](default:PI) commands, which only apply changes to one output.
+For example: the `afg.source_channel["SOURCE1"].set_amplitude()` call will change the amplitude only for source output 1.
+
+!!! tip
+
+The source channel classes not only provide easy access to basic [SCPI](default:SCPI) commands but also helper functions, like `set_function_properties()`
+
+---
+
+### SignalGeneratorMixin Methods
+
+!!! warning
+    Each method performs little to no validation, as the end user can change aspects outside its purview.
+    There are several distinct instances where this can cause unwanted behavior, depending on the signal generator and what
+    state it was in previously. Attempting validation when changes can occur outside its scope leads to many redundant checks.
+    As such, it is up to the user to implement these checks.
+
+Each class has children which inherit the base abstracted methods. These methods are tailored to each signal generator, so the
+methods handle similarly, regardless of the different [PI](default:PI) commands required.
+
+- `source_device_constants` is a property that holds information about what functions
+    and memory sizes are allowed.
+
+!!! tip
+    `source_device_constants.functions` will provide an enum of possible functions to generate on the current signal generator.
+
+`generate_function()` is a method that allows the user to request a function from
+any source channel, provided an amplitude, frequency, and offset are supplied. Other key features
+include the ability to manipulate specific aspects of certain functions. Ramp waveforms can have their symmetry changed
+and duty cycle can be altered for pulse functions. The termination of the [IAFG](default:IAFG) and any [AFG](default:AFG) can be
+specified using `HIGHZ` or `FIFTY` string literals. If the output needs to be inverted,
+the polarity can be changed on [AFGs](default:AFG).
+
+!!! warning
+    `generate_function()` allows function parameters that can exceed actual generation bounds.
+    `get_waveform_constraints()` should be used in tandem with `generate_function()`, or utilizing the constraints provided in
+    [Signal Generators](#signal-generators).
+
+The `setup_burst()` method places the signal generator in a state for waveforms to be generated a set number
+of times. All parameters passed into the method are functionally identical to `generate_function()`, besides `burst_count`.
+`burst_count` specifies how many cycles of the waveform are to be generated.
+
+!!! warning
+    `setup_burst()` will set parameters that can affect the signal generator's behavior. Changing these parameters
+    manually will likely cause burst to stop functioning.
+
+`generate_burst()`  writes a trigger to the signal generator, initiating the generation of a burst of waveforms.
+
+`get_waveform_constraints()` will return a series of ranges that a waveform's parameters must
+be within to be generated. These constraints can be used before generating a function to
+make sure that the parameters you will be supplying
+are not outside the bounds. The method only requires the desired waveform function (except on [AWGs](default:AWG)) to be provided.
+However, different aspects may need to be provided to get a more accurate representation of what can actually be generated.
+If no other inputs are provided, the smallest range of the attribute is chosen.
+For an [AWG](default:AWG), the signal path affects the range of the offset and amplitude.
+Higher frequencies on [AFGs](default:AFG) will lower the upper bound of the amplitude,
+alongside which impedance is set.
+
+`set_waveform_properties()` is functionally identical to `generate_function()`, but does not turn the
+source channel off or on, nor will it stop or start an [AWG](default:AWG).
+
+## Signal Generators
+
+An overview of the different signal generators that are available in `tm_devices`.
+This includes:
+
+- Features unique to each.
+- Constraints for each waveform parameter.
+
+---
+
+### TekScope Internal Arbitrary Function Generators
+
+```mermaid
+classDiagram
+    direction LR
+
+    Tekscope <|-- MSO2
+    Tekscope <|-- MSO4
+    MSO4 <|-- MSO4B
+    Tekscope <|-- MSO5
+    MSO5 <|-- MSO5B
+    MSO5 <|-- MSO5LP
+    Tekscope <|-- MSO6
+    MSO6 <|-- MSO6B
+    MSO6 <|-- LPD6
+
+```
+
+The TekScope series instruments are signal generators focused on waveform generation and operate on the Windows operating system.
+They accept communication through [USB](default:USB) and [TCPIP](default:TCPIP) interfaces.
+
+Requesting function generation on an [IAFG](default:IAFG) will initially turn it off. The frequency, offset,
+function, impedance, and amplitude are set in the stated order.
+If the function is a `SQUARE` wave, the duty cycle is used
+to set how long the pulses are. Symmetry decides what direction the `RAMP` function
+skews. After all parameters are set, the source channel is then turned back on.
+
+Setting up bursts of an [IAFG](default:IAFG) involves setting it to burst mode and loading in a specified number of bursts.
+
+[IAFGs](default:IAFG) have access to the following functions, listed within
+[`SignalGeneratorFunctionsIAFG`][tm_devices.helpers.enums.SignalGeneratorFunctionsIAFG]:
+`SIN`, `SQUARE`, `RAMP`, `PULSE`, `PRNOISE`, `DC`, `SINC`, `GAUSSIAN`, `LORENTZ`, `ERISE`, `EDECAY`, `HAVERSINE`, `CARDIAC`, `ARBITRARY`
+
+!!! note
+    [IAFGs](default:IAFG) are only accessible if the oscilloscope has the `AFG` license installed.
+
+!!! note
+    [IAFGs](default:IAFG) contain no waveform list, editable memory, or user-defined waveforms. This means arbitrary waveforms
+    must be loaded from the hard drive.
+
+!!! note
+    Some functions, like `SINC`, `GAUSSIAN`, `LORENTZ`, `ERISE`, `EDECAY`, and `HAVERSINE` already have an inbuilt offset.
+
+!!! note
+    If the output termination matching is set to FIFTY instead of HIGHZ, then the offset and amplitude bounds will be halved.
+
+!!! caution
+    Although `ARBITRARY` is a valid function, it will not generate properly when using `generate_function()`.
+
+#### MSO2, MSO4, MSO4B, MSO5, MSO5LP, MSO6, MSO6B, LPD6
+
+##### Constraints:
+
+The amplitude and frequency range for the [IAFG](default:IAFG) vary based on the desired function.
+These ranges are the same for each of the classes listed:
+[`MSO2`][tm_devices.drivers.pi.scopes.tekscope.mso2.MSO2]
+[`MSO4`][tm_devices.drivers.pi.scopes.tekscope.mso4.MSO4]
+[`MSO4B`][tm_devices.drivers.pi.scopes.tekscope.mso4b.MSO4B]
+[`MSO5`][tm_devices.drivers.pi.scopes.tekscope.mso5.MSO5]
+[`MSO5LP`][tm_devices.drivers.pi.scopes.tekscope.mso5lp.MSO5LP]
+[`MSO6`][tm_devices.drivers.pi.scopes.tekscope.mso6.MSO6]
+[`MSO6B`][tm_devices.drivers.pi.scopes.tekscope.mso6b.MSO6B]
+[`LPD6`][tm_devices.drivers.pi.scopes.tekscope.lpd6.LPD6]
+
+Sample rates are 250.0MS/s for `ARBITRARY` waveforms.
+
+<div markdown="1" class="custom-table-title">
+
+_[IAFG](default:IAFG) Constraints_
+
+</div>
+
+|                                          | Sin                                          | Square<br/>Pulse<br/>Arbitrary               | Ramp<br/>Triangle<br/>Cardiac               | Sinc                                        | Gaussian<br/>Haversine                      | Lorentz                                     |
+| ---------------------------------------- | -------------------------------------------- | -------------------------------------------- | ------------------------------------------- | ------------------------------------------- | ------------------------------------------- | ------------------------------------------- |
+| <span style="font-size:0.7em;">Frequency | <span style="font-size:0.7em;">0.1Hz–50.0MHz | <span style="font-size:0.7em;">0.1Hz–25.0MHz | <span style="font-size:0.7em;">0.1Hz–0.5MHz | <span style="font-size:0.7em;">0.1Hz–2.0MHz | <span style="font-size:0.7em;">0.1Hz–5.0MHz | <span style="font-size:0.7em;">0.1Hz–5.0MHz |
+| <span style="font-size:0.7em;">Amplitude | <span style="font-size:0.7em;">20.0mV–5.0V   | <span style="font-size:0.7em;">20.0mV–5.0V   | <span style="font-size:0.7em;">20.0mV–5.0V  | <span style="font-size:0.7em;">20.0mV–3.0V  | <span style="font-size:0.7em;">20.0mV–5.0V  | <span style="font-size:0.7em;">20.0mV–2.4V  |
+| <span style="font-size:0.7em;">Offset    | <span style="font-size:0.7em;">-2.5V–2.5V    | <span style="font-size:0.7em;">-2.5V–2.5V    | <span style="font-size:0.7em;">-2.5V–2.5V   | <span style="font-size:0.7em;">-2.5V–2.5V   | <span style="font-size:0.7em;">-2.5V–2.5V   | <span style="font-size:0.7em;">-2.5V–2.5V   |
+
+#### MSO5B
+
+##### Constraints:
+
+The constraints for the [`MSO5B`][tm_devices.drivers.pi.scopes.tekscope.mso5b.MSO5B] are identical to
+[other tekscope models](#mso2-mso4-mso4b-mso5-mso5lp-mso6-mso6b-lpd6), except the upper frequency bound is doubled.
+
+---
+
+### Arbitrary Function Generators
+
+```mermaid
+classDiagram
+    direction LR
+
+    AFG <|-- AFG3K
+    AFG3K <|-- AFG3KB
+    AFG3K <|-- AFG3KC
+    AFG <|-- AFG31K
+
+```
+
+[AFGs](default:AFG) handle [function generation](#tekscope-internal-arbitrary-function-generators) identically to [IAFGs](default:IAFG)
+except for the order in which the parameters are set.
+
+All functions that are shared by each [AFG](default:AFG) exist within the
+[`AFG`][tm_devices.drivers.pi.signal_generators.afgs.afg.AFG] class.
+
+Setting up bursts of the [AFG](default:AFG) involves setting the trigger on the device to external, so the burst does not activate
+on the internal trigger. Following this, the burst state is set to `ON` and mode set to `TRIGGERED`.
+
+[AFGs](default:AFG) have access to the following functions, listed within
+[`SignalGeneratorFunctionsAFG`][tm_devices.helpers.enums.SignalGeneratorFunctionsAFG]:
+`SIN`, `SQUARE`, `RAMP`, `PULSE`, `DC`, `SINC`, `GAUSSIAN`, `LORENTZ`, `ERISE`, `EDECAY`, `HAVERSINE`, `CARDIAC`, `NOISE`, `ARBITRARY`
+
+!!! note
+    If the output termination matching is set to 50.0Ω instead of INFINITY, then the offset and amplitude bounds will be halved.
+
+!!! caution
+    Although `Arbitrary` is a valid function, it will not generate properly when using `generate_function`.
+
+#### AFG3K, AFG3KB, AFG3KC
+
+The AFG3K series instruments are function generating devices that also offer the capacity to generate arbitrary waveforms. They accept
+communication through [USB](default:USB), [TCPIP](default:TCPIP) and [GPIB](default:GPIB) interfaces. These instruments have their
+own class representations, corresponding to the
+[`AFG3K`][tm_devices.drivers.pi.signal_generators.afgs.afg3k.AFG3K],
+[`AFG3KB`][tm_devices.drivers.pi.signal_generators.afgs.afg3kb.AFG3KB], and
+[`AFG3KC`][tm_devices.drivers.pi.signal_generators.afgs.afg3kc.AFG3KC].
+
+##### Constraints:
+
+The amplitude, offset, and frequency range for AFG3Ks are extremely varied, dependent on model number, frequency, and function.
+However, the sample rate of the entire AFG3K series is 250.0MS/s.
+
+<div markdown="1" class="custom-table-title">
+
+_AFG3K Constraints_
+
+</div>
+
+|                                          | Sin                                              | Square                                             | Pulse                                              | Ramp<br/>Sinc<br/>Gaussian<br/>Lorentz<br/>ERise<br/>EDecay<br/>Haversine | Arbitrary                                     |
+| ---------------------------------------- | ------------------------------------------------ | -------------------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------- |
+| **3011/C:**                              |                                                  |                                                    |                                                    |                                                                           |                                               |
+| <span style="font-size:0.7em;">Frequency | <span style="font-size:0.7em;">1.0µHz–10.0MHz    | <span style="font-size:0.7em;">1.0µHz–5.0MHz       | <span style="font-size:0.7em;">1.0mHz–5.0MHz       | <span style="font-size:0.7em;">1.0µHz–0.1MHz                              | <span style="font-size:0.7em;">1.0mHz–5.0MHz  |
+| <span style="font-size:0.7em;">Amplitude | <span style="font-size:0.7em;">40.0mV–40.0V      | <span style="font-size:0.7em;">40.0mV–40.0V        | <span style="font-size:0.7em;">40.0mV–40.0V        | <span style="font-size:0.7em;">40.0mV–40.0V                               | <span style="font-size:0.7em;">40.0mV–40.0V   |
+| <span style="font-size:0.7em;">Offset    | <span style="font-size:0.7em;">-20.0V–20.0V      | <span style="font-size:0.7em;">-20.0V–20.0V        | <span style="font-size:0.7em;">-20.0V–20.0V        | <span style="font-size:0.7em;">-20.0V–20.0V                               | <span style="font-size:0.7em;">-20.0V–20.0V   |
+| **302xB/C:**                             |                                                  |                                                    |                                                    |                                                                           |                                               |
+| <span style="font-size:0.7em;">Frequency | <span style="font-size:0.7em;">1.0µHz–25.0MHz    | <span style="font-size:0.7em;">1.0µHz–25.0MHz[^TA] | <span style="font-size:0.7em;">1.0mHz–25.0MHz[^TA] | <span style="font-size:0.7em;">1.0µHz–0.5MHz[^TA]                         | <span style="font-size:0.7em;">1.0mHz–12.5MHz |
+| <span style="font-size:0.7em;">Amplitude | <span style="font-size:0.7em;">20.0mV–20V        | <span style="font-size:0.7em;">20.0mV–20.0V        | <span style="font-size:0.7em;">20.0mV–20.0V        | <span style="font-size:0.7em;">20.0mV–20.0V                               | <span style="font-size:0.7em;">20.0mV–20.0V   |
+| <span style="font-size:0.7em;">Offset    | <span style="font-size:0.7em;">-10.0V–10.0V      | <span style="font-size:0.7em;">-10.0V–10.0V        | <span style="font-size:0.7em;">-10.0V–10.0V        | <span style="font-size:0.7em;">-10.0V–10.0V                               | <span style="font-size:0.7em;">-10.0V–10.0V   |
+| **305xC:**                               |                                                  |                                                    |                                                    |                                                                           |                                               |
+| <span style="font-size:0.7em;">Frequency | <span style="font-size:0.7em;">1.0µHz–50MHz      | <span style="font-size:0.7em;">1.0µHz–40.0MHz      | <span style="font-size:0.7em;">1.0mHz–40.0MHz      | <span style="font-size:0.7em;">1.0µHz–0.8MHz                              | <span style="font-size:0.7em;">1.0mHz–25.0MHz |
+| <span style="font-size:0.7em;">Amplitude | <span style="font-size:0.7em;">20.0mV–20.0V      | <span style="font-size:0.7em;">20.0mV–20.0V        | <span style="font-size:0.7em;">20.0mV–20.0V        | <span style="font-size:0.7em;">20.0mV–20.0V                               | <span style="font-size:0.7em;">20.0mV–20.0V   |
+| <span style="font-size:0.7em;">Offset    | <span style="font-size:0.7em;">-10.0V–10.0V      | <span style="font-size:0.7em;">-10.0V–10.0V        | <span style="font-size:0.7em;">-10.0V–10.0V        | <span style="font-size:0.7em;">-10.0V–10.0V                               | <span style="font-size:0.7em;">-10.0V–10.0V   |
+| **310x/C:**                              |                                                  |                                                    |                                                    |                                                                           |                                               |
+| <span style="font-size:0.7em;">Frequency | <span style="font-size:0.7em;">1.0µHz–0.1GHz     | <span style="font-size:0.7em;">1.0µHz–50.0MHz      | <span style="font-size:0.7em;">1.0mHz–50.0MHz      | <span style="font-size:0.7em;">1.0µHz–1.0MHz                              | <span style="font-size:0.7em;">1.0mHz–50.0MHz |
+| <span style="font-size:0.7em;">Amplitude | <span style="font-size:0.7em;">40.0mV–20.0V      | <span style="font-size:0.7em;">40.0mV–20.0V        | <span style="font-size:0.7em;">40.0mV–20.0V        | <span style="font-size:0.7em;">4.0mV–20.0V                                | <span style="font-size:0.7em;">40.0mV–20.0V   |
+| <span style="font-size:0.7em;">Offset    | <span style="font-size:0.7em;">-10.0V–10.0V      | <span style="font-size:0.7em;">-10.0V–10.0V        | <span style="font-size:0.7em;">-10.0V–10.0V        | <span style="font-size:0.7em;">-10.0V–10.0V                               | <span style="font-size:0.7em;">-10.0V–10.0V   |
+| **315xC:**                               |                                                  |                                                    |                                                    |                                                                           |                                               |
+| <span style="font-size:0.7em;">Frequency | <span style="font-size:0.7em;">1.0µHz–0.15GHz    | <span style="font-size:0.7em;">1.0µHz–0.1GHz       | <span style="font-size:0.7em;">1.0mHz–0.1GHz       | <span style="font-size:0.7em;">1.0µHz–1.5MHz                              | <span style="font-size:0.7em;">1.0mHz–0.1GHz  |
+| <span style="font-size:0.7em;">Amplitude | <span style="font-size:0.7em;">40.0mV–20.0V[^TB] | <span style="font-size:0.7em;">40.0mV–20.0V        | <span style="font-size:0.7em;">40.0mV–20.0V        | <span style="font-size:0.7em;">40.0mV–20.0V                               | <span style="font-size:0.7em;">40.0mV–20.0V   |
+| <span style="font-size:0.7em;">Offset    | <span style="font-size:0.7em;">-10.0V–10.0V      | <span style="font-size:0.7em;">-10.0V–10.0V        | <span style="font-size:0.7em;">-10.0V–10.0V        | <span style="font-size:0.7em;">-10.0V–10.0V                               | <span style="font-size:0.7em;">-10.0V–10.0V   |
+| **325x/C:**                              |                                                  |                                                    |                                                    |                                                                           |                                               |
+| <span style="font-size:0.7em;">Frequency | <span style="font-size:0.7em;">1.0µHz–0.24GHz    | <span style="font-size:0.7em;">1.0µHz–0.12GHz      | <span style="font-size:0.7em;">1.0mHz–0.12GHz      | <span style="font-size:0.7em;">1.0µHz–2.4MHz                              | <span style="font-size:0.7em;">1.0mHz–0.12GHz |
+| <span style="font-size:0.7em;">Amplitude | <span style="font-size:0.7em;">0.1V–10.0V[^TC]   | <span style="font-size:0.7em;">0.1V–10.0V          | <span style="font-size:0.7em;">0.1V–10.0V          | <span style="font-size:0.7em;">0.1V–10.0V                                 | <span style="font-size:0.7em;">0.1V–10.0V     |
+| <span style="font-size:0.7em;">Offset    | <span style="font-size:0.7em;">-5.0V–5.0V        | <span style="font-size:0.7em;">-5.0V–5.0V          | <span style="font-size:0.7em;"> -5.0V–5.0V         | <span style="font-size:0.7em;">-5.0V–5.0V                                 | <span style="font-size:0.7em;">-5.0V–5.0V     |
+
+#### AFG31K
+
+The AFG31K series instruments are function generating devices that also offer the capacity to generate arbitrary waveforms. They accept
+communication through [USB](default:USB), [TCPIP](default:TCPIP), and [GPIB](default:GPIB) interfaces. The AFG31K has its
+own class representation, corresponding to
+[`AFG31K`][tm_devices.drivers.pi.signal_generators.afgs.afg31k.AFG31K].
+
+##### Constraints:
+
+<div markdown="1" class="custom-table-title">
+
+_AFG31K Constraints_
+
+</div>
+
+|                                            | Sin                                              | Square<br/>Pulse                                 | Pulse                                         | Ramp<br/>Sinc<br/>Gaussian<br/>Lorentz<br/>ERise<br/>EDecay<br/>Haversine | Arbitrary                                     |
+| ------------------------------------------ | ------------------------------------------------ | ------------------------------------------------ | --------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------- |
+| **3102x:**                                 |                                                  |                                                  |                                               |                                                                           |                                               |
+| <span style="font-size:0.7em;">Frequency   | <span style="font-size:0.7em;">1.0µHz–25.0MHz    | <span style="font-size:0.7em;">1.0µHz–20.0MHz    | <span style="font-size:0.7em;">1.0m–25.0MHz   | <span style="font-size:0.7em;">1.0µHz–0.5MHz                              | <span style="font-size:0.7em;">1.0mHz–12.5MHz |
+| <span style="font-size:0.7em;">Amplitude   | <span style="font-size:0.7em;">2.0mV–20.0V       | <span style="font-size:0.7em;">2.0mV–20.0V       | <span style="font-size:0.7em;">2.0mV–20.0V    | <span style="font-size:0.7em;">2.0mV–20.0V                                | <span style="font-size:0.7em;">2.0mV–20.0V    |
+| <span style="font-size:0.7em;">Offset      | <span style="font-size:0.7em;">-10.0V–10.0V      | <span style="font-size:0.7em;">-10.0V–10.0V      | <span style="font-size:0.7em;">-10.0V–10.0V   | <span style="font-size:0.7em;">-10.0V–10.0V                               | <span style="font-size:0.7em;">-10.0V–10.0V   |
+| <span style="font-size:0.7em;">Sample Rate |                                                  |                                                  |                                               |                                                                           | <span style="font-size:0.7em;">250.0MS/s      |
+| **3105x:**                                 |                                                  |                                                  |                                               |                                                                           |                                               |
+| <span style="font-size:0.7em;">Frequency   | <span style="font-size:0.7em;">1.0µHz–50.0MHz    | <span style="font-size:0.7em;">1.0µHz–40.0MHz    | <span style="font-size:0.7em;">1.0mHz–40.0MHz | <span style="font-size:0.7em;">1.0µHz–0.8MHz                              | <span style="font-size:0.7em;">1.0mHz–25.0MHz |
+| <span style="font-size:0.7em;">Amplitude   | <span style="font-size:0.7em;">2.0mV–20.0V       | <span style="font-size:0.7em;">2.0mV–20.0V       | <span style="font-size:0.7em;">2.0mV–20.0V    | <span style="font-size:0.7em;">2.0Vm–20.0V                                | <span style="font-size:0.7em;">2.0mV–20.0V    |
+| <span style="font-size:0.7em;">Offset      | <span style="font-size:0.7em;">-10.0V–10.0V      | <span style="font-size:0.7em;">-10.0V–10.0V      | <span style="font-size:0.7em;">-10.0V–10.0V   | <span style="font-size:0.7em;">-10.0V–10.0V                               | <span style="font-size:0.7em;">-10.0V–10.0V   |
+| <span style="font-size:0.7em;">Sample Rate |                                                  |                                                  |                                               |                                                                           | <span style="font-size:0.7em;">1.0GS/s[^TOA]  |
+| **3110x:**                                 |                                                  |                                                  |                                               |                                                                           |                                               |
+| <span style="font-size:0.7em;">Frequency   | <span style="font-size:0.7em;">1.0µHz–0.1GHz     | <span style="font-size:0.7em;">1.0µHz–80.0MHz    | <span style="font-size:0.7em;">1.0mHz–50.0MHz | <span style="font-size:0.7em;">1.0µHz–1.0MHz                              | <span style="font-size:0.7em;">1.0mHz–50.0MHz |
+| <span style="font-size:0.7em;">Amplitude   | <span style="font-size:0.7em;">2.0mV–20.0V[^TOB] | <span style="font-size:0.7em;">2.0mV–20.0V[^TOB] | <span style="font-size:0.7em;">2.0mV–20.0V    | <span style="font-size:0.7em;">2.0Vm–20.0V                                | <span style="font-size:0.7em;">2.0mV–20.0V    |
+| <span style="font-size:0.7em;">Offset      | <span style="font-size:0.7em;">-10.0V–10.0V      | <span style="font-size:0.7em;">-10.0V–10.0V      | <span style="font-size:0.7em;">-10.0V–10.0V   | <span style="font-size:0.7em;">-10.0V–10.0V                               | <span style="font-size:0.7em;">-10.0V–10.0V   |
+| <span style="font-size:0.7em;">Sample Rate |                                                  |                                                  |                                               |                                                                           | <span style="font-size:0.7em;">1.0GS/s[^TOA]  |
+| **3115x:**                                 |                                                  |                                                  |                                               |                                                                           |                                               |
+| <span style="font-size:0.7em;">Frequency   | <span style="font-size:0.7em;">1.0µHz–0.15GHz    | <span style="font-size:0.7em;">1.0µHz–0.12G      | <span style="font-size:0.7em;">1mHz–0.1GHz    | <span style="font-size:0.7em;">1.0µHz–1.5MHz                              | <span style="font-size:0.7em;">1.0mHz–75.0MHz |
+| <span style="font-size:0.7em;">Amplitude   | <span style="font-size:0.7em;">2.0mV–10.0V       | <span style="font-size:0.7em;">2.0mV–10.0V       | <span style="font-size:0.7em;">2mV–10V        | <span style="font-size:0.7em;">2.0Vm–10.0V                                | <span style="font-size:0.7em;">2.0mV–10.0V    |
+| <span style="font-size:0.7em;">Offset      | <span style="font-size:0.7em;">-5.0V–5.0V        | <span style="font-size:0.7em;">-5.0V–5.0V        | <span style="font-size:0.7em;">-5V–5V         | <span style="font-size:0.7em;">-5.0V–5.0V                                 | <span style="font-size:0.7em;">-5.0V–5.0V     |
+| <span style="font-size:0.7em;">Sample Rate |                                                  |                                                  |                                               |                                                                           | <span style="font-size:0.7em;">2.0GS/s[^TOA]  |
+| **3125x:**                                 |                                                  |                                                  |                                               |                                                                           |                                               |
+| <span style="font-size:0.7em;">Frequency   | <span style="font-size:0.7em;">1.0µHz–0.25GHz    | <span style="font-size:0.7em;">1.0µHz–0.16GHz    | <span style="font-size:0.7em;">1mHz–0.12GHz   | <span style="font-size:0.7em;">1.0µHz–2.5MHz                              | <span style="font-size:0.7em;">1.0m–0.125GHz  |
+| <span style="font-size:0.7em;">Amplitude   | <span style="font-size:0.7em;">2.0mV–10.0V[^TOC] | <span style="font-size:0.7em;">2.0mV–10.0V       | <span style="font-size:0.7em;">2mV–10V        | <span style="font-size:0.7em;">2.0Vm–10.0V                                | <span style="font-size:0.7em;">2.0mV–10.0V    |
+| <span style="font-size:0.7em;">Offset      | <span style="font-size:0.7em;">-5.0V–5.0V        | <span style="font-size:0.7em;">-5.0V–5.0V        | <span style="font-size:0.7em;">-5V–5V         | <span style="font-size:0.7em;">-5.0V–5.0V                                 | <span style="font-size:0.7em;">-5.0V–5.0V     |
+| <span style="font-size:0.7em;">Sample Rate |                                                  |                                                  |                                               |                                                                           | <span style="font-size:0.7em;">2.0GS/s[^TOA]  |
+
+---
+
+### Arbitrary Waveform Generators
+
+```mermaid
+classDiagram
+    direction LR
+
+    AWG <|-- AWG5K
+    AWG5K <|-- AWG5KB
+    AWG5KB <|-- AWG5KC
+    AWG <|-- AWG7K
+    AWG7K <|-- AWG7KB
+    AWG7KB <|-- AWG7KC
+    AWG <|-- AWG5200
+    AWG <|-- AWG70KA
+    AWG70KA <|-- AWG70KB
+```
+
+All functions that are shared by each [AWG](default:AWG) exist within the
+[`AWG`][tm_devices.drivers.pi.signal_generators.awgs.awg.AWG] class.
+
+Function generation on [AWGs](default:AWG) is fundamentally different from [AFGs](default:AFG). The [AWG](default:AWG) is stopped and the source channel being used
+is turned off. Predefined waveforms provided with the [AWG](default:AWG)
+are then loaded from the hard drive into the waveform list for the AWG5200 and AWG70K. The sample rate is not source dependent,
+instead, it is set through the `SignalGenerator` class. The source channel provided has its waveform, offset, amplitude, and signal path set.
+These attributes can take a while to be set, though once complete, the source channels are turned back on and `AWGCONTROL:RUN`
+is sent to begin the transmission of the waveform.
+
+!!! note
+    If the waveform is `RAMP`, a symmetry of 50 will set the waveform to a `TRIANGLE`.
+
+The [`AWG`][tm_devices.drivers.pi.signal_generators.awgs.awg.AWG] class has some unique methods.
+`generate_waveform()` allows for a waveform name from the waveform list
+to be provided, instead of a function. The method is also distinctly different from the generate function as it relies on a sample
+rate being provided to generate the waveform. All functions that are generic to the [AWG](default:AWG)
+exist within the [`AWG`][tm_devices.drivers.pi.signal_generators.awgs.awg.AWG] class.
+
+[AWGs](default:AWG) have access to the following functions, listed within
+[`SignalGeneratorFunctionsAWG`][tm_devices.helpers.enums.SignalGeneratorFunctionsAWG]:
+`SIN`, `SQUARE`, `RAMP`, `TRIANGLE`, `DC`, `CLOCK`
+
+#### AWG5K/AWG7K
+
+The AWG5K/7K series instruments are signal generators focused on waveform generation and operate on the Windows operating system.
+They accept communication through [TCPIP](default:TCPIP) and [GPIB](default:GPIB) interfaces.
+
+`set_output_signal_path()` is uniquely defined within the AWG5K and AWG7K classes, as it will set the value for
+`AWGCONTROL:DOUTPUTx:STATE`, which is a unique option not seen in the other [AWGs](default:AWG).
+
+`set_offset()` is conditioned to make sure that the [AWG](default:AWG) output signal path is not DIR, as the [VISA](default:VISA) query will time
+out otherwise.
+
+!!! note
+    Operation complete commands will always return 1 on the AWG5K/7K series.
+
+!!! caution
+    All waveforms must be the same length when sending the AWGCONTROL:RUN command.
+
+##### AWG5K, AWG5KB, AWG5KC
+
+The AWG5K series instruments have their
+own class representations, corresponding to the
+[`AWG5K`][tm_devices.drivers.pi.signal_generators.awgs.awg5k.AWG5K],
+[`AWG5KB`][tm_devices.drivers.pi.signal_generators.awgs.awg5kb.AWG5KB], and
+[`AWG5KC`][tm_devices.drivers.pi.signal_generators.awgs.awg5kc.AWG5KC].
+
+###### Constraints:
+
+The AWG5K series offers an upper sample rate range from 600.0MS/s to 1.2GS/s depending on the model number.
+Sending `AWGControl:DOUTput[n] 1` or using `DIR` in `set_output_signal_path()` will reduce the maximum amplitude
+to 0.6V. This occurs by bypassing the internal amplifier, which reroutes the [DAC](default:DAC) directly to the
+differential output.
+
+<div markdown="1" class="custom-table-title">
+
+_AWG5K Constraints_
+
+</div>
+
+|                                            | 500x/B/C                                          | 501x/B/C                                        | 5xxx/B/C DIR                                 |
+| ------------------------------------------ | ------------------------------------------------- | ----------------------------------------------- | -------------------------------------------- |
+| <span style="font-size:0.7em;">Sample Rate | <span style="font-size:0.7em;">10.0MS/s–600.0MS/s | <span style="font-size:0.7em;">10.0MS/s–1.2MS/s | \*                                           |
+| <span style="font-size:0.7em;">Amplitude   | <span style="font-size:0.7em;">20.0mV–2.25V       | <span style="font-size:0.7em;">20.0mV–2.25V     | <span style="font-size:0.7em;">20.0mV-0.6.0V |
+| <span style="font-size:0.7em;">Offset      | <span style="font-size:0.7em;">-2.25V–2.25V       | <span style="font-size:0.7em;">-2.25V–2.25V     | <span style="font-size:0.7em;">N/A           |
+
+!!! note
+    AWG5K's have digitized outputs on the rear of the device.
+
+##### AWG7K, AWG7KB, AWG7KC
+
+The AWG7K series instruments have their
+own class representations, corresponding to the
+[`AWG7K`][tm_devices.drivers.pi.signal_generators.awgs.awg7k.AWG7K],
+[`AWG7KB`][tm_devices.drivers.pi.signal_generators.awgs.awg7kb.AWG7KB], and
+[`AWG7KC`][tm_devices.drivers.pi.signal_generators.awgs.awg7kc.AWG7KC].
+
+###### Constraints:
+
+The AWG7K series instruments function identically to the AWG5K series,
+excluding the higher sample rate, lower amplitude, and offset range.
+The model number conveys information about the unit, with the second and third numbers indicating
+the maximum sample rate in gigahertz allowed on the unit.
+
+<div markdown="1" class="custom-table-title">
+
+_AWG7K Constraints_
+
+</div>
+|                                            | 705x                                            | 710x                                             | 706xB                                           | 712xB/C                                          | 708xC                                           |
+|--------------------------------------------|-------------------------------------------------|--------------------------------------------------|-------------------------------------------------|--------------------------------------------------|-------------------------------------------------|
+| <span style="font-size:0.7em;">Sample Rate | <span style="font-size:0.7em;">10.0MS/s–5.0GS/s | <span style="font-size:0.7em;">10.0MS/s–10.0GS/s | <span style="font-size:0.7em;">10.0MS/s–6.0GS/s | <span style="font-size:0.7em;">10.0MS/s–12.0GS/s | <span style="font-size:0.7em;">10.0MS/s–8.0GS/s |
+| <span style="font-size:0.7em;">Amplitude   | <span style="font-size:0.7em;">50.0mV–2.0V      | <span style="font-size:0.7em;">50.0mV–2.0V       | <span style="font-size:0.7em;">50.0mV–2.0V      | <span style="font-size:0.7em;">50.0mV–2.0V       | <span style="font-size:0.7em;">50.0mV–2.0V      |
+| <span style="font-size:0.7em;">Offset      | <span style="font-size:0.7em;">-0.5V–0.5V       | <span style="font-size:0.7em;">-0.5V–0.5V        | <span style="font-size:0.7em;">-0.5V–0.5V       | <span style="font-size:0.7em;">-0.5V–0.5V        | <span style="font-size:0.7em;">-0.5V–0.5V       |
+
+The AWG7K also includes varying options that directly affect these ranges, such as option 02 and option 06.
+These options will enforce the output signal path to always go directly from the [DAC](default:DAC) to the differential output.
+
+<div markdown="1" class="custom-table-title">
+
+_AWG7K Option Constraints_
+
+</div>
+
+|                                            | 7102 OPT 06                                           | 7122B/C OPT 06                                        | 7xxx/B/C OPT 02                          | 7xxx/B/C DIR                               |
+| ------------------------------------------ | ----------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------- | ------------------------------------------ |
+| <span style="font-size:0.7em;">Sample Rate | <span style="font-size:0.7em;">10.0MS/s–20.0GS/s[^SA] | <span style="font-size:0.7em;">10.0MS/s–24.0GS/s[^SA] | \*                                       | \*                                         |
+| <span style="font-size:0.7em;">Amplitude   | <span style="font-size:0.7em;">0.5V–1.0V              | <span style="font-size:0.7em;">0.5V–1.0V              | <span style="font-size:0.7em;">0.5V–1.0V | <span style="font-size:0.7em;">50.0mV-1.0V |
+| <span style="font-size:0.7em;">Offset      | <span style="font-size:0.7em;">N/A                    | <span style="font-size:0.7em;">N/A                    | <span style="font-size:0.7em;">N/A       | <span style="font-size:0.7em;">N/A         |
+
+#### AWG5200
+
+The AWG5200 series instruments are signal generators focused on waveform generation which operate on Windows.
+They accept communication through [USB](default:USB), [TCPIP](default:TCPIP), and [GPIB](default:GPIB) interfaces.
+
+The AWG52000 has its
+own class representation, corresponding to
+[`AWG5200`][tm_devices.drivers.pi.signal_generators.awgs.awg5200.AWG5200].
+
+`set_output_signal_path()` is uniquely defined within the AWG5200 as it has special output signal paths.
+
+`load_waveform()` inherently has an operation complete check, as attempting to run overlapping commands while loading a waveform can lead to
+unintended behavior.
+
+##### Constraints:
+
+The AWG5200 does not have a sample rate range dependent on the model number. Instead, it refers to
+which option is installed to provide the range of the sample rate. Option 25 on the devices provides
+a maximum rate of 2.5GS/s, whereas option 50 allows a maximum rate of 5.0GS/s.
+If option DC is provided, the DCHB signal output path amplitude range will be increased to have a maximum
+of 1.5V. The DCHV signal path increases this range further to 5.0V by including another amplifier.
+
+<div markdown="1" class="custom-table-title">
+
+_AWG5200 Constraints_
+
+</div>
+
+|                                            | OPT 25                                           | OPT 50                                           | OPT DC DCHB                                | OPT HV DCHV                                |
+| ------------------------------------------ | ------------------------------------------------ | ------------------------------------------------ | ------------------------------------------ | ------------------------------------------ |
+| <span style="font-size:0.7em;">Sample Rate | <span style="font-size:0.7em;">0.298kS/s–2.5GS/s | <span style="font-size:0.7em;">0.298kS/s–5.0GS/s | \*                                         | \*                                         |
+| <span style="font-size:0.7em;">Amplitude   | <span style="font-size:0.7em;">25.0mV–0.75V      | <span style="font-size:0.7em;">25.0mV–0.75V      | <span style="font-size:0.7em;">25.0mV–1.5V | <span style="font-size:0.7em;">10.0mV-5.0V |
+| <span style="font-size:0.7em;">Offset      | <span style="font-size:0.7em;">-2.0V–2.0V        | <span style="font-size:0.7em;">-2.0V–2.0V        | <span style="font-size:0.7em;">-2.0V–2.0V  | <span style="font-size:0.7em;">-2.0V-2.0V  |
+
+##### Sequential, Blocking and Overlapping Commands:
+
+The AWG5200's programming commands are separated into three separated categories: Sequential, Blocking, and Overlapping.
+The type of command is important to consider, as using them in an incorrect order can lead to unintended results.
+
+Sequential commands function as standard [PI](default:PI) commands. They will not start until the previous command has finished. These commands
+tend to be fast and will allow for quick response times even if they are queued in the input buffer.
+
+Blocking commands are very similar to Sequential commands. The main difference between Sequential and Blocking is that
+Blocking commands often take longer to execute.
+
+!!! caution
+    Due to the length of Blocking commands, a query may time out when sent if performed immediately after a large series of consecutive Blocking commands.
+
+Some commands can perform data analysis on another thread; these are referred to as Overlapping commands. They allow any command to be started
+while they are being executed. They cannot begin if the previous command was blocking or sequential, or if the operation complete status register is
+not set.
+
+!!! tip
+    Overlapping commands run in parallel with any other command, so placing them first in a sequence is always preferable.
+
+!!! tip
+    There are multiple ways of synchronizing overlapping commands. This includes using [OPC](default:OPC) or [WAI](default:WAI) to
+    wait for the operation complete to clear in the [SESR](default:SESR). This can also be done using an [SRQ](default:SRQ),
+    along with waiting for the trigger bit in the [OCR](default:OCR).
+
+!!! caution
+    The operation complete register will only wait for the first overlapping command to finish before clearing. This means
+    that if multiple overlapping commands are run, then subsequent overlapping commands being finished will be ignored.
+
+!!! danger
+    Overlapping commands can cause unintended behavior when performed alongside critical hardware functionality.
+    If the AWG5200 is experiencing problems, this may be a cause.
+
+#### AWG70KA, AWG70KB
+
+The AWG70K series instruments are signal generators focused on waveform generation.
+These instruments operate on the Windows operating system, and they accept communication through
+[USB](default:USB), [TCPIP](default:TCPIP), and [GPIB](default:GPIB) interfaces.
+
+`set_output_signal_path()` is uniquely defined within the
+[`AWG70KA`][tm_devices.drivers.pi.signal_generators.awgs.awg70ka.AWG70KA] and
+[`AWG70KB`][tm_devices.drivers.pi.signal_generators.awgs.awg70kb.AWG70KB] classes.
+By default, it will first attempt to set the output signal path to [DCA](default:DCA).
+If this fails (implying an MDC4500-4B is not connected), then a direct signal path will be set.
+
+`set_offset()` is conditioned to make sure that the [AWG](default:AWG) output signal path has a [DCA](default:DCA), as the [VISA](default:VISA) query will time
+out otherwise.
+
+##### Constraints:
+
+The AWG70K signal generator is a special case where only the direct signal output path is allowed
+(unless option AC is installed). This means the amplitude is limited,
+and offset is not allowed to be set by default. However, there is a secondary device that allows for DC amplification, the MDC4500-4B.
+The MDC4500-4B provides the ability to utilize DC offset and large range of amplitude on an AWG70K.
+
+Just like the AWG5200, the frequency is dependent on the option installed (150, 225, 216, 208).
+The first number in the option provides the number of source channels the AWG70K has; the next two numbers
+indicate the sample rate in gigahertz.
+
+!!! tip
+    Though the AWG70K has no offset by default, one can be simulated by changing the raw data in the waveform.
+    As long as all points are within the amplitude bounds, this can be achieved using `WLIST:WAVEFORM:AOFFSET`.
+
+<div markdown="1" class="custom-table-title">
+
+_AWG70K Constraints_
+
+</div>
+
+|                                            | OPT 150                                        | OPT 225                                          | OPT 216                                          | OPT 208                                         | MDC4500 DCA                                    |
+| ------------------------------------------ | ---------------------------------------------- | ------------------------------------------------ | ------------------------------------------------ | ----------------------------------------------- | ---------------------------------------------- |
+| <span style="font-size:0.7em;">Sample Rate | <span style="font-size:0.7em;">1.49kS/s–50GS/s | <span style="font-size:0.7em;">1.49kS/s–25.0GS/s | <span style="font-size:0.7em;">1.49kS/s–16.0GS/s | <span style="font-size:0.7em;">1.49kS/s–8.0GS/s | \*                                             |
+| <span style="font-size:0.7em;">Amplitude   | <span style="font-size:0.7em;">0.125V–0.5V     | <span style="font-size:0.7em;">0.125V–0.5V       | <span style="font-size:0.7em;">0.125V–0.5V       | <span style="font-size:0.7em;">0.125V–0.5V      | <span style="font-size:0.7em;">31mV–1.2V[^SZA] |
+| <span style="font-size:0.7em;">Offset      | <span style="font-size:0.7em;">N/A             | <span style="font-size:0.7em;">N/A               | <span style="font-size:0.7em;">N/A               | <span style="font-size:0.7em;">N/A              | <span style="font-size:0.7em;">-0.4V–0.8V      |
+
+##### Sequential, Blocking and Overlapping Commands:
+
+The AWG70K also supports the [Sequential, Blocking, and Overlapping commands](#awg5200) mentioned in
+the AWG5200 section.
+
+[^TA]: AFG302xB has its upper bound for frequency halved for these functions.
+
+[^TB]: The amplitude upper bound is reduced to 16.0V when the frequency is greater than 100.0MHz.
+
+[^TC]: The amplitude upper bound is reduced to 8.0V when the frequency is greater than 200.0MHz.
+
+[^TOA]: When waveform length is greater than 16Kb, otherwise, the sample rate is 250.0MS/s.
+
+[^TOB]: The amplitude upper bound is reduced to 16.0V when the frequency is greater than 60.0MHz. It is further reduced to 12.0V when the frequency is greater than 80.0MHz
+
+[^TOC]: The amplitude upper bound is reduced to 8.0V when the frequency is greater than 200.0MHz.
+
+[^SA]: Sample rates higher than 10GS/S(12GS/s for B/C) can only be done through Interleave.
+
+[^SZA]: Although the MDC4500-4B allows for greater than 1.0V amplitude, there is a drop in accuracy.
diff --git a/docs/glossary.md b/docs/glossary.md
index d301bfa6..6efa5dc5 100644
--- a/docs/glossary.md
+++ b/docs/glossary.md
@@ -20,15 +20,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.
+
 IDE
 : Integrated Development Environment
 
@@ -47,6 +59,12 @@ MSO
 MT
 : Margin Tester
 
+OCR
+: Operation Condition Register
+
+OPC
+: Operation Complete
+
 PI
 : Programmable Interface
 
@@ -56,11 +74,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
diff --git a/docs/known_words.txt b/docs/known_words.txt
index ab02848d..b8d67578 100644
--- a/docs/known_words.txt
+++ b/docs/known_words.txt
@@ -6,6 +6,7 @@ api
 artifact
 artifacts
 autocomplete
+awgs
 backend
 backends
 basic_curve_query
@@ -32,9 +33,11 @@ en
 enum
 executables
 filepath
+generate_function
 get_acquisition_data
 gh
 github
+haversine
 hostname
 hostnames
 html
@@ -57,6 +60,7 @@ namespace
 noqa
 opensource
 parallelization
+pi
 pre
 psu
 py
@@ -73,7 +77,9 @@ repo
 runtime
 sarif
 sexualized
+signal_generators
 sim_devices
+sinc
 socio
 stdout
 styleguide
diff --git a/examples/scopes/tekscope/generate_internal_afg_signal.py b/examples/scopes/tekscope/generate_internal_afg_signal.py
index b656a20f..e66eca9b 100644
--- a/examples/scopes/tekscope/generate_internal_afg_signal.py
+++ b/examples/scopes/tekscope/generate_internal_afg_signal.py
@@ -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
@@ -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
diff --git a/examples/signal_generators/generate_function.py b/examples/signal_generators/generate_function.py
new file mode 100644
index 00000000..392eff43
--- /dev/null
+++ b/examples/signal_generators/generate_function.py
@@ -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,
+    )
diff --git a/examples/signal_generators/set_source_channel_properties.py b/examples/signal_generators/set_source_channel_properties.py
new file mode 100644
index 00000000..3a5dce83
--- /dev/null
+++ b/examples/signal_generators/set_source_channel_properties.py
@@ -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)
diff --git a/examples/signal_generators/waveform_constraints.py b/examples/signal_generators/waveform_constraints.py
new file mode 100644
index 00000000..1e144a3f
--- /dev/null
+++ b/examples/signal_generators/waveform_constraints.py
@@ -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,
+            )
+        )
diff --git a/mkdocs.yml b/mkdocs.yml
index ba665a99..29a80d65 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -51,6 +51,7 @@ markdown_extensions:
   - abbr
   - smarty
   - admonition
+  - footnotes
   - md_in_html
   - sane_lists
   - attr_list
@@ -72,7 +73,6 @@ plugins:
   - search:
       prebuild_index: true
   - include-markdown
-  - mermaid2
   - section-index
   - autorefs
   - macros:
@@ -90,6 +90,7 @@ plugins:
       use_default: true
       inline_refs: none
       markdown_links: true
+  - mermaid2
   - mkdocstrings:  # additional customization takes place in docs/generate_api_pages.py
       # noinspection YAMLIncompatibleTypes
       enabled: !ENV [TM_DEVICES_API_GENERATION, true]
@@ -165,6 +166,7 @@ nav:  # The first entry in each section needs a hard-coded title to avoid being
   - Advanced Topics:
       - Architectural Overview: advanced/architecture.md
       - advanced/advanced_usage.md
+      - advanced/signal_generators.md
   - Troubleshooting:
       - Troubleshooting Usage: troubleshooting/usage.md
       - Troubleshooting Contributions: troubleshooting/contributions.md
diff --git a/src/tm_devices/device_manager.py b/src/tm_devices/device_manager.py
index c415439d..a4192ac3 100644
--- a/src/tm_devices/device_manager.py
+++ b/src/tm_devices/device_manager.py
@@ -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 (
diff --git a/src/tm_devices/driver_mixins/signal_generator_mixin.py b/src/tm_devices/driver_mixins/signal_generator_mixin.py
index 68604ef7..88385891 100644
--- a/src/tm_devices/driver_mixins/signal_generator_mixin.py
+++ b/src/tm_devices/driver_mixins/signal_generator_mixin.py
@@ -2,15 +2,37 @@
 
 from abc import ABC, abstractmethod
 from dataclasses import dataclass
-from typing import Literal, Type, TypeVar
+from typing import Literal, NamedTuple, Optional, Type, TypeVar
 
 from tm_devices.driver_mixins.class_extension_mixin import ExtendableMixin
 from tm_devices.helpers.enums import (
-    SignalSourceFunctionBase,
+    LoadImpedanceAFG,
+    SignalGeneratorFunctionBase,
+    SignalGeneratorOutputPathsBase,
 )
 
 _SourceDeviceTypeVar = TypeVar("_SourceDeviceTypeVar", bound="SourceDeviceConstants")
-_SignalSourceTypeVar = TypeVar("_SignalSourceTypeVar", bound=SignalSourceFunctionBase)
+_SignalSourceTypeVar = TypeVar("_SignalSourceTypeVar", bound=SignalGeneratorFunctionBase)
+
+
+class ParameterBounds(NamedTuple):
+    """The upper and lower bounds of a parameter."""
+
+    lower: float
+    upper: float
+
+
+@dataclass(frozen=True)
+class ExtendedSourceDeviceConstants:
+    """Class to hold source device constants."""
+
+    amplitude_range: ParameterBounds
+    offset_range: ParameterBounds
+    frequency_range: ParameterBounds
+    sample_rate_range: ParameterBounds
+    square_duty_cycle_range: Optional[ParameterBounds] = None
+    pulse_width_range: Optional[ParameterBounds] = None
+    ramp_symmetry_range: Optional[ParameterBounds] = None
 
 
 @dataclass(frozen=True)
@@ -20,7 +42,7 @@ class SourceDeviceConstants:
     memory_page_size: int
     memory_max_record_length: int
     memory_min_record_length: int
-    functions: Type[SignalSourceFunctionBase]
+    functions: Type[SignalGeneratorFunctionBase]
 
 
 class SignalGeneratorMixin(ExtendableMixin, ABC):
@@ -36,7 +58,7 @@ def _validate_generated_function(function: _SignalSourceTypeVar) -> _SignalSourc
         Raises:
             TypeError: Tells the user that they are using an incorrect function type.
         """
-        if not issubclass(type(function), SignalSourceFunctionBase):
+        if not issubclass(type(function), SignalGeneratorFunctionBase):
             msg = (
                 "Generate Waveform does not accept functions as non Enums. "
                 "Please use 'source_device_constants.functions'."
@@ -52,20 +74,20 @@ def source_device_constants(
         """Return the device constants."""
 
     @abstractmethod
-    def generate_waveform(  # noqa: PLR0913
+    def generate_function(  # noqa: PLR0913
         self,
         frequency: float,
         function: _SignalSourceTypeVar,  # pyright: ignore[reportInvalidTypeVarUse]
         amplitude: float,
         offset: float,
         channel: str = "all",
-        burst: int = 0,
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
         termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",
         duty_cycle: float = 50.0,
         polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
         symmetry: float = 50.0,
     ) -> None:
-        """Generate a signal given the following parameters.
+        """Generate a predefined waveform given the following parameters.
 
         Args:
             frequency: The frequency of the waveform to generate.
@@ -73,9 +95,67 @@ def generate_waveform(  # noqa: PLR0913
             amplitude: The amplitude of the signal to generate.
             offset: The offset of the signal to generate.
             channel: The channel name to output the signal from, or 'all'.
-            burst: The number of wavelengths to be generated.
+            output_signal_path: The output signal path of the specified channel.
             termination: The impedance this device's ``channel`` expects to see at the received end.
             duty_cycle: The duty cycle percentage within [10.0, 90.0].
             polarity: The polarity to set the signal to.
             symmetry: The symmetry to set the signal to, only applicable to certain functions.
         """
+        raise NotImplementedError
+
+    @abstractmethod
+    def setup_burst(  # noqa: PLR0913
+        self,
+        frequency: float,
+        function: _SignalSourceTypeVar,  # pyright: ignore[reportInvalidTypeVarUse]
+        amplitude: float,
+        offset: float,
+        burst_count: int,
+        channel: str = "all",
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
+        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",
+        duty_cycle: float = 50.0,
+        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
+        symmetry: float = 50.0,
+    ) -> None:
+        """Set up the device for sending a burst of waveforms given the following parameters.
+
+        Args:
+            frequency: The frequency of the waveform to generate.
+            function: The function to generate.
+            amplitude: The amplitude of the signal to generate.
+            offset: The offset of the signal to generate.
+            burst_count: The number of wavelengths to be generated.
+            channel: The channel number to output the signal from, or 'all'.
+            output_signal_path: The output signal path of the specified channel.
+            termination: The impedance to set the channel to.
+            duty_cycle: The duty cycle to set the signal to.
+            polarity: The polarity to set the signal to.
+            symmetry: The symmetry to set the signal to, only applicable to certain functions.
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def generate_burst(self) -> None:
+        """Generate a burst of waveforms by forcing trigger."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def get_waveform_constraints(
+        self,
+        function: Optional[SignalGeneratorFunctionBase] = None,
+        waveform_length: Optional[int] = None,
+        frequency: Optional[float] = None,
+        output_signal_path: str = "DIR",
+        load_impedance: LoadImpedanceAFG = LoadImpedanceAFG.HIGHZ,
+    ) -> ExtendedSourceDeviceConstants:
+        """Get the constraints that restrict the waveform to certain parameter ranges.
+
+        Args:
+            function: The function that needs to be generated.
+            waveform_length: The length of the waveform if no function or arbitrary is provided.
+            frequency: The frequency of the waveform that needs to be generated.
+            output_signal_path: The output signal path that was set on the channel.
+            load_impedance: The suggested impedance on the source.
+        """
+        raise NotImplementedError
diff --git a/src/tm_devices/drivers/__init__.py b/src/tm_devices/drivers/__init__.py
index b80fc4f2..23ba4ec8 100644
--- a/src/tm_devices/drivers/__init__.py
+++ b/src/tm_devices/drivers/__init__.py
@@ -5,19 +5,19 @@
 from tm_devices.drivers.device_driver_mapping import DEVICE_DRIVER_MODEL_MAPPING
 from tm_devices.drivers.device_type_classes import DEVICE_TYPE_CLASSES
 from tm_devices.helpers.enums import SupportedModels
-from tm_devices.drivers.pi.signal_sources.afgs.afg3k import AFG3K
-from tm_devices.drivers.pi.signal_sources.afgs.afg3kb import AFG3KB
-from tm_devices.drivers.pi.signal_sources.afgs.afg3kc import AFG3KC
-from tm_devices.drivers.pi.signal_sources.afgs.afg31k import AFG31K
-from tm_devices.drivers.pi.signal_sources.awgs.awg5k import AWG5K
-from tm_devices.drivers.pi.signal_sources.awgs.awg5kb import AWG5KB
-from tm_devices.drivers.pi.signal_sources.awgs.awg5kc import AWG5KC
-from tm_devices.drivers.pi.signal_sources.awgs.awg7k import AWG7K
-from tm_devices.drivers.pi.signal_sources.awgs.awg7kb import AWG7KB
-from tm_devices.drivers.pi.signal_sources.awgs.awg7kc import AWG7KC
-from tm_devices.drivers.pi.signal_sources.awgs.awg70ka import AWG70KA
-from tm_devices.drivers.pi.signal_sources.awgs.awg70kb import AWG70KB
-from tm_devices.drivers.pi.signal_sources.awgs.awg5200 import AWG5200
+from tm_devices.drivers.pi.signal_generators.afgs.afg3k import AFG3K
+from tm_devices.drivers.pi.signal_generators.afgs.afg3kb import AFG3KB
+from tm_devices.drivers.pi.signal_generators.afgs.afg3kc import AFG3KC
+from tm_devices.drivers.pi.signal_generators.afgs.afg31k import AFG31K
+from tm_devices.drivers.pi.signal_generators.awgs.awg5k import AWG5K
+from tm_devices.drivers.pi.signal_generators.awgs.awg5kb import AWG5KB
+from tm_devices.drivers.pi.signal_generators.awgs.awg5kc import AWG5KC
+from tm_devices.drivers.pi.signal_generators.awgs.awg7k import AWG7K
+from tm_devices.drivers.pi.signal_generators.awgs.awg7kb import AWG7KB
+from tm_devices.drivers.pi.signal_generators.awgs.awg7kc import AWG7KC
+from tm_devices.drivers.pi.signal_generators.awgs.awg70ka import AWG70KA
+from tm_devices.drivers.pi.signal_generators.awgs.awg70kb import AWG70KB
+from tm_devices.drivers.pi.signal_generators.awgs.awg5200 import AWG5200
 from tm_devices.drivers.pi.scopes.tekscope.lpd6 import LPD6
 from tm_devices.drivers.pi.scopes.tekscope.mso2 import MSO2
 from tm_devices.drivers.pi.scopes.tekscope.mso4 import MSO4
diff --git a/src/tm_devices/drivers/device_driver_mapping.py b/src/tm_devices/drivers/device_driver_mapping.py
index 7ae1b496..871eab3b 100644
--- a/src/tm_devices/drivers/device_driver_mapping.py
+++ b/src/tm_devices/drivers/device_driver_mapping.py
@@ -57,19 +57,19 @@
 from tm_devices.drivers.pi.scopes.tekscope_5k_7k_70k.mso70kc import MSO70KC
 from tm_devices.drivers.pi.scopes.tekscope_5k_7k_70k.mso70kdx import MSO70KDX
 from tm_devices.drivers.pi.scopes.tso.tsovu import TSOVu
-from tm_devices.drivers.pi.signal_sources.afgs.afg3k import AFG3K
-from tm_devices.drivers.pi.signal_sources.afgs.afg3kb import AFG3KB
-from tm_devices.drivers.pi.signal_sources.afgs.afg3kc import AFG3KC
-from tm_devices.drivers.pi.signal_sources.afgs.afg31k import AFG31K
-from tm_devices.drivers.pi.signal_sources.awgs.awg5k import AWG5K
-from tm_devices.drivers.pi.signal_sources.awgs.awg5kb import AWG5KB
-from tm_devices.drivers.pi.signal_sources.awgs.awg5kc import AWG5KC
-from tm_devices.drivers.pi.signal_sources.awgs.awg7k import AWG7K
-from tm_devices.drivers.pi.signal_sources.awgs.awg7kb import AWG7KB
-from tm_devices.drivers.pi.signal_sources.awgs.awg7kc import AWG7KC
-from tm_devices.drivers.pi.signal_sources.awgs.awg70ka import AWG70KA
-from tm_devices.drivers.pi.signal_sources.awgs.awg70kb import AWG70KB
-from tm_devices.drivers.pi.signal_sources.awgs.awg5200 import AWG5200
+from tm_devices.drivers.pi.signal_generators.afgs.afg3k import AFG3K
+from tm_devices.drivers.pi.signal_generators.afgs.afg3kb import AFG3KB
+from tm_devices.drivers.pi.signal_generators.afgs.afg3kc import AFG3KC
+from tm_devices.drivers.pi.signal_generators.afgs.afg31k import AFG31K
+from tm_devices.drivers.pi.signal_generators.awgs.awg5k import AWG5K
+from tm_devices.drivers.pi.signal_generators.awgs.awg5kb import AWG5KB
+from tm_devices.drivers.pi.signal_generators.awgs.awg5kc import AWG5KC
+from tm_devices.drivers.pi.signal_generators.awgs.awg7k import AWG7K
+from tm_devices.drivers.pi.signal_generators.awgs.awg7kb import AWG7KB
+from tm_devices.drivers.pi.signal_generators.awgs.awg7kc import AWG7KC
+from tm_devices.drivers.pi.signal_generators.awgs.awg70ka import AWG70KA
+from tm_devices.drivers.pi.signal_generators.awgs.awg70kb import AWG70KB
+from tm_devices.drivers.pi.signal_generators.awgs.awg5200 import AWG5200
 from tm_devices.drivers.pi.source_measure_units.smu24xx.smu2400 import SMU2400
 from tm_devices.drivers.pi.source_measure_units.smu24xx.smu2401 import SMU2401
 from tm_devices.drivers.pi.source_measure_units.smu24xx.smu2410 import SMU2410
diff --git a/src/tm_devices/drivers/device_type_classes.py b/src/tm_devices/drivers/device_type_classes.py
index e18a3d06..706198f6 100644
--- a/src/tm_devices/drivers/device_type_classes.py
+++ b/src/tm_devices/drivers/device_type_classes.py
@@ -10,8 +10,8 @@
 from tm_devices.drivers.pi.digital_multimeters.digital_multimeter import DigitalMultimeter
 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
 
diff --git a/src/tm_devices/drivers/pi/_base_afg_source_channel.py b/src/tm_devices/drivers/pi/_base_afg_source_channel.py
new file mode 100644
index 00000000..64a25c4b
--- /dev/null
+++ b/src/tm_devices/drivers/pi/_base_afg_source_channel.py
@@ -0,0 +1,95 @@
+"""Base AFG source channel driver module."""
+
+from abc import abstractmethod
+from typing import Literal
+
+from tm_devices.drivers.pi._base_source_channel import BaseSourceChannel
+from tm_devices.drivers.pi.pi_device import PIDevice
+from tm_devices.helpers.enums import SignalGeneratorFunctionBase
+
+
+class BaseAFGSourceChannel(BaseSourceChannel):
+    """Base AFG source channel driver."""
+
+    def __init__(self, pi_device: PIDevice, channel_name: str) -> None:
+        """Create an AFG source channel.
+
+        Args:
+            pi_device: A PI device.
+            channel_name: The channel name for the AFG source channel.
+        """
+        super().__init__(pi_device=pi_device, channel_name=channel_name)
+
+    @abstractmethod
+    def set_function_properties(  # noqa: PLR0913
+        self,
+        frequency: float,
+        function: SignalGeneratorFunctionBase,
+        amplitude: float,
+        offset: float,
+        burst_count: int = 0,
+        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",
+        duty_cycle: float = 50.0,
+        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
+        symmetry: float = 100.0,
+    ) -> None:
+        """Set the given parameters on the provided source channel.
+
+        Args:
+            frequency: The frequency of the waveform to generate.
+            function: The waveform shape to generate.
+            amplitude: The amplitude of the signal to generate.
+            offset: The offset of the signal to generate.
+            burst_count: The number of wavelengths to be generated.
+            termination: The impedance this device's ``channel`` expects to see at the received end.
+            duty_cycle: The duty cycle percentage within [0.4, 99.6].
+            polarity: The polarity to set the signal to.
+            symmetry: The symmetry to set the signal to, only applicable to certain functions.
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def set_burst_count(self, value: int) -> None:
+        """Set the number of wavelengths to be generated when the source channel is set to burst.
+
+        Args:
+            value: The number of wavelengths to be generated within [1, 1000000].
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def set_frequency(self, value: float, absolute_tolerance: float = 0) -> None:
+        """Set the frequency on the source channel.
+
+        Args:
+            value: The frequency value to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def set_function(self, value: SignalGeneratorFunctionBase) -> None:
+        """Set the function to output on the source channel.
+
+        Args:
+            value: The name of the function to output.
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def set_ramp_symmetry(self, value: float) -> None:
+        """Set the symmetry of the ramp waveform on the source channel.
+
+        Args:
+            value: The symmetry value to set within [0, 100].
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def setup_burst_waveform(self, burst_count: int) -> None:
+        """Prepare the source channel for a burst waveform.
+
+        Args:
+            burst_count: The number of wavelengths to be generated.
+        """
+        raise NotImplementedError
diff --git a/src/tm_devices/drivers/pi/_base_source_channel.py b/src/tm_devices/drivers/pi/_base_source_channel.py
new file mode 100644
index 00000000..05ac9709
--- /dev/null
+++ b/src/tm_devices/drivers/pi/_base_source_channel.py
@@ -0,0 +1,62 @@
+"""Base source channel driver module."""
+
+from abc import ABC, abstractmethod
+from typing import Optional
+
+from tm_devices.drivers.pi.pi_device import PIDevice
+
+
+class BaseSourceChannel(ABC):
+    """Base source channel driver."""
+
+    def __init__(self, pi_device: PIDevice, channel_name: str) -> None:
+        """Create a source channel.
+
+        Args:
+            pi_device: A PI device.
+            channel_name: The channel name for the source channel.
+        """
+        self._name = channel_name
+        self._num = None
+        if channel_num := "".join(filter(str.isdigit, channel_name)):
+            self._num = int(channel_num)
+        self._pi_device = pi_device
+
+    @property
+    def name(self) -> str:
+        """Return the channel name."""
+        return self._name
+
+    @property
+    def num(self) -> Optional[int]:
+        """Return the channel number."""
+        return self._num
+
+    @abstractmethod
+    def set_amplitude(self, value: float, absolute_tolerance: float = 0) -> None:
+        """Set the amplitude on the source channel.
+
+        Args:
+            value: The amplitude value to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def set_offset(self, value: float, absolute_tolerance: float = 0) -> None:
+        """Set the offset on the source channel.
+
+        Args:
+            value: The offset value to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def set_state(self, value: int) -> None:
+        """Set the output state to ON/OFF (1/0) on the source channel.
+
+        Args:
+            value: The output state.
+        """
+        raise NotImplementedError
diff --git a/src/tm_devices/drivers/pi/pi_device.py b/src/tm_devices/drivers/pi/pi_device.py
index 0d969437..2b83e5cb 100644
--- a/src/tm_devices/drivers/pi/pi_device.py
+++ b/src/tm_devices/drivers/pi/pi_device.py
@@ -1,4 +1,4 @@
-# pyright: reportUnnecessaryTypeIgnoreComment=none
+# pylint: disable=too-many-lines  # pyright: reportUnnecessaryTypeIgnoreComment=none
 """Base Programmable Interface (PI) device driver module."""
 
 import inspect
@@ -9,7 +9,7 @@
 
 from abc import ABC, abstractmethod
 from contextlib import contextmanager
-from typing import final, Generator, Optional, Sequence, Tuple, Union
+from typing import final, Generator, List, Optional, Sequence, Tuple, Union
 
 import pyvisa as visa
 
@@ -35,8 +35,7 @@
 from tm_devices.helpers.constants_and_dataclasses import UNIT_TEST_TIMEOUT
 
 
-# pylint: disable=too-many-public-methods
-class PIDevice(Device, ABC):
+class PIDevice(Device, ABC):  # pylint: disable=too-many-public-methods
     """Base Programmable Interface (PI) device driver."""
 
     # This is a class constant that can be overwritten by children which defines
@@ -301,6 +300,76 @@ def get_visa_stb(self) -> int:  # pragma: no cover
         """Return the VISA status byte."""
         return self._visa_resource.read_stb()
 
+    def poll_query(  # noqa: PLR0913
+        self,
+        number_of_polls: int,
+        query: str,
+        wanted_val: Union[float, str],
+        sleep_time: float = 0.4,
+        tolerance: float = 0,
+        percentage: bool = False,
+        invert_range: bool = False,
+        invalid_values: Union[List[Union[float, str]], None] = None,
+    ) -> None:
+        """Poll the query until the wanted value appears.
+
+        Args:
+            number_of_polls: The number of times to poll the query.
+            query: The query to poll.
+            wanted_val: The desired value to poll for.
+            sleep_time: The time to wait between polls (in seconds).
+            tolerance: The acceptable difference between two floating point values.
+            percentage: A boolean indicating what kind of tolerance check to perform.
+                 False means absolute tolerance: +/- tolerance.
+                 True means percent tolerance: +/- (tolerance / 100) * value.
+            invert_range: A boolean indicating when to stop polling.
+                 False means polling until the wanted value appears.
+                 True means polling until a different value from the wanted value appears.
+            invalid_values: A list of values that should never be received when polling.
+
+        Raises:
+            AssertionError: Indicating that the device never reached the wanted value.
+        """
+        if invalid_values is None:
+            invalid_values = []
+        poll_number: int = 0
+        query_list = ""
+        if isinstance(wanted_val, float):
+            tolerance = abs(tolerance * 0.01 * wanted_val if percentage else tolerance)
+        while poll_number < number_of_polls:
+            queried_value = self.query(query)
+            query_list += f"\t{poll_number} - {queried_value}\n"
+            if invalid_values:
+                self.ieee_cmds.cls()
+                error_check = (
+                    float(queried_value) not in invalid_values
+                    if isinstance(wanted_val, (float, int))
+                    else queried_value not in invalid_values
+                )
+            else:
+                error_check = True
+            if error_check:
+                float_comparison = (
+                    isinstance(wanted_val, (float, int))
+                    and wanted_val - tolerance
+                    <= float(queried_value)
+                    <= float(wanted_val) + tolerance
+                )
+                str_comparison = isinstance(wanted_val, str) and queried_value == wanted_val
+                if (
+                    not invert_range
+                    and (float_comparison or str_comparison)
+                    or invert_range
+                    and not (float_comparison or str_comparison)
+                ):
+                    return
+            time.sleep(sleep_time)
+            poll_number += 1
+        raise AssertionError(  # noqa: TRY003
+            f"{query} {'never' if not invert_range else 'always'} "
+            f"returned {wanted_val}, received:\n{query_list}"
+        )
+
     def query(
         self,
         query: str,
@@ -515,6 +584,7 @@ def query_response(
         percentage: bool = False,
         remove_quotes: bool = False,
         custom_message_prefix: str = "",
+        allow_empty: bool = False,
     ) -> Tuple[bool, str]:
         """Query the and verify the result.
 
@@ -527,11 +597,12 @@ def query_response(
                  True means percent tolerance: +/- (tolerance / 100) * value.
             remove_quotes: Set this to True to remove all double quotes from the returned value.
             custom_message_prefix: A custom message to be prepended to the failure message.
+            allow_empty: Set this to True if an empty return string is permitted.
 
         Returns:
             Tuple containing the boolean verification result and the value returned from the query.
         """
-        actual_value = self.query(query, remove_quotes=remove_quotes)
+        actual_value = self.query(query, remove_quotes=remove_quotes, allow_empty=allow_empty)
         message_prefix = f"query_response failed for query: {query}"
         if custom_message_prefix:
             message_prefix = f"{custom_message_prefix}\n{message_prefix}"
@@ -586,7 +657,7 @@ def reset_visa_timeout(self) -> None:
         """Reset the VISA timeout to the default value."""
         self.visa_timeout = self._default_visa_timeout
 
-    def set_and_check(
+    def set_and_check(  # noqa: PLR0913
         self,
         command: str,
         value: Union[str, float],
@@ -596,6 +667,7 @@ def set_and_check(
         custom_message_prefix: str = "",
         *,
         expected_value: Optional[Union[str, float]] = None,
+        opc: bool = False,
     ) -> str:
         """Send the given command with the given value and then verify the results.
 
@@ -611,11 +683,12 @@ def set_and_check(
             remove_quotes: Set this to True to remove all double quotes from the returned value.
             custom_message_prefix: A custom message to be prepended to the failure message.
             expected_value: An optional, alternative value expected to be returned.
+            opc: Boolean indicating if ``*OPC?`` should be queried after sending the command.
 
         Returns:
             The output of the query portion of the method.
         """
-        self.write(f"{command} {value}")
+        self.write(f"{command} {value}", opc=opc)
         if self._enable_verification:
             check = self.query(command + "?", remove_quotes=remove_quotes)
             message_prefix = f"Failed to set {command} to {value}"
@@ -633,6 +706,70 @@ def set_and_check(
             check = ""
         return check
 
+    def set_if_needed(  # noqa: PLR0913
+        self,
+        command: str,
+        value: Union[str, float],
+        tolerance: float = 0,
+        percentage: bool = False,
+        remove_quotes: bool = False,
+        custom_message_prefix: str = "",
+        *,
+        expected_value: Optional[Union[str, float]] = None,
+        opc: bool = False,
+        allow_empty: bool = False,
+        verify_value: bool = False,
+    ) -> Tuple[bool, str]:
+        """Query the command's field and update it if the value does not match the input.
+
+        Args:
+            command: The command to send.
+                For example: ``:AFG:FUNCTION``
+            value: The value being set by the command.
+                For example: ``SINE``
+            tolerance: The acceptable difference between two floating point values.
+            percentage: A boolean indicating what kind of tolerance check to perform.
+                 False means absolute tolerance: +/- tolerance.
+                 True means percent tolerance: +/- (tolerance / 100) * value.
+            remove_quotes: Set this to True to remove all double quotes from the returned value.
+            custom_message_prefix: A custom message to be prepended to the failure message.
+            expected_value: An optional, alternative value expected to be returned.
+            opc: Boolean indicating if ``*OPC?`` should be queried after sending the command.
+            allow_empty: Set this to True if an empty return string is permitted.
+            verify_value: Boolean indicating to verify value after write.
+
+        Returns:
+            Tuple containing the boolean value indicating if the command needed to be set and
+            the value returned from the query.
+        """
+        try:
+            query_passed, actual_value = self.query_response(
+                query=command + "?",
+                value=value,
+                tolerance=tolerance,
+                percentage=percentage,
+                remove_quotes=remove_quotes,
+                custom_message_prefix=custom_message_prefix,
+                allow_empty=allow_empty,
+            )
+        except AssertionError:
+            query_passed = False
+            if verify_value:
+                actual_value = self.set_and_check(
+                    command,
+                    value,
+                    tolerance,
+                    percentage,
+                    remove_quotes,
+                    custom_message_prefix,
+                    expected_value=expected_value,
+                    opc=opc,
+                )
+            else:
+                self.write(f"{command} {value}", opc=bool(opc))
+                actual_value = ""
+        return not query_passed, actual_value
+
     @final
     def turn_all_channels_off(self) -> None:
         """Turn all channels off."""
diff --git a/src/tm_devices/drivers/pi/power_supplies/power_supply.py b/src/tm_devices/drivers/pi/power_supplies/power_supply.py
index b15541ce..0205d54e 100644
--- a/src/tm_devices/drivers/pi/power_supplies/power_supply.py
+++ b/src/tm_devices/drivers/pi/power_supplies/power_supply.py
@@ -7,7 +7,7 @@
 from typing import Tuple, Union
 
 from tm_devices.drivers.pi.pi_device import PIDevice
-from tm_devices.drivers.pi.signal_sources.signal_source import SignalSource
+from tm_devices.drivers.pi.signal_generators.signal_generator import SignalGenerator
 from tm_devices.helpers import DeviceTypes
 
 
@@ -32,7 +32,7 @@ def expect_esr(self, esr: Union[int, str], error_string: str = "") -> Tuple[bool
         Returns:
             Boolean indicating if the check passed or failed and a string with the results.
         """
-        return SignalSource.expect_esr(self, esr, error_string)  # type: ignore[arg-type]
+        return SignalGenerator.expect_esr(self, esr, error_string)  # type: ignore[arg-type]
 
     def get_eventlog_status(self) -> Tuple[bool, str]:
         """Help function for getting the eventlog status.
@@ -40,4 +40,4 @@ def get_eventlog_status(self) -> Tuple[bool, str]:
         Returns:
             Boolean indicating no error, String containing concatenated contents of event log.
         """
-        return SignalSource.get_eventlog_status(self)  # type: ignore[arg-type]
+        return SignalGenerator.get_eventlog_status(self)  # type: ignore[arg-type]
diff --git a/src/tm_devices/drivers/pi/scopes/tekscope/mso5b.py b/src/tm_devices/drivers/pi/scopes/tekscope/mso5b.py
index cad74820..2308762b 100644
--- a/src/tm_devices/drivers/pi/scopes/tekscope/mso5b.py
+++ b/src/tm_devices/drivers/pi/scopes/tekscope/mso5b.py
@@ -28,3 +28,11 @@ def __init__(
         """
         # NOTE: This method must be defined for the documentation to properly generate
         super().__init__(config_entry, verbose, visa_resource)
+
+    ################################################################################################
+    # Private Methods
+    ################################################################################################
+    @staticmethod
+    def _get_driver_specific_multipliers() -> float:
+        """Return a value to multiply the original Tekscope IAFG frequency by."""
+        return 2.0
diff --git a/src/tm_devices/drivers/pi/scopes/tekscope/tekscope.py b/src/tm_devices/drivers/pi/scopes/tekscope/tekscope.py
index 36e673de..548e8a6c 100644
--- a/src/tm_devices/drivers/pi/scopes/tekscope/tekscope.py
+++ b/src/tm_devices/drivers/pi/scopes/tekscope/tekscope.py
@@ -1,5 +1,7 @@
 """Base TekScope scope device driver module."""
 
+# pylint: disable=too-many-lines
+import math
 import os
 import warnings
 
@@ -32,24 +34,31 @@
 )
 from tm_devices.driver_mixins.licensed_mixin import LicensedMixin
 from tm_devices.driver_mixins.signal_generator_mixin import (
+    ExtendedSourceDeviceConstants,
+    ParameterBounds,
     SignalGeneratorMixin,
     SourceDeviceConstants,
 )
 from tm_devices.driver_mixins.usb_drives_mixin import USBDrivesMixin
 from tm_devices.drivers.device import family_base_class
+from tm_devices.drivers.pi._base_afg_source_channel import BaseAFGSourceChannel
 from tm_devices.drivers.pi.scopes.scope import Scope
-from tm_devices.helpers import DeviceConfigEntry, SignalSourceFunctionsIAFG
+from tm_devices.helpers import DeviceConfigEntry, LoadImpedanceAFG
 
 # noinspection PyPep8Naming
 from tm_devices.helpers import ReadOnlyCachedProperty as cached_property  # noqa: N813
 from tm_devices.helpers.constants_and_dataclasses import UNIT_TEST_TIMEOUT
+from tm_devices.helpers.enums import (
+    SignalGeneratorFunctionsIAFG,
+    SignalGeneratorOutputPathsBase,
+)
 
 
 @dataclass(frozen=True)
 class TekScopeSourceDeviceConstants(SourceDeviceConstants):
     """Class to hold source device constants."""
 
-    functions: Type[SignalSourceFunctionsIAFG] = SignalSourceFunctionsIAFG
+    functions: Type[SignalGeneratorFunctionsIAFG] = SignalGeneratorFunctionsIAFG
 
 
 @dataclass(frozen=True)
@@ -61,19 +70,9 @@ class TekProbeData:
     probe_id_type: str = "1X"
 
 
-@dataclass
-class TekScopeChannel:
-    """Scope channel information."""
-
-    name: str
-    """Name of the channel."""
-    probe: TekProbeData
-    """Details about channel setup and any connected probe."""
-    termination: Optional[Literal[50, 250_000, 1_000_000]] = None
-    """Defines impedance at channel port."""
-
-
 # pylint: disable=too-many-public-methods
+# TODO: Move signal generator mixin to different abstract class, so that TekScopePC
+#   does not inherit the IAFG.
 @family_base_class
 class TekScope(
     Scope,
@@ -173,6 +172,11 @@ def channel(self) -> "MappingProxyType[str, TekScopeChannel]":
             self.set_and_check(":VERBose", old_pi_verbosity)
         return MappingProxyType(channel_map)
 
+    @cached_property
+    def internal_afg(self) -> "InternalAFGChannel":
+        """The scope's internal AFG."""
+        return InternalAFGChannel(self)
+
     @property
     def commands(
         self,
@@ -379,61 +383,207 @@ def curve_query(  # noqa: PLR0912,C901
 
         return wfm_data  # return list of frames
 
-    def generate_waveform(  # noqa: PLR0913  # pyright: ignore[reportIncompatibleMethodOverride]
+    def generate_function(  # noqa: PLR0913  # pyright: ignore[reportIncompatibleMethodOverride]
         self,
         frequency: float,
-        function: SignalSourceFunctionsIAFG,
+        function: SignalGeneratorFunctionsIAFG,
         amplitude: float,
         offset: float,
         channel: str = "all",
-        burst: int = 0,
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
         termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",
         duty_cycle: float = 50.0,
         polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
         symmetry: float = 50.0,
     ) -> None:
-        """Generate a signal given the following parameters.
+        """Generate a predefined waveform given the following parameters.
 
         Args:
             frequency: The frequency of the waveform to generate.
             function: The function to generate.
             amplitude: The amplitude of the signal to generate.
             offset: The offset of the signal to generate.
-            channel: The channel number to output the signal from, or 'all'.
-            burst: The number of waveforms to be generated.
+            channel: Unused in this class.
+            output_signal_path: Unused in this class.
             termination: The impedance to set the channel to.
             duty_cycle: The duty cycle to set the signal to.
-            polarity: The polarity to set the signal to.
+            polarity: Unused in this class.
             symmetry: The symmetry to set the signal to, only applicable to certain functions.
         """
-        del polarity, channel  # these aren't used
+        del polarity, channel, output_signal_path  # these aren't used
         self._validate_generated_function(function)
         # Turn off the Internal AFG
-        self.set_and_check("AFG:OUTPUT:STATE", 0)
-        if burst > 0:
-            self.set_and_check("AFG:OUTPUT:MODE", "BURST")
-            self.set_and_check("AFG:BURST:CCOUNT", f"{burst}")
-        # Generate the waveform from the Internal AFG
-        # Frequency
-        self.set_and_check("AFG:FREQUENCY", frequency)
-        # Offset
-        self.set_and_check("AFG:OFFSET", offset)
-        # Duty Cycle
-        self.set_and_check("AFG:SQUARE:DUTY", duty_cycle)
-        # Function
-        if function == SignalSourceFunctionsIAFG.RAMP:
-            self.set_and_check("AFG:RAMP:SYMMETRY", symmetry)
-        self.set_and_check("AFG:FUNCTION", function.value)
-        # Termination impedance
-        self.set_and_check("AFG:OUTPUT:LOAD:IMPEDANCE", termination)
-        # Amplitude, needs to be after termination so that the amplitude is properly adjusted
-        self.set_and_check("AFG:AMPLITUDE", amplitude)
+        self.internal_afg.set_state(0)
+        self.internal_afg.set_function_properties(
+            frequency=frequency,
+            function=function,
+            amplitude=amplitude,
+            offset=offset,
+            burst_count=0,
+            termination=termination,
+            duty_cycle=duty_cycle,
+            symmetry=symmetry,
+        )
         # Turn on the Internal AFG
-        self.set_and_check("AFG:OUTPUT:STATE", 1)
-        if burst > 0:
-            self.write("AFG:BURST:TRIGGER")
+        self.internal_afg.set_state(1)
+        # Don't check for errors as any measurement with low amplitude will generate an error
+
+    def setup_burst(  # noqa: PLR0913  # pyright: ignore[reportIncompatibleMethodOverride]
+        self,
+        frequency: float,
+        function: SignalGeneratorFunctionsIAFG,
+        amplitude: float,
+        offset: float,
+        burst_count: int,
+        channel: str = "all",
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
+        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",
+        duty_cycle: float = 50.0,
+        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
+        symmetry: float = 50.0,
+    ) -> None:
+        """Set up the Internal AFG for sending a burst of waveforms given the following parameters.
+
+        Args:
+            frequency: The frequency of the waveform to generate.
+            function: The function to generate.
+            amplitude: The amplitude of the signal to generate.
+            offset: The offset of the signal to generate.
+            burst_count: The number of wavelengths to be generated.
+            channel: Unused in this class.
+            output_signal_path: Unused in this class.
+            termination: The impedance to set the channel to.
+            duty_cycle: The duty cycle to set the signal to.
+            polarity: Unused in this class.
+            symmetry: The symmetry to set the signal to, only applicable to certain functions.
+        """
+        del polarity, channel, output_signal_path  # these aren't used
+        self._validate_generated_function(function)
+        self.internal_afg.set_function_properties(
+            frequency=frequency,
+            function=function,
+            amplitude=amplitude,
+            offset=offset,
+            burst_count=burst_count,
+            termination=termination,
+            duty_cycle=duty_cycle,
+            symmetry=symmetry,
+        )
+
+    def generate_burst(self) -> None:
+        """Generate a burst of waveforms by forcing trigger."""
+        self.internal_afg.trigger_burst()
         # Don't check for errors as any measurement with low amplitude will generate an error
 
+    # pylint: disable=too-many-locals
+    def get_waveform_constraints(  # pyright: ignore[reportIncompatibleMethodOverride]
+        self,
+        function: Optional[SignalGeneratorFunctionsIAFG] = None,
+        waveform_length: Optional[int] = None,
+        frequency: Optional[float] = None,
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
+        load_impedance: LoadImpedanceAFG = LoadImpedanceAFG.HIGHZ,
+    ) -> ExtendedSourceDeviceConstants:
+        """Get the constraints that restrict the waveform to certain parameter ranges.
+
+        Args:
+            function: The function that needs to be generated.
+            waveform_length: Unused in this class.
+            frequency: The frequency of the waveform that needs to be generated.
+            output_signal_path: Unused in this class.
+            load_impedance: The suggested impedance on the source.
+
+        Returns:
+            A Named Tuple containing a set of parameters and their restricted bounds.
+        """
+        del output_signal_path, waveform_length
+
+        if not function:
+            msg = "IAFGs must have a function defined."
+            raise ValueError(msg)
+
+        base_frequency_low = 100.0e-3
+        base_frequency_high = 50.0e6 * self._get_driver_specific_multipliers()
+
+        load_impedance_multiplier = 1.0 if load_impedance == LoadImpedanceAFG.HIGHZ else 0.5
+
+        base_amplitude_low = 20.0e-3
+        base_amplitude_high = 5.0
+
+        square_duty_cycle_range = ParameterBounds(lower=10.0, upper=90.0)
+        pulse_width_range = None
+        # handle logic to constrain limits of duty cycle and pulse width range due to frequency
+        if frequency is not None:
+            max_duty = 90.0
+            min_duty = 10.0
+            max_square_pulse_freq = base_frequency_high / 2
+            # limit to valid range for calcs, otherwise values are outside valid ranges
+            calc_freq = max((base_amplitude_high, min((max_square_pulse_freq, frequency))))
+            # above 10MHz (or 20MHz), then SQUARE duty cycle and PULSE width ranges get coerced
+            if frequency > (duty_cycle_coercion_start_freq := base_frequency_high / 5):
+                # 15 percent change over the difference between max frequency and start of coercion
+                coercion_slope = 15.0 / (max_square_pulse_freq - duty_cycle_coercion_start_freq)
+                max_duty = 90.0 - (coercion_slope * (calc_freq - duty_cycle_coercion_start_freq))
+                min_duty = 10.0 + (coercion_slope * (calc_freq - duty_cycle_coercion_start_freq))
+                # one decimal place of accuracy as percentage,
+                # and uses floor/ceil on the max/min to stay in valid zone
+                square_duty_cycle_range = ParameterBounds(
+                    lower=math.ceil(min_duty * 10) / 10, upper=math.floor(max_duty * 10) / 10
+                )
+            # TODO: There is an edge case where the IAFG period doesn't exactly equal 1/frequency
+            #  this can cause the pulse width max and min to be outside valid range by tiny bit,
+            #  workaround is to query actual period time and set frequency=1/<actual_period>
+            # limited to 0.1ns resolution regardless of frequency
+            # also uses floor/ceil to stay in valid zone
+            pulse_width_range = ParameterBounds(
+                lower=math.ceil(min_duty / 100 * 1e10 / calc_freq) / 1e10,
+                upper=math.floor(max_duty / 100 * 1e10 / calc_freq) / 1e10,
+            )
+        amplitude_multiplier = 1
+
+        if function in {SignalGeneratorFunctionsIAFG.SIN}:
+            frequency_multiplier = 1
+        elif function in {
+            SignalGeneratorFunctionsIAFG.SQUARE,
+            SignalGeneratorFunctionsIAFG.PULSE,
+            SignalGeneratorFunctionsIAFG.ARBITRARY,
+        }:
+            frequency_multiplier = 0.5
+        elif function in {SignalGeneratorFunctionsIAFG.SINC}:
+            frequency_multiplier = 0.04
+            amplitude_multiplier = 0.6
+        elif function in {
+            SignalGeneratorFunctionsIAFG.RAMP,
+            SignalGeneratorFunctionsIAFG.CARDIAC,
+        }:
+            frequency_multiplier = 0.01
+        else:
+            frequency_multiplier = 0.1
+            amplitude_multiplier = 0.5 if function != SignalGeneratorFunctionsIAFG.LORENTZ else 0.48
+
+        frequency_range = ParameterBounds(
+            lower=base_frequency_low, upper=base_frequency_high * frequency_multiplier
+        )
+        amplitude_range = ParameterBounds(
+            lower=base_amplitude_low * load_impedance_multiplier,
+            upper=base_amplitude_high * amplitude_multiplier * load_impedance_multiplier,
+        )
+        offset_range = ParameterBounds(
+            lower=-2.5 * load_impedance_multiplier, upper=2.5 * load_impedance_multiplier
+        )
+        # RAMP symmetry range never changes with frequency
+        ramp_symmetry_range = ParameterBounds(lower=0.0, upper=100.0)
+        sample_rate_range = ParameterBounds(lower=250.0e6, upper=250.0e6)
+        return ExtendedSourceDeviceConstants(
+            amplitude_range=amplitude_range,
+            frequency_range=frequency_range,
+            offset_range=offset_range,
+            sample_rate_range=sample_rate_range,
+            square_duty_cycle_range=square_duty_cycle_range,
+            pulse_width_range=pulse_width_range,
+            ramp_symmetry_range=ramp_symmetry_range,
+        )
+
     def recall_reference(self, reference_path: str, ref_number: Union[int, str]) -> None:
         """Recall a reference waveform file.
 
@@ -701,6 +851,11 @@ def _reboot(self) -> None:
         """Reboot the device."""
         self.write(":SCOPEAPP REBOOT")
 
+    @staticmethod
+    def _get_driver_specific_multipliers() -> float:
+        """Return a value to multiply the original Tekscope IAFG frequency by."""
+        return 1.0
+
     def _set_channel_display_state(
         self, channel_str: str, state: bool, turn_on_group: bool = True
     ) -> None:
@@ -723,3 +878,198 @@ def _set_channel_display_state(
         ):
             channel_str = channel_str_base + "_DALL"
         self.set_and_check(f":DISPLAY:WAVEVIEW:{channel_str}:STATE", int(state))
+
+
+@dataclass
+class TekScopeChannel:
+    """Scope channel information."""
+
+    name: str
+    """Name of the channel."""
+    probe: TekProbeData
+    """Details about channel setup and any connected probe."""
+    termination: Optional[Literal[50, 250_000, 1_000_000]] = None
+    """Defines impedance at channel port."""
+
+
+class InternalAFGChannel(BaseAFGSourceChannel):
+    """Internal AFG channel driver."""
+
+    def __init__(self, tekscope: TekScope) -> None:
+        """Create an Internal AFG channel.
+
+        Args:
+            tekscope: A TekScope.
+        """
+        super().__init__(pi_device=tekscope, channel_name="AFG")
+        self._tekscope = tekscope
+
+    def __getattribute__(self, item: Any) -> Any:
+        """Get a class attribute.
+
+        Args:
+            item: The attribute to access.
+        """
+        obj: TekScope = super().__getattribute__("_tekscope")
+        if obj.has_license("AFG"):
+            return super().__getattribute__(item)
+        msg = "No AFG License, the class instance attributes are not accessible."
+        raise AttributeError(msg)
+
+    def set_function_properties(  # noqa: PLR0913  # pyright: ignore[reportIncompatibleMethodOverride]
+        self,
+        frequency: float,
+        function: SignalGeneratorFunctionsIAFG,
+        amplitude: float,
+        offset: float,
+        burst_count: int = 0,
+        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",
+        duty_cycle: float = 50.0,
+        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
+        symmetry: float = 50.0,
+    ) -> None:
+        """Set the given parameters on the internal AFG.
+
+        Args:
+            frequency: The frequency of the waveform to generate.
+            function: The waveform shape to generate.
+            amplitude: The amplitude of the signal to generate.
+            offset: The offset of the signal to generate.
+            burst_count: The number of wavelengths to be generated.
+            termination: The impedance this device's ``channel`` expects to see at the received end.
+            duty_cycle: The duty cycle percentage within [10.0, 90.0].
+            polarity: Unused in this class.
+            symmetry: The symmetry to set the signal to, only applicable to certain functions.
+        """
+        del polarity
+        if burst_count > 0:
+            self.setup_burst_waveform(burst_count)
+        # Generate the waveform from the Internal AFG
+        # Frequency
+        self.set_frequency(frequency)
+        # Offset
+        self.set_offset(offset)
+        # Function
+        self.set_function(function)
+        # Duty Cycle
+        if function == SignalGeneratorFunctionsIAFG.SQUARE:
+            self.set_square_duty_cycle(duty_cycle)
+        # Symmetry
+        if function == SignalGeneratorFunctionsIAFG.RAMP:
+            self.set_ramp_symmetry(symmetry)
+        # Termination impedance
+        self.set_impedance(termination)
+        # Amplitude, needs to be after termination so that the amplitude is properly adjusted
+        self.set_amplitude(amplitude)
+
+    def set_amplitude(self, value: float, absolute_tolerance: float = 0) -> None:
+        """Set the amplitude on the internal AFG.
+
+        Args:
+            value: The amplitude value to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+        """
+        self._tekscope.set_if_needed(
+            f"{self.name}:AMPLITUDE",
+            value,
+            tolerance=absolute_tolerance,
+        )
+
+    def set_burst_count(self, value: int) -> None:
+        """Set the number of wavelengths to be generated when the internal AFG is set to burst.
+
+        Args:
+            value: The number of wavelengths to be generated within [1, 1000000].
+        """
+        self._tekscope.set_if_needed(f"{self.name}:BURST:CCOUNT", f"{value}")
+
+    def set_frequency(self, value: float, absolute_tolerance: float = 0) -> None:
+        """Set the frequency on the internal AFG.
+
+        Args:
+            value: The frequency value to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+        """
+        self._tekscope.set_if_needed(
+            f"{self.name}:FREQUENCY",
+            value,
+            tolerance=absolute_tolerance,
+        )
+
+    def set_function(self, value: SignalGeneratorFunctionsIAFG) -> None:  # pyright: ignore[reportIncompatibleMethodOverride]
+        """Set the function to output on the internal AFG.
+
+        Args:
+            value: The name of the function to output.
+        """
+        self._tekscope.set_if_needed(f"{self.name}:FUNCTION", str(value.value))
+
+    def set_impedance(self, value: Literal["FIFTY", "HIGHZ"]) -> None:
+        """Set the output load impedance on the internal AFG.
+
+        Args:
+            value: The impedance value to set (Options: "FIFTY", "HIGHZ").
+        """
+        self._tekscope.set_if_needed(f"{self.name}:OUTPUT:LOAD:IMPEDANCE", value)
+
+    def set_offset(self, value: float, absolute_tolerance: float = 0) -> None:
+        """Set the offset on the internal AFG.
+
+        Args:
+            value: The offset value to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+        """
+        self._tekscope.set_if_needed(
+            f"{self.name}:OFFSET",
+            value,
+            tolerance=absolute_tolerance,
+        )
+
+    def set_output_mode(self, value: Literal["OFF", "CONTINUOUS", "BURST"]) -> None:
+        """Set the output mode on the internal AFG.
+
+        Args:
+            value: The output mode to set (Options: "OFF", "CONTINUOUS", "BURST").
+        """
+        self._tekscope.set_if_needed(f"{self.name}:OUTPUT:MODE", value)
+
+    def set_ramp_symmetry(self, value: float) -> None:
+        """Set the symmetry of the ramp waveform on the internal AFG.
+
+        Args:
+            value: The symmetry percentage within [0, 100].
+        """
+        self._tekscope.set_if_needed(f"{self.name}:RAMP:SYMMETRY", value)
+
+    def set_state(self, value: int) -> None:
+        """Set the output state to ON/OFF (1/0) on the internal AFG.
+
+        Args:
+            value: The output state.
+        """
+        if value not in [0, 1]:
+            error_message = "Output state value must be 1 (ON) or 0 (OFF)."
+            raise ValueError(error_message)
+        self._tekscope.set_if_needed(f"{self.name}:OUTPUT:STATE", value)
+
+    def set_square_duty_cycle(self, value: float) -> None:
+        """Set the duty cycle of the square waveform on the internal AFG.
+
+        Args:
+            value: The duty cycle percentage within [10.0, 90.0].
+        """
+        self._tekscope.set_if_needed(f"{self.name}:SQUARE:DUTY", value)
+
+    def setup_burst_waveform(self, burst_count: int) -> None:
+        """Prepare the internal AFG for a burst waveform.
+
+        Args:
+            burst_count: The number of wavelengths to be generated.
+        """
+        # set to external as to not burst every millisecond
+        self.set_output_mode("BURST")
+        self.set_burst_count(burst_count)
+
+    def trigger_burst(self) -> None:
+        """Trigger a burst on the internal AFG."""
+        self._tekscope.write(f"{self.name}:BURST:TRIGGER")
diff --git a/src/tm_devices/drivers/pi/signal_generators/__init__.py b/src/tm_devices/drivers/pi/signal_generators/__init__.py
new file mode 100644
index 00000000..0dbec054
--- /dev/null
+++ b/src/tm_devices/drivers/pi/signal_generators/__init__.py
@@ -0,0 +1,1 @@
+"""Signal Generators package init file."""
diff --git a/src/tm_devices/drivers/pi/signal_sources/afgs/__init__.py b/src/tm_devices/drivers/pi/signal_generators/afgs/__init__.py
similarity index 100%
rename from src/tm_devices/drivers/pi/signal_sources/afgs/__init__.py
rename to src/tm_devices/drivers/pi/signal_generators/afgs/__init__.py
diff --git a/src/tm_devices/drivers/pi/signal_generators/afgs/afg.py b/src/tm_devices/drivers/pi/signal_generators/afgs/afg.py
new file mode 100644
index 00000000..25e4c129
--- /dev/null
+++ b/src/tm_devices/drivers/pi/signal_generators/afgs/afg.py
@@ -0,0 +1,471 @@
+"""Base AFG device driver module."""
+
+import re
+
+from abc import ABC, abstractmethod
+from dataclasses import dataclass
+from types import MappingProxyType
+from typing import Dict, Literal, Optional, Tuple, Type, Union
+
+from tm_devices.driver_mixins.signal_generator_mixin import (
+    ExtendedSourceDeviceConstants,
+    ParameterBounds,
+    SourceDeviceConstants,
+)
+from tm_devices.drivers.device import family_base_class
+from tm_devices.drivers.pi._base_afg_source_channel import BaseAFGSourceChannel
+from tm_devices.drivers.pi.signal_generators.signal_generator import SignalGenerator
+from tm_devices.helpers import DeviceTypes, LoadImpedanceAFG
+
+# noinspection PyPep8Naming
+from tm_devices.helpers import ReadOnlyCachedProperty as cached_property  # noqa: N813
+from tm_devices.helpers.enums import (
+    SignalGeneratorFunctionsAFG,
+    SignalGeneratorOutputPathsBase,
+)
+
+
+@dataclass(frozen=True)
+class AFGSourceDeviceConstants(SourceDeviceConstants):
+    """Class to hold source device constants."""
+
+    functions: Type[SignalGeneratorFunctionsAFG] = SignalGeneratorFunctionsAFG
+
+
+@family_base_class
+class AFG(SignalGenerator, ABC):
+    """Base AFG device driver."""
+
+    _DEVICE_TYPE = DeviceTypes.AFG.value
+
+    ################################################################################################
+    # Properties
+    ################################################################################################
+    @cached_property
+    def source_channel(self) -> "MappingProxyType[str, AFGSourceChannel]":
+        """Mapping of channel names to AFGSourceChannel objects."""
+        channel_map: Dict[str, AFGSourceChannel] = {}
+        for channel_name in self.all_channel_names_list:
+            channel_map[channel_name] = AFGSourceChannel(self, channel_name)
+        return MappingProxyType(channel_map)
+
+    @property
+    def source_device_constants(self) -> AFGSourceDeviceConstants:
+        """Return the device constants."""
+        return self._DEVICE_CONSTANTS  # type: ignore[attr-defined]
+
+    @cached_property
+    def total_channels(self) -> int:
+        """Return the total number of channels (all types)."""
+        if match := re.match(r"AFG\d+(\d)", self.model):
+            return int(match.group(1))
+        return 0  # pragma: no cover
+
+    ################################################################################################
+    # Public Methods
+    ################################################################################################
+    def generate_function(  # noqa: PLR0913  # pyright: ignore[reportIncompatibleMethodOverride]
+        self,
+        frequency: float,
+        function: SignalGeneratorFunctionsAFG,
+        amplitude: float,
+        offset: float,
+        channel: str = "all",
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
+        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",
+        duty_cycle: float = 50.0,
+        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
+        symmetry: float = 100.0,
+    ) -> None:
+        """Generate a predefined waveform given the following parameters.
+
+        Args:
+            frequency: The frequency of the waveform to generate.
+            function: The waveform shape to generate.
+            amplitude: The amplitude of the signal to generate.
+            offset: The offset of the signal to generate.
+            channel: The channel name to output the signal from, or 'all'.
+            output_signal_path: Unused in this class.
+            termination: The impedance this device's ``channel`` expects to see at the received end.
+            duty_cycle: The duty cycle percentage within [0.4, 99.6].
+            polarity: The polarity to set the signal to.
+            symmetry: The symmetry to set the signal to, only applicable to certain functions.
+        """
+        del output_signal_path  # Not used in AFGs.
+        self._validate_generated_function(function)
+
+        # Generate the waveform on the given channel
+        for channel_name in self._validate_channels(channel):
+            source_channel = self.source_channel[channel_name]
+            # Temporarily turn off this channel
+            source_channel.set_state(0)
+            source_channel.set_function_properties(
+                frequency=frequency,
+                function=function,
+                amplitude=amplitude,
+                offset=offset,
+                burst_count=0,
+                termination=termination,
+                duty_cycle=duty_cycle,
+                polarity=polarity,
+                symmetry=symmetry,
+            )
+            # Turn on the channel
+            source_channel.set_state(1)
+
+            # Check if burst is enabled on any channel of the AFG
+            burst_state = False
+            for burst_channel in range(1, self.total_channels + 1):
+                if self.query(f"SOURCE{burst_channel}:BURST:STATE?") == "1":
+                    burst_state = True
+            if (
+                self.total_channels > 1  # pylint: disable=comparison-with-callable
+                and function.value != SignalGeneratorFunctionsAFG.DC.value
+                and not burst_state
+            ):
+                # Initiate a phase sync (between CH 1 and CH 2 output waveforms on two channel AFGs)
+                self.source_channel["SOURCE1"].initiate_phase_sync()
+            # Check for system errors
+            self.expect_esr(0)
+
+    def setup_burst(  # noqa: PLR0913  # pyright: ignore[reportIncompatibleMethodOverride]
+        self,
+        frequency: float,
+        function: SignalGeneratorFunctionsAFG,
+        amplitude: float,
+        offset: float,
+        burst_count: int,
+        channel: str = "all",
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
+        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",
+        duty_cycle: float = 50.0,
+        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
+        symmetry: float = 100.0,
+    ) -> None:
+        """Set up the AFG for sending a burst of waveforms given the following parameters.
+
+        Args:
+            frequency: The frequency of the waveform to generate.
+            function: The waveform shape to generate.
+            amplitude: The amplitude of the signal to generate.
+            offset: The offset of the signal to generate.
+            burst_count: The number of wavelengths to be generated.
+            channel: The channel name to output the signal from, or 'all'.
+            output_signal_path: Unused in this class.
+            termination: The impedance this device's ``channel`` expects to see at the received end.
+            duty_cycle: The duty cycle percentage within [0.4, 99.6].
+            polarity: The polarity to set the signal to.
+            symmetry: The symmetry to set the signal to, only applicable to certain functions.
+        """
+        del output_signal_path  # Not used in AFGs.
+        self._validate_generated_function(function)
+        # Generate the waveform on the given channel
+        for channel_name in self._validate_channels(channel):
+            source_channel = self.source_channel[channel_name]
+            source_channel.set_function_properties(
+                frequency=frequency,
+                function=function,
+                amplitude=amplitude,
+                offset=offset,
+                burst_count=burst_count,
+                termination=termination,
+                duty_cycle=duty_cycle,
+                polarity=polarity,
+                symmetry=symmetry,
+            )
+            source_channel.set_state(1)
+
+    def generate_burst(self) -> None:
+        """Generate a burst of waveforms by forcing trigger."""
+        self.write("*TRG")
+        self.expect_esr(0)
+
+    def get_waveform_constraints(  # pyright: ignore[reportIncompatibleMethodOverride]
+        self,
+        function: Optional[SignalGeneratorFunctionsAFG] = None,
+        waveform_length: Optional[int] = None,
+        frequency: Optional[float] = None,
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
+        load_impedance: LoadImpedanceAFG = LoadImpedanceAFG.HIGHZ,
+    ) -> ExtendedSourceDeviceConstants:
+        """Get the constraints that restrict the waveform to certain parameter ranges.
+
+        Args:
+            function: The function that needs to be generated.
+            waveform_length: The length of the waveform if no function or arbitrary is provided.
+            frequency: The frequency of the waveform that needs to be generated.
+            output_signal_path: Unused in this class.
+            load_impedance: The suggested impedance on the source.
+
+        Returns:
+            A Named Tuple containing a set of parameters and their restricted bounds.
+        """
+        del output_signal_path
+
+        if not function:
+            msg = "AFGs must have a function defined."
+            raise ValueError(msg)
+        (
+            amplitude_range,
+            frequency_range,
+            offset_range,
+            sample_rate_range,
+        ) = self._get_series_specific_constraints(
+            function, waveform_length, frequency, load_impedance
+        )
+
+        return ExtendedSourceDeviceConstants(
+            amplitude_range=amplitude_range,
+            frequency_range=frequency_range,
+            offset_range=offset_range,
+            sample_rate_range=sample_rate_range,
+        )
+
+    ################################################################################################
+    # Private Methods
+    ################################################################################################
+    @abstractmethod
+    def _get_series_specific_constraints(
+        self,
+        function: SignalGeneratorFunctionsAFG,
+        waveform_length: Optional[int] = None,
+        frequency: Optional[float] = None,
+        load_impedance: LoadImpedanceAFG = LoadImpedanceAFG.HIGHZ,
+    ) -> Tuple[ParameterBounds, ParameterBounds, ParameterBounds, ParameterBounds]:
+        """Get constraints which are dependent on the model series.
+
+        Args:
+            function: The function that needs to be generated.
+            waveform_length: The length of the waveform if no function or arbitrary is provided.
+            frequency: The frequency of the waveform that needs to be generated.
+            load_impedance: The suggested impedance on the source.
+
+        Returns:
+            Ranges for amplitude, frequency, offset, and sample rate.
+        """
+
+
+class AFGSourceChannel(BaseAFGSourceChannel):
+    """AFG signal source channel composite."""
+
+    ################################################################################################
+    # Magic Methods
+    ################################################################################################
+    def __init__(self, afg: AFG, channel_name: str) -> None:
+        """Create an AFG source channel.
+
+        Args:
+            afg: An AFG.
+            channel_name: The channel name for the AFG source channel.
+        """
+        super().__init__(pi_device=afg, channel_name=channel_name)
+        self._afg = afg
+
+    ################################################################################################
+    # Public Methods
+    ################################################################################################
+    def initiate_phase_sync(self) -> None:
+        """Initialize a phase sync between SOURCE1 and SOURCE2 on the device.
+
+        Does the same operation if called on SOURCE1 or SOURCE2.
+        """
+        self._afg.write(f"{self.name}:PHASE:INITIATE")
+
+    def set_function_properties(  # noqa: PLR0913  # pyright: ignore[reportIncompatibleMethodOverride]
+        self,
+        frequency: float,
+        function: SignalGeneratorFunctionsAFG,
+        amplitude: float,
+        offset: float,
+        burst_count: int = 0,
+        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",
+        duty_cycle: float = 50.0,
+        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
+        symmetry: float = 100.0,
+    ) -> None:
+        """Set the given parameters on the provided source channel.
+
+        Args:
+            frequency: The frequency of the waveform to generate.
+            function: The waveform shape to generate.
+            amplitude: The amplitude of the signal to generate.
+            offset: The offset of the signal to generate.
+            burst_count: The number of wavelengths to be generated.
+            termination: The impedance this device's ``channel`` expects to see at the received end.
+            duty_cycle: The duty cycle percentage within [0.4, 99.6].
+            polarity: The polarity to set the signal to.
+            symmetry: The symmetry to set the signal to, only applicable to certain functions.
+        """
+        # Termination
+        if termination == "FIFTY":
+            self.set_impedance(50)
+        elif termination == "HIGHZ":  # pragma: no cover
+            self.set_impedance("INFINITY")
+        else:  # pragma: no cover
+            # if termination is MAXIMUM or MINIMUM or INFINITY
+            self._afg.set_if_needed(f"OUTPUT{self.num}:IMPEDANCE", termination)
+        # Frequency
+        self.set_frequency(frequency)
+        # Offset
+        self.set_offset(offset, absolute_tolerance=0.01)
+        if function == SignalGeneratorFunctionsAFG.PULSE:
+            # Duty cycle is only valid for pulse
+            self.set_pulse_duty_cycle(duty_cycle)
+        # Polarity
+        self.set_polarity(polarity)
+        # Function
+        if function == SignalGeneratorFunctionsAFG.RAMP:
+            self.set_ramp_symmetry(symmetry)
+        self.set_function(function)
+        # Amplitude, needs to be after termination so that the amplitude is properly adjusted
+        self.set_amplitude(amplitude, absolute_tolerance=0.01)
+        if burst_count > 0:
+            self.setup_burst_waveform(burst_count)
+
+    def set_amplitude(self, value: float, absolute_tolerance: float = 0) -> None:
+        """Set the amplitude on the source channel.
+
+        Args:
+            value: The amplitude value to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+        """
+        self._afg.set_if_needed(
+            f"{self._name}:VOLTAGE:AMPLITUDE",
+            value,
+            tolerance=absolute_tolerance,
+        )
+
+    def set_burst_state(self, value: int) -> None:
+        """Set the burst mode to ON/OFF (1/0) on the source channel.
+
+        Args:
+            value: The burst state.
+        """
+        if value not in [0, 1]:
+            error_message = "Burst state value must be 1 (ON) or 0 (OFF)."
+            raise ValueError(error_message)
+        self._afg.set_if_needed(f"{self.name}:BURST:STATE", value)
+
+    def set_burst_mode(self, value: Literal["TRIGGERED", "GATED"]) -> None:
+        """Set the burst mode on the source channel.
+
+        Args:
+            value: The burst mode (Options: "TRIGGERED", "GATED").
+        """
+        burst_mode_mapping = {
+            "TRIGGERED": "TRIG",
+            "GATED": "GAT",
+        }
+        self._afg.set_if_needed(f"{self.name}:BURST:MODE", burst_mode_mapping[value])
+
+    def set_burst_count(self, value: int) -> None:
+        """Set the number of wavelengths to be generated when the source channel is set to burst.
+
+        Args:
+            value: The number of wavelengths to be generated within [1, 1000000].
+        """
+        self._afg.set_if_needed(f"{self.name}:BURST:NCYCLES", value)
+
+    def set_frequency(self, value: float, absolute_tolerance: float = 0) -> None:
+        """Set the frequency on the source channel.
+
+        Args:
+            value: The frequency value to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+        """
+        self._afg.set_if_needed(
+            f"{self._name}:FREQUENCY:FIXED",
+            value,
+            tolerance=absolute_tolerance,
+        )
+
+    def set_function(self, value: SignalGeneratorFunctionsAFG) -> None:  # pyright: ignore[reportIncompatibleMethodOverride]
+        """Set the function to output on the source channel.
+
+        Args:
+            value: The name of the function to output.
+        """
+        self._afg.set_if_needed(f"{self.name}:FUNCTION", str(value.value))
+
+    def set_impedance(self, value: Union[float, Literal["INFINITY"]]) -> None:
+        """Set the output load impedance on the source channel.
+
+        Args:
+            value: The impedance value to set within [1, 10e3] or "INFINITY".
+        """
+        # The AFG only accepts float values within [1, 10e3] so "INFINITY" must be passed in.
+        if value == "INFINITY":  # pragma: no cover
+            impedance_value = "INF"
+            expected_value = 9.9e37
+        else:
+            impedance_value = value
+            expected_value = value
+        self._afg.set_if_needed(
+            f"OUTPUT{self.num}:IMPEDANCE", impedance_value, expected_value=expected_value
+        )
+
+    def set_offset(self, value: float, absolute_tolerance: float = 0) -> None:
+        """Set the offset on the source channel.
+
+        Args:
+            value: The offset value to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+        """
+        self._afg.set_if_needed(
+            f"{self._name}:VOLTAGE:OFFSET",
+            value,
+            tolerance=absolute_tolerance,
+        )
+
+    def set_polarity(self, value: Literal["NORMAL", "INVERTED"]) -> None:
+        """Set the polarity on the source channel.
+
+        An "INVERTED" polarity inverts the specific output waveform relative to the offset level.
+
+        Args:
+            value: The polarity value to set (Options: "NORMAL", "INVERTED").
+        """
+        polarity_mapping = {
+            "NORMAL": "NORM",
+            "INVERTED": "INV",
+        }
+        self._afg.set_if_needed(f"OUTPUT{self.num}:POLARITY", polarity_mapping[value])
+
+    def set_pulse_duty_cycle(self, value: float) -> None:
+        """Set the duty cycle of the pulse waveform on the source channel.
+
+        Args:
+            value: The duty cycle percentage within [0.4, 99.6].
+        """
+        self._afg.set_if_needed(f"{self.name}:PULSE:DCYCLE", value)
+
+    def set_ramp_symmetry(self, value: float) -> None:
+        """Set the symmetry of the ramp waveform on the source channel.
+
+        Args:
+            value: The symmetry value to set within [0, 100].
+        """
+        self._afg.set_if_needed(f"{self.name}:FUNCTION:RAMP:SYMMETRY", value)
+
+    def set_state(self, value: int) -> None:
+        """Set the output state to ON/OFF (1/0) on the source channel.
+
+        Args:
+            value: The output state.
+        """
+        if value not in [0, 1]:
+            error_message = "Output state value must be 1 (ON) or 0 (OFF)."
+            raise ValueError(error_message)
+        self._afg.set_if_needed(f"OUTPUT{self.num}:STATE", value)
+
+    def setup_burst_waveform(self, burst_count: int) -> None:
+        """Prepare the source channel for a burst waveform.
+
+        Args:
+            burst_count: The number of wavelengths to be generated.
+        """
+        # set to external as to not burst every millisecond
+        self._afg.set_if_needed("TRIGGER:SEQUENCE:SOURCE", "EXT")
+        self.set_burst_state(1)
+        self.set_burst_mode("TRIGGERED")
+        self.set_burst_count(burst_count)
diff --git a/src/tm_devices/drivers/pi/signal_generators/afgs/afg31k.py b/src/tm_devices/drivers/pi/signal_generators/afgs/afg31k.py
new file mode 100644
index 00000000..aec2038a
--- /dev/null
+++ b/src/tm_devices/drivers/pi/signal_generators/afgs/afg31k.py
@@ -0,0 +1,180 @@
+"""AFG31K device driver module."""
+
+from typing import Optional, Tuple
+
+import pyvisa as visa
+
+from tm_devices.drivers.pi.signal_generators.afgs.afg3k import (
+    AFG,
+    AFGSourceDeviceConstants,
+    LoadImpedanceAFG,
+    ParameterBounds,
+    SignalGeneratorFunctionsAFG,
+)
+from tm_devices.helpers import DeviceConfigEntry
+
+
+class AFG31K(AFG):
+    """AFG31K device driver."""
+
+    _DEVICE_CONSTANTS = AFGSourceDeviceConstants(
+        memory_page_size=2,
+        memory_max_record_length=131072,
+        memory_min_record_length=2,
+    )
+
+    _PRE_15_FREQ_THRESHOLD_1 = 60.0e6
+    _PRE_15_FREQ_THRESHOLD_2 = 80.0e6
+    _POST_15_FREQ_THRESHOLD = 200.0e6
+    _16KB_THRESHOLD = 16 * 1024
+
+    ################################################################################################
+    # Magic Methods
+    ################################################################################################
+    def __init__(  # pylint: disable=useless-parent-delegation
+        self,
+        config_entry: DeviceConfigEntry,
+        verbose: bool,
+        visa_resource: visa.resources.MessageBasedResource,
+    ) -> None:
+        """Create an AFG31K device.
+
+        Args:
+            config_entry: A config entry object parsed by the DMConfigParser.
+            verbose: A boolean indicating if verbose output should be printed.
+            visa_resource: The VISA resource object.
+        """
+        # NOTE: This method must be defined for the documentation to properly generate
+        super().__init__(config_entry, verbose, visa_resource)
+
+    ################################################################################################
+    # Properties
+    ################################################################################################
+
+    ################################################################################################
+    # Public Methods
+    ################################################################################################
+
+    ################################################################################################
+    # Private Methods
+    ################################################################################################
+    @staticmethod
+    def _get_driver_specific_multipliers(model_number: str) -> Tuple[float, float, float]:
+        """Get multipliers for frequency dependent on the function.
+
+        Args:
+            model_number: The numbers not pertaining to series or channel count in the name.
+                ie: AFG31 ->25<- 1
+
+        Returns:
+            The necessary values to multiply the frequency by dependent on the function.
+        """
+        # the square wave constraints are difference for model 25
+        square_wave_multiplier = 0.64 if model_number == "25" else 0.8
+
+        # other wave constraints are 1% of the SIN wave constraints, except for models 02 and 05
+        if model_number == "02":
+            other_wave_multiplier = 0.025
+        elif model_number == "05":
+            other_wave_multiplier = 0.016
+        else:
+            other_wave_multiplier = 0.01
+
+        # the arbitrary waveform constraints are half og the SIN wave constraints
+        arbitrary_multiplier = 0.5
+        return square_wave_multiplier, other_wave_multiplier, arbitrary_multiplier
+
+    # pylint: disable=too-many-locals
+    def _get_series_specific_constraints(
+        self,
+        function: SignalGeneratorFunctionsAFG,
+        waveform_length: Optional[int] = None,
+        frequency: Optional[float] = None,
+        load_impedance: LoadImpedanceAFG = LoadImpedanceAFG.HIGHZ,
+    ) -> Tuple[ParameterBounds, ParameterBounds, ParameterBounds, ParameterBounds]:
+        """Get constraints which are dependent on the model series and parameters.
+
+        Args:
+            function: The function that needs to be generated.
+            waveform_length: The length of the waveform if no function or arbitrary is provided.
+            frequency: The frequency of the waveform that needs to be generated.
+            load_impedance: The suggested impedance on the source.
+
+        Returns:
+            Ranges for amplitude, frequency, offset, and sample rate.
+        """
+        # the model number is the third and fourth digit of the model serial, ex. 31(25)2
+        model_number = self.model[5:7]
+
+        load_impedance_multiplier = 1.0 if load_impedance == LoadImpedanceAFG.HIGHZ else 0.5
+
+        freq_base = 10.0e6
+
+        lower_amplitude = 2.0e-3
+        # the higher the frequency, the lower the maximum amplitude can be
+        if model_number in {"02", "05", "10"}:
+            if frequency and frequency <= self._PRE_15_FREQ_THRESHOLD_1:
+                upper_amplitude = 20.0
+            elif frequency and frequency <= self._PRE_15_FREQ_THRESHOLD_2:
+                upper_amplitude = 16.0
+            else:
+                upper_amplitude = 12.0
+            # offset bound is always 10.0
+            offset_bound = 10.0
+        else:
+            upper_amplitude = (
+                10.0 if (frequency and frequency <= self._POST_15_FREQ_THRESHOLD) else 8.0
+            )
+            # offset bound is always 5.0
+            offset_bound = 5.0
+
+        # special case for 02
+        model_multiplier = 2.5 if "02" in model_number else float(model_number)
+
+        sample_rate = 250.0e6
+        # if the waveform length is less than 16 KB, than the AFG can use a higher sample rate
+        if waveform_length and waveform_length < self._16KB_THRESHOLD:
+            if model_number in {"05", "10"}:
+                sample_rate = 1.0e9
+            elif model_number in {"15", "25"}:
+                sample_rate = 2.0e9
+
+        square_mult, other_mult, arb_mult = self._get_driver_specific_multipliers(model_number)
+        low_freq_range = 1.0e-6
+        # the maximum frequency of the SIN wave depends on the model mumber
+        if function.name in {SignalGeneratorFunctionsAFG.SIN.name}:
+            high_freq_range = model_multiplier * freq_base
+        # each waveform has a maximum frequency which is a multiple of SIN wave constraints
+        elif function.name == SignalGeneratorFunctionsAFG.ARBITRARY.name:
+            high_freq_range = model_multiplier * arb_mult * freq_base
+        elif function.name in {
+            SignalGeneratorFunctionsAFG.PULSE.name,
+            SignalGeneratorFunctionsAFG.SQUARE.name,
+        }:
+            high_freq_range = model_multiplier * square_mult * freq_base
+        else:
+            high_freq_range = model_multiplier * other_mult * freq_base
+
+        # load impedance can double the amplitude and offset range
+        amplitude_range = ParameterBounds(
+            lower=lower_amplitude * load_impedance_multiplier,
+            upper=upper_amplitude * load_impedance_multiplier,
+        )
+
+        frequency_range = ParameterBounds(
+            lower=low_freq_range,
+            upper=high_freq_range,
+        )
+
+        offset_range = ParameterBounds(
+            lower=-offset_bound * load_impedance_multiplier,
+            upper=offset_bound * load_impedance_multiplier,
+        )
+
+        sample_rate_range = ParameterBounds(lower=sample_rate, upper=sample_rate)
+
+        return amplitude_range, frequency_range, offset_range, sample_rate_range
+
+    def _reboot(self) -> None:
+        """Reboot the device."""
+        self.write("SYSTem:RESTart")
diff --git a/src/tm_devices/drivers/pi/signal_generators/afgs/afg3k.py b/src/tm_devices/drivers/pi/signal_generators/afgs/afg3k.py
new file mode 100644
index 00000000..a5980563
--- /dev/null
+++ b/src/tm_devices/drivers/pi/signal_generators/afgs/afg3k.py
@@ -0,0 +1,184 @@
+"""AFG3K device driver module."""
+
+from typing import Optional, Tuple
+
+import pyvisa as visa
+
+from tm_devices.commands import AFG3KMixin
+from tm_devices.drivers.pi.signal_generators.afgs.afg import (
+    AFG,
+    AFGSourceDeviceConstants,
+    LoadImpedanceAFG,
+    ParameterBounds,
+    SignalGeneratorFunctionsAFG,
+)
+from tm_devices.helpers import DeviceConfigEntry
+
+
+class AFG3K(AFG3KMixin, AFG):
+    """AFG3K device driver."""
+
+    _DEVICE_CONSTANTS = AFGSourceDeviceConstants(
+        memory_page_size=2,
+        memory_max_record_length=128 * 1024,
+        memory_min_record_length=2,
+    )
+
+    _16KB_THRESHOLD = 16 * 1024
+
+    ################################################################################################
+    # Magic Methods
+    ################################################################################################
+    def __init__(
+        self,
+        config_entry: DeviceConfigEntry,
+        verbose: bool,
+        visa_resource: visa.resources.MessageBasedResource,
+    ) -> None:
+        """Create an AFG3K device.
+
+        Args:
+            config_entry: A config entry object parsed by the DMConfigParser.
+            verbose: A boolean indicating if verbose output should be printed.
+            visa_resource: The VISA resource object.
+        """
+        # NOTE: This method must be defined for the documentation to properly generate
+        super().__init__(config_entry, verbose, visa_resource)
+
+    ################################################################################################
+    # Private Methods
+    ################################################################################################
+    @staticmethod
+    def _get_driver_specific_multipliers(model_number: str) -> Tuple[float, float]:
+        """Get multipliers for frequency dependent on the function.
+
+        Args:
+            model_number: Unused in the AFG3K.
+
+        Returns:
+            The necessary values to multiply the frequency by dependent on the function.
+        """
+        del model_number
+        # the square waveform constraints are half og the SIN wave constraints
+        square_wave_multiplier = 0.5
+        # other wave constraints are 1% of the SIN wave constraints
+        other_wave_multiplier = 0.01
+        return square_wave_multiplier, other_wave_multiplier
+
+    # pylint: disable=too-many-locals
+    def _get_series_specific_constraints(
+        self,
+        function: SignalGeneratorFunctionsAFG,
+        waveform_length: Optional[int] = None,
+        frequency: Optional[float] = None,
+        load_impedance: LoadImpedanceAFG = LoadImpedanceAFG.HIGHZ,
+    ) -> Tuple[ParameterBounds, ParameterBounds, ParameterBounds, ParameterBounds]:
+        """Get constraints which are dependent on the model series and parameters.
+
+        Args:
+            function: The function that needs to be generated.
+            waveform_length: The length of the waveform if no function or arbitrary is provided.
+            frequency: The frequency of the waveform that needs to be generated.
+            load_impedance: The suggested impedance on the source.
+
+        Returns:
+            Ranges for amplitude, frequency, offset, and sample rate.
+        """
+        # the model number is the second and third digit of the model serial, ex. 3(15)1
+        model_number = self.model[4:6]
+
+        lower_base_amplitude = 40.0e-3
+        upper_base_amplitude = 40.0
+
+        load_impedance_multiplier = 1.0 if load_impedance == LoadImpedanceAFG.HIGHZ else 0.5
+
+        offset_bound = 20.0
+
+        base_freq = 10.0e6
+
+        # the amplitude of the waveforms depends on the model number
+        if model_number in {"02", "05"}:
+            lower_amplitude_multiplier = 0.5
+            upper_amplitude_multiplier = 0.5
+
+            offset_multiplier = 0.5
+        elif model_number in {"10", "15"}:
+            lower_amplitude_multiplier = 1
+            upper_amplitude_multiplier = 0.5
+
+            offset_multiplier = 0.5
+        elif model_number == "25":
+            lower_amplitude_multiplier = 2.5
+            upper_amplitude_multiplier = 0.25
+
+            offset_multiplier = 0.25
+        else:
+            lower_amplitude_multiplier = 1
+            upper_amplitude_multiplier = 1
+
+            offset_multiplier = 1
+
+        # if the waveform length is less than 16 KB, than the AFG can use a higher sample rate
+        if (
+            model_number in {"05", "10", "15", "25"}
+            and waveform_length
+            and waveform_length < self._16KB_THRESHOLD
+        ):
+            # upper amplitude multiplier just happens to reflect what the sampling rate should be
+            sample_rate = 0.5e9 / upper_amplitude_multiplier
+        else:
+            sample_rate = 250.0e6
+
+        # model 15 and 25 have threshold where the amplitude is reduced when frequency is too high
+        if model_number in {"15", "25"} and (
+            not frequency or frequency > 50.0e6 / upper_amplitude_multiplier
+        ):
+            upper_amplitude_multiplier *= 0.8
+
+        # special case multiplier for model 02 and model 25
+        model_multiplier = 2.5 if "02" in model_number else min(float(model_number), 24.0)
+
+        square_wave_mult, other_wave_mult = self._get_driver_specific_multipliers(model_number)
+
+        # SIN waves are representative of the model number
+        if function.name in {SignalGeneratorFunctionsAFG.SIN.name}:
+            high_freq_range = base_freq * model_multiplier
+            low_freq_range = 1.0e-6
+        # each waveform has a maximum frequency which is a multiple of SIN wave constraints
+        elif function.name in {
+            SignalGeneratorFunctionsAFG.ARBITRARY.name,
+            SignalGeneratorFunctionsAFG.PULSE.name,
+            SignalGeneratorFunctionsAFG.SQUARE.name,
+        }:
+            high_freq_range = base_freq * model_multiplier * square_wave_mult
+            low_freq_range = 1.0e-3
+        else:
+            high_freq_range = base_freq * model_multiplier * other_wave_mult
+            low_freq_range = 1.0e-6
+
+        # load impedance can double the amplitude and offset range
+        amplitude_range = ParameterBounds(
+            lower=lower_base_amplitude * lower_amplitude_multiplier * load_impedance_multiplier,
+            upper=upper_base_amplitude * upper_amplitude_multiplier * load_impedance_multiplier,
+        )
+
+        frequency_range = ParameterBounds(
+            lower=low_freq_range,
+            upper=high_freq_range,
+        )
+
+        offset_range = ParameterBounds(
+            lower=-offset_bound * offset_multiplier * load_impedance_multiplier,
+            upper=offset_bound * offset_multiplier * load_impedance_multiplier,
+        )
+
+        sample_rate_range = ParameterBounds(lower=sample_rate, upper=sample_rate)
+
+        return amplitude_range, frequency_range, offset_range, sample_rate_range
+
+    def _reboot(self) -> None:
+        """Reboot the device."""
+        # TODO: implement
+        raise NotImplementedError(
+            f"``._reboot()`` is not yet implemented for the {self.__class__.__name__} driver"
+        )
diff --git a/src/tm_devices/drivers/pi/signal_sources/afgs/afg3kb.py b/src/tm_devices/drivers/pi/signal_generators/afgs/afg3kb.py
similarity index 95%
rename from src/tm_devices/drivers/pi/signal_sources/afgs/afg3kb.py
rename to src/tm_devices/drivers/pi/signal_generators/afgs/afg3kb.py
index 7d576cfd..0c4a6d82 100644
--- a/src/tm_devices/drivers/pi/signal_sources/afgs/afg3kb.py
+++ b/src/tm_devices/drivers/pi/signal_generators/afgs/afg3kb.py
@@ -3,7 +3,7 @@
 import pyvisa as visa
 
 from tm_devices.commands import AFG3KBMixin
-from tm_devices.drivers.pi.signal_sources.afgs.afg3k import AFG3K
+from tm_devices.drivers.pi.signal_generators.afgs.afg3k import AFG3K
 from tm_devices.helpers import DeviceConfigEntry
 
 
diff --git a/src/tm_devices/drivers/pi/signal_sources/afgs/afg31k.py b/src/tm_devices/drivers/pi/signal_generators/afgs/afg3kc.py
similarity index 55%
rename from src/tm_devices/drivers/pi/signal_sources/afgs/afg31k.py
rename to src/tm_devices/drivers/pi/signal_generators/afgs/afg3kc.py
index 7ad1675e..781515c8 100644
--- a/src/tm_devices/drivers/pi/signal_sources/afgs/afg31k.py
+++ b/src/tm_devices/drivers/pi/signal_generators/afgs/afg3kc.py
@@ -1,30 +1,29 @@
 """AFG31K device driver module."""
 
+from typing import Tuple
+
 import pyvisa as visa
 
-from tm_devices.drivers.pi.signal_sources.afgs.afg import AFG, AFGSourceDeviceConstants
+from tm_devices.commands import AFG3KCMixin
+from tm_devices.drivers.pi.signal_generators.afgs.afg3k import (
+    AFG3K,
+)
 from tm_devices.helpers import DeviceConfigEntry
 
 
-class AFG31K(AFG):
-    """AFG31K device driver."""
-
-    _DEVICE_CONSTANTS = AFGSourceDeviceConstants(
-        memory_page_size=2,
-        memory_max_record_length=131072,
-        memory_min_record_length=2,
-    )
+class AFG3KC(AFG3KCMixin, AFG3K):  # pyright: ignore[reportIncompatibleMethodOverride]
+    """AFG3KC device driver."""
 
     ################################################################################################
     # Magic Methods
     ################################################################################################
-    def __init__(  # pylint: disable=useless-parent-delegation
+    def __init__(
         self,
         config_entry: DeviceConfigEntry,
         verbose: bool,
         visa_resource: visa.resources.MessageBasedResource,
     ) -> None:
-        """Create an AFG31K device.
+        """Create an AFG3KC device.
 
         Args:
             config_entry: A config entry object parsed by the DMConfigParser.
@@ -45,6 +44,25 @@ def __init__(  # pylint: disable=useless-parent-delegation
     ################################################################################################
     # Private Methods
     ################################################################################################
-    def _reboot(self) -> None:
-        """Reboot the device."""
-        self.write("SYSTem:RESTart")
+    @staticmethod
+    def _get_driver_specific_multipliers(model_number: str) -> Tuple[float, float]:
+        """Get multipliers for frequency dependent on the function.
+
+        Args:
+            model_number: The numbers not pertaining to series or channel count in the name.
+                ie: AFG3 ->25<- 1C
+
+        Returns:
+            The necessary values to multiply the frequency by dependent on the function.
+        """
+        # the square wave and other wave constraints are difference for model 02 and 05
+        if model_number == "02":
+            square_wave_multiplier = 1
+            other_wave_multiplier = 0.02
+        elif model_number == "05":
+            square_wave_multiplier = 0.8
+            other_wave_multiplier = 0.016
+        else:
+            square_wave_multiplier = 0.5
+            other_wave_multiplier = 0.01
+        return square_wave_multiplier, other_wave_multiplier
diff --git a/src/tm_devices/drivers/pi/signal_sources/awgs/__init__.py b/src/tm_devices/drivers/pi/signal_generators/awgs/__init__.py
similarity index 100%
rename from src/tm_devices/drivers/pi/signal_sources/awgs/__init__.py
rename to src/tm_devices/drivers/pi/signal_generators/awgs/__init__.py
diff --git a/src/tm_devices/drivers/pi/signal_generators/awgs/awg.py b/src/tm_devices/drivers/pi/signal_generators/awgs/awg.py
new file mode 100644
index 00000000..2e0f11a7
--- /dev/null
+++ b/src/tm_devices/drivers/pi/signal_generators/awgs/awg.py
@@ -0,0 +1,463 @@
+"""Base AWG device driver module."""
+
+from abc import ABC, abstractmethod
+from dataclasses import dataclass
+from types import MappingProxyType
+from typing import ClassVar, Dict, List, Literal, Optional, Tuple, Type
+
+from tm_devices.driver_mixins.class_extension_mixin import ExtendableMixin
+from tm_devices.driver_mixins.signal_generator_mixin import (
+    ExtendedSourceDeviceConstants,
+    ParameterBounds,
+    SourceDeviceConstants,
+)
+from tm_devices.drivers.device import family_base_class
+from tm_devices.drivers.pi._base_source_channel import BaseSourceChannel
+from tm_devices.drivers.pi.signal_generators.signal_generator import SignalGenerator
+from tm_devices.helpers import DeviceTypes, LoadImpedanceAFG
+
+# noinspection PyPep8Naming
+from tm_devices.helpers import ReadOnlyCachedProperty as cached_property  # noqa: N813
+from tm_devices.helpers.enums import (
+    SignalGeneratorFunctionsAWG,
+    SignalGeneratorOutputPathsBase,
+    SignalGeneratorOutputPathsNon5200,
+)
+
+
+@dataclass(frozen=True)
+class AWGSourceDeviceConstants(SourceDeviceConstants):
+    """Class to hold source device constants."""
+
+    functions: Type[SignalGeneratorFunctionsAWG] = SignalGeneratorFunctionsAWG
+
+
+class AWG(SignalGenerator, ABC):
+    """Base AWG device driver."""
+
+    OutputSignalPath = SignalGeneratorOutputPathsNon5200
+
+    _DEVICE_TYPE = DeviceTypes.AWG.value
+    # The record lengths for the predefined SIN waveforms.
+    _PRE_DEFINED_SIGNAL_RECORD_LENGTH_SIN: ClassVar[List[int]] = [3600, 1000, 960, 360, 100, 36, 10]
+    # The record lengths for the predefined CLOCK waveforms.
+    _PRE_DEFINED_SIGNAL_RECORD_LENGTH_CLOCK: ClassVar[List[int]] = [960]
+    # All predefined waveforms have these record lengths.
+    _PRE_DEFINED_SIGNAL_RECORD_LENGTH_DEFAULT: ClassVar[List[int]] = [1000, 960, 100, 10]
+
+    ################################################################################################
+    # Magic Methods
+    ################################################################################################
+
+    ################################################################################################
+    # Properties
+    ################################################################################################
+    @cached_property
+    def source_channel(self) -> "MappingProxyType[str, AWGSourceChannel]":  # pragma: no cover
+        """Mapping of channel names to AWGSourceChannel objects.
+
+        Returns:
+            The dictionary of channel string to the source channel object.
+        """
+        channel_map: Dict[str, AWGSourceChannel] = {}
+        for channel_name in self.all_channel_names_list:
+            channel_map[channel_name] = AWGSourceChannel(self, channel_name)
+        return MappingProxyType(channel_map)
+
+    @property
+    def source_device_constants(self) -> AWGSourceDeviceConstants:
+        """Return the device constants."""
+        return self._DEVICE_CONSTANTS  # type: ignore[attr-defined]
+
+    @cached_property
+    def total_channels(self) -> int:
+        """Return the total number of channels (all types)."""
+        return int(self.query("AWGControl:CONFigure:CNUMber?", verbose=False))
+
+    ################################################################################################
+    # Public Methods
+    ################################################################################################
+    def load_waveform(self, wfm_name: str, waveform_file_path: str, wfm_type: str) -> None:
+        """Load a waveform into the memory of the AWG.
+
+        Args:
+            wfm_name: The name to set the loaded waveform to on the AWG.
+            waveform_file_path: The path to the waveform.
+            wfm_type: The type of the waveform to import.
+        """
+        if not waveform_file_path.startswith('"'):
+            waveform_file_path = '"' + waveform_file_path
+        if not waveform_file_path.endswith('"'):
+            waveform_file_path += '"'
+        self.write(f'MMEMory:IMPort "{wfm_name}", {waveform_file_path}, {wfm_type}')
+        self._ieee_cmds.opc()
+
+    def generate_function(  # noqa: PLR0913  # pyright: ignore[reportIncompatibleMethodOverride]
+        self,
+        frequency: float,
+        function: SignalGeneratorFunctionsAWG,
+        amplitude: float,
+        offset: float,
+        channel: str = "all",
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
+        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",
+        duty_cycle: float = 50.0,
+        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
+        symmetry: float = 50.0,
+    ) -> None:
+        """Generate a predefined waveform given the following parameters.
+
+        Args:
+            frequency: The frequency of the waveform to generate.
+            function: The waveform shape to generate.
+            amplitude: The amplitude of the signal to generate.
+            offset: The offset of the signal to generate.
+            channel: The channel name to output the signal from, or 'all'.
+            output_signal_path: The output signal path of the specified channel.
+            termination: The impedance this device's ``channel`` expects to see at the received end.
+            duty_cycle: The duty cycle percentage within [10.0, 90.0].
+            polarity: The polarity to set the signal to.
+            symmetry: The symmetry to set the signal to, only applicable to certain functions.
+        """
+        predefined_name, needed_sample_rate = self._get_predefined_waveform_name(
+            frequency, function, output_signal_path, symmetry
+        )
+        self.generate_waveform(
+            needed_sample_rate=needed_sample_rate,
+            waveform_name=predefined_name,
+            amplitude=amplitude,
+            offset=offset,
+            channel=channel,
+            output_signal_path=output_signal_path,
+            termination=termination,
+            duty_cycle=duty_cycle,
+            polarity=polarity,
+            symmetry=symmetry,
+        )
+
+    def generate_waveform(  # noqa: PLR0913
+        self,
+        needed_sample_rate: float,
+        waveform_name: str,
+        amplitude: float,
+        offset: float,
+        channel: str = "all",
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
+        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",  # noqa: ARG002
+        duty_cycle: float = 50.0,  # noqa: ARG002
+        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",  # noqa: ARG002
+        symmetry: float = 50.0,  # noqa: ARG002
+    ) -> None:
+        """Generate a waveform given the following parameters.
+
+        Args:
+            needed_sample_rate: The required sample rate.
+            waveform_name: The name of the waveform to generate.
+            amplitude: The amplitude of the signal to generate.
+            offset: The offset of the signal to generate.
+            channel: The channel name to output the signal from, or 'all'.
+            output_signal_path: The output signal path of the specified channel.
+            termination: The impedance this device's ``channel`` expects to see at the received end.
+            duty_cycle: The duty cycle percentage within [10.0, 90.0].
+            polarity: The polarity to set the signal to.
+            symmetry: The symmetry to set the signal to, only applicable to certain functions.
+        """
+        for channel_name in self._validate_channels(channel):
+            self.set_sample_rate(needed_sample_rate)
+            source_channel = self.source_channel[channel_name]
+            source_channel.set_state(0)
+            source_channel.set_waveform_properties(
+                output_signal_path=output_signal_path,
+                waveform_name=waveform_name,
+                amplitude=amplitude,
+                offset=offset,
+            )
+            source_channel.set_state(1)
+        self.write("AWGCONTROL:RUN")
+        self.expect_esr(0)
+
+    def setup_burst(  # noqa: PLR0913  # pyright: ignore[reportIncompatibleMethodOverride]
+        self,
+        frequency: float,
+        function: SignalGeneratorFunctionsAWG,
+        amplitude: float,
+        offset: float,
+        burst_count: int,
+        channel: str = "all",
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
+        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",
+        duty_cycle: float = 50.0,
+        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
+        symmetry: float = 50.0,
+    ) -> None:
+        """Set up the AWG for sending a burst of waveforms given the following parameters.
+
+        Args:
+            frequency: The frequency of the waveform to generate.
+            function: The waveform shape to generate.
+            amplitude: The amplitude of the signal to generate.
+            offset: The offset of the signal to generate.
+            burst_count: The number of wavelengths to be generated.
+            channel: The channel name to output the signal from, or 'all'.
+            output_signal_path: The output signal path of the specified channel.
+            termination: The impedance this device's ``channel`` expects to see at the received end.
+            duty_cycle: The duty cycle percentage within [10.0, 90.0].
+            polarity: The polarity to set the signal to.
+            symmetry: The symmetry to set the signal to, only applicable to certain functions.
+        """
+        raise NotImplementedError
+
+    def generate_burst(self) -> None:
+        """Generate a burst of waveforms by forcing trigger."""
+        raise NotImplementedError
+
+    def get_waveform_constraints(  # pyright: ignore[reportIncompatibleMethodOverride]
+        self,
+        function: Optional[SignalGeneratorFunctionsAWG] = None,
+        waveform_length: Optional[int] = None,
+        frequency: Optional[float] = None,
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
+        load_impedance: LoadImpedanceAFG = LoadImpedanceAFG.HIGHZ,
+    ) -> ExtendedSourceDeviceConstants:
+        """Get the constraints that restrict the waveform to certain parameter ranges.
+
+        Args:
+            function: The function that needs to be generated.
+            waveform_length: The length of the waveform if no function or arbitrary is provided.
+            frequency: Unused in this class.
+            output_signal_path: The output signal path that was set on the channel.
+            load_impedance: Unused in this class.
+
+        Returns:
+            A Named Tuple containing a set of parameters and their restricted bounds.
+        """
+        del frequency, load_impedance
+
+        amplitude_range, offset_range, sample_rate_range = self._get_series_specific_constraints(
+            output_signal_path,
+        )
+
+        if function and not waveform_length:
+            # use the min and max waveform length dependent on the predefined files
+            func_sample_rate_lookup: Dict[str, ParameterBounds] = {
+                SignalGeneratorFunctionsAWG.SIN.name: ParameterBounds(lower=10, upper=3600),
+                SignalGeneratorFunctionsAWG.CLOCK.name: ParameterBounds(lower=960, upper=960),
+                SignalGeneratorFunctionsAWG.SQUARE.name: ParameterBounds(lower=10, upper=1000),
+                SignalGeneratorFunctionsAWG.RAMP.name: ParameterBounds(lower=10, upper=1000),
+                SignalGeneratorFunctionsAWG.TRIANGLE.name: ParameterBounds(lower=10, upper=1000),
+                SignalGeneratorFunctionsAWG.DC.name: ParameterBounds(lower=1000, upper=1000),
+            }
+            slowest_frequency = (
+                sample_rate_range.lower / func_sample_rate_lookup[function.name].upper
+            )
+            fastest_frequency = (
+                sample_rate_range.upper / func_sample_rate_lookup[function.name].lower
+            )
+        elif waveform_length and not function:
+            slowest_frequency = sample_rate_range.lower / waveform_length
+            fastest_frequency = sample_rate_range.upper / waveform_length
+        else:
+            msg = "AWG Constraints require exclusively function or waveform_length."
+            raise ValueError(msg)
+
+        frequency_range = ParameterBounds(
+            lower=slowest_frequency,
+            upper=fastest_frequency,
+        )
+        return ExtendedSourceDeviceConstants(
+            amplitude_range=amplitude_range,
+            offset_range=offset_range,
+            frequency_range=frequency_range,
+            sample_rate_range=sample_rate_range,
+        )
+
+    def set_sample_rate(self, value: float, absolute_tolerance: Optional[float] = None) -> None:
+        """Set the rate at which samples are generated/transmitted.
+
+        Args:
+            value: The sample rate to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+                                Default value is 0.1% of the provided value.
+        """
+        if absolute_tolerance is None:
+            # Default the absolute tolerance to 0.1% of the provided frequency value
+            # due to 32 bit rounding.
+            absolute_tolerance = value * 0.001
+        self.set_if_needed("SOURCE1:FREQUENCY", value, tolerance=absolute_tolerance)
+
+    ################################################################################################
+    # Private Methods
+    ################################################################################################
+    def _get_predefined_waveform_name(
+        self,
+        frequency: float,
+        function: SignalGeneratorFunctionsAWG,
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase],
+        symmetry: Optional[float] = 50.0,
+    ) -> Tuple[str, float]:
+        """Get the predefined waveform name for the provided function.
+
+        Args:
+            frequency: The frequency of the waveform to generate.
+            function: The waveform shape to generate.
+            output_signal_path: The output signal path of the specified channel.
+            symmetry: The symmetry to set the signal to, only applicable to certain functions.
+
+        Returns:
+            A tuple containing the best waveform from the predefined files and the sample rate.
+        """
+        if function == function.RAMP and symmetry == 50:  # noqa: PLR2004
+            function = function.TRIANGLE
+        if function != SignalGeneratorFunctionsAWG.DC and not function.value.startswith("*"):
+            device_constraints = self.get_waveform_constraints(
+                function=function, frequency=frequency, output_signal_path=output_signal_path
+            )
+            if function == SignalGeneratorFunctionsAWG.SIN:
+                premade_signal_rl = self._PRE_DEFINED_SIGNAL_RECORD_LENGTH_SIN
+            elif function == SignalGeneratorFunctionsAWG.CLOCK:
+                premade_signal_rl = self._PRE_DEFINED_SIGNAL_RECORD_LENGTH_CLOCK
+            else:
+                premade_signal_rl = self._PRE_DEFINED_SIGNAL_RECORD_LENGTH_DEFAULT
+            # for each of these three records lengths
+            for record_length in premade_signal_rl:  # pragma: no cover
+                needed_sample_rate = frequency * record_length
+                # try for the highest record length that can generate the frequency
+                if (
+                    device_constraints.sample_rate_range
+                    and device_constraints.sample_rate_range.lower
+                    <= needed_sample_rate
+                    <= device_constraints.sample_rate_range.upper
+                ):
+                    predefined_name = f"*{function.value.title()}{record_length}"
+                    break
+            else:
+                # This clause is skipped if break is called in for loop.
+                error_message = (
+                    f"Unable to generate {function.value} waveform with provided frequency of "
+                    f"{frequency} Hz."
+                )
+                raise ValueError(error_message)
+        else:
+            predefined_name = "*DC"
+            needed_sample_rate = 15000000.0
+
+        return predefined_name, needed_sample_rate
+
+    @abstractmethod
+    def _get_series_specific_constraints(
+        self,
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase],
+    ) -> Tuple[ParameterBounds, ParameterBounds, ParameterBounds]:
+        raise NotImplementedError
+
+    def _reboot(self) -> None:
+        """Reboot the device."""
+        raise NotImplementedError(
+            f"``._reboot()`` is not yet implemented for the {self.__class__.__name__} driver"
+        )
+
+
+@family_base_class
+class AWGSourceChannel(BaseSourceChannel, ExtendableMixin):
+    """AWG signal source channel composite."""
+
+    ################################################################################################
+    # Magic Methods
+    ################################################################################################
+    def __init__(self, awg: AWG, channel_name: str) -> None:
+        """Create an AWG source channel.
+
+        Args:
+            awg: An AWG.
+            channel_name: The channel name for the AWG source channel.
+        """
+        super().__init__(pi_device=awg, channel_name=channel_name)
+        self._awg = awg
+
+    ################################################################################################
+    # Public Methods
+    ################################################################################################
+    def set_waveform_properties(
+        self,
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase],
+        waveform_name: str,
+        amplitude: float,
+        offset: float,
+    ) -> None:
+        """Set the given parameters on the provided source channel.
+
+        Args:
+            output_signal_path: The output signal path of the specified channel.
+            waveform_name: The name of the waveform from the waveform list to generate.
+            amplitude: The amplitude of the signal to generate.
+            offset: The offset of the signal to generate.
+        """
+        self.set_amplitude(amplitude)
+        self.set_output_signal_path(output_signal_path)
+        self.set_offset(offset)
+        self.load_waveform(waveform_name)
+
+    def set_amplitude(self, value: float, absolute_tolerance: float = 0) -> None:
+        """Set the amplitude on the source channel.
+
+        Args:
+            value: The amplitude value to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+        """
+        self._awg.set_if_needed(
+            f"{self.name}:VOLTAGE:AMPLITUDE",
+            value,
+            tolerance=absolute_tolerance,
+        )
+
+    def set_offset(self, value: float, absolute_tolerance: float = 0) -> None:
+        """Set the offset on the source channel.
+
+        Args:
+            value: The offset value to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+        """
+        output_path = self._awg.query(f"OUTPUT{self.num}:PATH?")
+        if output_path == self._awg.OutputSignalPath.DCA.value:
+            self._awg.set_if_needed(
+                f"{self.name}:VOLTAGE:OFFSET",
+                value,
+                tolerance=absolute_tolerance,
+            )
+        elif value:
+            # No error is raised when 0 is the offset value and the output signal path is
+            # in a state where offset cannot be set.
+            offset_error = (
+                f"The offset can only be set with an output signal path of "
+                f"{self._awg.OutputSignalPath.DCA.value}."
+            )
+            raise ValueError(offset_error)
+
+    def set_state(self, value: int) -> None:
+        """Set the output state to ON/OFF (1/0) on the source channel.
+
+        Args:
+            value: The output state.
+        """
+        if value not in [0, 1]:
+            error_message = "Output state value must be 1 (ON) or 0 (OFF)."
+            raise ValueError(error_message)
+        self._awg.set_if_needed(f"OUTPUT{self.num}:STATE", value)
+
+    def set_output_signal_path(
+        self, value: Optional[SignalGeneratorOutputPathsBase] = None
+    ) -> None:
+        """Set the output signal path on the source channel.
+
+        Args:
+            value: The output signal path.
+        """
+        raise NotImplementedError
+
+    def load_waveform(self, waveform_name: str) -> None:
+        """Load in a waveform from the waveform list to the source channel.
+
+        Args:
+            waveform_name: The name of the waveform to load.
+        """
+        self._awg.set_if_needed(f"{self.name}:WAVEFORM", f'"{waveform_name}"', allow_empty=True)
diff --git a/src/tm_devices/drivers/pi/signal_generators/awgs/awg5200.py b/src/tm_devices/drivers/pi/signal_generators/awgs/awg5200.py
new file mode 100644
index 00000000..b4af9756
--- /dev/null
+++ b/src/tm_devices/drivers/pi/signal_generators/awgs/awg5200.py
@@ -0,0 +1,335 @@
+"""AWG5200 device driver module."""
+
+from pathlib import Path
+from types import MappingProxyType
+from typing import cast, Dict, Literal, Optional, Tuple
+
+import pyvisa as visa
+
+from tm_devices.commands import AWG5200Mixin
+from tm_devices.drivers.device import family_base_class
+from tm_devices.drivers.pi.signal_generators.awgs.awg import (
+    AWG,
+    AWGSourceChannel,
+    AWGSourceDeviceConstants,
+    ParameterBounds,
+)
+from tm_devices.helpers import (
+    DeviceConfigEntry,
+    SASSetWaveformFileTypes,
+)
+from tm_devices.helpers import ReadOnlyCachedProperty as cached_property  # noqa: N813
+from tm_devices.helpers.enums import (
+    SignalGeneratorFunctionsAWG,
+    SignalGeneratorOutputPaths5200,
+    SignalGeneratorOutputPathsBase,
+)
+
+
+@family_base_class
+class AWG5200(AWG5200Mixin, AWG):
+    """AWG5200 device driver."""
+
+    OutputSignalPath = SignalGeneratorOutputPaths5200
+
+    _DEVICE_CONSTANTS = AWGSourceDeviceConstants(
+        memory_page_size=1,
+        memory_max_record_length=16200000,
+        memory_min_record_length=1,
+    )
+    sample_waveform_set_file = (
+        "C:\\Program Files\\Tektronix\\AWG5200\\Samples\\AWG5k7k Predefined Waveforms.awgx"
+    )
+
+    ################################################################################################
+    # Magic Methods
+    ################################################################################################
+    def __init__(
+        self,
+        config_entry: DeviceConfigEntry,
+        verbose: bool,
+        visa_resource: visa.resources.MessageBasedResource,
+    ) -> None:
+        """Create an AWG5200 device.
+
+        Args:
+            config_entry: A config entry object parsed by the DMConfigParser.
+            verbose: A boolean indicating if verbose output should be printed.
+            visa_resource: The VISA resource object.
+        """
+        # NOTE: This method must be defined for the documentation to properly generate
+        super().__init__(config_entry, verbose, visa_resource)
+
+    ################################################################################################
+    # Properties
+    ################################################################################################
+    @cached_property
+    def source_channel(self) -> "MappingProxyType[str, AWGSourceChannel]":
+        """Mapping of channel names to AWG5200SourceChannel objects."""
+        channel_map: Dict[str, AWG5200SourceChannel] = {}
+        for channel_name in self.all_channel_names_list:
+            channel_map[channel_name] = AWG5200SourceChannel(self, channel_name)
+        return MappingProxyType(channel_map)
+
+    ################################################################################################
+    # Public Methods
+    ################################################################################################
+    def load_waveform_set(self, waveform_set_file: Optional[str] = None) -> None:
+        """Load a waveform set into the memory of the AWG.
+
+        Arguments:
+            waveform_set_file: The waveform set file to load
+                (The default is defined in the ``sample_wave_file`` attribute).
+        """
+        self._load_waveform_or_set(waveform_set_file=waveform_set_file, waveform_name=None)
+
+    def load_waveform_from_set(
+        self,
+        waveform_name: str,
+        waveform_set_file: Optional[str] = None,
+    ) -> None:
+        """Load in a specific waveform from a waveform set into the memory of the AWG.
+
+        Arguments:
+            waveform_name: The waveform name to load from the waveform set file.
+            waveform_set_file: The waveform set file to load
+                (The default is defined in the ``sample_wave_file`` attribute).
+        """
+        self._load_waveform_or_set(waveform_set_file=waveform_set_file, waveform_name=waveform_name)
+
+    def generate_function(  # noqa: PLR0913
+        self,
+        frequency: float,
+        function: SignalGeneratorFunctionsAWG,
+        amplitude: float,
+        offset: float,
+        channel: str = "all",
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
+        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",
+        duty_cycle: float = 50.0,
+        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
+        symmetry: float = 50.0,
+    ) -> None:
+        """Generate a predefined waveform given the following parameters.
+
+        Args:
+            frequency: The frequency of the waveform to generate.
+            function: The waveform shape to generate.
+            amplitude: The amplitude of the signal to generate.
+            offset: The offset of the signal to generate.
+            channel: The channel name to output the signal from, or 'all'.
+            output_signal_path: The output signal path of the specified channel.
+            termination: The impedance this device's ``channel`` expects to see at the received end.
+            duty_cycle: The duty cycle percentage within [10.0, 90.0].
+            polarity: The polarity to set the signal to.
+            symmetry: The symmetry to set the signal to, only applicable to certain functions.
+        """
+        predefined_name, needed_sample_rate = self._get_predefined_waveform_name(
+            frequency, function, output_signal_path, symmetry
+        )
+        self.generate_waveform(
+            needed_sample_rate=needed_sample_rate,
+            waveform_name=predefined_name,
+            amplitude=amplitude,
+            offset=offset,
+            channel=channel,
+            output_signal_path=output_signal_path,
+            termination=termination,
+            duty_cycle=duty_cycle,
+            polarity=polarity,
+            symmetry=symmetry,
+        )
+
+    def generate_waveform(  # noqa: PLR0913
+        self,
+        needed_sample_rate: float,
+        waveform_name: str,
+        amplitude: float,
+        offset: float,
+        channel: str = "all",
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
+        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",  # noqa: ARG002
+        duty_cycle: float = 50.0,  # noqa: ARG002
+        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",  # noqa: ARG002
+        symmetry: float = 50.0,  # noqa: ARG002
+    ) -> None:
+        """Generate a waveform given the following parameters.
+
+        Args:
+            needed_sample_rate: The required sample rate.
+            waveform_name: The name of the waveform to generate.
+            amplitude: The amplitude of the signal to generate.
+            offset: The offset of the signal to generate.
+            channel: The channel name to output the signal from, or 'all'.
+            output_signal_path: The output signal path of the specified channel.
+            termination: The impedance this device's ``channel`` expects to see at the received end.
+            duty_cycle: The duty cycle percentage within [10.0, 90.0].
+            polarity: The polarity to set the signal to.
+            symmetry: The symmetry to set the signal to, only applicable to certain functions.
+        """
+        # If the waveform is not in the waveform list, load in the waveform set defined at
+        # self.sample_waveform_set_file
+        if waveform_name not in self.query("WLISt:LIST?", allow_empty=True).replace('"', "").split(
+            ","
+        ):
+            self.load_waveform_set()
+        for channel_name in self._validate_channels(channel):
+            self.set_sample_rate(needed_sample_rate)
+            source_channel = cast(AWG5200SourceChannel, self.source_channel[channel_name])
+            # turn channel off
+            self.set_and_check(f"OUTPUT{source_channel.num}:STATE", "0")
+            source_channel.set_waveform_properties(
+                output_signal_path=output_signal_path,
+                waveform_name=waveform_name,
+                amplitude=amplitude,
+                offset=offset,
+            )
+            self.set_if_needed(f"OUTPUT{source_channel.num}:STATE", "1")
+        # this is an overlapping command
+        self.write("AWGCONTROL:RUN")
+        self.ieee_cmds.opc()
+        # we expect no errors
+        self.expect_esr(0)
+
+    def set_sample_rate(self, value: float, absolute_tolerance: Optional[float] = None) -> None:
+        """Set the rate at which samples are generated/transmitted.
+
+        Args:
+            value: The sample rate to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+                                Default value is 0.1% of the provided value.
+        """
+        if absolute_tolerance is None:
+            # Default the absolute tolerance to 0.1% of the provided frequency value
+            # due to 32 bit rounding.
+            absolute_tolerance = value * 0.001
+        # This is an overlapping command for the AWG5200, and will overlap the
+        # next command and/or overlap the previous if it is still running.
+        self.set_if_needed("CLOCK:SRATE", value, verify_value=False, tolerance=absolute_tolerance)
+
+    ################################################################################################
+    # Private Methods
+    ################################################################################################
+    def _get_series_specific_constraints(
+        self,
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase],
+    ) -> Tuple[ParameterBounds, ParameterBounds, ParameterBounds]:
+        """Get constraints which are dependent on the model series and parameters.
+
+        Args:
+            output_signal_path: The signal path that the output is taking.
+
+        Returns:
+            Ranges for amplitude, offset, and sample rate.
+        """
+        if not output_signal_path:
+            output_signal_path = self.OutputSignalPath.DCHB
+        # Direct Current High Bandwidth with the DC options has 1.5 V amplitude
+        if "DC" in self.opt_string and output_signal_path == self.OutputSignalPath.DCHB:
+            amplitude_range = ParameterBounds(lower=25.0e-3, upper=1.5)
+        # Direct Current High Voltage path connected has an even higher amplitude, 5 V
+        elif output_signal_path == self.OutputSignalPath.DCHV:
+            amplitude_range = ParameterBounds(lower=10.0e-3, upper=5.0)
+        # Else, the upper bound is 750 mV
+        else:
+            amplitude_range = ParameterBounds(lower=25.0e-3, upper=750.0e-3)
+
+        offset_range = ParameterBounds(lower=-2.0, upper=2.0)
+        # option is the sample rate in hundreds of Mega Hertz
+        max_sample_rate = 25.0 if "25" in self.opt_string else 50.0
+        # option 50 would have 5.0 GHz, option 2.5 would have 2.5 GHz
+        sample_rate_range = ParameterBounds(lower=300.0, upper=max_sample_rate * 100.0e6)
+
+        return amplitude_range, offset_range, sample_rate_range
+
+    def _load_waveform_or_set(
+        self,
+        waveform_set_file: Optional[str] = None,
+        waveform_name: Optional[str] = None,
+    ) -> None:
+        """Load in a waveform set or a specific waveform from a waveform set into memory.
+
+        Arguments:
+            waveform_set_file: The waveform set file to load.
+                (The default is defined in the ``sample_wave_file`` attribute).
+            waveform_name: The waveform name to load from the waveform set file.
+        """
+        # This function is identical to the one in the AWG70KA.
+        if not waveform_set_file:
+            waveform_set_file = self.sample_waveform_set_file
+        waveform_file_type = Path(waveform_set_file).suffix.lower()
+        try:
+            SASSetWaveformFileTypes(waveform_file_type)
+            if not waveform_name:
+                self.write(f'MMEMORY:OPEN:SASSET "{waveform_set_file}"', opc=True)
+            else:
+                self.write(
+                    f'MMEMORY:OPEN:SASSET:WAVEFORM "{waveform_set_file}", "{waveform_name}"',
+                    opc=True,
+                )
+        except ValueError as err:
+            waveform_file_type_error = (
+                f"{waveform_file_type} is an invalid waveform file extension."
+            )
+            raise ValueError(waveform_file_type_error) from err
+
+        self.expect_esr(0)
+
+
+class AWG5200SourceChannel(AWGSourceChannel):
+    """AWG5200 signal source channel composite."""
+
+    ################################################################################################
+    # Magic Methods
+    ################################################################################################
+    def __init__(self, awg: AWG5200, channel_name: str) -> None:
+        """Create an AWG5200 source channel.
+
+        Args:
+            awg: An AWG.
+            channel_name: The channel name for the AWG source channel.
+        """
+        super().__init__(awg=awg, channel_name=channel_name)
+        self._awg = awg
+
+    ################################################################################################
+    # Public Methods
+    ################################################################################################
+    def load_waveform(self, waveform_name: str) -> None:
+        """Load in a waveform from the waveform list to the source channel.
+
+        Args:
+            waveform_name: The name of the waveform to load.
+        """
+        self._awg.ieee_cmds.opc()
+        self._awg.set_if_needed(f"{self.name}:WAVEFORM", f'"{waveform_name}"', allow_empty=True)
+
+    def set_offset(self, value: float, absolute_tolerance: float = 0) -> None:
+        """Set the offset on the source channel.
+
+        Args:
+            value: The offset value to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+        """
+        self._awg.set_if_needed(
+            f"{self.name}:VOLTAGE:OFFSET",
+            value,
+            tolerance=absolute_tolerance,
+        )
+
+    def set_output_signal_path(
+        self, value: Optional[SignalGeneratorOutputPathsBase] = None
+    ) -> None:
+        """Set the output signal path on the source channel.
+
+        Args:
+            value: The output signal path.
+        """
+        if not value:
+            value = self._awg.OutputSignalPath.DCHB
+        if value not in self._awg.OutputSignalPath:
+            output_signal_path_error = (
+                f"{value.value} is an invalid output signal path for {self._awg.model}."
+            )
+            raise ValueError(output_signal_path_error)
+        self._awg.set_if_needed(f"OUTPUT{self.num}:PATH", value.value)
diff --git a/src/tm_devices/drivers/pi/signal_generators/awgs/awg5k.py b/src/tm_devices/drivers/pi/signal_generators/awgs/awg5k.py
new file mode 100644
index 00000000..ebce2d0e
--- /dev/null
+++ b/src/tm_devices/drivers/pi/signal_generators/awgs/awg5k.py
@@ -0,0 +1,160 @@
+"""AWG5K device driver module."""
+
+from types import MappingProxyType
+from typing import Dict, Optional, Tuple
+
+import pyvisa as visa
+
+from tm_devices.commands import AWG5KMixin
+from tm_devices.drivers.device import family_base_class
+from tm_devices.drivers.pi.signal_generators.awgs.awg import (
+    AWG,
+    AWGSourceChannel,
+    AWGSourceDeviceConstants,
+    ParameterBounds,
+)
+from tm_devices.helpers import DeviceConfigEntry
+
+# noinspection PyPep8Naming
+from tm_devices.helpers import ReadOnlyCachedProperty as cached_property  # noqa: N813
+from tm_devices.helpers.enums import SignalGeneratorOutputPathsBase
+
+
+@family_base_class
+class AWG5K(AWG5KMixin, AWG):
+    """AWG5K device driver."""
+
+    _DEVICE_CONSTANTS = AWGSourceDeviceConstants(
+        memory_page_size=1,
+        memory_max_record_length=16200000,
+        memory_min_record_length=1,
+    )
+
+    ################################################################################################
+    # Magic Methods
+    ################################################################################################
+    def __init__(
+        self,
+        config_entry: DeviceConfigEntry,
+        verbose: bool,
+        visa_resource: visa.resources.MessageBasedResource,
+    ) -> None:
+        """Create an AWG5K device.
+
+        Args:
+            config_entry: A config entry object parsed by the DMConfigParser.
+            verbose: A boolean indicating if verbose output should be printed.
+            visa_resource: The VISA resource object.
+        """
+        # NOTE: This method must be defined for the documentation to properly generate
+        super().__init__(config_entry, verbose, visa_resource)
+
+    ################################################################################################
+    # Properties
+    ################################################################################################
+    @cached_property
+    def source_channel(self) -> "MappingProxyType[str, AWGSourceChannel]":
+        """Mapping of channel names to AWG5KSourceChannel objects."""
+        channel_map: Dict[str, AWG5KSourceChannel] = {}
+        for channel_name in self.all_channel_names_list:
+            channel_map[channel_name] = AWG5KSourceChannel(self, channel_name)
+        return MappingProxyType(channel_map)
+
+    ################################################################################################
+    # Private Methods
+    ################################################################################################
+    def _get_series_specific_constraints(
+        self,
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase],
+    ) -> Tuple[ParameterBounds, ParameterBounds, ParameterBounds]:
+        """Get constraints which are dependent on the model series and parameters.
+
+        Args:
+            output_signal_path: The signal path that the output is taking.
+
+        Returns:
+            Ranges for amplitude, offset, and sample rate.
+        """
+        if not output_signal_path:
+            output_signal_path = self.OutputSignalPath.DCA
+
+        # if DIR output path is disconnected
+        if output_signal_path == self.OutputSignalPath.DCA:
+            amplitude_range = ParameterBounds(lower=20.0e-3, upper=4.5)
+            offset_range = ParameterBounds(lower=-2.25, upper=2.25)
+        # otherwise the DIR output path is connected
+        else:
+            amplitude_range = ParameterBounds(lower=20.0e-3, upper=0.6)
+            offset_range = ParameterBounds(lower=-0.0, upper=0.0)
+
+        # AWG(Arbitrary Waveform Generator)5(Series)0x(.6 + .6x GS/s)x(x Channels)z(Model)
+        # AWG5012B means the sample rate max is 1.2 GHz, a AWG5002B would have a 600 MHz max
+        sample_rate_range = ParameterBounds(
+            lower=10.0e6, upper=600.0e6 + (600.0e6 * int(self.model[5]))
+        )
+        return amplitude_range, offset_range, sample_rate_range
+
+
+class AWG5KSourceChannel(AWGSourceChannel):
+    """AWG5K signal source channel composite."""
+
+    ################################################################################################
+    # Magic Methods
+    ################################################################################################
+    def __init__(self, awg: "AWG5K", channel_name: str) -> None:
+        """Create an AWG5200 source channel.
+
+        Args:
+            awg: An AWG.
+            channel_name: The channel name for the AWG source channel.
+        """
+        super().__init__(awg=awg, channel_name=channel_name)
+        self._awg = awg
+
+    ################################################################################################
+    # Public Methods
+    ################################################################################################
+    def set_offset(self, value: float, absolute_tolerance: float = 0) -> None:
+        """Set the offset on the source channel.
+
+        Args:
+            value: The offset value to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+        """
+        if not float(self._awg.query(f"AWGCONTROL:DOUTPUT{self.num}:STATE?")):
+            # Can only set offset when then output state is 0.
+            self._awg.set_if_needed(
+                f"{self.name}:VOLTAGE:OFFSET",
+                value,
+                tolerance=absolute_tolerance,
+            )
+        elif value:
+            # No error is raised when 0 is the offset value and the output signal path
+            # is in a state where offset cannot be set.
+            offset_error = (
+                f"The offset can only be set on {self._awg.model} with an output signal path of "
+                f"{self._awg.OutputSignalPath.DCA.value} "
+                f"(AWGCONTROL:DOUTPUT{self.num}:STATE set to 0)."
+            )
+            raise ValueError(offset_error)
+
+    def set_output_signal_path(
+        self, value: Optional[SignalGeneratorOutputPathsBase] = None
+    ) -> None:
+        """Set the output signal path on the source channel.
+
+        Args:
+            value: The output signal path.
+        """
+        if not value or value == self._awg.OutputSignalPath.DCA:
+            # Translate DCA to output state of 0.
+            output_state = 0
+        elif value == self._awg.OutputSignalPath.DIR:
+            # Translate DIR to output state of 1.
+            output_state = 1
+        else:
+            output_signal_path_error = (
+                f"{value.value} is an invalid output signal path for {self._awg.model}."
+            )
+            raise ValueError(output_signal_path_error)
+        self._awg.set_if_needed(f"AWGCONTROL:DOUTPUT{self.num}:STATE", output_state)
diff --git a/src/tm_devices/drivers/pi/signal_sources/awgs/awg5kb.py b/src/tm_devices/drivers/pi/signal_generators/awgs/awg5kb.py
similarity index 93%
rename from src/tm_devices/drivers/pi/signal_sources/awgs/awg5kb.py
rename to src/tm_devices/drivers/pi/signal_generators/awgs/awg5kb.py
index 6b43cae4..524a2628 100644
--- a/src/tm_devices/drivers/pi/signal_sources/awgs/awg5kb.py
+++ b/src/tm_devices/drivers/pi/signal_generators/awgs/awg5kb.py
@@ -2,7 +2,7 @@
 
 import pyvisa as visa
 
-from tm_devices.drivers.pi.signal_sources.awgs.awg5k import AWG5K
+from tm_devices.drivers.pi.signal_generators.awgs.awg5k import AWG5K
 from tm_devices.helpers import DeviceConfigEntry
 
 
diff --git a/src/tm_devices/drivers/pi/signal_sources/awgs/awg5kc.py b/src/tm_devices/drivers/pi/signal_generators/awgs/awg5kc.py
similarity index 93%
rename from src/tm_devices/drivers/pi/signal_sources/awgs/awg5kc.py
rename to src/tm_devices/drivers/pi/signal_generators/awgs/awg5kc.py
index 3fca1341..63981ffd 100644
--- a/src/tm_devices/drivers/pi/signal_sources/awgs/awg5kc.py
+++ b/src/tm_devices/drivers/pi/signal_generators/awgs/awg5kc.py
@@ -3,7 +3,7 @@
 import pyvisa as visa
 
 from tm_devices.commands import AWG5KCMixin
-from tm_devices.drivers.pi.signal_sources.awgs.awg5kb import AWG5KB
+from tm_devices.drivers.pi.signal_generators.awgs.awg5kb import AWG5KB
 from tm_devices.helpers import DeviceConfigEntry
 
 
diff --git a/src/tm_devices/drivers/pi/signal_generators/awgs/awg70ka.py b/src/tm_devices/drivers/pi/signal_generators/awgs/awg70ka.py
new file mode 100644
index 00000000..6a14b1f8
--- /dev/null
+++ b/src/tm_devices/drivers/pi/signal_generators/awgs/awg70ka.py
@@ -0,0 +1,277 @@
+"""AWG70KA device driver module."""
+
+from pathlib import Path
+from types import MappingProxyType
+from typing import Dict, Literal, Optional, Tuple
+
+import pyvisa as visa
+
+from tm_devices.commands import AWG70KAMixin
+from tm_devices.drivers.device import family_base_class
+from tm_devices.drivers.pi.signal_generators.awgs.awg import (
+    AWG,
+    AWGSourceChannel,
+    AWGSourceDeviceConstants,
+    ParameterBounds,
+)
+from tm_devices.helpers import (
+    DeviceConfigEntry,
+    SASSetWaveformFileTypes,
+)
+from tm_devices.helpers import ReadOnlyCachedProperty as cached_property  # noqa: N813
+from tm_devices.helpers.enums import SignalGeneratorOutputPathsBase
+
+
+@family_base_class
+class AWG70KA(AWG70KAMixin, AWG):
+    """AWG70KA device driver."""
+
+    _DEVICE_CONSTANTS = AWGSourceDeviceConstants(
+        memory_page_size=1,
+        memory_max_record_length=2000000000,
+        memory_min_record_length=1,
+    )
+    sample_waveform_set_file = (
+        "C:\\Program Files\\Tektronix\\AWG70000\\Samples\\AWG5k7k Predefined Waveforms.awgx"
+    )
+
+    ################################################################################################
+    # Magic Methods
+    ################################################################################################
+    def __init__(
+        self,
+        config_entry: DeviceConfigEntry,
+        verbose: bool,
+        visa_resource: visa.resources.MessageBasedResource,
+    ) -> None:
+        """Create an AWG70KA device.
+
+        Args:
+            config_entry: A config entry object parsed by the DMConfigParser.
+            verbose: A boolean indicating if verbose output should be printed.
+            visa_resource: The VISA resource object.
+        """
+        # NOTE: This method must be defined for the documentation to properly generate
+        super().__init__(config_entry, verbose, visa_resource)
+
+    ################################################################################################
+    # Properties
+    ################################################################################################
+    @cached_property
+    def source_channel(self) -> "MappingProxyType[str, AWGSourceChannel]":
+        """Mapping of channel names to AWG70KASourceChannel objects."""
+        channel_map: Dict[str, AWG70KASourceChannel] = {}
+        for channel_name in self.all_channel_names_list:
+            channel_map[channel_name] = AWG70KASourceChannel(self, channel_name)
+        return MappingProxyType(channel_map)
+
+    ################################################################################################
+    # Public Methods
+    ################################################################################################
+    def load_waveform_set(self, waveform_set_file: Optional[str] = None) -> None:
+        """Load a waveform set into the memory of the AWG.
+
+        Arguments:
+            waveform_set_file: The waveform set file to load
+                (The default is defined in the ``sample_wave_file`` attribute).
+        """
+        self._load_waveform_or_set(waveform_set_file=waveform_set_file, waveform_name=None)
+
+    def load_waveform_from_set(
+        self,
+        waveform_name: str,
+        waveform_set_file: Optional[str] = None,
+    ) -> None:
+        """Load in a specific waveform from a waveform set into the memory of the AWG.
+
+        Arguments:
+            waveform_name: The waveform name to load from the waveform set file.
+            waveform_set_file: The waveform set file to load
+                (The default is defined in the ``sample_wave_file`` attribute).
+        """
+        self._load_waveform_or_set(waveform_set_file=waveform_set_file, waveform_name=waveform_name)
+
+    def generate_waveform(  # noqa: PLR0913
+        self,
+        needed_sample_rate: float,
+        waveform_name: str,
+        amplitude: float,
+        offset: float,
+        channel: str = "all",
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase] = None,
+        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",  # noqa: ARG002
+        duty_cycle: float = 50.0,
+        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
+        symmetry: float = 50.0,
+    ) -> None:
+        """Generate a waveform given the following parameters.
+
+        Args:
+            needed_sample_rate: The required sample rate.
+            waveform_name: The name of the waveform to generate.
+            amplitude: The amplitude of the signal to generate.
+            offset: The offset of the signal to generate.
+            channel: The channel name to output the signal from, or 'all'.
+            output_signal_path: The output signal path of the specified channel.
+            termination: The impedance this device's ``channel`` expects to see at the received end.
+            duty_cycle: The duty cycle percentage within [10.0, 90.0].
+            polarity: The polarity to set the signal to.
+            symmetry: The symmetry to set the signal to, only applicable to certain functions.
+        """
+        # If the waveform is not in the waveform list, load in the waveform set defined at
+        # self.sample_waveform_set_file
+        if waveform_name not in self.query("WLISt:LIST?", allow_empty=True).replace('"', "").split(
+            ","
+        ):
+            self.load_waveform_set()
+        super().generate_waveform(
+            needed_sample_rate=needed_sample_rate,
+            waveform_name=waveform_name,
+            amplitude=amplitude,
+            offset=offset,
+            channel=channel,
+            output_signal_path=output_signal_path,
+            duty_cycle=duty_cycle,
+            polarity=polarity,
+            symmetry=symmetry,
+        )
+
+    def set_sample_rate(self, value: float, absolute_tolerance: Optional[float] = None) -> None:
+        """Set the rate at which samples are generated/transmitted.
+
+        Args:
+            value: The sample rate to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+                                Default value is 0.1% of the provided value.
+        """
+        if absolute_tolerance is None:
+            # Default the absolute tolerance to 0.1% of the provided frequency value
+            # due to 32 bit rounding.
+            absolute_tolerance = value * 0.001
+        self.set_if_needed("SOURCE1:FREQUENCY", value, tolerance=absolute_tolerance, opc=True)
+
+    ################################################################################################
+    # Private Methods
+    ################################################################################################
+    def _get_series_specific_constraints(
+        self,
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase],
+    ) -> Tuple[ParameterBounds, ParameterBounds, ParameterBounds]:
+        """Get constraints which are dependent on the model series and parameters.
+
+        Args:
+            output_signal_path: The signal path that the output is taking.
+
+        Returns:
+            Ranges for amplitude, offset, and sample rate.
+        """
+        if not output_signal_path:
+            output_signal_path = self.OutputSignalPath.DIR
+
+        if output_signal_path == self.OutputSignalPath.DCA:
+            amplitude_range = ParameterBounds(lower=31.0e-3, upper=1.2)
+            offset_range = ParameterBounds(lower=-400.0e-3, upper=800.0e-3)
+        else:
+            amplitude_range = ParameterBounds(lower=0.125, upper=0.5)
+            offset_range = ParameterBounds(lower=-0.0, upper=0.0)
+
+        rates = ["50", "25", "16", "08"]
+        max_sample_rate = [rate for rate in rates if rate in self.opt_string][0]  # noqa: RUF015
+        # first digit indicates the number of channels, second and third indicate sample rate (GHz)
+        # option 150 would be a one channel source with 50 GHz sample rate
+        sample_rate_range = ParameterBounds(lower=1.5e3, upper=int(max_sample_rate) * 1.0e9)
+        return amplitude_range, offset_range, sample_rate_range
+
+    def _cleanup(self) -> None:
+        """Perform the cleanup defined for the device."""
+        super()._cleanup()
+        for source_channel in self.source_channel.values():
+            source_channel.set_output_signal_path()
+            source_channel.set_offset(0)
+
+    def _load_waveform_or_set(
+        self,
+        waveform_set_file: Optional[str] = None,
+        waveform_name: Optional[str] = None,
+    ) -> None:
+        """Load in a waveform set or a specific waveform from a waveform set into memory.
+
+        Arguments:
+            waveform_set_file: The waveform set file to load.
+                (The default is defined in the ``sample_wave_file`` attribute).
+            waveform_name: The waveform name to load from the waveform set file.
+        """
+        if not waveform_set_file:
+            waveform_set_file = self.sample_waveform_set_file
+        waveform_file_type = Path(waveform_set_file).suffix.lower()
+        try:
+            SASSetWaveformFileTypes(waveform_file_type)
+            if not waveform_name:
+                self.write(f'MMEMORY:OPEN:SASSET "{waveform_set_file}"', opc=True)
+            else:
+                self.write(
+                    f'MMEMORY:OPEN:SASSET:WAVEFORM "{waveform_set_file}", "{waveform_name}"',
+                    opc=True,
+                )
+        except ValueError as err:
+            waveform_file_type_error = (
+                f"{waveform_file_type} is an invalid waveform file extension."
+            )
+            raise ValueError(waveform_file_type_error) from err
+
+        self.expect_esr(0)
+
+
+class AWG70KASourceChannel(AWGSourceChannel):
+    """AWG70KA signal source channel composite."""
+
+    ################################################################################################
+    # Magic Methods
+    ################################################################################################
+    def __init__(self, awg: AWG70KA, channel_name: str) -> None:
+        """Create an AWG5200 source channel.
+
+        Args:
+            awg: An AWG.
+            channel_name: The channel name for the AWG source channel.
+        """
+        super().__init__(awg=awg, channel_name=channel_name)
+        self._awg = awg
+
+    ################################################################################################
+    # Public Methods
+    ################################################################################################
+    def set_output_signal_path(
+        self, value: Optional[SignalGeneratorOutputPathsBase] = None
+    ) -> None:
+        """Set the output signal path on the source channel.
+
+        Can only set the output signal path to DCA when an MDC4500 is connected to the AWG70K.
+
+        Args:
+            value: The output signal path.
+                (The default is to attempt to set output signal path to DCA and falling back to DIR)
+        """
+        if not value:
+            # Attempt to set the output signal path to DCA.
+            try:
+                self._awg.set_and_check(
+                    f"OUTPUT{self.num}:PATH", self._awg.OutputSignalPath.DCA.value
+                )
+            except AssertionError:  # pragma: no cover
+                # If error, set output signal path to DIR.
+                expected_esr_message = (
+                    '-222,"Data out of range;Data Out of Range - '
+                    f'OUTPUT{self.num}:PATH DCA\r\n"\n0,"No error"'
+                )
+                self._awg.expect_esr("16", expected_esr_message)
+                self._awg.set_and_check(
+                    f"OUTPUT{self.num}:PATH", self._awg.OutputSignalPath.DIR.value
+                )
+        elif value in self._awg.OutputSignalPath:
+            self._awg.set_if_needed(f"OUTPUT{self.num}:PATH", value.value)
+        else:
+            output_signal_path_error = (
+                f"{value.value} is an invalid output signal path for {self._awg.model}."
+            )
+            raise ValueError(output_signal_path_error)
diff --git a/src/tm_devices/drivers/pi/signal_sources/awgs/awg70kb.py b/src/tm_devices/drivers/pi/signal_generators/awgs/awg70kb.py
similarity index 93%
rename from src/tm_devices/drivers/pi/signal_sources/awgs/awg70kb.py
rename to src/tm_devices/drivers/pi/signal_generators/awgs/awg70kb.py
index 51e04369..958d31b2 100644
--- a/src/tm_devices/drivers/pi/signal_sources/awgs/awg70kb.py
+++ b/src/tm_devices/drivers/pi/signal_generators/awgs/awg70kb.py
@@ -3,7 +3,7 @@
 import pyvisa as visa
 
 from tm_devices.commands import AWG70KBMixin
-from tm_devices.drivers.pi.signal_sources.awgs.awg70ka import AWG70KA
+from tm_devices.drivers.pi.signal_generators.awgs.awg70ka import AWG70KA
 from tm_devices.helpers import DeviceConfigEntry
 
 
diff --git a/src/tm_devices/drivers/pi/signal_generators/awgs/awg7k.py b/src/tm_devices/drivers/pi/signal_generators/awgs/awg7k.py
new file mode 100644
index 00000000..45e8dfa9
--- /dev/null
+++ b/src/tm_devices/drivers/pi/signal_generators/awgs/awg7k.py
@@ -0,0 +1,159 @@
+"""AWG7K device driver module."""
+
+from types import MappingProxyType
+from typing import cast, Dict, Optional, Tuple
+
+import pyvisa as visa
+
+from tm_devices.commands import AWG7KMixin
+from tm_devices.drivers.device import family_base_class
+from tm_devices.drivers.pi.signal_generators.awgs.awg import (
+    AWG,
+    AWGSourceChannel,
+    AWGSourceDeviceConstants,
+    ParameterBounds,
+)
+from tm_devices.drivers.pi.signal_generators.awgs.awg5k import (
+    AWG5K,
+    AWG5KSourceChannel,
+)
+from tm_devices.helpers import DeviceConfigEntry
+
+# noinspection PyPep8Naming
+from tm_devices.helpers import ReadOnlyCachedProperty as cached_property  # noqa: N813
+from tm_devices.helpers.enums import SignalGeneratorOutputPathsBase
+
+
+@family_base_class
+class AWG7K(AWG7KMixin, AWG):
+    """AWG7K device driver."""
+
+    _DEVICE_CONSTANTS = AWGSourceDeviceConstants(
+        memory_page_size=1,
+        memory_max_record_length=32400000,
+        memory_min_record_length=2,
+    )
+
+    ################################################################################################
+    # Magic Methods
+    ################################################################################################
+    def __init__(
+        self,
+        config_entry: DeviceConfigEntry,
+        verbose: bool,
+        visa_resource: visa.resources.MessageBasedResource,
+    ) -> None:
+        """Create an AWG7K device.
+
+        Args:
+            config_entry: A config entry object parsed by the DMConfigParser.
+            verbose: A boolean indicating if verbose output should be printed.
+            visa_resource: The VISA resource object.
+        """
+        # NOTE: This method must be defined for the documentation to properly generate
+        super().__init__(config_entry, verbose, visa_resource)
+
+    ################################################################################################
+    # Properties
+    ################################################################################################
+    @cached_property
+    def source_channel(self) -> "MappingProxyType[str, AWGSourceChannel]":
+        """Mapping of channel names to AWG7KSourceChannel objects."""
+        channel_map: Dict[str, AWG7KSourceChannel] = {}
+        for channel_name in self.all_channel_names_list:
+            channel_map[channel_name] = AWG7KSourceChannel(self, channel_name)
+        return MappingProxyType(channel_map)
+
+    ################################################################################################
+    # Private Methods
+    ################################################################################################
+    def _get_series_specific_constraints(
+        self,
+        output_signal_path: Optional[SignalGeneratorOutputPathsBase],
+    ) -> Tuple[ParameterBounds, ParameterBounds, ParameterBounds]:
+        """Get constraints which are dependent on the model series and parameters.
+
+        Args:
+            output_signal_path: The signal path that the output is taking.
+
+        Returns:
+            Ranges for amplitude, offset, and sample rate.
+        """
+        if not output_signal_path:
+            output_signal_path = self.OutputSignalPath.DCA
+
+        # if we are using the high bandwidth options
+        if "02" in self.opt_string or "06" in self.opt_string:
+            amplitude_range = ParameterBounds(lower=500.0e-3, upper=1.0)
+            offset_range = ParameterBounds(lower=-0.0, upper=0.0)
+        elif output_signal_path == self.OutputSignalPath.DCA:
+            amplitude_range = ParameterBounds(lower=50e-3, upper=2.0)
+            offset_range = ParameterBounds(lower=-0.5, upper=0.5)
+        # if the DIR path is connected
+        else:
+            amplitude_range = ParameterBounds(lower=50e-3, upper=1.0)
+            offset_range = ParameterBounds(lower=-0.0, upper=0.0)
+        # AWG(Arbitrary Waveform Generator)7(Series)xx(GS/s)x(Channels)z(Model)
+        # AWG7122C means the sample rate max is 12 GHz, a AWG7062C would have a 6 GHz max
+        sample_rate_range = ParameterBounds(lower=10.0e6, upper=int(self.model[4:6]) * 1.0e9)
+
+        return amplitude_range, offset_range, sample_rate_range
+
+
+class AWG7KSourceChannel(AWG5KSourceChannel):
+    """AWG7K signal source channel composite."""
+
+    ################################################################################################
+    # Magic Methods
+    ################################################################################################
+    def __init__(self, awg: AWG7K, channel_name: str) -> None:
+        """Create an AWG5200 source channel.
+
+        Args:
+            awg: An AWG.
+            channel_name: The channel name for the AWG source channel.
+        """
+        super().__init__(awg=cast(AWG5K, awg), channel_name=channel_name)
+        self._awg = awg
+
+    ################################################################################################
+    # Public Methods
+    ################################################################################################
+    def set_offset(self, value: float, absolute_tolerance: float = 0) -> None:
+        """Set the offset on the source channel.
+
+        Args:
+            value: The offset value to set.
+            absolute_tolerance: The acceptable difference between two floating point values.
+        """
+        output_path = float(self._awg.query(f"AWGCONTROL:DOUTPUT{self.num}:STATE?"))
+        if not ("02" in self._awg.opt_string or "06" in self._awg.opt_string) and not output_path:
+            # Can only set offset on AWG7k's without 02 and 06 options and when then
+            # output state is 0.
+            self._awg.set_if_needed(
+                f"{self.name}:VOLTAGE:OFFSET",
+                value,
+                tolerance=absolute_tolerance,
+            )
+        elif value:
+            # No error is raised when 0 is the offset value and the output signal path
+            # is in a state where offset cannot be set.
+            offset_error = (
+                f"The offset can only be set on {self._awg.model} without an 02 or 06 "
+                "option and with an output signal path of "
+                f"{self._awg.OutputSignalPath.DCA.value} "
+                f"(AWGCONTROL:DOUTPUT{self.num}:STATE set to 0)."
+            )
+            raise ValueError(offset_error)
+
+    def set_output_signal_path(
+        self, value: Optional[SignalGeneratorOutputPathsBase] = None
+    ) -> None:
+        """Set the output signal path on the source channel.
+
+        Args:
+            value: The output signal path.
+        """
+        if not ("02" in self._awg.opt_string or "06" in self._awg.opt_string):
+            # Can only set the output signal path on AWG7k's without 02 and 06 options.
+            super().set_output_signal_path(value)
diff --git a/src/tm_devices/drivers/pi/signal_sources/awgs/awg7kb.py b/src/tm_devices/drivers/pi/signal_generators/awgs/awg7kb.py
similarity index 93%
rename from src/tm_devices/drivers/pi/signal_sources/awgs/awg7kb.py
rename to src/tm_devices/drivers/pi/signal_generators/awgs/awg7kb.py
index 5f1a0551..c4ab72d6 100644
--- a/src/tm_devices/drivers/pi/signal_sources/awgs/awg7kb.py
+++ b/src/tm_devices/drivers/pi/signal_generators/awgs/awg7kb.py
@@ -2,7 +2,7 @@
 
 import pyvisa as visa
 
-from tm_devices.drivers.pi.signal_sources.awgs.awg7k import AWG7K
+from tm_devices.drivers.pi.signal_generators.awgs.awg7k import AWG7K
 from tm_devices.helpers import DeviceConfigEntry
 
 
diff --git a/src/tm_devices/drivers/pi/signal_sources/awgs/awg7kc.py b/src/tm_devices/drivers/pi/signal_generators/awgs/awg7kc.py
similarity index 92%
rename from src/tm_devices/drivers/pi/signal_sources/awgs/awg7kc.py
rename to src/tm_devices/drivers/pi/signal_generators/awgs/awg7kc.py
index d371f93d..ef4c7191 100644
--- a/src/tm_devices/drivers/pi/signal_sources/awgs/awg7kc.py
+++ b/src/tm_devices/drivers/pi/signal_generators/awgs/awg7kc.py
@@ -1,9 +1,9 @@
-"""AWG7K device driver module."""
+"""AWG7KC device driver module."""
 
 import pyvisa as visa
 
 from tm_devices.commands import AWG7KCMixin
-from tm_devices.drivers.pi.signal_sources.awgs.awg7kb import AWG7KB
+from tm_devices.drivers.pi.signal_generators.awgs.awg7kb import AWG7KB
 from tm_devices.helpers import DeviceConfigEntry
 
 
diff --git a/src/tm_devices/drivers/pi/signal_sources/signal_source.py b/src/tm_devices/drivers/pi/signal_generators/signal_generator.py
similarity index 90%
rename from src/tm_devices/drivers/pi/signal_sources/signal_source.py
rename to src/tm_devices/drivers/pi/signal_generators/signal_generator.py
index e624c55d..ca6f3761 100644
--- a/src/tm_devices/drivers/pi/signal_sources/signal_source.py
+++ b/src/tm_devices/drivers/pi/signal_generators/signal_generator.py
@@ -1,9 +1,9 @@
-"""Base Source device driver module.
+"""Base Generator device driver module.
 
-Sources include PI devices such as AFGs and AWGs.
+Generators include PI devices such as AFGs and AWGs.
 """
 
-from abc import ABC, abstractmethod
+from abc import ABC
 from typing import Tuple, Union
 
 from tm_devices.driver_mixins.signal_generator_mixin import SignalGeneratorMixin
@@ -14,8 +14,8 @@
 from tm_devices.helpers import ReadOnlyCachedProperty as cached_property  # noqa: N813
 
 
-class SignalSource(PIDevice, SignalGeneratorMixin, ABC):
-    """Base Source device driver."""
+class SignalGenerator(PIDevice, SignalGeneratorMixin, ABC):
+    """Base Signal Generator device driver."""
 
     ################################################################################################
     # Properties
@@ -110,15 +110,6 @@ def get_eventlog_status(self) -> Tuple[bool, str]:
     ################################################################################################
     # Private Methods
     ################################################################################################
-    @abstractmethod
-    def _send_waveform(self, target_file: str) -> None:
-        """Send the waveform information to the source as a file in memory.
-
-        Args:
-            target_file: The name of the waveform file.
-        """
-        raise NotImplementedError
-
     def _validate_channels(self, channel: str) -> Tuple[str, ...]:
         """Create a list of channels to use on the source based on the desired channel number.
 
diff --git a/src/tm_devices/drivers/pi/signal_sources/__init__.py b/src/tm_devices/drivers/pi/signal_sources/__init__.py
deleted file mode 100644
index fbe53c80..00000000
--- a/src/tm_devices/drivers/pi/signal_sources/__init__.py
+++ /dev/null
@@ -1,1 +0,0 @@
-"""Signal Sources package init file."""
diff --git a/src/tm_devices/drivers/pi/signal_sources/afgs/afg.py b/src/tm_devices/drivers/pi/signal_sources/afgs/afg.py
deleted file mode 100644
index 91276a82..00000000
--- a/src/tm_devices/drivers/pi/signal_sources/afgs/afg.py
+++ /dev/null
@@ -1,153 +0,0 @@
-"""Base AFG device driver module."""
-
-import re
-
-from abc import ABC
-from dataclasses import dataclass
-from typing import Literal, Type
-
-from tm_devices.driver_mixins.signal_generator_mixin import SourceDeviceConstants
-from tm_devices.drivers.device import family_base_class
-from tm_devices.drivers.pi.signal_sources.signal_source import SignalSource
-from tm_devices.helpers import DeviceTypes, SignalSourceFunctionsAFG
-
-# noinspection PyPep8Naming
-from tm_devices.helpers import ReadOnlyCachedProperty as cached_property  # noqa: N813
-
-
-@dataclass(frozen=True)
-class AFGSourceDeviceConstants(SourceDeviceConstants):
-    """Class to hold source device constants."""
-
-    functions: Type[SignalSourceFunctionsAFG] = SignalSourceFunctionsAFG
-
-
-@family_base_class
-class AFG(SignalSource, ABC):
-    """Base AFG device driver."""
-
-    _DEVICE_TYPE = DeviceTypes.AFG.value
-
-    ################################################################################################
-    # Properties
-    ################################################################################################
-    @property
-    def source_device_constants(self) -> AFGSourceDeviceConstants:
-        """Return the device constants."""
-        return self._DEVICE_CONSTANTS  # type: ignore[attr-defined]
-
-    @cached_property
-    def total_channels(self) -> int:
-        """Return the total number of channels (all types)."""
-        if match := re.match(r"AFG\d+(\d)", self.model):
-            return int(match.group(1))
-        return 0  # pragma: no cover
-
-    ################################################################################################
-    # Public Methods
-    ################################################################################################
-    # pylint: disable=too-many-locals,line-too-long
-    def generate_waveform(  # noqa: C901, PLR0913  # pyright: ignore[reportIncompatibleMethodOverride]
-        self,
-        frequency: float,
-        function: SignalSourceFunctionsAFG,
-        amplitude: float,
-        offset: float,
-        channel: str = "all",
-        burst: int = 0,
-        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",
-        duty_cycle: float = 50.0,
-        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
-        symmetry: float = 100.0,
-    ) -> None:
-        """Generate a signal given the following parameters.
-
-        Args:
-            frequency: The frequency of the waveform to generate.
-            function: The waveform shape to generate.
-            amplitude: The amplitude of the signal to generate.
-            offset: The offset of the signal to generate.
-            channel: The channel name to output the signal from, or 'all'.
-            burst: The number of wavelengths to be generated.
-            termination: The impedance this device's ``channel`` expects to see at the received end.
-            duty_cycle: The duty cycle percentage within [10.0, 90.0].
-            polarity: The polarity to set the signal to.
-            symmetry: The symmetry to set the signal to, only applicable to certain functions.
-        """
-        polarity_mapping = {
-            "NORMAL": "NORM",
-            "INVERTED": "INV",
-        }
-        self._validate_generated_function(function)
-
-        # Generate the waveform on the given channel
-        for channel_name in self._validate_channels(channel):
-            # grab the number(s) in the channel name
-            # noinspection PyTypeChecker
-            channel_num = "".join(filter(str.isdigit, channel_name))
-            # Temporarily turn off this channel
-            self.set_and_check(f"OUTPUT{channel_num}:STATE", 0)
-            # Termination
-            if termination == "FIFTY":
-                self.set_and_check(f"OUTPUT{channel_num}:IMPEDANCE", 50)
-            elif termination == "HIGHZ":
-                self.write(f"OUTPUT{channel_num}:IMPEDANCE INFINITY")
-            else:  # pragma: no cover
-                # if termination is MAXIMUM or MINIMUM or INFINITY
-                self.set_and_check(f"OUTPUT{channel_num}:IMPEDANCE", termination)
-            # Frequency
-            self.set_and_check(f"SOURCE{channel_num}:FREQUENCY:FIXED", frequency)
-            # Offset
-            self.set_and_check(f"SOURCE{channel_num}:VOLTAGE:OFFSET", offset, tolerance=0.01)
-            if function == SignalSourceFunctionsAFG.PULSE:
-                # Duty cycle is only valid for pulse
-                self.set_and_check(f"SOURCE{channel_num}:PULSE:DCYCLE", duty_cycle)
-            # Polarity
-            self.set_and_check(f"OUTPUT{channel_num}:POLARITY", polarity_mapping[polarity])
-            # Function
-            if function == SignalSourceFunctionsAFG.RAMP:
-                self.set_and_check(f"SOURCE{channel_num}:FUNCTION:RAMP:SYMMETRY", symmetry)
-            self.set_and_check(f"SOURCE{channel_num}:FUNCTION", function.value)
-            # Amplitude, needs to be after termination so that the amplitude is properly adjusted
-            self.set_and_check(f"SOURCE{channel_num}:VOLTAGE:AMPLITUDE", amplitude, tolerance=0.01)
-            if burst > 0:
-                # set to external as to not burst every millisecond
-                self.set_and_check("TRIGGER:SEQUENCE:SOURCE", "EXT")
-                self.set_and_check(f"SOURCE{channel_num}:BURST:STATE", 1)
-                self.set_and_check(f"SOURCE{channel_num}:BURST:MODE", "TRIG")
-                self.set_and_check(f"SOURCE{channel_num}:BURST:NCYCLES", burst)
-            # Turn on the channel
-            self.set_and_check(f"OUTPUT{channel_num}:STATE", 1)
-
-            # Check if burst is enabled on any channel of the AFG
-            burst_state = False
-            for burst_channel in range(1, self.total_channels + 1):
-                if self.query(f"SOURCE{burst_channel}:BURST:STATE?") == "1":
-                    burst_state = True
-
-            if burst > 0:
-                self.write("*TRG")
-            # Initiate a phase sync (between CH 1 and CH 2 output waveforms on two channel AFGs)
-            elif (
-                self.total_channels > 1  # pylint: disable=comparison-with-callable
-                and function != SignalSourceFunctionsAFG.DC
-                and not burst_state
-            ):
-                self.write("SOURCE1:PHASE:INITIATE")
-            # Check for system errors
-            self.expect_esr(0)
-
-    ################################################################################################
-    # Private Methods
-    ################################################################################################
-    def _reboot(self) -> None:
-        """Reboot the device."""
-        # TODO: implement
-
-    def _send_waveform(self, target_file: str) -> None:
-        """Send the waveform information to the AWG as a file in memory.
-
-        Args:
-            target_file: The name of the waveform file.
-        """
-        # TODO: implement
diff --git a/src/tm_devices/drivers/pi/signal_sources/afgs/afg3k.py b/src/tm_devices/drivers/pi/signal_sources/afgs/afg3k.py
deleted file mode 100644
index 6569e912..00000000
--- a/src/tm_devices/drivers/pi/signal_sources/afgs/afg3k.py
+++ /dev/null
@@ -1,36 +0,0 @@
-"""AFG3K device driver module."""
-
-import pyvisa as visa
-
-from tm_devices.commands import AFG3KMixin
-from tm_devices.drivers.pi.signal_sources.afgs.afg import AFG, AFGSourceDeviceConstants
-from tm_devices.helpers import DeviceConfigEntry
-
-
-class AFG3K(AFG3KMixin, AFG):
-    """AFG3K device driver."""
-
-    _DEVICE_CONSTANTS = AFGSourceDeviceConstants(
-        memory_page_size=2,
-        memory_max_record_length=128 * 1024,
-        memory_min_record_length=2,
-    )
-
-    ################################################################################################
-    # Magic Methods
-    ################################################################################################
-    def __init__(
-        self,
-        config_entry: DeviceConfigEntry,
-        verbose: bool,
-        visa_resource: visa.resources.MessageBasedResource,
-    ) -> None:
-        """Create an AFG3K device.
-
-        Args:
-            config_entry: A config entry object parsed by the DMConfigParser.
-            verbose: A boolean indicating if verbose output should be printed.
-            visa_resource: The VISA resource object.
-        """
-        # NOTE: This method must be defined for the documentation to properly generate
-        super().__init__(config_entry, verbose, visa_resource)
diff --git a/src/tm_devices/drivers/pi/signal_sources/afgs/afg3kc.py b/src/tm_devices/drivers/pi/signal_sources/afgs/afg3kc.py
deleted file mode 100644
index 7cc7b7bc..00000000
--- a/src/tm_devices/drivers/pi/signal_sources/afgs/afg3kc.py
+++ /dev/null
@@ -1,34 +0,0 @@
-"""AFG3KC device driver module."""
-
-import pyvisa as visa
-
-from tm_devices.commands import AFG3KCMixin
-from tm_devices.drivers.pi.signal_sources.afgs.afg3kb import AFG3KB
-from tm_devices.helpers import DeviceConfigEntry
-
-
-class AFG3KC(AFG3KCMixin, AFG3KB):  # pyright: ignore[reportIncompatibleMethodOverride]
-    """AFG3KC device driver."""
-
-    ################################################################################################
-    # Magic Methods
-    ################################################################################################
-    def __init__(
-        self,
-        config_entry: DeviceConfigEntry,
-        verbose: bool,
-        visa_resource: visa.resources.MessageBasedResource,
-    ) -> None:
-        """Create an AFG3KC device.
-
-        Args:
-            config_entry: A config entry object parsed by the DMConfigParser.
-            verbose: A boolean indicating if verbose output should be printed.
-            visa_resource: The VISA resource object.
-        """
-        # NOTE: This method must be defined for the documentation to properly generate
-        super().__init__(config_entry, verbose, visa_resource)
-
-    ################################################################################################
-    # Public Methods
-    ################################################################################################
diff --git a/src/tm_devices/drivers/pi/signal_sources/awgs/awg.py b/src/tm_devices/drivers/pi/signal_sources/awgs/awg.py
deleted file mode 100644
index b8289640..00000000
--- a/src/tm_devices/drivers/pi/signal_sources/awgs/awg.py
+++ /dev/null
@@ -1,139 +0,0 @@
-"""Base AWG device driver module."""
-
-import inspect
-import struct
-
-from abc import ABC
-from dataclasses import dataclass
-from pathlib import Path
-from typing import Literal, Type
-
-from tm_devices.driver_mixins.signal_generator_mixin import SourceDeviceConstants
-from tm_devices.drivers.device import family_base_class
-from tm_devices.drivers.pi.signal_sources.signal_source import SignalSource
-from tm_devices.helpers import DeviceTypes, SignalSourceFunctionsAWG
-
-# noinspection PyPep8Naming
-from tm_devices.helpers import ReadOnlyCachedProperty as cached_property  # noqa: N813
-
-
-@dataclass(frozen=True)
-class AWGSourceDeviceConstants(SourceDeviceConstants):
-    """Class to hold source device constants."""
-
-    functions: Type[SignalSourceFunctionsAWG] = SignalSourceFunctionsAWG
-
-
-@family_base_class
-class AWG(SignalSource, ABC):
-    """Base AWG device driver."""
-
-    _DEVICE_TYPE = DeviceTypes.AWG.value
-
-    ################################################################################################
-    # Magic Methods
-    ################################################################################################
-
-    ################################################################################################
-    # Properties
-    ################################################################################################
-    @property
-    def source_device_constants(self) -> AWGSourceDeviceConstants:
-        """Return the device constants."""
-        return self._DEVICE_CONSTANTS  # type: ignore[attr-defined]
-
-    @cached_property
-    def total_channels(self) -> int:
-        """Return the total number of channels (all types)."""
-        return int(self.query("AWGControl:CONFigure:CNUMber?", verbose=False))
-
-    ################################################################################################
-    # Public Methods
-    ################################################################################################
-    def load_waveform(self, wfm_name: str, waveform_file_path: str, wfm_type: str) -> None:
-        """Load a waveform into the memory of the AWG.
-
-        Args:
-            wfm_name: The name to set the loaded waveform to on the AWG.
-            waveform_file_path: The path to the waveform.
-            wfm_type: The type of the waveform to import.
-        """
-        if not waveform_file_path.startswith('"'):
-            waveform_file_path = '"' + waveform_file_path
-        if not waveform_file_path.endswith('"'):
-            waveform_file_path += '"'
-        self.write(f'MMEMory:IMPort "{wfm_name}", {waveform_file_path}, {wfm_type}')
-        self._ieee_cmds.opc()
-
-    def generate_waveform(  # noqa: PLR0913  # pyright: ignore[reportIncompatibleMethodOverride]
-        self,
-        frequency: float,
-        function: AWGSourceDeviceConstants,
-        amplitude: float,
-        offset: float,
-        channel: str = "all",
-        burst: int = 0,
-        termination: Literal["FIFTY", "HIGHZ"] = "FIFTY",
-        duty_cycle: float = 50.0,
-        polarity: Literal["NORMAL", "INVERTED"] = "NORMAL",
-        symmetry: float = 50.0,
-    ) -> None:
-        """Generate a signal given the following parameters.
-
-        Args:
-            frequency: The frequency of the waveform to generate.
-            function: The waveform shape to generate.
-            amplitude: The amplitude of the signal to generate.
-            offset: The offset of the signal to generate.
-            channel: The channel name to output the signal from, or 'all'.
-            burst: The number of wavelengths to be generated.
-            termination: The impedance this device's ``channel`` expects to see at the received end.
-            duty_cycle: The duty cycle percentage within [10.0, 90.0].
-            polarity: The polarity to set the signal to.
-            symmetry: The symmetry to set the signal to, only applicable to certain functions.
-
-        Raises:
-            NotImplementedError: Indicates the current driver has not implemented this method.
-        """
-        # TODO: implement
-        raise NotImplementedError(
-            f"``.{inspect.currentframe().f_code.co_name}()``"  # pyright: ignore[reportOptionalMemberAccess]
-            f" is not yet implemented for the {self.__class__.__name__} driver"
-        )
-
-    ################################################################################################
-    # Private Methods
-    ################################################################################################
-    def _reboot(self) -> None:
-        """Reboot the device."""
-        # TODO: overwrite the reboot code here
-
-    # TODO: add testing for this
-    def _send_waveform(self, target_file: str) -> None:  # pragma: no cover
-        """Send the waveform information to the AWG as a file in memory.
-
-        Args:
-            target_file: The name of the waveform file.
-        """
-        with open(target_file, "rb") as file_handle:
-            file_handle.seek(0, 0)
-            waveform_data = file_handle.read()
-            # must be even to send
-            if len(waveform_data) % 2:
-                waveform_data += b"\0"
-
-            info_len = int(len(waveform_data) / 2)
-            bin_waveform = struct.unpack(">" + str(info_len) + "H", waveform_data)
-
-            # Turn "path/to/stuff.wfm" into "stuff.wfm".
-            filename_target = Path(target_file).name
-            # Write the waveform data to the AWG memory.
-            string_to_send = 'MMEMORY:DATA "' + filename_target + '",'
-            self._visa_resource.write_binary_values(
-                string_to_send,
-                bin_waveform,
-                datatype="H",
-                is_big_endian=True,
-                termination="\r\n",
-                encoding="latin-1",
-            )
diff --git a/src/tm_devices/drivers/pi/signal_sources/awgs/awg5200.py b/src/tm_devices/drivers/pi/signal_sources/awgs/awg5200.py
deleted file mode 100644
index 17ba9338..00000000
--- a/src/tm_devices/drivers/pi/signal_sources/awgs/awg5200.py
+++ /dev/null
@@ -1,40 +0,0 @@
-"""AWG5200 device driver module."""
-
-import pyvisa as visa
-
-from tm_devices.commands import AWG5200Mixin
-from tm_devices.drivers.pi.signal_sources.awgs.awg import AWG, AWGSourceDeviceConstants
-from tm_devices.helpers import DeviceConfigEntry
-
-
-class AWG5200(AWG5200Mixin, AWG):
-    """AWG5200 device driver."""
-
-    _DEVICE_CONSTANTS = AWGSourceDeviceConstants(
-        memory_page_size=1,
-        memory_max_record_length=16200000,
-        memory_min_record_length=1,
-    )
-
-    ################################################################################################
-    # Magic Methods
-    ################################################################################################
-    def __init__(
-        self,
-        config_entry: DeviceConfigEntry,
-        verbose: bool,
-        visa_resource: visa.resources.MessageBasedResource,
-    ) -> None:
-        """Create an AWG5200 device.
-
-        Args:
-            config_entry: A config entry object parsed by the DMConfigParser.
-            verbose: A boolean indicating if verbose output should be printed.
-            visa_resource: The VISA resource object.
-        """
-        # NOTE: This method must be defined for the documentation to properly generate
-        super().__init__(config_entry, verbose, visa_resource)
-
-    ################################################################################################
-    # Public Methods
-    ################################################################################################
diff --git a/src/tm_devices/drivers/pi/signal_sources/awgs/awg5k.py b/src/tm_devices/drivers/pi/signal_sources/awgs/awg5k.py
deleted file mode 100644
index 1789697c..00000000
--- a/src/tm_devices/drivers/pi/signal_sources/awgs/awg5k.py
+++ /dev/null
@@ -1,40 +0,0 @@
-"""AWG5K device driver module."""
-
-import pyvisa as visa
-
-from tm_devices.commands import AWG5KMixin
-from tm_devices.drivers.pi.signal_sources.awgs.awg import AWG, AWGSourceDeviceConstants
-from tm_devices.helpers import DeviceConfigEntry
-
-
-class AWG5K(AWG5KMixin, AWG):
-    """AWG5K device driver."""
-
-    _DEVICE_CONSTANTS = AWGSourceDeviceConstants(
-        memory_page_size=1,
-        memory_max_record_length=16200000,
-        memory_min_record_length=1,
-    )
-
-    ################################################################################################
-    # Magic Methods
-    ################################################################################################
-    def __init__(
-        self,
-        config_entry: DeviceConfigEntry,
-        verbose: bool,
-        visa_resource: visa.resources.MessageBasedResource,
-    ) -> None:
-        """Create an AWG5K device.
-
-        Args:
-            config_entry: A config entry object parsed by the DMConfigParser.
-            verbose: A boolean indicating if verbose output should be printed.
-            visa_resource: The VISA resource object.
-        """
-        # NOTE: This method must be defined for the documentation to properly generate
-        super().__init__(config_entry, verbose, visa_resource)
-
-    ################################################################################################
-    # Public Methods
-    ################################################################################################
diff --git a/src/tm_devices/drivers/pi/signal_sources/awgs/awg70ka.py b/src/tm_devices/drivers/pi/signal_sources/awgs/awg70ka.py
deleted file mode 100644
index 46b225d5..00000000
--- a/src/tm_devices/drivers/pi/signal_sources/awgs/awg70ka.py
+++ /dev/null
@@ -1,40 +0,0 @@
-"""AWG70KA device driver module."""
-
-import pyvisa as visa
-
-from tm_devices.commands import AWG70KAMixin
-from tm_devices.drivers.pi.signal_sources.awgs.awg import AWG, AWGSourceDeviceConstants
-from tm_devices.helpers import DeviceConfigEntry
-
-
-class AWG70KA(AWG70KAMixin, AWG):
-    """AWG70KA device driver."""
-
-    _DEVICE_CONSTANTS = AWGSourceDeviceConstants(
-        memory_page_size=1,
-        memory_max_record_length=2000000000,
-        memory_min_record_length=1,
-    )
-
-    ################################################################################################
-    # Magic Methods
-    ################################################################################################
-    def __init__(
-        self,
-        config_entry: DeviceConfigEntry,
-        verbose: bool,
-        visa_resource: visa.resources.MessageBasedResource,
-    ) -> None:
-        """Create an AWG70KA device.
-
-        Args:
-            config_entry: A config entry object parsed by the DMConfigParser.
-            verbose: A boolean indicating if verbose output should be printed.
-            visa_resource: The VISA resource object.
-        """
-        # NOTE: This method must be defined for the documentation to properly generate
-        super().__init__(config_entry, verbose, visa_resource)
-
-    ################################################################################################
-    # Public Methods
-    ################################################################################################
diff --git a/src/tm_devices/drivers/pi/signal_sources/awgs/awg7k.py b/src/tm_devices/drivers/pi/signal_sources/awgs/awg7k.py
deleted file mode 100644
index e5e6f1c6..00000000
--- a/src/tm_devices/drivers/pi/signal_sources/awgs/awg7k.py
+++ /dev/null
@@ -1,40 +0,0 @@
-"""AWG7K device driver module."""
-
-import pyvisa as visa
-
-from tm_devices.commands import AWG7KMixin
-from tm_devices.drivers.pi.signal_sources.awgs.awg import AWG, AWGSourceDeviceConstants
-from tm_devices.helpers import DeviceConfigEntry
-
-
-class AWG7K(AWG7KMixin, AWG):
-    """AWG7K device driver."""
-
-    _DEVICE_CONSTANTS = AWGSourceDeviceConstants(
-        memory_page_size=1,
-        memory_max_record_length=32400000,
-        memory_min_record_length=2,
-    )
-
-    ################################################################################################
-    # Magic Methods
-    ################################################################################################
-    def __init__(
-        self,
-        config_entry: DeviceConfigEntry,
-        verbose: bool,
-        visa_resource: visa.resources.MessageBasedResource,
-    ) -> None:
-        """Create an AWG7K device.
-
-        Args:
-            config_entry: A config entry object parsed by the DMConfigParser.
-            verbose: A boolean indicating if verbose output should be printed.
-            visa_resource: The VISA resource object.
-        """
-        # NOTE: This method must be defined for the documentation to properly generate
-        super().__init__(config_entry, verbose, visa_resource)
-
-    ################################################################################################
-    # Public Methods
-    ################################################################################################
diff --git a/src/tm_devices/drivers/pi/source_measure_units/smu24xx/smu24xx_standard.py b/src/tm_devices/drivers/pi/source_measure_units/smu24xx/smu24xx_standard.py
index 3fa1aa93..d1019416 100644
--- a/src/tm_devices/drivers/pi/source_measure_units/smu24xx/smu24xx_standard.py
+++ b/src/tm_devices/drivers/pi/source_measure_units/smu24xx/smu24xx_standard.py
@@ -8,7 +8,7 @@
 from tm_devices.drivers.device import family_base_class
 from tm_devices.drivers.pi.ieee488_2_commands import IEEE4882Commands
 from tm_devices.drivers.pi.pi_device import PIDevice
-from tm_devices.drivers.pi.signal_sources.signal_source import SignalSource
+from tm_devices.drivers.pi.signal_generators.signal_generator import SignalGenerator
 from tm_devices.drivers.pi.source_measure_units.source_measure_unit import SourceMeasureUnit
 
 # noinspection PyPep8Naming
@@ -62,7 +62,7 @@ def expect_esr(self, esr: Union[int, str], error_string: str = "") -> Tuple[bool
         Returns:
             Boolean indicating if the check passed or failed and a string with the results.
         """
-        return SignalSource.expect_esr(self, esr, error_string)  # type: ignore[arg-type]
+        return SignalGenerator.expect_esr(self, esr, error_string)  # type: ignore[arg-type]
 
     def get_eventlog_status(self) -> Tuple[bool, str]:
         """Help function for getting the eventlog status.
@@ -70,14 +70,14 @@ def get_eventlog_status(self) -> Tuple[bool, str]:
         Returns:
             Boolean indicating no error, String containing concatenated contents of event log.
         """
-        return SignalSource.get_eventlog_status(self)  # type: ignore[arg-type]
+        return SignalGenerator.get_eventlog_status(self)  # type: ignore[arg-type]
 
     def run_script(self, script_name: str) -> None:  # noqa: ARG002
         """Not Implemented."""
         msg = f"This functionality is not available on the {self.__class__.__name__} instrument."
         raise NotImplementedError(msg)
 
-    def set_and_check(
+    def set_and_check(  # noqa: PLR0913
         self,
         command: str,
         value: Union[str, float],
@@ -87,6 +87,7 @@ def set_and_check(
         custom_message_prefix: str = "",
         *,
         expected_value: Optional[Union[str, float]] = None,
+        opc: bool = False,
     ) -> str:
         """Send the given command with the given value and then verify the results.
 
@@ -102,6 +103,7 @@ def set_and_check(
             remove_quotes: Set this to True to remove all double quotes from the returned value.
             custom_message_prefix: A custom message to be prepended to the failure message.
             expected_value: An optional, alternative value expected to be returned.
+            opc: Boolean indicating if ``*OPC?`` should be queried after sending the command.
 
         Returns:
             The output of the query portion of the method.
@@ -115,6 +117,7 @@ def set_and_check(
             remove_quotes,
             custom_message_prefix,
             expected_value=expected_value,
+            opc=opc,
         )
 
     def load_script(
diff --git a/src/tm_devices/drivers/pi/source_measure_units/smu60xx/smu6xxx.py b/src/tm_devices/drivers/pi/source_measure_units/smu60xx/smu6xxx.py
index f0233329..8d46e7ad 100644
--- a/src/tm_devices/drivers/pi/source_measure_units/smu60xx/smu6xxx.py
+++ b/src/tm_devices/drivers/pi/source_measure_units/smu60xx/smu6xxx.py
@@ -10,7 +10,7 @@
 from tm_devices.drivers.device import family_base_class
 from tm_devices.drivers.pi.ieee488_2_commands import IEEE4882Commands
 from tm_devices.drivers.pi.pi_device import PIDevice
-from tm_devices.drivers.pi.signal_sources.signal_source import SignalSource
+from tm_devices.drivers.pi.signal_generators.signal_generator import SignalGenerator
 from tm_devices.drivers.pi.source_measure_units.source_measure_unit import SourceMeasureUnit
 
 # noinspection PyPep8Naming
@@ -64,7 +64,7 @@ def expect_esr(self, esr: Union[int, str], error_string: str = "") -> Tuple[bool
         Returns:
             Boolean indicating if the check passed or failed and a string with the results.
         """
-        return SignalSource.expect_esr(self, esr, error_string)  # type: ignore[arg-type]
+        return SignalGenerator.expect_esr(self, esr, error_string)  # type: ignore[arg-type]
 
     def get_eventlog_status(self) -> Tuple[bool, str]:
         """Help function for getting the eventlog status.
@@ -72,14 +72,14 @@ def get_eventlog_status(self) -> Tuple[bool, str]:
         Returns:
             Boolean indicating no error, String containing concatenated contents of event log.
         """
-        return SignalSource.get_eventlog_status(self)  # type: ignore[arg-type]
+        return SignalGenerator.get_eventlog_status(self)  # type: ignore[arg-type]
 
     def run_script(self, script_name: str) -> None:  # noqa: ARG002
         """Not Implemented."""
         msg = f"This functionality is not available on the {self.__class__.__name__} instrument."
         raise NotImplementedError(msg)
 
-    def set_and_check(
+    def set_and_check(  # noqa: PLR0913
         self,
         command: str,
         value: Union[str, float],
@@ -89,6 +89,7 @@ def set_and_check(
         custom_message_prefix: str = "",
         *,
         expected_value: Optional[Union[str, float]] = None,
+        opc: bool = False,
     ) -> str:
         """Send the given command with the given value and then verify the results.
 
@@ -104,6 +105,7 @@ def set_and_check(
             remove_quotes: Set this to True to remove all double quotes from the returned value.
             custom_message_prefix: A custom message to be prepended to the failure message.
             expected_value: An optional, alternative value expected to be returned.
+            opc: Boolean indicating if ``*OPC?`` should be queried after sending the command.
 
         Returns:
             The output of the query portion of the method.
@@ -117,6 +119,7 @@ def set_and_check(
             remove_quotes,
             custom_message_prefix,
             expected_value=expected_value,
+            opc=opc,
         )
 
     def load_script(
diff --git a/src/tm_devices/drivers/pi/tsp_device.py b/src/tm_devices/drivers/pi/tsp_device.py
index c3cbd5a6..3a686898 100644
--- a/src/tm_devices/drivers/pi/tsp_device.py
+++ b/src/tm_devices/drivers/pi/tsp_device.py
@@ -193,7 +193,7 @@ def run_script(self, script_name: str) -> None:
         """
         self.write(f"{script_name}()")
 
-    def set_and_check(
+    def set_and_check(  # noqa: PLR0913
         self,
         command: str,
         value: Union[str, float],
@@ -203,6 +203,7 @@ def set_and_check(
         custom_message_prefix: str = "",
         *,
         expected_value: Optional[Union[str, float]] = None,
+        opc: bool = False,
     ) -> str:
         """Send the given command with the given value and then verify the results.
 
@@ -218,11 +219,12 @@ def set_and_check(
             remove_quotes: Set this to True to remove all double quotes from the returned value.
             custom_message_prefix: A custom message to be prepended to the failure message.
             expected_value: An optional, alternative value expected to be returned.
+            opc: Boolean indicating if ``*OPC?`` should be queried after sending the command.
 
         Returns:
             The output of the query portion of the method.
         """
-        self.write(f"{command} = {value}")
+        self.write(f"{command} = {value}", opc=opc)
         if self._enable_verification:
             check = self.query("print(" + command + ")", remove_quotes=remove_quotes)
             message_prefix = f"Failed to set {command} to {value}"
diff --git a/src/tm_devices/helpers/__init__.py b/src/tm_devices/helpers/__init__.py
index 823dee69..6c6ded24 100644
--- a/src/tm_devices/helpers/__init__.py
+++ b/src/tm_devices/helpers/__init__.py
@@ -17,10 +17,8 @@
     ConfigFileType,
     ConnectionTypes,
     DeviceTypes,
-    SignalSourceFunctionBase,
-    SignalSourceFunctionsAFG,
-    SignalSourceFunctionsAWG,
-    SignalSourceFunctionsIAFG,
+    LoadImpedanceAFG,
+    SASSetWaveformFileTypes,
     SupportedModels,
     SupportedRequestTypes,
 )
@@ -76,8 +74,6 @@
     "validate_address",
     "VISA_RESOURCE_EXPRESSION_REGEX",
     "ReadOnlyCachedProperty",
-    "SignalSourceFunctionBase",
-    "SignalSourceFunctionsAWG",
-    "SignalSourceFunctionsAFG",
-    "SignalSourceFunctionsIAFG",
+    "SASSetWaveformFileTypes",
+    "LoadImpedanceAFG",
 ]
diff --git a/src/tm_devices/helpers/constants_and_dataclasses.py b/src/tm_devices/helpers/constants_and_dataclasses.py
index 18f4d66c..80f11ee1 100644
--- a/src/tm_devices/helpers/constants_and_dataclasses.py
+++ b/src/tm_devices/helpers/constants_and_dataclasses.py
@@ -4,7 +4,7 @@
 
 from dataclasses import dataclass
 from types import MappingProxyType
-from typing import Final, FrozenSet, List, Mapping, Optional, Tuple
+from typing import Final, FrozenSet, List, Mapping, Optional, Tuple, Union
 
 from pyvisa import constants as pyvisa_constants
 
@@ -12,7 +12,7 @@
     AsDictionaryMixin,
     AsDictionaryUseEnumNameUseCustEnumStrValueMixin,
 )
-from tm_devices.helpers.enums import ConnectionTypes, DeviceTypes, SupportedModels
+from tm_devices.helpers.enums import ConnectionTypes, DeviceTypes, LoadImpedanceAFG, SupportedModels
 from tm_devices.helpers.standalone_functions import validate_address
 
 
@@ -705,3 +705,14 @@ def __str__(self) -> str:
     }
 )
 """A mapping of model USBTMC info."""
+
+LOAD_IMPEDANCE_LOOKUP: Final[Mapping[Union[float, str], LoadImpedanceAFG]] = MappingProxyType(
+    {
+        9.97e37: LoadImpedanceAFG.HIGHZ,
+        1.0e6: LoadImpedanceAFG.HIGHZ,
+        "HIGHZ": LoadImpedanceAFG.HIGHZ,
+        50.0: LoadImpedanceAFG.FIFTY,
+        "FIFTY": LoadImpedanceAFG.FIFTY,
+    }
+)
+"""Conversions of literal values representing impedances to Enum values representing impedances."""
diff --git a/src/tm_devices/helpers/enums.py b/src/tm_devices/helpers/enums.py
index b1556a9b..d6c6b94e 100644
--- a/src/tm_devices/helpers/enums.py
+++ b/src/tm_devices/helpers/enums.py
@@ -206,11 +206,11 @@ class SupportedRequestTypes(CustomStrEnum):
     PATCH = "PATCH"
 
 
-class SignalSourceFunctionBase(Enum):
+class SignalGeneratorFunctionBase(Enum):
     """An enumeration for all function name enumerations to inherit from."""
 
 
-class SignalSourceFunctionsAWG(SignalSourceFunctionBase):
+class SignalGeneratorFunctionsAWG(SignalGeneratorFunctionBase):
     """AWG function names."""
 
     SIN = "Sine"
@@ -221,7 +221,7 @@ class SignalSourceFunctionsAWG(SignalSourceFunctionBase):
     DC = "DC"
 
 
-class SignalSourceFunctionsAFG(SignalSourceFunctionBase):
+class SignalGeneratorFunctionsAFG(SignalGeneratorFunctionBase):
     """AFG function names."""
 
     SIN = "SIN"
@@ -239,7 +239,7 @@ class SignalSourceFunctionsAFG(SignalSourceFunctionBase):
     ARBITRARY = "ARB"
 
 
-class SignalSourceFunctionsIAFG(SignalSourceFunctionBase):
+class SignalGeneratorFunctionsIAFG(SignalGeneratorFunctionBase):
     """IAFG function names."""
 
     SIN = "SINE"
@@ -256,3 +256,37 @@ class SignalSourceFunctionsIAFG(SignalSourceFunctionBase):
     CARDIAC = "CARDIAC"
     NOISE = "NOISE"
     ARBITRARY = "ARBITRARY"
+
+
+class SignalGeneratorOutputPathsBase(CustomStrEnum):
+    """Base output signal path types."""
+
+
+class SignalGeneratorOutputPathsNon5200(SignalGeneratorOutputPathsBase):
+    """Output signal path types for non-AWG5200's."""
+
+    DCA = "DCA"  # Direct Coupled Amplifier, signal enters an amplifier before the output (+/-)
+    DIR = "DIR"  # DIRect output, bypasses the amplifier and filter directly to the output (+/-)
+
+
+class SignalGeneratorOutputPaths5200(SignalGeneratorOutputPathsBase):
+    """Output signal path types for AWG5200's."""
+
+    DCHB = "DCHB"  # DC High Bandwidth, goes directly to the differential output (+/-)
+    DCHV = "DCHV"  # DC High Voltage, enters another amplifier before going to the output (+/-)
+
+
+class SASSetWaveformFileTypes(CustomStrEnum):
+    """Supported file types for the command ``MMEMory:OPEN:SASSet[:WAVeform]``."""
+
+    AWG = ".awg"
+    AWGX = ".awgx"
+    MAT = ".mat"
+    SEQX = ".seqx"
+
+
+class LoadImpedanceAFG(Enum):
+    """AFG/IAFG Impedance Values."""
+
+    HIGHZ = "HIGHZ"  # High Impedance (1 Mega Ohm) termination
+    FIFTY = "FIFTY"  # 50 Ohm termination


Test patch:
diff --git a/tests/samples/simulated_config.yaml b/tests/samples/simulated_config.yaml
index 149a751b..61813341 100644
--- a/tests/samples/simulated_config.yaml
+++ b/tests/samples/simulated_config.yaml
@@ -1,6 +1,6 @@
 ---
 devices:
-  - address: AFG3KC-HOSTNAME
+  - address: AFG3252C-HOSTNAME
     device_type: AFG
   - address: MSO58B-HOSTNAME
     alias: MSO58B
diff --git a/tests/samples/simulated_config_no_cleanup.yaml b/tests/samples/simulated_config_no_cleanup.yaml
index 0164147f..19844f8f 100644
--- a/tests/samples/simulated_config_no_cleanup.yaml
+++ b/tests/samples/simulated_config_no_cleanup.yaml
@@ -1,6 +1,6 @@
 ---
 devices:
-  - address: AFG3KC-HOSTNAME
+  - address: AFG3252C-HOSTNAME
     device_type: AFG
   - address: MSO58B-HOSTNAME
     alias: MSO58B
diff --git a/tests/sim_devices/afg/afg31k.yaml b/tests/sim_devices/afg/afg31k.yaml
index 11a32025..7b2d3fbb 100644
--- a/tests/sim_devices/afg/afg31k.yaml
+++ b/tests/sim_devices/afg/afg31k.yaml
@@ -29,3 +29,59 @@ devices:
         - q: SYSTEM:ERROR?
           default: 0,"No error"
           command_error: 1, Command error
+  afg31051:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AFG31051,C100101,SCPI:99.0 FV:1.0
+  afg31101:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AFG31101,C100101,SCPI:99.0 FV:1.0
+  afg31152:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AFG31152,C100101,SCPI:99.0 FV:1.0
+  afg31251:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AFG31251,C100101,SCPI:99.0 FV:1.0
diff --git a/tests/sim_devices/afg/afg3k.yaml b/tests/sim_devices/afg/afg3k.yaml
index aafb3582..6d024948 100644
--- a/tests/sim_devices/afg/afg3k.yaml
+++ b/tests/sim_devices/afg/afg3k.yaml
@@ -1,7 +1,7 @@
 ---
 spec: '1.1'
 devices:
-  afg3252:
+  afg3051:
     eom:
       USB INSTR:
         q: "\n"
@@ -14,7 +14,7 @@ devices:
         r: "\n"
     dialogues:
       - q: '*IDN?'
-        r: TEKTRONIX,AFG3252,C022738,SCPI:99.0 FV:3.2.3
+        r: TEKTRONIX,AFG3051,C022738,SCPI:99.0 FV:3.2.3
       - q: '*OPC?'
         r: '1'
       - q: '*RST'
diff --git a/tests/sim_devices/afg/afg3kb.yaml b/tests/sim_devices/afg/afg3kb.yaml
index 543e31f0..abe83b28 100644
--- a/tests/sim_devices/afg/afg3kb.yaml
+++ b/tests/sim_devices/afg/afg3kb.yaml
@@ -1,7 +1,7 @@
 ---
 spec: '1.1'
 devices:
-  afg3kb:
+  afg3022b:
     eom:
       USB INSTR:
         q: "\n"
diff --git a/tests/sim_devices/afg/afg3kc.yaml b/tests/sim_devices/afg/afg3kc.yaml
index b10b8a00..9ee77df2 100644
--- a/tests/sim_devices/afg/afg3kc.yaml
+++ b/tests/sim_devices/afg/afg3kc.yaml
@@ -1,7 +1,77 @@
 ---
 spec: '1.1'
 devices:
-  afg3kc:
+  afg3011c:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AFG3011C,SERIAL,SCPI:99.0 FV:3.2.3
+  afg3021c:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AFG3021C,SERIAL,SCPI:99.0 FV:3.2.3
+  afg3052c:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AFG3052C,SERIAL,SCPI:99.0 FV:3.2.3
+  afg3101c:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AFG3101C,SERIAL,SCPI:99.0 FV:3.2.3
+  afg3151c:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AFG3151C,SERIAL3,SCPI:99.0 FV:3.2.3
+  afg3252c:
     eom:
       USB INSTR:
         q: "\n"
@@ -81,11 +151,9 @@ devices:
         default: 50.0
         getter:
           q: SOURCE1:PULSE:DCYCLE?
-          r: '{:f}'
+          r: '{:s}'
         setter:
-          q: SOURCE1:PULSE:DCYCLE {:f}
-        specs:
-          type: float
+          q: SOURCE1:PULSE:DCYCLE {:s}
       output_1_polarity:
         default: NORM
         getter:
@@ -316,51 +384,3 @@ devices:
         - q: SYSTEM:ERROR?
           default: 0,"No error"
           command_error: 1, Command error
-  afg3kc-1ch:
-    eom:
-      USB INSTR:
-        q: "\n"
-        r: "\n"
-      TCPIP INSTR:
-        q: "\n"
-        r: "\n"
-      TCPIP SOCKET:
-        q: "\n"
-        r: "\n"
-    dialogues:
-      - q: '*IDN?'
-        r: TEKTRONIX,AFG3151C,SERIAL3,SCPI:99.0 FV:3.2.3
-      - q: '*OPC?'
-        r: '1'
-  afg3051c:
-    eom:
-      USB INSTR:
-        q: "\n"
-        r: "\n"
-      TCPIP INSTR:
-        q: "\n"
-        r: "\n"
-      TCPIP SOCKET:
-        q: "\n"
-        r: "\n"
-    dialogues:
-      - q: '*IDN?'
-        r: TEKTRONIX,AFG3051C,SERIAL4,SCPI:99.0 FV:3.2.3
-      - q: '*OPC?'
-        r: '1'
-  afg3021c:
-    eom:
-      USB INSTR:
-        q: "\n"
-        r: "\n"
-      TCPIP INSTR:
-        q: "\n"
-        r: "\n"
-      TCPIP SOCKET:
-        q: "\n"
-        r: "\n"
-    dialogues:
-      - q: '*IDN?'
-        r: TEKTRONIX,AFG3021C,SERIAL,SCPI:99.0 FV:3.2.3
-      - q: '*OPC?'
-        r: '1'
diff --git a/tests/sim_devices/awg/awg5200.yaml b/tests/sim_devices/awg/awg5200.yaml
index 4049296a..7914a98c 100644
--- a/tests/sim_devices/awg/awg5200.yaml
+++ b/tests/sim_devices/awg/awg5200.yaml
@@ -1,7 +1,7 @@
 ---
 spec: '1.1'
 devices:
-  awg5200:
+  awg5200opt50:
     eom:
       USB INSTR:
         q: "\n"
@@ -19,12 +19,17 @@ devices:
       - q: '*OPC?'
         r: '1'
       - q: '*OPT?'
-        r: '0'
+        r: 50,SEQ
       - q: '*RST'
       - q: '*CLS'
+      - q: '*WAI'
       - q: AWGControl:CONFigure:CNUMber?
         r: '4'
       - q: 'MMEMory:IMPort '
+      - q: AWGCONTROL:RUN
+      - q: WLISt:LIST?
+        r: '"*Triangle960,*Triangle1000,*Triangle100,*Triangle10,*Square960,*Square1000,*Square100,*Square10,*Sine960,*Sine3600,*Sine360,*Sine36,*Sine1000,*Sine100,*Sine10,*Ramp96
+          0,*Ramp1000,*Ramp100,*Ramp10,*DC"'
     error:
       status_register:
         - q: '*ESR?'
@@ -46,3 +51,220 @@ devices:
           min: 0
           max: 255
           type: int
+      output_1_state:
+        default: 0
+        getter:
+          q: OUTPUT1:STATE?
+          r: '{:d}'
+        setter:
+          q: OUTPUT1:STATE {:d}
+        specs:
+          valid: [0, 1]
+          type: int
+      clock_srate:
+        default: 5000000000.0
+        getter:
+          q: CLOCK:SRATE?
+          r: '{:f}'
+        setter:
+          q: CLOCK:SRATE {:f}
+        specs:
+          min: 300.0
+          max: 5000000000.0
+          type: float
+      source1_waveform:
+        default: ''
+        getter:
+          q: SOURCE1:WAVEFORM?
+          r: '{:s}'
+        setter:
+          q: SOURCE1:WAVEFORM {:s}
+      source1_amplitude:
+        default: 0.5
+        getter:
+          q: SOURCE1:VOLTAGE:AMPLITUDE?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:AMPLITUDE {:f}
+        specs:
+          min: 0.1
+          max: 2.0
+          type: float
+      source1_offset:
+        default: 0.0
+        getter:
+          q: SOURCE1:VOLTAGE:OFFSET?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:OFFSET {:f}
+        specs:
+          min: -0.5
+          max: 0.5
+          type: float
+      rstate:
+        default: 2
+        getter:
+          q: AWGControl:RSTate?
+          r: '{:d}'
+        setter:
+          q: AWGControl:RSTate {:d}
+        specs:
+          type: int
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
+      output1_path:
+        default: DCHB
+        getter:
+          q: OUTPUT1:PATH?
+          r: '{:s}'
+        setter:
+          q: OUTPUT1:PATH {:s}
+        specs:
+          type: str
+          valid: [DCHB, DCHV, ACD]
+      mmemory_open_sasset:
+        default: ''
+        setter:
+          q: MMEMORY:OPEN:SASSET "{:s}"
+      mmemory_open_sasset_waveform:
+        default: ''
+        setter:
+          q: MMEMORY:OPEN:SASSET:WAVEFORM "{:s}", "{:s}"
+  awg5200opt25:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AWG5204,SERIAL1,FV:6.3.0080.0
+      - q: '*OPC'
+      - q: '*OPC?'
+        r: '1'
+      - q: '*OPT?'
+        r: 25,DC
+      - q: '*RST'
+      - q: '*CLS'
+      - q: '*WAI'
+      - q: AWGCONTROL:RUN
+      - q: AWGControl:CONFigure:CNUMber?
+        r: '4'
+      - q: 'MMEMory:IMPort '
+      - q: WLISt:LIST?
+        r: '"*DC"'
+    error:
+      status_register:
+        - q: '*ESR?'
+          command_error: 32
+          query_error: 4
+      error_queue:
+        - q: SYSTEM:ERROR?
+          default: 0,"No error"
+          command_error: 1, "Command error"
+    properties:
+      sre:
+        default: 0
+        getter:
+          q: '*SRE?'
+          r: '{:d}'
+        setter:
+          q: '*SRE {:d}'
+        specs:
+          min: 0
+          max: 255
+          type: int
+      output_1_state:
+        default: 0
+        getter:
+          q: OUTPUT1:STATE?
+          r: '{:d}'
+        setter:
+          q: OUTPUT1:STATE {:d}
+        specs:
+          valid: [0, 1]
+          type: int
+      clock_srate:
+        default: 5000000000.0
+        getter:
+          q: CLOCK:SRATE?
+          r: '{:f}'
+        setter:
+          q: CLOCK:SRATE {:f}
+        specs:
+          min: 300.0
+          max: 5000000000.0
+          type: float
+      source1_waveform:
+        default: ''
+        getter:
+          q: SOURCE1:WAVEFORM?
+          r: '{:s}'
+        setter:
+          q: SOURCE1:WAVEFORM {:s}
+      source1_amplitude:
+        default: 0.5
+        getter:
+          q: SOURCE1:VOLTAGE:AMPLITUDE?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:AMPLITUDE {:f}
+        specs:
+          min: 0.1
+          max: 2.0
+          type: float
+      source1_offset:
+        default: 0.0
+        getter:
+          q: SOURCE1:VOLTAGE:OFFSET?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:OFFSET {:f}
+        specs:
+          min: -0.5
+          max: 0.5
+          type: float
+      rstate:
+        default: 2
+        getter:
+          q: AWGControl:RSTate?
+          r: '{:d}'
+        setter:
+          q: AWGControl:RSTate {:d}
+        specs:
+          type: int
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
+      output1_path:
+        default: DCHB
+        getter:
+          q: OUTPUT1:PATH?
+          r: '{:s}'
+        setter:
+          q: OUTPUT1:PATH {:s}
+        specs:
+          type: str
+          valid: [DCHB, DCHV, ACD]
+      mmemory_open_sasset:
+        default: ''
+        setter:
+          q: MMEMORY:OPEN:SASSET "{:s}"
+      mmemory_open_sasset_waveform:
+        default: ''
+        setter:
+          q: MMEMORY:OPEN:SASSET:WAVEFORM "{:s}", "{:s}"
diff --git a/tests/sim_devices/awg/awg5k.yaml b/tests/sim_devices/awg/awg5k.yaml
index f2ba5e64..feece028 100644
--- a/tests/sim_devices/awg/awg5k.yaml
+++ b/tests/sim_devices/awg/awg5k.yaml
@@ -16,13 +16,15 @@ devices:
       - q: '*IDN?'
         r: TEKTRONIX,AWG5012,SERIAL1,FV:6.3.0080.0
       - q: '*OPC'
+      - q: '*OPT?'
+        r: '01'
       - q: '*OPC?'
         r: '1'
       - q: '*RST'
       - q: '*CLS'
       - q: AWGControl:CONFigure:CNUMber?
         r: '2'
-      - q: 'MMEMory:IMPort '
+      - q: AWGCONTROL:RUN
     error:
       status_register:
         - q: '*ESR?'
@@ -44,3 +46,70 @@ devices:
           min: 0
           max: 255
           type: int
+      output_1_state:
+        default: 0
+        getter:
+          q: OUTPUT1:STATE?
+          r: '{:d}'
+        setter:
+          q: OUTPUT1:STATE {:d}
+        specs:
+          valid: [0, 1]
+          type: int
+      source1_frequency:
+        default: 10000000.0
+        getter:
+          q: SOURCE1:FREQUENCY?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:FREQUENCY {:f}
+        specs:
+          min: 10000000.0
+          max: 1200000000.0
+          type: float
+      source1_waveform:
+        default: ''
+        getter:
+          q: SOURCE1:WAVEFORM?
+          r: '{:s}'
+        setter:
+          q: SOURCE1:WAVEFORM {:s}
+      source1_amplitude:
+        default: 0.5
+        getter:
+          q: SOURCE1:VOLTAGE:AMPLITUDE?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:AMPLITUDE {:f}
+        specs:
+          min: 0.02
+          max: 4.5
+          type: float
+      source1_offset:
+        default: 0.0
+        getter:
+          q: SOURCE1:VOLTAGE:OFFSET?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:OFFSET {:f}
+        specs:
+          min: -2.25
+          max: 2.25
+          type: float
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
+      awgcontrol_doutput_state:
+        default: 0
+        getter:
+          q: AWGCONTROL:DOUTPUT1:STATE?
+          r: '{:d}'
+        setter:
+          q: AWGCONTROL:DOUTPUT1:STATE {:d}
+        specs:
+          valid: [0, 1]
+          type: int
diff --git a/tests/sim_devices/awg/awg5kb.yaml b/tests/sim_devices/awg/awg5kb.yaml
index 7e1a4bc6..6d4dd2cc 100644
--- a/tests/sim_devices/awg/awg5kb.yaml
+++ b/tests/sim_devices/awg/awg5kb.yaml
@@ -1,7 +1,7 @@
 ---
 spec: '1.1'
 devices:
-  awg5012b:
+  awg5002b:
     eom:
       USB INSTR:
         q: "\n"
@@ -22,7 +22,6 @@ devices:
       - q: '*CLS'
       - q: AWGControl:CONFigure:CNUMber?
         r: '2'
-      - q: 'MMEMory:IMPort '
     error:
       status_register:
         - q: '*ESR?'
@@ -44,3 +43,10 @@ devices:
           min: 0
           max: 255
           type: int
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
diff --git a/tests/sim_devices/awg/awg5kc.yaml b/tests/sim_devices/awg/awg5kc.yaml
index b64bce79..770f0950 100644
--- a/tests/sim_devices/awg/awg5kc.yaml
+++ b/tests/sim_devices/awg/awg5kc.yaml
@@ -22,7 +22,6 @@ devices:
       - q: '*CLS'
       - q: AWGControl:CONFigure:CNUMber?
         r: '2'
-      - q: 'MMEMory:IMPort '
     error:
       status_register:
         - q: '*ESR?'
@@ -44,3 +43,10 @@ devices:
           min: 0
           max: 255
           type: int
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
diff --git a/tests/sim_devices/awg/awg70ka.yaml b/tests/sim_devices/awg/awg70ka.yaml
index daf62b4b..e3b74393 100644
--- a/tests/sim_devices/awg/awg70ka.yaml
+++ b/tests/sim_devices/awg/awg70ka.yaml
@@ -1,7 +1,7 @@
 ---
 spec: '1.1'
 devices:
-  awg70001a:
+  awg70001aopt150:
     eom:
       USB INSTR:
         q: "\n"
@@ -18,11 +18,17 @@ devices:
       - q: '*OPC'
       - q: '*OPC?'
         r: '1'
+      - q: '*OPT?'
+        r: '150'
       - q: '*RST'
       - q: '*CLS'
       - q: AWGControl:CONFigure:CNUMber?
         r: '1'
       - q: 'MMEMory:IMPort '
+      - q: AWGCONTROL:RUN
+      - q: WLISt:LIST?
+        r: '"*Clock960,*Triangle960,*Triangle1000,*Triangle100,*Triangle10,*Square960,*Square1000,*Square100,*Square10,*Sine960,*Sine3600,*Sine360,*Sine36,*Sine1000,*Sine100,*Sine10,*Ramp96
+          0,*Ramp1000,*Ramp100,*Ramp10,*DC"'
     error:
       status_register:
         - q: '*ESR?'
@@ -44,3 +50,340 @@ devices:
           min: 0
           max: 255
           type: int
+      output_1_state:
+        default: 0
+        getter:
+          q: OUTPUT1:STATE?
+          r: '{:d}'
+        setter:
+          q: OUTPUT1:STATE {:d}
+        specs:
+          valid: [0, 1]
+          type: int
+      source1_waveform:
+        default: ''
+        getter:
+          q: SOURCE1:WAVEFORM?
+          r: '{:s}'
+        setter:
+          q: SOURCE1:WAVEFORM {:s}
+      source1_frequency:
+        default: 25000000000
+        getter:
+          q: SOURCE1:FREQUENCY?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:FREQUENCY {:f}
+        specs:
+          min: 1490
+          max: 50000000000
+          type: float
+      source1_amplitude:
+        default: 0.5
+        getter:
+          q: SOURCE1:VOLTAGE:AMPLITUDE?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:AMPLITUDE {:f}
+        specs:
+          min: 0.5
+          max: 1.0
+          type: float
+      source1_offset:
+        default: 0.0
+        getter:
+          q: SOURCE1:VOLTAGE:OFFSET?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:OFFSET {:f}
+        specs:
+          min: -0.5
+          max: 0.5
+          type: float
+      output1_path:
+        default: DIR
+        getter:
+          q: OUTPUT1:PATH?
+          r: '{:s}'
+        setter:
+          q: OUTPUT1:PATH {:s}
+        specs:
+          type: str
+          valid: [DIR, DCA]
+      output2_path:
+        default: DIR
+        getter:
+          q: OUTPUT2:PATH?
+          r: '{:s}'
+        setter:
+          q: OUTPUT2:PATH {:s}
+        specs:
+          type: str
+          valid: [DIR, DCA]
+      source2_offset:
+        default: 0.0
+        getter:
+          q: SOURCE2:VOLTAGE:OFFSET?
+          r: '{:f}'
+        setter:
+          q: SOURCE2:VOLTAGE:OFFSET {:f}
+        specs:
+          min: -0.5
+          max: 0.5
+          type: float
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
+      mmemory_open_sasset:
+        default: ''
+        setter:
+          q: MMEMORY:OPEN:SASSET "{:s}"
+      mmemory_open_sasset_waveform:
+        default: ''
+        setter:
+          q: MMEMORY:OPEN:SASSET:WAVEFORM "{:s}", "{:s}"
+  awg70002aopt225:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AWG70002A,SERIAL1,FV:6.3.0080.0
+      - q: '*OPC'
+      - q: AWGCONTROL:RUN
+      - q: '*OPC?'
+        r: '1'
+      - q: '*OPT?'
+        r: '225'
+      - q: '*RST'
+      - q: '*CLS'
+      - q: AWGControl:CONFigure:CNUMber?
+        r: '1'
+      - q: 'MMEMory:IMPort '
+      - q: WLISt:LIST?
+        r: ''
+    error:
+      status_register:
+        - q: '*ESR?'
+          command_error: 32
+          query_error: 4
+      error_queue:
+        - q: SYSTEM:ERROR?
+          default: 0,"No error"
+          command_error: 1, "Command error"
+    properties:
+      sre:
+        default: 0
+        getter:
+          q: '*SRE?'
+          r: '{:d}'
+        setter:
+          q: '*SRE {:d}'
+        specs:
+          min: 0
+          max: 255
+          type: int
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
+      output_1_state:
+        default: 0
+        getter:
+          q: OUTPUT1:STATE?
+          r: '{:d}'
+        setter:
+          q: OUTPUT1:STATE {:d}
+        specs:
+          valid: [0, 1]
+          type: int
+      source1_waveform:
+        default: ''
+        getter:
+          q: SOURCE1:WAVEFORM?
+          r: '{:s}'
+        setter:
+          q: SOURCE1:WAVEFORM {:s}
+      source1_frequency:
+        default: 25000000000
+        getter:
+          q: SOURCE1:FREQUENCY?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:FREQUENCY {:f}
+        specs:
+          min: 1490
+          max: 50000000000
+          type: float
+      source1_amplitude:
+        default: 0.5
+        getter:
+          q: SOURCE1:VOLTAGE:AMPLITUDE?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:AMPLITUDE {:f}
+        specs:
+          min: 0.5
+          max: 1.0
+          type: float
+      output1_path:
+        default: DIR
+        getter:
+          q: OUTPUT1:PATH?
+          r: '{:s}'
+        setter:
+          q: OUTPUT1:PATH {:s}
+        specs:
+          type: str
+          valid: [DIR, DCA]
+      source1_offset:
+        default: 0.0
+        getter:
+          q: SOURCE1:VOLTAGE:OFFSET?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:OFFSET {:f}
+        specs:
+          min: -0.5
+          max: 0.5
+          type: float
+      output2_path:
+        default: DIR
+        getter:
+          q: OUTPUT2:PATH?
+          r: '{:s}'
+        setter:
+          q: OUTPUT2:PATH {:s}
+        specs:
+          type: str
+          valid: [DIR, DCA]
+      source2_offset:
+        default: 0.0
+        getter:
+          q: SOURCE2:VOLTAGE:OFFSET?
+          r: '{:f}'
+        setter:
+          q: SOURCE2:VOLTAGE:OFFSET {:f}
+        specs:
+          min: -0.5
+          max: 0.5
+          type: float
+      mmemory_open_sasset:
+        default: ''
+        setter:
+          q: MMEMORY:OPEN:SASSET "{:s}"
+      mmemory_open_sasset_waveform:
+        default: ''
+        setter:
+          q: MMEMORY:OPEN:SASSET:WAVEFORM "{:s}", "{:s}"
+  awg70002aopt216:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AWG70002A,SERIAL1,FV:6.3.0080.0
+      - q: '*OPC'
+      - q: '*OPC?'
+        r: '1'
+      - q: '*OPT?'
+        r: '216'
+      - q: '*RST'
+      - q: '*CLS'
+      - q: AWGControl:CONFigure:CNUMber?
+        r: '1'
+      - q: 'MMEMory:IMPort '
+      - q: WLISt:LIST?
+        r: '"*Triangle960,*Triangle1000,*Triangle100,*Triangle10,*Square960,*Square1000,*Square100,*Square10,*Sine960,*Sine3600,*Sine360,*Sine36,*Sine1000,*Sine100,*Sine10,*Ramp96
+          0,*Ramp1000,*Ramp100,*Ramp10,*DC"'
+    error:
+      status_register:
+        - q: '*ESR?'
+          command_error: 32
+          query_error: 4
+      error_queue:
+        - q: SYSTEM:ERROR?
+          default: 0,"No error"
+          command_error: 1, "Command error"
+    properties:
+      sre:
+        default: 0
+        getter:
+          q: '*SRE?'
+          r: '{:d}'
+        setter:
+          q: '*SRE {:d}'
+        specs:
+          min: 0
+          max: 255
+          type: int
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
+      output1_path:
+        default: DIR
+        getter:
+          q: OUTPUT1:PATH?
+          r: '{:s}'
+        setter:
+          q: OUTPUT1:PATH {:s}
+        specs:
+          type: str
+          valid: [DIR, DCA]
+      source1_offset:
+        default: 0.0
+        getter:
+          q: SOURCE1:VOLTAGE:OFFSET?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:OFFSET {:f}
+        specs:
+          min: -0.5
+          max: 0.5
+          type: float
+      output2_path:
+        default: DIR
+        getter:
+          q: OUTPUT2:PATH?
+          r: '{:s}'
+        setter:
+          q: OUTPUT2:PATH {:s}
+        specs:
+          type: str
+          valid: [DIR, DCA]
+      source2_offset:
+        default: 0.0
+        getter:
+          q: SOURCE2:VOLTAGE:OFFSET?
+          r: '{:f}'
+        setter:
+          q: SOURCE2:VOLTAGE:OFFSET {:f}
+        specs:
+          min: -0.5
+          max: 0.5
+          type: float
diff --git a/tests/sim_devices/awg/awg70kb.yaml b/tests/sim_devices/awg/awg70kb.yaml
index a9d3ccc6..d6ebacc0 100644
--- a/tests/sim_devices/awg/awg70kb.yaml
+++ b/tests/sim_devices/awg/awg70kb.yaml
@@ -1,7 +1,7 @@
 ---
 spec: '1.1'
 devices:
-  awg70002b:
+  awg70002bopt208:
     eom:
       USB INSTR:
         q: "\n"
@@ -18,11 +18,12 @@ devices:
       - q: '*OPC'
       - q: '*OPC?'
         r: '1'
+      - q: '*OPT?'
+        r: '208'
       - q: '*RST'
       - q: '*CLS'
       - q: AWGControl:CONFigure:CNUMber?
         r: '2'
-      - q: 'MMEMory:IMPort '
     error:
       status_register:
         - q: '*ESR?'
@@ -44,3 +45,52 @@ devices:
           min: 0
           max: 255
           type: int
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
+      output1_path:
+        default: DIR
+        getter:
+          q: OUTPUT1:PATH?
+          r: '{:s}'
+        setter:
+          q: OUTPUT1:PATH {:s}
+        specs:
+          type: str
+          valid: [DIR, DCA]
+      source1_offset:
+        default: 0.0
+        getter:
+          q: SOURCE1:VOLTAGE:OFFSET?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:OFFSET {:f}
+        specs:
+          min: -0.5
+          max: 0.5
+          type: float
+      output2_path:
+        default: DIR
+        getter:
+          q: OUTPUT2:PATH?
+          r: '{:s}'
+        setter:
+          q: OUTPUT2:PATH {:s}
+        specs:
+          type: str
+          valid: [DIR, DCA]
+      source2_offset:
+        default: 0.0
+        getter:
+          q: SOURCE2:VOLTAGE:OFFSET?
+          r: '{:f}'
+        setter:
+          q: SOURCE2:VOLTAGE:OFFSET {:f}
+        specs:
+          min: -0.5
+          max: 0.5
+          type: float
diff --git a/tests/sim_devices/awg/awg7k.yaml b/tests/sim_devices/awg/awg7k.yaml
index a830ad6f..d5940f97 100644
--- a/tests/sim_devices/awg/awg7k.yaml
+++ b/tests/sim_devices/awg/awg7k.yaml
@@ -1,7 +1,7 @@
 ---
 spec: '1.1'
 devices:
-  awg7122:
+  awg7051opt01:
     eom:
       USB INSTR:
         q: "\n"
@@ -14,15 +14,18 @@ devices:
         r: "\n"
     dialogues:
       - q: '*IDN?'
-        r: TEKTRONIX,AWG7122,B010123,SCPI:99.0 FW:3.0.136.602
+        r: TEKTRONIX,AWG7051,B010123,SCPI:99.0 FW:3.0.136.602
       - q: '*OPC'
       - q: '*OPC?'
         r: '1'
+      - q: '*OPT?'
+        r: '01'
       - q: '*RST'
       - q: '*CLS'
       - q: AWGControl:CONFigure:CNUMber?
         r: '2'
       - q: 'MMEMory:IMPort '
+      - q: AWGCONTROL:RUN
     error:
       status_register:
         - q: '*ESR?'
@@ -44,3 +47,215 @@ devices:
           min: 0
           max: 255
           type: int
+      rmode:
+        default: CONT
+        getter:
+          q: AWGCONTROL:RMODE?
+          r: '{:s}'
+        setter:
+          q: AWGCONTROL:RMODE {:s}
+      sequence_length:
+        default: 0
+        getter:
+          q: SEQUENCE:LENGTH?
+          r: '{:d}'
+        setter:
+          q: SEQUENCE:LENGTH {:d}
+        specs:
+          type: int
+      element1_waveform1:
+        default: ''
+        getter:
+          q: SEQUENCE:ELEMENT1:WAVEFORM1?
+          r: '{:s}'
+        setter:
+          q: SEQUENCE:ELEMENT1:WAVEFORM1 {:s}
+      element1_loop_count:
+        default: 0
+        getter:
+          q: SEQUENCE:ELEMENT1:LOOP:COUNT?
+          r: '{:d}'
+        setter:
+          q: SEQUENCE:ELEMENT1:LOOP:COUNT {:d}
+        specs:
+          type: int
+      output_1_state:
+        default: 0
+        getter:
+          q: OUTPUT1:STATE?
+          r: '{:d}'
+        setter:
+          q: OUTPUT1:STATE {:d}
+        specs:
+          valid: [0, 1]
+          type: int
+      source1_frequency:
+        default: 10000000.0
+        getter:
+          q: SOURCE1:FREQUENCY?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:FREQUENCY {:f}
+        specs:
+          min: 10000000.0
+          max: 51000000000.0
+          type: float
+      source1_waveform:
+        default: ''
+        getter:
+          q: SOURCE1:WAVEFORM?
+          r: '{:s}'
+        setter:
+          q: SOURCE1:WAVEFORM {:s}
+      source1_amplitude:
+        default: 0.5
+        getter:
+          q: SOURCE1:VOLTAGE:AMPLITUDE?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:AMPLITUDE {:f}
+        specs:
+          min: .05
+          max: 2.0
+          type: float
+      source1_offset:
+        default: 0.0
+        getter:
+          q: SOURCE1:VOLTAGE:OFFSET?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:OFFSET {:f}
+        specs:
+          min: -0.5
+          max: 0.5
+          type: float
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
+      awgcontrol_doutput_state:
+        default: 0
+        getter:
+          q: AWGCONTROL:DOUTPUT1:STATE?
+          r: '{:d}'
+        setter:
+          q: AWGCONTROL:DOUTPUT1:STATE {:d}
+        specs:
+          valid: [0, 1]
+          type: int
+  awg7102opt06:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AWG7102,B010123,SCPI:99.0 FW:3.0.136.602
+      - q: '*OPC'
+      - q: '*OPC?'
+        r: '1'
+      - q: '*OPT?'
+        r: '06'
+      - q: '*RST'
+      - q: '*CLS'
+      - q: AWGControl:CONFigure:CNUMber?
+        r: '2'
+      - q: 'MMEMory:IMPort '
+      - q: AWGCONTROL:RUN
+    error:
+      status_register:
+        - q: '*ESR?'
+          command_error: 32
+          query_error: 4
+      error_queue:
+        - q: SYSTEM:ERROR?
+          default: 0,"No error"
+          command_error: 1, "Command error"
+    properties:
+      sre:
+        default: 0
+        getter:
+          q: '*SRE?'
+          r: '{:d}'
+        setter:
+          q: '*SRE {:d}'
+        specs:
+          min: 0
+          max: 255
+          type: int
+      output_1_state:
+        default: 0
+        getter:
+          q: OUTPUT1:STATE?
+          r: '{:d}'
+        setter:
+          q: OUTPUT1:STATE {:d}
+        specs:
+          valid: [0, 1]
+          type: int
+      source1_frequency:
+        default: 10000000.0
+        getter:
+          q: SOURCE1:FREQUENCY?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:FREQUENCY {:f}
+        specs:
+          min: 10000000.0
+          max: 102000000000.0
+          type: float
+      source1_waveform:
+        default: ''
+        getter:
+          q: SOURCE1:WAVEFORM?
+          r: '{:s}'
+        setter:
+          q: SOURCE1:WAVEFORM {:s}
+      source1_amplitude:
+        default: 0.5
+        getter:
+          q: SOURCE1:VOLTAGE:AMPLITUDE?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:AMPLITUDE {:f}
+        specs:
+          min: 0.5
+          max: 1
+          type: float
+      source1_offset:
+        default: 0.0
+        getter:
+          q: SOURCE1:VOLTAGE:OFFSET?
+          r: '{:f}'
+        setter:
+          q: SOURCE1:VOLTAGE:OFFSET {:f}
+        specs:
+          min: -0.5
+          max: 0.5
+          type: float
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
+      awgcontrol_doutput_state:
+        default: 0
+        getter:
+          q: AWGCONTROL:DOUTPUT1:STATE?
+          r: '{:d}'
+        setter:
+          q: AWGCONTROL:DOUTPUT1:STATE {:d}
+        specs:
+          valid: [0, 1]
+          type: int
diff --git a/tests/sim_devices/awg/awg7kb.yaml b/tests/sim_devices/awg/awg7kb.yaml
index 401ca4bb..fb7fba3a 100644
--- a/tests/sim_devices/awg/awg7kb.yaml
+++ b/tests/sim_devices/awg/awg7kb.yaml
@@ -1,7 +1,7 @@
 ---
 spec: '1.1'
 devices:
-  awg7122b:
+  awg7062bopt02:
     eom:
       USB INSTR:
         q: "\n"
@@ -14,10 +14,12 @@ devices:
         r: "\n"
     dialogues:
       - q: '*IDN?'
-        r: TEKTRONIX,AWG7122B,B010123,SCPI:99.0 FW:3.0.136.602
+        r: TEKTRONIX,AWG7062B,B010123,SCPI:99.0 FW:3.0.136.602
       - q: '*OPC'
       - q: '*OPC?'
         r: '1'
+      - q: '*OPT?'
+        r: '02'
       - q: '*RST'
       - q: '*CLS'
       - q: AWGControl:CONFigure:CNUMber?
@@ -44,3 +46,62 @@ devices:
           min: 0
           max: 255
           type: int
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
+  awg7121bopt01:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AWG7121B,B010123,SCPI:99.0 FW:3.0.136.602
+      - q: '*OPC'
+      - q: '*OPC?'
+        r: '1'
+      - q: '*OPT?'
+        r: '01'
+      - q: '*RST'
+      - q: '*CLS'
+      - q: AWGControl:CONFigure:CNUMber?
+        r: '2'
+      - q: 'MMEMory:IMPort '
+    error:
+      status_register:
+        - q: '*ESR?'
+          command_error: 32
+          query_error: 4
+      error_queue:
+        - q: SYSTEM:ERROR?
+          default: 0,"No error"
+          command_error: 1, "Command error"
+    properties:
+      sre:
+        default: 0
+        getter:
+          q: '*SRE?'
+          r: '{:d}'
+        setter:
+          q: '*SRE {:d}'
+        specs:
+          min: 0
+          max: 255
+          type: int
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
diff --git a/tests/sim_devices/awg/awg7kc.yaml b/tests/sim_devices/awg/awg7kc.yaml
index a5aafea5..72588a30 100644
--- a/tests/sim_devices/awg/awg7kc.yaml
+++ b/tests/sim_devices/awg/awg7kc.yaml
@@ -1,7 +1,59 @@
 ---
 spec: '1.1'
 devices:
-  awg7122c:
+  awg7082copt01:
+    eom:
+      USB INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP INSTR:
+        q: "\n"
+        r: "\n"
+      TCPIP SOCKET:
+        q: "\n"
+        r: "\n"
+    dialogues:
+      - q: '*IDN?'
+        r: TEKTRONIX,AWG7082C,B010123,SCPI:99.0 FW:3.0.136.602
+      - q: '*OPC'
+      - q: '*OPC?'
+        r: '1'
+      - q: '*OPT?'
+        r: '01'
+      - q: '*RST'
+      - q: '*CLS'
+      - q: AWGControl:CONFigure:CNUMber?
+        r: '2'
+      - q: 'MMEMory:IMPort '
+    error:
+      status_register:
+        - q: '*ESR?'
+          command_error: 32
+          query_error: 4
+      error_queue:
+        - q: SYSTEM:ERROR?
+          default: 0,"No error"
+          command_error: 1, "Command error"
+    properties:
+      sre:
+        default: 0
+        getter:
+          q: '*SRE?'
+          r: '{:d}'
+        setter:
+          q: '*SRE {:d}'
+        specs:
+          min: 0
+          max: 255
+          type: int
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
+  awg7122copt06:
     eom:
       USB INSTR:
         q: "\n"
@@ -18,6 +70,8 @@ devices:
       - q: '*OPC'
       - q: '*OPC?'
         r: '1'
+      - q: '*OPT?'
+        r: '06'
       - q: '*RST'
       - q: '*CLS'
       - q: AWGControl:CONFigure:CNUMber?
@@ -44,3 +98,10 @@ devices:
           min: 0
           max: 255
           type: int
+      memory_import:
+        default: ''
+        getter:
+          q: MMEMory:IMPort?
+          r: '{:s}'
+        setter:
+          q: MMEMory:IMPort {:s}
diff --git a/tests/sim_devices/devices.yaml b/tests/sim_devices/devices.yaml
index 2a8cfb5f..084b6458 100644
--- a/tests/sim_devices/devices.yaml
+++ b/tests/sim_devices/devices.yaml
@@ -57,67 +57,104 @@ resources:
 
   # --- afg ---
   #   afg3k
-  TCPIP::AFG3K-HOSTNAME::INSTR:
-    device: afg3252
+  TCPIP::AFG3051-HOSTNAME::INSTR:
+    device: afg3051
     filename: afg/afg3k.yaml
-  TCPIP::AFG3KB-HOSTNAME::INSTR:
-    device: afg3kb
+  TCPIP::AFG3022B-HOSTNAME::INSTR:
+    device: afg3022b
     filename: afg/afg3kb.yaml
-  TCPIP::AFG3KC-HOSTNAME::INSTR:
-    device: afg3kc
-    filename: afg/afg3kc.yaml
-  TCPIP::AFG3KC-HOSTNAME::10001::SOCKET:
-    device: afg3kc
-    filename: afg/afg3kc.yaml
   TCPIP::AFG3KC-TIMEOUT::INSTR:
     device: afg3kc-timeout
     filename: afg/afg3kc.yaml
-  TCPIP::AFG3KC-1CH::INSTR:
-    device: afg3kc-1ch
-    filename: afg/afg3kc.yaml
-  TCPIP::AFG3051C::INSTR:
-    device: afg3051c
+  TCPIP::AFG3011C-HOSTNAME::INSTR:
+    device: afg3011c
     filename: afg/afg3kc.yaml
-  TCPIP::AFG3021C::INSTR:
+  TCPIP::AFG3021C-HOSTNAME::INSTR:
     device: afg3021c
     filename: afg/afg3kc.yaml
+  TCPIP::AFG3052C-HOSTNAME::INSTR:
+    device: afg3052c
+    filename: afg/afg3kc.yaml
+  TCPIP::AFG3101C-HOSTNAME::INSTR:
+    device: afg3101c
+    filename: afg/afg3kc.yaml
+  TCPIP::AFG3151C-HOSTNAME::INSTR:
+    device: afg3151c
+    filename: afg/afg3kc.yaml
+  TCPIP::AFG3252C-HOSTNAME::INSTR:
+    device: afg3252c
+    filename: afg/afg3kc.yaml
+  TCPIP::AFG3252C-HOSTNAME::10001::SOCKET:
+    device: afg3252c
+    filename: afg/afg3kc.yaml
+
   #   afg31k
-  TCPIP::AFG31K-HOSTNAME::INSTR:
+  TCPIP::AFG31021-HOSTNAME::INSTR:
     device: afg31021
     filename: afg/afg31k.yaml
+  TCPIP::AFG31051-HOSTNAME::INSTR:
+    device: afg31051
+    filename: afg/afg31k.yaml
+  TCPIP::AFG31101-HOSTNAME::INSTR:
+    device: afg31101
+    filename: afg/afg31k.yaml
+  TCPIP::AFG31152-HOSTNAME::INSTR:
+    device: afg31152
+    filename: afg/afg31k.yaml
+  TCPIP::AFG31251-HOSTNAME::INSTR:
+    device: afg31251
+    filename: afg/afg31k.yaml
 
   # --- awg ---
   #   awg5200
-  TCPIP::AWG5200-HOSTNAME::INSTR:
-    device: awg5200
+  TCPIP::AWG5200OPT50-HOSTNAME::INSTR:
+    device: awg5200opt50
+    filename: awg/awg5200.yaml
+  TCPIP::AWG5200OPT25-HOSTNAME::INSTR:
+    device: awg5200opt25
     filename: awg/awg5200.yaml
   #   awg5k
-  TCPIP::AWG5K-HOSTNAME::INSTR:
+  TCPIP::AWG5012-HOSTNAME::INSTR:
     device: awg5012
     filename: awg/awg5k.yaml
-  TCPIP::AWG5KB-HOSTNAME::INSTR:
-    device: awg5012b
+  TCPIP::AWG5002B-HOSTNAME::INSTR:
+    device: awg5002b
     filename: awg/awg5kb.yaml
-  TCPIP::AWG5KC-HOSTNAME::INSTR:
+  TCPIP::AWG5012C-HOSTNAME::INSTR:
     device: awg5012c
     filename: awg/awg5kc.yaml
   #   awg7k
-  TCPIP::AWG7K-HOSTNAME::INSTR:
-    device: awg7122
+  TCPIP::AWG7051OPT01-HOSTNAME::INSTR:
+    device: awg7051opt01
     filename: awg/awg7k.yaml
-  TCPIP::AWG7KB-HOSTNAME::INSTR:
-    device: awg7122b
+  TCPIP::AWG7102OPT06-HOSTNAME::INSTR:
+    device: awg7102opt06
+    filename: awg/awg7k.yaml
+  TCPIP::AWG7062BOPT02-HOSTNAME::INSTR:
+    device: awg7062bopt02
+    filename: awg/awg7kb.yaml
+  TCPIP::AWG7121BOPT01-HOSTNAME::INSTR:
+    device: awg7121bopt01
     filename: awg/awg7kb.yaml
-  TCPIP::AWG7KC-HOSTNAME::INSTR:
-    device: awg7122c
+  TCPIP::AWG7082COPT01-HOSTNAME::INSTR:
+    device: awg7082copt01
+    filename: awg/awg7kc.yaml
+  TCPIP::AWG7122COPT06-HOSTNAME::INSTR:
+    device: awg7122copt06
     filename: awg/awg7kc.yaml
   #   awg70k
-  TCPIP::AWG70KB-HOSTNAME::INSTR:
-    device: awg70002b
-    filename: awg/awg70kb.yaml
-  TCPIP::AWG70KA-HOSTNAME::INSTR:
-    device: awg70001a
+  TCPIP::AWG70001AOPT150-HOSTNAME::INSTR:
+    device: awg70001aopt150
+    filename: awg/awg70ka.yaml
+  TCPIP::AWG70002AOPT225-HOSTNAME::INSTR:
+    device: awg70002aopt225
     filename: awg/awg70ka.yaml
+  TCPIP::AWG70002AOPT216-HOSTNAME::INSTR:
+    device: awg70002aopt216
+    filename: awg/awg70ka.yaml
+  TCPIP::AWG70002BOPT208-HOSTNAME::INSTR:
+    device: awg70002bopt208
+    filename: awg/awg70kb.yaml
 
   # --- TekScope ---
   #   lpd6
diff --git a/tests/sim_devices/scope/mso5.yaml b/tests/sim_devices/scope/mso5.yaml
index c28ba102..3a44bb24 100644
--- a/tests/sim_devices/scope/mso5.yaml
+++ b/tests/sim_devices/scope/mso5.yaml
@@ -53,7 +53,7 @@ devices:
         r: REF1
       - q: HEADER OFF
       - q: :LICENSE:APPID?
-        r: LIC5,LIC4
+        r: LIC5,LIC4,AFG
       - q: RECALL:WAVEFORM "temp.wfm", REF1
       - q: :RECALL:WAVEFORM "REF1.wfm",REF1
       - q: :SAVE:WAVEFORM temp.wfm,"REF1.wfm"
diff --git a/tests/sim_devices/scope/mso5b.yaml b/tests/sim_devices/scope/mso5b.yaml
index 08992178..51ea7056 100644
--- a/tests/sim_devices/scope/mso5b.yaml
+++ b/tests/sim_devices/scope/mso5b.yaml
@@ -20,6 +20,8 @@ devices:
         r: MSO58B-200201
       - q: :FILESystem:LDIR?
         r: 'E:,F:,G:,H:,I:,J:,K:'
+      - q: :LICENSE:APPID?
+        r: AFG
       - q: :FILESystem:CWD "E:"
       - q: :FILESystem:CWD "F:"
       - q: :FILESystem:CWD "G:"
diff --git a/tests/test_afgs.py b/tests/test_afgs.py
index bc42578f..04592ab0 100644
--- a/tests/test_afgs.py
+++ b/tests/test_afgs.py
@@ -9,111 +9,221 @@
 from packaging.version import Version
 
 from tm_devices import DeviceManager
-from tm_devices.drivers.pi.signal_sources.afgs.afg import AFGSourceDeviceConstants
+from tm_devices.drivers.pi.signal_generators.afgs.afg import (
+    AFGSourceDeviceConstants,
+    ExtendedSourceDeviceConstants,
+    ParameterBounds,
+    SignalGeneratorFunctionsAFG,
+)
 from tm_devices.helpers.constants_and_dataclasses import UNIT_TEST_TIMEOUT
 
 
-def test_afg3kc(device_manager: DeviceManager) -> None:
+def test_afg3k(device_manager: DeviceManager) -> None:  # noqa: PLR0915  # pylint: disable=too-many-locals
     """Test the AFG3KC driver.
 
     Args:
         device_manager: The DeviceManager object.
     """
-    afg3kc = device_manager.add_afg(
-        "afg3kc-hostname", alias="afg3kc", connection_type="SOCKET", port=10001
-    )
-    assert id(device_manager.get_afg(number_or_alias="afg3kc")) == id(afg3kc)
-    assert id(device_manager.get_afg(number_or_alias=1)) == id(afg3kc)
-    assert afg3kc.visa_timeout == UNIT_TEST_TIMEOUT
-    assert afg3kc.default_visa_timeout == UNIT_TEST_TIMEOUT
-    assert afg3kc.resource_expression == "TCPIP0::AFG3KC-HOSTNAME::10001::SOCKET"
-    assert afg3kc.total_channels == 2
-    assert afg3kc.idn_string == "TEKTRONIX,AFG3252C,SERIAL1,SCPI:99.0 FV:3.2.3"
-    assert afg3kc.sw_version == Version("3.2.3")
-    assert afg3kc.all_channel_names_list == ("SOURCE1", "SOURCE2")
-    assert afg3kc.source_device_constants == AFGSourceDeviceConstants(
+    afg3252c = device_manager.add_afg(
+        "afg3252c-hostname", alias="afg3252c", connection_type="SOCKET", port=10001
+    )
+    assert id(device_manager.get_afg(number_or_alias="afg3252c")) == id(afg3252c)
+    assert id(device_manager.get_afg(number_or_alias=1)) == id(afg3252c)
+    assert afg3252c.visa_timeout == UNIT_TEST_TIMEOUT
+    assert afg3252c.default_visa_timeout == UNIT_TEST_TIMEOUT
+    assert afg3252c.resource_expression == "TCPIP0::AFG3252C-HOSTNAME::10001::SOCKET"
+    assert afg3252c.total_channels == 2
+    assert afg3252c.idn_string == "TEKTRONIX,AFG3252C,SERIAL1,SCPI:99.0 FV:3.2.3"
+    assert afg3252c.sw_version == Version("3.2.3")
+    assert afg3252c.all_channel_names_list == ("SOURCE1", "SOURCE2")
+    assert afg3252c.visa_backend == "PyVISA-sim"
+    assert afg3252c.source_device_constants == AFGSourceDeviceConstants(
         memory_page_size=2,
         memory_max_record_length=128 * 1024,
         memory_min_record_length=2,
     )
-    assert afg3kc.check_visa_connection()
-    afg3kc.write("*OPC?")
-    assert afg3kc.read() == "1"
-    assert afg3kc.opt_string == "0"
-    assert afg3kc.query("SYSTEM:ERROR?") == '0,"No error"'
-    assert afg3kc.query("SYSTEM:ERROR?", remove_quotes=True) == "0,No error"
-    assert afg3kc.expect_esr(0)[0]
+    assert afg3252c.check_visa_connection()
+    afg3252c.write("*OPC?")
+    assert afg3252c.read() == "1"
+    assert afg3252c.opt_string == "0"
+    assert afg3252c.query("SYSTEM:ERROR?") == '0,"No error"'
+    assert afg3252c.query("SYSTEM:ERROR?", remove_quotes=True) == "0,No error"
+    assert afg3252c.expect_esr(0)[0]
     with mock.patch("pyvisa.highlevel.VisaLibraryBase.clear", mock.MagicMock(return_value=None)):
-        assert afg3kc.query_expect_timeout("INVALID?", timeout_ms=1) == ""
+        assert afg3252c.query_expect_timeout("INVALID?", timeout_ms=1) == ""
     with mock.patch(
         "pyvisa.resources.messagebased.MessageBasedResource.query",
         mock.MagicMock(side_effect=visa.errors.Error("custom error")),
     ), pytest.raises(visa.errors.Error):
-        afg3kc.query_expect_timeout("INVALID?", timeout_ms=1)
-    assert afg3kc.expect_esr(32, '1, Command error\n0,"No error"')[0]
+        assert afg3252c.query_expect_timeout("INVALID?", timeout_ms=1) == ""
+    assert afg3252c.expect_esr(32, '1, Command error\n0,"No error"')[0]
     with pytest.raises(AssertionError):
-        afg3kc.expect_esr(32, '1, Command error\n0,"No error"')
-
-    afg3kc.generate_waveform(25e6, afg3kc.source_device_constants.functions.PULSE, 1.0, 0.0, "all")
-    afg3kc.generate_waveform(
-        25e6,
-        afg3kc.source_device_constants.functions.SIN,
-        1.0,
-        0.0,
-        "SOURCE1",
-        burst=1,
-        termination="HIGHZ",
-    )
-    afg3kc.generate_waveform(
-        25e6,
-        afg3kc.source_device_constants.functions.RAMP,
-        1.0,
-        0.0,
-        "SOURCE1",
-        burst=1,
-        termination="FIFTY",
-    )
-    afg3kc.generate_waveform(
-        25e6,
-        afg3kc.source_device_constants.functions.DC,
-        1.0,
-        0.0,
-        "SOURCE1",
-        termination="HIGHZ",
-    )
-    afg3kc.generate_waveform(
-        25e6,
-        afg3kc.source_device_constants.functions.PULSE,
-        1.0,
-        0.0,
-        "SOURCE1",
-        termination="FIFTY",
-    )
-    assert afg3kc.expect_esr(0)[0]
-    assert afg3kc.get_eventlog_status() == (True, '0,"No error"')
+        afg3252c.expect_esr(32, '1, Command error\n0,"No error"')
 
     with pytest.raises(AssertionError, match="No error string was provided"):
-        afg3kc.expect_esr(1)
+        afg3252c.expect_esr(1)
 
     with pytest.raises(AssertionError, match="Invalid channel name 'ch', valid items: "):
-        afg3kc._validate_channels("ch")  # noqa: SLF001
+        afg3252c._validate_channels("ch")  # noqa: SLF001
 
     with pytest.raises(AssertionError, match="Invalid channel name '99', valid items: "):
-        afg3kc._validate_channels("99")  # noqa: SLF001
+        afg3252c._validate_channels("99")  # noqa: SLF001
 
     with pytest.raises(
         TypeError,
         match="Generate Waveform does not accept functions as non Enums. "
         "Please use 'source_device_constants.functions'.",
     ):
-        afg3kc.generate_waveform(
+        afg3252c.generate_function(
             25e6,
-            afg3kc.source_device_constants.functions.PULSE.value,  # pyright: ignore[reportArgumentType]
+            afg3252c.source_device_constants.functions.PULSE.value,  # type: ignore[arg-type]
             1.0,
             0.0,
             "all",
         )
 
+    afg3051 = device_manager.add_afg("afg3051-hostname", alias="afg3051")
+
+    afg3051_constraints = afg3051.get_waveform_constraints(SignalGeneratorFunctionsAFG.RAMP)
+
+    assert afg3051_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=20.0e-3, upper=20.0),
+        offset_range=ParameterBounds(lower=-10.0, upper=10.0),
+        frequency_range=ParameterBounds(lower=1.0e-6, upper=500.0e3),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+
+    afg3011c = device_manager.add_afg("afg3011c-hostname", alias="afg3011c")
+
+    afg3011c_constraints = afg3011c.get_waveform_constraints(SignalGeneratorFunctionsAFG.SIN)
+
+    assert afg3011c_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=40.0e-3, upper=40.0),
+        offset_range=ParameterBounds(lower=-20.0, upper=20.0),
+        frequency_range=ParameterBounds(lower=1.0e-6, upper=10.0e6),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+
+    afg3021c = device_manager.add_afg("afg3021c-hostname", alias="afg3021c")
+
+    afg3021c_constraints = afg3021c.get_waveform_constraints(SignalGeneratorFunctionsAFG.SQUARE)
+
+    assert afg3021c_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=20.0e-3, upper=20.0),
+        offset_range=ParameterBounds(lower=-10.0, upper=10.0),
+        frequency_range=ParameterBounds(lower=1.0e-3, upper=25.0e6),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+
+    afg3052c = device_manager.add_afg("afg3052c-hostname", alias="afg3052c")
+
+    afg3052c_constraints = afg3052c.get_waveform_constraints(SignalGeneratorFunctionsAFG.RAMP)
+
+    assert afg3052c_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=20.0e-3, upper=20.0),
+        offset_range=ParameterBounds(lower=-10.0, upper=10.0),
+        frequency_range=ParameterBounds(lower=1.0e-6, upper=800.0e3),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+
+    afg3101c = device_manager.add_afg("afg3101c-hostname", alias="afg3101c")
+
+    afg3101c_constraints = afg3101c.get_waveform_constraints(
+        SignalGeneratorFunctionsAFG.ARBITRARY, waveform_length=5_000
+    )
+
+    assert afg3101c_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=40.0e-3, upper=20.0),
+        offset_range=ParameterBounds(lower=-10.0, upper=10.0),
+        frequency_range=ParameterBounds(lower=1.0e-3, upper=50.0e6),
+        sample_rate_range=ParameterBounds(lower=1.0e9, upper=1.0e9),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+
+    afg3151c = device_manager.add_afg("afg3151c-hostname", alias="afg3151c")
+
+    afg3151c_constraints = afg3151c.get_waveform_constraints(SignalGeneratorFunctionsAFG.LORENTZ)
+
+    assert afg3151c_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=40.0e-3, upper=16.0),
+        offset_range=ParameterBounds(lower=-10.0, upper=10.0),
+        frequency_range=ParameterBounds(lower=1.0e-6, upper=1.5e6),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+
+    afg3252c_constraints = afg3252c.get_waveform_constraints(SignalGeneratorFunctionsAFG.SIN)
+
+    assert afg3252c_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=100.0e-3, upper=8.0),
+        offset_range=ParameterBounds(lower=-5.0, upper=5.0),
+        frequency_range=ParameterBounds(lower=1.0e-6, upper=240.0e6),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+
+    with pytest.raises(ValueError, match="AFGs must have a function defined."):
+        afg3252c.get_waveform_constraints()
+
+    with pytest.raises(ValueError, match=r"Output state value must be 1 \(ON\) or 0 \(OFF\)\."):
+        afg3252c.source_channel["SOURCE1"].set_state(-1)
+
+    afg3252c.source_channel["SOURCE1"].set_impedance(1)
+    query_value = afg3252c.query("OUTPUT1:IMPEDANCE?")
+    assert float(query_value) == 1
+    afg3252c.source_channel["SOURCE1"].set_impedance(10e3)
+    query_value = afg3252c.query("OUTPUT1:IMPEDANCE?")
+    assert float(query_value) == 10e3
+
+    afg3252c.source_channel["SOURCE1"].set_pulse_duty_cycle(0.4)
+    query_value = afg3252c.query("SOURCE1:PULSE:DCYCLE?")
+    assert float(query_value) == 0.4
+    afg3252c.source_channel["SOURCE1"].set_pulse_duty_cycle(99.6)
+    query_value = afg3252c.query("SOURCE1:PULSE:DCYCLE?")
+    assert float(query_value) == 99.6
+    afg3252c.source_channel["SOURCE1"].set_pulse_duty_cycle(75)
+    query_value = afg3252c.query("SOURCE1:PULSE:DCYCLE?")
+    assert float(query_value) == 75
+
+    afg3252c.source_channel["SOURCE1"].set_ramp_symmetry(0.0)
+    query_value = afg3252c.query("SOURCE1:FUNCTION:RAMP:SYMMETRY?")
+    assert not float(query_value)
+    afg3252c.source_channel["SOURCE1"].set_ramp_symmetry(100.0)
+    query_value = afg3252c.query("SOURCE1:FUNCTION:RAMP:SYMMETRY?")
+    assert float(query_value) == 100.0
+    afg3252c.source_channel["SOURCE1"].set_ramp_symmetry(25.0)
+    query_value = afg3252c.query("SOURCE1:FUNCTION:RAMP:SYMMETRY?")
+    assert float(query_value) == 25.0
+
+    afg3252c.source_channel["SOURCE1"].set_burst_state(1)
+    query_value = afg3252c.query("SOURCE1:BURST:STATE?")
+    assert float(query_value) == 1
+    error_message = r"Burst state value must be 1 \(ON\) or 0 \(OFF\)\."
+    with pytest.raises(ValueError, match=error_message):
+        afg3252c.source_channel["SOURCE1"].set_burst_state(5)
+
+    afg3252c.source_channel["SOURCE1"].set_burst_count(10)
+    query_value = afg3252c.query("SOURCE1:BURST:NCYCLES?")
+    assert float(query_value) == 10
+
 
 def test_afg31k(device_manager: DeviceManager, capsys: pytest.CaptureFixture[str]) -> None:
     """Test the AFG31K driver.
@@ -122,17 +232,99 @@ def test_afg31k(device_manager: DeviceManager, capsys: pytest.CaptureFixture[str
         device_manager: The DeviceManager object.
         capsys: The pytest capture fixture.
     """
-    afg31k = device_manager.add_afg("afg31k-hostname")
+    afg31021 = device_manager.add_afg("afg31021-hostname")
 
     _ = capsys.readouterr().out  # throw away stdout
 
     # Check hostname
-    assert afg31k.hostname == "AFG31K-HOSTNAME"
+    assert afg31021.hostname == "AFG31021-HOSTNAME"
 
     # simulate a reboot
-    afg31k.reboot()
+    afg31021.reboot()
 
     stdout = capsys.readouterr().out
     assert "SYSTem:RESTart" in stdout
 
-    assert not afg31k.has_errors()
+    assert not afg31021.has_errors()
+
+    afg31021_constraints = afg31021.get_waveform_constraints(
+        SignalGeneratorFunctionsAFG.SIN,
+        waveform_length=16383,
+        frequency=20.0e6,
+    )
+
+    assert afg31021_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=2.0e-3, upper=20.0),
+        offset_range=ParameterBounds(lower=-10.0, upper=10.0),
+        frequency_range=ParameterBounds(lower=1.0e-6, upper=25.0e6),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+
+    afg31051 = device_manager.add_afg("afg31051-hostname")
+    afg31051_constraints = afg31051.get_waveform_constraints(
+        SignalGeneratorFunctionsAFG.SQUARE,
+        waveform_length=16383,
+        frequency=70.0e6,
+    )
+
+    assert afg31051_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=2.0e-3, upper=16.0),
+        offset_range=ParameterBounds(lower=-10.0, upper=10.0),
+        frequency_range=ParameterBounds(lower=1.0e-6, upper=40.0e6),
+        sample_rate_range=ParameterBounds(lower=1.0e9, upper=1.0e9),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+
+    afg31101 = device_manager.add_afg("afg31101-hostname")
+    afg31101_constraints = afg31101.get_waveform_constraints(
+        SignalGeneratorFunctionsAFG.RAMP,
+        frequency=90.0e6,
+    )
+
+    assert afg31101_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=2.0e-3, upper=12.0),
+        offset_range=ParameterBounds(lower=-10.0, upper=10.0),
+        frequency_range=ParameterBounds(lower=1.0e-6, upper=1.0e6),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+
+    afg31152 = device_manager.add_afg("afg31152-hostname")
+    afg31152_constraints = afg31152.get_waveform_constraints(
+        SignalGeneratorFunctionsAFG.ARBITRARY,
+        waveform_length=16383,
+        frequency=0.5e6,
+    )
+
+    assert afg31152_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=2.0e-3, upper=10.0),
+        offset_range=ParameterBounds(lower=-5.0, upper=5.0),
+        frequency_range=ParameterBounds(lower=1.0e-6, upper=75.0e6),
+        sample_rate_range=ParameterBounds(lower=2.0e9, upper=2.0e9),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+
+    afg31251 = device_manager.add_afg("afg31251-hostname")
+    afg31251_constraints = afg31251.get_waveform_constraints(
+        SignalGeneratorFunctionsAFG.SIN,
+        frequency=210.0e6,
+    )
+
+    assert afg31251_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=2.0e-3, upper=8.0),
+        offset_range=ParameterBounds(lower=-5.0, upper=5.0),
+        frequency_range=ParameterBounds(lower=1.0e-6, upper=250.0e6),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
diff --git a/tests/test_all_device_drivers.py b/tests/test_all_device_drivers.py
index cc36fb0f..345c8a96 100644
--- a/tests/test_all_device_drivers.py
+++ b/tests/test_all_device_drivers.py
@@ -22,18 +22,19 @@ def test_all_device_drivers(
     """
     # A list of entries containing: (device_type, address, port, connection_type, device_driver)
     simulated_device_list = (
-        ("AFG", "AFG3KC-HOSTNAME", 10001, "SOCKET", None),
-        ("AFG", "AFG3K-HOSTNAME", None, None, None),
-        ("AFG", "AFG31K-HOSTNAME", None, None, None),
-        ("AWG", "AWG5200-HOSTNAME", None, None, None),
-        ("AWG", "AWG5K-HOSTNAME", None, None, None),
-        ("AWG", "AWG7K-HOSTNAME", None, None, None),
-        ("AWG", "AWG7KB-HOSTNAME", None, None, None),
-        ("AWG", "AWG7KC-HOSTNAME", None, None, None),
-        ("AWG", "AWG5KC-HOSTNAME", None, None, None),
-        ("AWG", "AWG5KB-HOSTNAME", None, None, None),
-        ("AWG", "AWG70KB-HOSTNAME", None, None, None),
-        ("AWG", "AWG70KA-HOSTNAME", None, None, None),
+        ("AFG", "AFG3051-HOSTNAME", None, None, None),
+        ("AFG", "AFG3022B-HOSTNAME", None, None, None),
+        ("AFG", "AFG3252C-HOSTNAME", 10001, "SOCKET", None),
+        ("AFG", "AFG31021-HOSTNAME", None, None, None),
+        ("AWG", "AWG5200OPT50-HOSTNAME", None, None, None),
+        ("AWG", "AWG5012-HOSTNAME", None, None, None),
+        ("AWG", "AWG5002B-HOSTNAME", None, None, None),
+        ("AWG", "AWG5012C-HOSTNAME", None, None, None),
+        ("AWG", "AWG7051OPT01-HOSTNAME", None, None, None),
+        ("AWG", "AWG7062BOPT02-HOSTNAME", None, None, None),
+        ("AWG", "AWG7082COPT01-HOSTNAME", None, None, None),
+        ("AWG", "AWG70001AOPT150-HOSTNAME", None, None, None),
+        ("AWG", "AWG70002BOPT208-HOSTNAME", None, None, None),
         ("SCOPE", "MSO22-HOSTNAME", None, None, None),
         ("SCOPE", "MSO56-SERIAL1", None, "USB", None),
         ("SCOPE", "MSO44-HOSTNAME", None, None, None),
@@ -101,7 +102,6 @@ def test_all_device_drivers(
         ("SMU", "SMU2401-HOSTNAME", None, None, None),
         ("SMU", "SMU2410-HOSTNAME", None, None, None),
         ("SS", "SS3706A-HOSTNAME", None, None, None),
-        ("AFG", "AFG3KB-HOSTNAME", None, None, None),
         ("PSU", "PSU2200-HOSTNAME", None, None, None),
         ("PSU", "PSU2231-HOSTNAME", None, None, None),
         ("PSU", "PSU2231A-HOSTNAME", None, None, None),
diff --git a/tests/test_alternative_connection_addresses.py b/tests/test_alternative_connection_addresses.py
index 9187ed12..d73836e4 100644
--- a/tests/test_alternative_connection_addresses.py
+++ b/tests/test_alternative_connection_addresses.py
@@ -110,7 +110,7 @@
             ("SERIAL", "SMU2614B", "Model 2614B", "4498311"),
         ),
         (
-            "TCPIP::AFG3KC-HOSTNAME::10001::SOCKET",
+            "TCPIP::AFG3252C-HOSTNAME::10001::SOCKET",
             "AFG",
             None,
             ("SOCKET", "AFG3KC", "AFG3252C", "SERIAL1"),
diff --git a/tests/test_awgs.py b/tests/test_awgs.py
index ab54d87c..dfc79844 100644
--- a/tests/test_awgs.py
+++ b/tests/test_awgs.py
@@ -1,12 +1,65 @@
 # pyright: reportPrivateUsage=none
 """Test the AWGs."""
 
+from typing import cast, Optional
+
 import pytest
 
 from tm_devices import DeviceManager
-from tm_devices.drivers.pi.signal_sources.awgs.awg import AWGSourceDeviceConstants
+from tm_devices.drivers import (
+    AWG5K,
+    AWG5KB,
+    AWG5KC,
+    AWG7K,
+    AWG7KB,
+    AWG7KC,
+    AWG70KA,
+    AWG70KB,
+    AWG5200,
+)
+from tm_devices.drivers.pi.signal_generators.awgs.awg import (
+    AWGSourceDeviceConstants,
+    ExtendedSourceDeviceConstants,
+    ParameterBounds,
+    SignalGeneratorFunctionsAWG,
+)
+from tm_devices.helpers.enums import (
+    SignalGeneratorOutputPaths5200,
+    SignalGeneratorOutputPathsNon5200,
+)
+
 
+def check_constraints(
+    constraint: ExtendedSourceDeviceConstants,
+    sample_range: ParameterBounds,
+    amplitude_range: ParameterBounds,
+    offset_range: ParameterBounds,
+    length_range: ParameterBounds,
+) -> None:
+    """Check to see if the waveform constraints are correct.
 
+    Args:
+        constraint: The constraints to verify.
+        sample_range: The wanted sample range.
+        amplitude_range: The wanted amplitude range.
+        offset_range: The wanted offset_range.
+        length_range: The length range of the waveform.
+    """
+    assert constraint == ExtendedSourceDeviceConstants(
+        amplitude_range=amplitude_range,
+        offset_range=offset_range,
+        frequency_range=ParameterBounds(
+            lower=sample_range.lower / length_range.upper,
+            upper=sample_range.upper / length_range.lower,
+        ),
+        sample_rate_range=sample_range,
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+
+
+# pylint: disable=too-many-locals
 def test_awg5200(device_manager: DeviceManager, capsys: pytest.CaptureFixture[str]) -> None:
     """Test the AWG5200 driver.
 
@@ -14,22 +67,324 @@ def test_awg5200(device_manager: DeviceManager, capsys: pytest.CaptureFixture[st
         device_manager: The DeviceManager object.
         capsys: The captured stdout and stderr.
     """
-    awg5200 = device_manager.add_awg("awg5200-hostname", alias="awg5200")
-    assert id(device_manager.get_awg(number_or_alias="awg5200")) == id(awg5200)
-    assert id(device_manager.get_awg(number_or_alias=awg5200.device_number)) == id(awg5200)
-    assert awg5200.total_channels == 4
-    assert awg5200.all_channel_names_list == ("SOURCE1", "SOURCE2", "SOURCE3", "SOURCE4")
-    awg5200.write("*SRE 256")
-    awg5200.expect_esr(32, '1, "Command error"\n0,"No error"')
+    awg520050 = cast(AWG5200, device_manager.add_awg("awg5200opt50-hostname", alias="awg520050"))
+    awg520025 = cast(AWG5200, device_manager.add_awg("awg5200opt25-hostname", alias="awg520025"))
+    assert id(device_manager.get_awg(number_or_alias="awg520050")) == id(awg520050)
+    assert id(device_manager.get_awg(number_or_alias=awg520050.device_number)) == id(awg520050)
+    assert awg520050.total_channels == 4
+    assert awg520050.all_channel_names_list == ("SOURCE1", "SOURCE2", "SOURCE3", "SOURCE4")
+    awg520050.write("*SRE 256")
+    awg520050.expect_esr(32, '1, "Command error"\n0,"No error"')
     with pytest.raises(AssertionError):
-        awg5200.expect_esr(32, '1, Command error\n0,"No error"')
-    awg5200.load_waveform("test", "file_path.txt", "TXT")
+        awg520050.expect_esr(32, '1, Command error\n0,"No error"')
+    _ = capsys.readouterr().out  # throw away stdout
+    awg520050.load_waveform("test", "file_path.txt", "TXT")
     assert 'MMEMory:IMPort "test", "file_path.txt", TXT' in capsys.readouterr().out
-    awg5200.load_waveform("test", '"file_path.txt"', "TXT")
+    awg520050.load_waveform("test", '"file_path.txt"', "TXT")
     assert 'MMEMory:IMPort "test", "file_path.txt", TXT' in capsys.readouterr().out
-    assert awg5200.source_device_constants == AWGSourceDeviceConstants(
+    assert awg520050.source_device_constants == AWGSourceDeviceConstants(
         memory_page_size=1,
         memory_max_record_length=16200000,
         memory_min_record_length=1,
     )
-    assert awg5200.opt_string == "0"
+    assert awg520050.opt_string == "50,SEQ"
+    awg520050_constraints = awg520050.get_waveform_constraints(SignalGeneratorFunctionsAWG.SIN)
+    min_smaple_50 = 300.0
+    max_sample_50 = 5.0e9
+    assert awg520050_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=25.0e-3, upper=750.0e-3),
+        offset_range=ParameterBounds(lower=-2.0, upper=2.0),
+        frequency_range=ParameterBounds(lower=min_smaple_50 / 3600.0, upper=max_sample_50 / 10.0),
+        sample_rate_range=ParameterBounds(lower=min_smaple_50, upper=max_sample_50),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+
+    awg520050_constraints = awg520050.get_waveform_constraints(
+        SignalGeneratorFunctionsAWG.SIN,
+        output_signal_path=awg520050.OutputSignalPath.DCHV,
+    )
+    min_smaple_50 = 300.0
+    max_sample_50 = 5.0e9
+    assert awg520050_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=10.0e-3, upper=5.0),
+        offset_range=ParameterBounds(lower=-2.0, upper=2.0),
+        frequency_range=ParameterBounds(lower=min_smaple_50 / 3600.0, upper=max_sample_50 / 10.0),
+        sample_rate_range=ParameterBounds(lower=min_smaple_50, upper=max_sample_50),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+
+    # Set output signal path as default.
+    default_path = awg520050.OutputSignalPath.DCHB
+    awg520050.source_channel["SOURCE1"].set_output_signal_path()
+    output_path = awg520050.query("OUTPUT1:PATH?")
+    assert output_path == default_path.value
+
+    _ = capsys.readouterr().out  # throw away stdout
+    awg520050.load_waveform_from_set(
+        "*SINE100",
+        awg520050.sample_waveform_set_file,
+    )
+    sasset_waveform_cmd = (
+        "MMEMORY:OPEN:SASSET:WAVEFORM "
+        '"C:\\\\Program Files\\\\Tektronix\\\\AWG5200\\\\Samples\\\\AWG5k7k Predefined Waveforms'
+        '.awgx", "*SINE100"'
+    )
+    stdout = capsys.readouterr().out
+    assert sasset_waveform_cmd in stdout
+
+    # Invalid file type for waveform file.
+    with pytest.raises(ValueError, match=".txt is an invalid waveform file extension."):
+        awg520050.load_waveform_set(
+            "unittest.txt",
+        )
+
+    assert awg520025.opt_string == "25,DC"
+    awg520025_constraints = awg520025.get_waveform_constraints(waveform_length=500)
+    min_smaple_25 = 300.0
+    max_sample_25 = 2.5e9
+    assert awg520025_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=25.0e-3, upper=1.5),
+        offset_range=ParameterBounds(lower=-2.0, upper=2.0),
+        frequency_range=ParameterBounds(lower=min_smaple_25 / 500.0, upper=max_sample_25 / 500.0),
+        sample_rate_range=ParameterBounds(lower=min_smaple_25, upper=max_sample_25),
+        square_duty_cycle_range=None,
+        pulse_width_range=None,
+        ramp_symmetry_range=None,
+    )
+    error = "AWG Constraints require exclusively function or waveform_length."
+    with pytest.raises(ValueError, match=error):
+        awg520025.get_waveform_constraints()
+
+    with pytest.raises(ValueError, match=r"Output state value must be 1 \(ON\) or 0 \(OFF\)\."):
+        awg520025.source_channel["SOURCE1"].set_state(-1)
+
+    # Coverage for setting frequency with absolute tolerance.
+    awg520050.set_sample_rate(5000000000, absolute_tolerance=10)
+    query_val = awg520050.query("CLOCK:SRATE?")
+    assert float(query_val) == 5000000000
+
+
+def test_awg70k(  # noqa: PLR0915  # pylint: disable=too-many-locals
+    device_manager: DeviceManager, capsys: pytest.CaptureFixture[str]
+) -> None:
+    """Test the AWG70K driver.
+
+    Args:
+        device_manager: The DeviceManager object.
+        capsys: The captured stdout and stderr.
+    """
+    awg70ka150 = cast(
+        AWG70KA, device_manager.add_awg("awg70001aopt150-hostname", alias="awg70ka150")
+    )
+    awg70ka225 = cast(
+        AWG70KA, device_manager.add_awg("awg70002aopt225-hostname", alias="awg70ka225")
+    )
+    awg70ka216 = cast(
+        AWG70KA, device_manager.add_awg("awg70002aopt216-hostname", alias="awg70ka216")
+    )
+    awg70kb208 = cast(
+        AWG70KB, device_manager.add_awg("awg70002bopt208-hostname", alias="awg70kb208")
+    )
+    length_range = ParameterBounds(lower=10, upper=1000)
+    min_smaple = 1.5e3
+    awg_list = [awg70ka150, awg70ka225, awg70ka216, awg70kb208]
+    output_path: Optional[SignalGeneratorOutputPathsNon5200] = None
+    for awg in awg_list:
+        option = awg.alias[-3:]
+        assert awg.opt_string == option
+
+        if not output_path:
+            offset_range = ParameterBounds(lower=-0.0, upper=0.0)
+            ampl_range = ParameterBounds(lower=0.125, upper=0.5)
+        else:
+            offset_range = ParameterBounds(lower=-0.4, upper=0.8)
+            ampl_range = ParameterBounds(lower=31.0e-3, upper=1.2)
+
+        constraints = awg.get_waveform_constraints(
+            SignalGeneratorFunctionsAWG.RAMP,
+            output_signal_path=output_path,
+        )
+
+        output_path = awg.OutputSignalPath.DCA
+
+        sample_range = ParameterBounds(lower=min_smaple, upper=int(option[1:3]) * 1.0e9)
+        check_constraints(
+            constraints,
+            sample_range,
+            ampl_range,
+            offset_range,
+            length_range,
+        )
+
+    # Invalid output signal path.
+    with pytest.raises(ValueError, match="DCHB is an invalid output signal path for AWG70001."):
+        awg70ka150.source_channel["SOURCE1"].set_output_signal_path(
+            SignalGeneratorOutputPaths5200.DCHB
+        )
+
+    # Set default output signal path (will try DCA and succeed).
+    awg70ka150.source_channel["SOURCE1"].set_output_signal_path()
+    output_path_query = awg70ka150.query("OUTPUT1:PATH?")
+    assert output_path_query == awg70ka150.OutputSignalPath.DCA.value
+
+    # Set output signal path to DIR.
+    awg70ka150.source_channel["SOURCE1"].set_output_signal_path(awg70ka150.OutputSignalPath.DIR)
+    output_path_query = awg70ka150.query("OUTPUT1:PATH?")
+    assert output_path_query == SignalGeneratorOutputPathsNon5200.DIR.value
+    # Cannot set offset with output signal path set to DIR.
+    with pytest.raises(
+        ValueError,
+        match="The offset can only be set with an output signal path of DCA.",
+    ):
+        awg70ka150.source_channel["SOURCE1"].set_offset(0.1)
+
+    # Even with output signal path set to DIR, no errors raised because offset is being set to 0.
+    _ = capsys.readouterr().out  # throw away stdout
+    awg70ka150.source_channel["SOURCE1"].set_offset(0)
+    stdout = capsys.readouterr().out
+    assert "SOURCE1:VOLTAGE:OFFSET" not in stdout
+
+    # DCA as output signal path.
+    awg70ka150.source_channel["SOURCE1"].set_output_signal_path(awg70ka150.OutputSignalPath.DCA)
+    output_path_query = awg70ka150.query("OUTPUT1:PATH?")
+    assert output_path_query == SignalGeneratorOutputPathsNon5200.DCA.value
+    awg70ka150.source_channel["SOURCE1"].set_offset(0.1)
+    offset = float(awg70ka150.query("SOURCE1:VOLTAGE:OFFSET?"))
+    assert offset == 0.1
+
+    awg70ka150.set_sample_rate(500000000, absolute_tolerance=10)
+    current_frequency = awg70ka150.query("SOURCE1:FREQUENCY?")
+    assert float(current_frequency) == 500000000
+
+    # Load specific waveform.
+    _ = capsys.readouterr().out  # throw away stdout
+    awg70ka150.load_waveform_from_set(
+        "*SINE100",
+        awg70ka150.sample_waveform_set_file,
+    )
+    sasset_waveform_cmd = (
+        "MMEMORY:OPEN:SASSET:WAVEFORM "
+        '"C:\\\\Program Files\\\\Tektronix\\\\AWG70000\\\\Samples\\\\AWG5k7k Predefined Waveforms'
+        '.awgx", "*SINE100"'
+    )
+    stdout = capsys.readouterr().out
+    assert sasset_waveform_cmd in stdout
+
+    # Invalid file type for waveform file.
+    with pytest.raises(ValueError, match=".txt is an invalid waveform file extension."):
+        awg70ka150.load_waveform_set(
+            "unittest.txt",
+        )
+
+    # Coverage for setting frequency with absolute tolerance.
+    awg70ka150.set_sample_rate(5000000000, absolute_tolerance=10)
+    query_val = awg70ka150.query("SOURCE1:FREQUENCY?")
+    assert float(query_val) == 5000000000
+
+
+def test_awg7k(device_manager: DeviceManager) -> None:  # pylint: disable=too-many-locals
+    """Test the AWG7K driver.
+
+    Args:
+        device_manager: The DeviceManager object.
+    """
+    awg7k01 = cast(AWG7K, device_manager.add_awg("awg7051opt01-hostname", alias="awg7k01"))
+    awg7k06 = cast(AWG7K, device_manager.add_awg("awg7102opt06-hostname", alias="awg7k06"))
+    awg7kb02 = cast(AWG7KB, device_manager.add_awg("awg7062bopt02-hostname", alias="awg7kb02"))
+    awg7kb01 = cast(AWG7KB, device_manager.add_awg("awg7121bopt01-hostname", alias="awg7kb01"))
+    awg7kc06 = cast(AWG7KC, device_manager.add_awg("awg7082copt01-hostname", alias="awg7kc01"))
+    awg7kc01 = cast(AWG7KC, device_manager.add_awg("awg7122copt06-hostname", alias="awg7kc06"))
+    length_range = ParameterBounds(lower=10, upper=1000)
+    awg_list = [awg7k01, awg7k06, awg7kb02, awg7kb01, awg7kc06, awg7kc01]
+
+    output_path: Optional[SignalGeneratorOutputPathsNon5200] = None
+    for awg in awg_list:
+        option = awg.alias[-2:]
+        assert awg.opt_string == option
+
+        sample_range = ParameterBounds(lower=10.0e6, upper=int(awg.model[4:6]) * 1.0e9)
+
+        if option in {"02", "06"}:
+            ampl_range = ParameterBounds(lower=0.5, upper=1.0)
+            offset_range = ParameterBounds(lower=-0.0, upper=0.0)
+        elif not output_path:
+            # Default output signal path is DCA
+            offset_range = ParameterBounds(lower=-0.5, upper=0.5)
+            ampl_range = ParameterBounds(lower=50.0e-3, upper=2.0)
+        else:
+            offset_range = ParameterBounds(lower=-0.0, upper=0.0)
+            ampl_range = ParameterBounds(lower=50.0e-3, upper=1.0)
+
+        constraints = awg.get_waveform_constraints(
+            SignalGeneratorFunctionsAWG.TRIANGLE,
+            output_signal_path=output_path,
+        )
+        output_path = awg.OutputSignalPath.DIR
+        check_constraints(
+            constraints,
+            sample_range,
+            ampl_range,
+            offset_range,
+            length_range,
+        )
+
+
+def test_awg5k(device_manager: DeviceManager) -> None:
+    """Test the AWG5K driver.
+
+    Args:
+        device_manager: The DeviceManager object.
+    """
+    awg5k = cast(AWG5K, device_manager.add_awg("awg5012-hostname", alias="awg5k"))
+    awg5kb = cast(AWG5KB, device_manager.add_awg("awg5002b-hostname", alias="awg5kb"))
+    awg5kc = cast(AWG5KC, device_manager.add_awg("awg5012c-hostname", alias="awg5kc"))
+    length_range = ParameterBounds(lower=960, upper=960)
+    awg_list = [awg5k, awg5kb, awg5kc]
+    ampl_range = ParameterBounds(lower=20.0e-3, upper=4.5)
+    offset_range = ParameterBounds(lower=-2.25, upper=2.25)
+    output_path: Optional[SignalGeneratorOutputPathsNon5200] = None
+    for awg in awg_list:
+        sample_range = ParameterBounds(lower=10.0e6, upper=int(awg.model[5]) * 600.0e6 + 600.0e6)
+
+        if output_path:
+            # Default output signal path is DCA
+            ampl_range = ParameterBounds(lower=20.0e-3, upper=0.6)
+            offset_range = ParameterBounds(lower=-0.0, upper=0.0)
+
+        constraints = awg.get_waveform_constraints(
+            SignalGeneratorFunctionsAWG.CLOCK,
+            output_signal_path=output_path,
+        )
+
+        output_path = awg.OutputSignalPath.DIR
+
+        check_constraints(
+            constraints,
+            sample_range,
+            ampl_range,
+            offset_range,
+            length_range,
+        )
+
+    # With DIR output signal path.
+    constraints = awg5k.get_waveform_constraints(
+        SignalGeneratorFunctionsAWG.CLOCK, output_signal_path=awg5k.OutputSignalPath.DIR
+    )
+    sample_range = ParameterBounds(lower=10.0e6, upper=int(awg5k.model[5]) * 600.0e6 + 600.0e6)
+    offset_range = ParameterBounds(lower=0, upper=0)
+    check_constraints(
+        constraints,
+        sample_range,
+        ampl_range,
+        offset_range,
+        length_range,
+    )
+
+    # Coverage for setting frequency with absolute tolerance.
+    awg5k.set_sample_rate(10.0e6, absolute_tolerance=0)
+    query_val = awg5k.query("SOURCE1:FREQUENCY?")
+    assert float(query_val) == 10.0e6
diff --git a/tests/test_device_manager.py b/tests/test_device_manager.py
index 8ec57f6f..bee271c1 100644
--- a/tests/test_device_manager.py
+++ b/tests/test_device_manager.py
@@ -23,8 +23,8 @@
 from tm_devices.drivers import AFG3K, AFG3KC
 from tm_devices.drivers.device import Device
 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.signal_source import SignalSource
+from tm_devices.drivers.pi.signal_generators.afgs.afg import AFG
+from tm_devices.drivers.pi.signal_generators.signal_generator import SignalGenerator
 from tm_devices.helpers import ConnectionTypes, DeviceTypes, PYVISA_PY_BACKEND, SerialConfig
 
 
@@ -42,7 +42,7 @@ def _remove_added_methods() -> Iterator[None]:
         (Device, "already_exists"),
         (Scope, "custom_model_getter_scope"),
         (Scope, "custom_return"),
-        (SignalSource, "custom_model_getter_ss"),
+        (SignalGenerator, "custom_model_getter_ss"),
         (AFG, "custom_model_getter_afg"),
         (AFG3K, "custom_model_getter_afg3k"),
         (AFG3KC, "custom_model_getter_afg3kc"),
@@ -329,10 +329,10 @@ def custom_model_getter_scope(device: Scope, value: str) -> str:
             """Return the model."""
             return f"Scope {device.model} {value}"
 
-        @SignalSource.add_method
-        def custom_model_getter_ss(device: SignalSource, value: str) -> str:
+        @SignalGenerator.add_method
+        def custom_model_getter_ss(device: SignalGenerator, value: str) -> str:
             """Return the model."""
-            return f"SignalSource {device.model} {value}"
+            return f"SignalGenerator {device.model} {value}"
 
         @AFG.add_method
         def custom_model_getter_afg(device: AFG, value: str) -> str:
@@ -384,7 +384,7 @@ def custom_model_getter_afg3kc(device: AFG3KC, value: str) -> str:
         assert generated_contents == golden_contents
 
         # Test the custom added properties
-        afg = device_manager.add_afg("afg3kc-hostname", alias="testing")
+        afg = device_manager.add_afg("afg3252c-hostname", alias="testing")
         # noinspection PyUnresolvedReferences
         assert afg.class_name == "AFG3KC"
         # noinspection PyUnresolvedReferences
@@ -400,7 +400,7 @@ def custom_model_getter_afg3kc(device: AFG3KC, value: str) -> str:
         # noinspection PyUnresolvedReferences
         assert afg.custom_model_getter("a", "b", "c", 0.1) == "Device AFG3252C a b c 0.1"
         # noinspection PyUnresolvedReferences
-        assert afg.custom_model_getter_ss("hello") == "SignalSource AFG3252C hello"
+        assert afg.custom_model_getter_ss("hello") == "SignalGenerator AFG3252C hello"
         # noinspection PyUnresolvedReferences
         assert afg.custom_model_getter_afg("hello") == "AFG AFG3252C hello"
         # noinspection PyUnresolvedReferences
@@ -476,7 +476,7 @@ def test_warnings(self, device_manager: DeviceManager) -> None:
             match="A VISA IO error occurred when attempting to read the status byte or "
             "clear the output buffer of the resource",
         ):
-            device_manager.add_afg("afg3kc-hostname", alias="warning_bad_read_stb")
+            device_manager.add_afg("afg3252c-hostname", alias="warning_bad_read_stb")
         device_manager.remove_device(alias="warning_bad_read_stb")
 
         # Test the warning logged with message available (MAV) bit set
@@ -487,7 +487,7 @@ def test_warnings(self, device_manager: DeviceManager) -> None:
         ), pytest.warns(
             UserWarning, match="had data sitting in the VISA Output Buffer on first connection."
         ):
-            device_manager.add_afg("afg3kc-hostname", alias="warning_mav")
+            device_manager.add_afg("afg3252c-hostname", alias="warning_mav")
         device_manager.remove_device(alias="warning_mav")
 
     def test_exceptions(self, device_manager: DeviceManager) -> None:
@@ -500,7 +500,7 @@ def test_exceptions(self, device_manager: DeviceManager) -> None:
         device_manager.remove_all_devices()
 
         # Test getting a device type that was not the specified type
-        device_manager.add_afg("afg3kc-hostname", alias="my-device")
+        device_manager.add_afg("afg3252c-hostname", alias="my-device")
         with pytest.raises(TypeError):
             device_manager.get_scope("my-device")
         device_manager.remove_all_devices()
@@ -518,10 +518,10 @@ def test_exceptions(self, device_manager: DeviceManager) -> None:
             mock.MagicMock(return_value="data"),
         ):
             with pytest.warns(UserWarning), pytest.raises(SystemError) as error:
-                device_manager.add_afg("afg3kc-hostname")
+                device_manager.add_afg("afg3252c-hostname")
             assert str(error.value) == (
                 "Unable to read *IDN? response.\n"
-                "\tThe device `TCPIP0::AFG3KC-HOSTNAME::inst0::INSTR` likely has data sitting "
+                "\tThe device `TCPIP0::AFG3252C-HOSTNAME::inst0::INSTR` likely has data sitting "
                 "in the VISA Output Buffer that survived device_clear()."
             )
 
@@ -538,7 +538,7 @@ def test_exceptions(self, device_manager: DeviceManager) -> None:
         ):
             # patched the stb to return 16 (message available)
             with pytest.warns(UserWarning), pytest.raises(SystemError) as error:
-                device_manager.add_afg("afg3kc-hostname")
+                device_manager.add_afg("afg3252c-hostname")
             assert str(error.value) == (
                 "VI_ERROR_TMO (-1073807339): Timeout expired before operation completed.\n"
                 "\tUnable to read data after 2s.\n"
@@ -555,7 +555,7 @@ def test_exceptions(self, device_manager: DeviceManager) -> None:
             mock.MagicMock(return_value="bad IDN"),
         ):
             with pytest.raises(SystemError) as error:
-                device_manager.add_afg("afg3kc-hostname")
+                device_manager.add_afg("afg3252c-hostname")
             assert (
                 str(error.value)
                 == "Unable to determine which device driver to use. *IDN? returned 'bad IDN'"
@@ -589,7 +589,7 @@ def test_exceptions(self, device_manager: DeviceManager) -> None:
             device_manager.get_available_devices()
 
         with pytest.raises(TypeError):
-            device_manager.add_awg("afg3kc-hostname", alias="mismatch")
+            device_manager.add_awg("afg3252c-hostname", alias="mismatch")
 
         device_manager.close()
         with pytest.raises(AttributeError):
@@ -622,7 +622,7 @@ def test_get_available_devices(self, device_manager: DeviceManager) -> None:
         local_rm.close()
 
         device_manager.remove_all_devices()
-        afg = device_manager.add_afg("afg3kc-hostname")
+        afg = device_manager.add_afg("afg3252c-hostname")
         assert afg.query("*IDN?") == "TEKTRONIX,AFG3252C,SERIAL1,SCPI:99.0 FV:3.2.3"
 
         available_devices = device_manager.get_available_devices(
@@ -640,7 +640,7 @@ def test_get_available_devices(self, device_manager: DeviceManager) -> None:
         assert afg.query("*IDN?") == "TEKTRONIX,AFG3252C,SERIAL1,SCPI:99.0 FV:3.2.3"
         assert available_devices["local"] == device_tuple
         assert available_devices["configured"] == (
-            "address=AFG3KC-HOSTNAME,connection_type=TCPIP,device_type=AFG",
+            "address=AFG3252C-HOSTNAME,connection_type=TCPIP,device_type=AFG",
         )
 
     def test_deleting_device_manager(self) -> None:
diff --git a/tests/test_generate_waveform.py b/tests/test_generate_waveform.py
new file mode 100644
index 00000000..9cf832eb
--- /dev/null
+++ b/tests/test_generate_waveform.py
@@ -0,0 +1,444 @@
+# pyright: reportPrivateUsage=none
+"""Test generate_function."""
+
+from typing import cast
+
+import pytest
+
+from tm_devices import DeviceManager
+from tm_devices.drivers import AWG5K, AWG7K, AWG70KA, AWG5200, MSO2, MSO5
+from tm_devices.helpers.enums import (
+    SignalGeneratorOutputPaths5200,
+    SignalGeneratorOutputPathsNon5200,
+)
+
+
+def test_awg5200_gen_waveform(
+    device_manager: DeviceManager, capsys: pytest.CaptureFixture[str]
+) -> None:
+    """Test generate waveform for AWG5200.
+
+    Args:
+        device_manager: The DeviceManager object.
+        capsys: The captured stdout and stderr.
+    """
+    awg520050 = cast(AWG5200, device_manager.add_awg("awg5200opt50-hostname", alias="awg520050a"))
+
+    awg520050.generate_function(
+        10e3, awg520050.source_device_constants.functions.SIN, 1.0, 0.2, channel="SOURCE1"
+    )
+    source1_srate = awg520050.query("CLOCK:SRATE?")
+    assert float(source1_srate) == 36000000
+    source1_waveform_file = awg520050.query("SOURCE1:WAVEFORM?")
+    assert source1_waveform_file == '"*Sine3600"'
+    source1_amplitude = awg520050.query("SOURCE1:VOLTAGE:AMPLITUDE?")
+    assert float(source1_amplitude) == 1.0
+    source1_offset = awg520050.query("SOURCE1:VOLTAGE:OFFSET?")
+    assert float(source1_offset) == 0.2
+    output1_state = awg520050.query("OUTPUT1:STATE?")
+    assert int(output1_state) == 1
+
+    awg520050.generate_function(
+        10e3, awg520050.source_device_constants.functions.DC, 1.0, 0.0, channel="SOURCE1"
+    )
+    source1_waveform_file = awg520050.query("SOURCE1:WAVEFORM?")
+    assert source1_waveform_file == '"*DC"'
+
+    assert awg520050.expect_esr(0)[0]
+    assert awg520050.get_eventlog_status() == (True, '0,"No error"')
+
+    # Frequency is too high to produce CLOCK function on this AWG.
+    with pytest.raises(
+        ValueError,
+        match="Unable to generate Clock waveform with provided frequency of 10000000000.0 Hz.",
+    ):
+        awg520050.generate_function(
+            10e9, awg520050.source_device_constants.functions.CLOCK, 1.0, 0.0, channel="SOURCE1"
+        )
+
+    # Invalid output signal path.
+    with pytest.raises(ValueError, match="DIR is an invalid output signal path for AWG5204."):
+        awg520050.generate_function(
+            10e3,
+            awg520050.source_device_constants.functions.SIN,
+            1.2,
+            0.2,
+            channel="SOURCE1",
+            output_signal_path=SignalGeneratorOutputPathsNon5200.DIR,
+        )
+
+    # Call generate_function with only *DC in the waveform list.
+    awg520025 = cast(AWG5200, device_manager.add_awg("awg5200opt25-hostname", alias="awg520025a"))
+    _ = capsys.readouterr().out  # throw away stdout
+    awg520025.generate_function(
+        10e3,
+        awg520025.source_device_constants.functions.SIN,
+        1.0,
+        0,
+        channel="SOURCE1",
+    )
+    stdout = capsys.readouterr().out
+    assert "MMEMORY:OPEN:SASSET" in stdout
+
+
+def test_awg70k_gen_waveform(
+    device_manager: DeviceManager, capsys: pytest.CaptureFixture[str]
+) -> None:
+    """Test generate waveform for AWG70k.
+
+    Args:
+        device_manager: The DeviceManager object.
+        capsys: The captured stdout and stderr.
+    """
+    awg70ka150 = cast(
+        AWG70KA, device_manager.add_awg("awg70001aopt150-hostname", alias="awg70ka150")
+    )
+    _ = capsys.readouterr().out
+    awg70ka150.generate_function(
+        frequency=10e4,
+        function=awg70ka150.source_device_constants.functions.CLOCK,
+        amplitude=1.0,
+        offset=0.1,
+        channel="SOURCE1",
+        output_signal_path=awg70ka150.OutputSignalPath.DCA,
+    )
+    stdout = capsys.readouterr().out
+    source1_frequency = awg70ka150.query("SOURCE1:FREQUENCY?")
+    assert float(source1_frequency) == 96000000
+    source1_waveform_file = awg70ka150.query("SOURCE1:WAVEFORM?")
+    assert source1_waveform_file == '"*Clock960"'
+    source1_amplitude = awg70ka150.query("SOURCE1:VOLTAGE:AMPLITUDE?")
+    assert float(source1_amplitude) == 1.0
+    source1_offset = awg70ka150.query("SOURCE1:VOLTAGE:OFFSET?")
+    assert float(source1_offset) == 0.1
+    output1_state = awg70ka150.query("OUTPUT1:STATE?")
+    assert int(output1_state) == 1
+
+    assert awg70ka150.expect_esr(0)[0]
+    assert awg70ka150.get_eventlog_status() == (True, '0,"No error"')
+    assert "MMEMORY:OPEN:SASSET" not in stdout
+
+    # call generate_function with only *DC in the waveform list.
+    awg70ka225 = cast(
+        AWG70KA, device_manager.add_awg("awg70002aopt225-hostname", alias="awg70ka225")
+    )
+    _ = capsys.readouterr().out  # throw away stdout
+    awg70ka225.generate_function(
+        frequency=10e4,
+        function=awg70ka150.source_device_constants.functions.CLOCK,
+        amplitude=1.0,
+        offset=0.1,
+        channel="SOURCE1",
+        output_signal_path=awg70ka150.OutputSignalPath.DCA,
+    )
+    stdout = capsys.readouterr().out
+    assert "MMEMORY:OPEN:SASSET" in stdout
+
+
+def test_awg7k_gen_waveform(device_manager: DeviceManager) -> None:
+    """Test generate waveform for AWG7k.
+
+    Args:
+        device_manager: The DeviceManager object.
+    """
+    awg7k01 = cast(AWG7K, device_manager.add_awg("awg7051opt01-hostname", alias="awg7k01"))
+    awg7k06 = cast(AWG7K, device_manager.add_awg("awg7102opt06-hostname", alias="awg7k06"))
+
+    error_match = (
+        "The offset can only be set on AWG7102 without an 02 or 06 option and with an output "
+        "signal path of DCA \(AWGCONTROL:DOUTPUT1:STATE set to 0\)."  # noqa: W605  # pylint: disable=anomalous-backslash-in-string  # pyright: ignore [reportInvalidStringEscapeSequence]
+    )
+    with pytest.raises(ValueError, match=error_match):
+        awg7k06.source_channel["SOURCE1"].set_offset(0.2)
+    awg7k06.generate_function(
+        10e4, awg7k06.source_device_constants.functions.CLOCK, 1.0, 0.0, channel="SOURCE1"
+    )
+    source1_frequency = awg7k06.query("SOURCE1:FREQUENCY?")
+    assert float(source1_frequency) == 96000000
+    source1_waveform_file = awg7k06.query("SOURCE1:WAVEFORM?")
+    assert source1_waveform_file == '"*Clock960"'
+    source1_amplitude = awg7k06.query("SOURCE1:VOLTAGE:AMPLITUDE?")
+    assert float(source1_amplitude) == 1.0
+    source1_offset = awg7k06.query("SOURCE1:VOLTAGE:OFFSET?")
+    assert not float(source1_offset)
+    output1_state = awg7k06.query("OUTPUT1:STATE?")
+    assert int(output1_state) == 1
+
+    assert awg7k06.expect_esr(0)[0]
+    assert awg7k06.get_eventlog_status() == (True, '0,"No error"')
+
+    # AWG7k with option 1 should set offset.
+    awg7k01.generate_function(
+        10e3, awg7k01.source_device_constants.functions.SIN, 1.2, 0.2, channel="SOURCE1"
+    )
+    source1_offset = awg7k01.query("SOURCE1:VOLTAGE:OFFSET?")
+    assert float(source1_offset) == 0.2
+
+    # AWG7k with option 1 should not be able to set offset with DIR output signal path.
+    error_match = (
+        "The offset can only be set on AWG7051 without an 02 or 06 option and with an output "
+        "signal path of DCA \(AWGCONTROL:DOUTPUT1:STATE set to 0\)."  # noqa: W605  # pylint: disable=anomalous-backslash-in-string  # pyright: ignore [reportInvalidStringEscapeSequence]
+    )
+    with pytest.raises(ValueError, match=error_match):
+        awg7k01.generate_function(
+            10e3,
+            awg7k01.source_device_constants.functions.SIN,
+            1.2,
+            0.2,
+            channel="SOURCE1",
+            output_signal_path=awg7k01.OutputSignalPath.DIR,
+        )
+
+    # DCHB is not a valid output signal path for AWG7k's.
+    with pytest.raises(ValueError, match="DCHB is an invalid output signal path for AWG7051."):
+        awg7k01.generate_function(
+            10e3,
+            awg7k01.source_device_constants.functions.SIN,
+            1.2,
+            0.2,
+            channel="SOURCE1",
+            output_signal_path=SignalGeneratorOutputPaths5200.DCHB,
+        )
+
+    assert awg7k01.expect_esr(0)[0]
+    assert awg7k01.get_eventlog_status() == (True, '0,"No error"')
+
+    # Clock
+    awg7k01.generate_function(
+        10e4,
+        awg7k01.source_device_constants.functions.CLOCK,
+        1.0,
+        0.0,
+        channel="SOURCE1",
+        output_signal_path=awg7k01.OutputSignalPath.DCA,
+    )
+    source1_frequency = awg7k01.query("SOURCE1:FREQUENCY?")
+    assert float(source1_frequency) == 96000000
+    source1_waveform_file = awg7k01.query("SOURCE1:WAVEFORM?")
+    assert source1_waveform_file == '"*Clock960"'
+
+    # Iterate through pre-made signal record length
+    awg7k01.generate_function(
+        10e7, awg7k01.source_device_constants.functions.RAMP, 1.0, 0.0, channel="SOURCE1"
+    )
+    source1_frequency = awg7k01.query("SOURCE1:FREQUENCY?")
+    assert float(source1_frequency) == 1000000000
+    source1_waveform_file = awg7k01.query("SOURCE1:WAVEFORM?")
+    assert source1_waveform_file == '"*Triangle10"'
+
+
+def test_awg5k_gen_waveform(device_manager: DeviceManager) -> None:
+    """Test generate waveform for AWG5k.
+
+    Args:
+        device_manager: The DeviceManager object.
+    """
+    awg5k = cast(AWG5K, device_manager.add_awg("AWG5012-hostname", alias="awg5k"))
+    # Sine
+    awg5k.generate_function(
+        10e3, awg5k.source_device_constants.functions.SIN, 2.0, 2.0, channel="SOURCE1"
+    )
+    source1_frequency = awg5k.query("SOURCE1:FREQUENCY?")
+    assert float(source1_frequency) == 36000000
+    source1_waveform_file = awg5k.query("SOURCE1:WAVEFORM?")
+    assert source1_waveform_file == '"*Sine3600"'
+    source1_amplitude = awg5k.query("SOURCE1:VOLTAGE:AMPLITUDE?")
+    assert float(source1_amplitude) == 2.0
+    source1_offset = awg5k.query("SOURCE1:VOLTAGE:OFFSET?")
+    assert float(source1_offset) == 2.0
+    output1_state = awg5k.query("OUTPUT1:STATE?")
+    assert int(output1_state) == 1
+
+    # Cannot set offset with DIR output signal path.
+    offset_error = (
+        "The offset can only be set on AWG5012 with an output signal path of DCA "
+        "\(AWGCONTROL:DOUTPUT1:STATE set to 0\)."  # noqa: W605  # pylint: disable=anomalous-backslash-in-string  # pyright: ignore [reportInvalidStringEscapeSequence]
+    )
+    with pytest.raises(ValueError, match=offset_error):
+        awg5k.generate_function(
+            10e3,
+            awg5k.source_device_constants.functions.SIN,
+            2.0,
+            2.0,
+            channel="SOURCE1",
+            output_signal_path=awg5k.OutputSignalPath.DIR,
+        )
+
+    # Even with an output signal path of DIR, no error is raised because no offset is requested (0).
+    awg5k.generate_function(
+        10e3,
+        awg5k.source_device_constants.functions.SIN,
+        2.0,
+        0,
+        channel="SOURCE1",
+        output_signal_path=awg5k.OutputSignalPath.DIR,
+    )
+
+
+def test_afg3k_gen_waveform(  # pylint: disable=too-many-locals
+    device_manager: DeviceManager, capsys: pytest.CaptureFixture[str]
+) -> None:
+    """Test generate waveform for AWG3k.
+
+    Args:
+        device_manager: The DeviceManager object.
+        capsys: The captured stdout and stderr.
+    """
+    _ = capsys.readouterr().out  # throw away stdout
+    afg3kc = device_manager.add_afg(
+        "afg3252c-hostname", alias="afg3kc", connection_type="SOCKET", port=10001
+    )
+    # Turn burst off on all channels to initiate a phase sync in generate_function
+    for source_channel in afg3kc.source_channel.values():
+        source_channel.set_burst_state(0)
+    afg3kc.generate_function(25e6, afg3kc.source_device_constants.functions.PULSE, 1.0, 0.0, "all")
+    stdout = capsys.readouterr().out
+    assert "SOURCE1:PHASE:INITIATE" in stdout
+
+    _ = capsys.readouterr().out  # throw away stdout
+    afg3kc.setup_burst(
+        frequency=25e6,
+        function=afg3kc.source_device_constants.functions.SIN,
+        amplitude=1.0,
+        offset=0.0,
+        burst_count=1,
+        channel="SOURCE1",
+    )
+    afg3kc.generate_burst()
+    stdout = capsys.readouterr().out
+    query_value = afg3kc.query("OUTPUT1:IMPEDANCE?")
+    assert float(query_value) == 50.0
+    source1_frequency = afg3kc.query("SOURCE1:FREQUENCY:FIXED?")
+    assert float(source1_frequency) == 25e6
+    source1_offset = afg3kc.query("SOURCE1:VOLTAGE:OFFSET?")
+    assert not float(source1_offset)
+    assert "SOURCE1:PULSE:DCYCLE" not in stdout
+    assert "SOURCE1:FUNCTION:RAMP:SYMMETRY" not in stdout
+    source1_function = afg3kc.query("SOURCE1:FUNCTION?")
+    assert source1_function == "SIN"
+    source1_amplitude = afg3kc.query("SOURCE1:VOLTAGE:AMPLITUDE?")
+    assert float(source1_amplitude) == 1.0
+    trigger_sequence_source = afg3kc.query("TRIGGER:SEQUENCE:SOURCE?")
+    assert trigger_sequence_source == "EXT"
+    source1_burst_state = afg3kc.query("SOURCE1:BURST:STATE?")
+    assert int(source1_burst_state) == 1
+    source1_burst_mode = afg3kc.query("SOURCE1:BURST:MODE?")
+    assert source1_burst_mode == "TRIG"
+    source1_burst_ncycles = afg3kc.query("SOURCE1:BURST:NCYCLES?")
+    assert int(source1_burst_ncycles) == 1
+    output1_state = afg3kc.query("OUTPUT1:STATE?")
+    assert int(output1_state) == 1
+
+    _ = capsys.readouterr().out  # throw away stdout
+    afg3kc.setup_burst(
+        frequency=25e6,
+        function=afg3kc.source_device_constants.functions.RAMP,
+        amplitude=1.0,
+        offset=0.0,
+        burst_count=1,
+        channel="SOURCE1",
+        termination="FIFTY",
+    )
+    afg3kc.generate_burst()
+    impedance = afg3kc.query("OUTPUT1:IMPEDANCE?")
+    assert float(impedance) == 50
+    ramp_symmetry = afg3kc.query("SOURCE1:FUNCTION:RAMP:SYMMETRY?")
+    assert float(ramp_symmetry) == 100
+    source1_function = afg3kc.query("SOURCE1:FUNCTION?")
+    assert source1_function == "RAMP"
+
+    afg3kc.generate_function(
+        25e6,
+        afg3kc.source_device_constants.functions.PULSE,
+        1.0,
+        0.0,
+        "SOURCE1",
+        termination="FIFTY",
+    )
+    pulse_dcycle = afg3kc.query("SOURCE1:PULSE:DCYCLE?")
+    assert float(pulse_dcycle) == 50
+
+    assert afg3kc.expect_esr(0)[0]
+    assert afg3kc.get_eventlog_status() == (True, '0,"No error"')
+
+
+def test_internal_afg_gen_waveform(
+    device_manager: DeviceManager, capsys: pytest.CaptureFixture[str]
+) -> None:
+    """Test generate waveform for IAFG in tekscopes.
+
+    Args:
+        device_manager: The DeviceManager object.
+        capsys: The captured stdout and stderr.
+    """
+    scope: MSO5 = cast(
+        MSO5, device_manager.add_scope("MSO56-SERIAL1", alias="mso56", connection_type="USB")
+    )
+
+    _ = capsys.readouterr().out
+    scope.generate_function(10e3, scope.source_device_constants.functions.SIN, 0.5, 0.0)
+    stdout = capsys.readouterr().out
+    assert "AFG:OUTPUT:MODE" not in stdout
+    assert "AFG:BURST:CCOUNT" not in stdout
+    frequency = scope.query("AFG:FREQUENCY?")
+    assert float(frequency) == 10e3
+    offset = scope.query("AFG:OFFSET?")
+    assert not float(offset)
+    assert "AFG:RAMP:SYMMETRY" not in stdout
+    function = scope.query("AFG:FUNCTION?")
+    assert function == "SINE"
+    impedance = scope.query("AFG:OUTPUT:LOAD:IMPEDANCE?")
+    assert impedance == "FIFTY"
+    amplitude = scope.query("AFG:AMPLITUDE?")
+    assert float(amplitude) == 0.5
+    output_state = scope.query("AFG:OUTPUT:STATE?")
+    assert int(output_state) == 1
+    assert "AFG:BURST:TRIGGER" not in stdout
+
+    scope.generate_function(
+        frequency=10e3,
+        function=scope.source_device_constants.functions.SQUARE,
+        amplitude=0.5,
+        offset=0.0,
+        termination="HIGHZ",
+        duty_cycle=50,
+    )
+    square_duty = scope.query("AFG:SQUARE:DUTY?")
+    assert float(square_duty) == 50
+    impedance = scope.query("AFG:OUTPUT:LOAD:IMPEDANCE?")
+    assert impedance == "HIGHZ"
+
+    _ = capsys.readouterr().out
+    scope.setup_burst(10e3, scope.source_device_constants.functions.RAMP, 0.5, 0.0, burst_count=1)
+    scope.generate_burst()
+    stdout = capsys.readouterr().out
+    output_mode = scope.query("AFG:OUTPUT:MODE?")
+    assert output_mode == "BURST"
+    burst_ccount = scope.query("AFG:BURST:CCOUNT?")
+    assert int(burst_ccount) == 1
+    ramp_symmetry = scope.query("AFG:RAMP:SYMMETRY?")
+    assert float(ramp_symmetry) == 50
+    assert "AFG:BURST:TRIGGER" in stdout
+    assert scope.expect_esr(0)[0]
+    assert scope.get_eventlog_status() == (True, '0,"No events to report - queue empty"')
+
+    with pytest.raises(
+        TypeError,
+        match="Generate Waveform does not accept functions as non Enums. "
+        "Please use 'source_device_constants.functions'.",
+    ):
+        scope.generate_function(
+            25e6,
+            scope.source_device_constants.functions.PULSE.value,  # type: ignore[reportArgumentType]
+            1.0,
+            0.0,
+            "all",
+        )
+
+    mso2_scope: MSO2 = device_manager.add_scope("MSO22-HOSTNAME", connection_type="TCPIP")
+
+    with pytest.raises(
+        AttributeError,
+        match="No AFG License, the class instance attributes are not accessible.",
+    ):
+        mso2_scope.internal_afg.set_offset(1.0)
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 7d33b65e..7da6819e 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -249,7 +249,7 @@ def test_create_and_check_visa_connection(capsys: pytest.CaptureFixture[str]) ->
     dev_config_3 = DeviceConfigEntry(
         device_type=DeviceTypes.AFG,
         connection_type=ConnectionTypes.SOCKET,
-        address="AFG3KC-HOSTNAME",
+        address="AFG3252C-HOSTNAME",
         lan_port=10001,
     )
     visa_obj = create_visa_connection(dev_config_3, SIMULATED_VISA_LIB)
diff --git a/tests/test_ieee_scpi_cmds.py b/tests/test_ieee_scpi_cmds.py
index b3870028..fb880d5c 100644
--- a/tests/test_ieee_scpi_cmds.py
+++ b/tests/test_ieee_scpi_cmds.py
@@ -8,7 +8,7 @@
 def test_ieee_scpi_cmds(device_manager: DeviceManager) -> None:
     """Test the IEEE 488.2 SCPI commands."""
     device_manager.remove_all_devices()
-    device = device_manager.add_afg("afg3kc-hostname")
+    device = device_manager.add_afg("afg3252c-hostname")
     assert device.ieee_cmds.esr() == "0"
     assert device.ieee_cmds.tst() == "1"
     assert device.ieee_cmds.stb() == "4"
diff --git a/tests/test_pi_device.py b/tests/test_pi_device.py
index cddd9294..097160ba 100644
--- a/tests/test_pi_device.py
+++ b/tests/test_pi_device.py
@@ -127,3 +127,26 @@ def test_pi_device(  # noqa: PLR0915
     # Re-open the device for device manager teardown
     with mock.patch.dict("os.environ", {"TM_DEVICES_UNIT_TESTS_REBOOT_ALLOW": "true"}, clear=True):
         assert scope._open()  # noqa: SLF001
+
+    # Ensure VERBose is off
+    scope.set_and_check(":VERBose", 0)
+    # Expect set not needed since verbose is already off.
+    assert scope.set_if_needed(":VERBose", 0) == (False, "0")
+    # Expect set needed
+    assert scope.set_if_needed(":VERBose", 1) == (True, "")
+    assert scope.set_if_needed(":VERBose", 0, verify_value=True) == (True, "0")
+    # Set VERBose back to off
+    scope.set_and_check(":VERBose", "0")
+
+    scope.set_and_check(":DISPLAY:WAVEVIEW:CH1:STATE", 1)
+    scope.poll_query(1, ":DISPLAY:WAVEVIEW:CH1:STATE?", 1.0, sleep_time=0)
+    # Display waveview state is set to 1, but we are wanting 0.
+    with pytest.raises(AssertionError):
+        scope.poll_query(2, ":DISPLAY:WAVEVIEW:CH1:STATE?", 0, sleep_time=0)
+    # Set display waveview state to a maximum bound value.
+    scope.set_and_check(":DISPLAY:WAVEVIEW:CH1:STATE", 9.9e37)
+    with pytest.raises(AssertionError):
+        # Pass in the maximum bound value as invalid values.
+        scope.poll_query(
+            1, ":DISPLAY:WAVEVIEW:CH1:STATE?", 9.9e37, sleep_time=0, invalid_values=[9.9e37]
+        )
diff --git a/tests/test_scopes.py b/tests/test_scopes.py
index 3923a862..d773e868 100644
--- a/tests/test_scopes.py
+++ b/tests/test_scopes.py
@@ -5,6 +5,7 @@
 import subprocess
 import sys
 
+from typing import cast
 from unittest import mock
 
 import pytest
@@ -13,8 +14,15 @@
 from packaging.version import Version
 
 from tm_devices import DeviceManager
-from tm_devices.drivers import MSO2, MSO5, MSO70KDX, TekScopeSW
-from tm_devices.drivers.pi.scopes.tekscope.tekscope import TekProbeData, TekScope, TekScopeChannel
+from tm_devices.drivers import MSO2, MSO5, MSO5B, MSO6, MSO70KDX, TekScopeSW
+from tm_devices.drivers.pi.scopes.tekscope.tekscope import (
+    ExtendedSourceDeviceConstants,
+    ParameterBounds,
+    TekProbeData,
+    TekScope,
+    TekScopeChannel,
+)
+from tm_devices.helpers.enums import SignalGeneratorFunctionsIAFG
 
 
 class TmDevicesUnitTestOnlyCustomMSO5(MSO5):
@@ -118,17 +126,17 @@ def test_tekscope(device_manager: DeviceManager) -> None:  # noqa: PLR0915
         scope.query("EMPTY:STRING?")
 
     # Test generating waveform functionality
-    scope.generate_waveform(10e3, scope.source_device_constants.functions.SIN, 0.5, 0.0)
-    scope.generate_waveform(
+    scope.generate_function(10e3, scope.source_device_constants.functions.SIN, 0.5, 0.0)
+    scope.generate_function(
         10e3, scope.source_device_constants.functions.SIN, 0.5, 0.0, termination="HIGHZ"
     )
-    scope.generate_waveform(10e3, scope.source_device_constants.functions.RAMP, 0.5, 0.0, burst=1)
+    scope.setup_burst(10e3, scope.source_device_constants.functions.RAMP, 0.5, 0.0, burst_count=1)
     with pytest.raises(
         TypeError,
         match="Generate Waveform does not accept functions as non Enums. "
         "Please use 'source_device_constants.functions'.",
     ):
-        scope.generate_waveform(
+        scope.generate_function(
             25e6,
             scope.source_device_constants.functions.PULSE.value,  # pyright: ignore[reportArgumentType]
             1.0,
@@ -177,7 +185,7 @@ def test_tekscope(device_manager: DeviceManager) -> None:  # noqa: PLR0915
     scope.expect_esr(0, '0,"No events to report - queue empty"')
 
     # Assert that getting license list returns the correct tuple
-    assert scope.license_list == ("LIC5", "LIC4")
+    assert scope.license_list == ("LIC5", "LIC4", "AFG")
     assert scope.has_license("LIC5")
     # Assert that the number of digital bits in channel is 8
     assert scope.num_dig_bits_in_ch == 8
@@ -237,6 +245,107 @@ def test_tekscope(device_manager: DeviceManager) -> None:  # noqa: PLR0915
         )
 
 
+def test_iafg(device_manager: DeviceManager) -> None:
+    """Test the IAFG.
+
+    Args:
+        device_manager: The DeviceManager object.
+    """
+    mso64: MSO6 = cast(MSO6, device_manager.add_scope("MSO64-HOSTNAME", alias="mso64"))
+    mso64_constraints = mso64.get_waveform_constraints(
+        SignalGeneratorFunctionsIAFG.SIN,
+        frequency=25.0e6,
+    )
+
+    assert mso64_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=20.0e-3, upper=5.0),
+        offset_range=ParameterBounds(lower=-2.5, upper=2.5),
+        frequency_range=ParameterBounds(lower=100.0e-3, upper=50.0e6),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=ParameterBounds(lower=25.0, upper=75.0),
+        pulse_width_range=ParameterBounds(lower=1.0e-8, upper=3.0e-8),
+        ramp_symmetry_range=ParameterBounds(lower=0.0, upper=100.0),
+    )
+
+    mso64_constraints = mso64.get_waveform_constraints(
+        SignalGeneratorFunctionsIAFG.SQUARE,
+        frequency=5.0e6,
+    )
+
+    assert mso64_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=20.0e-3, upper=5.0),
+        offset_range=ParameterBounds(lower=-2.5, upper=2.5),
+        frequency_range=ParameterBounds(lower=100.0e-3, upper=25.0e6),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=ParameterBounds(lower=10.0, upper=90.0),
+        pulse_width_range=ParameterBounds(lower=2.0e-08, upper=1.8e-07),
+        ramp_symmetry_range=ParameterBounds(lower=0.0, upper=100.0),
+    )
+
+    mso64_constraints = mso64.get_waveform_constraints(
+        SignalGeneratorFunctionsIAFG.RAMP,
+    )
+
+    assert mso64_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=20.0e-3, upper=5.0),
+        offset_range=ParameterBounds(lower=-2.5, upper=2.5),
+        frequency_range=ParameterBounds(lower=100.0e-3, upper=500.0e3),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=ParameterBounds(lower=10.0, upper=90.0),
+        pulse_width_range=None,
+        ramp_symmetry_range=ParameterBounds(lower=0.0, upper=100.0),
+    )
+
+    mso64_constraints = mso64.get_waveform_constraints(
+        SignalGeneratorFunctionsIAFG.SINC,
+    )
+
+    assert mso64_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=20.0e-3, upper=3.0),
+        offset_range=ParameterBounds(lower=-2.5, upper=2.5),
+        frequency_range=ParameterBounds(lower=100.0e-3, upper=2.0e6),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=ParameterBounds(lower=10.0, upper=90.0),
+        pulse_width_range=None,
+        ramp_symmetry_range=ParameterBounds(lower=0.0, upper=100.0),
+    )
+
+    mso64_constraints = mso64.get_waveform_constraints(
+        SignalGeneratorFunctionsIAFG.HAVERSINE,
+    )
+
+    assert mso64_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=20.0e-3, upper=2.5),
+        offset_range=ParameterBounds(lower=-2.5, upper=2.5),
+        frequency_range=ParameterBounds(lower=100.0e-3, upper=5.0e6),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=ParameterBounds(lower=10.0, upper=90.0),
+        pulse_width_range=None,
+        ramp_symmetry_range=ParameterBounds(lower=0.0, upper=100.0),
+    )
+
+    mso56b: MSO5 = cast(MSO5B, device_manager.add_scope("MSO58B-HOSTNAME", alias="mso56b"))
+    mso56b_constraints = mso56b.get_waveform_constraints(
+        SignalGeneratorFunctionsIAFG.SIN,
+    )
+
+    assert mso56b_constraints == ExtendedSourceDeviceConstants(
+        amplitude_range=ParameterBounds(lower=20.0e-3, upper=5.0),
+        offset_range=ParameterBounds(lower=-2.5, upper=2.5),
+        frequency_range=ParameterBounds(lower=100.0e-3, upper=100.0e6),
+        sample_rate_range=ParameterBounds(lower=250.0e6, upper=250.0e6),
+        square_duty_cycle_range=ParameterBounds(lower=10.0, upper=90.0),
+        pulse_width_range=None,
+        ramp_symmetry_range=ParameterBounds(lower=0.0, upper=100.0),
+    )
+
+    with pytest.raises(ValueError, match="IAFGs must have a function defined."):
+        mso56b.get_waveform_constraints()
+
+    with pytest.raises(ValueError, match=r"Output state value must be 1 \(ON\) or 0 \(OFF\)\."):
+        mso56b.internal_afg.set_state(-1)
+
+
 def test_exceptions(device_manager: DeviceManager) -> None:
     """Test visa error handling within tekscope.
 
diff --git a/tests/validate_device_manager_delete.py b/tests/validate_device_manager_delete.py
index 3319efb1..af27b597 100644
--- a/tests/validate_device_manager_delete.py
+++ b/tests/validate_device_manager_delete.py
@@ -40,7 +40,7 @@ def verify_deleting_device_manager() -> None:
     dev_manager.visa_library = SIMULATED_VISA_LIB
     # Set up the device manager with a single device
     assert not dev_manager.devices
-    dev_manager.add_afg("afg3kc-hostname")
+    dev_manager.add_afg("afg3252c-hostname")
     assert len(dev_manager.devices) == 1
     # Delete the device manager
     stdout_buffer = StringIO()
@@ -58,7 +58,7 @@ def verify_deleting_device_manager() -> None:
     dev_manager.visa_library = SIMULATED_VISA_LIB
     # Set up the device manager with a single device
     assert not dev_manager.devices
-    dev_manager.add_afg("afg3kc-hostname")
+    dev_manager.add_afg("afg3252c-hostname")
     dev_manager.close()
     assert len(dev_manager.devices) == 1
     # Delete the device manager
@@ -77,7 +77,7 @@ def verify_deleting_device_manager() -> None:
     dev_manager.visa_library = SIMULATED_VISA_LIB
     # Set up the device manager with a single device
     assert not dev_manager.devices
-    dev_manager.add_afg("afg3kc-hostname")
+    dev_manager.add_afg("afg3252c-hostname")
 
 
 if __name__ == "__main__":