blob: e7e20cf05fab4df8062a631e4cb699d344fd09f6 [file] [log] [blame]
# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import glob
import serial
import sys
import time
from function_generator import FunctionGenerator
class HP33120A(FunctionGenerator):
""" This class supplies a FunctionGenerator object that handles communication
with the HP33120A function generator.
The HP33120A uses standard SCPI serial communication over RS232 8N2.
Protocol Manual:
http://sdphca.ucsd.edu/Lab_Equip_Manuals/Agilent_33120a_Users.pdf
"""
RESPONSE_DELAY_S = 0.5
WAVEFORM_CODES = {FunctionGenerator.SQUARE_WAVE: 'SQU',
FunctionGenerator.SIN_WAVE: 'SIN'}
def __init__(self, device_path=None):
# Connect to the device either by scanning the ttyUSB* devices or using a
# specifically supplied path.
possible_paths = [device_path] if device_path else glob.glob('/dev/ttyUSB*')
self.serial_connection = self._ProbeSerialConnection(possible_paths)
if not self.serial_connection:
print 'ERROR: Unable to detect function generator!'
def __del__(self):
self.Off()
if self.serial_connection:
self.serial_connection.close()
def IsValid(self):
return self.serial_connection is not None
def Off(self):
""" Turn off the function generator's output. """
self._SendMessage('APPL:DC 1, 1, 0')
def GenerateFunction(self, form, frequency, amplitude):
""" Configure the function that the function generator is outputting. """
if form not in HP33120A.WAVEFORM_CODES:
print 'ERROR: Invalid waveform "%s"!' % form
# Put the generator in "remote mode" so it accepts our commands
self._SendMessage('SYST:REM')
# Always set it to Hi-Z mode so we don't accidentally double our voltages.
# The function generator will otherwise assume there is a 50 Ohm load on the
# line, and match it internally effectively making a voltage divider of 1/2.
# Therefore it will compensate by outputting 2x the voltage you expect.
# In Hi-Z mode this doesn't happen, which is what we want since there is no
# 50 Ohm load to divide the voltage back down.
self._SendMessage('OUTP:LOAD INF')
# Convert the generic waveforms into this generator's code for them
form_code = HP33120A.WAVEFORM_CODES[form]
# Configure the output function
cmd = 'APPL:%s %E, %d' % (form_code, int(frequency), int(amplitude))
self._SendMessage(cmd)
def _ProbeSerialConnection(self, possible_paths):
""" Find which path in possible_paths points to the serial port that the
function generator is on. This is done by opening a serial connection with
each tty device with the configuration parameters for the HP33120A function
generator, and probing to determine if it's the correct one. This
particular function generator responds with "HEWLETT-PACKARD" when you send
it "*IDN?" If the device doesn't repond that way, we know we have the wrong
device.
This function returns a serial connection object for the correct device, or
None if no function generator was found.
"""
for device_path in possible_paths:
ser = serial.Serial(
port=device_path,
baudrate=9600,
stopbits=serial.STOPBITS_TWO,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
timeout=1
)
if 'HEWLETT-PACKARD' in self._SendMessage_Aux('*IDN?', ser):
return ser
else:
ser.close()
return None
def _SendMessage(self, msg):
""" Convenience function to automatically send messages to the already
connected serial port.
"""
return self._SendMessage_Aux(msg, self.serial_connection)
def _SendMessage_Aux(self, msg, serial_connection):
""" Send a message to a function generator at the specified serial
connection and wait for its response (if there is one). This function
either returns the response string or None.
"""
if not serial_connection:
return None
# Newlines are required to terminate each message
serial_connection.write(msg + '\n')
if '?' in msg:
# The function generator only responds to commands with a '?' If this
# message has one, we have to wait for the response and return it.
time.sleep(0.1)
return serial_connection.readline()
else:
# Otherwise, we need to delay for a moment to allow the function generator
# to execute our command.
time.sleep(HP33120A.RESPONSE_DELAY_S)
return None