Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
C
Conv TTL Blocking
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
5
Issues
5
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
image/svg+xml
Discourse
Discourse
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Projects
Conv TTL Blocking
Commits
389c01b4
Commit
389c01b4
authored
Feb 02, 2017
by
Projects
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Script to configure pulse burst for Agilent 33250A
parent
a7f5f242
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
264 additions
and
0 deletions
+264
-0
pulse_gen.py
software/utils/pulse_gen.py
+264
-0
No files found.
software/utils/pulse_gen.py
0 → 100755
View file @
389c01b4
#!/usr/bin/env python
#===============================================================================
# CERN (BE-CO-HT)
# Agilent 33250A pulse generator controller
#===============================================================================
# author: Maciej Suminski (maciej.suminski@cern.ch)
# date of creation: 02.02.2017
# version: 1.0
#
# description:
# This script is used to configure an Agilent 33250A signal generator
# to generate an arbitrary burst of pulses via serial port interface.
#
# Be sure that both the generator and the serial port have the same settings:
# - **RTS/CTS handshake enabled**
# - the same baud rate (recommended 115200)
# - parity bit disabled
# - 1 bit start, 1 bit stop,
#
#===============================================================================
# GNU LESSER GENERAL PUBLIC LICENSE
#===============================================================================
# This source file is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version. This source is distributed in the hope that it
# will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details. You should have
# received a copy of the GNU Lesser General Public License along with this
# source; if not, download it from http://www.gnu.org/licenses/lgpl-2.1.html
#===============================================================================
# last changes:
#===============================================================================
# TODO: -
#===============================================================================
import
sys
import
serial
import
getopt
import
math
# default parameters
device
=
'/dev/ttyUSB0'
baud
=
115200
count
=
2000000
freq
=
2e6
# Hz
width
=
250e-9
# s
samples_count
=
500
# number of samples for arbitrary waveform
print
(
'Agilent 33250A pulse burst generator
\r\n
'
)
def
usage
():
print
(
'(c) CERN 2017'
)
print
(
'Maciej Suminski <maciej.suminski@cern.ch>'
)
print
(
''
)
print
(
'usage:
%
s [opts]'
%
sys
.
argv
[
0
])
print
(
'Options:'
)
print
(
'-b|--baud= serial port baud rate (default:
%
d)'
%
baud
)
print
(
'-d|--device= serial port device path (default:
%
s'
%
device
)
print
(
''
)
print
(
'-c|--count= number of requested pulses (default:
%
d)'
%
count
)
print
(
'-f|--freq= frequency of the pulses [Hz] (default:
%
G)'
%
freq
)
print
(
'-w|--width= pulse width [s] (default:
%
G)'
%
width
)
print
(
'-s|--samples= arbitrary waveform samples count (default:
%
d)'
%
samples_count
)
print
(
''
)
print
(
'-h|--help shows this information'
)
# generator limits
# max number of pulses in a single burst, higher than that is done using arbitrary waveform
MAX_COUNT
=
1000000
try
:
opts
,
args
=
getopt
.
getopt
(
sys
.
argv
[
1
:],
'b:c:d:f:hw:'
,
[
'baud='
,
'count='
,
'device='
,
'freq='
,
'help'
,
'width='
])
except
getopt
.
GetoptError
as
err
:
print
(
str
(
err
))
usage
()
sys
.
exit
(
2
)
for
opt
,
arg
in
opts
:
if
opt
in
(
'-b'
,
'--baud'
):
baud
=
int
(
arg
)
elif
opt
in
(
'-c'
,
'--count'
):
count
=
int
(
arg
)
elif
opt
in
(
'-d'
,
'--device'
):
device
=
arg
elif
opt
in
(
'-f'
,
'--freq'
):
freq
=
float
(
arg
)
elif
opt
in
(
'-s'
,
'--samples'
):
samples
=
int
(
arg
)
if
samples_count
>
65536
:
print
(
'ERROR: Samples number limit is 65536'
)
sys
.
exit
(
2
)
elif
opt
in
(
'-w'
,
'--width'
):
width
=
float
(
arg
)
elif
opt
in
(
'-h'
,
'--help'
):
usage
()
sys
.
exit
()
else
:
assert
False
,
'unhandled option'
###############################################################################
# Connect to the generator and reset it
try
:
print
(
'Connecting to
%
s @
%
s'
%
(
device
,
baud
))
ser
=
serial
.
Serial
(
device
,
baud
,
timeout
=
1
,
rtscts
=
1
)
except
serial
.
serialutil
.
SerialException
as
err
:
print
(
str
(
err
))
sys
.
exit
(
2
)
# flush buffer that may contain an invalid command
ser
.
write
(
b
'
\r\n
'
)
# verify the device is there
ser
.
write
(
b
'*IDN?
\r\n
'
)
ident
=
ser
.
readline
()
.
decode
()
if
(
ident
==
''
):
print
(
'ERROR: Could not identify the generator'
)
sys
.
exit
(
2
)
print
(
'Read device ID:
%
s'
%
ident
)
if
not
'33250A'
in
ident
:
print
(
'ERROR: Invalid generator type'
)
sys
.
exit
(
2
)
# reset to a known state
ser
.
write
(
b
'OUTP OFF
\r\n
'
)
ser
.
write
(
b
'*RST
\r\n
'
)
# clear errors
while
True
:
ser
.
write
(
b
'SYST:ERR?
\r\n
'
)
status
=
ser
.
readline
()
.
decode
()
if
status
==
''
:
print
(
'ERROR: Could not read the error queue'
)
sys
.
exit
(
2
)
if
status
.
startswith
(
'+0'
):
break
###############################################################################
# Select the way of generating pulses
# 33250A has a limit of 1e6 pulses in a single burst. To overcome this
# limitation, one can create an arbitrary waveform containing n pulses
# and then request n times pulses less.
if
(
count
<=
MAX_COUNT
):
# normal case, just use the pulse generator, no tricks needed
div
=
1
real_count
=
count
# rising/falling edge time
edge
=
5e-9
print
(
'Configuring pulse waveform'
)
ser
.
write
(
b
'PULS:TRAN
%
G
\r\n
'
%
edge
)
ser
.
write
(
b
'PULS:WIDT
%
G
\r\n
'
%
width
)
ser
.
write
(
b
'PULS:PER
%
G
\r\n
'
%
(
1.0
/
freq
))
ser
.
write
(
b
'FUNC:SHAP PULS
\r\n
'
)
else
:
orig_count
=
count
orig_width
=
width
# deal with the limitation described above
# number of pulses in the arbitrary waveform
div
=
int
(
math
.
floor
(
count
/
MAX_COUNT
))
+
1
period
=
1.0
/
freq
if
width
>
period
*
0.9
:
print
(
'ERROR: Pulse width is greater than 90
%
of the pulse period'
)
sys
.
exit
(
2
)
# generate the string that represents the requested waveform
duty_cycle
=
width
/
period
one_count
=
int
(
samples_count
*
duty_cycle
)
zero_count
=
samples_count
-
one_count
# generator DAC values mapping
# see VOLT and VOLT:OFFS commands below to see the details
one_value
=
b
', 2047'
zero_value
=
b
', -2047'
# single pulse (take into account the duty cycle)
chunk
=
b
'
%
s
%
s'
%
(
zero_count
*
zero_value
,
one_count
*
one_value
)
# full waveform (repeat the pulse 'div' times)
pattern
=
chunk
*
div
# update all variables basing on the divider
freq
=
freq
/
div
count
=
int
(
count
/
div
)
width
=
duty_cycle
*
period
# the actual number of pulses is rounded to the number of pulses in
# the arbitrary waveform
real_count
=
count
*
div
if
width
!=
orig_width
:
print
(
'WARNING: Pulse width had to be rounded to
%
G (requested
%
G)'
%
(
width
,
orig_width
))
if
real_count
!=
orig_count
:
print
(
'WARNING: Number of pulses had to be rounded to
%
d (requested
%
d)'
%
(
real_count
,
orig_count
))
# set the arbitrary waveform (a number of consecutive pulses)
print
(
'Configuring arbitrary waveform'
)
ser
.
write
(
b
'FREQ
%
G
\r\n
'
%
freq
)
ser
.
write
(
b
'DATA:DAC VOLATILE
%
s
\r\n
'
%
pattern
)
# set the arbitrary waveform to the one we have just uploaded
ser
.
write
(
b
'FUNC:USER VOLATILE
\r\n
'
)
# switch the generator to arbitrary waveform
ser
.
write
(
b
'FUNC USER
\r\n
'
)
###############################################################################
# Common part
# peak-to-peak voltage 5V, offset = 2.5V
# makes arbitrary waveform values mapping:
# -2047 => 0V
# 2047 => +5V
ser
.
write
(
b
'VOLT 5
\r\n
'
)
ser
.
write
(
b
'VOLT:OFFS 2.5
\r\n
'
)
# configure software trigger (executed by *TRG command)
ser
.
write
(
b
'TRIG:SOUR BUS
\r\n
'
)
# configure burst mode (number of cycles, software triggered, 0 phase)
ser
.
write
(
b
'BURS:MODE TRIG
\r\n
'
)
ser
.
write
(
b
'BURS:NCYC
%
d
\r\n
'
%
count
)
ser
.
write
(
b
'BURS:PHAS 0
\r\n
'
)
ser
.
write
(
b
'BURS:STAT ON
\r\n
'
)
# check errors queue
error
=
False
while
True
:
ser
.
write
(
b
'SYST:ERR?
\r\n
'
)
status
=
ser
.
readline
()
.
decode
()
if
status
.
startswith
(
'+0'
):
# no more errors
break
print
(
'ERROR: Generator error occurred:
%
s'
%
status
)
error
=
True
if
error
:
sys
.
exit
(
2
)
# we should not proceed, the results are unknown
print
(
'Sending
%
d pulses (
%
g s) at
%
g Hz'
%
(
real_count
,
width
,
freq
*
div
))
# enable output for load = 50 ohms
ser
.
write
(
b
'OUTP:LOAD 50
\r\n
'
)
ser
.
write
(
b
'OUTP ON
\r\n
'
)
# trigger the output
ser
.
write
(
b
'*TRG
\r\n
'
)
ser
.
write
(
b
'*WAI
\r\n
'
)
ser
.
write
(
b
'OUTP OFF
\r\n
'
)
ser
.
close
()
print
(
'Finished successfully'
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment