Fork PyRPL on GitHub

pyrpl.hardware_modules package

Submodules

pyrpl.hardware_modules.ams module

class pyrpl.hardware_modules.ams.AMS(parent, name=None)[source]

Bases: pyrpl.modules.HardwareModule

mostly deprecated module (redpitaya has removed adc support).
only here for dac2 and dac3

Setup Attributes:

addr_base = 1077936128
dac0

PWM output 0 [V]

dac1

PWM output 1 [V]

dac2

PWM output 2 [V]

dac3

PWM output 3 [V]

setup(**kwds)
sets up the AMS (just setting the attributes is OK)

pyrpl.hardware_modules.asg module

There are two Arbitrary Signal Generator modules: asg1 and asg2. For these modules, any waveform composed of \(2^{14}\) programmable points is sent to the output with arbitrary frequency and start phase upon a trigger event.

Let’s set up the ASG to output a sawtooth signal of amplitude 0.8 V (peak-to-peak 1.6 V) at 1 MHz on output 2:

asg.output_direct = 'out2'
asg.setup(waveform='halframp', frequency=20e4, amplitude=0.8, offset=0, trigger_source='immediately')
pyrpl.hardware_modules.asg.Asg0

alias of Asg

pyrpl.hardware_modules.asg.Asg1

alias of Asg

class pyrpl.hardware_modules.asg.AsgAmplitudeAttribute(address, bits=14, bitmask=None, norm=1.0, signed=True, invert=False, **kwargs)[source]

Bases: pyrpl.attributes.FloatRegister

workaround to make rms amplitude work

get_value(obj)[source]
set_value(obj, val)[source]
class pyrpl.hardware_modules.asg.AsgOffsetAttribute(**kwargs)[source]

Bases: pyrpl.attributes.FloatProperty

get_value(obj)[source]
set_value(instance, val)[source]
class pyrpl.hardware_modules.asg.WaveformAttribute(options=[], **kwargs)[source]

Bases: pyrpl.attributes.SelectProperty

default = 'sin'
set_value(instance, waveform)[source]
pyrpl.hardware_modules.asg.make_asg(channel=0)[source]

pyrpl.hardware_modules.dsp module

class pyrpl.hardware_modules.dsp.DspModule(rp, name)[source]

Bases: pyrpl.modules.HardwareModule, pyrpl.modules.SignalModule

A module with an input and an auxiliary output_direct signal.

DSP modules can be chained one after the other by setting the input port of module B to module ‘A’. e.g:

from pyrpl import Pyrpl

r = Pyrpl().redpitaya

# Route the signal from analog input 'in1' to pid0 then pid1, then
# monitor the result on the scope:
r.pid0.input = 'in1'
r.pid1.input = 'pid0'
r.scope.input1 = r.pid1  # modules can also be resolved by object

An auxiliary output can be used to output a signal to the DACs. All signals routed to the same DAC are summed together.

# output the signal to analog output 1:
r.pid1.output_direct = 'out1'

# the modulation output of iq0 will be summed with the previous signal
r.iq0.output_direct = 'out1'

Setup Attributes:

input

selects the input signal of the module

inputs
out1_saturated

True if out1 is saturated

out2_saturated

True if out2 is saturated

output_direct

selects to which analog output the module signal is sent directly

output_directs
setup(**kwds)
Sets the module up for acquisition with the current setup attribute values.
class pyrpl.hardware_modules.dsp.InputSelectProperty(options=<function all_inputs_keys>, **kwargs)[source]

Bases: pyrpl.attributes.SelectProperty

a select register that stores logical signals if possible, otherwise the underlying dsp signals

validate_and_normalize(obj, value)[source]
class pyrpl.hardware_modules.dsp.InputSelectRegister(address, options=<function all_inputs>, **kwargs)[source]

Bases: pyrpl.hardware_modules.dsp.InputSelectProperty, pyrpl.attributes.SelectRegister

pyrpl.hardware_modules.dsp.all_inputs(instance)[source]

collects all available logical inputs, composed of all dsp inputs and all submodule inputs, such as lockbox signals etc.

pyrpl.hardware_modules.dsp.all_inputs_keys(instance)[source]

collects all available logical inputs, composed of all dsp inputs and all submodule inputs, such as lockbox signals etc.

pyrpl.hardware_modules.dsp.all_output_directs(instance)[source]
pyrpl.hardware_modules.dsp.dsp_addr_base(name)[source]

pyrpl.hardware_modules.filter module

class pyrpl.hardware_modules.filter.FilterModule(rp, name)[source]

Bases: pyrpl.hardware_modules.dsp.DspModule

Setup Attributes:

inputfilter

Input filter bandwidths [Hz]. 0 = off, positive bandwidth <=> lowpass, negative bandwidth <=> highpass.

inputfilter_options
setup(**kwds)
Sets the module up for acquisition with the current setup attribute values.

pyrpl.hardware_modules.hk module

class pyrpl.hardware_modules.hk.HK(parent, name=None)[source]

Bases: pyrpl.modules.HardwareModule

