Keysight M320XA AWGs
This module contains the many methods and types necessary to control and configure the Keysight M3202A/M3201A AWGs. In essence, it wraps the KeysightInstruments.jl package, which in itself is a bare-bones wrapper to the native C library which controls the Keysight instruments, in a way which affords compatibility with the rest of the InstrumentControl package, as well as consistency between the control code of this instrument and other instruments represented in this package.
Usage/Configuration
Instrument Overview
Each M320XA AWG card has several channels; each channel has it's own Function Generator as well as it's own Arbitary Waveform Generator. The function generator outputs sinusoids, triangular waves, and square waves. The user can select the output of the channel to either come from the arbitrary waveform generator, or from the function generator.
Moreover, the arbitrary waveform generator can be used to modulate the function generator output: it can be used for amplitude modulation, phase modulation, frequency modulation, or DC offset modulation
When used for amplitude modulation, the output signal is given by: Output(t) = (A + G*AWG(t))* cos(ωt + ϕ) + DC
When used for phase modulation, the output signal is given by: Output(t) = A*cos(ωt + ϕ + G*AWG(t)) + DC
When used for frequency modulation, the output signal is given by: Output(t) = A*cos((ω+G*AWG(t))* t + ϕ) + DC
When used for DC offset modulation, the output signal is given by: Output(t) = A*cos(ωt + ϕ) + DC + G*AWG(t)
When using the AWG for direct output (not modulation): Output(t) = A*AWG(t) + DC
- A is the channel amplitude configured by the user
- AWG(t) is the normalized waveform which is loaded and queued by the user and generated by the AWG
- f = ω/2π is the channel's function generator's frequency set by the user
- ϕ is the channel's function generator's relative phase configured by the user
- G is the "amplitude" of the AWG signal, which is configured by the user
- DC is the channel's DC offset, configured by the user.
Waveforms are loaded as normalized arrays (i.e., from -1 to 1) to the onboard RAM of the AWG; when they are loaded the user specifies an number ID which servers as the handle to the waveform from then on. The loaded waveforms are then queued onto particular channels with the following parameters:
- Trigger mode (i.e.: Trigger from PXI or Software or Automatically)
- Delay from trigger to output
- Number of repetitions
- Prescaler
Then, after queueing the waveforms, we "start" the AWG (with our awg_start
function), which then starts outputting waveforms upon acquisition of triggers.
Moreover, each AWG card has it's own internal 100MHz clock which is phase-locked to the chassis clock: any actions taken by the AWG occur on a clock tick (so in intervals of 10ns), and different AWG/Digitizer cards are synchronized through the chassis clock. Each card also has an extra port called the TRG port, on which triggers can be received or generated.
Usage
All instrument control and configuration happens through the following Instrument
subtype:
#
InstrumentControl.AWGM320XA.InsAWGM320XA
— Type.
mutable struct InsAWGM320XA <: Instrument serial_num::String product_name::String index::Int chassis_num::Int slot_num::Int channels::Dict{Int, Dict{Any, Any}} waveforms::Dict{Int, Waveform} end
Object representing an AWGM320XA instrument. It holds, as fields, instrument information such as AWG instrument index, slot number, chassis number, serial number, product name, etc. We take this object to represent both M3202A AWGs and M3201A AWGs, since they have the exact same functionality, they only have different specs. The product_name
field can be used to distinguish wether an object of type InsAWGM320XA
correponds to an M3202A or M3101A AWG via its "name".
In addition, the AWG object holds all the waveforms stored in RAM in a dictionary, named waveforms
, where the waveforms are indexed by their identifier number, which the user specifies when he/she loads an waveform into the RAM of the AWG.
This object also holds individual channel properties in a dictionary named channels
, where in the type's implementation, the values of the dictionaries are themselves dictionaries that hold all configuration information for a particular channel.For example, for an object ins::InsAWGM320XA
, ins.channels[1]
will return a dictionary that holds all configuration information for channel 1; its keys will be subtypes of the InstrumentProperty
abstract type, and its values will be the values which the instrument property associated that subtype are configured to in that channel in the digitizer.
Two inner constructors are provided: one which initializes the object with a given slot number and chassis number, and one which initializes the object with a given serial number and name. Both inner constructors also take a num_channels
keyword argument, corresponding to the number of channels on the instrument, which is used for initial configuration of the instrument channels. When the object is initialized, it obtains all other instrument information described above with the passed arguments, initializes the waveforms
dictionary, and initializes all the channels properties to some standard values and records them in the channels
dictionary through the configure_channels!
function.
For example, calling InsAWGM320XA(3,1,num_channels = 4)
will open the AWG card on slot 3 on the PXI chassis indexed by 1 (internal Keysight software does the chassis "indexing"); num_channels
corresponds to the number of channels in the AWG card. The second input, which sets the value for the chassis
field in the InsAWGM320XA
object, is an optional argument with default value 1
(for the case when only one chassis is connected to the computer), while the num_channels
input is a keyword argument whose default value is 4
–> Hence, InsAWGM320XA(3)
would make an instance of the same object.
Moreover, once waveforms are loaded and queued, each channel's AWG can be started and checked to see if it's running with the following methods
#
InstrumentControl.AWGM320XA.awg_start
— Function.
awg_start(awg::InsAWGM320XA, ch::Integer) awg_start(awg::InsAWGM320XA, chs::Vararg{Int}) Starts acquisition of triggers for generation/ouputting of all waveforms queued on the awg(s) of channel `ch` or channels `chs`.
#
InstrumentControl.AWGM320XA.awg_is_running
— Function.
awg_is_run(awg::InsAWGM320XA, ch::Integer) Checks if the AWG corresponding to channel `ch` or channels `ch` on AWG card corresponding to object `awg` is "running", i.e. it is waiting for triggers to output waveforms or is actively outputting waveforms. Prints either "YES" or "NO"
Configuration
Configuration/Inspection of settings happens through setindex!/getindex methods, as described in Overview
These are the following properties which can be configured, each of which has it's own InstrumentProperty
subtype:
#
InstrumentControl.AWGM320XA.OutputMode
— Type.
Configures the type of output from an AWG channel. Can be configured to be either :Off, :Sinusoidal, :Triangular, :Square, :DC, :Arbitrary, or :Differential
#
InstrumentControl.AWGM320XA.DCOffset
— Type.
Sets a channel's DC offset on output. Configured to be a Float64
number.
#
InstrumentControl.AWGM320XA.FGFrequency
— Type.
Sets a channel's function generator frequency. Configured to be a Float64
number.
#
InstrumentControl.AWGM320XA.FGPhase
— Type.
Sets a channel's function generator's relative phase. Configured to be a Float64
number.
#
InstrumentControl.AWGM320XA.TrigSource
— Type.
Source of external trigger. Can be configured to be either a number 0-7, which corresponds to a PXI line on the PXI backplane, or :TrgPort, which corresponds to the Trg port on the AWG card
#
InstrumentControl.AWGM320XA.TrigBehavior
— Type.
External trigger behavior is what behavior by the external trigger actually triggers the DAQ to start acquiring data. Can be configured to either: :Rising, :Falling, :High, or :Low; corresponding to, respectively, trigger on the rising edge, trigger on the falling edge, trigger on high voltage, trigger on low voltage
#
InstrumentControl.AWGM320XA.TrigSync
— Type.
Can be configured to be :CLKsys or :CLK10, which, respectively, corresponds to processing an acquired trigger on the next internal clock tick, or on the next chassis clock tick.
#
InstrumentControl.AWGM320XA.Queue
— Type.
List of waveform IDs, corresponding to waveforms queued in a particular channel. Order of waveform IDs corresponds to order of queued waveforms
#
InstrumentControl.AWGM320XA.QueueCycleMode
— Type.
Can be configured to, respectively, :Cyclic or :OneShot, which corresponds to, respectively, configuring the queue to repeat indefinitely, or configuring the queue to only be outputted one time
#
InstrumentControl.AWGM320XA.QueueSyncMode
— Type.
Can be configured to be :CLKsys or :CLK10, which, respectively, corresponds to start sequencing a queue on the next internal clock tick, or on the next chassis clock tick.
#
InstrumentControl.AWGM320XA.AmpModMode
— Type.
Amplitude modulation mode. Can be configured to either :NoMod, :AmplitudeMod, or :DCMod
#
InstrumentControl.AWGM320XA.AngModMode
— Type.
Angle modulation mode. Can be configure to either :NoMod, :PhaseMod, or :FrequencyMod.
#
InstrumentControl.AWGM320XA.AmpModGain
— Type.
Amplitude of amplitude modulating signal. Configured to be a Float64
number.
#
InstrumentControl.AWGM320XA.AngModGain
— Type.
Amplitude of angle modulating signal. Configured to be a Float64
number.
Waveforms
Basics In this module's implementation, we allow for handling of waveforms through the following type:
#
InstrumentControl.AWGM320XA.Waveform
— Type.
mutable struct Waveform waveformValues::Array{Float64} name::String ch_properties::Dict{Int, Dict{Any, Any}} Waveform(waveformValues, name) = begin wav = new() wav.name = name wav.waveformValues = waveformValues wav.ch_properties = Dict{Int, Dict{Any, Any}}() return wav end end
Object representing a waveform; it holds as fields a waveform name, the actual waveform digital values, and individual channel properties in a dictionary named ch_properties
. When waveforms are queued in an AWG channel from the RAM, various settings pertaining to when and how the waveform will be generated and outputted must be specified, such as: what triggers the waveform to start, how many times it should repeat in a cycle, what is the delay between the trigger and the generation of the waveform, etc. These settings can also vary from channel to channel, and cannot be queried directly from the instrument
Thus, we store this waveform channel configuration information in the dictionary ch_properties
. In this type's implementation, the values of the dictionaries are themselves dictionaries that hold all configuration information particular to a channel.For example, for an object wav::Waveform
, wav.ch_properties[1]
will return a dictionary that holds all configuration information specific to channel 1. Its keys will be subtypes of the WaveChProperty
abstract type, and its values will be the values which the channel property associated that subtype are configured to for the waveform.
The inner constructor provided takes an array of points and a waveform name, and initializes a blank ch_properties
dictionary.
When a waveform is loaded into the RAM, the corresponding Waveform
object is stored in the waveforms
field of the InsAWGM320XA
object, indexed by the ID specified when loading the waveform. When a waveform is queued in a particular channel, it's ID is added to a list of the queued waveforms which can be accessed by a getindex
with Queue
as the "index". This object conveniently stores the waveform array values, a name (meant to be more descriptive than an integer), and queue properties once a waveform is queued.
Natively, you load waveforms into the AWG as arrays through Keysight's native C functions, where you specify an "id" which is the defacto handle for handling waveforms for all subsequent queueing. However, once you load a waveform you can't query the instrument to see it, and an id is not exactly a descriptive handle; moreover, once you queue a waveform you can't query the instrument for what settings the waveform was queued with (trigger_mode, delay, etc). We get around these nuisances by making all loading and queueing of waveforms be tied to instantiating and manipulating these Waveform
objects. However, waveform manipulation can also be done through the waveform ID's if so desired by the user.
Usage
Once you have a Waveform
object, or an array of values to load onto the AWG's RAM as a waveform, these are the methods to load and queue waveforms, as well as to empty the queues, empty the RAM, etc:
#
InstrumentControl.AWGM320XA.load_waveform
— Function.
load_waveform(ins::InsAWGM320XA, waveform::Waveform, id::Integer; input_type::Symbol = :Analog16) load_waveform(ins::InsAWGM320XA, waveformValues::Array{Float64}, id::Integer, name::AbstractString = string(id); input_type::Symbol = :Analog16) load_waveform(ins::InsAWGM320XA, waveformFile::String, id::Integer, name::AbstractString = string(id))
Loads a waveform into the the RAM of the AWG corresponding to object ins
. When loading a waveform, the user picks an id to be the identifier and handle for the loaded waveform. Thus, the function takes as arguments either: A Waveform
object , or the waveform digital values, or a filepath to a waveform file, as well as the user-specified id, a name for the waveform (meant to be a more descriptive identifier than an integer), and the input type(refer to Table 9 of the userguide for discussion on input types). The waveform digital values can be passed as an array of values, or as a path to a file containing the values.
The loads a the waveform, initializes a waveform object if passed an array or filepath, and initializes the blank dictionaries for each channel in the field ch_properties
. The function returns a handle to the new Waveform
object created and stored in ins.waveforms
, indexed by it's user-specified id.
#
InstrumentControl.AWGM320XA.queue_waveform
— Function.
queue_waveform(ins::InsAWGM320XA, ch::Integer, id::Integer, trigger_mode::Symbol; repetitions::Integer = 1, delay::Integer = 0, prescaler::Integer = 0 ) queue_waveform(ins::InsAWGM320XA, ch::Integer, wav::Waveform, trigger_mode::Symbol; repetitions::Integer = 1, delay::Integer = 0, prescaler::Integer = 0 )
Queues a waveform in a channel of the AWG either by a passed Waveform
object or the waveform id. It also takes inputs for configuration settings pertaining to when and how the waveform will be generated and outputted, such as trigger mode, number of repetitions, delay between the trigger and generation, and the prescaler value for the outputted waveform. While the channel and trigger_mode must be specified, the other settings are used as keyword arguments with standard values (chosen by me).
#
InstrumentControl.AWGM320XA.waveforms_flush
— Function.
waveforms_flush(ins::InsAWGM320XA)
Erases all waveforms stored in the RAM of the AWG, and empties the queue of all the channels. This function both: uses the native C SD_AOU_waveformFlush
function to empty the erase the RAM and empty the queues in the instrument, it re-initializes ins.channels[ch][Queue]
vector as a blank vector for all channels, erasing the record of the queue in the InsAWGM30XA
object as well, and re-initializes ins.waveforms
as a blank dictionary
#
InstrumentControl.AWGM320XA.memory_size
— Function.
memory_size(ins::InsAWGM320XA, wav::Waveform) memory_size(ins::InsAWGM320XA, id::Integer)
Obtain size of waveform in memory