def estimate_accuracy(lat, lon, points, minimum): if len(points) == 1: accuracy = points[0].range else: # Terrible approximation, but hopefully better # than the old approximation, "worst-case range": # this one takes the maximum distance from location # to any of the provided points. accuracy = max([distance(to_degrees(lat), to_degrees(lon), to_degrees(p.lat), to_degrees(p.lon)) * 1000 for p in points]) return max(accuracy, minimum)
def _nearest_tower(missing_lat, missing_lon, centroids): """ We just need the closest cell, so we can approximate using the haversine formula. """ lat1 = to_degrees(missing_lat) lon1 = to_degrees(missing_lon) min_dist = None for pt in centroids: lat2 = to_degrees(pt['lat']) lon2 = to_degrees(pt['lon']) dist = distance(lat1, lon1, lat2, lon2) if min_dist is None or min_dist['dist'] > dist: min_dist = {'dist': dist, 'pt': pt} if min_dist['dist'] <= NEAREST_DISTANCE: return min_dist
def update_lac(self, radio, mcc, mnc, lac): with self.db_session() as session: # Select all the cells in this LAC that aren't the virtual # cell itself, and derive a bounding box for them. q = session.query(Cell).filter(Cell.radio == radio).filter( Cell.mcc == mcc).filter(Cell.mnc == mnc).filter( Cell.lac == lac).filter(Cell.cid != CELLID_LAC) cells = q.all() points = [(to_degrees(c.lat), to_degrees(c.lon)) for c in cells] min_lat = to_degrees(min([c.min_lat for c in cells])) min_lon = to_degrees(min([c.min_lon for c in cells])) max_lat = to_degrees(max([c.max_lat for c in cells])) max_lon = to_degrees(max([c.max_lon for c in cells])) bbox_points = [(min_lat, min_lon), (min_lat, max_lon), (max_lat, min_lon), (max_lat, max_lon)] ctr = centroid(points) rng = range_to_points(ctr, bbox_points) # switch units back to DB preferred centimicrodegres angle # and meters distance. ctr_lat = from_degrees(ctr[0]) ctr_lon = from_degrees(ctr[1]) rng = int(round(rng * 1000.0)) # Now create or update the LAC virtual cell q = session.query(Cell).filter(Cell.radio == radio).filter( Cell.mcc == mcc).filter(Cell.mnc == mnc).filter( Cell.lac == lac).filter(Cell.cid == CELLID_LAC) lac = q.first() if lac is None: lac = Cell(radio=radio, mcc=mcc, mnc=mnc, lac=lac, cid=CELLID_LAC, lat=ctr_lat, lon=ctr_lon, range=rng) else: lac.new_measures = 0 lac.lat = ctr_lat lac.lon = ctr_lon lac.range = rng session.commit()
def update_lac(self, radio, mcc, mnc, lac): with self.db_session() as session: # Select all the cells in this LAC that aren't the virtual # cell itself, and derive a bounding box for them. q = session.query(Cell).filter( Cell.radio == radio).filter( Cell.mcc == mcc).filter( Cell.mnc == mnc).filter( Cell.lac == lac).filter( Cell.cid != CELLID_LAC).filter( Cell.new_measures == 0).filter( Cell.lat.isnot(None)).filter( Cell.lon.isnot(None)) cells = q.all() if len(cells) == 0: return points = [(to_degrees(c.lat), to_degrees(c.lon)) for c in cells] min_lat = to_degrees(min([c.min_lat for c in cells])) min_lon = to_degrees(min([c.min_lon for c in cells])) max_lat = to_degrees(max([c.max_lat for c in cells])) max_lon = to_degrees(max([c.max_lon for c in cells])) bbox_points = [(min_lat, min_lon), (min_lat, max_lon), (max_lat, min_lon), (max_lat, max_lon)] ctr = centroid(points) rng = range_to_points(ctr, bbox_points) # switch units back to DB preferred centimicrodegres angle # and meters distance. ctr_lat = from_degrees(ctr[0]) ctr_lon = from_degrees(ctr[1]) rng = int(round(rng * 1000.0)) # Now create or update the LAC virtual cell q = session.query(Cell).filter( Cell.radio == radio).filter( Cell.mcc == mcc).filter( Cell.mnc == mnc).filter( Cell.lac == lac).filter( Cell.cid == CELLID_LAC) lac = q.first() if lac is None: lac = Cell(radio=radio, mcc=mcc, mnc=mnc, lac=lac, cid=CELLID_LAC, lat=ctr_lat, lon=ctr_lon, range=rng) else: lac.new_measures = 0 lac.lat = ctr_lat lac.lon = ctr_lon lac.range = rng session.commit()
def calculate_new_position(station, measures, moving_stations, max_dist_km, backfill=True): # if backfill is true, we work on older measures for which # the new/total counters where never updated length = len(measures) latitudes = [w[0] for w in measures] longitudes = [w[1] for w in measures] new_lat = sum(latitudes) // length new_lon = sum(longitudes) // length if station.lat and station.lon: latitudes.append(station.lat) longitudes.append(station.lon) existing_station = True else: station.lat = new_lat station.lon = new_lon existing_station = False # calculate extremes of measures, existing location estimate # and existing extreme values def extreme(vals, attr, function): new = function(vals) old = getattr(station, attr, None) if old is not None: return function(new, old) else: return new min_lat = extreme(latitudes, 'min_lat', min) min_lon = extreme(longitudes, 'min_lon', min) max_lat = extreme(latitudes, 'max_lat', max) max_lon = extreme(longitudes, 'max_lon', max) # calculate sphere-distance from opposite corners of # bounding box containing current location estimate # and new measurements; if too big, station is moving box_dist = distance(to_degrees(min_lat), to_degrees(min_lon), to_degrees(max_lat), to_degrees(max_lon)) if existing_station: if box_dist > max_dist_km: # add to moving list, return early without updating # station since it will be deleted by caller momentarily moving_stations.add(station) return if backfill: new_total = station.total_measures + length old_length = station.total_measures # update total to account for new measures # new counter never got updated to include the measures station.total_measures = new_total else: new_total = station.total_measures old_length = new_total - length station.lat = ((station.lat * old_length) + (new_lat * length)) // new_total station.lon = ((station.lon * old_length) + (new_lon * length)) // new_total if not backfill: # decrease new counter, total is already correct # in the backfill case new counter was never increased station.new_measures = station.new_measures - length # update max/min lat/lon columns station.min_lat = min_lat station.min_lon = min_lon station.max_lat = max_lat station.max_lon = max_lon # give radio-range estimate between extreme values and centroid ctr = (to_degrees(station.lat), to_degrees(station.lon)) points = [(to_degrees(min_lat), to_degrees(min_lon)), (to_degrees(min_lat), to_degrees(max_lon)), (to_degrees(max_lat), to_degrees(min_lon)), (to_degrees(max_lat), to_degrees(max_lon))] station.range = range_to_points(ctr, points) * 1000.0