├── .gitignore ├── DAQ.py ├── README.md └── read_me.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .htaccess 2 | wp-config.php 3 | wp-content/uploads/ 4 | wp-content/blogs.dir/ 5 | wp-content/upgrade/ 6 | wp-content/backup-db/ 7 | wp-content/advanced-cache.php 8 | wp-content/wp-cache-config.php 9 | sitemap.xml 10 | *.log 11 | wp-content/cache/ 12 | wp-content/backups/ 13 | sitemap.xml.gz 14 | -------------------------------------------------------------------------------- /DAQ.py: -------------------------------------------------------------------------------- 1 | #DAQ.py 2 | #Suman Giri 3 | 4 | #program that reads from a data-acquisition card (NI-DAQ 9215) 5 | #and plots the voltage 6 | #lets the user see the information in time and frequency domain 7 | #lets the user save a snapshot, write the data to file, and adjust scales 8 | #while writing, splits the written data into file snippets of 50 Mb csv files 9 | 10 | 11 | 12 | import ctypes #library used here for handling/accessing dlls 13 | import numpy #libarary for list manipulations 14 | import time 15 | from time import * 16 | import os 17 | import pprint 18 | import random 19 | import csv #for writing data into csv files 20 | import sys 21 | import wx #graphics 22 | import matplotlib #library used here for plotting 23 | matplotlib.use('WXAgg') 24 | from matplotlib.figure import * 25 | from matplotlib.backends.backend_wxagg import * 26 | import pylab 27 | from scipy import* 28 | from pylab import * 29 | 30 | 31 | #this loads the dll for the NIDAQ 32 | nidaq = ctypes.windll.nicaiu 33 | 34 | 35 | 36 | # typedefs are setup to correspond to NIDAQmx.h 37 | int32 = ctypes.c_long 38 | uInt32 = ctypes.c_ulong 39 | uInt64 = ctypes.c_ulonglong 40 | float64 = ctypes.c_double 41 | TaskHandle = uInt32 42 | written = int32() 43 | pointsRead = uInt32() 44 | 45 | #constants are setup to correspond to NIDAQmx.h 46 | DAQmx_Val_Volts = 10348 47 | DAQmx_Val_Rising = 10280 48 | DAQmx_Val_Cfg_Default = int32(-1) 49 | DAQmx_Val_ContSamps = 10123 50 | DAQmx_Val_ChanForAllLines = 1 51 | DAQmx_Val_RSE = 10083 52 | DAQmx_Val_Volts = 10348 53 | DAQmx_Val_GroupByScanNumber = 1 54 | DAQmx_Val_FiniteSamps = 10178 55 | DAQmx_Val_GroupByChannel = 0 56 | 57 | 58 | 59 | #adapted with info from .NET and C code in 60 | #http://zone.ni.com/devzone/cda/tut/p/id/5409#toc4 61 | 62 | 63 | # initialize variables 64 | taskHandle = TaskHandle(0) 65 | 66 | #range of the DAQ 67 | min1 = float64(-10.0) 68 | max1 = float64(10.0) 69 | timeout = float64(10.0) 70 | bufferSize = uInt32(10) 71 | pointsToRead = bufferSize 72 | pointsRead = uInt32() 73 | 74 | #sampling rate 75 | sampleRate = float64(200.0) 76 | samplesPerChan = uInt64(100) 77 | 78 | #specifiy the channels 79 | chan = ctypes.create_string_buffer('Dev1/ai0') 80 | clockSource = ctypes.create_string_buffer('OnboardClock') 81 | 82 | #create a list of zeros for data 83 | data = numpy.zeros((1000,),dtype=numpy.float64) 84 | 85 | 86 | # set up the task in the required channel and 87 | #fix sampling through internal clock 88 | def SetupTask(): 89 | nidaq.DAQmxCreateTask("",ctypes.byref(taskHandle)) 90 | nidaq.DAQmxCreateAIVoltageChan(taskHandle,chan,"", 91 | DAQmx_Val_Cfg_Default, 92 | min1,max1,DAQmx_Val_Volts,None) 93 | nidaq.DAQmxCfgSampClkTiming(taskHandle,clockSource,sampleRate, 94 | DAQmx_Val_Rising,DAQmx_Val_ContSamps,samplesPerChan) 95 | nidaq.DAQmxCfgInputBuffer(taskHandle,200000) 96 | 97 | #Start Task 98 | def StartTask(): 99 | nidaq.DAQmxStartTask (taskHandle) 100 | 101 | #Read Samples 102 | def ReadSamples(points): 103 | bufferSize = uInt32(points) 104 | pointsToRead = bufferSize 105 | data = numpy.zeros((points,),dtype=numpy.float64) 106 | nidaq.DAQmxReadAnalogF64(taskHandle,pointsToRead,timeout, 107 | DAQmx_Val_GroupByScanNumber,data.ctypes.data, 108 | uInt32(2*bufferSize.value),ctypes.byref(pointsRead),None) 109 | return data 110 | 111 | #stop and clear 112 | def StopAndClearTask(): 113 | if taskHandle.value != 0: 114 | nidaq.DAQmxStopTask(taskHandle) 115 | nidaq.DAQmxClearTask(taskHandle) 116 | 117 | #On specifying the number of points to be sampled, it gets 118 | #the voltage value and returns it as a list data 119 | def get(points=100): 120 | SetupTask() 121 | StartTask() 122 | data = ReadSamples(points) 123 | StopAndClearTask() 124 | return data 125 | 126 | 127 | #received info from 128 | #http://matplotlib.sourceforge.net/examples/axes_rid/index.html 129 | 130 | class Samples(object): 131 | #Samples from the DAQ card for plotting 132 | 133 | def __init__(self, init=100): 134 | self.data = self.init = 0 135 | 136 | def getAnotherBatch(self): 137 | #gets the next 100 points each time it is called 138 | return get() 139 | 140 | def Transform(self): 141 | #gets the FFT of 1000 points when called 142 | return abs(fft(get(100))) 143 | 144 | class Scaling(wx.Panel): 145 | #subclass 146 | #an object to create the framework to 147 | #autoscale/manually scale the plot 148 | 149 | def __init__(self, other, ID, label, value): 150 | wx.Panel.__init__(self, other, ID) 151 | self.value = value 152 | box = wx.StaticBox(self, -1, label) 153 | 154 | #to resize the box accordingly 155 | sizer = wx.StaticBoxSizer(box, wx.VERTICAL) 156 | 157 | #radio buttons for options of scaling 158 | self.radioAuto = wx.RadioButton(self, -1, 159 | label="Auto Scale", style=wx.RB_GROUP) 160 | self.radioManual = wx.RadioButton(self, -1, 161 | label="Manual Scale") 162 | 163 | #input box for manual scaling 164 | self.text = wx.TextCtrl(self, -1, 165 | size=(35,-1), 166 | value=str(value), 167 | style=wx.TE_PROCESS_ENTER) 168 | 169 | #call self.text upon value update 170 | self.Bind(wx.EVT_UPDATE_UI, self.updatemanual, self.text) 171 | self.Bind(wx.EVT_TEXT_ENTER, self.textmanual, self.text) 172 | 173 | manualBox = wx.BoxSizer(wx.HORIZONTAL) 174 | manualBox.Add(self.radioManual, flag=wx.ALIGN_CENTER_VERTICAL) 175 | manualBox.Add(self.text, flag=wx.ALIGN_CENTER_VERTICAL) 176 | sizer.Add(self.radioAuto, 0, wx.ALL, 10) 177 | sizer.Add(manualBox, 0, wx.ALL, 10) 178 | self.SetSizer(sizer) 179 | sizer.Fit(self) 180 | 181 | def updateauto(self): 182 | #when the auto radio button is updated 183 | return self.radioAuto.GetValue() 184 | 185 | def manual_value(self): 186 | return self.value 187 | 188 | def updatemanual(self, event): 189 | #when the manual radio button is updated 190 | self.text.Enable(self.radioManual.GetValue()) 191 | 192 | def textmanual(self, event): 193 | #when a text is entered 194 | self.value = self.text.GetValue() 195 | 196 | 197 | 198 | 199 | class Window(wx.Frame): 200 | # The window object for the plot 201 | # subclass of wx.Frame 202 | 203 | 204 | def __init__(self): 205 | wx.Frame.__init__(self, None, -1, 'Real-Time Voltage') #frame title 206 | 207 | self.paused = False 208 | self.transform=False 209 | self.write=False 210 | self.filenum=1 #counter for time domain files 211 | self.filenum1=1 #counter for freq domain files 212 | self.filesize=50*1024*1024 #size that files need to be split to 213 | self.samples = Samples() 214 | 215 | #get 100 points in an array 216 | self.data = self.samples.getAnotherBatch() 217 | 218 | #create a menubar with ability to save plots 219 | self.makeMenubar() 220 | self.makeStatusbar() 221 | self.makeMainPanel() 222 | 223 | #initialize timer 224 | self.timerFired = wx.Timer(self) 225 | self.Bind(wx.EVT_TIMER, self.goTimerFired, self.timerFired) 226 | self.timerFired.Start(0.00001) 227 | 228 | def makeMenubar(self): 229 | #create a menubar with ability to save plots 230 | self.menubar = wx.MenuBar() 231 | 232 | menu = wx.Menu() 233 | #save 234 | m_expt = menu.Append(-1, "&Save plot\tCtrl-S", "Save plot to file") 235 | self.Bind(wx.EVT_MENU, self.saveMenu, m_expt) 236 | menu.AppendSeparator() 237 | #exit 238 | m_exit = menu.Append(-1, "&Exit\tCtrl-C", "Exit") 239 | self.Bind(wx.EVT_MENU, self.exitMenu, m_exit) 240 | #menu 241 | self.menubar.Append(menu, "&Menu") 242 | self.SetMenuBar(self.menubar) 243 | 244 | def makeMainPanel(self): 245 | #fill in the objects inside the mainpanel 246 | self.panel = wx.Panel(self) 247 | 248 | self.prepareGraph() 249 | #load canvas from wx library 250 | self.canvas = FigureCanvasWxAgg(self.panel, -1, self.fig) 251 | 252 | 253 | self.xmin = Scaling(self.panel, -1, "X min", 0) 254 | self.xmax = Scaling(self.panel, -1, "X max", 100) 255 | self.ymin = Scaling(self.panel, -1, "Y min", 0) 256 | self.ymax = Scaling(self.panel, -1, "Y max", 100) 257 | 258 | #create Pause button 259 | self.pauseButton = wx.Button(self.panel, -1, "Pause") 260 | self.Bind(wx.EVT_BUTTON, self.pausePressed, self.pauseButton) 261 | self.Bind(wx.EVT_UPDATE_UI, self.pauseUpdate, self.pauseButton) 262 | 263 | self.hbox1 = wx.BoxSizer(wx.HORIZONTAL) 264 | self.hbox1.Add(self.pauseButton, border=5, 265 | flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) 266 | 267 | #create Transform button 268 | self.transformButton = wx.Button(self.panel, -1, "Transform") 269 | self.Bind(wx.EVT_BUTTON, self.transformPressed, self.transformButton) 270 | self.Bind(wx.EVT_UPDATE_UI, self.transformUpdate, self.transformButton) 271 | self.hbox1.Add(self.transformButton, border=5, 272 | flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) 273 | 274 | #create Write button 275 | self.writeButton = wx.Button(self.panel, -1, "Write") 276 | self.Bind(wx.EVT_BUTTON, self.writePressed, self.writeButton) 277 | self.Bind(wx.EVT_UPDATE_UI, self.writeUpdate, self.writeButton) 278 | self.hbox1.Add(self.writeButton, border=5, 279 | flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) 280 | 281 | 282 | #layout management using sizers for xmin,ymin,xmax,ymax 283 | self.hbox2 = wx.BoxSizer(wx.HORIZONTAL) 284 | self.hbox2.Add(self.xmin, border=5, flag=wx.ALL) 285 | self.hbox2.Add(self.xmax, border=5, flag=wx.ALL) 286 | self.hbox2.AddSpacer(24) 287 | self.hbox2.Add(self.ymin, border=5, flag=wx.ALL) 288 | self.hbox2.Add(self.ymax, border=5, flag=wx.ALL) 289 | 290 | self.vbox = wx.BoxSizer(wx.VERTICAL) 291 | self.vbox.Add(self.canvas, 1, flag=wx.LEFT | wx.TOP | wx.GROW) 292 | self.vbox.Add(self.hbox1, 0, flag=wx.ALIGN_LEFT | wx.TOP) 293 | self.vbox.Add(self.hbox2, 0, flag=wx.ALIGN_LEFT | wx.TOP) 294 | 295 | self.panel.SetSizer(self.vbox) 296 | self.vbox.Fit(self) 297 | 298 | def makeStatusbar(self): 299 | self.statusbar = self.CreateStatusBar() 300 | 301 | def prepareGraph(self): 302 | #prepares the frame and the axes 303 | self.dpi = 120 304 | self.fig = Figure((2.5, 2.5), dpi=self.dpi) 305 | 306 | self.axes = self.fig.add_subplot(110) 307 | self.axes.set_axis_bgcolor('white') 308 | self.axes.set_title('Voltage Information', size=14) 309 | 310 | pylab.setp(self.axes.get_xticklabels(), fontsize=8) 311 | pylab.setp(self.axes.get_yticklabels(), fontsize=8) 312 | 313 | #plot the first batch of points 314 | self.plot_data = self.axes.plot( 315 | self.data, 316 | linewidth=1, 317 | color='blue', 318 | )[0] 319 | 320 | def plotPoints(self): 321 | #plots the points in self.data 322 | 323 | #set values for xmin and xmax 324 | if self.xmax.updateauto(): 325 | #set a window of 100 for auto for time domain 326 | if not self.transform: 327 | xmax = len(self.data) if len(self.data) > 100 else 100 328 | else: 329 | #set the max of the harmonics for auto in freq domain 330 | xmax =max(sampleRate.value* 331 | r_[0:len(self.data)/2]/len(self.data)) 332 | else: 333 | xmax = int(self.xmax.manual_value()) 334 | 335 | if self.xmin.updateauto(): 336 | if not self.transform: 337 | xmin = xmax - 100 338 | else: 339 | xmin=0 340 | else: 341 | xmin = int(self.xmin.manual_value()) 342 | 343 | #set values for ymin and ymax 344 | #ymax/min is the max/min of all the values in the data set 345 | 346 | if self.ymin.updateauto(): 347 | ymin = round(min(self.data), 0) - 1 348 | else: 349 | ymin = int(self.ymin.manual_value()) 350 | 351 | if self.ymax.updateauto(): 352 | ymax = round(max(self.data), 0) + 1 353 | else: 354 | ymax = int(self.ymax.manual_value()) 355 | 356 | self.axes.set_xbound(lower=xmin, upper=xmax) 357 | self.axes.set_ybound(lower=ymin, upper=ymax) 358 | 359 | 360 | pylab.setp(self.axes.get_xticklabels(), 361 | visible=True) 362 | 363 | if not self.transform: 364 | #for time domain set the x-axis to be a list of time points 365 | self.plot_data.set_xdata(numpy.arange(len(self.data))) 366 | self.plot_data.set_ydata(numpy.array(self.data)) 367 | else: 368 | #for frequency domain x-axis has to be dependent on the sample rate 369 | self.plot_data.set_xdata(sampleRate.value* 370 | r_[0:len(self.data)/2]/len(self.data)) 371 | self.plot_data.set_ydata(numpy.array(self.data[0:len(self.data)/2])) 372 | 373 | self.canvas.draw() 374 | 375 | def pausePressed(self, event): 376 | #if pause is pressed update accordingly 377 | self.paused = not self.paused 378 | self.pauseUpdate(event) 379 | 380 | def transformPressed (self,event): 381 | #if the transform button is pressed, update accordingly 382 | self.transform=not self.transform 383 | self.transformUpdate(event) 384 | 385 | def writePressed (self,event): 386 | #if the write button is pressed, update accordingly 387 | self.write=not self.write 388 | self.writeUpdate(event) 389 | 390 | 391 | 392 | def pauseUpdate(self, event): 393 | #change the text in the pause button 394 | if self.paused: 395 | txt = "Collect" 396 | else: 397 | txt= "Pause" 398 | self.pauseButton.SetLabel(txt) 399 | 400 | def transformUpdate(self, event): 401 | #change the text in the transform button 402 | if self.transform: 403 | txt="Time Domain" 404 | else: 405 | txt="Freq. Domain" 406 | self.transformButton.SetLabel(txt) 407 | 408 | def writeUpdate(self, event): 409 | #change the text in the write button 410 | if self.write: 411 | txt="Stop Writing" 412 | else: 413 | txt="Start Writing" 414 | self.writeButton.SetLabel(txt) 415 | 416 | 417 | def goTimerFired(self, event): 418 | #if not paused get data 419 | if not self.paused: 420 | if not self.transform: 421 | self.data=(self.samples.getAnotherBatch()) 422 | else: 423 | self.data=self.samples.Transform() 424 | if self.write: 425 | #module to write on file 426 | self.writeFiles() 427 | 428 | self.plotPoints() 429 | 430 | def writeFiles(self): 431 | #writes data into filesizes of 50Mb 432 | if not self.transform: 433 | self.timeWrite() 434 | else: 435 | self.frequencyWrite() 436 | 437 | 438 | 439 | def timeWrite(self): 440 | #if time domain data is being written 441 | #create a file time-domain-data.csv 442 | ofile = open("time-domain-data%d.csv"% self.filenum, "a") 443 | writer = csv.writer(ofile, delimiter='\t', quotechar='"', 444 | quoting=csv.QUOTE_ALL) 445 | 446 | #while the file size is less than 50 Mb 447 | while os.path.getsize("time-domain-data%d.csv"% 448 | self.filenum)