Skip to content
Snippets Groups Projects
Commit 66ceffa8 authored by Michael DM Dryden's avatar Michael DM Dryden
Browse files

Merge branch 'hotfix/extra-data'

parents 00c2072f 73293c5a
Branches
Tags
No related merge requests found
Showing
with 151 additions and 55 deletions
......@@ -4,6 +4,9 @@ Breakpoints_v2.xcbkptlist
*.c
*.so
*.pyc
*.pyo
*~
/dstatInterface/dist/
/dstatInterface/build/
\ No newline at end of file
/dstatInterface/build/
/dstat-interface/dstat-interface/dist/
/dstat-interface/dstat-interface/build/
\ No newline at end of file
File moved
<?xml version="1.0" ?>
<project name="dstat-interface" version="1.0">
<optionset name="general"/>
<package name="dstat-interface"/>
<folder name="tests"/>
</project>
......@@ -5,7 +5,7 @@ __requires__ = 'PyInstaller==2.1'
import os, sys
os.chdir(os.path.dirname(sys.argv[0]))
args = ['interface_test.spec']
args = ['dstat.spec']
args.extend(sys.argv[1:])
import PyInstaller.main as pyi #For some reason, it gets the path here, so working dir must be set first
......
# -*- mode: python -*-
a = Analysis(['./main.py'],
pathex=['/Users/mdryden/src/dstat-interface2/dstatInterface'],
hiddenimports=[],
hookspath=None,
runtime_hooks=None)
pyz = PYZ(a.pure)
exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
name='DStat',
debug=False,
strip=None,
upx=True,
console=False )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=None,
upx=True,
name='DStat')
app = BUNDLE(coll,
name='DStat.app',
icon=None)
#!/usr/bin/env python
import serial, io, time, struct, sys, os
from types import *
import serial
from serial.tools import list_ports
import numpy as np
import time
import struct
import multiprocessing as mp
from Queue import Empty
def call_it(instance, name, args=(), kwargs=None):
"indirect caller for instance methods and multiprocessing"
"""Indirect caller for instance methods and multiprocessing.
Arguments:
instance -- instance to which the method belongs
name -- method to call
args -- passed to method
kwargs -- passed to method
"""
if kwargs is None:
kwargs = {}
return getattr(instance, name)(*args, **kwargs)
class delayedSerial(serial.Serial): #overrides normal serial write so that characters are output individually with a slight delay
class delayedSerial(serial.Serial):
"""Extends Serial.write so that characters are output individually
with a slight delay
"""
def write(self, data):
for i in data:
serial.Serial.write(self, i)
time.sleep(.001)
class SerialDevices:
class SerialDevices(object):
"""Retrieves and stores list of serial devices in self.ports"""
def __init__(self):
try:
self.ports, _, _ = zip(*list_ports.comports())
......@@ -29,22 +39,33 @@ class SerialDevices:
print "No serial ports found"
def refresh(self):
"""Refreshes list of ports."""
self.ports, _, _ = zip(*list_ports.comports())
class Experiment:
class Experiment(object):
"""Store and acquire a potentiostat experiment. Meant to be subclassed
to by different experiment types and not used instanced directly.
"""
def run_wrapper(self, *argv):
self.p = mp.Process(target=call_it, args=(self, 'run', argv))
self.p.start()
"""Execute experiment indirectly using call_it to bypass lack of fork()
on Windows for multiprocessing.
"""
self.proc = mp.Process(target=call_it, args=(self, 'run', argv))
self.proc.start()
def __init__(self): #will always be overriden, but self.parameters, self.viewparameters, and self.databytes should be defined
pass
def __init__(self, parameters, main_pipe):
"""Must be overridden to define self.parameters, and self.databytes."""
self.parameters = parameters
self.main_pipe = main_pipe
self.databytes = 8
def init(self):
self.data_extra = [] #must be defined even when not needed
"""Adds commands for gain and ADC."""
self.data_extra = [] # must be defined even when not needed
self.__gaintable = [1e2, 3e2, 3e3, 3e4, 3e5, 3e6, 3e7, 5e8]
self.gain = self.__gaintable[int(self.parameters['gain'])]
self.commands = ["A","G"]
self.commands = ["A", "G"]
self.commands[0] += (self.parameters['adc_buffer'])
self.commands[0] += " "
......@@ -55,8 +76,15 @@ class Experiment:
self.commands[1] += (self.parameters['gain'])
self.commands[1] += " "
def run(self, strPort):
self.serial = delayedSerial(strPort, 1024000, timeout=1)
def run(self, ser_port):
"""Execute experiment. Connects and sends handshake signal to DStat
then sendsself.commands. Don't call directly as a process in Windows,
use run_wrapper instead.
Arguments:
ser_port -- address of serial port to use
"""
self.serial = delayedSerial(ser_port, 1024000, timeout=1)
self.serial.write("ck")
self.serial.flushInput()
......@@ -77,6 +105,10 @@ class Experiment:
self.main_pipe.close()
def serial_handler(self):
"""Handles incoming serial transmissions from DStat. Returns False
if stop button pressed and sends abort signal to instrument. Sends
data to self.main_pipe as result of self.data_handler).
"""
scan = 0
while True:
if self.main_pipe.poll():
......@@ -87,7 +119,8 @@ class Experiment:
for line in self.serial:
if line.startswith('B'):
self.main_pipe.send(self.data_handler((scan, self.serial.read(size=self.databytes))))
self.main_pipe.send(self.data_handler(
(scan, self.serial.read(size=self.databytes))))
elif line.startswith('S'):
scan += 1
elif line.startswith("#"):
......@@ -98,22 +131,31 @@ class Experiment:
return True
def data_handler(self, input):
scan, data = input
def data_handler(self, data_input):
"""Takes data_input as tuple -- (scan, data).
Returns:
(scan number, [voltage, current]) -- voltage in mV, current in A
"""
scan, data = data_input
voltage, current = struct.unpack('<Hl', data) #uint16 + int32
return (scan, [(voltage-32768)*3000./65536, current*(1.5/self.gain/8388607)])
return (scan,
[(voltage-32768)*3000./65536, current*(1.5/self.gain/8388607)])
def data_postprocessing(self):
"""No data postprocessing done by default, can be overridden
in subclass.
"""
pass
class chronoamp(Experiment):
"""Chronoamperometry experiment"""
def __init__(self, parameters, main_pipe):
self.main_pipe = main_pipe
self.parameters = parameters
self.datatype = "linearData"
self.xlabel = "Time (s)"
self.ylabel = "Current (A)"
self.data = [[],[]]
self.data = [[], []]
self.datalength = 2
self.databytes = 8
self.xmin = 0
......@@ -122,7 +164,7 @@ class chronoamp(Experiment):
for i in self.parameters['time']:
self.xmax += int(i)
self.init() #need to call after xmin and xmax are set
self.init() # need to call after xmin and xmax are set
self.commands += "R"
self.commands[2] += str(len(self.parameters['potential']))
......@@ -134,12 +176,16 @@ class chronoamp(Experiment):
self.commands[2] += str(i)
self.commands[2] += " "
def data_handler(self, input): #overrides inherited method to not convert x axis
scan, data = input
seconds, milliseconds, current = struct.unpack('<HHl', data) #2*uint16 + int32
return (scan, [seconds+milliseconds/1000., current*(1.5/self.gain/8388607)])
def data_handler(self, data_input):
"""Overrides Experiment method to not convert x axis to mV."""
scan, data = data_input
# 2*uint16 + int32
seconds, milliseconds, current = struct.unpack('<HHl', data)
return (scan,
[seconds+milliseconds/1000., current*(1.5/self.gain/8388607)])
class lsv_exp(Experiment):
"""Linear Scan Voltammetry experiment"""
def __init__(self, parameters, send_pipe):
self.main_pipe = send_pipe
self.parameters = parameters
......@@ -147,22 +193,24 @@ class lsv_exp(Experiment):
self.datatype = "linearData"
self.xlabel = "Voltage (mV)"
self.ylabel = "Current (A)"
self.data = [[],[]]
self.data = [[], []]
self.datalength = 2
self.databytes = 6 #uint16 + int32
self.databytes = 6 # uint16 + int32
self.xmin = self.parameters['start']
self.xmax = self.parameters['stop']
self.init() #need to call after xmin and xmax are set
self.init() # need to call after xmin and xmax are set
self.commands += "L"
self.commands[2] += str(self.parameters['clean_s'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['dep_s'])
self.commands[2] += " "
self.commands[2] += str(int(self.parameters['clean_mV']*(65536./3000)+32768))
self.commands[2] += str(int(self.parameters['clean_mV']*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(int(self.parameters['dep_mV']*(65536./3000)+32768))
self.commands[2] += str(int(self.parameters['dep_mV']*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(self.parameters['start'])
self.commands[2] += " "
......@@ -172,6 +220,7 @@ class lsv_exp(Experiment):
self.commands[2] += " "
class cv_exp(Experiment):
"""Cyclic Voltammetry experiment"""
def __init__(self, parameters, main_pipe):
self.main_pipe = main_pipe
self.parameters = parameters
......@@ -179,9 +228,9 @@ class cv_exp(Experiment):
self.datatype = "CVData"
self.xlabel = "Voltage (mV)"
self.ylabel = "Current (A)"
self.data = [[],[]] #Will have to alter data_handler to add new lists as needed
self.datalength = 2 * self.parameters['scans'] #x and y for each scan
self.databytes = 6 #uint16 + int32
self.data = [[], []]
self.datalength = 2 * self.parameters['scans'] # x and y for each scan
self.databytes = 6 # uint16 + int32
self.xmin = self.parameters['v1']
self.xmax = self.parameters['v2']
......@@ -192,9 +241,11 @@ class cv_exp(Experiment):
self.commands[2] += " "
self.commands[2] += str(self.parameters['dep_s'])
self.commands[2] += " "
self.commands[2] += str(int(self.parameters['clean_mV']*(65536./3000)+32768))
self.commands[2] += str(int(self.parameters['clean_mV']*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(int(self.parameters['dep_mV']*(65536./3000)+32768))
self.commands[2] += str(int(self.parameters['dep_mV']*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(self.parameters['v1'])
self.commands[2] += " "
......@@ -206,13 +257,9 @@ class cv_exp(Experiment):
self.commands[2] += " "
self.commands[2] += str(self.parameters['slope'])
self.commands[2] += " "
def data_handler(self, input):
scan, data = input
voltage, current = struct.unpack('<Hl', data) #uint16 + int32
return (scan, [(voltage-32768)*3000./65536, current*(1.5/self.gain/8388607)])
class swv_exp(Experiment):
"""Square Wave Voltammetry experiment"""
def __init__(self, parameters, main_pipe):
self.main_pipe = main_pipe
self.parameters = parameters
......@@ -220,7 +267,7 @@ class swv_exp(Experiment):
self.datatype = "SWVData"
self.xlabel = "Voltage (mV)"
self.ylabel = "Current (A)"
self.data = [[],[]] #only difference stored here
self.data = [[], []] # only difference stored here
self.datalength = 2 * self.parameters['scans']
self.databytes = 10
......@@ -228,16 +275,20 @@ class swv_exp(Experiment):
self.xmax = self.parameters['stop']
self.init()
self.data_extra = [[],[]] #forward/reverse stored here - needs to be after self.init to keep from being redefined
# forward/reverse stored here - needs to be after
# self.init to keep from being redefined
self.data_extra = [[], []]
self.commands += "S"
self.commands[2] += str(self.parameters['clean_s'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['dep_s'])
self.commands[2] += " "
self.commands[2] += str(int(self.parameters['clean_mV']*(65536./3000)+32768))
self.commands[2] += str(int(self.parameters['clean_mV']*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(int(self.parameters['dep_mV']*(65536./3000)+32768))
self.commands[2] += str(int(self.parameters['dep_mV']*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(self.parameters['start'])
self.commands[2] += " "
......@@ -252,21 +303,28 @@ class swv_exp(Experiment):
self.commands[2] += str(self.parameters['scans'])
self.commands[2] += " "
def data_handler(self, input):
scan, data = input
voltage, forward, reverse = struct.unpack('<Hll', data) #uint16 + int32
return (scan, [(voltage-32768)*3000./65536, (forward-reverse)*(1.5/self.gain/8388607), forward*(1.5/self.gain/8388607), reverse*(1.5/self.gain/8388607)])
def data_handler(self, input_data):
"""Overrides Experiment method to calculate difference current"""
scan, data = input_data
# uint16 + int32
voltage, forward, reverse = struct.unpack('<Hll', data)
return (scan, [(voltage-32768)*3000./65536,
(forward-reverse)*(1.5/self.gain/8388607),
forward*(1.5/self.gain/8388607),
reverse*(1.5/self.gain/8388607)])
class dpv_exp(swv_exp):
"""Diffential Pulse Voltammetry experiment."""
def __init__(self, parameters, main_pipe):
"""Overrides swv_exp method"""
self.main_pipe = main_pipe
self.parameters = parameters
self.datatype = "SWVData"
self.xlabel = "Voltage (mV)"
self.ylabel = "Current (A)"
self.data = [[],[]] #only difference stored here
self.data = [[], []] # only difference stored here
self.datalength = 2
self.databytes = 10
......@@ -274,16 +332,20 @@ class dpv_exp(swv_exp):
self.xmax = self.parameters['stop']
self.init()
self.data_extra = [[],[]] #forward/reverse stored here - needs to be after self.init to keep from being redefined
# forward/reverse stored here - needs to be after self.init to
# keep from being redefined
self.data_extra = [[], []]
self.commands += "D"
self.commands[2] += str(self.parameters['clean_s'])
self.commands[2] += " "
self.commands[2] += str(self.parameters['dep_s'])
self.commands[2] += " "
self.commands[2] += str(int(self.parameters['clean_mV']*(65536./3000)+32768))
self.commands[2] += str(int(self.parameters['clean_mV']*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(int(self.parameters['dep_mV']*(65536./3000)+32768))
self.commands[2] += str(int(self.parameters['dep_mV']*
(65536./3000)+32768))
self.commands[2] += " "
self.commands[2] += str(self.parameters['start'])
self.commands[2] += " "
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment