|
@@ -0,0 +1,140 @@
|
|
|
+import serial
|
|
|
+import operator
|
|
|
+import logging
|
|
|
+import struct
|
|
|
+import math
|
|
|
+import argparse
|
|
|
+
|
|
|
+
|
|
|
+log = logging.getLogger(__name__)
|
|
|
+
|
|
|
+
|
|
|
+def checksum(msg):
|
|
|
+ return reduce(operator.xor, (ord(x) for x in msg))
|
|
|
+
|
|
|
+
|
|
|
+class Generator(object):
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ self.conn = serial.Serial(
|
|
|
+ port='/dev/ttyUSB0',
|
|
|
+ baudrate=57600,
|
|
|
+ bytesize=serial.EIGHTBITS,
|
|
|
+ parity=serial.PARITY_NONE,
|
|
|
+ stopbits=serial.STOPBITS_ONE,
|
|
|
+ timeout=1,
|
|
|
+ )
|
|
|
+
|
|
|
+ def send_command(self, cmd):
|
|
|
+ if len(cmd) != 5:
|
|
|
+ raise ValueError("cmd must be five characters")
|
|
|
+
|
|
|
+ def fmt(msg):
|
|
|
+ return ' '.join(hex(ord(x)) for x in msg)
|
|
|
+
|
|
|
+ message = 'X{}{}'.format(cmd, chr(checksum(cmd)))
|
|
|
+ log.debug("send {}".format(fmt(message)))
|
|
|
+
|
|
|
+ self.conn.write(message)
|
|
|
+ result = self.conn.read(7)
|
|
|
+ log.debug("recv {}".format(fmt(result)))
|
|
|
+
|
|
|
+ if checksum(result[1:6]) != ord(result[6]):
|
|
|
+ raise IOError("Wrong checksum")
|
|
|
+
|
|
|
+ return result[2:5]
|
|
|
+
|
|
|
+ def set_remote(self, enable):
|
|
|
+ self.send_command('R{}xxx'.format(chr(int(not enable))))
|
|
|
+ r = self.send_command('rxxxx')
|
|
|
+
|
|
|
+ @property
|
|
|
+ def remote_enabled(self):
|
|
|
+ r = self.send_command('rxxxx')
|
|
|
+ return r[0] == '0'
|
|
|
+
|
|
|
+ @property
|
|
|
+ def frequency(self):
|
|
|
+ r = self.send_command('fxxxx')
|
|
|
+ hi, lo, exp = struct.unpack('BBB', r)
|
|
|
+ return ((hi << 8) | lo) / 1000.0 * 10**exp
|
|
|
+
|
|
|
+ @frequency.setter
|
|
|
+ def frequency(self, freq):
|
|
|
+ exp = int(math.log(freq, 10))
|
|
|
+ mantissa = int(round(freq / math.exp(exp * math.log(10)))) * 1000
|
|
|
+ hi, lo = mantissa >> 8, mantissa & 0xff
|
|
|
+ self.send_command('F{}{}{}{}'.format(chr(hi), chr(lo), chr(exp), chr(0xff)))
|
|
|
+
|
|
|
+ @property
|
|
|
+ def amplitude(self):
|
|
|
+ r = self.send_command('axxxx')
|
|
|
+ hi, lo = struct.unpack('BB', r[:2])
|
|
|
+ return ((hi << 8) | lo) / 1000.0
|
|
|
+
|
|
|
+ @amplitude.setter
|
|
|
+ def amplitude(self, amp):
|
|
|
+ val = int(amp * 1000)
|
|
|
+ hi, lo = val >> 8, val & 0xff
|
|
|
+ self.send_command('A{}{}x{}'.format(chr(hi), chr(lo), chr(0xff)))
|
|
|
+
|
|
|
+ @property
|
|
|
+ def offset(self):
|
|
|
+ r = self.send_command('oxxxx')
|
|
|
+ hi, lo = struct.unpack('BB', r[:2])
|
|
|
+ return ((hi << 8) | lo) / 1000.0
|
|
|
+
|
|
|
+ @offset.setter
|
|
|
+ def offset(self, off):
|
|
|
+ val = int(off * 1000)
|
|
|
+ hi, lo = val >> 8, val & 0xff
|
|
|
+ self.send_command('O{}{}x{}'.format(chr(hi), chr(lo), chr(0xff)))
|
|
|
+
|
|
|
+ @property
|
|
|
+ def dutycycle(self):
|
|
|
+ r = self.send_command('dxxxx')
|
|
|
+ return ord(r[0])
|
|
|
+
|
|
|
+ @dutycycle.setter
|
|
|
+ def dutycycle(self, cycle):
|
|
|
+ if cycle < 0 or cycle > 100:
|
|
|
+ raise ValueError("cycle must be between 0 and 100")
|
|
|
+
|
|
|
+ self.send_command('D{}xx{}'.format(chr(cycle), chr(0xff)))
|
|
|
+
|
|
|
+
|
|
|
+def main():
|
|
|
+ logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(message)s")
|
|
|
+ g = Generator()
|
|
|
+
|
|
|
+ parser = argparse.ArgumentParser()
|
|
|
+ parser.add_argument('-f', '--frequency', type=float, default=None)
|
|
|
+ parser.add_argument('-a', '--amplitude', type=float, default=None)
|
|
|
+ parser.add_argument('-o', '--offset', type=float, default=None)
|
|
|
+ parser.add_argument('-d', '--dutycycle', type=float, default=None)
|
|
|
+ args = parser.parse_args()
|
|
|
+
|
|
|
+ g.set_remote(True)
|
|
|
+
|
|
|
+ if args.frequency:
|
|
|
+ g.frequency = args.frequency
|
|
|
+
|
|
|
+ if args.amplitude:
|
|
|
+ g.amplitude = args.amplitude
|
|
|
+
|
|
|
+ if args.offset:
|
|
|
+ g.offset = args.offset
|
|
|
+
|
|
|
+ if args.dutycycle:
|
|
|
+ g.dutycycle = argsycle
|
|
|
+
|
|
|
+ print("Frequency: {} Hz".format(g.frequency))
|
|
|
+ print("Amplitude: {} V".format(g.amplitude))
|
|
|
+ print("Offset: {} V".format(g.offset))
|
|
|
+ print("Duty cycle: {} %".format(g.dutycycle))
|
|
|
+
|
|
|
+ g.set_remote(False)
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ main()
|