Alazar digitizers

We put all Alazar digitizers in module AlazarModule; the feature set and API is so similar for the various models that just one module makes sense.

An AlazarResponse type is given for each measurement mode of the digitizer. ("Traditional record mode" has not been implemented yet for lack of immediate need.) Additionally, one can define custom AlazarResponse types so that some custom computations or data reductions can be performed during acquisition. IQSoftwareResponse is a good example of this.

In the following discussion, it is important to understand some Alazar terminology. Newer Alazar digitizers use direct memory access (DMA) to stream data into a computer's RAM. A single acquisition uses one or many buffers, which constitute preallocated regions in the computer's physical memory. Each buffer contains one or many records. Each record contains many samples, which are the voltages measured by the digitizer. In streaming mode, there is only one record per buffer, but in other modes there can be many records per buffer.

Looking at the source code, it would seem that there is some redundancy in the types, for instance there is an NPTRecordMode and an NPTRecordResponse object. The former is used internally in the code to denote a particular method of configuring the instrument, mirroring the terminology used in the Alazar API documentation. The latter specifies what you actually want to do using that configuration of the instrument. Perhaps you want to measure in a given mode, but do some custom post-processing or processing during acquisition. IQSoftwareResponse is an example of this: it uses NPTRecordMode but reduces the data during measurement. Typically the user doesn't have to deal with the various mode objects, unless developing a new AlazarResponse type.

Usage

Here are the currently defined AlazarResponse types you can use:

# InstrumentControl.AlazarModule.ContinuousStreamResponseType.

mutable struct ContinuousStreamResponse{S,T} <: StreamResponse{S,T}

Response type implementing the "continuous streaming mode" of the Alazar API.

Usage example:

ats = AlazarATS9870()
samples_per_ch = 1024
res = ContinuousStreamResponse(ats, samples_per_ch)
measure(res)

source

# InstrumentControl.AlazarModule.TriggeredStreamResponseType.

mutable struct TriggeredStreamResponse{S,T} <: StreamResponse{S,T}

Response type implementing the "triggered streaming mode" of the Alazar API.

Usage example:

ats = AlazarATS9870()
samples_per_ch = 1024
res = TriggeredStreamResponse(ats, samples_per_ch)
measure(res)

source

# InstrumentControl.AlazarModule.NPTRecordResponseType.

mutable struct NPTRecordResponse{S,T} <: RecordResponse{S,T}

Response type implementing the "NPT record mode" of the Alazar API.

Usage example:

ats = AlazarATS9870()
samples_per_rec_per_ch = 1024
total_recs = 10
res = NPTRecordResponse(ats, sam_per_rec_per_ch, total_recs)
measure(res)

source

# InstrumentControl.AlazarModule.FFTHardwareResponseType.

mutable struct FFTHardwareResponse{S,T,U} <: FFTResponse{S,T}

Response type implementing the FPGA-based "FFT record mode" of the Alazar API. Not all Alazar digitizers support this mode.

Usage example:

ats = AlazarATS9360()
samples_per_rec = 1024
sam_per_fft = 512
total_recs = 10
res = FFTHardwareResponse(ats, sam_per_rec, sam_per_fft, total_recs, Alazar.S32Real)
measure(res)

source

# InstrumentControl.AlazarModule.IQSoftwareResponseType.

mutable struct IQSoftwareResponse{S,T} <: RecordResponse{S,T}

Response type for measuring with NPT record mode and mixing in software to find the phase and amplitude of a component at frequency f. Slower than doing it in an FPGA, but ultimately necessary if we want to use both channels as inputs to the FFT.

Usage example:

ats = AlazarATS9870()
samples_per_rec_per_ch = 1024
total_recs = 10
f = 100e6
res = IQSoftwareResponse(ats, samples_per_rec_per_ch, total_recs, f)
measure(res)

source

These definitions referenced some abstract types, described here:

# InstrumentControl.AlazarModule.AlazarResponseType.

abstract type AlazarResponse{S,T} <: Response

Abstract Response from an Alazar digitizer instrument. Subtypes should implement return_type to specify the output array type if they rely on the generic method measure(::AlazarResponse).

source

# InstrumentControl.AlazarModule.StreamResponseType.

abstract type StreamResponse{S,T} <: AlazarResponse{S,T}

Abstract time-domain streaming Response from an Alazar digitizer instrument.

source

# InstrumentControl.AlazarModule.RecordResponseType.

abstract type RecordResponse{S,T} <: AlazarResponse{S,T}

Abstract time-domain record Response from an Alazar digitizer instrument.

source

Technical details

Digitizer requirements

The Alazar digitizers expect buffers in physical memory which are page-aligned. The size of each buffer should also be chosen appropriately.

The behavior of the digitizer is not specified when the buffer is made larger than 64 MB. On our computer, it seems like an ApiWaitTimeout error is thrown when the buffer is too large (for some unspecified definition of "large" greater than 64 MB). The digitizer will then throw ApiInsufficientResources errors whenever another acquisition is attempted, until the computer is restarted. Just restarting the Julia kernel, forcing a reload of the Alazar DLLs, does not appear to be enough to reset the digitizer fully.

For performance reasons, a buffer should not be made much smaller than 1 MB if mulitple buffers are required. There is also a minimum record size for each model of digitizer. For the ATS9360, if a record has fewer than 256 samples (could be 128 from channel A + 128 from channel B) then the acquisition will proceed, but return garbage data. Allocating too small of a buffer is therefore still bad, but less fatal than allocating one that is too large.

How to allocate appropriate buffers in Julia

In Julia, just allocating an array will not necessarily return a page-aligned block in memory. The Alazar.jl package provides two array types to help with this. These arrays are not required to be created by the user.

  • Alazar.PageAlignedVector is just like Base.Vector except that the memory backing the array is guaranteed to be page-aligned.
  • The elements of an Alazar.DMABufferVector are pointers to different locations in memory which are page-aligned and can act as DMA buffers. The array is iterable and indexable as usual. The Alazar.DMABufferVector must itself be backed by a page-aligned array type, like Alazar.PageAlignedVector or Base.SharedVector (although support for Base.SharedVector is absent in InstrumentControl).