FlexEye testing: running FlexEye child processess
This Python example demonstates using FlexEye independent acquisition. The example works with FlexDCA offline and installs simulated modules. No hardware is required. For this example, two programs are used. One program is used to launch another program as a child process. The child process performs a measurement on a specific channels. To work, both programs must be located in the same folder. From the sidebar shown in this topic, you must save these two files:
- FlexEye_sessions.py (parent process)
- FlexEye_sessions_child.py (child process)
The child process, FlexEye_sessions_child.py, is run on five FlexEye sessions using Python's standard subprocess library.
The child process acquires 10000 waveforms and performs a risetime measurement on the resulting waveforms. Because of the very large number of acquisitions, the data takes long enough for the parent process to illustrate the different FlexEye states.
Parent Script (FlexEYE_sessions.py)
The arguments passed to subprocess.Popen()
are passed as a list. When you run this program, you will see the entire list (converted to a string) that is passed to each call to subprocess.Popen()
. The examine_FlexEye() function allows the user to make selections while the five child processes run and after they complete. This illustrates FlexEye in pause mode where the sessions can be viewed. When FlexEye is paused, notice that the acquisition tests stop and then resume when FlexEye is returned to run mode.
FlexEye_sessions.py
# -*- coding: utf-8 -*-
""" Launches subprocess: FlexEye_sessions_child.py
Demonstrates FlexEye independent eye acquisition.
As the child processes are running, the script prompts you
to make selections that show:
- The "sessions" dictionary, that is near the top of the script,
defines the FlexEye sessions to be created. Dictionary keys represent
FlexEye session numbers and the dictionary values represents
valid channels to measure for the session. The assignments show
the variety of channel combinations allowed for sessions.
- Each session will run as independent oscilloscope.
- Child processes can be paused and restarted.
This script uses two simulated modules. The child processes
do nothing but take a lot of time so that you can view different
tabs and pause FlexEye. No data is returned from the child processes.
The parent script prints VISA address and channels tested for each
session (child process). """
import subprocess
import sys
import pyvisa as visa # import VISA library
IOTIMEOUT = 60000 # 60 seconds
ADDRESS = 'TCPIP0::localhost::hislip0,4880::INSTR'
SLOT5 = '5'
SLOT6 = '6'
# FlexEye channel assignments for sessions
sessions = {1: 'D5A',
2: '5C,6A',
3: '5D',
4: '6B',
5: '6C,6D',
6: '', 7: '', 8: '', 9: '', 10: '', 11: '', 12: '', 13: '', 14: '', 15: ''}
def open_flexdca_connection(address):
""" Opens visa connection to FlexFlexDCA. """
print('Connecting to Flexdca ...')
try:
rm = visa.ResourceManager()
connection = rm.open_resource(address)
connection.timeout = 20000 # Set connection timeout to 20s
connection.read_termination = '\n'
connection.write_termination = '\n'
inst_id = connection.query('*IDN?')
print('\nFlexDCA connection established to:\n' + inst_id, flush=True)
except (visa.VisaIOError, visa.InvalidSession):
print('\nVISA ERROR: Cannot open instrument address.\n', flush=True)
return None
except Exception as other:
print('\nVISA ERROR: Cannot connect to instrument:', other, flush=True)
print('\n')
return None
return connection
def initialize_FlexDCA(flexdca):
""" Place FlexDCA in a know starting state.
Checks to see if FlexEye is already running. If so exit FlexEye. Perform
a default setup.
"""
flexeye_state = flexdca.query(':FEYE:STATe?')
if 'RUN' in flexeye_state:
flexdca.query(':FEYE:STATe PAUSe;*OPC?')
flexdca.query(':FEYE:STATe STOP;*OPC?')
elif 'PAUSe' in flexeye_state:
flexdca.query(':FEYE:STATe STOP;*OPC?')
flexdca.write('*CLS')
flexdca.query(':SYSTem:DEFault;*OPC?')
flexdca.query(':SYSTem:MODE EYE;*OPC?')
def install_simulated_module(flexdca, slot):
""" Installs a simulated module in the first available slot.
Second argument selects type of module based on arguments to
:EMODules:SLOT:SELection command. If argument is missing, a
quad-electrical module is installed. Returns a slot number (string). '0'
if no available slots.
"""
flexdca.write(':EMODules:SLOT'+slot+':SELection QEM')
for c in ['A', 'B', 'C', 'D']:
channel = slot + c
flexdca.write(':SOURce'+channel+':FORMat NRZ')
flexdca.write(':SOURce'+channel+':DRATe 9.95328e9')
flexdca.write(':SOURce'+channel+':WTYPe DATA')
flexdca.write(':SOURce'+channel+':PLENgth 127')
# 90 mV amplitude
flexdca.write(':SOURce'+channel+':AMPLitude 90E-3')
# Add 3 uV random noise
flexdca.write(':SOURce'+channel+':NOISe:RN 3.0E-6')
# Add 4 ps random jitter
flexdca.write(':SOURce'+channel+':JITTer:RJ 4.0E-12')
print('Simulated module installed in slot ' + slot, flush=True)
return slot
def all_channels_off(flexdca):
""" Turns all available channels off. """
for slot in '12345678':
for letter in 'ABCD':
channel = slot + letter
flexdca.write(':CHANnel' + channel + ':DISPlay OFF')
def configure_FlexEye(flexdca, sessions):
""" Configures each FlexEye session to the state defined in the
sessions dictionary. Then, turns FlexEye on. This populates FlexDCA's
FlexEye Streaming Setup dialog.
"""
print('Configuring FlexEye. Please wait...', flush=True)
for session in sessions: # iterate through 15 session keys
# create iterable list of channels from comma separated string
channels = sessions[session].split(',')
if channels[0]: # if channels list has channels
for ch in channels: # run through channel tupple
if ch[0] in 'D': # diff channel, eg, D1A
channel = ch[1:3] # eg, 1A
flexdca.write(':SOURce'+channel+':DIFFerential ON')
flexdca.query(':DIFF'+channel+':DMODe ON;*OPC?')
flexdca.write(':DIFF'+channel+':DISPlay ON')
flexdca.write(':FEYE:CHANnel' + channel +
':SID SESSion' + str(session))
flexdca.write(':FEYE:DIFF' + channel + ':ENABled ON')
else: # configure channel, eg, 1A
flexdca.write(':CHANnel' + ch + ':DISPlay ON')
flexdca.write(':FEYE:CHANnel' + ch + ':SID SESSion' +
str(session))
flexdca.write(':FEYE:CHANnel' + ch + ':ENABled ON')
flexdca.query(':FEYE:STATe RUN;*OPC?')
print('Sessions are configured.', flush=True)
sys.stdout.flush() # Flush print message in stdout
def run_child_processes(primary_address, sessions):
""" Starts the FlexEye child process for each session. Creates
the session VISA address based on the address of the primary
process. Passes the address and list of session channels as
arguments to the child process.
"""
print('Starting child processes.')
child_processes = [] # list of child processes
command = ['python', 'FlexEye_sessions_child.py', '', '']
for session in sessions: # iterate through 15 session keys
channels = sessions[session] # comma separated string of channels
if channels: # if valid session with channels
command[2] = primary_address.replace('hislip0',
'hislip' + str(session))
command[3] = channels
p = subprocess.Popen(command,
stdout=subprocess.PIPE,
universal_newlines=True)
child_processes.append(p)
print('\nCommand arguments for Session ' + str(session) + ':')
print(' VISA address: ' + command[2])
print(' Channels: ' + command[3])
print('\nChild processes are running.', flush=True)
return
def examine_FlexEye(flexdca):
""" Pauses FlexEye to allow user to click on Session tabs. User exits
FlexEye when ready.
"""
enabled_sessions = ['1', '2', '3', '4', '5']
flexdca.write(':FEYE:VIEW ' + 'SESSion' + enabled_sessions[0])
prompt = 'FlexEye is RUNNING! You can view Sessions ' \
+ 'in progress.\nSelect an action: '
while True:
print('-' * 50)
print('"Enter" key toggles PAUSE/RUN modes.')
print('Enter "q" exits FlexEye.')
print('Enter number to select session:')
print(' 0. Primary')
# print session selections for user
i = 1
for session in enabled_sessions:
print(' ' + str(i) + '. Session ' + session)
i += 1
choice = input(prompt)
if (choice == 'q' or choice == 'Q'):
state = flexdca.query(':FEYE:STATe?')
if 'PAUS' in state:
flexdca.query(':FEYE:STATe RUN;*OPC?')
flexdca.write(':FEYE:VIEW PRIMary')
flexdca.query(':FEYE:STATe STOP;*OPC?')
print('Script Finished.')
return
elif choice == '':
prompt = toggle_FlexEye_Mode(flexdca, enabled_sessions)
elif choice == '0':
flexdca.write(':FEYE:VIEW PRIMary')
prompt = 'Select an action: '
else:
try:
n = int(choice) - 1
flexdca.write(':FEYE:VIEW ' + 'SESSion' + enabled_sessions[n])
prompt = 'Select an action: '
except:
prompt = 'Incorrect entry. Select an action: '
continue
def toggle_FlexEye_Mode(flexdca, enabled_sessions):
if 'RUN' in flexdca.query(':FEYE:STATe?'):
flexdca.query(':FEYE:STATe PAUSe;*OPC?')
flexdca.write(':FEYE:VIEW ' + enabled_sessions[0])
return 'FlexEye is PAUSED!\nSelect an action: '
else:
flexdca.query(':FEYE:STATe RUN;*OPC?')
flexdca.write(':FEYE:VIEW ' + enabled_sessions[0])
return 'FlexEye is RUNNING! You can view Sessions \
in progress.\nSelect an action to end FlexEye: '
FlexDCA = open_flexdca_connection(ADDRESS)
FlexDCA.timeout = IOTIMEOUT
initialize_FlexDCA(FlexDCA)
install_simulated_module(FlexDCA, SLOT5)
install_simulated_module(FlexDCA, SLOT6)
all_channels_off(FlexDCA)
configure_FlexEye(FlexDCA, sessions)
run_child_processes(ADDRESS, sessions)
examine_FlexEye(FlexDCA)
FlexDCA.write(':SYSTem:GTLocal')
FlexDCA.close()
Child Script (FlexEye_sessions_child.py)
FlexEye_sessions_child.py
# -*- coding: utf-8 -*-
""" Subprocess: FlexEye_sessions_child.py launched by FlexEye_sessions.py
This program is designed to be a sub-process that is run by
FlexEye_sessions.py as an example of FlexEye independent acquisitions.
This program performs a Rise Time measurement on an eye diagram.
sys.argv[1] is the session's VISA address command-line argument
sys.argv[2] is a list of channels. """
import sys
import pyvisa as visa
IOTIMEOUT = 120000 # Set connection timeout to 2 minutes.
def initialize_setup(FlexDCA, channels):
""" Places session in known state.
Turns on and autoscales session channels.
"""
FlexDCA.query(':SYSTem:DEFault;*OPC?')
for ch in channels:
if 'D' in ch[0]: # differential channel. eg, D1A
s = ch[1:3]
FlexDCA.write(':CHAN'+s+':DISPlay OFF')
# turn on differential channel
FlexDCA.query(':DIFF'+s+':DMODe ON;*OPC?')
FlexDCA.write(':DIFF'+s+':DISPlay ON')
else: # configure channel
FlexDCA.write(':CHAN'+ch+':DISPlay ON')
FlexDCA.query(':SYSTem:AUToscale;*OPC?')
def acquisition_limit_test(FlexDCA):
""" A limit acquisition test ensures that enough waveform
data is available to start measuring the eye. Captures
10000 waveforms which will take long enough to show
advantage of running parallel processes.
"""
FlexDCA.write(':ACQuire:STOP')
FlexDCA.write(':ACQuire:CDISplay')
FlexDCA.write(':MEASure:EYE:LIST:CLEar')
FlexDCA.write(':LTESt:ACQuire:CTYPe:WAVeforms 100')
FlexDCA.write(':LTESt:ACQuire:STATe ON')
FlexDCA.query(':ACQuire:RUN;*OPC?')
FlexDCA.write(':LTESt:ACQuire:STATe OFF')
def make_measurements(FlexDCA, channels):
""" Measure each channel's risetime. """
for ch in channels: # run through list of channels
if 'D' in ch[0]: # differential channel. eg, D1A
s = ch[1:3] # eg, 1A
FlexDCA.write(':MEASure:EYE:RISetime:SOURce1 DIFF' + s)
else: # non-diff channel
FlexDCA.write(':MEASure:EYE:RISetime:SOURce1 CHAN' + ch)
FlexDCA.write(':MEASure:EYE:RISetime')
address = sys.argv[1] # VISA address command-line argument
s = sys.argv[2] # comma separated string of channels
channels = s.split(',') # a list of channels
rm = visa.ResourceManager()
FlexDCA = rm.open_resource(address)
FlexDCA.timeout = IOTIMEOUT
FlexDCA.read_termination = '\n'
FlexDCA.write_termination = '\n'
initialize_setup(FlexDCA, channels)
acquisition_limit_test(FlexDCA)
make_measurements(FlexDCA, channels)
FlexDCA.write(':SYSTem:GTLocal')
FlexDCA.close() # Close VISA session