Setup Attributes:

  • led: LED control with bits 1:8
  • expansion_P0: positive digital io
  • expansion_P1: positive digital io
  • expansion_P2: positive digital io
  • expansion_P3: positive digital io
  • expansion_P4: positive digital io
  • expansion_P5: positive digital io
  • expansion_P6: positive digital io
  • expansion_P7: positive digital io
  • expansion_N0: positive digital io
  • expansion_N1: positive digital io
  • expansion_N2: positive digital io
  • expansion_N3: positive digital io
  • expansion_N4: positive digital io
  • expansion_N5: positive digital io
  • expansion_N6: positive digital io
  • expansion_N7: positive digital io
addr_base = 1073741824
digital_loop

enables digital loop

expansion_N0

positive digital io

expansion_N1

positive digital io

expansion_N2

positive digital io

expansion_N3

positive digital io

expansion_N4

positive digital io

expansion_N5

positive digital io

expansion_N6

positive digital io

expansion_N7

positive digital io

expansion_P0

positive digital io

expansion_P1

positive digital io

expansion_P2

positive digital io

expansion_P3

positive digital io

expansion_P4

positive digital io

expansion_P5

positive digital io

expansion_P6

positive digital io

expansion_P7

positive digital io

i = 7
id

device ID

led

LED control with bits 1 – 8

setup(**kwds)
Sets the HouseKeeping module of the redpitaya up. (just setting the attributes is OK)
led

LED control with bits 1:8

expansion_P0

positive digital io

expansion_P1

positive digital io

expansion_P2

positive digital io

expansion_P3

positive digital io

expansion_P4

positive digital io

expansion_P5

positive digital io

expansion_P6

positive digital io

expansion_P7

positive digital io

expansion_N0

positive digital io

expansion_N1

positive digital io

expansion_N2

positive digital io

expansion_N3

positive digital io

expansion_N4

positive digital io

expansion_N5

positive digital io

expansion_N6

positive digital io

expansion_N7

positive digital io

pyrpl.hardware_modules.iq module

Demodulation of a signal means convolving it with a sine and cosine at the ‘carrier frequency’. The two resulting signals are usually low-pass filtered and called ‘quadrature I’ and ‘quadrature Q’. Based on this simple idea, the IQ module of pyrpl can implement several functionalities, depending on the particular setting of the various registers. In most cases, the configuration can be completely carried out through the setup function of the module.

Lock-in detection / PDH / synchronous detection

#reload to make sure settings are default ones
from pyrpl import Pyrpl
r = Pyrpl(hostname="192.168.1.100").rp

#shortcut
iq = r.iq0

# modulation/demodulation frequency 25 MHz
# two lowpass filters with 10 and 20 kHz bandwidth
# input signal is analog input 1
# input AC-coupled with cutoff frequency near 50 kHz
# modulation amplitude 0.1 V
# modulation goes to out1
# output_signal is the demodulated quadrature 1
# quadrature_1 is amplified by 10
iq.setup(frequency=25e6, bandwidth=[10e3,20e3], gain=0.0,
         phase=0, acbandwidth=50000, amplitude=0.5,
         input='in1', output_direct='out1',
         output_signal='quadrature', quadrature_factor=10)

After this setup, the demodulated quadrature is available as the output_signal of iq0, and can serve for example as the input of a PID module to stabilize the frequency of a laser to a reference cavity. The module was tested and is in daily use in our lab. Frequencies as low as 20 Hz and as high as 50 MHz have been used for this technique. At the present time, the functionality of a PDH-like detection as the one set up above cannot be conveniently tested internally. We plan to upgrade the IQ-module to VCO functionality in the near future, which will also enable testing the PDH functionality.

Network analyzer

When implementing complex functionality in the RedPitaya, the network analyzer module is by far the most useful tool for diagnostics. The network analyzer is able to probe the transfer function of any other module or external device by exciting the device with a sine of variable frequency and analyzing the resulting output from that device. This is done by demodulating the device output (=network analyzer input) with the same sine that was used for the excitation and a corresponding cosine, lowpass-filtering, and averaging the two quadratures for a well-defined number of cycles. From the two quadratures, one can extract the magnitude and phase shift of the device’s transfer function at the probed frequencies. Let’s illustrate the behaviour. For this example, you should connect output 1 to input 1 of your RedPitaya, such that we can compare the analog transfer function to a reference. Make sure you put a 50 Ohm terminator in parallel with input 1.

# shortcut for na
na = p.networkanalyzer
na.iq_name = 'iq1'

# setup network analyzer with the right parameters
na.setup(start=1e3,stop=62.5e6,points=1001,rbw=1000, avg=1,
amplitude=0.2,input='iq1',output_direct='off', acbandwidth=0)

#take transfer functions. first: iq1 -> iq1, second iq1->out1->(your cable)->adc1
iq1 = na.curve()
na.setup(input='in1', output_direct='out1')
in1 = na.curve()

# get x-axis for plotting
f = na.frequencies

#plot
from pyrpl.hardware_modules.iir.iir_theory import bodeplot
%matplotlib inline
bodeplot([(f, iq1, "iq1->iq1"), (f, in1, "iq1->out1->in1->iq1")], xlog=True)

If your cable is properly connected, you will see that both magnitudes are near 0 dB over most of the frequency range. Near the Nyquist frequency (62.5 MHz), one can see that the internal signal remains flat while the analog signal is strongly attenuated, as it should be to avoid aliasing. One can also see that the delay (phase lag) of the internal signal is much less than the one through the analog signal path.

