Skip to content

Commit ce33c86

Browse files
committed
functionalize, package
1 parent 56b9c21 commit ce33c86

File tree

7 files changed

+184
-137
lines changed

7 files changed

+184
-137
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
*.log
2+
*.txt
3+
*.csv
14
*.kml
25
# Byte-compiled / optimized / DLL files
36
__pycache__/

MozLoc.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env python
2+
"""
3+
https://mozilla.github.io/ichnaea/api/geolocate.html
4+
you should get your own Mozilla Location Services API key
5+
6+
Don't abuse the API or you'll get banned (excessive polling rate)
7+
8+
Uses ``nmcli`` from Linux only. Could be extended to other tools and OS.
9+
"""
10+
from mozloc import logwifiloc
11+
12+
13+
if __name__ == '__main__':
14+
"""
15+
output: lat lon [deg] accuracy [m]
16+
"""
17+
import signal
18+
signal.signal(signal.SIGINT, signal.SIG_DFL)
19+
20+
from argparse import ArgumentParser
21+
p = ArgumentParser()
22+
p.add_argument('logfile',help='logfile to append location to',nargs='?')
23+
p.add_argument('-T','--cadence',help='how often to ping [sec]. Some laptops cannot go faster than 30 sec.',
24+
default=60,type=float)
25+
p = p.parse_args()
26+
27+
28+
logwifiloc(p.cadence, p.logfile)

README.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,33 @@ Uses nmcli on Linux in a short, simple Mozilla Location Services with Wifi from
33
Goal was to be as simple as possible.
44
Works with Python 2.7 and 3.
55

6-
## prereqs
6+
## Install
7+
```sh
8+
python -m pip install -e .
9+
```
10+
11+
### prereqs
712
Linux system with NetworkManager (e.g. Ubuntu, Raspberry Pi, etc.).
813

9-
pip install pandas requests
1014

1115

1216
## Usage
1317

1418
./mozloc.py
1519

16-
Returns `dict` containing `lat` `lng` `accuracy`.
17-
In urban areas, accuracy ~ 100 meters.
20+
Returns `dict` containing `lat` `lng` `accuracy` `N BSSIDs heard`.
21+
In urban areas, accuracy ~ 5 - 100 meters.
1822

1923

2024
### convert to KML
2125
You can display your logged data in Google Earth or other KML value after converting by
2226

2327
./csv2kml.py in.log out.kml
24-
28+
2529
with
26-
30+
2731
pip install simplekml
28-
32+
2933

3034
## Contributing
3135
Pull request if you have another favorite approach.
@@ -42,9 +46,9 @@ Would like to add Bluetooth, should be simple.
4246
Debian comes without NetworkManager by default.
4347
Be careful as you lose Wifi password etc. by this procedure
4448

45-
1. Install network manager and remove the old
49+
1. Install network manager and remove the old
4650
```sh
47-
sudo apt install network-manager
51+
sudo apt install network-manager
4852
sudo apt purge dhcpcd5
4953
```
5054
reboot

csv2kml.py

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,6 @@
11
#!/usr/bin/env python
22
"""convert logged positions to KML"""
3-
import numpy as np
4-
from simplekml import Kml
5-
6-
def makekml(t, lonLatAlt, ofn):
7-
"""
8-
write KML track/positions
9-
10-
t: vector of times
11-
lonLatAlt: longitude, latitude, altitude or just lon,lat
12-
ofn: KML filename to create
13-
"""
14-
lonLatAlt = np.asarray(lonLatAlt)
15-
assert lonLatAlt.ndim==2 and lonLatAlt.shape[1] in (2,3), 'Expect Nx2 or Nx3 array'
16-
17-
kml = Kml(name='My Kml',open=1)
18-
19-
if t is not None: # track over time
20-
trk = kml.newgxtrack(name='My Track')
21-
trk.newwhen(t)
22-
trk.newgxcoord(lonLatAlt.tolist()) #list of lon,lat,alt, NOT ndarray!
23-
else: # just a bunch of points
24-
for i,p in enumerate(lonLatAlt): # iterate over rows
25-
kml.newpoint(name=str(i), coords=[p])
26-
27-
print('writing',ofn)
28-
kml.save(ofn)
29-
3+
from mozloc import csv2kml
304

315
if __name__ == '__main__':
326
from argparse import ArgumentParser
@@ -35,6 +9,5 @@ def makekml(t, lonLatAlt, ofn):
359
p.add_argument('kmlfn',help='kml filename to write')
3610
p = p.parse_args()
3711

38-
dat = np.fliplr(np.loadtxt(p.logfn, usecols=(1,2)))
3912

40-
makekml(None,dat,p.kmlfn)
13+
csv2kml(p.logfn, p.kmlfn)

mozloc.py

Lines changed: 0 additions & 99 deletions
This file was deleted.

