June 12, 2021 Test Equipment

Rigol DG1022 - How to create waveforms with python

Another useful python script

Rigol DG1022 - How to create waveforms with python

Have you ever tried using Rigol's Ultrawave software for the DG1022 function generator? Well... How can I put it mildly? It sucks.

CAUTION! Mental health hazzard. Consult your doctor before using this software.

The DG1022 is a great waveform generator though. It has served me well for many years. I recently needed it to output some custom waveforms. One option would have been to write the waveforms in txt files and load them with a USB stick. Rigol's formatting is really simple. But moving the stick back and forth didn't seem like a good idea.

So I decided to write a Python script that saves waveforms to the non-volatile memory of the DG1022. Since I have no idea what kind of non-volatile memory it uses and in order not to stress it with multiple writes I also included a function that writes the waveform directly to the volatile memory and outputs it on channel 1.

A sample waveform plotted from the script

The script

# ******************************************************************************
# RIGOL DG1022 - Mathematical function loader
# this script lets you load waveforms writen in python to the DG1022
# more details here:
# https://btbm.ch/rigol-dg1022-how-to-create-waveforms-with-python
# 2021 Grease monkey - No rights reserved
# !!! Don't laugh at the code. I change engine oils for a living !!!
# ******************************************************************************
################################################################################
# Packages
################################################################################
import pyvisa
import time
import math
import matplotlib.pyplot as plt
################################################################################
# Functions
################################################################################
# Example of a function that requires 2 parameters
def log_10(x):
return math.log(x, 10)
def create_data_points(func, x_start, x_end, number_of_steps):
"""
This function calculates and scales to [-1,1] the Y points of the mathematical function func in the domain
[x_start, x_end]. It also plots the results.
:param func: The mathematical function to be transferred. Single parameter functions only (e.g. math.cos).
The output of the function must be float.
:type func: str
:param x_start: The first element of the domain that x belongs. Must be less than x_end
:type x_start: float
:param x_end: The last element of the domain that x belongs. Must be greater than x_start
:type x_end: float
:param number_of_steps: The number of points on the X axis
:type number_of_steps: int
:return: The data points separated by commas (e.g. ,-1.0,-0.999,0.123)
:rtype: str
"""
output_string = ''
y = []
for i in range(number_of_steps):
x = x_start + i * (x_end - x_start) / number_of_steps
y.append(func(x))
# Y scaling
y_max = max(y)
y_min = min(y)
y_delta = y_max - y_min
y_l_scaled = []
for i in y:
y_scaled = (i - y_min) / (y_delta / 2) # Scale Y to [0,2]
y_scaled -= 1.0 # Scale Y to [-1,1]
output_string += ',' + str(round(y_scaled, 6)) # Round to 6 digits
y_l_scaled.append(y_scaled) # append to list
# plot function
plt.plot(y_l_scaled)
plt.ylabel(func.__name__ + '(x) (scaled), [' + str(x_start) + ',' + str(x_end) + ']')
plt.show()
return output_string
def dg_send(message):
"""
Sends a command to the RIGOL DG1022.
It also calculates the time needed for the message to be transmitted.
Time estimates may be over-conservative.
The function does not return anything.
:param message: The command to be sent
:type message: str
"""
DG1022.write(message)
delay = 0.001 * len(message)
if delay < 0.2:
delay = 0.2
print(message)
time.sleep(delay)
def store_to_arb(points, user_function_name):
"""
Saves the points to the user memory of the DG1022 under the name user_function_name.
The function does not return anything.
:param points: the data points separated by commas (e.g. ,-1.0,-0.999,0.123)
:type points: str
:param user_function_name: The name of the waveform as it will be stored in the non-volatile memory of
the DG1022. 12 characters max A-Z, a-z, 0-9 and _ first char must be a letter.
:type user_function_name: str
"""
dg_send('DATA:DEL ' + user_function_name) # First delete any stored waveform with the same name
# Delete the volatile memory (probably not needed)
dg_send('DATA:DEL VOLATILE')
# Load the data to the volatile memory
dg_send('DATA VOLATILE' + points)
# Copy the VOLATILE memory to the USER memory
dg_send('DATA:COPY ' + user_function_name)
def load_to_channel1_and_start(frequency, volts_low, volts_high, points):
"""
:param frequency: The frequency in Hertz
:type frequency: float
:param volts_low: The lowest voltage of VPP
:type volts_low: float
:param volts_high: The highest voltage of VPP
:type volts_high: float
:param points: the data points separated by commas (e.g. ,-1.0,-0.999,0.123)
:type points: str
"""
dg_send('OUTP OFF') # Switch CH1 off
dg_send('FUNC USER') # Select user function
dg_send('FREQ ' + str(frequency)) # Set the frequency
dg_send('VOLT:UNIT VPP') # Set VPP mode
dg_send('VOLT:HIGH ' + str(volts_high)) # Set the max value for VPP
dg_send('VOLT:LOW ' + str(volts_low)) # Set the min value for VPP
dg_send('DATA VOLATILE' + points) # Send the arb function to the VOLATILE memory
dg_send('FUNC:USER VOLATILE') # Load the volatile memory
dg_send('OUTP ON') # Turn CH1 on
# Start pyvisa and print the resources found
rm = pyvisa.ResourceManager()
print(rm.list_resources())
# Replace 'USB0::0x0400::0x09C4::DG1D162150321::INSTR' with your instrument
DG1022 = rm.open_resource('USB0::0x0400::0x09C4::DG1D162150321::INSTR')
dg_send('*IDN?')
print(DG1022.read())
# Example 1
# Create the data points for sin(x) xE[0,π]
data_points = create_data_points(func=math.sin, x_start=0, x_end=math.pi * 2, number_of_steps=1000)
# Store the data points to C1 (overwrite)
store_to_arb(data_points, 'C1')
# Example 2
# Create the data points for log(x) (base 10) xE[1,1000]
data_points = create_data_points(func=math.cos, x_start=1, x_end=math.pi * 2, number_of_steps=100)
# Store the data points
store_to_arb(data_points, 'C2')
# Example 3
# Create the data points for acos(x) xE[-1,1]
data_points = create_data_points(func=math.acos, x_start=-1.0, x_end=1.0, number_of_steps=1000)
load_to_channel1_and_start(frequency=100000, volts_low=-1, volts_high=1, points=data_points)
rm.close()
exit()