Note

The Network Analyzer is implemented as a software module, distinct from the iq module. This is the reason why networkanalyzer is accessed directly at the Pyrpl-object level p.networkanalyzer and not at the redpitaya level p.rp.networkanalyzer. However, an iq module is reserved whenever the network analyzer is acquiring data.

If you have executed the last example (PDH detection) in this python session, iq0 should still send a modulation to out1, which is added to the signal of the network analyzer, and sampled by input1. In this case, you should see a little peak near the PDH modulation frequency, which was 25 MHz in the example above.

Lorentzian bandpass filter

The iq module can also be used as a bandpass filter with continuously tunable phase. Let’s measure the transfer function of such a bandpass with the network analyzer:

# shortcut for na and bpf (bandpass filter)
na = p.networkanalyzer
bpf = p.rp.iq2

# setup bandpass
bpf.setup(frequency = 2.5e6, #center frequency
          bandwidth=1.e3, # the filter quality factor
          acbandwidth = 10e5, # ac filter to remove pot. input offsets
          phase=0, # nominal phase at center frequency (propagation phase lags not accounted for)
          gain=2.0, # peak gain = +6 dB
          output_direct='off',
          output_signal='output_direct',
          input='iq1')

# setup the network analyzer
na.setup(start=1e5, stop=4e6, points=201, rbw=100, avg=3,
                         amplitude=0.2, input='iq2',output_direct='off')

# take transfer function
tf1 = na.curve()

# add a phase advance of 82.3 degrees and measure transfer function
bpf.phase = 82.3
tf2 = na.curve()

f = na.frequencies

#plot
from pyrpl.hardware_modules.iir.iir_theory import bodeplot
%matplotlib inline
bodeplot([(f, tf1, "phase = 0.0"), (f, tf2, "phase = %.1f"%bpf.phase)])

Note

To measure the transfer function of an internal module, we cannot

use the output_direct property of the network ananlyzer (only ‘out1’, ‘out2’ or ‘off’ are allowed). To circumvent the problem, we set the input of the module to be measured to the network analyzer’s iq.

Frequency comparator module

To lock the frequency of a VCO (Voltage controlled oscillator) to a frequency reference defined by the RedPitaya, the IQ module contains the frequency comparator block. This is how you set it up. You have to feed the output of this module through a PID block to send it to the analog output. As you will see, if your feedback is not already enabled when you turn on the module, its integrator will rapidly saturate (-585 is the maximum value here, while a value of the order of 1e-3 indicates a reasonable frequency lock).

iq = p.rp.iq0

# turn off pfd module for settings
iq.pfd_on = False

# local oscillator frequency
iq.frequency = 33.7e6

# local oscillator phase
iq.phase = 0
iq.input = 'in1'
iq.output_direct = 'off'
iq.output_signal = 'pfd'

print("Before turning on:")
print("Frequency difference error integral", iq.pfd_integral)

print("After turning on:")
iq.pfd_on = True
for i in range(10):
    print("Frequency difference error integral", iq.pfd_integral)
class pyrpl.hardware_modules.iq.Iq(rp, name)[source]

Bases: pyrpl.hardware_modules.filter.FilterModule

A modulator/demodulator module.

Setup Attributes:

  • acbandwidth: positive corner frequency of input high pass filter
  • frequency: frequency of iq demodulation [Hz]
  • bandwidth: Quadrature filter bandwidths [Hz].0 = off, negative bandwidth = highpass
  • quadrature_factor: amplification factor of demodulated signal [a.u.]
  • output_signal: Signal to send back to DSP multiplexer
  • gain: gain of the iq module (see drawing)
  • amplitude: amplitude of coherent modulation [volts]
  • phase: Phase shift between modulation and demodulation [degrees]
acbandwidth

positive corner frequency of input high pass filter

acbandwidths = [0, 4, 9, 18, 37, 75, 151, 303, 607, 1214, 2428, 4857, 9714, 19428, 38856, 77712, 155424, 310849, 621698, 1243397, 2486795, 4973591, 9947183, 19894367, 39788735, 79577471, 159154943]
amplitude

amplitude of coherent modulation [volts]

bandwidth

Quadrature filter bandwidths [Hz].0 = off, negative bandwidth = highpass

bandwidths
frequency

frequency of iq demodulation [Hz]

gain

gain of the iq module (see drawing)

n = 26
na_trace(start=0, stop=100000.0, points=1001, rbw=100, avg=1.0, amplitude=0.1, input='adc1', output_direct='off', acbandwidth=0, sleeptimes=0.5, logscale=False, stabilize=None, maxamplitude=1.0)[source]
on

If set to False, turns off the module, e.g. to re-synchronize the phases

output_signal

Signal to send back to DSP multiplexer

output_signals = ['quadrature', 'output_direct', 'pfd', 'off', 'quadrature_hf']
pfd_integral

value of the pfd integral [volts]

pfd_on

**If True* – Turns on the PFD module, if False* – turns it off and resets integral

phase

Phase shift between modulation and demodulation [degrees]

quadrature_factor

amplification factor of demodulated signal [a.u.]

setup(**kwds)
Sets up an iq demodulator, refer to the drawing in the GUI for an explanation of the IQ layout. (just setting the attributes is OK).
input

