1*b7c941bbSAndroid Build Coastguard Worker#!/usr/bin/env python 2*b7c941bbSAndroid Build Coastguard Worker 3*b7c941bbSAndroid Build Coastguard Worker# Copyright (C) 2014 The Android Open Source Project 4*b7c941bbSAndroid Build Coastguard Worker# 5*b7c941bbSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*b7c941bbSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*b7c941bbSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*b7c941bbSAndroid Build Coastguard Worker# 9*b7c941bbSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*b7c941bbSAndroid Build Coastguard Worker# 11*b7c941bbSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*b7c941bbSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*b7c941bbSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*b7c941bbSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*b7c941bbSAndroid Build Coastguard Worker# limitations under the License. 16*b7c941bbSAndroid Build Coastguard Worker 17*b7c941bbSAndroid Build Coastguard Worker"""Interface for a USB-connected Monsoon power meter 18*b7c941bbSAndroid Build Coastguard Worker(http://msoon.com/LabEquipment/PowerMonitor/). 19*b7c941bbSAndroid Build Coastguard WorkerThis file requires gflags, which requires setuptools. 20*b7c941bbSAndroid Build Coastguard WorkerTo install setuptools: sudo apt-get install python-setuptools 21*b7c941bbSAndroid Build Coastguard WorkerTo install gflags, see http://code.google.com/p/python-gflags/ 22*b7c941bbSAndroid Build Coastguard WorkerTo install pyserial, see http://pyserial.sourceforge.net/ 23*b7c941bbSAndroid Build Coastguard Worker 24*b7c941bbSAndroid Build Coastguard WorkerExample usages: 25*b7c941bbSAndroid Build Coastguard Worker Set the voltage of the device 7536 to 4.0V 26*b7c941bbSAndroid Build Coastguard Worker python monsoon.py --voltage=4.0 --serialno 7536 27*b7c941bbSAndroid Build Coastguard Worker 28*b7c941bbSAndroid Build Coastguard Worker Get 5000hz data from device number 7536, with unlimited number of samples 29*b7c941bbSAndroid Build Coastguard Worker python monsoon.py --samples -1 --hz 5000 --serialno 7536 30*b7c941bbSAndroid Build Coastguard Worker 31*b7c941bbSAndroid Build Coastguard Worker Get 200Hz data for 5 seconds (1000 events) from default device 32*b7c941bbSAndroid Build Coastguard Worker python monsoon.py --samples 100 --hz 200 33*b7c941bbSAndroid Build Coastguard Worker 34*b7c941bbSAndroid Build Coastguard Worker Get unlimited 200Hz data from device attached at /dev/ttyACM0 35*b7c941bbSAndroid Build Coastguard Worker python monsoon.py --samples -1 --hz 200 --device /dev/ttyACM0 36*b7c941bbSAndroid Build Coastguard Worker 37*b7c941bbSAndroid Build Coastguard WorkerOutput columns for collection with --samples, separated by space: 38*b7c941bbSAndroid Build Coastguard Worker 39*b7c941bbSAndroid Build Coastguard Worker TIMESTAMP OUTPUT OUTPUT_AVG USB USB_AVG 40*b7c941bbSAndroid Build Coastguard Worker | | | | 41*b7c941bbSAndroid Build Coastguard Worker | | | ` (if --includeusb and --avg) 42*b7c941bbSAndroid Build Coastguard Worker | | ` (if --includeusb) 43*b7c941bbSAndroid Build Coastguard Worker | ` (if --avg) 44*b7c941bbSAndroid Build Coastguard Worker ` (if --timestamp) 45*b7c941bbSAndroid Build Coastguard Worker""" 46*b7c941bbSAndroid Build Coastguard Worker 47*b7c941bbSAndroid Build Coastguard Workerimport fcntl 48*b7c941bbSAndroid Build Coastguard Workerimport os 49*b7c941bbSAndroid Build Coastguard Workerimport select 50*b7c941bbSAndroid Build Coastguard Workerimport signal 51*b7c941bbSAndroid Build Coastguard Workerimport stat 52*b7c941bbSAndroid Build Coastguard Workerimport struct 53*b7c941bbSAndroid Build Coastguard Workerimport sys 54*b7c941bbSAndroid Build Coastguard Workerimport time 55*b7c941bbSAndroid Build Coastguard Workerimport collections 56*b7c941bbSAndroid Build Coastguard Worker 57*b7c941bbSAndroid Build Coastguard Workerimport gflags as flags # http://code.google.com/p/python-gflags/ 58*b7c941bbSAndroid Build Coastguard Worker 59*b7c941bbSAndroid Build Coastguard Workerimport serial # http://pyserial.sourceforge.net/ 60*b7c941bbSAndroid Build Coastguard Worker 61*b7c941bbSAndroid Build Coastguard WorkerFLAGS = flags.FLAGS 62*b7c941bbSAndroid Build Coastguard Worker 63*b7c941bbSAndroid Build Coastguard Workerclass Monsoon: 64*b7c941bbSAndroid Build Coastguard Worker """ 65*b7c941bbSAndroid Build Coastguard Worker Provides a simple class to use the power meter, e.g. 66*b7c941bbSAndroid Build Coastguard Worker mon = monsoon.Monsoon() 67*b7c941bbSAndroid Build Coastguard Worker mon.SetVoltage(3.7) 68*b7c941bbSAndroid Build Coastguard Worker mon.StartDataCollection() 69*b7c941bbSAndroid Build Coastguard Worker mydata = [] 70*b7c941bbSAndroid Build Coastguard Worker while len(mydata) < 1000: 71*b7c941bbSAndroid Build Coastguard Worker mydata.extend(mon.CollectData()) 72*b7c941bbSAndroid Build Coastguard Worker mon.StopDataCollection() 73*b7c941bbSAndroid Build Coastguard Worker """ 74*b7c941bbSAndroid Build Coastguard Worker 75*b7c941bbSAndroid Build Coastguard Worker def __init__(self, device=None, serialno=None, wait=1): 76*b7c941bbSAndroid Build Coastguard Worker """ 77*b7c941bbSAndroid Build Coastguard Worker Establish a connection to a Monsoon. 78*b7c941bbSAndroid Build Coastguard Worker By default, opens the first available port, waiting if none are ready. 79*b7c941bbSAndroid Build Coastguard Worker A particular port can be specified with "device", or a particular Monsoon 80*b7c941bbSAndroid Build Coastguard Worker can be specified with "serialno" (using the number printed on its back). 81*b7c941bbSAndroid Build Coastguard Worker With wait=0, IOError is thrown if a device is not immediately available. 82*b7c941bbSAndroid Build Coastguard Worker """ 83*b7c941bbSAndroid Build Coastguard Worker 84*b7c941bbSAndroid Build Coastguard Worker self._coarse_ref = self._fine_ref = self._coarse_zero = self._fine_zero = 0 85*b7c941bbSAndroid Build Coastguard Worker self._coarse_scale = self._fine_scale = 0 86*b7c941bbSAndroid Build Coastguard Worker self._last_seq = 0 87*b7c941bbSAndroid Build Coastguard Worker self.start_voltage = 0 88*b7c941bbSAndroid Build Coastguard Worker 89*b7c941bbSAndroid Build Coastguard Worker if device: 90*b7c941bbSAndroid Build Coastguard Worker self.ser = serial.Serial(device, timeout=1) 91*b7c941bbSAndroid Build Coastguard Worker return 92*b7c941bbSAndroid Build Coastguard Worker 93*b7c941bbSAndroid Build Coastguard Worker while True: # try all /dev/ttyACM* until we find one we can use 94*b7c941bbSAndroid Build Coastguard Worker for dev in os.listdir("/dev"): 95*b7c941bbSAndroid Build Coastguard Worker if not dev.startswith("ttyACM"): continue 96*b7c941bbSAndroid Build Coastguard Worker tmpname = "/tmp/monsoon.%s.%s" % (os.uname()[0], dev) 97*b7c941bbSAndroid Build Coastguard Worker self._tempfile = open(tmpname, "w") 98*b7c941bbSAndroid Build Coastguard Worker try: 99*b7c941bbSAndroid Build Coastguard Worker os.chmod(tmpname, 0666) 100*b7c941bbSAndroid Build Coastguard Worker except OSError: 101*b7c941bbSAndroid Build Coastguard Worker pass 102*b7c941bbSAndroid Build Coastguard Worker try: # use a lockfile to ensure exclusive access 103*b7c941bbSAndroid Build Coastguard Worker fcntl.lockf(self._tempfile, fcntl.LOCK_EX | fcntl.LOCK_NB) 104*b7c941bbSAndroid Build Coastguard Worker except IOError as e: 105*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, "device %s is in use" % dev 106*b7c941bbSAndroid Build Coastguard Worker continue 107*b7c941bbSAndroid Build Coastguard Worker 108*b7c941bbSAndroid Build Coastguard Worker try: # try to open the device 109*b7c941bbSAndroid Build Coastguard Worker self.ser = serial.Serial("/dev/%s" % dev, timeout=1) 110*b7c941bbSAndroid Build Coastguard Worker self.StopDataCollection() # just in case 111*b7c941bbSAndroid Build Coastguard Worker self._FlushInput() # discard stale input 112*b7c941bbSAndroid Build Coastguard Worker status = self.GetStatus() 113*b7c941bbSAndroid Build Coastguard Worker except Exception as e: 114*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, "error opening device %s: %s" % (dev, e) 115*b7c941bbSAndroid Build Coastguard Worker continue 116*b7c941bbSAndroid Build Coastguard Worker 117*b7c941bbSAndroid Build Coastguard Worker if not status: 118*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, "no response from device %s" % dev 119*b7c941bbSAndroid Build Coastguard Worker elif serialno and status["serialNumber"] != serialno: 120*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, ("Note: another device serial #%d seen on %s" % 121*b7c941bbSAndroid Build Coastguard Worker (status["serialNumber"], dev)) 122*b7c941bbSAndroid Build Coastguard Worker else: 123*b7c941bbSAndroid Build Coastguard Worker self.start_voltage = status["voltage1"] 124*b7c941bbSAndroid Build Coastguard Worker return 125*b7c941bbSAndroid Build Coastguard Worker 126*b7c941bbSAndroid Build Coastguard Worker self._tempfile = None 127*b7c941bbSAndroid Build Coastguard Worker if not wait: raise IOError("No device found") 128*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, "waiting for device..." 129*b7c941bbSAndroid Build Coastguard Worker time.sleep(1) 130*b7c941bbSAndroid Build Coastguard Worker 131*b7c941bbSAndroid Build Coastguard Worker 132*b7c941bbSAndroid Build Coastguard Worker def GetStatus(self): 133*b7c941bbSAndroid Build Coastguard Worker """ Requests and waits for status. Returns status dictionary. """ 134*b7c941bbSAndroid Build Coastguard Worker 135*b7c941bbSAndroid Build Coastguard Worker # status packet format 136*b7c941bbSAndroid Build Coastguard Worker STATUS_FORMAT = ">BBBhhhHhhhHBBBxBbHBHHHHBbbHHBBBbbbbbbbbbBH" 137*b7c941bbSAndroid Build Coastguard Worker STATUS_FIELDS = [ 138*b7c941bbSAndroid Build Coastguard Worker "packetType", "firmwareVersion", "protocolVersion", 139*b7c941bbSAndroid Build Coastguard Worker "mainFineCurrent", "usbFineCurrent", "auxFineCurrent", "voltage1", 140*b7c941bbSAndroid Build Coastguard Worker "mainCoarseCurrent", "usbCoarseCurrent", "auxCoarseCurrent", "voltage2", 141*b7c941bbSAndroid Build Coastguard Worker "outputVoltageSetting", "temperature", "status", "leds", 142*b7c941bbSAndroid Build Coastguard Worker "mainFineResistor", "serialNumber", "sampleRate", 143*b7c941bbSAndroid Build Coastguard Worker "dacCalLow", "dacCalHigh", 144*b7c941bbSAndroid Build Coastguard Worker "powerUpCurrentLimit", "runTimeCurrentLimit", "powerUpTime", 145*b7c941bbSAndroid Build Coastguard Worker "usbFineResistor", "auxFineResistor", 146*b7c941bbSAndroid Build Coastguard Worker "initialUsbVoltage", "initialAuxVoltage", 147*b7c941bbSAndroid Build Coastguard Worker "hardwareRevision", "temperatureLimit", "usbPassthroughMode", 148*b7c941bbSAndroid Build Coastguard Worker "mainCoarseResistor", "usbCoarseResistor", "auxCoarseResistor", 149*b7c941bbSAndroid Build Coastguard Worker "defMainFineResistor", "defUsbFineResistor", "defAuxFineResistor", 150*b7c941bbSAndroid Build Coastguard Worker "defMainCoarseResistor", "defUsbCoarseResistor", "defAuxCoarseResistor", 151*b7c941bbSAndroid Build Coastguard Worker "eventCode", "eventData", ] 152*b7c941bbSAndroid Build Coastguard Worker 153*b7c941bbSAndroid Build Coastguard Worker self._SendStruct("BBB", 0x01, 0x00, 0x00) 154*b7c941bbSAndroid Build Coastguard Worker while True: # Keep reading, discarding non-status packets 155*b7c941bbSAndroid Build Coastguard Worker bytes = self._ReadPacket() 156*b7c941bbSAndroid Build Coastguard Worker if not bytes: return None 157*b7c941bbSAndroid Build Coastguard Worker if len(bytes) != struct.calcsize(STATUS_FORMAT) or bytes[0] != "\x10": 158*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, "wanted status, dropped type=0x%02x, len=%d" % ( 159*b7c941bbSAndroid Build Coastguard Worker ord(bytes[0]), len(bytes)) 160*b7c941bbSAndroid Build Coastguard Worker continue 161*b7c941bbSAndroid Build Coastguard Worker 162*b7c941bbSAndroid Build Coastguard Worker status = dict(zip(STATUS_FIELDS, struct.unpack(STATUS_FORMAT, bytes))) 163*b7c941bbSAndroid Build Coastguard Worker assert status["packetType"] == 0x10 164*b7c941bbSAndroid Build Coastguard Worker for k in status.keys(): 165*b7c941bbSAndroid Build Coastguard Worker if k.endswith("VoltageSetting"): 166*b7c941bbSAndroid Build Coastguard Worker status[k] = 2.0 + status[k] * 0.01 167*b7c941bbSAndroid Build Coastguard Worker elif k.endswith("FineCurrent"): 168*b7c941bbSAndroid Build Coastguard Worker pass # needs calibration data 169*b7c941bbSAndroid Build Coastguard Worker elif k.endswith("CoarseCurrent"): 170*b7c941bbSAndroid Build Coastguard Worker pass # needs calibration data 171*b7c941bbSAndroid Build Coastguard Worker elif k.startswith("voltage") or k.endswith("Voltage"): 172*b7c941bbSAndroid Build Coastguard Worker status[k] = status[k] * 0.000125 173*b7c941bbSAndroid Build Coastguard Worker elif k.endswith("Resistor"): 174*b7c941bbSAndroid Build Coastguard Worker status[k] = 0.05 + status[k] * 0.0001 175*b7c941bbSAndroid Build Coastguard Worker if k.startswith("aux") or k.startswith("defAux"): status[k] += 0.05 176*b7c941bbSAndroid Build Coastguard Worker elif k.endswith("CurrentLimit"): 177*b7c941bbSAndroid Build Coastguard Worker status[k] = 8 * (1023 - status[k]) / 1023.0 178*b7c941bbSAndroid Build Coastguard Worker return status 179*b7c941bbSAndroid Build Coastguard Worker 180*b7c941bbSAndroid Build Coastguard Worker def RampVoltage(self, start, end): 181*b7c941bbSAndroid Build Coastguard Worker v = start 182*b7c941bbSAndroid Build Coastguard Worker if v < 3.0: v = 3.0 # protocol doesn't support lower than this 183*b7c941bbSAndroid Build Coastguard Worker while (v < end): 184*b7c941bbSAndroid Build Coastguard Worker self.SetVoltage(v) 185*b7c941bbSAndroid Build Coastguard Worker v += .1 186*b7c941bbSAndroid Build Coastguard Worker time.sleep(.1) 187*b7c941bbSAndroid Build Coastguard Worker self.SetVoltage(end) 188*b7c941bbSAndroid Build Coastguard Worker 189*b7c941bbSAndroid Build Coastguard Worker def SetVoltage(self, v): 190*b7c941bbSAndroid Build Coastguard Worker """ Set the output voltage, 0 to disable. """ 191*b7c941bbSAndroid Build Coastguard Worker if v == 0: 192*b7c941bbSAndroid Build Coastguard Worker self._SendStruct("BBB", 0x01, 0x01, 0x00) 193*b7c941bbSAndroid Build Coastguard Worker else: 194*b7c941bbSAndroid Build Coastguard Worker self._SendStruct("BBB", 0x01, 0x01, int((v - 2.0) * 100)) 195*b7c941bbSAndroid Build Coastguard Worker 196*b7c941bbSAndroid Build Coastguard Worker 197*b7c941bbSAndroid Build Coastguard Worker def SetMaxCurrent(self, i): 198*b7c941bbSAndroid Build Coastguard Worker """Set the max output current.""" 199*b7c941bbSAndroid Build Coastguard Worker assert i >= 0 and i <= 8 200*b7c941bbSAndroid Build Coastguard Worker 201*b7c941bbSAndroid Build Coastguard Worker val = 1023 - int((i/8)*1023) 202*b7c941bbSAndroid Build Coastguard Worker self._SendStruct("BBB", 0x01, 0x0a, val & 0xff) 203*b7c941bbSAndroid Build Coastguard Worker self._SendStruct("BBB", 0x01, 0x0b, val >> 8) 204*b7c941bbSAndroid Build Coastguard Worker 205*b7c941bbSAndroid Build Coastguard Worker def SetUsbPassthrough(self, val): 206*b7c941bbSAndroid Build Coastguard Worker """ Set the USB passthrough mode: 0 = off, 1 = on, 2 = auto. """ 207*b7c941bbSAndroid Build Coastguard Worker self._SendStruct("BBB", 0x01, 0x10, val) 208*b7c941bbSAndroid Build Coastguard Worker 209*b7c941bbSAndroid Build Coastguard Worker 210*b7c941bbSAndroid Build Coastguard Worker def StartDataCollection(self): 211*b7c941bbSAndroid Build Coastguard Worker """ Tell the device to start collecting and sending measurement data. """ 212*b7c941bbSAndroid Build Coastguard Worker self._SendStruct("BBB", 0x01, 0x1b, 0x01) # Mystery command 213*b7c941bbSAndroid Build Coastguard Worker self._SendStruct("BBBBBBB", 0x02, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe8) 214*b7c941bbSAndroid Build Coastguard Worker 215*b7c941bbSAndroid Build Coastguard Worker 216*b7c941bbSAndroid Build Coastguard Worker def StopDataCollection(self): 217*b7c941bbSAndroid Build Coastguard Worker """ Tell the device to stop collecting measurement data. """ 218*b7c941bbSAndroid Build Coastguard Worker self._SendStruct("BB", 0x03, 0x00) # stop 219*b7c941bbSAndroid Build Coastguard Worker 220*b7c941bbSAndroid Build Coastguard Worker 221*b7c941bbSAndroid Build Coastguard Worker def CollectData(self): 222*b7c941bbSAndroid Build Coastguard Worker """ Return some current samples. Call StartDataCollection() first. """ 223*b7c941bbSAndroid Build Coastguard Worker while True: # loop until we get data or a timeout 224*b7c941bbSAndroid Build Coastguard Worker bytes = self._ReadPacket() 225*b7c941bbSAndroid Build Coastguard Worker if not bytes: return None 226*b7c941bbSAndroid Build Coastguard Worker if len(bytes) < 4 + 8 + 1 or bytes[0] < "\x20" or bytes[0] > "\x2F": 227*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, "wanted data, dropped type=0x%02x, len=%d" % ( 228*b7c941bbSAndroid Build Coastguard Worker ord(bytes[0]), len(bytes)) 229*b7c941bbSAndroid Build Coastguard Worker continue 230*b7c941bbSAndroid Build Coastguard Worker 231*b7c941bbSAndroid Build Coastguard Worker seq, type, x, y = struct.unpack("BBBB", bytes[:4]) 232*b7c941bbSAndroid Build Coastguard Worker data = [struct.unpack(">hhhh", bytes[x:x+8]) 233*b7c941bbSAndroid Build Coastguard Worker for x in range(4, len(bytes) - 8, 8)] 234*b7c941bbSAndroid Build Coastguard Worker 235*b7c941bbSAndroid Build Coastguard Worker if self._last_seq and seq & 0xF != (self._last_seq + 1) & 0xF: 236*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, "data sequence skipped, lost packet?" 237*b7c941bbSAndroid Build Coastguard Worker self._last_seq = seq 238*b7c941bbSAndroid Build Coastguard Worker 239*b7c941bbSAndroid Build Coastguard Worker if type == 0: 240*b7c941bbSAndroid Build Coastguard Worker if not self._coarse_scale or not self._fine_scale: 241*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, "waiting for calibration, dropped data packet" 242*b7c941bbSAndroid Build Coastguard Worker continue 243*b7c941bbSAndroid Build Coastguard Worker 244*b7c941bbSAndroid Build Coastguard Worker def scale(val): 245*b7c941bbSAndroid Build Coastguard Worker if val & 1: 246*b7c941bbSAndroid Build Coastguard Worker return ((val & ~1) - self._coarse_zero) * self._coarse_scale 247*b7c941bbSAndroid Build Coastguard Worker else: 248*b7c941bbSAndroid Build Coastguard Worker return (val - self._fine_zero) * self._fine_scale 249*b7c941bbSAndroid Build Coastguard Worker 250*b7c941bbSAndroid Build Coastguard Worker out_main = [] 251*b7c941bbSAndroid Build Coastguard Worker out_usb = [] 252*b7c941bbSAndroid Build Coastguard Worker for main, usb, aux, voltage in data: 253*b7c941bbSAndroid Build Coastguard Worker out_main.append(scale(main)) 254*b7c941bbSAndroid Build Coastguard Worker out_usb.append(scale(usb)) 255*b7c941bbSAndroid Build Coastguard Worker return (out_main, out_usb) 256*b7c941bbSAndroid Build Coastguard Worker 257*b7c941bbSAndroid Build Coastguard Worker elif type == 1: 258*b7c941bbSAndroid Build Coastguard Worker self._fine_zero = data[0][0] 259*b7c941bbSAndroid Build Coastguard Worker self._coarse_zero = data[1][0] 260*b7c941bbSAndroid Build Coastguard Worker # print >>sys.stderr, "zero calibration: fine 0x%04x, coarse 0x%04x" % ( 261*b7c941bbSAndroid Build Coastguard Worker # self._fine_zero, self._coarse_zero) 262*b7c941bbSAndroid Build Coastguard Worker 263*b7c941bbSAndroid Build Coastguard Worker elif type == 2: 264*b7c941bbSAndroid Build Coastguard Worker self._fine_ref = data[0][0] 265*b7c941bbSAndroid Build Coastguard Worker self._coarse_ref = data[1][0] 266*b7c941bbSAndroid Build Coastguard Worker # print >>sys.stderr, "ref calibration: fine 0x%04x, coarse 0x%04x" % ( 267*b7c941bbSAndroid Build Coastguard Worker # self._fine_ref, self._coarse_ref) 268*b7c941bbSAndroid Build Coastguard Worker 269*b7c941bbSAndroid Build Coastguard Worker else: 270*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, "discarding data packet type=0x%02x" % type 271*b7c941bbSAndroid Build Coastguard Worker continue 272*b7c941bbSAndroid Build Coastguard Worker 273*b7c941bbSAndroid Build Coastguard Worker if self._coarse_ref != self._coarse_zero: 274*b7c941bbSAndroid Build Coastguard Worker self._coarse_scale = 2.88 / (self._coarse_ref - self._coarse_zero) 275*b7c941bbSAndroid Build Coastguard Worker if self._fine_ref != self._fine_zero: 276*b7c941bbSAndroid Build Coastguard Worker self._fine_scale = 0.0332 / (self._fine_ref - self._fine_zero) 277*b7c941bbSAndroid Build Coastguard Worker 278*b7c941bbSAndroid Build Coastguard Worker 279*b7c941bbSAndroid Build Coastguard Worker def _SendStruct(self, fmt, *args): 280*b7c941bbSAndroid Build Coastguard Worker """ Pack a struct (without length or checksum) and send it. """ 281*b7c941bbSAndroid Build Coastguard Worker data = struct.pack(fmt, *args) 282*b7c941bbSAndroid Build Coastguard Worker data_len = len(data) + 1 283*b7c941bbSAndroid Build Coastguard Worker checksum = (data_len + sum(struct.unpack("B" * len(data), data))) % 256 284*b7c941bbSAndroid Build Coastguard Worker out = struct.pack("B", data_len) + data + struct.pack("B", checksum) 285*b7c941bbSAndroid Build Coastguard Worker self.ser.write(out) 286*b7c941bbSAndroid Build Coastguard Worker 287*b7c941bbSAndroid Build Coastguard Worker 288*b7c941bbSAndroid Build Coastguard Worker def _ReadPacket(self): 289*b7c941bbSAndroid Build Coastguard Worker """ Read a single data record as a string (without length or checksum). """ 290*b7c941bbSAndroid Build Coastguard Worker len_char = self.ser.read(1) 291*b7c941bbSAndroid Build Coastguard Worker if not len_char: 292*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, "timeout reading from serial port" 293*b7c941bbSAndroid Build Coastguard Worker return None 294*b7c941bbSAndroid Build Coastguard Worker 295*b7c941bbSAndroid Build Coastguard Worker data_len = struct.unpack("B", len_char) 296*b7c941bbSAndroid Build Coastguard Worker data_len = ord(len_char) 297*b7c941bbSAndroid Build Coastguard Worker if not data_len: return "" 298*b7c941bbSAndroid Build Coastguard Worker 299*b7c941bbSAndroid Build Coastguard Worker result = self.ser.read(data_len) 300*b7c941bbSAndroid Build Coastguard Worker if len(result) != data_len: return None 301*b7c941bbSAndroid Build Coastguard Worker body = result[:-1] 302*b7c941bbSAndroid Build Coastguard Worker checksum = (data_len + sum(struct.unpack("B" * len(body), body))) % 256 303*b7c941bbSAndroid Build Coastguard Worker if result[-1] != struct.pack("B", checksum): 304*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, "invalid checksum from serial port" 305*b7c941bbSAndroid Build Coastguard Worker return None 306*b7c941bbSAndroid Build Coastguard Worker return result[:-1] 307*b7c941bbSAndroid Build Coastguard Worker 308*b7c941bbSAndroid Build Coastguard Worker def _FlushInput(self): 309*b7c941bbSAndroid Build Coastguard Worker """ Flush all read data until no more available. """ 310*b7c941bbSAndroid Build Coastguard Worker self.ser.flush() 311*b7c941bbSAndroid Build Coastguard Worker flushed = 0 312*b7c941bbSAndroid Build Coastguard Worker while True: 313*b7c941bbSAndroid Build Coastguard Worker ready_r, ready_w, ready_x = select.select([self.ser], [], [self.ser], 0) 314*b7c941bbSAndroid Build Coastguard Worker if len(ready_x) > 0: 315*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, "exception from serial port" 316*b7c941bbSAndroid Build Coastguard Worker return None 317*b7c941bbSAndroid Build Coastguard Worker elif len(ready_r) > 0: 318*b7c941bbSAndroid Build Coastguard Worker flushed += 1 319*b7c941bbSAndroid Build Coastguard Worker self.ser.read(1) # This may cause underlying buffering. 320*b7c941bbSAndroid Build Coastguard Worker self.ser.flush() # Flush the underlying buffer too. 321*b7c941bbSAndroid Build Coastguard Worker else: 322*b7c941bbSAndroid Build Coastguard Worker break 323*b7c941bbSAndroid Build Coastguard Worker if flushed > 0: 324*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, "dropped >%d bytes" % flushed 325*b7c941bbSAndroid Build Coastguard Worker 326*b7c941bbSAndroid Build Coastguard Workerdef main(argv): 327*b7c941bbSAndroid Build Coastguard Worker """ Simple command-line interface for Monsoon.""" 328*b7c941bbSAndroid Build Coastguard Worker useful_flags = ["voltage", "status", "usbpassthrough", "samples", "current"] 329*b7c941bbSAndroid Build Coastguard Worker if not [f for f in useful_flags if FLAGS.get(f, None) is not None]: 330*b7c941bbSAndroid Build Coastguard Worker print __doc__.strip() 331*b7c941bbSAndroid Build Coastguard Worker print FLAGS.MainModuleHelp() 332*b7c941bbSAndroid Build Coastguard Worker return 333*b7c941bbSAndroid Build Coastguard Worker 334*b7c941bbSAndroid Build Coastguard Worker if FLAGS.includeusb: 335*b7c941bbSAndroid Build Coastguard Worker num_channels = 2 336*b7c941bbSAndroid Build Coastguard Worker else: 337*b7c941bbSAndroid Build Coastguard Worker num_channels = 1 338*b7c941bbSAndroid Build Coastguard Worker 339*b7c941bbSAndroid Build Coastguard Worker if FLAGS.avg and FLAGS.avg < 0: 340*b7c941bbSAndroid Build Coastguard Worker print "--avg must be greater than 0" 341*b7c941bbSAndroid Build Coastguard Worker return 342*b7c941bbSAndroid Build Coastguard Worker 343*b7c941bbSAndroid Build Coastguard Worker mon = Monsoon(device=FLAGS.device, serialno=FLAGS.serialno) 344*b7c941bbSAndroid Build Coastguard Worker 345*b7c941bbSAndroid Build Coastguard Worker if FLAGS.voltage is not None: 346*b7c941bbSAndroid Build Coastguard Worker if FLAGS.ramp is not None: 347*b7c941bbSAndroid Build Coastguard Worker mon.RampVoltage(mon.start_voltage, FLAGS.voltage) 348*b7c941bbSAndroid Build Coastguard Worker else: 349*b7c941bbSAndroid Build Coastguard Worker mon.SetVoltage(FLAGS.voltage) 350*b7c941bbSAndroid Build Coastguard Worker 351*b7c941bbSAndroid Build Coastguard Worker if FLAGS.current is not None: 352*b7c941bbSAndroid Build Coastguard Worker mon.SetMaxCurrent(FLAGS.current) 353*b7c941bbSAndroid Build Coastguard Worker 354*b7c941bbSAndroid Build Coastguard Worker if FLAGS.status: 355*b7c941bbSAndroid Build Coastguard Worker items = sorted(mon.GetStatus().items()) 356*b7c941bbSAndroid Build Coastguard Worker print "\n".join(["%s: %s" % item for item in items]) 357*b7c941bbSAndroid Build Coastguard Worker 358*b7c941bbSAndroid Build Coastguard Worker if FLAGS.usbpassthrough: 359*b7c941bbSAndroid Build Coastguard Worker if FLAGS.usbpassthrough == 'off': 360*b7c941bbSAndroid Build Coastguard Worker mon.SetUsbPassthrough(0) 361*b7c941bbSAndroid Build Coastguard Worker elif FLAGS.usbpassthrough == 'on': 362*b7c941bbSAndroid Build Coastguard Worker mon.SetUsbPassthrough(1) 363*b7c941bbSAndroid Build Coastguard Worker elif FLAGS.usbpassthrough == 'auto': 364*b7c941bbSAndroid Build Coastguard Worker mon.SetUsbPassthrough(2) 365*b7c941bbSAndroid Build Coastguard Worker else: 366*b7c941bbSAndroid Build Coastguard Worker sys.exit('bad passthrough flag: %s' % FLAGS.usbpassthrough) 367*b7c941bbSAndroid Build Coastguard Worker 368*b7c941bbSAndroid Build Coastguard Worker if FLAGS.samples: 369*b7c941bbSAndroid Build Coastguard Worker # Make sure state is normal 370*b7c941bbSAndroid Build Coastguard Worker mon.StopDataCollection() 371*b7c941bbSAndroid Build Coastguard Worker status = mon.GetStatus() 372*b7c941bbSAndroid Build Coastguard Worker native_hz = status["sampleRate"] * 1000 373*b7c941bbSAndroid Build Coastguard Worker 374*b7c941bbSAndroid Build Coastguard Worker # Collect and average samples as specified 375*b7c941bbSAndroid Build Coastguard Worker mon.StartDataCollection() 376*b7c941bbSAndroid Build Coastguard Worker 377*b7c941bbSAndroid Build Coastguard Worker # In case FLAGS.hz doesn't divide native_hz exactly, use this invariant: 378*b7c941bbSAndroid Build Coastguard Worker # 'offset' = (consumed samples) * FLAGS.hz - (emitted samples) * native_hz 379*b7c941bbSAndroid Build Coastguard Worker # This is the error accumulator in a variation of Bresenham's algorithm. 380*b7c941bbSAndroid Build Coastguard Worker emitted = offset = 0 381*b7c941bbSAndroid Build Coastguard Worker chan_buffers = tuple([] for _ in range(num_channels)) 382*b7c941bbSAndroid Build Coastguard Worker # past n samples for rolling average 383*b7c941bbSAndroid Build Coastguard Worker history_deques = tuple(collections.deque() for _ in range(num_channels)) 384*b7c941bbSAndroid Build Coastguard Worker 385*b7c941bbSAndroid Build Coastguard Worker try: 386*b7c941bbSAndroid Build Coastguard Worker last_flush = time.time() 387*b7c941bbSAndroid Build Coastguard Worker while emitted < FLAGS.samples or FLAGS.samples == -1: 388*b7c941bbSAndroid Build Coastguard Worker # The number of raw samples to consume before emitting the next output 389*b7c941bbSAndroid Build Coastguard Worker need = (native_hz - offset + FLAGS.hz - 1) / FLAGS.hz 390*b7c941bbSAndroid Build Coastguard Worker if need > len(chan_buffers[0]): # still need more input samples 391*b7c941bbSAndroid Build Coastguard Worker chans_samples = mon.CollectData() 392*b7c941bbSAndroid Build Coastguard Worker if not all(chans_samples): break 393*b7c941bbSAndroid Build Coastguard Worker for chan_buffer, chan_samples in zip(chan_buffers, chans_samples): 394*b7c941bbSAndroid Build Coastguard Worker chan_buffer.extend(chan_samples) 395*b7c941bbSAndroid Build Coastguard Worker else: 396*b7c941bbSAndroid Build Coastguard Worker # Have enough data, generate output samples. 397*b7c941bbSAndroid Build Coastguard Worker # Adjust for consuming 'need' input samples. 398*b7c941bbSAndroid Build Coastguard Worker offset += need * FLAGS.hz 399*b7c941bbSAndroid Build Coastguard Worker while offset >= native_hz: # maybe multiple, if FLAGS.hz > native_hz 400*b7c941bbSAndroid Build Coastguard Worker this_sample = [sum(chan[:need]) / need for chan in chan_buffers] 401*b7c941bbSAndroid Build Coastguard Worker 402*b7c941bbSAndroid Build Coastguard Worker if FLAGS.timestamp: print int(time.time()), 403*b7c941bbSAndroid Build Coastguard Worker 404*b7c941bbSAndroid Build Coastguard Worker if FLAGS.avg: 405*b7c941bbSAndroid Build Coastguard Worker chan_avgs = [] 406*b7c941bbSAndroid Build Coastguard Worker for chan_deque, chan_sample in zip(history_deques, this_sample): 407*b7c941bbSAndroid Build Coastguard Worker chan_deque.appendleft(chan_sample) 408*b7c941bbSAndroid Build Coastguard Worker if len(chan_deque) > FLAGS.avg: chan_deque.pop() 409*b7c941bbSAndroid Build Coastguard Worker chan_avgs.append(sum(chan_deque) / len(chan_deque)) 410*b7c941bbSAndroid Build Coastguard Worker # Interleave channel rolling avgs with latest channel data 411*b7c941bbSAndroid Build Coastguard Worker data_to_print = [datum 412*b7c941bbSAndroid Build Coastguard Worker for pair in zip(this_sample, chan_avgs) 413*b7c941bbSAndroid Build Coastguard Worker for datum in pair] 414*b7c941bbSAndroid Build Coastguard Worker else: 415*b7c941bbSAndroid Build Coastguard Worker data_to_print = this_sample 416*b7c941bbSAndroid Build Coastguard Worker 417*b7c941bbSAndroid Build Coastguard Worker fmt = ' '.join('%f' for _ in data_to_print) 418*b7c941bbSAndroid Build Coastguard Worker print fmt % tuple(data_to_print) 419*b7c941bbSAndroid Build Coastguard Worker 420*b7c941bbSAndroid Build Coastguard Worker sys.stdout.flush() 421*b7c941bbSAndroid Build Coastguard Worker 422*b7c941bbSAndroid Build Coastguard Worker offset -= native_hz 423*b7c941bbSAndroid Build Coastguard Worker emitted += 1 # adjust for emitting 1 output sample 424*b7c941bbSAndroid Build Coastguard Worker chan_buffers = tuple(c[need:] for c in chan_buffers) 425*b7c941bbSAndroid Build Coastguard Worker now = time.time() 426*b7c941bbSAndroid Build Coastguard Worker if now - last_flush >= 0.99: # flush every second 427*b7c941bbSAndroid Build Coastguard Worker sys.stdout.flush() 428*b7c941bbSAndroid Build Coastguard Worker last_flush = now 429*b7c941bbSAndroid Build Coastguard Worker except KeyboardInterrupt: 430*b7c941bbSAndroid Build Coastguard Worker print >>sys.stderr, "interrupted" 431*b7c941bbSAndroid Build Coastguard Worker 432*b7c941bbSAndroid Build Coastguard Worker mon.StopDataCollection() 433*b7c941bbSAndroid Build Coastguard Worker 434*b7c941bbSAndroid Build Coastguard Worker 435*b7c941bbSAndroid Build Coastguard Workerif __name__ == '__main__': 436*b7c941bbSAndroid Build Coastguard Worker # Define flags here to avoid conflicts with people who use us as a library 437*b7c941bbSAndroid Build Coastguard Worker flags.DEFINE_boolean("status", None, "Print power meter status") 438*b7c941bbSAndroid Build Coastguard Worker flags.DEFINE_integer("avg", None, 439*b7c941bbSAndroid Build Coastguard Worker "Also report average over last n data points") 440*b7c941bbSAndroid Build Coastguard Worker flags.DEFINE_float("voltage", None, "Set output voltage (0 for off)") 441*b7c941bbSAndroid Build Coastguard Worker flags.DEFINE_float("current", None, "Set max output current") 442*b7c941bbSAndroid Build Coastguard Worker flags.DEFINE_string("usbpassthrough", None, "USB control (on, off, auto)") 443*b7c941bbSAndroid Build Coastguard Worker flags.DEFINE_integer("samples", None, 444*b7c941bbSAndroid Build Coastguard Worker "Collect and print this many samples. " 445*b7c941bbSAndroid Build Coastguard Worker "-1 means collect indefinitely.") 446*b7c941bbSAndroid Build Coastguard Worker flags.DEFINE_integer("hz", 5000, "Print this many samples/sec") 447*b7c941bbSAndroid Build Coastguard Worker flags.DEFINE_string("device", None, 448*b7c941bbSAndroid Build Coastguard Worker "Path to the device in /dev/... (ex:/dev/ttyACM1)") 449*b7c941bbSAndroid Build Coastguard Worker flags.DEFINE_integer("serialno", None, "Look for a device with this serial number") 450*b7c941bbSAndroid Build Coastguard Worker flags.DEFINE_boolean("timestamp", None, 451*b7c941bbSAndroid Build Coastguard Worker "Also print integer (seconds) timestamp on each line") 452*b7c941bbSAndroid Build Coastguard Worker flags.DEFINE_boolean("ramp", True, "Gradually increase voltage") 453*b7c941bbSAndroid Build Coastguard Worker flags.DEFINE_boolean("includeusb", False, "Include measurements from USB channel") 454*b7c941bbSAndroid Build Coastguard Worker 455*b7c941bbSAndroid Build Coastguard Worker main(FLAGS(sys.argv)) 456