thesis.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. import logging
  2. from collections import defaultdict
  3. from matplotlib import pyplot as plt
  4. from matplotlib.ticker import FuncFormatter
  5. import scipy
  6. import numpy as np
  7. import lws.localize as localizer
  8. import utils.accelerated as speed
  9. from utils import path
  10. log = logging.getLogger('lws')
  11. def currentApSurfacePlot(gui):
  12. num_contours = gui.surface_number_of_contours
  13. gui.surface_view.visible = False
  14. gui.reloadAP('108')
  15. gui.ipw_z.ipw.slice_index = 23
  16. for so in gui.scene_object_groups:
  17. so.visible = so.name in ('OG 1', 'Stuff', 'Stairs')
  18. gui.showScalarBar(title='RSSI in dbm')
  19. gui.scene.background = (1.0,1.0,1.0)
  20. gui.ipw_z.visible = False
  21. gui.surface_number_of_contours = 150
  22. gui.scene.mlab.view(51.2, 13.49, 40.13, [ 38.82, 6.77 , -0.58])
  23. gui.scene.set_size((1200, 680))
  24. gui.surface_view.visible = True
  25. # gui.surface_view.contour.auto_contours = False
  26. #~ _debug()
  27. gui.scene.mlab.savefig(r'D:\loco-dev\dirk\thesis\figures\current_ap_surface.png')
  28. gui.scene.mlab.savefig(r'D:\loco-dev\dirk\thesis\figures\current_ap_surface.jpg')
  29. #~ gui.surface_number_of_contours = num_contours
  30. def pruningPrepare(gui, cube_width=3, prune_to=3000):
  31. gui.device_name = 'iconia'
  32. gui.doLoadPaths()
  33. for s in gui.localization_paths:
  34. if s.id == 'og1_eg_right':
  35. gui.selected_path_sequence = s
  36. gui.measurement_runid = '4'
  37. gui.loadMeasurementRun()
  38. gui.cube_width = cube_width
  39. gui.doLocalize(threaded=False)
  40. def pruningPlot(gui):
  41. gui.surface_number_of_contours = 5
  42. pruningPrepare(gui)
  43. for so in gui.scene_object_groups:
  44. so.visible = so.name in ('OG 1', 'Stuff', 'Stairs')
  45. gui.ipw_z.visible = False
  46. gui.pruning_idx = 6
  47. gui.pruning_visible_count = 300
  48. gui.viewPruningSingle()
  49. gui.surface_number_of_contours = 100
  50. gui.scene.mlab.view(-57.603641999035005, 25.219052810285703, 27.428001533423213, [ 32.94838163, 9.41659425, 1.45038033])
  51. gui.scene.set_size((1200, 680))
  52. gui.surface_view.visible = True
  53. gui.scene.background = (0.8,0.8,0.8)
  54. gui.scene.mlab.savefig(r'D:\loco-dev\dirk\thesis\figures\thesis_pruning3d_plot.png')
  55. gui.scene.mlab.savefig(r'D:\loco-dev\dirk\thesis\figures\thesis_pruning3d_plot.jpg')
  56. gui.scene.background = (0.5,0.5,0.5)
  57. def localizationPlot(gui):
  58. pruningPrepare(gui, cube_width=2)
  59. for so in gui.scene_object_groups:
  60. so.visible = so.name in ('EG', 'OG 1', 'Stairs')
  61. gui.showScalarBar(title='Error in m')
  62. gui.ipw_z.visible = False
  63. #~ gui.pruning_idx = 6
  64. #~ gui.pruning_visible_count = 300
  65. #~ gui.viewPruningSingle()
  66. gui.surface_number_of_contours = 50
  67. gui.scene.mlab.view(-61.482491732159787, 22.479891467276911, 35.710743801652669, [40.16922802, 8.7447422 , -2.92382367])
  68. gui.scene.set_size((1100, 570))
  69. gui.surface_view.visible = True
  70. gui.scene.background = (0.8,0.8,0.8)
  71. gui.scene.mlab.savefig(r'D:\loco-dev\dirk\thesis\figures\thesis_localization_result3d.png')
  72. gui.scene.mlab.savefig(r'D:\loco-dev\dirk\thesis\figures\thesis_localization_result3d.jpg')
  73. gui.scene.background = (0.5,0.5,0.5)
  74. def pruningScatter(gui):
  75. destfile = r'D:\loco-dev\dirk\thesis\figures\pruning_scatter.png'
  76. pruningPrepare(gui, cube_width=3, prune_to=500)
  77. POINT_SIZE = 1.0
  78. size_x, size_y, size_z = gui.localizer.cubed_data_shape
  79. ravel = lambda x, y, z: size_z * size_y * x + size_z * y + z
  80. aspectRatio = 0.25
  81. dpi = 90
  82. width = 1000
  83. figsize = width / float(dpi), width / float(dpi) * aspectRatio
  84. fig = plt.figure(figsize=figsize)
  85. ax = plt.axes([0.08, 0.16, 0.91, 0.81], xlabel='time $t$', ylabel=r'states $s$')
  86. ax.set_ylim(0, size_x * size_y * size_z)
  87. ax.set_xlim(0, len(gui.localizer.decoder.pruning_history))
  88. for i, p_data in enumerate(gui.localizer.decoder.pruning_history):
  89. xs = []
  90. ys = []
  91. log.info('processing idx %s of %s' % (i, len(gui.localizer.decoder.pruning_history)))
  92. curr_ys = []
  93. for tx, ty, tz, cost in p_data:
  94. r = ravel(tx, ty, tz)
  95. ys.append(r)
  96. curr_ys.append(r)
  97. xs.append(i)
  98. INTERPOLATE = 20
  99. for j in range(1, INTERPOLATE):
  100. for y in curr_ys:
  101. xs.append(i + j / float(INTERPOLATE))
  102. ys.append(y)
  103. log.info('scattering...')
  104. ax.scatter(xs, ys, s=POINT_SIZE, linewidth=0, color='black', marker='.', antialiased=False, alpha=0.002)
  105. #~ leg = plt.legend(apids)
  106. #~ ltext = leg.get_texts()
  107. #~ llines = leg.get_lines()
  108. #~ plt.setp(ltext, fontsize='x-small')
  109. #~ plt.setp(llines, linewidth=0.7)
  110. #~ leg.draw_frame(False)
  111. #~ if fname is None:
  112. #~ plt.show()
  113. #~ else:
  114. log.info('saving to %s...' % destfile)
  115. fig.savefig(destfile, dpi=dpi)
  116. def latex3dmodel(gui, mesh):
  117. print mesh
  118. matnames = ['MatConcrete', 'MatLightWalls', 'MatDoors',
  119. 'MatGlassDoor', 'MatIronDoor', 'MatFascade', 'MatGlassWindow',
  120. 'MatCupboard', 'MatTable', 'MatHardware', 'MatRailing']
  121. mat2avgthickness = defaultdict(lambda : '')
  122. mat2volumeratio = defaultdict(lambda : 0.3)
  123. mat2avgthickness['MatDoors'] = 0.03
  124. mat2volumeratio['MatDoors'] = 0.08
  125. mat2avgthickness['MatGlassDoor'] = 0.02
  126. mat2volumeratio['MatGlassDoor'] = 0.05
  127. mat2avgthickness['MatIronDoor'] = 0.05
  128. mat2volumeratio['MatIronDoor'] = 0.13
  129. mat2avgthickness['MatGlassWindow'] = 0.05
  130. mat2volumeratio['MatGlassWindow'] = 0.13
  131. mat2avgthickness['MatConcrete'] = 0.3
  132. mat2volumeratio['MatConcrete'] = 0.75
  133. mat2avgthickness['MatLightWalls'] = 0.15
  134. mat2volumeratio['MatLightWalls'] = 0.40
  135. mat2avgthickness['MatFascade'] = 0.4
  136. mat2volumeratio['MatFascade'] = 0.8
  137. mat2triangles = defaultdict(set)
  138. mat2vertices = defaultdict(set)
  139. for objname, triangles in mesh.objects.items():
  140. matname = mesh.materials[objname]
  141. mat2triangles[matname].update(triangles)
  142. for v1, v2, v3 in triangles:
  143. mat2vertices[matname].update([v1, v2, v3])
  144. env = localizer.Environment(gui.objfile, aps=localizer.getAPsFromGui(gui))
  145. shape = (gui.resolution.x, gui.resolution.y, gui.resolution.z)
  146. size = gui.resolution.x * gui.resolution.y * gui.resolution.z
  147. cubesize_m3 = env.di.sx * env.di.sy * env.di.sz
  148. print cubesize_m3
  149. mat2volume = defaultdict(list)
  150. for objname, triangles in mesh.objects.items():
  151. np_ts = np.zeros((len(triangles), 3, 3))
  152. vv = mesh.vertices
  153. for i, (v1, v2, v3) in enumerate(triangles):
  154. p1 = (vv[0][v1], vv[1][v1], vv[2][v1])
  155. p2 = (vv[0][v2], vv[1][v2], vv[2][v2])
  156. p3 = (vv[0][v3], vv[1][v3], vv[2][v3])
  157. np_ts[i, 0, 0] = p1[0]
  158. np_ts[i, 0, 1] = p1[1]
  159. np_ts[i, 0, 2] = p1[2]
  160. np_ts[i, 1, 0] = p2[0]
  161. np_ts[i, 1, 1] = p2[1]
  162. np_ts[i, 1, 2] = p2[2]
  163. np_ts[i, 2, 0] = p3[0]
  164. np_ts[i, 2, 1] = p3[1]
  165. np_ts[i, 2, 2] = p3[2]
  166. print 'building %s' % objname
  167. CACHE = True
  168. cachefile = path('r:/%s.npy' % objname)
  169. if CACHE and cachefile.exists():
  170. blockedCubes = np.load(cachefile)
  171. else:
  172. blockedCubes = speed.buildTriangleCubeIntersection(np_ts, np.zeros((0, 3, 3)), np.zeros((0, 3, 3)), cubed_data_shape=shape, cube_width=1, vi_dimensions=env.di)
  173. np.save(cachefile, blockedCubes)
  174. numblocked = len(np.where(blockedCubes > 0)[0])
  175. matname = mesh.materials[objname]
  176. mat2volume[matname].append(numblocked * cubesize_m3)
  177. print '----'
  178. for matname in matnames:
  179. avgt = mat2avgthickness[matname]
  180. ratio = mat2volumeratio[matname]
  181. vol = sum(mat2volume[matname]) * ratio
  182. print ' %s & %s & %s & %d & %s \\\\ \\hline' % (
  183. matname[3:], len(mat2vertices[matname]), len(mat2triangles[matname]), vol, avgt)
  184. print '----'
  185. def plotAPCoverage(gui):
  186. destfile = r'D:\loco-dev\dirk\thesis\figures\ap_coverage_hist.png'
  187. to_be_summed = []
  188. for ap in gui.aps:
  189. print 'processing: %s' % ap
  190. if not ap.used:
  191. continue
  192. gui.lazyLoadAP(ap)
  193. a = np.zeros(ap.data_in_db.shape, dtype=np.int)
  194. a[np.where(ap.data_in_db > -100)] = 1
  195. to_be_summed.append(a)
  196. print 'combining...'
  197. coverage_map = np.add.reduce(to_be_summed)
  198. print 'plotting'
  199. aspectRatio = 0.3
  200. dpi = 90
  201. width = 1000
  202. figsize = width / float(dpi), width / float(dpi) * aspectRatio
  203. fig = plt.figure(figsize=figsize)
  204. ax = plt.axes([0.095, 0.16, 0.90, 0.80], xlabel='number of visible APs', ylabel=r'covered Volume')
  205. b = coverage_map.ravel()
  206. avg_numaps = sum(b) / float(len(b))
  207. plt.axvline(avg_numaps, color='black', linewidth=2.0)
  208. print 'avg: %.1f' % avg_numaps
  209. ax.hist(b, bins=20, color='#9999FF')
  210. #~ ax.set_ylim(0, size_x * size_y * size_z)
  211. ax.set_xlim(0, 21)
  212. ax.yaxis.set_major_formatter(FuncFormatter(lambda x, pos=None: '%d $m^3$' % (x * 0.2**3)))
  213. ax.grid()
  214. log.info('saving to %s...' % destfile)
  215. fig.savefig(destfile, dpi=dpi)
  216. def plotReadingCounts(counts):
  217. destfile = r'D:\loco-dev\dirk\thesis\figures\reading_counts_hist.png'
  218. print 'plotting'
  219. aspectRatio = 0.3
  220. dpi = 90
  221. width = 1000
  222. figsize = width / float(dpi), width / float(dpi) * aspectRatio
  223. fig = plt.figure(figsize=figsize)
  224. ax = plt.axes([0.095, 0.16, 0.90, 0.80], xlabel='XX', ylabel=r'YY')
  225. plt.axvline(scipy.average(counts), color='black', linewidth=2.0)
  226. print 'avg: %.1f' % scipy.average(counts)
  227. ax.hist(counts, color='#9999FF')
  228. #~ ax.set_ylim(0, size_x * size_y * size_z)
  229. #~ ax.set_xlim(0, 21)
  230. #~ ax.yaxis.set_major_formatter(FuncFormatter(lambda x, pos=None: '%d $m^3$' % (x * 0.2**3)))
  231. ax.grid()
  232. log.info('saving to %s...' % destfile)
  233. fig.savefig(destfile, dpi=dpi)
  234. def latexDevice2Measurements(gui):
  235. device2measuredloc = defaultdict(set)
  236. device2measuredap = defaultdict(set)
  237. device2readingcount = defaultdict(int)
  238. device2readingcount_all = defaultdict(int)
  239. device2api = {'iconia': 'android', 'nexus':'android', 'galaxyp':'android', 'nexus1':'android', 'messbook':'libpcap', 'wispy':'custom'}
  240. device2class = {'iconia': 'tablet', 'nexus':'phone', 'galaxyp':'phone', 'nexus1':'phone', 'messbook':'usb-adapter', 'wispy':'spectrum analyzer'}
  241. device2ap2count = defaultdict(lambda: defaultdict(int))
  242. device2apgroup2stddev = defaultdict(lambda: defaultdict(list))
  243. device2apgroup2count = defaultdict(lambda: defaultdict(int))
  244. device2stddevs = defaultdict(list)
  245. locid2readingcount = defaultdict(int)
  246. dnames = ['iconia', 'nexus', 'galaxyp', 'nexus1', 'messbook', 'wispy']
  247. for dname in dnames:
  248. gui.retriveMeasurements(device_name=dname, autoreload=False, overlay=False)
  249. gui.loadLocations(dname)
  250. for ap in gui.aps:
  251. gui.loadMeasurements(ap)
  252. for m in gui.measurements:
  253. if m.available:
  254. device2measuredloc[dname].add(m.locid)
  255. device2readingcount[dname] += 1
  256. device2readingcount_all[dname] += len(m.rssis)
  257. device2ap2count[dname][ap.id] +=1
  258. #~ print scipy.std(m.rssis)
  259. #~ print m.rssis
  260. device2stddevs[dname].append(scipy.std(m.rssis))
  261. device2apgroup2stddev[dname][ap.powid].append(scipy.std(m.rssis))
  262. device2apgroup2count[dname][ap.powid] += 1
  263. locid2readingcount[m.locid] += len(m.rssis)
  264. device2measuredap[dname].add(ap.id)
  265. # a little bit ugly - but the AP has been moved is therefore not included anymore
  266. device2measuredap['wispy'].add('110')
  267. device2readingcount['wispy'] += 6
  268. device2readingcount_all['wispy'] += 6
  269. print '---'
  270. for dname in dnames:
  271. clocs = len(device2measuredloc[dname])
  272. rc = device2readingcount[dname]
  273. arc = device2readingcount_all[dname]
  274. numaps = len(device2measuredap[dname])
  275. print ' %s & %s & %s & %s & %s & %s & %s\\\\ \\hline' % (dname, device2class[dname], device2api[dname], clocs, numaps, rc, arc)
  276. print '---'
  277. _fmt = lambda powid: powid.replace('wrt54g', 'wrt').replace('eduroam', 'edu')
  278. powids = device2apgroup2stddev['iconia'].keys()
  279. print r' \begin{tabularx}{0.98\textwidth}{|X|c|c|' + ''.join(['c|'] * len(powids) *2) + '}\hline'
  280. print r' \rowcolor[gray]{.92}'
  281. print r' \textbf{Device} & \textbf{$N_{all}$} & \textbf{$\sigma_{all}$} &',
  282. print ' & '.join(r'\textbf{$N_{%s}$} & \textbf{$\sigma_{%s}$}' % (_fmt(e), _fmt(e)) for e in powids),
  283. print r'\\ \hline\hline'
  284. _avg = lambda l: sum(l) / float(len(l))
  285. totals_powids = []
  286. for dname in dnames[:5]:
  287. print ' %s &' % dname,
  288. print '%s &' % device2readingcount[dname],
  289. print ' %.1f &' % _avg(device2stddevs[dname]),
  290. print ' & '.join('%s & %.1f' % (device2apgroup2count[dname][e], _avg(device2apgroup2stddev[dname][e])) for e in powids),
  291. #~ print ' & '.join(str(e) for e in device2ap2count[dname]values()),
  292. print '\\\\ \\hline'
  293. print 'total/avg & ',
  294. print sum(device2readingcount[dname] for dname in dnames[:5]),
  295. print ' & ',
  296. print '%.1f' % _avg(reduce(lambda a, b: a + b, (device2stddevs[dname] for dname in dnames[:4]))),
  297. print ' & ',
  298. for i, e in enumerate(powids):
  299. print sum(device2apgroup2count[dname][e] for dname in dnames[:5]),
  300. print ' & ' ,
  301. print '%.1f' % _avg(reduce(lambda a, b: a + b, (device2apgroup2stddev[dname][e] for dname in dnames[:4]))),
  302. if i < len(powids)-1:
  303. print '& ',
  304. print '\\\\ \\hline'
  305. #~ print sum(device2apgroup2count[dname][e] for e in powid for dname in dnames[:4]),
  306. print r' \end{tabularx}'
  307. print '----'
  308. print 'avg reading count: %d' % scipy.average(locid2readingcount.values())
  309. plotReadingCounts(locid2readingcount.values())
  310. def generateSyntheticPaths(gui):
  311. aps = localizer.getAPsFromGui(gui)
  312. #~ for ap in aps:
  313. #~ gui.lazyLoadAP(ap)
  314. env = localizer.Environment(gui.objfile, aps=aps)
  315. SPEED = 1.0
  316. GRANULARITY = 0.75
  317. destpath = path(r'D:\loco-dev\dirk\tmp\tracked_path_synthetic')
  318. #~ gui.cube_width = 3
  319. assert len(gui.localization_paths) > 0
  320. for NOISE in np.arange(0, 18, 0.5):
  321. for p in gui.localization_paths:
  322. if p.id.startswith('still_') or p.id == 'custom':
  323. continue
  324. print p.id, 'noise: %s' % NOISE
  325. d = destpath.joinpath('sigma_%.1f/%s' % (NOISE, p.id))
  326. if d.exists():
  327. d.rmtree()
  328. d.makedirs()
  329. gui._loadPathSequence(p)
  330. gui._localization_path_changed(refreshCubes=False)
  331. #~ half_cube = (gui.res_dx * gui.cube_width) / 2.0
  332. #~ positions = [np.array(gui.translateToMeter(*(xyz * gui.cube_width))) + half_cube for xyz in cubes]
  333. for x in range(20):
  334. corners = [(c.x, c.y, c.z) for c in gui.localization_path]
  335. measurements = env.generateSyntheticMeasurementsFromCorners(corners=corners,
  336. speed=SPEED,
  337. noise=NOISE,
  338. granularity=GRANULARITY)
  339. #~ measurements = env.generateSyntheticMeasurementsAtPositions(positions, speed=SPEED, noise=NOISE, sample_environment=half_cube/2.0)
  340. f = d.joinpath('measurements_%02d.txt' % x)
  341. f.write_text(str(measurements))
  342. print 'writing %s' % f.abspath()
  343. #~ print measurements
  344. def pathInfo(gui):
  345. assert len(gui.localization_paths) > 0
  346. print '---'
  347. devices = 'iconia', 'nexus'
  348. stats = defaultdict(dict)
  349. device2pathid2collected = defaultdict(lambda: defaultdict(int))
  350. for device in devices:
  351. gui.device_name = device
  352. gui.doLoadPaths()
  353. for p in gui.localization_paths:
  354. if p.id.startswith('still_') or p.id == 'custom':
  355. continue
  356. gui._loadPathSequence(p)
  357. gui._localization_path_changed(refreshCubes=False)
  358. pid = p.id
  359. if pid.endswith('_r'):
  360. pid = p.id[:-2]
  361. device2pathid2collected[device][p.id] = p.collect_runs
  362. corners = gui.localization_path
  363. dist = 0
  364. for c1, c2 in zip(corners, corners[1:]):
  365. dist += ((c2.x - c1.x)**2 + (c2.y - c1.y)**2 + (c2.z - c1.z)**2)**0.5
  366. stats[pid]['dist'] = dist
  367. stats[pid]['turns'] = len(gui.localization_path)
  368. num_aps = []
  369. num_signals = []
  370. durations = []
  371. for r in gui.measurement_runs:
  372. run = gui.runid2run.get(r)
  373. measurements = localizer.Measurements()
  374. measurements.loadFromString(run.measurements)
  375. #~ measurements.interpolateSignals()
  376. for m in measurements:
  377. num_aps.append(len(m.apdata))
  378. durations.append((measurements[-1].timestamp - measurements[0].timestamp).total_seconds())
  379. num_signals.append(len(measurements))
  380. if not 'num_aps' in stats[pid]:
  381. stats[pid]['num_aps'] = []
  382. stats[pid]['num_aps'].extend(num_aps)
  383. if not 'num_signals' in stats[pid]:
  384. stats[pid]['num_signals'] = []
  385. stats[pid]['num_signals'].extend(num_signals)
  386. if not 'durations' in stats[pid]:
  387. stats[pid]['durations'] = []
  388. stats[pid]['durations'].extend(durations)
  389. #~ gui.measurement_runid = r
  390. #~ gui.loadMeasurementRun()
  391. for pid, data in sorted(stats.items()):
  392. avg_signals = sum(data['num_signals']) / float(len(data['num_signals']))
  393. avg_aps = sum(data['num_aps']) / float(len(data['num_aps']))
  394. p = pid.replace('_', '-')
  395. print ' %s & $%.1fm$ & %d & %.1f & %.1f & %s\\\\ \\hline' % (
  396. p, data['dist'], data['turns'],
  397. avg_signals, avg_aps, r'\ref{fig:path_%s}' % pid)
  398. print '---'
  399. pids = stats.keys()
  400. for pid in pids:
  401. p = pid.replace('_', '-')
  402. a = device2pathid2collected['iconia'][pid]
  403. b = device2pathid2collected['iconia'][pid + '_r']
  404. c = device2pathid2collected['nexus'][pid]
  405. d = device2pathid2collected['nexus'][pid + '_r']
  406. fig = r'\ref{fig:path_%s}' % pid
  407. speed = duration = 0
  408. dd = stats[pid]['durations']
  409. avg_duration = sum(dd) / float(len(dd))
  410. speed = stats[pid]['dist'] / avg_duration
  411. print ' %s & $%ds$ & $%.1fm/s$ & %d/%d & %d/%d\\\\ \\hline' % (p, avg_duration, speed, a, b, c, d)