selects the input signal of the module

acbandwidth

positive corner frequency of input high pass filter

frequency

frequency of iq demodulation [Hz]

bandwidth

Quadrature filter bandwidths [Hz].0 = off, negative bandwidth = highpass

quadrature_factor

amplification factor of demodulated signal [a.u.]

output_signal

Signal to send back to DSP multiplexer

gain

gain of the iq module (see drawing)

amplitude

amplitude of coherent modulation [volts]

phase

Phase shift between modulation and demodulation [degrees]

output_direct

selects to which analog output the module signal is sent directly

transfer_function(frequencies, extradelay=0)[source]

Returns a complex np.array containing the transfer function of the current IQ module setting for the given frequency array. The given transfer function is only relevant if the module is used as a bandpass filter, i.e. with the setting (gain != 0). If extradelay = 0, only the default delay is taken into account, i.e. the propagation delay from input to output_signal.

Parameters:
  • frequencies (np.array or float) – Frequencies to compute the transfer function for
  • extradelay (float) – External delay to add to the transfer function (in s). If zero, only the delay for internal propagation from input to output_signal is used. If the module is fed to analog inputs and outputs, an extra delay of the order of 200 ns must be passed as an argument for the correct delay modelisation.
Returns:

tf – The complex open loop transfer function of the module.

Return type:

np.array(.., dtype=np.complex)

class pyrpl.hardware_modules.iq.IqAcbandwidth(default=None, doc='', ignore_errors=False, call_setup=False)[source]

Bases: pyrpl.attributes.FilterProperty

descriptor for the acbandwidth of the Iq module

get_value(obj)[source]
set_value(instance, val)[source]
valid_frequencies(module)[source]
class pyrpl.hardware_modules.iq.IqGain(min=<MagicMock name='mock.inf.__neg__()' id='139827380281936'>, max=<MagicMock name='mock.inf' id='139827380218960'>, increment=0, log_increment=False, **kwargs)[source]

Bases: pyrpl.attributes.FloatProperty

descriptor for the gain of the Iq module

get_value(obj)[source]
set_value(obj, val)[source]

pyrpl.hardware_modules.pid module

We have already seen some use of the pid module above. There are three PID modules available: pid0 to pid2.

print r.pid0.help()

Proportional and integral gain

#make shortcut
pid = r.pid0

#turn off by setting gains to zero
pid.p,pid.i = 0,0
print("P/I gain when turned off:", pid.i,pid.p)
# small nonzero numbers set gain to minimum value - avoids rounding off to zero gain
pid.p = 1e-100
pid.i = 1e-100
print("Minimum proportional gain: ", pid.p)
print("Minimum integral unity-gain frequency [Hz]: ", pid.i)
# saturation at maximum values
pid.p = 1e100
pid.i = 1e100
print("Maximum proportional gain: ", pid.p)
print("Maximum integral unity-gain frequency [Hz]: ", pid.i)

Control with the integral value register

import numpy as np
#make shortcut
pid = r.pid0

# set input to asg1
pid.input = "asg1"

# set asg to constant 0.1 Volts
r.asg1.setup(waveform="dc", offset = 0.1)

# set scope ch1 to pid0
r.scope.input1 = 'pid0'

#turn off the gains for now
pid.p,pid.i = 0, 0

#set integral value to zero
pid.ival = 0

#prepare data recording
from time import time
times, ivals, outputs = [], [], []

# turn on integrator to whatever negative gain
pid.i = -10

# set integral value above the maximum positive voltage
pid.ival = 1.5

#take 1000 points - jitter of the ethernet delay will add a noise here but we dont care
for n in range(1000):
    times.append(time())
    ivals.append(pid.ival)
    outputs.append(r.scope.voltage_in1)

#plot
import matplotlib.pyplot as plt
%matplotlib inline
times = np.array(times)-min(times)
plt.plot(times,ivals,times,outputs)
plt.xlabel("Time [s]")
plt.ylabel("Voltage")

Again, what do we see? We set up the pid module with a constant (positive) input from the ASG. We then turned on the integrator (with negative gain), which will inevitably lead to a slow drift of the output towards negative voltages (blue trace). We had set the integral value above the positive saturation voltage, such that it takes longer until it reaches the negative saturation voltage. The output of the pid module is bound to saturate at +- 1 Volts, which is clearly visible in the green trace. The value of the integral is internally represented by a 32 bit number, so it can practically take arbitrarily large values compared to the 14 bit output. You can set it within the range from +4 to -4V, for example if you want to exloit the delay, or even if you want to compensate it with proportional gain.

Input filters

The pid module has one more feature: A bank of 4 input filters in series. These filters can be either off (bandwidth=0), lowpass (bandwidth positive) or highpass (bandwidth negative). The way these filters were implemented demands that the filter bandwidths can only take values that scale as the powers of 2.

# off by default
r.pid0.inputfilter
# minimum cutoff frequency is 1.1 Hz, maximum 3.1 MHz (for now)
r.pid0.inputfilter = [1,1e10,-1,-1e10]
print(r.pid0.inputfilter)
# not setting a coefficient turns that filter off
r.pid0.inputfilter = [0,4,8]
print(r.pid0.inputfilter)
# setting without list also works
r.pid0.inputfilter = -2000
print(r.pid0.inputfilter)
# turn off again
r.pid0.inputfilter = []
print(r.pid0.inputfilter)

You should now go back to the Scope and ASG example above and play around with the setting of these filters to convince yourself that they do what they are supposed to.

class pyrpl.hardware_modules.pid.IValAttribute(min=<MagicMock name='mock.inf.__neg__()' id='139827380281936'>, max=<MagicMock name='mock.inf' id='139827380218960'>, increment=0, log_increment=False, **kwargs)[source]

Bases: pyrpl.attributes.FloatProperty

Attribute for integrator value

get_value(obj)[source]
set_value(obj, value)[source]

set the value of the register holding the integrator’s sum [volts]

class pyrpl.hardware_modules.pid.Pid(rp, name)[source]

Bases: pyrpl.hardware_modules.filter.FilterModule

A proportional/Integrator/Differential filter.

The PID filter consists of a 4th order filter input stage, followed by a proportional and integral stage in parallel.

Warning

at the moment, the differential stage of PIDs is disabled.

Example:

from pyrpl import Pyrpl
pid = Pyrpl().rp.pid0

# set a second order low-pass filter with 100 Hz cutoff frequency
pid.inputfilter = [100, 100]
# set asg0 as input
pid.input = 'asg0'
# setpoint at -0.1
pid.setpoint = -0.1
# integral gain at 0.1
pid.i = 0.1
# proportional gain at 0.1
pid.p = 0.1
>>> print(pid.ival)
0.43545
>>> print(pid.ival)
0.763324

Setup Attributes:

  • setpoint: pid setpoint [volts]
  • p: pid proportional gain [1]
  • i: pid integral unity-gain frequency [Hz]
  • max_voltage: maximum output signal [volts]
  • min_voltage: minimum output signal [volts]
derivative
i

pid integral unity-gain frequency [Hz]

integral
ival

Current value of the integrator memory (i.e. pid output voltage offset)

max_voltage

maximum output signal [volts]

min_voltage

minimum output signal [volts]

p

pid proportional gain [1]

proportional
reg_integral
setpoint

pid setpoint [volts]

setup(**kwds)
sets up the pid (just setting the attributes is OK).
input

selects the input signal of the module

output_direct

selects to which analog output the module signal is sent directly

setpoint

pid setpoint [volts]

p

pid proportional gain [1]

i

pid integral unity-gain frequency [Hz]

inputfilter

Input filter bandwidths [Hz]. 0 = off, positive bandwidth <=> lowpass, negative bandwidth <=> highpass.

max_voltage

maximum output signal [volts]

min_voltage

minimum output signal [volts]

transfer_function(frequencies, extradelay=0)[source]

Returns a complex np.array containing the transfer function of the current PID module setting for the given frequency array. The settings for p, i, d and inputfilter, as well as delay are aken into account for the modelisation. There is a slight dependency of delay on the setting of inputfilter, i.e. about 2 extracycles per filter that is not set to 0, which is however taken into account.

Parameters:
  • frequencies (np.array or float) – Frequencies to compute the transfer function for
  • extradelay (float) – External delay to add to the transfer function (in s). If zero, only the delay for internal propagation from input to output_signal is used. If the module is fed to analog inputs and outputs, an extra delay of the order of 200 ns must be passed as an argument for the correct delay modelisation.
Returns:

tf – The complex open loop transfer function of the module.

Return type:

np.array(.., dtype=np.complex)

class pyrpl.hardware_modules.pid.SignalLauncherPid(module)[source]

Bases: pyrpl.modules.SignalLauncher

update_ival = <MagicMock name='mock.QtCore.Signal()' id='139827380610576'>

pyrpl.hardware_modules.pwm module

class pyrpl.hardware_modules.pwm.Pwm(rp, name=None)[source]

Bases: pyrpl.hardware_modules.dsp.DspModule

Auxiliary outputs. PWM0-3 correspond to pins 17-20 on E2 connector.

See http://wiki.redpitaya.com/index.php?title=Extension_connectors to find out where to connect your output device to the board. Outputs are 0-1.8V, but we will map this to -1 to 1 V internally to guarantee compatibility with other modules. So setting a pwm voltage to ‘-1V’ means you’ll measure 0V, setting it to ‘+1V’ you’ll find 1.8V.

Usage: pwm0 = AuxOutput(output=’pwm0’) pwm0.input = ‘pid0’ Pid(client, module=’pid0’).ival = 0 # -> outputs 0.9V on PWM0

Make sure you have an analog low-pass with cutoff of at most 1 kHz behind the output pin, and possibly an output buffer for proper performance. Only recommended for temperature control or other slow actuators. Big noise peaks are expected around 480 kHz.

Currently, only pwm1 and pwm2 are available.

Setup Attributes:

output_direct = None
output_directs = None
setup(**kwds)
Sets the module up for acquisition with the current setup attribute values.

pyrpl.hardware_modules.sampler module

class pyrpl.hardware_modules.sampler.Sampler(parent, name=None)[source]

Bases: pyrpl.modules.HardwareModule

this module provides a sample of each signal.

This is a momentary workaround, will be improved later on with an upgraded FPGA version

