pyrpl.hardware_modules.ams.
AMS
(parent, name=None)[source]¶Bases: pyrpl.modules.HardwareModule
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)
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
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
pyrpl.hardware_modules.asg.
WaveformAttribute
(options=[], **kwargs)[source]¶Bases: pyrpl.attributes.SelectProperty
default
= 'sin'¶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 objectAn 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.
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
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.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.
HK
(parent, name=None)[source]¶Bases: pyrpl.modules.HardwareModule
Setup Attributes:
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
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.
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.
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.
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)
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
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]
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: |
|
---|---|
Returns: | tf – The complex open loop transfer function of the module. |
Return type: | np.array(.., dtype=np.complex) |
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
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
We have already seen some use of the pid module above. There are three PID modules available: pid0 to pid2.
print r.pid0.help()
#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)
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.
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.
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
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:
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: |
|
---|---|
Returns: | tf – The complex open loop transfer function of the module. |
Return type: | np.array(.., dtype=np.complex) |
pyrpl.hardware_modules.pid.
SignalLauncherPid
(module)[source]¶Bases: pyrpl.modules.SignalLauncher
update_ival
= <MagicMock name='mock.QtCore.Signal()' id='139827380610576'>¶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.
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: |
|
---|---|
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: |
|
---|---|
Returns: | mean, stddev, max, min |
Return type: | mean and standard deviation of all samples |
trig
¶current value of trig
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)
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¶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
pyrpl.hardware_modules.scope.
DurationProperty
(options=[], **kwargs)[source]¶pyrpl.hardware_modules.scope.
SamplingTimeProperty
(options=[], **kwargs)[source]¶pyrpl.hardware_modules.scope.
Scope
(parent, name=None)[source]¶Bases: pyrpl.modules.HardwareModule
, pyrpl.acquisition_module.AcquisitionModule
Setup Attributes:
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]
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.
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]
xy_mode
¶in xy-mode, data are plotted vs the other channel (instead of time)
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:
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]
All modules are extensively discussed in the Tutorial. Please refer to there for more information.