📘 Documentation and Examples for Jupyter Notebooks:
📁 Jupyter Notebooks file:
When you have opened the Jupyter notebook app, drag the .ipynb file into the window. Alternatively open the file in a new tab and save it with the suffix .ipynb using CTRL+S.
🧠 Code Explanation
- Importing libraries
time for delays. numpy for numerical work. matplotlib.pyplot for plotting. rp_overlay for FPGA overlay support. rp for Red Pitaya control.
import time import numpy as np from matplotlib import pyplot as plt from rp_overlay import overlay import rp
- Initialization
Loads the FPGA overlay and initializes the Red Pitaya API so generation and acquisition calls are available.
# Initialize Red Pitaya overlay fpga = overlay() rp.rp_Init()
- Constants for the experiment
Defines sweep start, step and max frequency, output amplitude, acquisition length, decimation and derived sampling rate. Stores the first measured gain as reference for −3 dB detection.
start_freq = 100 # Hz freq_step = 100 # Hz increment max_freq = 5000 # Hz ampl = 1 # V N = 16384 # samples initial_gain = None dec = rp.RP_DEC_128 sampling_rate = 125e6 / dec
- Acquisition settings
Sets trigger level and delay, and selects AWG positive edge as the trigger source for synchronous capture.
trig_lvl = 0.5 trig_dly = 0 acq_trig_sour = rp.RP_TRIG_SRC_AWG_PE
- Signal generation
Resets generator and acquisition, configures a sine on CH1 at the current sweep frequency and amplitude, enables output, and arms a software trigger.
rp.rp_GenReset() rp.rp_AcqReset() print(f"Generating {freq} Hz signal") rp.rp_GenWaveform(rp.RP_CH_1, rp.RP_WAVEFORM_SINE) rp.rp_GenFreqDirect(rp.RP_CH_1, freq) rp.rp_GenAmp(rp.RP_CH_1, ampl) rp.rp_GenOutEnable(rp.RP_CH_1) rp.rp_GenTriggerOnly(rp.RP_CH_1)
- Signal acquisition
Applies decimation, trigger level and delay. Sets CH2 gain to HV range for headroom. Starts acquisition and sets the trigger source.
rp.rp_AcqSetDecimation(dec) rp.rp_AcqSetTriggerLevel(rp.RP_T_CH_1, trig_lvl) rp.rp_AcqSetTriggerDelay(trig_dly) rp.rp_AcqSetGain(rp.RP_CH_2, rp.RP_HIGH) rp.rp_AcqStart() rp.rp_AcqSetTriggerSrc(acq_trig_sour)
- Wait and trigger
Gives the acquisition engine time to arm, then issues a generation trigger so the capture can start reliably.
time.sleep(1) rp.rp_GenTriggerOnly(rp.RP_CH_1)
- Trigger and buffer fill wait
Spins until the trigger occurs and the buffer is fully populated, ensuring complete frames are retrieved.
# Wait for acquisition trigger while rp.rp_AcqGetTriggerState()[1] != rp.RP_TRIG_STATE_TRIGGERED: pass # Wait until buffer is full while not rp.rp_AcqGetBufferFillState()[1]: pass
- Data retrieval
Reads oldest samples from both channels and converts them to numpy arrays for analysis and plotting.
fbuff_ch1 = rp.fBuffer(N) rp.rp_AcqGetOldestDataV(rp.RP_CH_1, N, fbuff_ch1) fbuff_ch2 = rp.fBuffer(N) rp.rp_AcqGetOldestDataV(rp.RP_CH_2, N, fbuff_ch2) # Convert the buffer data to numpy arrays data_V_ch1 = np.array([fbuff_ch1[i] for i in range(N)]) data_V_ch2 = np.array([fbuff_ch2[i] for i in range(N)])
- Phase analysis at 100 Hz
Optional FFT-based phase measurement at a known good frequency to confirm circuit orientation and reference behavior.
if freq == 100: # Example: phase check at low frequency (FFT-based) # Compute phase difference to detect inverting vs non-inverting behavior pass
- Gain calculation and reference
Computes Vin and Vout peak‑to‑peak amplitudes and gain in dB. Stores first gain as reference for −3 dB comparison.
input_amplitude = np.max(data_V_ch1) - np.min(data_V_ch1) output_amplitude = np.max(data_V_ch2) - np.min(data_V_ch2) gain = 20 * np.log10(output_amplitude / input_amplitude) if initial_gain is None: initial_gain = gain
- −3 dB point detection
Checks for a 3 dB drop from the initial gain to find the bandwidth limit, then captures plots and exits the sweep.
if gain <= initial_gain - 3: # Reached −3 dB point: plot and break the sweep pass
- Plotting
Visualizes Vin and Vout for the current frequency to validate measurement stability and clipping headroom.
plt.figure(figsize=(10, 5)) plt.plot(data_V_ch1, label='Input Signal (Vin)') plt.plot(data_V_ch2, label='Output Signal (Vout)') plt.title(f'Signals at {freq} Hz') plt.xlabel('Sample Number') plt.ylabel('Voltage (V)') plt.legend() plt.grid(True) plt.show()
- Cleanup
Releases Red Pitaya resources to leave the device in a clean state.
rp.rp_Release()
Full Code
import time import numpy as np from matplotlib import pyplot as plt from rp_overlay import overlay import rp # Initialize Red Pitaya overlay fpga = overlay() rp.rp_Init() start_freq = 100 # Hz freq_step = 100 # Hz increment max_freq = 5000 # Hz ampl = 1 # V N = 16384 # samples initial_gain = None dec = rp.RP_DEC_128 sampling_rate = 125e6 / dec trig_lvl = 0.5 trig_dly = 0 acq_trig_sour = rp.RP_TRIG_SRC_AWG_PE rp.rp_GenReset() rp.rp_AcqReset() print(f"Generating {freq} Hz signal") rp.rp_GenWaveform(rp.RP_CH_1, rp.RP_WAVEFORM_SINE) rp.rp_GenFreqDirect(rp.RP_CH_1, freq) rp.rp_GenAmp(rp.RP_CH_1, ampl) rp.rp_GenOutEnable(rp.RP_CH_1) rp.rp_GenTriggerOnly(rp.RP_CH_1) rp.rp_AcqSetDecimation(dec) rp.rp_AcqSetTriggerLevel(rp.RP_T_CH_1, trig_lvl) rp.rp_AcqSetTriggerDelay(trig_dly) rp.rp_AcqSetGain(rp.RP_CH_2, rp.RP_HIGH) rp.rp_AcqStart() rp.rp_AcqSetTriggerSrc(acq_trig_sour) time.sleep(1) rp.rp_GenTriggerOnly(rp.RP_CH_1) # Wait for acquisition trigger while rp.rp_AcqGetTriggerState()[1] != rp.RP_TRIG_STATE_TRIGGERED: pass # Wait until buffer is full while not rp.rp_AcqGetBufferFillState()[1]: pass fbuff_ch1 = rp.fBuffer(N) rp.rp_AcqGetOldestDataV(rp.RP_CH_1, N, fbuff_ch1) fbuff_ch2 = rp.fBuffer(N) rp.rp_AcqGetOldestDataV(rp.RP_CH_2, N, fbuff_ch2) # Convert the buffer data to numpy arrays data_V_ch1 = np.array([fbuff_ch1[i] for i in range(N)]) data_V_ch2 = np.array([fbuff_ch2[i] for i in range(N)]) if freq == 100: # Example: phase check at low frequency (FFT-based) # Compute phase difference to detect inverting vs non-inverting behavior pass input_amplitude = np.max(data_V_ch1) - np.min(data_V_ch1) output_amplitude = np.max(data_V_ch2) - np.min(data_V_ch2) gain = 20 * np.log10(output_amplitude / input_amplitude) if initial_gain is None: initial_gain = gain if gain <= initial_gain - 3: # Reached −3 dB point: plot and break the sweep pass plt.figure(figsize=(10, 5)) plt.plot(data_V_ch1, label='Input Signal (Vin)') plt.plot(data_V_ch2, label='Output Signal (Vout)') plt.title(f'Signals at {freq} Hz') plt.xlabel('Sample Number') plt.ylabel('Voltage (V)') plt.legend() plt.grid(True) plt.show() rp.rp_Release()