Setup Attributes:

addr_base = 1076887552
asg0

current value of asg0

asg1

current value of asg1

iir

current value of iir

in1

current value of in1

in2

current value of in2

iq0

current value of iq0

iq1

current value of iq1

iq2

current value of iq2

iq2_2

current value of iq2_2

mean_stddev(signal='in1', t=0.01)[source]

computes the mean and standard deviation of the chosen signal

Parameters:
  • signal (input signal) –
  • t (duration over which to average) –
  • obsolete
  • n (equivalent number of FPGA clock cycles to average over) –
Returns:

mean, stddev

Return type:

mean and standard deviation of all samples

off

current value of off

out1

current value of out1

out2

current value of out2

pid0

current value of pid0

pid1

current value of pid1

pid2

current value of pid2

setup(**kwds)
Sets the module up for acquisition with the current setup attribute values.
stats(signal='in1', t=0.01)[source]

computes the mean, standard deviation, min and max of the chosen signal over duration t

Parameters:
  • signal (input signal) –
  • t (duration over which to average) –
  • obsolete
  • n (equivalent number of FPGA clock cycles to average over) –
Returns:

mean, stddev, max, min

Return type:

mean and standard deviation of all samples

trig

current value of trig

pyrpl.hardware_modules.scope module

The scope works similar to the ASG but in reverse: Two channels are available. A table of \(2^{14}\) datapoints for each channel is filled with the time series of incoming data. Downloading a full trace takes about 10 ms over standard ethernet. The rate at which the memory is filled is the sampling rate (125 MHz) divided by the value of ‘decimation’. The property ‘average’ decides whether each datapoint is a single sample or the average of all samples over the decimation interval.

s = r.scope # shortcut
print("Available decimation factors:", s.decimations)
print("Trigger sources:", s.trigger_sources)
print("Available inputs: ", s.inputs)

Let’s have a look at a signal generated by asg1. Later we will use convenience functions to reduce the amount of code necessary to set up the scope:

asg = r.asg1
s = r.scope

# turn off asg so the scope has a chance to measure its "off-state" as well
asg.output_direct = "off"

# setup scope
s.input1 = 'asg1'

# pass asg signal through pid0 with a simple integrator - just for fun (detailed explanations for pid will follow)
r.pid0.input = 'asg1'
r.pid0.ival = 0 # reset the integrator to zero
r.pid0.i = 1000 # unity gain frequency of 1000 hz
r.pid0.p = 1.0 # proportional gain of 1.0
r.pid0.inputfilter = [0,0,0,0] # leave input filter disabled for now

# show pid output on channel2
s.input2 = 'pid0'

# trig at zero volt crossing
s.threshold_ch1 = 0

# positive/negative slope is detected by waiting for input to
# sweep through hysteresis around the trigger threshold in
# the right direction
s.hysteresis_ch1 = 0.01

# trigger on the input signal positive slope
s.trigger_source = 'ch1_positive_edge'

# take data symetrically around the trigger event
s.trigger_delay = 0

# set decimation factor to 64 -> full scope trace is 8ns * 2^14 * decimation = 8.3 ms long
s.decimation = 64

# launch a single (asynchronous) curve acquisition, the asynchronous
# acquisition means that the function returns immediately, eventhough the
# data-acquisition is still going on.
res = s.curve_async()

print("Before turning on asg:")
print("Curve ready:", s.curve_ready()) # trigger should still be armed

# turn on asg and leave enough time for the scope to record the data
asg.setup(frequency=1e3, amplitude=0.3, start_phase=90, waveform='halframp', trigger_source='immediately')
sleep(0.010)

# check that the trigger has been disarmed
print("After turning on asg:")
print("Curve ready:", s.curve_ready())
print("Trigger event age [ms]:",8e-9*((
s.current_timestamp&0xFFFFFFFFFFFFFFFF) - s.trigger_timestamp)*1000)

# The function curve_async returns a *future* (or promise) of the curve. To
# access the actual curve, use result()
ch1, ch2 = res.result()

# plot the data
%matplotlib inline
plt.plot(s.times*1e3, ch1, s.times*1e3, ch2)
plt.xlabel("Time [ms]")
plt.ylabel("Voltage")

What do we see? The blue trace for channel 1 shows just the output signal of the asg. The time=0 corresponds to the trigger event. One can see that the trigger was not activated by the constant signal of 0 at the beginning, since it did not cross the hysteresis interval. One can also see a ‘bug’: After setting up the asg, it outputs the first value of its data table until its waveform output is triggered. For the halframp signal, as it is implemented in pyrpl, this is the maximally negative value. However, we passed the argument start_phase=90 to the asg.setup function, which shifts the first point by a quarter period. Can you guess what happens when we set start_phase=180? You should try it out!

In green, we see the same signal, filtered through the pid module. The nonzero proportional gain leads to instant jumps along with the asg signal. The integrator is responsible for the constant decrease rate at the beginning, and the low-pass that smoothens the asg waveform a little. One can also foresee that, if we are not paying attention, too large an integrator gain will quickly saturate the outputs.

