Software written in Python.

Mailcap files, defined in RFC 1524, allow you to tell you mail clients, web browsers, and other programs how you want to view and edit various MIME types. Since interaction with your mailcap files often occurs deep within the bowels of your program, I've written up a very simple Python script to test your mailcap entries: mailcap-test.py. Enjoy!

Posted Thu Dec 1 20:43:14 2011 Tags: python

In the course of my research, I've spend a good deal of time developing clean, Python interfaces to much of our lab equipment. I also tend to have strong opinions on the One True Way® to solve a problem. This means that I occasionaly end up writing script to run other people's experiment, especially when they don't take all that much time to write.

I wrote slow_bend for Liming Zhao, who was a postdoc in our lab from 2008 to 2010. Liming coated one side of an AFM cantilever with a film of cellulose and used slow bend.py (version 0.2) to monitor the cantilever deflection as he flushed in different buffers (paper). Unfortunately, the paper claims the data aquisition was carried out in LabView.

slow_bend is not a complicated program; it polls analog input channels using pycomedi (and optionally reads temperatures using backends from pypid). The polling continues until slow_bend recieves a KeyboardInterrupt.

$ slow_bend.py --version
0.4
$ slow_bend.py 0 3
#time (second)  chan 0 (bit)    chan 0 (volt)   chan 3 (bit)    chan 3 (volt)
1.81198e-05 34727   0.598001    39679   2.10925
4.00409 34956   0.667887    38033   1.60693
8.00408 35074   0.703899    36780   1.22454
12.0041 35041   0.693828    35814   0.929732
16.0041 34917   0.655985    35044   0.694743
^C
Posted Sat Nov 12 17:50:00 2011 Tags: python

Available in a git repository.
Repository: cookbook
Author: W. Trevor King

I've been running a home-rolled recipe webapp for a year now, and it worked fairly well in a bare-bones sort of way. However, I recently had to make some changes to my personal website (since EveryDNS and aparently most other free DNS providers were bought by Dyn), which prompted me to translate cookbook into a Django app. Thanks to the wonders of Django, Grappelli, and django-taggit, the code is now leaner, meaner, and prettier!

See the README for details.

Posted Fri Aug 5 13:59:01 2011 Tags: python

Available in a git repository.
Repository: h5config
Author: W. Trevor King

Since the number of packages mooching off pypiezo's configuration scheme was growing, I've split it out into it's own package. Now there's a general package for all your HDF5-based configuration needs.

The README is posted on the PyPI page.

Posted Thu Jul 28 12:25:06 2011 Tags: python

Available in a git repository.
Repository: pypid
Author: W. Trevor King

I've just finished rewriting my PID temperature control package in pure-Python, and it's now clean enough to go up on PyPI. Features:

  • Backend-agnostic architecture. I've written a first-order process with dead time (FOPDT) test backend and a pymodbus-based backend for our Melcor MTCA controller, but it should be easy to plug in your own custom backend.
  • The general PID controller will automatically tune your backend using any of a variety of tuning rules.

The README is posted on the PyPI page.

Posted Wed Jul 27 12:06:19 2011 Tags: python

Available in a git repository.
Repository: insider
Author: W. Trevor King

Insider is a little Django app I wrote to help my brother, Garrett, track insider trading with a simple, familiar web interface. It's a pretty simple app, partly thanks to Bradley Ayers' django-tables2, which does the table formatting. Just goes to show that a good scripting language and framework make developing simple apps a breeze!

The README is posted on the PyPI page.

Posted Thu Jul 21 18:30:33 2011 Tags: python

pyproj is a Python wrapper around PROJ.4. Here's a quick walkthrough.

Initialize a geodetic converter:

>>> from pyproj import Geod
>>> g = Geod(ellps='clrk66')

where ellps='clrk66' selects Clarke's 1866 reference ellipsoid. help(Geod.__new__) gives a list of possible ellipsoids.

Calculate the distance between two points, as well as the local heading, try

>>> lat1,lng1 = (40.7143528, -74.0059731)  # New York, NY
>>> lat2,lng2 = (49.261226, -123.1139268)   # Vancouver, Canada
>>> az12,az21,dist = g.inv(lng1,lat1,lng2,lat2)
>>> az12,az21,dist
(-59.10918706123901, 84.99453463527395, 3914198.2912370963)

which gives forward and back azimuths as well as the geodesic distance in meters. Not that longitude comes before latitude in the these pyproj argument lists.

Calculate the terminus of a geodesic from an initial point, azimuth, and distance with:

>>> lng3,lat3,az3 = g.fwd(lng1,lat1,az12, dist)
>>> lat3,lng3,az3
(49.26122600306212, -123.11392684861474, 84.99453467574762)

Plan your trip with:

>>> pts = g.npts(lng1,lat1,lng2,lat2,npts=5)
>>> pts.insert(0, (lng1, lat1))
>>> pts.append((lng2, lat2))
>>> import numpy
>>> npts = numpy.array(pts)
>>> npts
array([[ -74.0059731 ,   40.7143528 ],
       [ -80.93566289,   43.52686057],
       [ -88.48167748,   45.87969433],
       [ -96.61187851,   47.6930911 ],
       [-105.22271807,   48.89347605],
       [-114.13503215,   49.42510006],
       [-123.1139268 ,   49.261226  ]])

