Arvont Hill

Sunday, December 7, 2008

Using Python for Test & Measurement

I picked up the November 2008 issue of Linux Journal after seeing it had an article about using Python for scientific computing. As I read through the article I learned about two modules, numpy and SciPy , that provide "extended" capabilities for math and science operations such as matrix calculations, Fourier transformations or numerical integrals. The article also mentioned two other modules named matplotlib and IPython. Together these 4 packages provide a Python programmer the ability do many scientific calculations and plot results all from a powerful interactive environment similar to matlab with all the power of Python.

Upon browsing the SciPy website, I discovered there were a number of "cookbooks" for using SciPy to do various tasks. The Data Acquisition with NI-DAQmx cookbook interested me because of my present job and familiarity with National Instruments products. I checked it out and decided to get a small data measurement system setup using python and a NI USB-6009 multi-function DAQ module on my Vista Home Basic laptop.

Here is what my setup looks like:

  • python 2.5.2
  • numpy 1.2.1 for python2.5
  • scipy 0.6.0 for python2.5
  • matplotlib 0.98.3win32
  • ipython 0.9.1
  • PyReadline 1.5 (Optional: This provides tab completion in Windows. It also provides colored text in an ipython window.)
  • NI-DAQmx 8.7.1f3 (most recent version should be ok as well)
  • NI USB-6009 Multifunction DAQ

  • This setup also relies on the ctypes module which is a default module in python 2.5. If you are using python 2.4 or less you may have to install this module.

    The example code found on the SciPy website shows an example that takes a C example provided with the installation of DAQmx and re-writes it using ctypes and numpy. My system copies this example and turns it into a module to be used by a higher level script that will acquire data and plot the data.

    Here is the module I named contAcquireNChan

    #contAcquireNChan.py

    import ctypes
    import numpy

    nidaq = ctypes.windll.nicaiu # load the DLL

    ##############################
    # Setup some typedefs and constants
    # to correspond with values in
    # C:\Program Files\National Instruments\NI-DAQ\DAQmx ANSI C Dev\include\NIDAQmx.h

    # the typedefs
    int32 = ctypes.c_long
    uInt32 = ctypes.c_ulong
    uInt64 = ctypes.c_ulonglong
    float64 = ctypes.c_double
    TaskHandle = uInt32
    written = int32()
    pointsRead = uInt32()

    # the constants
    DAQmx_Val_Cfg_Default = int32(-1)
    DAQmx_Val_Volts = 10348
    DAQmx_Val_Rising = 10280
    DAQmx_Val_FiniteSamps = 10178
    DAQmx_Val_GroupByChannel = 0
    DAQmx_Val_ChanForAllLines = 1
    DAQmx_Val_RSE = 10083
    DAQmx_Val_Volts = 10348
    DAQmx_Val_ContSamps = 10123
    DAQmx_Val_GroupByScanNumber = 1
    ##############################

    def CHK(err):
        """a simple error checking routine"""
        if err < 0:
            buf_size = 1000
            buf = ctypes.create_string_buffer('\000' * buf_size)
            nidaq.DAQmxGetErrorString(err,ctypes.byref(buf),buf_size)
            raise RuntimeError('nidaq call failed with error %d: %s'%(err,repr(buf.value)))

    # initialize variables
    taskHandle = TaskHandle(0)
    min = float64(-10.0)
    max = float64(10.0)
    timeout = float64(10.0)
    bufferSize = uInt32(10)
    pointsToRead = bufferSize
    pointsRead = uInt32()
    sampleRate = float64(10000.0)
    samplesPerChan = uInt64(2000)
    chan = ctypes.create_string_buffer('Dev1/ai0')
    clockSource = ctypes.create_string_buffer('OnboardClock')


    data = numpy.zeros((1000,),dtype=numpy.float64)


    # Create Task and Voltage Channel and Configure Sample Clock
    def SetupTask():
        CHK(nidaq.DAQmxCreateTask("",ctypes.byref(taskHandle)))
        CHK(nidaq.DAQmxCreateAIVoltageChan(taskHandle,chan,"",DAQmx_Val_RSE,min,max,
            DAQmx_Val_Volts,None))
        CHK(nidaq.DAQmxCfgSampClkTiming(taskHandle,clockSource,sampleRate,
            DAQmx_Val_Rising,DAQmx_Val_ContSamps,samplesPerChan))
        CHK(nidaq.DAQmxCfgInputBuffer(taskHandle,200000))

    #Start Task
    def StartTask():
        CHK(nidaq.DAQmxStartTask (taskHandle))

    #Read Samples
    def ReadSamples(points):
        bufferSize = uInt32(points)
        pointsToRead = bufferSize
        data = numpy.zeros((points,),dtype=numpy.float64)
        CHK(nidaq.DAQmxReadAnalogF64(taskHandle,pointsToRead,timeout,
                DAQmx_Val_GroupByScanNumber,data.ctypes.data,
                uInt32(2*bufferSize.value),ctypes.byref(pointsRead),None))

        print "Acquired %d pointx(s)"%(pointsRead.value)
        return data

    def StopAndClearTask():
        if taskHandle.value != 0:
            nidaq.DAQmxStopTask(taskHandle)
            nidaq.DAQmxClearTask(taskHandle)
    I wanted to capture a time varying signal and plot the time based signal as well as its spectrum. I created a 100Hz signal with a PIC16F690 that I had on my bench and connected the output to ai0 on the USB-6009. Then I ran this script, measure.py, that calls the contAcquireNChan module. The script plots the results using pylab and also returns that data read from the USB-6009 as a numpy array.



    #measure.py

    from contAcquireNChan import *
    from pylab import *
    import time

    def acquire(points):
        times = []
        SetupTask()
        StartTask()
        data = ReadSamples(points)
        acqTime = points/sampleRate.value
        StopAndClearTask()
        t = linspace(0,acqTime,points)
        clf()
        plot(t,data)
        axis([0,acqTime,-10,10])
        ylabel("Volts")
        xlabel("time (sec)")
        grid(True)
        return data


    I made another script that acquired data and then plotted the spectrum of the same 100Hz signal.



    #plot_example1.py

    from scipy import *
    from pylab import *
    import time


    from contAcquireNChan import *


    def acquire(points):
        SetupTask()
        StartTask()
        tstart = time.time()
        data = ReadSamples(points)
        StopAndClearTask()
        return data

    #Capture 600ms of data
    sample_rate=sampleRate.value
    t=r_[0:0.6:1/sample_rate]
    N=len(t)
    s=acquire(N)
    S=fft(s)
    f=sample_rate*r_[0:(N/2)]/N
    n=len(f)
    clf()
    plot(f,abs(S[0:n])/N)
    show()
    grid(True)

    Sunday, August 3, 2008

    More iRobot Create

    Virtual Wall Search: Trial 1

    The goal of this project was to have the iRobot Create robot spin around until it sees an infrared signal from a "Virtual Wall". Upon seeing the virtual wall signal the Create will stop. After a quick test I had success and changed the requirements so that the Create would always try to keep the virtual wall in its sights.

    I started out by writing a crude python module that would allow me to send iRobot Open Interface (OI) commands from the host computer. Not only does the module send commands but it also processes sensor data transmitted from the Create. So the host computer is actually the brains of the operation.

    Another thing I did was to use my xbee 802.15.4 modules as a wireless serial connection between the host computer and the Create. So now I have remote control of the robot. Check out some pictures here and here.

    Here's the function that's doing all of the work. Python syntax is pretty similar to other languages with one difference...indentations matter and they are used to mark off different blocks of code. If you can't read code there's a translation at the bottom of the post.

    def spinForVirtualWall(debug):
        packetId = 13
        ir = readPacket(packetId)
        time.sleep(0.1)
        spinCw()

        while 1:
            while ir[0] != 1:
                ir = readPacket(packetId)
                if debug:
                    print "ir = "+str(ir[0])
            stopDrive()

            while ir[0] == 1:
                ir = readPacket(packetId)
                if debug:
                    print "ir = "+str(ir[0])
            spinCw()



    First the robot looks for the virtual wall. If it doesn't see it it starts spinning clockwise and stops when (or if) it sees a virtual wall. After stopping, the robot continues looking for the virtual wall signal. If the robot loses sight of the signal then it will continue spinning clockwise until it sees the virtual wall again.

    Tuesday, June 3, 2008

    Virtual Wall

    I built a virtual wall from these plans to see if I could program the iRobot Create to follow a beacon. The virtual wall puts out an infrared signal just like a TV remote control that the iRobot Create can see. I wrote a program in Python on my mac that controls the robot. When the robot sees the virtual wall it stops, when the virtual wall moves the robot starts spinning again to look for the virtual wall.

    Next step...add an ultrasonic sensor and change the program so that the robot can follow the virtual wall in two dimensions instead of just spinning around.

    Sunday, May 18, 2008

    Wubi+Hardy Heron

    Ubuntu 8.04 Hardy Heron came out and I wanted to check it out to see what was new. I only had one machine that was fast enough to evaluate the distro, my Gateway ML3109 laptop with a Celeron M processor and ATI Radeon graphics. It was running Vista and I didn't want to fool around with making new partitions or fooling around with a bootloader. However, after reading an ars technica review of Hardy Heron, I discovered I could use Wubi to install the distro on my laptop as if it was a normal Windows program. Better yet I could easily just uninstall it as well.

    So I ran the installer, the installer downloaded about 500MB or so of files. The next time I booted up the Windows boot loader was presented and gave an option of Ubuntu.

    I had played around with 7.10 on an old P3 with 384MB of RAM and was satisfied with it. I was anxious to see what 8.04 could do on the faster processor.

    Here's my first impression after using it

    • Flash: I wanted to watch a TV show on Hulu but when I got to the landing page it said I was missing a plug-in. I installed the missing plug-in from Firefox and I was soon watching my show.

    • DVDs: This installation can't play DVDs by default. I had to install a bunch of different packages to get a DVD to play on the included Totem Movie Player. There are probably some copyright issues that these packages are excluded. I got hints on what to install from the Ubuntu Help program under the heading Playing DVDs. I followed the steps there to confirm that the DVD would play. Step 2 tells you to run the install-css.sh script. Be careful this will only work if you have the libc6-dev-i386 and libc6-i386 packages installed.

      After I knew I could play a DVD I installed VLC using Synaptic Package Manager. This is a much better choice for playing videos.

    • Compiz: This is a package that let's you do a bunch of visual effects. I wanted to try out the cube so I did some research and found out how to configure it. To get it to work I had to install restricted ATI drivers to take advantage of the 3D capabilities of my graphics card.

    • Sound: The sound volume was low compared to what I was getting with Vista. The DVD audio and other audio seem to be played through the PCM channel on the mixer. A google search gave me hint on how to open up the mixer to adjust the PCM volume. If you double-click the speaker icon it will open the full mixer. I adjusted PCM to full and Master to full and the sound is now adequate. I pretty much have to do the same thing on Vista to get decent sound as well.

    Tuesday, March 25, 2008

    iRobot Create: Logitech Harmony 520 Remote Control


    I bought a iRobot Create programmable robot to play around with and learn a little something about robotics. I had always wanted to have my own robot to play around with and the iRobot seemed to be the best option for me. I didn't want to deal with the mechanics or construction, and I wanted to focus on the modularity of the platform and the software interface.

    One of the first mini-projects I worked on was controlling the Create with a universal remote programmed like the standard iRobot Roomba remote.






    Logitech Harmony 520

    Sunday, July 29, 2007

    My first day with the iPhone


    The Purchase
    I walked into the AT&T store by my house and was greeted by a friendly sales guy. I told him I wanted to buy an iPhone. I chose the one I wanted and about 2 or 3 minutes later I was at the checkout counter with another salesperson.

    Since I was an old AT&T Wireless customer that never switched over to an official Cingular plan, I had to choose a plan and officially become a "new" AT&T customer. This made the activation process a little easier for transferring my number to the new SIM. I was asked for my e-mail address. I reluctantly gave it because I was wondering why they needed it. I paid the cash for an 8 GB, and I received the phone and a temporary SIM card. The temporary SIM card was what I could use for my old phone in between the time of the purchase and my in-home activation with iTunes.

    The Activation
    The activation had one snag that I noticed. I connected the iPhone to my Mac, started the activation and iTunes made it to a page that instructed me to check for voicemail on my old phone because it would be lost as I continued with this activation. Now at this point I tried to check my voicemail on my old phone with the temporary SIM card. Well, I didn't have access to my mailbox anymore. The mailbox wasn't setup on the new SIM and the old SIM was deactivated. Luckily I didn't have any voicemail messages so it wasn't that bad.

    Other than that the setup was very smooth. Apple did a good job in my opinion.

    Music and Video iPod
    The user interface is just as easy to use as the click wheel on the iPod...nothing special here. The video looks pretty good and the audio is adequate. It works just fine.

    Bluetooth
    My V600 let me setup shortcuts for different functions. That's what I used to setup and activate Bluetooth. The iPhone doesn't have shortcuts yet...at least I haven't found them. To activate Bluetooth, from the main screen: choose Settings->General->Bluetooth. Make your headset discoverable; enter the passcode and you're done. I use a motorola HS820.

    Again nothing special here. I just wish I could jump right to the bluetooth screen to deactivate and activate Bluetooth when necessary.


    Pinch, Touchscreen, and typing
    Pinch is cool, using the touchscreen along with Pinch will take some getting used to though. I found myself having to zoom in on some links to ensure that I was hitting the right link with my fingers.

    WiFi
    I don't broadcast my SSID from my home home network, and apparently if the SSID isn't being broadcast the iPhone can't use it, even if you enter all of the info manually. I'll have to do more testing to confirm this. Once I had the SSID and WEP password loaded into the iPhone and set my Netgear router to broadcast the SSID I was able to connect the iPhone to the router. However, there was a little snag and I needed to reset my router. I'm not sure what the problem was but it was easily fixed with the reset.

    There was a noticeable difference between WiFi and EDGE with the WiFi being much faster.

    Syncing
    The one problem I am seeing with syncing is that there are 44 music files that get synced every time I sync the iPhone. Oddly enough the files are made up of multiple tracks by each of the following artists only: Beastie Boys, Kanye West, Maroon 5, Justin Timberlake, Snoop Dogg and Knee Deep Shag.

    I have it setup to sync selected playlists. I tried de-selecting the playlist where those files are, syncing, selecting the playlist again, and finally syncing one last time. So far this process has lessened the number of files but it didn't fix. For now it's an annoyance that I'll have to live with.

    Accessories
    I have a Kenwood KDC-MP5028 Stereo with a KCA-iP500 iPod Interface also by Kenwood. Despite the warning on the iPhone's screen all three of them play nicely together.

    What's Missing That I need or want
    Shortcuts to setup menus
    Bluetooth file transfer of photos
    Bluetooth transfer of contacts
    A2DP Bluetooth streaming.

    What's Missing That I'm not to concerned about
    Video recording


    Further Testing
    My Motorola V600 is unlocked and I was able to use my iPhone SIM with it. The next experiment will be tethering the V600 to my iBook with bluetooth to try to use the data plan. Even at a lower speed using the configuration can be more desirable in some situations than using EDGE on an iPhone. If I'm typing a long e-mail or reading a long blog post, I'm much rather use a full screen and full keyboard than the iPhone's UI in Safari.

    I also want to see what happens when the iPhone is connected to my KDC-MP5028 when I receive a phone call. What if the headset is on?

    Overall Impression
    I'm satisfied with the iPhone so far. It delivers what it promises so I could only grade on some kind of arbitrary pass/fail basis. I give it a pass. So far there's nothing outstandingly great nor really bad.

    Tuesday, July 17, 2007

    July 15, 2007 Bike Ride Transmountain Trail

    Here is the file for my bike ride on July 15th, 2007. You will need Google Earth to view it.