# useful functions for scope diagnostics
print("Curve ready:", s.curve_ready())
print("Trigger source:",s.trigger_source)
print("Trigger threshold [V]:",s.threshold_ch1)
print("Averaging:",s.average)
print("Trigger delay [s]:",s.trigger_delay)
print("Trace duration [s]: ",s.duration)
print("Trigger hysteresis [V]", s.hysteresis_ch1)
print("Current scope time [cycles]:",hex(s.current_timestamp))
print("Trigger time [cycles]:",hex(s.trigger_timestamp))
print("Current voltage on channel 1 [V]:", r.scope.voltage_in1)
print("First point in data buffer 1 [V]:", s.ch1_firstpoint)
class pyrpl.hardware_modules.scope.ContinuousRollingFuture(module)[source]

Bases: pyrpl.async_utils.PyrplFuture

This Future object is the one controlling the acquisition in rolling_mode. It will never be fullfilled (done), since rolling_mode is always continuous, but the timer/slot mechanism to control the rolling_mode acquisition is encapsulated in this object.

DELAY_ROLLING_MODE_MS = 20
current_avg = 1
pause()[source]
start()[source]
class pyrpl.hardware_modules.scope.DecimationRegister(address, bitmask=None, options={}, **kwargs)[source]

Bases: pyrpl.attributes.SelectRegister

Careful: changing decimation changes duration and sampling_time as well

set_value(obj, value)[source]
class pyrpl.hardware_modules.scope.DurationProperty(options=[], **kwargs)[source]

Bases: pyrpl.attributes.SelectProperty

get_value(obj)[source]
set_value(obj, value)[source]

sets returns the duration of a full scope sequence the rounding makes sure that the actual value is longer or equal to the set value

validate_and_normalize(obj, value)[source]
class pyrpl.hardware_modules.scope.SamplingTimeProperty(options=[], **kwargs)[source]

Bases: pyrpl.attributes.SelectProperty

get_value(obj)[source]
set_value(instance, value)[source]

sets or returns the time separation between two subsequent points of a scope trace the rounding makes sure that the actual value is shorter or equal to the set value

validate_and_normalize(obj, value)[source]
class pyrpl.hardware_modules.scope.Scope(parent, name=None)[source]

Bases: pyrpl.modules.HardwareModule, pyrpl.acquisition_module.AcquisitionModule

Setup Attributes:

  • input1: selects the input signal of the module
  • input2: selects the input signal of the module
  • duration:
  • average: Enables averaging during decimation if set to True
  • trigger_source: Trigger source for the scope. Use ‘immediately’ if no synchronisation is required. Trigger_source will be ignored in rolling_mode.

Options: [‘off’, ‘immediately’, ‘ch1_positive_edge’, ‘ch1_negative_edge’, ‘ch2_positive_edge’, ‘ch2_negative_edge’, ‘ext_positive_edge’, ‘ext_negative_edge’, ‘asg0’, ‘asg1’, ‘dsp’] - trigger_delay: delay between trigger and acquisition start. negative values down to -duration are allowed for pretrigger. In trigger_source=’immediately’, trigger_delay is ignored. - threshold: trigger threshold [volts] - hysteresis: hysteresis for trigger [volts] - ch1_active: should ch1 be displayed in the gui? - ch2_active: should ch2 be displayed in the gui? - xy_mode: in xy-mode, data are plotted vs the other channel (instead of time) - rolling_mode: In rolling mode, the curve is continuously acquired and translated from the right to the left of the screen while new data arrive.

addr_base = 1074790400
average

Enables averaging during decimation if set to True

ch1_active

should ch1 be displayed in the gui?

ch1_firstpoint

1 sample of ch1 data [volts]

ch2_active

should ch2 be displayed in the gui?

ch2_firstpoint

1 sample of ch2 data [volts]

current_timestamp

An absolute counter for the time [cycles]

curve_ready()[source]

Returns True if new data is ready for transfer

data_length = 16384
data_x
dec = 65536
decimation

decimation factor

decimations = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536]
duration
durations = [0.000131072, 0.000262144, 0.000524288, 0.001048576, 0.002097152, 0.004194304, 0.008388608, 0.016777216, 0.033554432, 0.067108864, 0.134217728, 0.268435456, 0.536870912, 1.073741824, 2.147483648, 4.294967296, 8.589934592]
hysteresis

hysteresis for trigger [volts]

hysteresis_ch1
hysteresis_ch2
input1

selects the input signal of the module

input2

selects the input signal of the module

inputs
name = 'scope'
pretrig_ok

True if enough data have been acquired to fill the pretrig buffer

rolling_mode

In rolling mode, the curve is continuously acquired and translated from the right to the left of the screen while new data arrive.

sampling_time
sampling_times = [8e-09, 1.6e-08, 3.2e-08, 6.4e-08, 1.28e-07, 2.56e-07, 5.12e-07, 1.024e-06, 2.048e-06, 4.096e-06, 8.192e-06, 1.6384e-05, 3.2768e-05, 6.5536e-05, 0.000131072, 0.000262144, 0.000524288]
save_curve()[source]

Saves the curve(s) that is (are) currently displayed in the gui in the db_system. Also, returns the list [curve_ch1, curve_ch2]...

setup(**kwds)
trace_average

number of curves to average in single mode. In continuous mode, a moving window average is performed.

curve_name

name of the curve to save.

input1

selects the input signal of the module

input2

selects the input signal of the module

