Raspberry Pi Portable 3G/4G Network Quality Logger Project – Part 2

Two months since I posted about it last, I think I’ve finally reached 1.0 of my Raspberry Pi Portable Cell Network Quality Logger (RIPCNQL, rolls off the tongue yeah). It took me a while because I’m a shitcunt at this stuff and I forgot about it for a few weeks. Go read my first post to find out what I’m trying to do here. If you don’t read that first post, this isn’t gonna make much sense. This post is to explain the scripts I’ve written and my setup and to get some feedback. So yeah, read the first post, before reading on.


There’s a few components I’m using to log this info:

  • gpsd (and it’s sister app gpspipe) – which talks to the GPS dongle to grab co-ordinates and speed
  • USB_ModeSwitch – which is vital in order for the Huawei Hilink driverless modem to work
  • bash script to get the modem to connect to the cell network
  • bash script run every 1 minute via cron, which cobbles together the gps info, signal quality info and ping results into a single csv file
  • speedtest-cli – a command line version of the speedtest.net web site, which is a really nice way to do bandwidth testing
  • bash script which runs every 10 minutes via cron, that runs a lightly modified version of speedtest-cli, tacks gps info to the result and pops it into a csv file

Setting up gpsd is relatively easy. This post explains how to set up the GPS device I’m using on the Raspberry Pi. I haven’t had a problem with it and it’s always logged the location of the Pi. I have had a few issues getting locations, but that’s more the fault of the Globalsat ND-100S, which isn’t the best GPS device out there. I’ll explain more about that later.

The first script to run is designed to talk to any Huawei modem using the Hilink technology. The modem has its own firmware which just appears as a network interface on Linux that you grab a DHCP lease from. Unfortunately, it isn’t straightforward to get going – until you know how. After a lot of digging, it’s almost plug and play on Linux using USB_ModeSwitch. Without using USB_ModeSwitch, the modem just presents itself as a virtual CD drive, which is used on Windows for the mobile partner & driver software, which isn’t required on Linux. USB_ModeSwitch sends the proper bits to the modem to tell it to go into “modem” mode and present itself as a network interface you can control like any other. On Windows this is all handled by the Mobile Partner software (that doesn’t exist on Linux). Once the modem mode has been switched, you then use a simple script to send some AT commands to the modem in order to tell it to connect to the network and grab a DHCP lease from the telco.

The tricky thing with this situation is that the modem isn’t always /dev/ttyUSB2, sometimes it’s /dev/ttyUSB1 – I initially thought /dev/gsmmodem would be a good place to point the script at as the device to talk to, but /dev/gsmmodem often flips between either a “diagnostic” device or a “comms” device. As the modem has two seperate interfaces that are both modems, but only one is a network interface, the other is just for software to use to get stats and shit like signal quality, etc.

# waits 10 sec, because if booting from cold, the modem takes a while to initialise - the 3 sec gaps are just to let the modem acknowledge the commands as if they're rapidly sent, they're combined into one command and fail before the previous command is recognised
sleep 10
echo 'AT' > /dev/ttyUSB2
sleep 3
echo 'ATZ' > /dev/ttyUSB2
sleep 3
echo 'ATQ0 V1 E1 S0=0' > /dev/ttyUSB2
sleep 3
# you'd change the live.vodafone.com bit to the APN for your carrier, e.g: "telstra.internet" for Telstra, or "connect" for Optus 4G pre-paid
echo 'AT^NDISDUP=1,1,"live.vodafone.com"' > /dev/ttyUSB2
# waits again for 15 sec for the modem to connect to the network
sleep 15
# assigns an IP address from the telco to the wwan0 interface - which is automatically established by usbswitchmode2/huawei hilink at boot time
sudo dhcpcd wwan0
exit 0

So on boot, that script runs (as part of rc.local) and most of the time, results in a 10.x.x.x IP (on Vodafone at least), which means it worked and can communicate with the Internet. Sometimes it doesn’t manage to get an IP addresses and the dhcp client gives it a 169.x.x.x IP, which means it didn’t work. I honestly don’t know why this happens, as I can run the same script after a reboot again and it grabs a 10.x.x.x IP. So instead of looking into why this happens, I hacked up a nasty script that runs in rc.local as well, that checks to see if the IP address on wwan0 (the modem’s network interface) has a 10. or 169. IP address. If it starts with 169, reboot.

# run ipconfig for wwan0, grep the IP address and use cut and awk to filter out the other shit on the line
ipaddress=$(ifconfig wwan0 | grep 'inet addr:' | cut -d: -f2 | awk '{print $1}')

# check if the ip address has 169 at the start, if it does, reboot
if [[ "$ipaddress" == 169* ]]
        then sudo reboot

As gross as the idea is, it actually works quite well. I once had the Pi reboot itself 4 times in a row until it got the right IP, heh. It’s particularly useful when starting up headless, as I don’t need to manually check if it has an IP before taking the Pi with me anywhere. I can plug it into a battery and be on my way.

This next script runs every 60 seconds to grab the signal info from the modem (RSSI & network tech) as well as do a ping test to see how the latency is. It attaches a time stamp, speed and co-ordinates to this info, so we know when and where the tests were done and if I was standing still, walking or driving/on a train when it was happening.

	# this snippet was taken from this guy's blog: http://thomasloughlin.com/gpspipe-gps-client/
	# gpspipe takes the serial output from an attached GPS device and
gpsdata=$( gpspipe -w -n 10 | grep -m 1 lon )
	# the next 4 lines use jsawk to parse the JSON feed gpspipe spits out
	# latitude
lat=$(echo "$gpsdata" | jsawk 'return this.lat')
	# longitude
lon=$(echo "$gpsdata" | jsawk 'return this.lon')
	# altitude is here, but kinda not important, that's why it's commented out, if I decide I want to include it later
#alt=$(echo "$gpsdata" | jsawk 'return this.alt')
	# the speed is measured in m/s from the GPS device - handy to know if the result was taken whilst walking, driving/train or standing still
speed=$(echo "$gpsdata"  | jsawk 'return this.speed')
	# just creates a timestamp value in the csv file
nowdate=$(date "+%d-%m-%y")
nowtime=$(date "+%H:%M:%S")
	# pings the speedtest server - in this case it's vodafone's melb server 10 times, and use grep to suck out the average of the 10 pings
ping=$(ping speedtest-te1.vodafone.com.au -c 10 | grep avg | cut -d"/" -f5)
	# comgt is a stdin/stdout terminal client which is designed to talk to cell modems, in this case, all it's doing is asking the modem what the signal quality is, which is returned as a value called RSSI
	# grep takes the output and strips away the extraneous info to give just the RSSI
rssi=$(comgt sig -d /dev/tty | grep -Po '[0-9.]+(?=,)')
	# sends the command AT^SYSINFOEX to the modem, which reports back something like this: ^SYSINFOEX:2,3,0,1,,3,"WCDMA",41,"WCDMA" and the grep and sed commands filter out the other info just to give the sub-tech string (the last value in the quotation marks)
tech=$(sudo comgt -s -d /dev/gsmmodem sysex | grep -Po '".*?"' | sed "1 d" | sed "s/\"//g")
	# returns the values out to a csv file
echo "$nowdate,$nowtime,$lat,$lon,$speed,$ping,$rssi,$tech" >> /home/pi/vha_pinglog.csv
exit 0

I’ve modified the speedtest-cli python script to return just numbers (and to not do the ping test) when using the –simple option, which makes it easy to implant to the CSV output. All I did was comment out the ping section of the script where the if statement for the –simple option is, and removed the “Your download speed is!” words from the results. If I knew what the fuck was going on with Python, I could edit the python script to return the values in the exact format I need them, but I’m too much of a dud to know how to do that, so I’m using “tr” to mangle the output to suit the CSV.

The speedtest-cli lets you pre-define which server you want to use too, so I can set the script to use Vodafone’s Melbourne server (2225 is Telstra MEL, 1564 is Optus MEL and 3252 is Vodafone MEL) and save the step of it having to auto-find an appropriate server. I also found that it’s ping results were often incorrect, so I disabled that in the script. The download and upload results are returned in Megabits.

I also added a feature to make the Pi send my iPad a push notification with the speedtest results, using the stupidly simple Pushover app – $5.49 very well spent.


Not only do I get to see the speedtest result as it happens, its also a primitive way to check if it’s working or not – if I don’t get a push notification after 10 minutes or so, something’s fucked.

# time stamp this shit yo
nowdate=$(date "+%d-%m-%y")
nowtime=$(date "+%H:%M:%S")
# stole this idea of using gpspipe, grep and awk from here: http://thomasloughlin.com/gpspipe-gps-client/
gpsdata=$(gpspipe -w -n 10 | grep -m 1 lon)
lat=$(echo "$gpsdata" | jsawk 'return this.lat')
lon=$(echo "$gpsdata" | jsawk 'return this.lon')
gpsspeed=$(echo "$gpsdata"  | jsawk 'return this.speed')
# runs my modified the speedtest-cli python script and uses tr instead of grep to remove some line feeds and change it to a comma to suit the csv
speed=$(python /home/pi/speed.py --simple --server 3252 | tr '\n' ',')
# writes the values to a csv file - the $speed value returns like 5.01,0.03, (note the trailing comma) which is why there's no comma between $speed and $lat
echo "$nowdate,$nowtime,$speed$lat,$lon,$gpsspeed" >> /home/pi/vha_speedlog.csv
# sends info to the Pushover service, which then sends a push notification to my iPad with the results of the speedtest
curl -s \
  -F "token=sekrit" \
  -F "user=sekrit" \
  -F "message=down,up speedtest result : $speed" \
exit 0

That’s it for the software side. This slapdash collection of mongrel scripts gets the job done. Wayyyy easier than manually firing off speed tests on 3x iPhones simultaneously each time I want to do a test. This is a sample of what the raw data looks like:





So it captures the info pretty well, and I want to add a script to back up the CSV files every 30 min or something (it would be a shit to lose the data) via rsync. So it’s not 100% but when is a project ever 100%?

Sometimes things go wrong with the logging, or I want to check up on the Pi and what it’s doing. It’s not as easy as connecting to the Pi over SSH on the wwan0/public IP – because it runs on the cell network, I can’t open the ports as the telco already does NAT on its own end and it’s hidden behind their proxy. So to keep an eye on the Pi, I’ve enlisted the help of a $26 USB powered wi-fi router – Edimax BR-6258nL. I should have got the BR-6258n, which looks more useful with double ethernet ports and a micro USB socket vs. this flimsy flip out USB port that is gonna break in a few days. But the BR-6258nL and its lame firmware is adequate. Besides, I got it from MSY so it’s not like I can get a refund anyways. The beauty of this little guy is that it’s totally USB powered, so it simply plugs into a USB battery and it can operate on the go for however long the battery lasts.


I’ve set the Pi network interface to a static IP, plugged an Ethernet cable between the Pi and the wi-fi router and hook up iPad or iPhone to the wi-fi network created by the Edimax BR-6258nL. If I’m using my laptop, I can just plug it directly in to the Pi as it has an Ethernet port (the iPad and iPhone do not, duh). There’s no internet access on the network, all it’s for is to talk to the Pi. On the Pi, I set wwan0 as the primary interface (using the metric settings in /etc/network/interfaces) and that seems to have done the trick to make sure that the Internet stuff (i.e: pings and speedtests) take place over wwan0 first, leaving the Ethernet port as an admin interface.

That lets me log in on the Ethernet port using the iOS app Prompt (because Panic rock) and check up on what’s happening. I could do more with this and say, publish an RSS feed or something, but that’s too fancy and I’m not a fancy kind of bloke.


The only thing I want to improve is indoor/marginal/city location accuracy. When I was testing in the CBD recently, the GPS was quite inaccurate around the tall buildings and hopeless indoors and totally failed to get a signal in some areas, leaving me with blank entries for location within the CSV file.

Using an iPhone doesn’t have this problem as it can tap into Apple’s Skyhook equivalent (they used to use Skyhook, but now use their own thing) and get at least a rough estimation of where you are based on other location sources. I’ve also used other GPS loggers and if you could see the sky through glass, it would often still give a rough location. The Globalsat ND-100S uses a rather antiquated GPS chipset, SiRF III, which has been superseded many times over. More modern chipsets have better sensitivity, can receive more channels, etc.

As a temporary solution, I can use a GPS logger app (I like Track by 7sols the best) on my 3G iPad (iPad is good because it’s got a big battery and can last a long time logging), export a GPX file, then cross reference the missing location info with the time stamps in the GPX file and my CSV file – but that’s extremely tedious. I want to buy a couple Navisys GR-5013 GNSS receivers, as they use the MTK3333 SoC, which not only is a very sensitive GPS receiver, it also uses GLONASS (the Russian GPS), QZSS (Japan’s GPS – which covers Australia) and Beidou (China’s GPS). It can also tap into Galileo, but there’s no support for Galileo in Australia (and probably wont until 2019). It’s not the same as an iPhone for indoor use, but should be a massive improvement over the dongle I’m using now, and costs only a few bucks more.

Once I’ve filled in the missing gaps in the location info, I can then share the results in a table, with some analysis via Google Fusion Tables, and even overlay it onto a map, which I can’t embed here on WordPress.com because they don’t think that’s appropriate, so here’s a screenshot.


Cool, yeah? Yeah, you know it is. I’m still not 100% sure on the best way to visualise the data, but it’s usefulness is diminished when only doing one telco at a time. The true benefit comes from a head to head comparison. To do that, I need to spend about $1000 to buy more Pi’s, modems, batteries, and credit with the other telcos (Vodafone has been super nice and let me keep using this SIM to do testing with). I think I will try crowdfunding to get the cash together to do some proper three-way tests between Vodafone, Optus and Telstra, in order to properly see what the gap is between the three telcos and find their strengths and weaknesses.

I’d love to hear any feedback you have, particularly in regards to my testing methodology – maybe there’s better ways to gauge network quality than the metrics I’ve chosen? aagius@gmail.com is where to send it.

Comments are closed.


Get every new post delivered to your Inbox.