DAC API Streaming Tutorial

This tutorial demonstrates how to stream waveform data to Red Pitaya DAC outputs using the streaming client library. You’ll learn how to generate custom waveforms, configure playback parameters, and efficiently stream signals using the API.


Overview

This example shows a complete implementation of DAC streaming with:

  • Custom waveform generation (sine wave example)

  • Configurable DAC sample rate (up to 125 MS/s)

  • WAV file format support for waveform data

  • Repeat mode configuration (finite or infinite)

  • Real-time streaming monitoring via callbacks

  • Automatic playback control and synchronization

Complete source code: View stream_dac_1.py on GitHub →


Prerequisites

Hardware:

  • Red Pitaya device (any model)

../../../_images/STEMlab125-14V1-0.svg

Software:

  • Python 3.8 or newer / C++ compiler

  • Red Pitaya streaming library (included in the Streaming command line client)

  • Python: NumPy and SciPy libraries: pip install numpy scipy

  • Streaming application running on Red Pitaya (see Quick Start)


Key Concepts

Architecture

The example uses a callback-based architecture for monitoring DAC streaming:

Your Computer                           Red Pitaya
+---------------------+                +------------------+
| DACStreamClient     |    TCP/IP      | Streaming Server |
|   |                 |--------------->|   |              |
|   +--> connect()    |                |   +--> FPGA DAC  |
|   +--> sendConfig() |                |                  |
|   +--> startStream()|                |   Deep Memory    |
|                     |                |   Buffer (4 MB)  |
| Callback Class      |                +------------------+
|   +--> sentPack()   |---> Monitors data sent
|         +--> Count  |
+---------------------+

Note

The code examples below are simplified for clarity. For complete, production-ready code with full error handling, see the complete example on GitHub.

DAC Rate Configuration

Red Pitaya DAC runs at a base rate of 125 MS/s. You can configure the exact sample rate:

\[\text{DAC Rate} \leq 125\text{ MS/s}\]

Common configurations:

DAC Rate

Samples/Second

Use Case

125 MS/s

125,000,000

Maximum bandwidth

62.5 MS/s

62,500,000

High-speed signals

15.625 MS/s

15,625,000

Audio-range signals

1 MS/s

1,000,000

Low-frequency control


Implementation Walkthrough

Step 1: Generate Custom Waveform

First, create the waveform data you want to output. This example generates a sine wave and saves it as a WAV file:

import numpy as np
from scipy.io.wavfile import write

# Waveform parameters
waveform_frequency = 1          # 1 Hz sine wave
waveform_samples = 1024 * 4     # 4096 samples
wav_filename = "sin.wav"

# Generate sine wave
t = np.linspace(0., 1., waveform_samples)
amplitude = np.iinfo(np.int16).max  # Maximum int16 value
data = amplitude * np.sin(2. * np.pi * waveform_frequency * t)

# Save as WAV file (16-bit PCM format)
write(wav_filename, waveform_samples, data.astype(np.int16))
print(f"Waveform saved to '{wav_filename}'")

Key points:

  • WAV format uses 16-bit signed integers (-32768 to 32767) - gets converted to 14-bit DAC voltage internally (lowest 2 bits ignored on most boards)

  • Sample rate in WAV header doesn’t affect DAC rate (set separately in config)

  • Waveform repeats seamlessly if size fits in memory


Step 2: Create Callback Handler

Implement a callback class to monitor streaming events:

import streaming

class Callback(streaming.DACCallback):
    def __init__(self):
        streaming.DACCallback.__init__(self)
        self.counter = 0  # Count packets sent

    def sentPack(self, client, ch1_size, ch2_size):
        """Called when data packet is sent to DAC"""
        self.counter += 1

    def connected(self, client, host):
        print(f"DAC client connected: {host}")

    def error(self, client, host, code):
        print(f"Error on {host}, code: {code}")
        client.notifyStop()

    def stoppedFileEnd(self, client, host):
        print(f"Playback finished: {host}")
        client.notifyStop()

Callback methods:

  • sentPack() - Called when data packet is sent to DAC

  • connected() - Client connected to server

  • error() - Error occurred during streaming

  • stopped*() - Various stop conditions (file end, memory error, etc.)


Step 3: Initialize Streaming Client

Create the client and register your callback:

# Create client and callback
client = streaming.DACStreamClient()
callback = Callback()

# Register callback (transfers ownership to client)
client.setCallbackFunction(callback.__disown__())

# Optional: Enable verbose logging
client.setVerbose(True)

Step 4: Connect to Red Pitaya

Establish TCP/IP connection to the streaming server:

if not client.connect():
    print("ERROR: Failed to connect to DAC streaming server")
    exit(1)

print("Connected successfully")

