spectral module¶
FFT-based measurements of spectral content.
Detailed information is provided in the documentation for each class.
Use the hardware in spectral mode. |
|
Specify the spectral measurement mode. |
|
Container for measurement results in spectral mode. |
|
A receiver of spectral data running in the background. |
Spectral class¶
- class presto.spectral.Spectral(*, nr_inputs=1, ext_ref_clk=False, force_reload=False, dry_run=False, address=None, port=None, adc_mode=AdcMode.Mixed, adc_fsample=None, dac_mode=DacMode.Mixed, dac_fsample=None, force_config=False)¶
Use the hardware in spectral mode.
Warning
Create only one instance at a time. This class is designed to be instantiated with Python’s with statement, see Examples section.
- Parameters:
nr_inputs (
int
) – number of input ports that are active simultaneously. The spectral content (FFT or PSD) will be measured in parallel from this many ports.ext_ref_clk (
Union
[None
,bool
,int
,float
]) – ifNone
orFalse
use internal reference clock; ifTrue
use external reference clock (10 MHz); iffloat
orint
use external reference clock atext_ref_clk
Hzforce_reload (
bool
) – ifTrue
re-configure clock and reload firmware even if there’s no change from the previous settings.dry_run (
bool
) – ifTrue
don’t connect to hardware, for testing only.address (
Optional
[str
]) – IP address or hostname of the hardware. IfNone
, use factory default"192.168.42.50"
.port (
Optional
[int
]) – port number of the server running on the hardware. IfNone
, use factory default7878
.adc_mode (AdcMode or list) – must be
AdcMode.Mixed
(default), using the digital mixers for downconversion.Direct
mode is not supported in spectral mode.adc_fsample (AdcFSample or list) – configure the inputs sampling rate. If
None
(default), choose optimal rate automatically. See Notes.dac_mode (DacMode or list) – must be
DacMode.Mixed
(default) or another of the mixed variants, using the digital mixers for upconversion.Direct
mode is not supported in spectral mode. See Notes.dac_fsample (DacFSample or list) – configure the outputs sampling rate. If
None
(default), choose optimal rate automatically. See Notes.force_config (
bool
) – ifTrue
skip checks onDacMode
s not recommended for high DAC sampling rates.
- Raises:
ValueError – if
adc_mode
ordac_mode
are not valid Converter modes; ifadc_fsample
ordac_fsample
are not valid Converter rates.
Notes
In all the Examples, it is assumed that the imports
import numpy as np
andfrom presto.spectral import Spectral, SpecMode
have been performed, and that this class has been instantiated in the formspec = Spectral()
, or, much much better, using thewith Spectral() as spec
construct as below.For an introduction to
adc_mode
anddac_mode
, see Direct and Mixed mode. For valid values ofadc_mode
anddac_mode
, see Converter modes. For valid values ofadc_fsample
anddac_fsample
, see Converter rates. For advanced configuration, e.g. different converter rates on different ports, see Advanced tile configuration.Examples
Drive two continuous-wave signals from output port 1 and 2, and measure averaged FFT from input port 1. Plot the results.
>>> import matplotlib.pyplot as plt >>> import numpy as np >>> >>> IN_PORT = 1 >>> OUT_PORT_1 = 1 >>> OUT_PORT_2 = 2 >>> >>> with Spectral(address="192.168.20.10") as spec: >>> # configure 1.5 GHz carrier for up- and down-conversion >>> spec.hardware.configure_mixer( >>> freq=1.5e9, >>> in_ports=IN_PORT, >>> out_ports=[OUT_PORT_1, OUT_PORT_2], >>> ) >>> >>> # 1 MHz resolution in FFT >>> period = spec.tune_period(1.0e-6) >>> >>> # on the first output port, >>> # drive two tones at 110 and 112 MHz from the carrier >>> # with same phase but different amplitude >>> # one below the carrier (lower sideband) and one above (upper sideband) >>> f1 = [-110.0e6, +120.0e6] >>> a1 = [0.1, 0.2] >>> p1 = [0.0, 0.0] >>> spec.output_multicos(OUT_PORT_1, period, f1, a1, p1) >>> >>> # on the second output port, >>> # drive one tone on port 2 at the carrier frequency >>> f2 = 0.0 >>> a2 = 0.5 >>> p2 = 0.0 >>> spec.output_multicos(OUT_PORT_2, period, f2, a2, p2) >>> >>> # measure FFT data, averaged 1000 times >>> res = spec.measure(SpecMode.FftAvg, IN_PORT, 1000, period) >>> >>> # plot data >>> >>> # frequencies in MHz >>> x = np.fft.fftshift(res.freqs) / 1.0e6 >>> # data in dBFS >>> y = np.fft.fftshift(res.to_db()[0]) >>> >>> fig, ax = plt.subplots() >>> ax.plot(x, y) >>> ax.set_xlabel("Offset frequency [MHz]") >>> ax.set_ylabel("Amplitude [dBFS]") >>> ax.set_title("Center frequency 1.5 GHz") >>> ax.grid() >>> plt.show()
Drive two random signals from output ports 1 and 2 into input ports 1 and 2, one delayed 100 ns from the other, and measure the correlation of the two.
>>> import matplotlib.pyplot as plt >>> import numpy as np >>> >>> IN_PORTS = [1, 2] >>> OUT_PORTS = [1, 2] >>> >>> with Spectral( >>> nr_inputs=len(IN_PORTS), >>> address="192.168.20.10", >>> ) as spec: >>> # configure 1.5 GHz carrier for up- and down-conversion >>> spec.hardware.configure_mixer(1.5e9, in_ports=IN_PORTS, out_ports=OUT_PORTS) >>> >>> # 1 MHz resolution in FFT >>> period = spec.tune_period(1.0e-6) >>> >>> # generate random data and output from first port >>> def random_complex(ns): >>> rng = np.random.default_rng() >>> real = 2.0 * rng.random(ns) - 1.0 >>> imag = 2.0 * rng.random(ns) - 1.0 >>> return real + 1j * imag >>> >>> duration = 10 * period >>> nr_samples = int(round(duration * spec.get_fs("dac"))) >>> data1 = 0.1 * random_complex(nr_samples) >>> spec.output_waveform(OUT_PORTS[0], data1) >>> >>> # output same data on second port, >>> # but delayed by 100 samples (100 ns) >>> data2 = np.roll(data1, 100) >>> spec.output_waveform(OUT_PORTS[1], data2) >>> >>> # measure cross power spectral density (CPSD) data >>> # averaged 10 times >>> res = spec.measure(SpecMode.Cpsd, IN_PORTS, 10, period) >>> >>> # calculate correlation from measured CPSD >>> # and plot results >>> >>> x, y = res.to_correlation() >>> fig, ax = plt.subplots() >>> ax.plot(1e9 * x, np.real(y[0])) >>> ax.plot(1e9 * x, np.imag(y[0])) >>> ax.set_xlabel("Time lag [ns]") >>> ax.set_ylabel("Correlation [arb. units]") >>> ax.set_title("Center frequency 1.5 GHz") >>> ax.grid() >>> plt.show()
- clear_outputs()¶
Clear the waveforms set up on all output ports.
- close()¶
Gracefully disconnect from the hardware.
Call this method if the class was instantiated without a with statement. This method is called automatically when exiting a
with
block and before the object is destructed.
- fftfreq(period)¶
Return the fast Fourier transform sample frequencies.
The returned array contains the frequency bin centers in hertz (cycles second), with zero at the start. This method is similar to
numpy.fft.fftfreq()
, but with different arguments.- Parameters:
period (
float
) – the FFT measurement period, or window length, in seconds.- Return type:
Note
The period is assumed to be tuned. See
tune_period()
for details.
- get_fs(which)¶
Get sampling frequency for
which
converter in Hz.Note
This is the sampling rate in the intermediate-frequency (IF) domain, and not the (higher) sampling rate in the radio-frequency (RF) domain.
- Parameters:
which (
str
) –"adc"
for input and"dac"
for output.- Raises:
ValueError – if
which
is unknown.- Return type:
- measure(mode, input_ports, nr_meas, period, auto_sync=True, quiet=False)¶
Measure the spectral content of the signal at
input_ports
.Depending on
mode
, measure the averaged fast Fourier transform (FFT), multiple FFT windows, the power spectral density (PSD), or the cross PSD (CPSD).- Parameters:
mode (
SpecMode
) – select measurement mode, seeSpecMode
for detailsinput_ports (int or list) – input port, can be a list of up to 4 ports
nr_meas (
int
) – number of individual FFT/PSD windows to measure or to average, depending onmode
period (
float
) – requested measurement period (integration time) for the FFT/PSD, corresponding to the inverse of the frequency resolution. See Notes section below for detailsauto_sync (
bool
) – ifTrue
(default), trigger signal generation and acquisition internally. Set toFalse
to synchronize to an external source, e.g. a Metronomo unit.quiet (
bool
) – set toTrue
to inhibit printing timing information
- Return type:
- Returns:
res
- a container for the measurement results, seeSpecResult
for documentation
Notes
The period is assumed to be tuned, see
tune_period()
for details. If period is not tuned, the backend will choose the available period that is closest to the user-requested value.The shape and data type of the returned
res.data
depends on the measurementmode
, seeSpecResult.data
for documentation.- Raises:
ValueError – if
input_ports
is not in [1,get_nr_ports()
]ValueError – if
len(input_ports)
is not 1, 2 or 4RuntimeError – if any setup error is detected while setting up the measurement
Examples
See documentation for the
Spectral
class for examples.
- property nr_inputs¶
The current number of interleaved input ports.
This value is read only. To change it, create a new instance of the
Spectral
class and pass the desired number of input ports as the nr_inputs parameter.
- output_multicos(output_ports, period, frequencies, amplitudes, phases=None)¶
Set up a multi-tone waveform on one or more output ports.
- Parameters:
output_ports (int or list) – output port, can be a list of up to 4 ports
period (
float
) – the repetition period of the output waveform, in secondsfrequencies (list of float) – frequencies of the individual tones that comprise the waveform, in hertz
amplitudes (list of float) – amplitudes of the individual tones that comprise the waveform, in full-scale units between -1.0 and +1.0 FS
phases (list of float, optional) – phases of the individual tones that comprise the waveform, in radians. If
None
(default), all tones have zero phase
Notes
Positive frequencies are up-coverted to the upper sideband (USB) by the digital up-conversion mixer, and negative frequencies are up-coverted to the lower sideband (LSB).
If
sum(abs(amplitudes))
> 1.0, it is possible for the tones in the waveform to interfere constructively and cause an overflow (clipping), resulting in a heavily-distorted output waveform. To prevent this effect, ensure thatsum(abs(amplitudes))
<= 1.0, or adjust thephases
of the tones to prevent isolated peaks in the resulting waveform.- Raises:
ValueError – if
output_ports
is not in [1,get_nr_ports()
]ValueError – if
len(amplitudes) != len(frequencies)
ValueError – if
len(phases) != len(frequencies)
ValueError – if any of
abs(frequencies)
>= Nyquist frequency, i.e. half ofget_fs("dac")
ValueError – if any of
abs(amplitudes) > 1.0
RuntimeError – if there are no more output slots available
See also
- output_waveform(output_ports, data)¶
Set up an arbitrary waveform on one or more output ports.
- Parameters:
Notes
The real part of
data
is fed to the I port of the digital up-conversion mixer, while the imaginary part ofdata
is fed to the Q port.- Raises:
ValueError – if
output_ports
is not in [1,get_nr_ports()
]ValueError – if
data
is out of range: both real and imaginary parts must be in [-1.0, +1.0]RuntimeError – if there are no more output slots available
See also
- setup_delay(*, pre_delay=0.0, start_delay=0.0, end_delay=0.0)¶
Configure FFT measurement delays.
- Parameters:
Notes
pre_delay
adds a delay at the very beginning of the measurement sequence, between the start of the output waveform (DAC) and the first input window (ADC).start_delay
andend_delay
add some “dead time” within each input window. Data measured during this dead time is discarded and not included in the FFT calculation:
- stream(input_ports, period, indexes=None, freqs=None, auto_sync=True, quiet=False)¶
Measure continuously the spectral content of the signal at
input_ports
.This will measure the fast Fourier transform (FFT) of the signal.
- Parameters:
input_ports (int or list) – input port, can be a list of up to 4 ports
period (
float
) – requested measurement period (integration time) for the FFT, corresponding to the inverse of the frequency resolution. See Notes section below for detailsindexes (list of int, optional) – which FFT frequency components (bin indexes) should be measured. If
None
(default), all of themfreqs (list of float, optional) – which FFT frequency components (bin indexes) should be measured. If
None
(default), all of themauto_sync (
bool
) – ifTrue
(default), trigger signal generation and acquisition internally. Set toFalse
to synchronize to an external source, e.g. a Metronomo unit.quiet (
bool
) – set toTrue
to inhibit printing timing information
- Return type:
- Returns:
recv
- a handle to the spectral receiver, seeSpecReceiver
for documentation
Notes
The period is assumed to be tuned, see
tune_period()
for details. If period is not tuned, the backend will choose the available period that is closest to the user-requested value.By default, all measured FFT frequencies will be streamed, see
fftfreq()
. Optional arguments indexes and freqs can be used to stream only a subset of all the measured frequencies and therefore save bandwidth and memory.Use freqs to select components based on their value in hertz (Hz). If the requested value is not a valid FFT frequency, the closest valid value will be selected. Use indexes to select components based on their index in the array returned by
fftfreq()
. It is an error to specify both freqs and indexes.Specifying frequency components with indexes or freqs will ensure that at least those components are streamed. The backend might stream more components than requested, see
recv.freqs
for a list of all the frequencies that are being streamed.- Raises:
ValueError – if
input_ports
is not in [1,get_nr_ports()
]ValueError – if
len(input_ports)
is not 1, 2 or 4ValueError – if both indexes and freqs are not None
RuntimeError – if any setup error is detected while setting up the measurement
Examples
See documentation for the
SpecReceiver
class for examples.
- tune_period(period, *, strategy=None)¶
Tune the provided measurement period to a value supported by the hardware.
Only a limited number of measurement periods are supported by the hardware. The default
tuning strategy
will choose the available period that is closest to the user-requested value.- Parameters:
period (
float
) – the desired FFT measurement period is seconds, equal to the inverse of the desired frequency resolution in hertzstrategy (
Optional
[TuningStrategy
]) – optional tuning strategy
- Return type:
- Returns:
the tuned
period
SpecMode class¶
- class presto.spectral.SpecMode(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)¶
Specify the spectral measurement mode.
Used as parameter to
Spectral.measure()
.- Cpsd¶
Measure the cross power spectral density (CPSD).
- FftAvg¶
Measure the averaged fast Fourier transform (FFT).
- FftStream¶
Measure multiple (stacked) fast Fourier transform (FFT) windows.
- Psd¶
Measure the averaged power spectral density (PSD).
SpecResult class¶
- class presto.spectral.SpecResult(*, mode, freqs, data, input_ports)¶
Container for measurement results in spectral mode.
Used as return type by
Spectral.measure()
.Warning
Do not instantiate this class directly! Call
Spectral.measure()
to obtain a valid instance.-
data:
ndarray
[tuple
[int
,...
],dtype
[TypeVar
(SR_T
, bound=Union
[float64
,complex64
,complex128
])]]¶ Array of measured spectral data (the y axis).
The shape and data type depend on the parameters
mode
,input_ports
andnr_meas
toSpectral.measure()
:Mode
Shape
Type
SpecMode.FftStream
(nr_meas, nr_ports, nr_freqs)
SpecMode.FftAvg
(nr_ports, nr_freqs)
SpecMode.Psd
(nr_ports, nr_freqs)
SpecMode.Cpsd
(nr_ports // 2, nr_freqs)
where
nr_meas
is the number of independent measurements,nr_ports = len(input_ports)
is the number of measured input ports, andnr_freqs = len(freqs)
is the number of measured frequency components.
- to_correlation()¶
Convenience function to convert measured PSD/CPSD
data
to auto/cross correlation.- Return type:
Tuple
[ndarray
[tuple
[int
,...
],dtype
[float64
]],ndarray
[tuple
[int
,...
],dtype
[complex128
]]]- Returns:
- a tuple of
(lags, corr)
where lags
: array of time lags of lengthdata.shape[-1]
corr
: array of auto/cross correlation with same shape asdata
- a tuple of
- Raises:
TypeError – if the measurement
mode
is notSpecMode.Psd
orSpecMode.Cpsd
.
-
data:
SpecReceiver class¶
- class presto.spectral.SpecReceiver(*, sock, freqs, input_ports, nr_inputs, bytes_per_meas, map, buf_size=1073741824)¶
A receiver of spectral data running in the background.
Used as return type by
Spectral.stream()
.Warning
Do not instantiate this class directly! Call
Spectral.stream()
to obtain a valid instance.Examples
>>> with Spectral( >>> address=PRESTOs_IP_ADDRESS, >>> ) as spec: >>> # configure 1.5 GHz carrier for up- and down-conversion >>> spec.hardware.configure_mixer( >>> freq=1.5e9, >>> in_ports=1, >>> out_ports=1, >>> ) >>> >>> # 1 MHz resolution in FFT >>> period = spec.tune_period(1.0e-6) >>> >>> # drive two tones at 110 and 112 MHz >>> # with same phase but different amplitude >>> # on output port 1 >>> freq = [110.0e6, 112.0e6] >>> amp = [0.1, 0.2] >>> phase = [0.0, 0.0] >>> spec.output_multicos(1, period, freq, amp, phase) >>> >>> # continuously receive FFT measurements >>> # on input port 1 >>> # in chunks of 100 measurements >>> with spec.stream(1, period) as recv: >>> data = recv.get_last(100)
- close()¶
Gracefully terminate the stream receiver.
Call this method if the class was instantiated without a with statement. This method is called automatically when exiting a
with
block and before the object is destructed.
- freqs¶
Array of measured frequencies (the x axis)
- get(n)¶
Return
n
measurements received since the last call to this method.This method might block the current thread until
n
new measurements are available.- Return type:
TypeVar
(SR_T
)
Notes
This method keeps track of what measurements have already been returned, and always returns contiguous measurements (without gaps). If the requested measurements are available in the local buffer, they are returned immediately. Otherwise, the thread will block until all the requested measurements have been received.
That means that calling
get(n)
twice results in the same data as callingget(2 * n)
once. It also means that the data returned might have been already buffered locally and not be the latest received from the hardware.See See also section below for getter methods with different semantics.
See also
get_new()
: always receive new data from the hardwareget_last()
: return latest data from local buffer
- get_last(n)¶
Return the last
n
measurements available in the local buffer.This method might block the current thread, if fewer than
n
measurements are available in the local buffer.- Return type:
TypeVar
(SR_T
)
Notes
This method returns the most up-to-date data available in the local buffer, regardless of whether this data has already been returned to the user.
That means that calling
get_last(n)
twice is not equivalent to callingget_last(2 * n)
once: in the former case, the two data sets might overlap.See See also section below for getter methods with different semantics.
- get_new(n)¶
Receive
n
new measurements.This method blocks the current thread until
n
new measurements are received.- Return type:
TypeVar
(SR_T
)
Notes
This method always waits for
n
new measurements to be received from the hardware, without regard for what measurements have already been received in the background but not yet returned to the user.That means that calling
get_new(n)
twice is not equivalent to callingget_new(2 * n)
once: in the former case an arbitrary time gap might be present between the two data sets. Nevertheless, the two data sets will be distinct and non overlapping.See See also section below for getter methods with different semantics.
See also
get()
: keep track of previously returned dataget_last()
: return latest data from local buffer
- property nr_inputs¶
The number of interleaved input ports.
Other¶
- class presto.spectral.TuningStrategy(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)¶
Specify an optional tuning strategy for the FFT period.
Used as parameter to
Spectral.tune_period()
.- AtLeast¶
Choose the period supported by the hardware that is greater than or equal to the user-requested value. If the requested value is greater than the maximum-supported period, choose the latter.
- AtMost¶
Choose the period supported by the hardware that is less than or equal to the user-requested value. If the requested value is smaller than the minimum-supported period, choose the latter.
- Closest¶
DEFAULT – Choose the period supported by the hardware that is closest to the user-requested value. This is the default strategy.