Shallow Thoughts : : Aug
Akkana's Musings on Open Source Computing and Technology, Science, and Nature.
Wed, 28 Aug 2013
Python on Android. Wouldn't that make so many things so much easier?
I've known for a long time about
SL4A, but
when I read, a year or two ago, that Google officially disclaimed
support for languages other than Java and C and didn't want their
employees working on projects like SL4A, I decided it wasn't a good bet.
But recently I heard from someone who had just discovered SL4A and
its Python support and talked about it like a going thing. I had an
Android scripting problem I really wanted to solve, and decided it
was time to take another look.
It turns out SL4A and its Python interpreter are still being
maintained, and indeed, I was able to solve my problem that way.
But the documentation was scanty at best. So here are some shortcuts.
Getting Python running on Android
How do you install it in the first place? Took me three or four tries:
it turns out it's extremely picky about the order in which you do
things, and the documentation doesn't warn you about that.
Follow these steps:
- Enable "Unknown Sources" under Application settings if you haven't already.
- Download both sl4a_r6.apk and PythonForAndroid_r4.apk
- Install sl4a from the apk. Do not install Python yet.
- Find SL4A in Applications and run it. It will say "no matches found"
(i.e. no scripts)
but that's okay: the important thing is that it creates the directory
where the scripts will live,
/sdcard/sl4a/scripts, without which PythonForAndroid would fail to install.
- Install PythonForAndroid from the apk.
- Find Python for Android in Applications and run it. Tap Install.
This will install the sample scripts, and you'll be ready to go.
Make a shortcut on the home screen:
You've written a script and it does what you want. But to run it, you
have to run SL4A, choose the Python interpreter, scroll around to find
the script, tap on it, and indicate whether or not you want to see
the console. Way too many steps!
Turns out you can make a shortcut on the home screen to an SL4A
script, like this:
(thanks to this
tip):
- Hit the add icon button ("+") on the main screen.
- tap on Shortcuts
- scroll down to Scripts
- choose your script
- choose the icon indicating whether you want to show the console
while the script is running
This will give you the familiar twin-snake Python icon on your home screen.
There doesn't seem to be any way to change this to a different icon.
Wait, what about UI?
Well, that still seems to be a big hole in the whole SL4A model.
You can write great scripts that print to the console. You can even
do a few specialized things, like popup menus, messages (what the
Python Android module calls makeToast()) and notifications.
The test.py sample script is a great illustration of how
to use all those features, plus a lot more.
But what if you want to show a window, put a few buttons in it,
let the user control things? Nobody seems to have thought about
that possibility. I mean, it's not "sorry, we haven't had time to
implement this", it isn't even mentioned as something someone would
want to do on an Android device. Boggle.
The only possibility I've found is that there is apparently a way to use
Android's
WebView class from Python.
I have not tried this yet; when I do, I'll write it up separately.
WebView may not be the best way to do UI. I've spent many hours
tearing my hair out over its limitations even when called from Java.
But still, it's something. And one very interesting thing about it
is that it provides an easy way to call up an HTML page, either local
or remote, from an Android home screen icon. So that may be the best
reason yet to check out SL4A.
Tags: android, python, programming
[
22:31 Aug 28, 2013
More programming |
permalink to this entry |
]
Sat, 24 Aug 2013
I love shell pipelines, and flatter myself that I'm pretty good at them.
But a discussion last week on the Linuxchix Techtalk mailing list
on finding added lines in a file
turned up a
terrific
bash/zsh shell redirection trick I'd never seen before:
join -v 2 <(sort A.txt) <(sort B.txt)
I've used backquotes, and their cognate $(), plenty. For instance,
you can do things like
PS1=$(hostname):
or PS1=`hostname`:
to set your prompt to the current hostname: the shell runs the
hostname command, takes its output, and substitutes that output in place
of the backquoted or parenthesized expression.
But I'd never seen that <(...) trick before, and immediately saw
how useful it was. Backquotes or $() let you replace arguments
to a command with a program's output -- they're great for generating
short strings for programs that take all their arguments on the
command line. But they're no good for programs that need to read a file,
or several files.
<(...) lets you take the output of a command and pass it to a program
as though it was the contents of a file. And if you can do it more
than once in the same command -- as in Little Girl's example --
that could be tremendously useful.
Playing with it to see if it really did what it looked like it did,
and what other useful things I could do with it,
I tried this (and it worked just fine):
$ diff <(echo hello; echo there) <(echo hello; echo world)
2c2
< there
---
> world
It acts as though I had two files, which each have "hello" as their
first line; but one has "there" as the second line, while the other
has "world". And diff shows the difference.
I don't think there's any way of doing anything like that with backquotes;
you'd need to use temp files.
Of course, I wanted to read more about it -- how have I gone all these
years without knowing about this? -- and it looks like I'm not the
only one who didn't know about it. In fact, none of the pages I found
on shell pipeline tricks even mentioned it.
It turns out it's called "process substitution" and I found it
documented in
Chapter 23
of the Advanced Bash-Scripting Guide.
I tweeted it, and a friend who is a zsh master gave me some
similar cool tricks. For instance, in zsh
echo hi > >(cat) > >(cat -n)
lets you pipe
the output of a command to more than one other command.
That's zsh, but in bash (or zsh too, of course), you can use >() and
tee to do the same thing: echo hi | tee >(cat) | cat -n
If you want a temp file to be created automatically, one you can both
read and write, you can use =(foo) (zsh only?)
Great stuff!
Some other pages that discuss some of these tricks:
Tags: shell, cmdline, zsh, bash
[
19:23 Aug 24, 2013
More linux/cmdline |
permalink to this entry |
]
Tue, 20 Aug 2013
A few days ago I wrote about how I use
making
waypoint files for a list of house addresses is OsmAnd.
For waypoint files, you need latitude/longitude coordinates, and I was
getting those from a web page that used the online Google Maps API to
convert
an address into latitude and longitude coordinates.
It was pretty cool at first, but pasting every address into the
latitude/longitude web page and then pasting the resulting coordinates
into the address file, got old, fast.
That's exactly the sort of repetitive task that computers are supposed
to handle for us.
The lat/lon page used Javascript and the Google Maps API.
and I already had a Google Maps API key (they have all sorts of fun
APIs for map geeks) ... but I really wanted something
that could run locally, reading and converting a local file.
And then I discovered the
Python googlemaps
package. Exactly what I needed! It's in the Python Package Index,
so I installed it with pip install googlemaps
.
That enabled me to change my
waymaker
Python script: if the first line of a
description wasn't a latitude and longitude, instead it looked for
something that might be an address.
Addresses in my data files might be one line or might be two,
but since they're all US addresses, I know they'll end with a
two-capital-letter state abbreviation and a 5-digit zip code:
2948 W Main St. Anytown, NM 12345.
You can find that with a regular expression:
match = re.search('.*[A-Z]{2}\s+\d{5}$', line)
But first I needed to check whether the first line of the entry was already
latitude/longitude coordinates, since I'd already converted some of
my files. That uses another regular expression. Python doesn't seem
to have a built-in way to search for generic numeric expressions
(containing digits, decimal points or +/- symbols) so I made one,
since I had to use it twice if I was searching for two numbers with
whitespace between them.
numeric = '[\+\-\d\.]'
match = re.search('^(%s+)\s+(%s+)$' % (numeric, numeric),
line)
(For anyone who wants to quibble, I know the regular expression
isn't perfect.
For instance, it would match expressions like 23+48..6.1-64.5.
Not likely to be a problem in these files, so I didn't tune it further.)
If the script doesn't find coordinates
but does find something that looks like an address, it feeds the
address into Google Maps and gets the resulting coordinates.
That code looks like this:
from googlemaps import GoogleMaps
gmaps = GoogleMaps('YOUR GOOGLE MAPS API KEY HERE')
try:
lat, lon = gmaps.address_to_latlng(addr)
except googlemaps.GoogleMapsError, e:
print "Oh, no! Couldn't geocode", addr
print e
Overall, a nice simple solution made possible with python-googlemaps.
The full script is on github:
waymaker.
Tags: GIS, mapping, geolocation, geocaching, python, househunting, programming, regexp
[
12:24 Aug 20, 2013
More mapping |
permalink to this entry |
]
Sun, 18 Aug 2013
Shallow Thoughts has been nominated as a competitor in
round two of the
Foss
Force Best Personal Linux or FOSS Blog Competition.
There are plenty of excellent blogs on the list and I'm flattered to
be included.
If you have a moment, take a look and vote for your favorite
(whether or not it's Shallow Thoughts). You can vote for up to two.
If nothing else, it's a good excuse to check out some excellent articles
on free software from a variety of writers.
Voting ends on Monday.
Tags: blogging
[
12:05 Aug 18, 2013
More blogging |
permalink to this entry |
]
I was awakened at 6:30 this morning by what sounded like a young house
finch learning to sing, just outside my window.
It got me thinking.
Every fall, songbirds which have stopped singing during high summer start
up again, briefly, to sing for a few weeks before weather gets cold.
A discussion several years ago on a local birding list concluded that
nobody knows for sure why birds sing in autumn -- are they
confused about the weather and think it's spring again, hoping
for a last fling before the cold weather sets in, or what?
There's a
a wonderful ditty about it, "The Autumnal Recrudescence of the Amatory Urge",
apparently written in the 1970s by Susan Stiles.
It's too early in the year right now for autumnal anything --
it's still quite warm.
But lying there in bed listening to the exploratory notes of a bird clearly
not yet confident in his song, I got to thinking about how birds
learn their songs.
In most birds it's not innate:
young male birds learn singing while still nestlings from listening to
their father sing, much like human babies learn the rhythms of their
native language from hearing their parents talk;
and if you raise a songbird in a nest of another species, they will
often learn the wrong song, or end up with some hybrid song that
doesn't attract females of either species. (A good overview:
The
Development of Birdsong on Nature.)
More recently, there have been all sorts of interesting studies on how
young birds learn their local dialect, since a species' song varies
quite a bit from one location to another.
But ... not all birds sing much once the eggs are laid, do they?
They sing their hearts out while acquiring a territory and trying to
attract a female; but once nesting starts, I don't remember hearing much
activity from the house finches. Mockingbirds are an exception:
I've seen mockers singing day and night even after they're feeding
nestlings, though not all male mockers are quite so industrious.
But I thought most species stopped singing much once the nest was
built and eggs laid.
But if that's true, when do the young males learn their songs?
Even if the father does sing a little, off and on, while the nestlings
are being raised, that's not very much time to learn.
Suppose the adults started singing again in the fall
before the family disperses. Wouldn't that be an advantage to the
young males who are just learning their songs? If a fledgling,
off the nest and mostly able to care for himself, is "babbling",
trying exploratory notes while learning what sounds he can make,
wouldn't it be helpful to have a few nearby males who occasionally
burst into song even if it's out of season?
Maybe the "Autumnal Recrudescence" isn't birds being confused about
the weather at all. Maybe it's an evolutionary aid to help the young
birds crystallize their songs before heading into their first winter.
By singing in autumn, the males help their sons crystallize their
songs for the next year, which helps the sons be more successful
when it's time to look for a mate next spring.
Just a theory ... but I think it makes some sense, and I'll be listening
to this autumn's chorus with new interest.
Tags: birds, nature
[
11:57 Aug 18, 2013
More nature/birds |
permalink to this entry |
]
Fri, 16 Aug 2013
Dave and I have been doing some exploratory househunting trips,
and one of the challenges is how to maintain a list of houses and
navigate from location to location. It's basically like geocaching,
navigating from one known location to the next.
Sure, there are smartphone apps to do things like "show houses for
sale near here" against a Google Maps background. But we didn't want
everything, just the few gems we'd picked out ahead of time.
And some of the places we're looking are fairly remote -- you can't
always count on a consistent signal everywhere as you drive around,
let alone a connection fast enough to download map tiles.
Fortunately, I use a wonderful open-source Android program called
OsmAnd.
It's the best, bar none, at offline mapping: download data files
prepared from OpenStreetMap
vector data, and you're good to go, even into remote areas with no
network connectivity. It's saved our butts more than once exploring
remote dirt tracks in the Mojave. And since the maps come from
OpenStreetMap, if you find anything wrong with the map, you can fix it.
So the map part is taken care of. What about that list of houses?
Making waypoint files
On the other hand, one of OsmAnd's many cool features is that it can
show track logs. I can upload a GPX file from my Garmin, or record a
track within OsmAnd, and display the track on OsmAnd's map.
GPX track files can include waypoints. What if I made a GPX file
consisting only of waypoints and descriptions for each house?
My husband was already making text files of potentially interesting houses:
404 E David Dr
Flagstaff, AZ 86001
$355,000
3 Bed 2 Bath
1,673 Sq Ft
0.23 acres
http://blahblah/long_url
2948 W Wilson Dr
Flagstaff, AZ 86001
$285,000
3 Bed 2 Bath
1,908 Sq Ft
8,000 Sq Ft Lot
http://blahblah/long_url
... (and so on)
So I just needed to turn those into GPX.
GPX is a fairly straightforward XML format -- I've parsed GPX files
for pytopo
and for ellie,
and generating them from Python should be easier than parsing.
But first I needed latitude and longitude coordinates.
A quick web search solved that: an excellent page called
Find
latitude and longitude with Google Maps.
You paste the address in and it shows you the location on a map
along with latitude and longitude. Thanks to Bernard Vatant at Mondeca!
For each house, I copied the coordinates directly from the page
and pasted them into the file. (Though that got old after about the fifth
house; I'll write about automating that step in a separate article.)
Then I wrote a script called
waymaker
that parses a file of coordinates and descriptions and makes waypoint files.
Run it like this: waymaker infile.txt outfile.gpx
and it will create (or overwrite) a gpx file consisting of those waypoints.
Getting it into OsmAnd
I plugged my Android device into my computer's USB port, mounted it as
usb-storage and copied all the GPX files into osmand/tracks
(I had to create the tracks subdirectory myself, since I hadn't
recorded any tracks. After restarting OsmAnd, it was able to see all
the waypoint files.
OsmAnd has a couple of other ways of showing points besides track files.
"Favorites" lets you mark a point on the map and save it to various
Favorites categories. But although there's a file named favorites.gpx,
changes you make to it never show up in the program. Apparently they're
cached somewhere else. "POI" (short for Points of Interest) can be
uploaded, but only as a .obf OsmAnd file or a .sqlitedb database, and
there isn't much documentation on how to create either one.
GPX tracks seemed like the easiest solution, and I've been happy
with them so far.
Update: I asked on the osmand mailing list; it turns out that on the
Favorites screen (Define View, then Favorites) there's a Refresh
button that makes osmand re-read favorites.gpx. Works great.
It uses pretty much the same format as track files -- I took
<wpt></wpt> sequences I'd generated with waymaker and
added them to my existing favorites.gpx file, adding appropriate
categories. It's nice to have two different ways to display and
categorize waypoints within the app.
Using waypoints in OsmAnd
How do you view these waypoints once they're loaded?
When you're in OsmAnd's map view, tap the menu button and choose
Define View, then GPX track...
You'll see a list of all your GPX files; choose the one you want.
You'll be taken back to the map view,
at a location and zoom level that shows all your waypoints. Don't
panic if you don't see them immediately; sometimes I needed
to scroll and zoom around a little before OsmAnd noticed there were
waypoints and started drawing them.
Then you can navigate in the usual way. When you get to a waypoint,
tap on it to see the description brieftly -- I was happy to find that
multiple line descriptions work just fine. Or long-press on it to pop up a
persistent description window that will stay up until you dismiss it.
It worked beautifully for our trip, both for houses and for
other things like motels and points of interest along the way.
Tags: mapping, GIS, geocaching, househunting, programming, osmand, pytopo
[
15:58 Aug 16, 2013
More mapping |
permalink to this entry |
]
Sun, 11 Aug 2013
Want to get started controlling hardware from your BeagleBone Black?
I've found a lot of the documentation and tutorials a little sketchy,
so here's what I hope is a quick start guide.
I used the
Adafruit
Python GPIO library for my initial hacking.
It's easy to set up:
once you have your network set up, run these commands:
opkg update && opkg install python-pip python-setuptools python-smbus
pip install Adafruit_BBIO
Pinout diagrams
First, where can you plug things in? The BBB has two huge header blocks,
P8 and P9,
but trying to find pinout diagrams for them is a problem. Don't blindly
trust any diagram you find on the net; compare it against several
others, and you may find there are big differences. I've found a
lot of mislabeled BBB diagrams out there.
The best I've found so far are the two tables at
elinux.org/BeagleBone.
No pictures, but the tables are fairly readable and seem to be
correct.
The
official
BeagleBone Black hardware manual is the reference you're actually
supposed to use.
It's a 121-page PDF full of incomprehensible and unexplained abbreviations.
Good luck! The pin tables for P8 and P9 are on pp. 80 and 82.
P8 and P8 are identified on p. 78.
Blinking an LED: basic GPIO output
For basic GPIO output, you have a wide choice of pins.
Use the tables to identify power and ground, then pick a GPIO pin
that doesn't seem to have too many other uses.
The Adafruit library can identify pins either by their location
on the P8 and P9 headers, e.g. "P9_11", or by GPIO number, e.g.
"GPIO0_26". Except -- with the latter designation, what's that extra
zero between GPIO and _26? Is it always 0? Adafruit doesn't explain it.
So for now I'm sticking to the PN_NN format.
I plugged my LED and resistor into ground (there are lots of ground
terminals -- I used pin 0 on the P9 header) and pin 11 on P9.
It's one line to enable it, and then you can turn it on and off:
import Adafruit_BBIO.GPIO as GPIO
GPIO.setup("P9_11", GPIO.OUT)
GPIO.output("P9_11", GPIO.HIGH)
GPIO.output("P9_11", GPIO.LOW)
GPIO.output("P9_11", GPIO.HIGH)
Or make it blink:
import time
while True:
GPIO.output("P9_11", GPIO.HIGH)
time.sleep(.5)
GPIO.output("P9_11", GPIO.LOW)
time.sleep(.5)
Fading an LED: PWM output
PWM is harder. Mostly because it's not easy to find out which pins
can be used for GPIO. All the promotional literature on the BBB says
it has 8 GPIO outputs -- but which ones are those?
If you spend half an hour searching for "pwm" in that long PDF manual
and collecting a list of pins with "pwm" in their description,
you'll find 13 of them on P9 and 12 on P8. So that's no help.
After comparing a bunch of references and cross-checking pin numbers
against the descriptions in the hardware manual, I think this is the list:
P9_14 P9_16 P9_21 P9_22 P9_28 P9_31
P8_13 P8_19
I haven't actually verified all of them yet, though.
Once you've found a pin that works for PWM, the rest is easy.
Start it and set an initial frequency with PWM.start(pin, freq),
and then you can change the duty cycle with set_duty_cycle(pin, cycle)
where cycle is a number between 0 and 100. The duty cycle
is the reverse of what you might expect: if you have an LED plugged
in, a duty cycle of 0 will be brightest, 100 will be dimmest.
You can also change the frequency with PWM.set_frequency(pin, freq).
I'm guessing freq is in Hertz, but they don't actually say.
When you're done, you should call PWM.stop() and PWM.cleanup().
Here's how to fade an LED from dim to bright ten times:
import Adafruit_BBIO.PWM as PWM
PWM.start("P9_14", 50)
for j in range(10):
for i in range(100, 0, -1):
PWM.set_duty_cycle("P9_14", i)
time.sleep(.02)
PWM.stop("P9_14")
PWM.cleanup()
Tags: hardware, linux, beaglebone, maker
[
13:36 Aug 11, 2013
More hardware |
permalink to this entry |
]