Fun pages! Nothing serious here.

Looking into old birth, death, and marriage records, I ran across dates such oas "on the 12th instant". Looking this up it turns out to be shorthand for "the current month". Related terms:

  • Ultimo (the previous month, from Latin ultimo mense, abbreviated ult)
  • Instant (the current month, from Latin instante mense, abbreviated inst)
  • Proximo (the next month, from Latin proximo mense, abbreviated prox)
Posted Mon Jul 25 06:55:26 2011 Tags: fun

I just discovered that gnuplot has an xterm terminal output mode which uses xterm's tektronix emulator (since gnuplot 4.2.6). This is useful if you have logged into a remote machine and want to plot something, but you didn't have the foresight to use ssh -Y to bring along a connection to your X server. I used to use

gnuplot> set term dumb

but often the low resolution available with standard terminal characters left the resulting output almost unintelligible. With

gnuplot> set term xterm

your xterm will pop up a new window in tektronix mode, in which you get very nice monochrome graphics.

Besides the “forgot to tunnel X” use case outlined above, this output would also be useful if you wanted to avoid exposing yourself to X vulnerabilies while logging into an untrusted machine.

Obviously, you'll have to be running xterm for any of this to work ;).

Posted Wed Jul 20 12:54:43 2011 Tags: fun

Add the compose key to your Xmodmap file with something like

keycode 134 = Multi_key

The compose key has it's own unicode symbol: ⎄, which I'll use in the following composition tables.

Accents

  • ⎄`a à
  • ⎄'a á
  • ⎄^a â
  • ⎄ca ǎ
  • ⎄ba ă
  • ⎄oa å
  • ⎄!a ạ
  • ⎄.a ȧ
  • ⎄"a ä
  • ⎄~a ã
  • ⎄_a ā
  • ⎄;a ą
  • ⎄,c ç

Weird letters

  • ⎄AE Æ
  • ⎄ae æ
  • ⎄ss ß
  • ⎄TH Þ
  • ⎄th þ

Currency

  • ⎄=C €
  • ⎄|c ¢
  • ⎄-L £

Typography

  • ⎄<< «
  • ⎄>> »
  • ⎄"< “
  • ⎄"> ”
  • ⎄'< ‘
  • ⎄'> ’
  • ⎄.. …
  • ⎄p! ¶
  • ⎄os §
  • ⎄?? ¿
  • ⎄!! ¡
  • ⎄--- — (em dash)
  • ⎄--. – (em dash)

Math

  • ⎄oo °
  • ⎄^0 ⁰
  • ⎄^1 ¹
  • ⎄_1 ₁
  • ⎄12 ½
  • ⎄+- ±
  • ⎄xx ×
  • ⎄:- ÷
  • ⎄/o ø
  • ⎄mu µ

Business

  • ⎄oc ©
  • ⎄or ®
  • ⎄tm ™

Emoticons

  • ⎄:) ☺
  • ⎄:( ☹

As you can imagine, the list goes on and on. The compositing system depends on your application (see the Ubuntu wiki for Gnome notes), but if you're using the X Input Method (XIM), your compose table depends on your locale:

$ echo $LANG
en_US.UTF-8
$ grep $LANG\$ /usr/share/X11/locale/compose.dir
en_US.UTF-8/Compose             en_US.UTF-8
en_US.UTF-8/Compose:            en_US.UTF-8
$ less /usr/share/X11/locale/en_US.UTF-8/Compose

If you want to customize your compose keys, just add your own rules to an ~/.XCompose file:

$ cat ~/.XCompose
include "%L"

<Multi_key> <h> <o> <m> <e> : "http://physics.drexel.edu/~wking"  # Home page

Read Compose(5) for more information. You may need to log out and log back in (or use the newgrp trick) for your new ~/.XCompose file to take effect.

Posted Mon Jul 18 00:08:20 2011 Tags: fun

It's a good idea to periodically replace old PGP encryption keys to minimize the amount of data exposed by cracking the old key.

$ gpg --edit-key F15F5BE8
...
pub  1024D/F15F5BE8  created: 2008-08-09  expires: 2011-08-08  usage: SC  
                     trust: ultimate      validity: ultimate
sub  2048g/42407C74  created: 2008-08-09  expired: 2009-08-09  usage: E   
sub  2048g/4DA3FC0B  created: 2009-07-26  expired: 2010-08-08  usage: E   
sub  1024D/EB357E60  created: 2009-07-26  expired: 2010-08-08  usage: S   
[ultimate] (1). William Trevor King <wking@drexel.edu>
[ultimate] (2)  William Trevor King <tvrkng@gmail.com>

The usage characters are:

  • e = encrypt/decrypt
  • s = sign
  • c = certify (sign another key)
  • a = authenticate (e.g. log in to SSH with a PGP key)

See doc/DETAILS in the GnuPG source directory for details on the output format (and the related colon listing format).

Note that my encryption keys have expired. This makes it hard for people to send me encrypted mail. Create a new encryption key with

gpg> addkey

Answering the prompts as you see fit (I usually pick Elgamal for encryption). You can also add signing keys with addkey (I usually pick RSA for signing, since DSA keys are limited to 1024 bits, see ssh-keygen(1)).

There doesn't seem to be much to differentiate Elgamml vs. RSA for encryption. I pick Elgamal for encryption since I've already picked RSA for signing, and this spreads my eggs across more baskets.

Several gpg operations require a particular subkey to be selected. Use key to select subkeys by index (marked with a *):

gpg> key 1

pub  1024D/F15F5BE8  created: 2008-08-09  expires: 2012-05-24  usage: SC  
                     trust: ultimate      validity: ultimate
sub* 2048g/42407C74  created: 2008-08-09  expired: 2009-08-09  usage: E   
sub  2048g/4DA3FC0B  created: 2009-07-26  expired: 2010-08-08  usage: E   
sub  1024D/EB357E60  created: 2009-07-26  expired: 2010-08-08  usage: S   
sub  2048g/3FB721E8  created: 2011-05-25  expires: 2012-05-24  usage: E   
sub  2048R/9CADC4D9  created: 2011-05-25  expires: 2012-05-24  usage: S   
[ultimate] (1). William Trevor King <wking@drexel.edu>
[ultimate] (2)  William Trevor King <tvrkng@gmail.com>

If you get confused, there's also a help command.

Save and quit when you're done:

gpg> save

Once you've got your key all fixed up, upload the new version to your chosen keyserver:

$ gpg --send-keys F15F5BE8

You probably also want to post your new key somewhere on your website:

$ gpg --export --armor -o ~/.gnupg/pubkey.txt F15F5BE8
$ scp ~/.gnupg/pubkey.txt you@somewhere:public_html/pubkey.txt

Checking signatures

Here are some quick notes on checking signatures:

$ gpg --check-sigs F15F5BE8

will list the status of signatures for which you have the signing key in your keyring. However, if you are missing one of the signing keys, you may get a message like

10 signatures not checked due to missing keys

If you run

$ gpg --list-sigs F15F5BE8

you'll see all the signatures, and you can use the usual gpg --recv-key KEYID to check out the ones you don't have.

Posted Sun May 29 11:16:10 2011 Tags: fun

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: fun

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: fun

I just stumbled across gnuclad and the GNU/Linux distribution timeline. Pretty sweet ;). Distro names in the big SVG link to the distro's website, so its a fun way to explore.

Posted Tue Mar 15 21:48:31 2011 Tags: fun

I've reorganized my music a few times, and it took me a while to get organized enough to want a single directory tree that held everything I owned. Every once and a while I go through the junk drawer and move a few more songs into the "official" tree, checking their metadata and whatnot. I was getting annoyed at finding duplicate songs in several junk drawers, so I wrote up a little script, find duplicates.py which can compare SHA1s for files in two trees and remove any duplicates from the lesser tree. Now there is a lot less junk to merge into my official tree.

Posted Sat Mar 12 22:55:46 2011 Tags: fun

ticker.py is a simple stock-quote scraper using Python's urllib2 to grab pages and lxml to parse the HTML. It's a pretty straightforward example of elementary scraping in Python.

Posted Tue Dec 28 16:32:33 2010 Tags: fun

Some songs are louder than others, and it's annoying to have to constantly tweak the volume knob to keep things at a reasonable level. Enter "replay gain". You use a tool to go through your music and add tags marking how loud each song/album is, then you play them with something that understands the tags and adjusts the volume for you. Very convenient. Robert Downes (Bobulous) has a nice post with scripts for tagging FLAC files (the heart of his scripts is metaflac --add-replay-gain *.flac). Then just add

replaygain "album"

to /etc/mpd.conf and restart mpd. Voilà!

If you're not using mpd, modern versions of flac come with an undocumented --apply-replaygain-which-is-not-lossless option. It's annoying that they haven't gotten around to documenting this option (since 2004!), but the syntax itself is pretty simple (description from this post):

--apply-replaygain-which-is-not-lossless[=<specification>]

where <specification> has the format:

[<preamp>][a|t][l|L][n{0|1|2|3}]

<preamp>    Floating point dB to add to the existing gain.
a|t         Album (a) or track (t) gain.
l|L         Peak-limit (l) or 6dB hard limiter (L)
n{0|1|2|3}  Noise shaping from none (0) to strong (3) when
            dithering back to integer amplitudes.

The default is 0aLn1. If you look in the flac source, this documentation is in doc/html/documentation_tools_flac.html (where it is commented out).

Posted Sat Dec 11 18:33:27 2010 Tags: fun