To plot the above New York to Vancouver route on a flat map, we need a Proj instance:

>>> from pyproj import Proj
>>> awips221 = Proj(proj='lcc', R=6371200, lat_1=50, lat_2=50,
...     lon_0=-107, ellps='clrk66')
>>> awips218 = Proj(proj='lcc', R=6371200, lat_1=25, lat_2=25,
...     lon_0=-95, ellps='clrk66')  #x_0=-llcrnrx,y_0=-llcrnry)

#llcrnrlon,llcrnrlat are lon and lat (in degrees) of lower
#    left hand corner of projection region.

where proj='lcc selects the Lambert conformal conic projection for the x/y points, and ellps='clrk66' selects the reference ellipsoid for the lat/lng coordinates. The other coordinates are LCC parameters that select the AWIPS 221 and AWIPS 226 projections respectively (lat_1 corresponds to Latin1, lat_2 corresponds to Latin2, and lon_0 corresponds to Lov; see this description of the two-standard-parallel LCC and its PROJ.4 parameters).

Convert our lat/lng pairs into grid points:

>>> awips221(lng1, lat1)
(2725283.842678774, 5823260.730665273)
>>> x221,y221 = awips221(npts[:,0], npts[:,1])
>>> # xy221 = numpy.concatenate((a1, a2, ...), axis=0)  # numpy-2.0
>>> xy221 = numpy.ndarray(shape=npts.shape, dtype=npts.dtype)
>>> xy221[:,0] = x221
>>> xy221[:,1] = y221
>>> xy221
array([[ 2725283.84267877,  5823260.73066527],
       [ 2071820.3526011 ,  5892518.49630526],
       [ 1422529.71760395,  5967565.49899035],
       [  775650.03731228,  6046475.43928965],
       [  129946.46495299,  6127609.80532071],
       [ -515306.57275941,  6209785.69230076],
       [-1160447.80254759,  6292455.41884832]])

Finally, you can convert points from one projection to another.

>>> from pyproj import transform
>>> x218,y218 = transform(awips221, awips218, x221, y221)
>>> xy218 = numpy.ndarray(shape=npts.shape, dtype=npts.dtype)
>>> xy218[:,0] = x218
>>> xy218[:,1] = y218
>>> xy218
array([[ 1834251.59591561,  4780900.70184736],
       [ 1197541.13209718,  5028862.9881648 ],
       [  542391.04388716,  5258740.71523961],
       [ -131577.34942316,  5464828.45934687],
       [ -822685.42269932,  5641393.59760613],
       [-1527077.85176048,  5783597.16169582],
       [-2239159.34620498,  5888495.91009021]])

Another useful coordinate system is the Universal Transverse Mercator projection which slices the world into zones.

>>> p = Proj(proj='utm', zone=10, ellps='clrk66')

Putting everything together, here's a route map based on digital lat/lng pairs stored in a text file:

>>> from numpy import array
>>> from pylab import plot, show
>>> from pyproj import Geod, Proj
>>> latlng = array([[float(x) for x in ln.split()]
...                for ln in open('coords', 'r')
...                if not ln.startswith('#')])
>>> g = Geod(ellps='WGS84')
>>> az12s,az21s,dists = g.inv(latlng[:-1,1], latlng[:-1,0],
...                           latlng[1:,1], latlng[1:,0])
>>> print('total distance: %g m' % dists.sum())
total distance: 2078.93 m
>>> mlng = latlng[:,1].mean()
>>> zone = int(round((mlng + 180) / 6.))
>>> p = Proj(proj='utm', zone=zone, ellps='WGS84')
>>> xs,ys = p(latlng[:,1], latlng[:,0])
>>> lines = plot(xs, ys, 'r.-')
>>> show()

I've written up a simple script using this approach: maproute.py. I've also written up a simple script to draw a map with labeled points: maplabel.py.

Note that you can easily get lat/lng pairs using geopy (ebuild in my Gentoo overlay):

>>> import geopy
>>> g = geopy.geocoders.Google()
>>> place1,(lat1,lng1) = g.geocode("New York, NY")
>>> place2,(lat2,lng2) = g.geocode("Vancouver, Canada")
>>> place1,(lat1,lng1)
(u'New York, NY, USA', (40.7143528, -74.0059731))
>>> place2,(lat2,lng2)
(u'Vancouver, BC, Canada', (49.261226, -123.1139268))

If you're looking for a more compact C++ package for geographic conversions, GeographicLib looks promising.

Posted Thu May 19 08:28:17 2011 Tags: python

Available in a git repository.
Repository: pyrisk
Author: W. Trevor King

Play Risk (or similar games) over email!

The README is posted on the PyPI page.

Posted Tue Apr 19 15:18:29 2011 Tags: python

Available in a git repository.
Repository: pycomedi
Author: W. Trevor King

I was getting frustrated with Comedi's SWIG wrappers, so I wrote a more object-oriented wrapper using Cython.

The README is posted on the PyPI page.

Posted Tue Apr 19 15:18:29 2011 Tags: python

Available in a git repository.
Repository: pypiezo
Author: W. Trevor King

This is a piezo-actuator control library based on pycomedi. It also contains some atomic-force-microscope-specific logic. The higher-level library pyafm extends the AFM-control framework with coarse positioning.

The README is posted on the PyPI page.

Posted Tue Apr 19 15:18:29 2011 Tags: python