duration
average

Enables averaging during decimation if set to True

trigger_source

Trigger source for the scope. Use ‘immediately’ if no synchronisation is required. Trigger_source will be ignored in rolling_mode.

Options: [‘off’, ‘immediately’, ‘ch1_positive_edge’, ‘ch1_negative_edge’, ‘ch2_positive_edge’, ‘ch2_negative_edge’, ‘ext_positive_edge’, ‘ext_negative_edge’, ‘asg0’, ‘asg1’, ‘dsp’]

trigger_delay: delay between trigger and acquisition start.
negative values down to -duration are allowed for pretrigger. In trigger_source=’immediately’, trigger_delay is ignored.
threshold: trigger threshold [volts] hysteresis: hysteresis for trigger [volts] ch1_active: should ch1 be displayed in the gui? ch2_active: should ch2 be displayed in the gui? xy_mode: in xy-mode, data are plotted vs the other channel (instead of time) rolling_mode: In rolling mode, the curve is continuously acquired and translated from the right to the left of the screen while new data arrive. running_state: Indicates whether the instrument is running acquisitions or not. See RunningStateProperty for available options.
st = 0.000524288
threshold

trigger threshold [volts]

threshold_ch1
threshold_ch2
times
trigger_debounce

Trigger debounce time [s]

trigger_delay

delay between trigger and acquisition start. negative values down to -duration are allowed for pretrigger. In trigger_source=’immediately’, trigger_delay is ignored.

trigger_source

Trigger source for the scope. Use ‘immediately’ if no synchronisation is required. Trigger_source will be ignored in rolling_mode. Options: [‘off’, ‘immediately’, ‘ch1_positive_edge’, ‘ch1_negative_edge’, ‘ch2_positive_edge’, ‘ch2_negative_edge’, ‘ext_positive_edge’, ‘ext_negative_edge’, ‘asg0’, ‘asg1’, ‘dsp’]

trigger_sources = ['off', 'immediately', 'ch1_positive_edge', 'ch1_negative_edge', 'ch2_positive_edge', 'ch2_negative_edge', 'ext_positive_edge', 'ext_negative_edge', 'asg0', 'asg1', 'dsp']
trigger_timestamp

An absolute counter for the trigger time [cycles]

voltage_in1

in1 current value [volts]

voltage_in2

in2 current value [volts]

voltage_out1

out1 current value [volts]

voltage_out2

out2 current value [volts]

wait_for_pretrigger()[source]

sleeps until scope trigger is ready (buffer has enough new data)

xy_mode

in xy-mode, data are plotted vs the other channel (instead of time)

pyrpl.hardware_modules.trig module

class pyrpl.hardware_modules.trig.Trig(rp, name)[source]

Bases: pyrpl.hardware_modules.filter.FilterModule

The trigger module implements a full-rate trigger on a DSP signal.

The trigger can be used to assert whether its input signal remains within pre-specified bounds or to record the phase of asg0 at the moment when the trigger was triggered. This makes it comparable in performance to an IQ module.

We plan to enable usage of the trigger module as additional trigger input for the scope, thereby enabling the recording of arbitrary data while triggering on a signal that is not necessarily the trigger source.

Setup Attributes:

  • output_signal: Signal to use as module output
  • trigger_source: Trigger source
  • threshold: trigger threshold [volts]
  • hysteresis: hysteresis for ch1 trigger [volts]
  • phase_offset: offset to add to the output phase (before taking absolute value)
  • auto_rearm: Automatically re-arm trigger?
  • phase_abs: Output the absolute value of the phase
armed

Set to True to arm trigger

auto_rearm

Automatically re-arm trigger?

current_timestamp

An absolute counter for the time [cycles]

hysteresis

hysteresis for ch1 trigger [volts]

output_signal

Signal to use as module output

output_signal_to_phase(v)[source]

Converts the output signal value from volts to degrees.

This is useful when Trig.output_signal is set to a phase and the phase is to be retrieved from a sampled output value.

The conversion is based on the following correspondence: \(0\,\mathrm{V} = 0\deg,\, -1\,\mathrm{V} = 180\deg,\, 1\,\mathrm{V} = 180\deg - \epsilon\,.\)

Parameters:v (float) – The output signal value in Volts.
Returns:The phase in degrees corresponding to the argument value.
Return type:float
output_signals = ['TTL', 'asg0_phase']
phase_abs

Output the absolute value of the phase

phase_offset

offset to add to the output phase (before taking absolute value)

setup(**kwds)

sets up the module (just setting the attributes is OK). .. attribute:: input

selects the input signal of the module
output_direct

selects to which analog output the module signal is sent directly

output_signal

Signal to use as module output

trigger_source

Trigger source

threshold

trigger threshold [volts]

hysteresis

hysteresis for ch1 trigger [volts]

phase_offset

offset to add to the output phase (before taking absolute value)

auto_rearm

Automatically re-arm trigger?

phase_abs

Output the absolute value of the phase

threshold

trigger threshold [volts]

trigger_source

Trigger source

trigger_sources = ['both_edge', 'neg_edge', 'off', 'pos_edge']
trigger_timestamp

An absolute counter for the trigger time [cycles]

Module contents

All modules are extensively discussed in the Tutorial. Please refer to there for more information.