Another thumb-twiddling commute into the city, with only another listicle to entertain, and once again, no mobile signal. I tweet in frustration:
— James Uther (@hemul) June 29, 2015
Which received an actual reply!
So, I have an android phone, and android can allow access to all sorts of link status information. You can add handlers to an Activity to listen to signal strength changes, cell changes, and so on. Now, I could write something myself that logs these along with the GPS location, but surely someone has already done this? Yes!
My return journey duely tracked (with extra power supplied by a laptop – gods this thing chews batteries!) it turns out that the signal tracker can generate a few kinds of files, including some funky KML which would show this:
Including blue wedge things that I don’t understand
Anyway, it’ll also give good old CSVs, which look like:
1,51495581,-115382,-65,3 Jul 2015 17:22:55,234,20,99,16756,0,0,HSPA+,0,IDLE,No,CONNECTED,INOUT,LGE,Nexus 4,54273,13992,54273,13992,-1,,
2,51495479,-115432,-81,3 Jul 2015 17:22:59,234,20,99,16756,0,0,HSPA+,0,IDLE,No,CONNECTED,INOUT,LGE,Nexus 4,18893,39644,18893,39644,-1,,
Break out ipython notebook
%matplotlib inline import math import pandas as pd import matplotlib.pyplot as plt import matplotlib import numpy as np
Let’s pull in the csv file, and in particular the latitude, longitude and rssi columns, where rssi is a measure of signal strength.
pd.set_option('display.mpl_style', 'default') plt.rcParams['figure.figsize'] = (15, 3) plt.rcParams['font.family'] = 'sans-serif’ trip = pd.read_csv('export_150703175551.csv', usecols=['rssi','logdate','latitude','longitude']) trip['longitude'] = trip['longitude']/1000000 trip['latitude'] = trip['latitude']/1000000 trip[:2]
latitude longitude rssi logdate 0 51.495581 -0.115382 -65 3 Jul 2015 17:22:55 1 51.495479 -0.115432 -81 3 Jul 2015 17:22:59
So we have some areas of significantly bad signal strength. Let’s focus in on rssi < -70, which in the iPhone at least triggers searching for a new cell tower.
bad_signal = trip[trip['rssi'] < -70] bad_signal = bad_signal.drop('logdate', 1) r_g = (matplotlib.colors.colorConverter.colors['r'], matplotlib.colors.colorConverter.colors['g']) cmap = matplotlib.colors.LinearSegmentedColormap.from_list('clist', r_g) bad_signal.plot(kind='scatter', x='longitude', y='latitude', c='rssi', cmap=cmap)
We know have a map of points on my trip with bad signal. But they want postcodes. I have the lat/lng position of these, and could just go for a reverse geocoder, but that’d be 400ish requests and probably get my blacklisted, so instead, i’ll just get ALL THE POSTCODZ!
postcodes = pd.read_csv('ONSPD_MAY_2015_UK.csv', header=0, usecols=['pcd','lat','long']) postcodes[:2]
pcd lat long 0 AB1 0AA 57.101474 -2.242851 1 AB1 0AB 57.102554 -2.246308
So we have something like a million postcodes and their central lat/lng. I could do something fancy, but ‘when in doubt, use brute force’ so for each of my points i’ll grab the postcodes from the list that are within a delta of that point, then from that subset find the closest to that point, and consider that to be the postcode for that point. And then we want a set of all those postcodes:
def get_near_pcodes(lat, lng, p): '''Whittle down full list to those within factor of lat,lng''' d = 0.01 return p[(p['lat'] > lat-d) & (p['lat'] < lat+d) & (p['long'] > lng-d) & (p['long'] < lng+d)].copy() def dist(x1, y1, x2, y2): '''dist from x1,y1 to x2,y2''' x = abs(x2-x1) x = x*x y = abs(y2-y1) y = y*y return math.sqrt(x + y) def dodist(s): '''Return the nearest postcode to the given lat,lng''' y = s['longitude'] x = s['latitude'] np = get_near_pcodes(x, y, postcodes) # add a 'd' column, the distance between x,y and the postcode withd = np.apply(lambda l: pd.Series([l['pcd'],dist(l['lat'], l['long'], x, y)],index=['pcd','d']),axis=1) # and find the minimum one. p = withd[withd['d'] == withd['d'].min()].iloc[0,0] return p pcds = bad_signal.apply(dodist,axis=1) set(pcds.values)
 once the signal returns