mozloc/__init__.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import subprocess
2+
import logging
3+
from io import StringIO
4+
import pandas
5+
import requests
6+
from datetime import datetime
7+
from time import sleep
8+
from pathlib import Path
9+
#
10+
URL='https://location.services.mozilla.com/v1/geolocate?key=test'
11+
NMCMD = ['nmcli','-g','SSID,BSSID,FREQ,SIGNAL','device','wifi'] # Debian stretch, Ubuntu 17.10
12+
NMLEG = ['nmcli','-t','-f','SSID,BSSID,FREQ,SIGNAL','device','wifi'] # ubuntu 16.04
13+
NMSCAN = ['nmcli','device','wifi','rescan']
14+
HEADER='time lat lon accuracy NumBSSIDs'
15+
16+
17+
def logwifiloc(T:float, logfile:Path):
18+
19+
if logfile:
20+
logfile = Path(logfile).expanduser()
21+
with logfile.open('a') as f:
22+
f.write(HEADER+'\n')
23+
24+
25+
print(f'updating every {T} seconds')
26+
print(HEADER)
27+
while True:
28+
loc = get_nmcli()
29+
if loc is None:
30+
sleep(T)
31+
continue
32+
33+
stat = f'{loc["t"].strftime("%xT%X")} {loc["lat"]} {loc["lng"]} {loc["accuracy"]:.1f} {loc["N"]:02d}'
34+
print(stat)
35+
36+
if logfile:
37+
with logfile.open('a') as f:
38+
f.write(stat+'\n')
39+
40+
sleep(T)
41+
42+
43+
def get_nmcli():
44+
45+
46+
ret = subprocess.check_output(NMLEG, universal_newlines=True)
47+
sleep(0.5) # nmcli crashed for less than about 0.2 sec.
48+
try:
49+
subprocess.check_call(NMSCAN) # takes several seconds to update, so do it now.
50+
except subprocess.CalledProcessError as e:
51+
logging.error(f'consider slowing scan cadence. {e}')
52+
53+
dat = pandas.read_csv(StringIO(ret), sep=r'(?<!\\):', index_col=False,
54+
header=0, encoding='utf8', engine='python',
55+
dtype=str, usecols=[0,1,3],
56+
names=['ssid','macAddress','signalStrength'])
57+
# %% optout
58+
dat = dat[~dat['ssid'].str.endswith('_nomap')]
59+
# %% cleanup
60+
dat['ssid'] = dat['ssid'].str.replace('nan','')
61+
dat['macAddress'] = dat['macAddress'].str.replace(r'\\:',':')
62+
# %% JSON
63+
jdat = dat.to_json(orient='records')
64+
jdat = '{ "wifiAccessPoints":' + jdat + '}'
65+
# print(jdat)
66+
# %% cloud MLS
67+
try:
68+
req = requests.post(URL, data=jdat)
69+
if req.status_code != 200:
70+
logging.error(req.text)
71+
return
72+
except requests.exceptions.ConnectionError as e:
73+
logging.error(f'no network connection. {e}')
74+
return
75+
# %% process MLS response
76+
jres = req.json()
77+
loc = jres['location']
78+
loc['accuracy'] = jres['accuracy']
79+
loc['N'] = dat.shape[0] # number of BSSIDs used
80+
loc['t'] = datetime.now()
81+
82+
return loc
83+
# %%
84+
85+
def csv2kml(csvfn:Path, kmlfn:Path):
86+
from simplekml import Kml
87+
88+
"""
89+
write KML track/positions
90+
91+
t: vector of times
92+
lonLatAlt: longitude, latitude, altitude or just lon,lat
93+
ofn: KML filename to create
94+
"""
95+
96+
# lon, lat
97+
dat = pandas.read_csv(csvfn, sep=' ', index_col=0, header=0)
98+
99+
t = dat.index.tolist()
100+
lla = dat.loc[:,['lon','lat']].values
101+
# %% write KML
102+
"""
103+
http://simplekml.readthedocs.io/en/latest/geometries.html#gxtrack
104+
"""
105+
kml = Kml(name='My Kml',open=1)
106+
107+
108+
doc = kml.newdocument(name='Mozilla Location Services')
109+
fol = doc.newfolder(name='Tracks')
110+
trk = fol.newgxtrack(name='My Track')
111+
trk.newwhen(t) # list of times
112+
trk.newgxcoord(lla.tolist()) #list of lon,lat,alt, NOT ndarray!
113+
114+
# just a bunch of points
115+
# for i,p in enumerate(lla): # iterate over rows
116+
# kml.newpoint(name=str(i), coords=[p])
117+
118+
print('writing', kmlfn)
119+
kml.save(kmlfn)

setup.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env python
2+
install_requires = ['pandas','requests']
3+
tests_require = ['nose','coveralls']
4+
# %%
5+
from setuptools import setup,find_packages
6+
7+
setup(name='mozilla-location-python',
8+
packages=find_packages(),
9+
author='Michael Hirsch, Ph.D.',
10+
url='https://github.com/scivision/mozilla-location-python',
11+
long_description=open('README.md').read(),
12+
description='Using Mozilla Location services, log location vs. time using WiFi or convert to KML.',
13+
install_requires=install_requires,
14+
tests_require=tests_require,
15+
extras_require={'tests':tests_require,
16+
'kml':['simplekml']},
17+
python_requires='>=3.6',
18+
)
19+

0 commit comments

Comments
 (0)