Before connecting, ensure:

  1. Red Pitaya is powered on and network-accessible

  2. Streaming FPGA image is loaded: overlay.sh stream_app

  3. Streaming server is running: streaming-server


Step 5: Configure DAC Parameters

Set the DAC rate, memory allocation, and playback mode:

dac_rate = 125000000        # 125 MS/s
block_size = 16384          # Network packet size
dac_memory = 1638400        # Memory for DAC buffer

# Configure DAC streaming
client.sendConfig('dac_pass_mode', 'NET')
client.sendConfig('dac_rate', f'{dac_rate}')
client.sendConfig('block_size', f'{block_size}')
client.sendConfig('dac_size', f'{dac_memory}')

# Configure repeat mode
client.setRepeatInf(False)      # False = finite repeat
client.setRepeatCount(2)        # Repeat 2 times

Configuration parameters:

  • dac_pass_mode - Set to 'NET' for network streaming (vs. SD card file)

  • dac_rate - DAC sampling rate in Hz (max 125000000)

  • block_size - Network packet size in bytes. For long waveforms, larger sizes improve efficiency (8192-65536 for short waveforms, 1048576-2097152 for long waveforms).

  • dac_size - Reserved memory for DAC buffer in bytes

  • setRepeatInf() - True for continuous playback, False for finite

  • setRepeatCount() - Number of times to repeat waveform


Step 6: Start Streaming

Stream the WAV file to the DAC:

# Start streaming from WAV file
if not client.startStreamingWAV("./sin.wav"):
    print("ERROR: Failed to start DAC streaming")
    exit(1)

print("Streaming started - outputting waveform to DAC")

# Wait for streaming to complete
client.wait()

print(f"Total packets sent: {callback.counter}")

Streaming methods:

  • startStreamingWAV(filename) - Stream waveform from WAV file

  • wait() - Block until streaming completes

  • notifyStop() - Manually stop streaming (called from callback)


Complete Example Code

Full working example combining all steps:

import streaming
import numpy as np
from scipy.io.wavfile import write

# Callback class
class Callback(streaming.DACCallback):
    def __init__(self):
        streaming.DACCallback.__init__(self)
        self.counter = 0

    def sentPack(self, client, ch1_size, ch2_size):
        self.counter += 1

    def connected(self, client, host):
        print(f"Connected: {host}")

    def stoppedFileEnd(self, client, host):
        print("Playback finished")
        client.notifyStop()

# Generate waveform
samples = 1024 * 4
t = np.linspace(0., 1., samples)
data = 32767 * np.sin(2. * np.pi * t)
write("sin.wav", samples, data.astype(np.int16))

# Create client
client = streaming.DACStreamClient()
callback = Callback()
client.setCallbackFunction(callback.__disown__())

# Connect and configure
if not client.connect():
    exit(1)

client.sendConfig('dac_pass_mode', 'NET')
client.sendConfig('dac_rate', '125000000')
client.setRepeatCount(2)

# Start streaming
if not client.startStreamingWAV("./sin.wav"):
    exit(1)

client.wait()
print(f"Packets sent: {callback.counter}")

Common Issues and Solutions

Streaming Doesn’t Start

Symptoms:

  • connect() returns False

  • No waveform on oscilloscope

Solutions:

  1. Verify streaming server is running on Red Pitaya:

    redpitaya> ps | grep streaming-server
    
  2. Check FPGA image is loaded:

    redpitaya> overlay.sh stream_app
    
  3. Verify network connection and IP address


Waveform Clipped or Distorted

Symptoms:

  • Output signal clipped at peaks

  • Unexpected waveform shape

Solutions:

  1. Check waveform amplitude (max ±32767 for int16)

  2. Verify DAC gain settings (SIGNALlab only): channel_gain_1 and channel_gain_2

  3. Ensure waveform data is correctly formatted as 16-bit signed integers

  4. Calibrate the Red Pitaya board using the calibration app


Memory Allocation Errors

Symptoms:

  • stoppedMemError() callback triggered

  • Streaming stops unexpectedly

Solutions:

  1. Increase dac_size parameter to accommodate waveform

  2. Check available DMM memory through the System info or DMM check reserved memory section

  3. Increase Red Pitaya DDR allocation for DMM if necessary

  4. Reduce waveform size or use repeat mode for large signals


Next Steps

Now that you understand basic DAC streaming:

  • Custom waveforms - Generate complex arbitrary signals

  • Dual-channel - Output different signals on CH1 and CH2

  • Synchronized ADC+DAC - Stimulus-response measurements

  • Real-time modulation - Update waveforms during playback


Complete Source Code

View the complete, production-ready implementation: stream_dac_1.py on GitHub →

The GitHub version includes:

  • Full error handling and validation

  • Detailed inline comments

  • Additional callback handlers

  • Configuration examples