Latest posts for tag openmoko
Getting dbus signatures right from Vala
I am trying to play a bit with Vala on the FreeRunner.
The freesmartphone.org stack on the OpenMoko is heavily based on DBus. Using DBus from Vala is rather simple, if mostly undocumented: you get a few examples in the Vala wiki and you make do with those.
All works fine with simple methods. But what with providing callbacks to
signals that have complex nested structures in their signatures, like aa{sv}
?
You try, and then if you don't get the method signature right, the signal is
just silently not delivered because it does not match the method signature.
So this is how to provide a callback to
org.freesmartphone.Usage.ResourceChanged
, with signature sba{sv}
:
public void on_resourcechanged(dynamic DBus.Object pos,
string name,
bool state,
HashTable<string, Value?> attributes)
{
stderr.printf("Resource %s changed\n", name);
}
And this is how to provide a callback to
org.freesmartphone.GPS.UBX.DebugPacket
, with signature siaa{sv}
:
protected void on_ubxdebug_packet(dynamic DBus.Object ubx, string clid, int length,
HashTable<string, Value?>[] wrongdata)
{
stderr.printf("Received UBX debug packet");
// Ugly ugly work-around
PtrArray< HashTable<string, Value?> >* data = (PtrArray< HashTable<string, Value?> >)wrongdata;
stderr.printf("%u elements received", data->len);
}
What is happening here is that the only method signature that I found matching the dbus signature is this one. However, the unmarshaller for some reason gets it wrong, and passes a PtrArray instead of a HashTable array. So you need to cast it back to what you've actually been passed.
Figuring all this out took several long hours and was definitely not fun.
Mapping using the Openmoko FreeRunner headset
The FreeRunner has a headset which includes a microphone and a button. When doing OpenStreetMap mapping, it would be very useful to be able to keep tangogps on the display and be able to mark waypoints using the headset button, and to record an audio track using the headset microphone.
In this way, I can use tangogps to see where I need to go, where it's already mapped and where it isn't, and then I can use the headset to mark waypoints corresponding to the audio track, so that later I can take advantage of JOSM's audio mapping features.
Enter audiomap:
$ audiomap --help
Usage: audiomap [options]
Create a GPX and audio trackFind the times in the wav file when there is clear
voice among the noise
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-v, --verbose verbose mode
-m, --monitor only keep the GPS on and monitor satellite status
-l, --levels only show input levels
If called without parameters, or with -v
which is suggested, it will:
- Fix the mixer settings so that it can record from the headset and detect headset button presses.
- Show a monitor of GPS satellite information until it gets a fix.
- Synchronize the system time with the GPS time so that the timestamps of the files that are created afterwards are accurate.
- Start recording a GPX track.
- Start recording audio.
- Record a GPX waypoint for every headset button press.
When you are done, you stop audiomap with ^C
and it will properly close the
.wav
file, close the tags in the GPX waypoint and track files and restore the
mixer settings.
You can plug the headset out and record using the handset microphone, but then you will not be able to set waypoints until you plug the headset back in.
After you stop audiomap
, you will have a track, waypoints and .wav
file
ready to be loaded in JOSM.
Big thanks go to Luca Capello for finding out how to detect headset button presses.
Simple tool to query the GPS using the OpenMoko FSO stack
I was missing a simple command line tool that allows me to perform basic GPS queries in shellscripts.
Enter getgps:
# getgps --help
Usage: getgps [options]
Simple GPS query tool for the FSO stack
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-v, --verbose verbose mode
-q, --quiet suppress normal output
--fix check if we have a fix
-s, --sync-time set system time from GPS time
--info get all GPS information
--info-connection get GPS connection information
--info-fix get GPS fix information
--info-position get GPS position information
--info-accuracy get GPS accuracy information
--info-course get GPS course information
--info-time get GPS time information
--info-satellite get GPS satellite information
So finally I can write little GPS-aware scripts:
if getgps --fix -q
then
start_gps_aware_program
else
start_gps_normal_program
fi
Or this.
Voice-controlled waypoints
I have it in my TODO list to implement taking waypoints when pressing the headset button of the openmoko, but that is not done yet.
In the meantime, I did some experiments with audio mapping, and since I did not manage to enter waypoints while recording them, I was looking for a way to make use of them anyway.
Enter findvoice:
$ ./findvoice --help
Usage: findvoice [options] wavfile
Find the times in the wav file when there is clear voice among the noise
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-v, --verbose verbose mode
-p NUM, --percentile=NUM
percentile to use to discriminate noise from voice
(default: 90)
-t, --timestamps print timestamps instead of human readable information
You give it a wav file, and it will output a list of timestamps corresponding to where it things that you were talking clearly and near the FreeRunner / voice recorder instead of leaving the recorder dangling to pick up background noise.
Its algorithm is crude and improvised because I have no background whatsoever in audio processing, but it basically finds those parts of the audio file where the variance of the samples is above a given percentile: the higher the percentile, the less timestamps you get; the lower the percentile, the more likely it is that it picks a period of louder noise.
For example, you can automatically extract waypoints out of an audio file by using it together with Geocoding Unix timestamps:
./findvoice -t today.wav | ./gpxinterpolate today.gpx > today-waypoints.gpx
The timestamps it outputs are computed using the modification time of the
.wav
file: if your system clock was decently synchronised (which you can do
with getgps), then the mtime of the wav is the time of the end of the
recording, which gives the needed reference to compute timestamps that are
absolute in time.
For example:
getgps --sync-time
arecord file.wav
^C
./findvoice -t file.wav | ./gpxinterpolate today.gpx > today-waypoints.gpx
Geocoding Unix timestamps
Geocoding EXIF tags in JPEG images is fun, but there is more that can benefit from interpolating timestamps over a GPX track.
Enter gpxinterpolate:
$ ./gpxinterpolate --help
Usage: gpxinterpolate [options] gpxfile [gpxfile...]
Read one or more GPX files and a list of timestamps on standard input. Output
a GPX file with waypoints at the location of the GPX track at the given
timestamps.
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-v, --verbose verbose mode
For example, you can create waypoints interpolating file modification times:
find . -printf "%Ts %p\n" | ./gpxinterpolate ~/tracks/*.gpx > myfiles.gpx
In case you wonder where you were when you modified or accessed a file, now you can find out.
Recording audio on the FreeRunner
The FreeRunner can record audio. It is nice to record audio: for example I can run the recording in background while I keep tangogps in the screen, and take audio notes about where I am while I am doing mapping for OpenStreetMap.
Here is the script that I put together to create geocoded audio notes:
#!/bin/sh
WORKDIR=~/rec
TMPINFO=`mktemp $WORKDIR/info.XXXXXXXX`
# Sync system time and get GPS info
echo "Synchronising system time..."
getgps --sync-time --info > $TMPINFO
# Compute an accurate basename for the files we generate
BASENAME=~/rec/rec-$(date +%Y-%m-%d-%H-%M-%S)
# Then give a proper name to the file with saved info
mv $TMPINFO $BASENAME.info
# Proper mixer settings for recording
echo "Recording..."
alsactl -f /usr/share/openmoko/scenarios/voip-handset.state restore
arecord -D hw -f cd -r 8000 -t wav $BASENAME.wav
echo "Done"
It works like this:
- It synchronizes the system time from the GPS (if there is a fix) so that the timestamps on the wav files will be as accurate as possible.
- It also gets all sort of information from the GPS and stores them into a file, should you want to inspect it later.
- It records audio until it gets interrupted.
The file name of the files that it generates corresponds to the beginning of the recording. The mtime of the wav file obviously corresponds to the end of the recording. This can be used to later georeference the start and end point of the recording.
You can use this to check mixer levels and that you're actually getting any input:
arecord -D hw -f cd -r 8000 -t wav -V mono /dev/null
The getgps script is now described in its own post.
You may now want to experiment, in JOSM, with "Preferences / Audio settings / Modified times (time stamps) of audio files".
How to read the Freerunner's accelerometers
This code has been take from moko_eightball by Jakob Westhoff: it just continuously prints the value of the three accelerometers.
#include <stdio.h>
#include <stdint.h>
void processInputEvents(FILE* in)
{
int x = 0, y = 0, z = 0;
while (1)
{
char padding[16];
uint16_t type, code;
int32_t value;
// Skip the timestamp
fread(padding, 1, 8, in);
// Read the type
fread(&type, 1, 2, in);
// Read the code
fread(&code, 1, 2, in);
// Read the value
fread(&value, 1, 4, in);
switch( type )
{
case 0:
switch( code )
{
case 0:
fprintf(stdout, "x%d y%d z%d\n", x, y, z);
break;
default:
//warning( "Unknown code ( 0x%02x ) for type 0x%02x\n", code, type );
break;
}
break;
case 2:
switch ( code )
{
case 0:
// Update to the new value
x = value;
break;
case 1:
// Update to the new value
y = value;
break;
case 2:
// Update to the new value
z = value;
break;
default:
//warning( "Unknown code ( 0x%02x ) for type 0x%02x\n", code, type );
break;
}
break;
default:
//warning( "Unknown type ( 0x%02x ) in accelerometer input stream\n", type );
break;
}
}
}
int main()
{
FILE* in = fopen("/dev/input/event2", "r");
processInputEvents(in);
fclose(in);
return 0;
}
Docking the FreeRunner into the laptop
Earlier in the day, I wrote:
So yes, my laptop can now be turned into a phone charger with networking, DNS and apt cache services. I shall look into hooking that script into dbus to have it run automatically when the phone is plugged and unplugged.
It's not dbus, it's udev, and I've managed to do it.
It's an udev rule:
# cat /etc/udev/rules.d/z60_openmoko_net.rules
ACTION=="add", SUBSYSTEM=="net", ATTRS{idVendor}=="1457", ATTRS{idProduct}=="5122", RUN+="/root/bin/share-openmoko"
ACTION=="remove", SUBSYSTEM=="net", INTERFACE="usb0", RUN+="/root/bin/share-openmoko"
And a script:
# cat bin/share-openmoko
#!/bin/sh
if [ "$ACTION" == "add" ]
then
ACTION=start
fi
if [ "$ACTION" == "remove" ]
then
ACTION=stop
fi
INTERFACE=${INTERFACE:-"usb0"}
ACTION=${ACTION:-"$1"}
case "$ACTION" in
start)
logger -t openmoko "Connected, setting up network"
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE -s 192.168.0.200/29
ifconfig usb0 192.168.0.200 netmask 255.255.255.248
/etc/init.d/dnsmasq start
echo 1 > /proc/sys/net/ipv4/ip_forward
;;
stop)
logger -t openmoko "Disconnected, bringing down network"
echo 0 > /proc/sys/net/ipv4/ip_forward
/etc/init.d/dnsmasq stop
iptables -t nat -F POSTROUTING
ifconfig usb0 down
;;
esac
The script has extra cruft that makes it double as a udev script and as a init.d style script, just because I didn't feel like abandoning the init.d style interface. However, udev handles it perfectly, so there's probably no use at all for the start/stop part.
This means that the interface and all supporting services will be brought up and down when the phone is connected/disconnected, and also when the phone is suspended/resumed. Of course, more fancy things can be plugged into the script, like syncing PIM info, file systems, turning on applets in panels, mounting phone file systems and whatnot.
Logging on the Freerunner
By default, the FreeRunner comes without a system log daemon, and rightfully so, because you don't want a daemon filling up /var, and a nightly cron job to rotate logs, on a mobile phone.
However, I do not want to give up logging. If there are bugs I want to investigate, I like to be able to look at the logs.
The solution: remote logging. Configure the phone to send all log info via UDP to my laptop, and configure my laptop to listen for UDP syslog messages from the phone only.
How to do it:
apt-get install rsyslog
(see below how other logging daemons fail)- Add a
/etc/hosts
entry for the laptop on the phone: 192.168.0.200 base - Add a
/etc/hosts
entry for the phone on the laptop: 192.168.0.202 openmoko -
Replace the phone's rsyslog rules to send everything to the laptop:
################ ##### RULES #### ################ # Everything goes to the main computer, via UDP, if available *.* @base # # Emergencies are sent to everybody logged in. # *.emerg *
-
Tell the laptop's rsyslog to accept UDP messages from the phone only:
# provides UDP syslog reception $ModLoad imudp $UDPServerRun 514 $UDPServerAddress 192.168.0.200
That is all. When the phone is not connected, the UDP packets from the logger will go nowhere. When it gets connected to the laptop, a script is triggered that creates the 192.168.0.200 interface, rsyslogd will listen on that interface and the UDP packets will be logged.
This has the extra potential to be able to get error messages in case of bugs in the SD card driver, which I seem to hit from time to time.
Why rsyslog
sysklogd could receive the messages from the phone, but can only listen on all interfaces, and therefore requires me to set up firewall on my laptop in order to get packets from the phone only.
syslog-ng can and can be told to listen on a given interface, but it will refuse to start if that interface isn't available. Since the phone is on an hotplugged interface that comes and goes, this is totally unacceptable.
And finally, rsyslog is going to replace sysklogd both in Debian and in Fedora, so it's just as well a good excuse to switch.
Running apt on the FreeRunner
I've already mentioned that I'm running approx in the laptop and I configured the FreeRunner to access the laptop's cache. Here are the other customisations needed to have a decently working apt:
# cat /etc/apt/apt.conf.d/99freerunner
APT::Install-Recommends "false";
Acquire::PDiffs "false";
The rationale is that recommends would bloat a system that is supposed to be small, and pdiff requires more CPU, memory and disk space/time than it actually saves in bandwidth.
Thanks to Michael Banck and Peter Palfrader for helping me to find out how to disable pdiffs.