Introducing "gsmmap" to convert SYSTEM INFORMATION log into a KML map
This commit is contained in:
parent
462015a91e
commit
a8ac4bb2df
15
src/Makefile
15
src/Makefile
|
@ -12,7 +12,7 @@ OSMOCORE_CONFIGURE_ENV= LIBOSMOCORE_LIBS=$(TOPDIR)/shared/libosmocore/build-host
|
|||
LIBOSMOCORE_CFLAGS=-I$(TOPDIR)/shared/libosmocore/include \
|
||||
LIBOSMOVTY_CFLAGS=-I$(TOPDIR)/shared/libosmocore/include
|
||||
|
||||
all: libosmocore-host libosmocore-target layer23 osmocon firmware
|
||||
all: libosmocore-host libosmocore-target layer23 osmocon gsmmap firmware
|
||||
|
||||
libosmocore-host: shared/libosmocore/build-host/src/.libs/libosmocore.la
|
||||
|
||||
|
@ -57,6 +57,19 @@ host/osmocon/osmocon: host/osmocon/Makefile libosmocore-host
|
|||
make -C host/osmocon
|
||||
|
||||
|
||||
.PHONY: gsmmap
|
||||
gsmmap: host/gsmmap/gsmmap
|
||||
|
||||
host/gsmmap/configure: host/gsmmap/configure.ac
|
||||
cd host/gsmmap && autoreconf -i
|
||||
|
||||
host/gsmmap/Makefile: host/gsmmap/configure
|
||||
cd host/gsmmap && $(OSMOCORE_CONFIGURE_ENV) ./configure
|
||||
|
||||
host/gsmmap/gsmmap: host/gsmmap/Makefile libosmocore-host
|
||||
make -C host/gsmmap
|
||||
|
||||
|
||||
.PHONY: layer23
|
||||
layer23: host/layer23/layer23
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# autoreconf by-products
|
||||
*.in
|
||||
|
||||
aclocal.m4
|
||||
autom4te.cache/
|
||||
configure
|
||||
depcomp
|
||||
install-sh
|
||||
missing
|
||||
|
||||
# configure by-products
|
||||
.deps/
|
||||
Makefile
|
||||
|
||||
config.status
|
||||
version.h
|
||||
|
||||
# build by-products
|
||||
*.o
|
||||
|
||||
gsmmap
|
||||
|
||||
# various
|
||||
.version
|
||||
.tarball-version
|
||||
|
||||
# IDA file
|
||||
*.id*
|
||||
*.nam
|
||||
*.til
|
||||
|
||||
# Other test files
|
||||
*.dump
|
||||
*.bin
|
||||
*.log
|
|
@ -0,0 +1,18 @@
|
|||
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
|
||||
|
||||
# versioning magic
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
$(top_srcdir)/.version:
|
||||
echo $(VERSION) > $@-t && mv $@-t $@
|
||||
dist-hook:
|
||||
echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
|
||||
|
||||
sbin_PROGRAMS = gsmmap
|
||||
|
||||
INCLUDES += -I../layer23/include -DHOST_BUILD
|
||||
gsmmap_SOURCES = gsmmap.c geo.c locate.c log.c ../layer23/src/common/sysinfo.c ../layer23/src/common/networks.c
|
||||
gsmmap_LDADD = $(LIBOSMOCORE_LIBS) -lm
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
dnl Process this file with autoconf to produce a configure script
|
||||
AC_INIT([gsmmap],
|
||||
m4_esyscmd([./git-version-gen .tarball-version]),
|
||||
[baseband-devel@lists.osmocom.org])
|
||||
|
||||
AM_INIT_AUTOMAKE([dist-bzip2])
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl checks for programs
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
|
||||
dnl checks for libraries
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore)
|
||||
|
||||
dnl checks for header files
|
||||
AC_HEADER_STDC
|
||||
|
||||
dnl Checks for typedefs, structures and compiler characteristics
|
||||
|
||||
AC_OUTPUT(
|
||||
Makefile)
|
|
@ -0,0 +1,47 @@
|
|||
#include <math.h>
|
||||
#include <geo.h>
|
||||
|
||||
void geo2space(double *x, double *y, double *z, double lon, double lat)
|
||||
{
|
||||
*z = sin(lat / 180.0 * PI) * POLE_RADIUS;
|
||||
*x = sin(lon / 180.0 * PI) * cos(lat / 180.0 * PI) * EQUATOR_RADIUS;
|
||||
*y = -cos(lon / 180.0 * PI) * cos(lat / 180.0 * PI) * EQUATOR_RADIUS;
|
||||
}
|
||||
|
||||
void space2geo(double *lon, double *lat, double x, double y, double z)
|
||||
{
|
||||
double r;
|
||||
|
||||
/* bring geoid to 1m radius */
|
||||
z = z / POLE_RADIUS;
|
||||
x = x / EQUATOR_RADIUS;
|
||||
y = y / EQUATOR_RADIUS;
|
||||
|
||||
/* normalize */
|
||||
r = sqrt(x * x + y * y + z * z);
|
||||
z = z / r;
|
||||
x = x / r;
|
||||
y = y / r;
|
||||
|
||||
*lat = asin(z) / PI * 180;
|
||||
*lon = atan2(x, -y) / PI * 180;
|
||||
}
|
||||
|
||||
double distinspace(double x1, double y1, double z1, double x2, double y2,
|
||||
double z2)
|
||||
{
|
||||
double x = x1 - x2;
|
||||
double y = y1 - y2;
|
||||
double z = z1 - z2;
|
||||
|
||||
return sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
double distonplane(double x1, double y1, double x2, double y2)
|
||||
{
|
||||
double x = x1 - x2;
|
||||
double y = y1 - y2;
|
||||
|
||||
return sqrt(x * x + y * y);
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
/* WGS 84 */
|
||||
#define EQUATOR_RADIUS 6378137.0
|
||||
#define POLE_RADIUS 6356752.314
|
||||
|
||||
#define PI 3.1415926536
|
||||
|
||||
void geo2space(double *x, double *y, double *z, double lat, double lon);
|
||||
void space2geo(double *lat, double *lon, double x, double y, double z);
|
||||
double distinspace(double x1, double y1, double z1, double x2, double y2,
|
||||
double z2);
|
||||
double distonplane(double x1, double y1, double x2, double y2);
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
#!/bin/sh
|
||||
# Print a version string.
|
||||
scriptversion=2010-01-28.01
|
||||
|
||||
# Copyright (C) 2007-2010 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
|
||||
# It may be run two ways:
|
||||
# - from a git repository in which the "git describe" command below
|
||||
# produces useful output (thus requiring at least one signed tag)
|
||||
# - from a non-git-repo directory containing a .tarball-version file, which
|
||||
# presumes this script is invoked like "./git-version-gen .tarball-version".
|
||||
|
||||
# In order to use intra-version strings in your project, you will need two
|
||||
# separate generated version string files:
|
||||
#
|
||||
# .tarball-version - present only in a distribution tarball, and not in
|
||||
# a checked-out repository. Created with contents that were learned at
|
||||
# the last time autoconf was run, and used by git-version-gen. Must not
|
||||
# be present in either $(srcdir) or $(builddir) for git-version-gen to
|
||||
# give accurate answers during normal development with a checked out tree,
|
||||
# but must be present in a tarball when there is no version control system.
|
||||
# Therefore, it cannot be used in any dependencies. GNUmakefile has
|
||||
# hooks to force a reconfigure at distribution time to get the value
|
||||
# correct, without penalizing normal development with extra reconfigures.
|
||||
#
|
||||
# .version - present in a checked-out repository and in a distribution
|
||||
# tarball. Usable in dependencies, particularly for files that don't
|
||||
# want to depend on config.h but do want to track version changes.
|
||||
# Delete this file prior to any autoconf run where you want to rebuild
|
||||
# files to pick up a version string change; and leave it stale to
|
||||
# minimize rebuild time after unrelated changes to configure sources.
|
||||
#
|
||||
# It is probably wise to add these two files to .gitignore, so that you
|
||||
# don't accidentally commit either generated file.
|
||||
#
|
||||
# Use the following line in your configure.ac, so that $(VERSION) will
|
||||
# automatically be up-to-date each time configure is run (and note that
|
||||
# since configure.ac no longer includes a version string, Makefile rules
|
||||
# should not depend on configure.ac for version updates).
|
||||
#
|
||||
# AC_INIT([GNU project],
|
||||
# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
|
||||
# [bug-project@example])
|
||||
#
|
||||
# Then use the following lines in your Makefile.am, so that .version
|
||||
# will be present for dependencies, and so that .tarball-version will
|
||||
# exist in distribution tarballs.
|
||||
#
|
||||
# BUILT_SOURCES = $(top_srcdir)/.version
|
||||
# $(top_srcdir)/.version:
|
||||
# echo $(VERSION) > $@-t && mv $@-t $@
|
||||
# dist-hook:
|
||||
# echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
case $# in
|
||||
1) ;;
|
||||
*) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
|
||||
esac
|
||||
|
||||
tarball_version_file=$1
|
||||
nl='
|
||||
'
|
||||
|
||||
# First see if there is a tarball-only version file.
|
||||
# then try "git describe", then default.
|
||||
if test -f $tarball_version_file
|
||||
then
|
||||
v=`cat $tarball_version_file` || exit 1
|
||||
case $v in
|
||||
*$nl*) v= ;; # reject multi-line output
|
||||
[0-9]*) ;;
|
||||
*) v= ;;
|
||||
esac
|
||||
test -z "$v" \
|
||||
&& echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
|
||||
fi
|
||||
|
||||
if test -n "$v"
|
||||
then
|
||||
: # use $v
|
||||
elif
|
||||
v=`git describe --abbrev=4 --match='osmocon_v*' HEAD 2>/dev/null \
|
||||
|| git describe --abbrev=4 HEAD 2>/dev/null` \
|
||||
&& case $v in
|
||||
osmocon_[0-9]*) ;;
|
||||
osmocon_v[0-9]*) ;;
|
||||
*) (exit 1) ;;
|
||||
esac
|
||||
then
|
||||
# Is this a new git that lists number of commits since the last
|
||||
# tag or the previous older version that did not?
|
||||
# Newer: v6.10-77-g0f8faeb
|
||||
# Older: v6.10-g0f8faeb
|
||||
case $v in
|
||||
*-*-*) : git describe is okay three part flavor ;;
|
||||
*-*)
|
||||
: git describe is older two part flavor
|
||||
# Recreate the number of commits and rewrite such that the
|
||||
# result is the same as if we were using the newer version
|
||||
# of git describe.
|
||||
vtag=`echo "$v" | sed 's/-.*//'`
|
||||
numcommits=`git rev-list "$vtag"..HEAD | wc -l`
|
||||
v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
|
||||
;;
|
||||
esac
|
||||
|
||||
# Change the first '-' to a '.', so version-comparing tools work properly.
|
||||
# Remove the "g" in git describe's output string, to save a byte.
|
||||
v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/;s/^osmocon_//'`;
|
||||
else
|
||||
v="UNKNOWN"
|
||||
fi
|
||||
|
||||
v=`echo "$v" |sed 's/^v//'`
|
||||
|
||||
# Don't declare a version "dirty" merely because a time stamp has changed.
|
||||
git status > /dev/null 2>&1
|
||||
|
||||
dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
|
||||
case "$dirty" in
|
||||
'') ;;
|
||||
*) # Append the suffix only if there isn't one already.
|
||||
case $v in
|
||||
*-dirty) ;;
|
||||
*) v="$v-dirty" ;;
|
||||
esac ;;
|
||||
esac
|
||||
|
||||
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
|
||||
echo "$v" | tr -d '\012'
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
|
@ -0,0 +1,646 @@
|
|||
/* Conversion of logged cells to KML file */
|
||||
|
||||
/* (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
#warning todo bsic
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
|
||||
#define GSM_TA_M 553.85
|
||||
#define PI 3.1415926536
|
||||
|
||||
#include <osmocom/bb/common/osmocom_data.h>
|
||||
#include <osmocom/bb/common/networks.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "geo.h"
|
||||
#include "locate.h"
|
||||
|
||||
/*
|
||||
* structure of power and cell infos
|
||||
*/
|
||||
|
||||
struct power power;
|
||||
struct sysinfo sysinfo;
|
||||
static struct node_power *node_power_first = NULL;
|
||||
static struct node_power **node_power_last_p = &node_power_first;
|
||||
struct node_mcc *node_mcc_first = NULL;
|
||||
int log_lines = 0, log_debug = 0;
|
||||
|
||||
|
||||
static void nomem(void)
|
||||
{
|
||||
fprintf(stderr, "No mem!\n");
|
||||
exit(-ENOMEM);
|
||||
}
|
||||
|
||||
static void add_power()
|
||||
{
|
||||
struct node_power *node_power;
|
||||
|
||||
// printf("New Power\n");
|
||||
/* append or insert to list */
|
||||
node_power = calloc(1, sizeof(struct node_power));
|
||||
if (!node_power)
|
||||
nomem();
|
||||
*node_power_last_p = node_power;
|
||||
node_power_last_p = &node_power->next;
|
||||
memcpy(&node_power->power, &power, sizeof(power));
|
||||
}
|
||||
|
||||
static void add_sysinfo()
|
||||
{
|
||||
struct gsm48_sysinfo s;
|
||||
struct node_mcc *mcc;
|
||||
struct node_mnc *mnc;
|
||||
struct node_lac *lac;
|
||||
struct node_cell *cell;
|
||||
struct node_meas *meas;
|
||||
|
||||
memset(&s, 0, sizeof(s));
|
||||
|
||||
/* decode sysinfo */
|
||||
if (sysinfo.si1[2])
|
||||
gsm48_decode_sysinfo1(&s,
|
||||
(struct gsm48_system_information_type_1 *) sysinfo.si1,
|
||||
23);
|
||||
if (sysinfo.si2[2])
|
||||
gsm48_decode_sysinfo2(&s,
|
||||
(struct gsm48_system_information_type_2 *) sysinfo.si2,
|
||||
23);
|
||||
if (sysinfo.si2bis[2])
|
||||
gsm48_decode_sysinfo2bis(&s,
|
||||
(struct gsm48_system_information_type_2bis *)
|
||||
sysinfo.si2bis,
|
||||
23);
|
||||
if (sysinfo.si2ter[2])
|
||||
gsm48_decode_sysinfo2ter(&s,
|
||||
(struct gsm48_system_information_type_2ter *)
|
||||
sysinfo.si2ter,
|
||||
23);
|
||||
if (sysinfo.si3[2])
|
||||
gsm48_decode_sysinfo3(&s,
|
||||
(struct gsm48_system_information_type_3 *) sysinfo.si3,
|
||||
23);
|
||||
if (sysinfo.si4[2])
|
||||
gsm48_decode_sysinfo4(&s,
|
||||
(struct gsm48_system_information_type_4 *) sysinfo.si4,
|
||||
23);
|
||||
|
||||
mcc = get_node_mcc(s.mcc);
|
||||
if (!mcc)
|
||||
nomem();
|
||||
mnc = get_node_mnc(mcc, s.mnc);
|
||||
if (!mnc)
|
||||
nomem();
|
||||
lac = get_node_lac(mnc, s.lac);
|
||||
if (!lac)
|
||||
nomem();
|
||||
cell = get_node_cell(lac, s.cell_id);
|
||||
if (!cell)
|
||||
nomem();
|
||||
meas = add_node_meas(cell);
|
||||
if (!meas)
|
||||
nomem();
|
||||
if (!cell->content) {
|
||||
cell->content = 1;
|
||||
memcpy(&cell->sysinfo, &sysinfo, sizeof(sysinfo));
|
||||
memcpy(&cell->s, &s, sizeof(s));
|
||||
} else {
|
||||
if (memcmp(&cell->sysinfo.si1, sysinfo.si1,
|
||||
sizeof(sysinfo.si1))) {
|
||||
new_sysinfo:
|
||||
fprintf(stderr, "FIXME: the cell changed sysinfo\n");
|
||||
return;
|
||||
}
|
||||
if (memcmp(&cell->sysinfo.si2, sysinfo.si2,
|
||||
sizeof(sysinfo.si2)))
|
||||
goto new_sysinfo;
|
||||
if (memcmp(&cell->sysinfo.si2bis, sysinfo.si2bis,
|
||||
sizeof(sysinfo.si2bis)))
|
||||
goto new_sysinfo;
|
||||
if (memcmp(&cell->sysinfo.si2ter, sysinfo.si2ter,
|
||||
sizeof(sysinfo.si2ter)))
|
||||
goto new_sysinfo;
|
||||
if (memcmp(&cell->sysinfo.si3, sysinfo.si3,
|
||||
sizeof(sysinfo.si3)))
|
||||
goto new_sysinfo;
|
||||
if (memcmp(&cell->sysinfo.si4, sysinfo.si4,
|
||||
sizeof(sysinfo.si4)))
|
||||
goto new_sysinfo;
|
||||
}
|
||||
}
|
||||
|
||||
void kml_header(FILE *outfp, char *name)
|
||||
{
|
||||
/* XML header */
|
||||
fprintf(outfp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||
|
||||
/* KML open tag */
|
||||
fprintf(outfp, "<kml xmlns=\"http://www.opengis.net/kml/2.2\" "
|
||||
"xmlns:gx=\"http://www.google.com/kml/ext/2.2\" "
|
||||
"xmlns:kml=\"http://www.opengis.net/kml/2.2\" "
|
||||
"xmlns:atom=\"http://www.w3.org/2005/Atom\">\n");
|
||||
|
||||
/* document open tag */
|
||||
fprintf(outfp, "<Document>\n");
|
||||
|
||||
/* pushpin */
|
||||
fprintf(outfp, "\t<Style id=\"sn_placemark_red_pushpin\">\n");
|
||||
fprintf(outfp, "\t\t<IconStyle>\n");
|
||||
fprintf(outfp, "\t\t\t<scale>1.1</scale>\n");
|
||||
fprintf(outfp, "\t\t\t<Icon>\n");
|
||||
fprintf(outfp, "\t\t\t\t<href>http://maps.google.com/mapfiles/kml/"
|
||||
"pushpin/red-pushpin.png</href>\n");
|
||||
fprintf(outfp, "\t\t\t</Icon>\n");
|
||||
fprintf(outfp, "\t\t</IconStyle>\n");
|
||||
fprintf(outfp, "\t\t<ListStyle>\n");
|
||||
fprintf(outfp, "\t\t</ListStyle>\n");
|
||||
fprintf(outfp, "\t</Style>\n");
|
||||
fprintf(outfp, "\t<Style id=\"sh_placemark_red_pushpin_highlight\">\n");
|
||||
fprintf(outfp, "\t\t<IconStyle>\n");
|
||||
fprintf(outfp, "\t\t\t<scale>1.3</scale>\n");
|
||||
fprintf(outfp, "\t\t\t<Icon>\n");
|
||||
fprintf(outfp, "\t\t\t\t<href>http://maps.google.com/mapfiles/kml/"
|
||||
"pushpin/red-pushpin.png</href>\n");
|
||||
fprintf(outfp, "\t\t\t</Icon>\n");
|
||||
fprintf(outfp, "\t\t</IconStyle>\n");
|
||||
fprintf(outfp, "\t\t<ListStyle>\n");
|
||||
fprintf(outfp, "\t\t</ListStyle>\n");
|
||||
fprintf(outfp, "\t</Style>\n");
|
||||
fprintf(outfp, "\t<StyleMap id=\"msn_placemark_red_pushpin\">\n");
|
||||
fprintf(outfp, "\t\t<Pair>\n");
|
||||
fprintf(outfp, "\t\t\t<key>normal</key>\n");
|
||||
fprintf(outfp, "\t\t\t<styleUrl>#sn_placemark_red_pushpin"
|
||||
"</styleUrl>\n");
|
||||
fprintf(outfp, "\t\t</Pair>\n");
|
||||
fprintf(outfp, "\t\t<Pair>\n");
|
||||
fprintf(outfp, "\t\t\t<key>highlight</key>\n");
|
||||
fprintf(outfp, "\t\t\t<styleUrl>#sh_placemark_red_pushpin_highlight"
|
||||
"</styleUrl>\n");
|
||||
fprintf(outfp, "\t\t</Pair>\n");
|
||||
fprintf(outfp, "\t</StyleMap>\n");
|
||||
|
||||
fprintf(outfp, "\t<Style id=\"sn_placemark_grn_pushpin\">\n");
|
||||
fprintf(outfp, "\t\t<IconStyle>\n");
|
||||
fprintf(outfp, "\t\t\t<scale>1.1</scale>\n");
|
||||
fprintf(outfp, "\t\t\t<Icon>\n");
|
||||
fprintf(outfp, "\t\t\t\t<href>http://maps.google.com/mapfiles/kml/"
|
||||
"pushpin/grn-pushpin.png</href>\n");
|
||||
fprintf(outfp, "\t\t\t</Icon>\n");
|
||||
fprintf(outfp, "\t\t</IconStyle>\n");
|
||||
fprintf(outfp, "\t\t<ListStyle>\n");
|
||||
fprintf(outfp, "\t\t</ListStyle>\n");
|
||||
fprintf(outfp, "\t</Style>\n");
|
||||
fprintf(outfp, "\t<Style id=\"sh_placemark_grn_pushpin_highlight\">\n");
|
||||
fprintf(outfp, "\t\t<IconStyle>\n");
|
||||
fprintf(outfp, "\t\t\t<scale>1.3</scale>\n");
|
||||
fprintf(outfp, "\t\t\t<Icon>\n");
|
||||
fprintf(outfp, "\t\t\t\t<href>http://maps.google.com/mapfiles/kml/"
|
||||
"pushpin/grn-pushpin.png</href>\n");
|
||||
fprintf(outfp, "\t\t\t</Icon>\n");
|
||||
fprintf(outfp, "\t\t</IconStyle>\n");
|
||||
fprintf(outfp, "\t\t<ListStyle>\n");
|
||||
fprintf(outfp, "\t\t</ListStyle>\n");
|
||||
fprintf(outfp, "\t</Style>\n");
|
||||
fprintf(outfp, "\t<StyleMap id=\"msn_placemark_grn_pushpin\">\n");
|
||||
fprintf(outfp, "\t\t<Pair>\n");
|
||||
fprintf(outfp, "\t\t\t<key>normal</key>\n");
|
||||
fprintf(outfp, "\t\t\t<styleUrl>#sn_placemark_grn_pushpin"
|
||||
"</styleUrl>\n");
|
||||
fprintf(outfp, "\t\t</Pair>\n");
|
||||
fprintf(outfp, "\t\t<Pair>\n");
|
||||
fprintf(outfp, "\t\t\t<key>highlight</key>\n");
|
||||
fprintf(outfp, "\t\t\t<styleUrl>#sh_placemark_grn_pushpin_highlight"
|
||||
"</styleUrl>\n");
|
||||
fprintf(outfp, "\t\t</Pair>\n");
|
||||
fprintf(outfp, "\t</StyleMap>\n");
|
||||
|
||||
/* circle */
|
||||
fprintf(outfp, "\t<Style id=\"sn_placemark_circle\">\n");
|
||||
fprintf(outfp, "\t\t<IconStyle>\n");
|
||||
fprintf(outfp, "\t\t\t<scale>1.0</scale>\n");
|
||||
fprintf(outfp, "\t\t\t<Icon>\n");
|
||||
fprintf(outfp, "\t\t\t\t<href>http://maps.google.com/mapfiles/kml/"
|
||||
"shapes/placemark_circle.png</href>\n");
|
||||
fprintf(outfp, "\t\t\t</Icon>\n");
|
||||
fprintf(outfp, "\t\t</IconStyle>\n");
|
||||
fprintf(outfp, "\t\t<ListStyle>\n");
|
||||
fprintf(outfp, "\t\t</ListStyle>\n");
|
||||
fprintf(outfp, "\t</Style>\n");
|
||||
fprintf(outfp, "\t<Style id=\"sh_placemark_circle_highlight\">\n");
|
||||
fprintf(outfp, "\t\t<IconStyle>\n");
|
||||
fprintf(outfp, "\t\t\t<scale>1.2</scale>\n");
|
||||
fprintf(outfp, "\t\t\t<Icon>\n");
|
||||
fprintf(outfp, "\t\t\t\t<href>http://maps.google.com/mapfiles/kml/"
|
||||
"shapes/placemark_circle_highlight.png</href>\n");
|
||||
fprintf(outfp, "\t\t\t</Icon>\n");
|
||||
fprintf(outfp, "\t\t</IconStyle>\n");
|
||||
fprintf(outfp, "\t\t<ListStyle>\n");
|
||||
fprintf(outfp, "\t\t</ListStyle>\n");
|
||||
fprintf(outfp, "\t</Style>\n");
|
||||
fprintf(outfp, "\t<StyleMap id=\"msn_placemark_circle\">\n");
|
||||
fprintf(outfp, "\t\t<Pair>\n");
|
||||
fprintf(outfp, "\t\t\t<key>normal</key>\n");
|
||||
fprintf(outfp, "\t\t\t<styleUrl>#sn_placemark_circle</styleUrl>\n");
|
||||
fprintf(outfp, "\t\t</Pair>\n");
|
||||
fprintf(outfp, "\t\t<Pair>\n");
|
||||
fprintf(outfp, "\t\t\t<key>highlight</key>\n");
|
||||
fprintf(outfp, "\t\t\t<styleUrl>#sh_placemark_circle_highlight"
|
||||
"</styleUrl>\n");
|
||||
fprintf(outfp, "\t\t</Pair>\n");
|
||||
fprintf(outfp, "\t</StyleMap>\n");
|
||||
}
|
||||
|
||||
void kml_footer(FILE *outfp)
|
||||
{
|
||||
/* document close tag */
|
||||
fprintf(outfp, "</Document>\n");
|
||||
|
||||
/* KML close tag */
|
||||
fprintf(outfp, "</kml>\n");
|
||||
|
||||
}
|
||||
|
||||
void kml_meas(FILE *outfp, struct node_meas *meas, int n, uint16_t mcc,
|
||||
uint16_t mnc, uint16_t lac, uint16_t cellid)
|
||||
{
|
||||
struct tm *tm = localtime(&meas->gmt);
|
||||
|
||||
fprintf(outfp, "\t\t\t\t\t<Placemark>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t<name>%d: %d</name>\n", n, meas->rxlev);
|
||||
fprintf(outfp, "\t\t\t\t\t\t<description>\n");
|
||||
fprintf(outfp, "MCC=%s MNC=%s\nLAC=%04x CELL-ID=%04x\n(%s %s)\n",
|
||||
gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac, cellid,
|
||||
gsm_get_mcc(mcc), gsm_get_mnc(mcc, mnc));
|
||||
fprintf(outfp, "\n%s", asctime(tm));
|
||||
fprintf(outfp, "RX-LEV %d dBm\n", meas->rxlev);
|
||||
if (meas->ta_valid)
|
||||
fprintf(outfp, "TA=%d (%d-%d meter)\n", meas->ta,
|
||||
(int)(GSM_TA_M * meas->ta),
|
||||
(int)(GSM_TA_M * (meas->ta + 1)));
|
||||
fprintf(outfp, "\t\t\t\t\t\t</description>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t<LookAt>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t\t<longitude>%.8f</longitude>\n",
|
||||
meas->longitude);
|
||||
fprintf(outfp, "\t\t\t\t\t\t\t<latitude>%.8f</latitude>\n",
|
||||
meas->latitude);
|
||||
fprintf(outfp, "\t\t\t\t\t\t\t<altitude>0</altitude>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t\t<tilt>0</tilt>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t\t<altitudeMode>relativeToGround"
|
||||
"</altitudeMode>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t\t<gx:altitudeMode>relativeToSeaFloor"
|
||||
"</gx:altitudeMode>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t</LookAt>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t<styleUrl>#msn_placemark_circle"
|
||||
"</styleUrl>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t<Point>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t\t<coordinates>%.8f,%.8f</coordinates>\n",
|
||||
meas->longitude, meas->latitude);
|
||||
fprintf(outfp, "\t\t\t\t\t\t</Point>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t</Placemark>\n");
|
||||
}
|
||||
|
||||
static void print_si(void *priv, const char *fmt, ...)
|
||||
{
|
||||
char buffer[1000];
|
||||
FILE *outfp = (FILE *)priv;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
va_end(args);
|
||||
|
||||
if (buffer[0])
|
||||
fprintf(outfp, "%s", buffer);
|
||||
}
|
||||
|
||||
double debug_long, debug_lat, debug_x_scale;
|
||||
FILE *debug_fp;
|
||||
|
||||
void kml_cell(FILE *outfp, struct node_cell *cell)
|
||||
{
|
||||
struct node_meas *meas;
|
||||
double x, y, z, sum_x = 0, sum_y = 0, sum_z = 0, longitude, latitude;
|
||||
int n, known = 0;
|
||||
|
||||
meas = cell->meas;
|
||||
n = 0;
|
||||
while (meas) {
|
||||
if (meas->gps_valid && meas->ta_valid) {
|
||||
geo2space(&x, &y, &z, meas->longitude, meas->latitude);
|
||||
sum_x += x;
|
||||
sum_y += y;
|
||||
sum_z += z;
|
||||
n++;
|
||||
}
|
||||
meas = meas->next;
|
||||
}
|
||||
if (!n)
|
||||
return;
|
||||
if (n < 3) {
|
||||
x = sum_x / n;
|
||||
y = sum_y / n;
|
||||
z = sum_z / n;
|
||||
space2geo(&longitude, &latitude, x, y, z);
|
||||
} else {
|
||||
struct probe *probe_first = NULL, *probe,
|
||||
**probe_last_p = &probe_first;
|
||||
double x_scale;
|
||||
|
||||
/* translate to flat surface */
|
||||
meas = cell->meas;
|
||||
x_scale = 1.0 / cos(meas->latitude / 180.0 * PI);
|
||||
longitude = meas->longitude;
|
||||
latitude = meas->latitude;
|
||||
debug_x_scale = x_scale;
|
||||
debug_long = longitude;
|
||||
debug_lat = latitude;
|
||||
debug_fp = outfp;
|
||||
while (meas) {
|
||||
if (meas->gps_valid && meas->ta_valid) {
|
||||
probe = calloc(1, sizeof(struct probe));
|
||||
if (!probe)
|
||||
nomem();
|
||||
probe->x = (meas->longitude - longitude) /
|
||||
x_scale;
|
||||
if (x < -180)
|
||||
x += 360;
|
||||
else if (x > 180)
|
||||
x -= 360;
|
||||
probe->y = meas->latitude - latitude;
|
||||
probe->dist = GSM_TA_M * (0.5 +
|
||||
(double)meas->ta) /
|
||||
(EQUATOR_RADIUS * PI / 180.0);
|
||||
*probe_last_p = probe;
|
||||
probe_last_p = &probe->next;
|
||||
}
|
||||
meas = meas->next;
|
||||
}
|
||||
|
||||
/* locate */
|
||||
locate_cell(probe_first, &x, &y);
|
||||
|
||||
/* translate from flat surface */
|
||||
longitude += x * x_scale;
|
||||
if (longitude < 0)
|
||||
longitude += 360;
|
||||
else if (longitude >= 360)
|
||||
longitude -= 360;
|
||||
latitude += y;
|
||||
|
||||
/* remove probes */
|
||||
while (probe_first) {
|
||||
probe = probe_first;
|
||||
probe_first = probe->next;
|
||||
free(probe);
|
||||
}
|
||||
|
||||
known = 1;
|
||||
}
|
||||
|
||||
if (!known)
|
||||
return;
|
||||
|
||||
fprintf(outfp, "\t\t\t\t\t<Placemark>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t<name>MCC=%s MNC=%s\nLAC=%04x "
|
||||
"CELL-ID=%04x\n(%s %s)</name>\n", gsm_print_mcc(cell->s.mcc),
|
||||
gsm_print_mnc(cell->s.mnc), cell->s.lac, cell->s.cell_id,
|
||||
gsm_get_mcc(cell->s.mcc),
|
||||
gsm_get_mnc(cell->s.mcc, cell->s.mnc));
|
||||
fprintf(outfp, "\t\t\t\t\t\t<description>\n");
|
||||
gsm48_sysinfo_dump(&cell->s, cell->sysinfo.arfcn, print_si, outfp);
|
||||
fprintf(outfp, "\t\t\t\t\t\t</description>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t<LookAt>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t\t<longitude>%.8f</longitude>\n",
|
||||
longitude);
|
||||
fprintf(outfp, "\t\t\t\t\t\t\t<latitude>%.8f</latitude>\n", latitude);
|
||||
fprintf(outfp, "\t\t\t\t\t\t\t<altitude>0</altitude>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t\t<tilt>0</tilt>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t\t<altitudeMode>relativeToGround"
|
||||
"</altitudeMode>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t\t<gx:altitudeMode>relativeToSeaFloor"
|
||||
"</gx:altitudeMode>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t</LookAt>\n");
|
||||
if (known)
|
||||
fprintf(outfp, "\t\t\t\t\t\t<styleUrl>#msn_placemark_grn_"
|
||||
"pushpin</styleUrl>\n");
|
||||
else
|
||||
fprintf(outfp, "\t\t\t\t\t\t<styleUrl>#msn_placemark_red_"
|
||||
"pushpin</styleUrl>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t<Point>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t\t\t<coordinates>%.8f,%.8f</coordinates>\n",
|
||||
longitude, latitude);
|
||||
fprintf(outfp, "\t\t\t\t\t\t</Point>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t</Placemark>\n");
|
||||
|
||||
if (!log_lines)
|
||||
return;
|
||||
|
||||
fprintf(outfp, "\t<Folder>\n");
|
||||
fprintf(outfp, "\t\t<name>Lines</name>\n");
|
||||
fprintf(outfp, "\t\t<open>0</open>\n");
|
||||
fprintf(outfp, "\t\t<visibility>0</visibility>\n");
|
||||
|
||||
geo2space(&x, &y, &z, longitude, latitude);
|
||||
meas = cell->meas;
|
||||
n = 0;
|
||||
while (meas) {
|
||||
if (meas->gps_valid) {
|
||||
double mx, my, mz, dist;
|
||||
|
||||
geo2space(&mx, &my, &mz, meas->longitude,
|
||||
meas->latitude);
|
||||
dist = distinspace(x, y, z, mx, my, mz);
|
||||
fprintf(outfp, "\t\t<Placemark>\n");
|
||||
fprintf(outfp, "\t\t\t<name>Range</name>\n");
|
||||
fprintf(outfp, "\t\t\t<description>\n");
|
||||
fprintf(outfp, "Distance: %d\n", (int)dist);
|
||||
fprintf(outfp, "TA=%d (%d-%d meter)\n", meas->ta,
|
||||
(int)(GSM_TA_M * meas->ta),
|
||||
(int)(GSM_TA_M * (meas->ta + 1)));
|
||||
fprintf(outfp, "\t\t\t</description>\n");
|
||||
fprintf(outfp, "\t\t\t<visibility>0</visibility>\n");
|
||||
fprintf(outfp, "\t\t\t<LineString>\n");
|
||||
fprintf(outfp, "\t\t\t\t<tessellate>1</tessellate>\n");
|
||||
fprintf(outfp, "\t\t\t\t<coordinates>\n");
|
||||
fprintf(outfp, "%.8f,%.8f\n", longitude, latitude);
|
||||
fprintf(outfp, "%.8f,%.8f\n", meas->longitude,
|
||||
meas->latitude);
|
||||
fprintf(outfp, "\t\t\t\t</coordinates>\n");
|
||||
fprintf(outfp, "\t\t\t</LineString>\n");
|
||||
fprintf(outfp, "\t\t</Placemark>\n");
|
||||
}
|
||||
meas = meas->next;
|
||||
}
|
||||
fprintf(outfp, "\t</Folder>\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
FILE *infp, *outfp;
|
||||
int type, n, i;
|
||||
char *p;
|
||||
struct node_mcc *mcc;
|
||||
struct node_mnc *mnc;
|
||||
struct node_lac *lac;
|
||||
struct node_cell *cell;
|
||||
struct node_meas *meas;
|
||||
|
||||
if (argc <= 2) {
|
||||
usage:
|
||||
fprintf(stderr, "Usage: %s <file.log> <file.kml> "
|
||||
"[lines] [debug]\n", argv[0]);
|
||||
fprintf(stderr, "lines: Add lines between cell and "
|
||||
"Measurement point\n");
|
||||
fprintf(stderr, "debug: Add debugging of location algorithm.\n"
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 3; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "lines"))
|
||||
log_lines = 1;
|
||||
else if (!strcmp(argv[i], "debug"))
|
||||
log_debug = 1;
|
||||
else goto usage;
|
||||
}
|
||||
|
||||
infp = fopen(argv[1], "r");
|
||||
if (!infp) {
|
||||
fprintf(stderr, "Failed to open '%s' for reading\n", argv[1]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
while ((type = read_log(infp))) {
|
||||
switch (type) {
|
||||
case LOG_TYPE_SYSINFO:
|
||||
add_sysinfo();
|
||||
break;
|
||||
case LOG_TYPE_POWER:
|
||||
add_power();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(infp);
|
||||
|
||||
if (!strcmp(argv[2], "-"))
|
||||
outfp = stdout;
|
||||
else
|
||||
outfp = fopen(argv[2], "w");
|
||||
if (!outfp) {
|
||||
fprintf(stderr, "Failed to open '%s' for writing\n", argv[2]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* document name */
|
||||
p = argv[2];
|
||||
while (strchr(p, '/'))
|
||||
p = strchr(p, '/') + 1;
|
||||
|
||||
kml_header(outfp, p);
|
||||
mcc = node_mcc_first;
|
||||
while (mcc) {
|
||||
printf("MCC: %02x\n", mcc->mcc);
|
||||
/* folder open */
|
||||
fprintf(outfp, "\t<Folder>\n");
|
||||
fprintf(outfp, "\t\t<name>MCC %s (%s)</name>\n",
|
||||
gsm_print_mcc(mcc->mcc), gsm_get_mcc(mcc->mcc));
|
||||
fprintf(outfp, "\t\t<open>0</open>\n");
|
||||
mnc = mcc->mnc;
|
||||
while (mnc) {
|
||||
printf(" MNC: %02x\n", mnc->mnc);
|
||||
/* folder open */
|
||||
fprintf(outfp, "\t\t<Folder>\n");
|
||||
fprintf(outfp, "\t\t\t<name>MNC %s (%s)</name>\n",
|
||||
gsm_print_mnc(mnc->mnc), gsm_get_mnc(mcc->mcc, mnc->mnc));
|
||||
fprintf(outfp, "\t\t\t<open>0</open>\n");
|
||||
lac = mnc->lac;
|
||||
while (lac) {
|
||||
printf(" LAC: %04x\n", lac->lac);
|
||||
/* folder open */
|
||||
fprintf(outfp, "\t\t\t<Folder>\n");
|
||||
fprintf(outfp, "\t\t\t\t<name>LAC %04x</name>\n", lac->lac);
|
||||
fprintf(outfp, "\t\t\t\t<open>0</open>\n");
|
||||
cell = lac->cell;
|
||||
while (cell) {
|
||||
printf(" CELL: %04x\n", cell->cellid);
|
||||
fprintf(outfp, "\t\t\t\t<Folder>\n");
|
||||
fprintf(outfp, "\t\t\t\t\t<name>CELL-ID %04x</name>\n", cell->cellid);
|
||||
fprintf(outfp, "\t\t\t\t\t<open>0</open>\n");
|
||||
meas = cell->meas;
|
||||
n = 0;
|
||||
while (meas) {
|
||||
if (meas->ta_valid)
|
||||
printf(" TA: %d\n", meas->ta);
|
||||
if (meas->gps_valid)
|
||||
kml_meas(outfp, meas, ++n, mcc->mcc, mnc->mnc,
|
||||
lac->lac, cell->cellid);
|
||||
meas = meas->next;
|
||||
}
|
||||
kml_cell(outfp, cell);
|
||||
/* folder close */
|
||||
fprintf(outfp, "\t\t\t\t</Folder>\n");
|
||||
cell = cell->next;
|
||||
}
|
||||
/* folder close */
|
||||
fprintf(outfp, "\t\t\t</Folder>\n");
|
||||
lac = lac->next;
|
||||
}
|
||||
/* folder close */
|
||||
fprintf(outfp, "\t\t</Folder>\n");
|
||||
mnc = mnc->next;
|
||||
}
|
||||
/* folder close */
|
||||
fprintf(outfp, "\t</Folder>\n");
|
||||
mcc = mcc->next;
|
||||
}
|
||||
#if 0
|
||||
FIXME: power
|
||||
/* folder open */
|
||||
fprintf(outfp, "\t<Folder>\n");
|
||||
fprintf(outfp, "\t\t<name>Power</name>\n");
|
||||
fprintf(outfp, "\t\t<open>0</open>\n");
|
||||
power = node_power_first;
|
||||
n = 0;
|
||||
while (power) {
|
||||
/* folder open */
|
||||
fprintf(outfp, "\t\t<Folder>\n");
|
||||
fprintf(outfp, "\t\t\t<name>Power %d</name>\n", ++n);
|
||||
fprintf(outfp, "\t\t\t<open>0</open>\n");
|
||||
/* folder close */
|
||||
fprintf(outfp, "\t\t</Folder>\n");
|
||||
power = power->next;
|
||||
}
|
||||
/* folder close */
|
||||
fprintf(outfp, "\t</Folder>\n");
|
||||
#endif
|
||||
kml_footer(outfp);
|
||||
|
||||
fclose(outfp);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
/* Algorithm to locate a destination by distance measurement:
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "geo.h"
|
||||
#include "locate.h"
|
||||
|
||||
#define CIRCLE_PROBE 30.0
|
||||
#define FINETUNE_RADIUS 5.0
|
||||
|
||||
extern double debug_long, debug_lat, debug_x_scale;
|
||||
extern FILE *debug_fp;
|
||||
extern int log_debug;
|
||||
|
||||
static double finetune_x[6], finetune_y[6], finetune_dist[6];
|
||||
|
||||
int locate_cell(struct probe *probe_first, double *min_x, double *min_y)
|
||||
{
|
||||
struct probe *probe, *min_probe;
|
||||
int i, test_steps, optimized;
|
||||
double min_dist, dist, x, y, rad, temp;
|
||||
double circle_probe, finetune_radius;
|
||||
|
||||
/* convert meters into degrees */
|
||||
circle_probe = CIRCLE_PROBE / (EQUATOR_RADIUS * PI / 180.0);
|
||||
finetune_radius = FINETUNE_RADIUS / (EQUATOR_RADIUS * PI / 180.0);
|
||||
|
||||
if (log_debug) {
|
||||
fprintf(debug_fp, "<Folder>\n");
|
||||
fprintf(debug_fp, "\t<name>Debug Locator</name>\n");
|
||||
fprintf(debug_fp, "\t<open>0</open>\n");
|
||||
fprintf(debug_fp, "\t<visibility>0</visibility>\n");
|
||||
}
|
||||
|
||||
/* get probe of minimum distance */
|
||||
min_probe = NULL;
|
||||
probe = probe_first;
|
||||
min_dist = 42;
|
||||
i = 0;
|
||||
while (probe) {
|
||||
if (log_debug) {
|
||||
fprintf(debug_fp, "\t<Placemark>\n");
|
||||
fprintf(debug_fp, "\t\t<name>MEAS</name>\n");
|
||||
fprintf(debug_fp, "\t\t<visibility>0</visibility>\n");
|
||||
fprintf(debug_fp, "\t\t<LineString>\n");
|
||||
fprintf(debug_fp, "\t\t\t<tessellate>1</tessellate>\n");
|
||||
fprintf(debug_fp, "\t\t\t<coordinates>\n");
|
||||
rad = 2.0 * 3.1415927 / 35;
|
||||
for (i = 0; i < 35; i++) {
|
||||
x = probe->x + probe->dist * sin(rad * i);
|
||||
y = probe->y + probe->dist * cos(rad * i);
|
||||
fprintf(debug_fp, "%.8f,%.8f\n", debug_long +
|
||||
x * debug_x_scale, debug_lat + y);
|
||||
}
|
||||
fprintf(debug_fp, "\t\t\t</coordinates>\n");
|
||||
fprintf(debug_fp, "\t\t</LineString>\n");
|
||||
fprintf(debug_fp, "\t</Placemark>\n");
|
||||
}
|
||||
|
||||
if (!min_probe || probe->dist < min_dist) {
|
||||
min_probe = probe;
|
||||
min_dist = probe->dist;
|
||||
}
|
||||
probe = probe->next;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i < 3) {
|
||||
fprintf(stderr, "Need at least 3 points\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* calculate the number of steps to search for destination point */
|
||||
test_steps = 2.0 * 3.1415927 * min_probe->dist / circle_probe;
|
||||
rad = 2.0 * 3.1415927 / test_steps;
|
||||
|
||||
if (log_debug) {
|
||||
fprintf(debug_fp, "\t<Placemark>\n");
|
||||
fprintf(debug_fp, "\t\t<name>Smallest MEAS</name>\n");
|
||||
fprintf(debug_fp, "\t\t<visibility>0</visibility>\n");
|
||||
fprintf(debug_fp, "\t\t<LineString>\n");
|
||||
fprintf(debug_fp, "\t\t\t<tessellate>1</tessellate>\n");
|
||||
fprintf(debug_fp, "\t\t\t<coordinates>\n");
|
||||
}
|
||||
|
||||
/* search on a circle for the location of the lowest distance
|
||||
* to the radius with the greatest distance */
|
||||
min_dist = 42;
|
||||
*min_x = *min_y = 42;
|
||||
for (i = 0; i < test_steps; i++) {
|
||||
x = min_probe->x + min_probe->dist * sin(rad * i);
|
||||
y = min_probe->y + min_probe->dist * cos(rad * i);
|
||||
if (log_debug)
|
||||
fprintf(debug_fp, "%.8f,%.8f\n", debug_long +
|
||||
x * debug_x_scale, debug_lat + y);
|
||||
/* look for greatest distance */
|
||||
dist = 0;
|
||||
probe = probe_first;
|
||||
while (probe) {
|
||||
if (probe != min_probe) {
|
||||
/* distance to the radius */
|
||||
temp = distonplane(probe->x, probe->y, x, y);
|
||||
temp -= probe->dist;
|
||||
if (temp < 0)
|
||||
temp = -temp;
|
||||
if (temp > dist)
|
||||
dist = temp;
|
||||
}
|
||||
probe = probe->next;
|
||||
}
|
||||
if (i == 0 || dist < min_dist) {
|
||||
min_dist = dist;
|
||||
*min_x = x;
|
||||
*min_y = y;
|
||||
}
|
||||
}
|
||||
|
||||
if (log_debug) {
|
||||
fprintf(debug_fp, "\t\t\t</coordinates>\n");
|
||||
fprintf(debug_fp, "\t\t</LineString>\n");
|
||||
fprintf(debug_fp, "\t</Placemark>\n");
|
||||
|
||||
fprintf(debug_fp, "\t<Placemark>\n");
|
||||
fprintf(debug_fp, "\t\t<name>Finetune</name>\n");
|
||||
fprintf(debug_fp, "\t\t<visibility>0</visibility>\n");
|
||||
fprintf(debug_fp, "\t\t<LineString>\n");
|
||||
fprintf(debug_fp, "\t\t\t<tessellate>1</tessellate>\n");
|
||||
fprintf(debug_fp, "\t\t\t<coordinates>\n");
|
||||
}
|
||||
|
||||
min_dist = 9999999999.0;
|
||||
tune_again:
|
||||
if (log_debug)
|
||||
fprintf(debug_fp, "%.8f,%.8f\n", debug_long +
|
||||
*min_x * debug_x_scale, debug_lat + *min_y);
|
||||
|
||||
/* finetune the point */
|
||||
rad = 2.0 * 3.1415927 / 6;
|
||||
for (i = 0; i < 6; i++) {
|
||||
x = *min_x + finetune_radius * sin(rad * i);
|
||||
y = *min_y + finetune_radius * cos(rad * i);
|
||||
/* search for the point with the lowest sum of distances */
|
||||
dist = 0;
|
||||
probe = probe_first;
|
||||
while (probe) {
|
||||
/* distance to the radius */
|
||||
temp = distonplane(probe->x, probe->y, x, y);
|
||||
temp -= probe->dist;
|
||||
if (temp < 0)
|
||||
temp = -temp;
|
||||
dist += temp;
|
||||
probe = probe->next;
|
||||
}
|
||||
finetune_dist[i] = dist;
|
||||
finetune_x[i] = x;
|
||||
finetune_y[i] = y;
|
||||
}
|
||||
|
||||
optimized = 0;
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (finetune_dist[i] < min_dist) {
|
||||
min_dist = finetune_dist[i];
|
||||
*min_x = finetune_x[i];
|
||||
*min_y = finetune_y[i];
|
||||
optimized = 1;
|
||||
}
|
||||
}
|
||||
if (optimized)
|
||||
goto tune_again;
|
||||
|
||||
if (log_debug) {
|
||||
fprintf(debug_fp, "\t\t\t</coordinates>\n");
|
||||
fprintf(debug_fp, "\t\t</LineString>\n");
|
||||
fprintf(debug_fp, "\t</Placemark>\n");
|
||||
fprintf(debug_fp, "</Folder>\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
struct probe {
|
||||
struct probe *next;
|
||||
double x, y, dist;
|
||||
};
|
||||
|
||||
int locate_cell(struct probe *probe_first, double *min_x, double *min_y);
|
||||
|
|
@ -0,0 +1,377 @@
|
|||
/* Conversion of logged cells to KML file */
|
||||
|
||||
/* (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <osmocom/bb/common/osmocom_data.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
extern struct power power;
|
||||
extern struct sysinfo sysinfo;
|
||||
extern struct node_power *node_power_first;
|
||||
extern struct node_power **node_power_last_p;
|
||||
extern struct node_mcc *node_mcc_first;
|
||||
|
||||
struct node_mcc *get_node_mcc(uint16_t mcc)
|
||||
{
|
||||
struct node_mcc *node_mcc;
|
||||
struct node_mcc **node_mcc_p = &node_mcc_first;
|
||||
|
||||
//printf("add mcc %d\n", mcc);
|
||||
while (*node_mcc_p) {
|
||||
/* found in list */
|
||||
if ((*node_mcc_p)->mcc == mcc)
|
||||
return *node_mcc_p;
|
||||
/* insert into list */
|
||||
if ((*node_mcc_p)->mcc > mcc)
|
||||
break;
|
||||
node_mcc_p = &((*node_mcc_p)->next);
|
||||
}
|
||||
|
||||
//printf("new mcc %d\n", mcc);
|
||||
/* append or insert to list */
|
||||
node_mcc = calloc(1, sizeof(struct node_mcc));
|
||||
if (!node_mcc)
|
||||
return NULL;
|
||||
node_mcc->mcc = mcc;
|
||||
node_mcc->next = *node_mcc_p;
|
||||
*node_mcc_p = node_mcc;
|
||||
return node_mcc;
|
||||
}
|
||||
|
||||
struct node_mnc *get_node_mnc(struct node_mcc *mcc, uint16_t mnc)
|
||||
{
|
||||
struct node_mnc *node_mnc;
|
||||
struct node_mnc **node_mnc_p = &mcc->mnc;
|
||||
|
||||
while (*node_mnc_p) {
|
||||
/* found in list */
|
||||
if ((*node_mnc_p)->mnc == mnc)
|
||||
return *node_mnc_p;
|
||||
/* insert into list */
|
||||
if ((*node_mnc_p)->mnc > mnc)
|
||||
break;
|
||||
node_mnc_p = &((*node_mnc_p)->next);
|
||||
}
|
||||
|
||||
/* append or insert to list */
|
||||
node_mnc = calloc(1, sizeof(struct node_mnc));
|
||||
if (!node_mnc)
|
||||
return NULL;
|
||||
node_mnc->mnc = mnc;
|
||||
node_mnc->next = *node_mnc_p;
|
||||
*node_mnc_p = node_mnc;
|
||||
return node_mnc;
|
||||
}
|
||||
|
||||
struct node_lac *get_node_lac(struct node_mnc *mnc, uint16_t lac)
|
||||
{
|
||||
struct node_lac *node_lac;
|
||||
struct node_lac **node_lac_p = &mnc->lac;
|
||||
|
||||
while (*node_lac_p) {
|
||||
/* found in list */
|
||||
if ((*node_lac_p)->lac == lac)
|
||||
return *node_lac_p;
|
||||
/* insert into list */
|
||||
if ((*node_lac_p)->lac > lac)
|
||||
break;
|
||||
node_lac_p = &((*node_lac_p)->next);
|
||||
}
|
||||
|
||||
/* append or insert to list */
|
||||
node_lac = calloc(1, sizeof(struct node_lac));
|
||||
if (!node_lac)
|
||||
return NULL;
|
||||
node_lac->lac = lac;
|
||||
node_lac->next = *node_lac_p;
|
||||
*node_lac_p = node_lac;
|
||||
return node_lac;
|
||||
}
|
||||
|
||||
struct node_cell *get_node_cell(struct node_lac *lac, uint16_t cellid)
|
||||
{
|
||||
struct node_cell *node_cell;
|
||||
struct node_cell **node_cell_p = &lac->cell;
|
||||
|
||||
while (*node_cell_p) {
|
||||
/* found in list */
|
||||
if ((*node_cell_p)->cellid == cellid)
|
||||
return *node_cell_p;
|
||||
/* insert into list */
|
||||
if ((*node_cell_p)->cellid > cellid)
|
||||
break;
|
||||
node_cell_p = &((*node_cell_p)->next);
|
||||
}
|
||||
|
||||
/* append or insert to list */
|
||||
node_cell = calloc(1, sizeof(struct node_cell));
|
||||
if (!node_cell)
|
||||
return NULL;
|
||||
node_cell->meas_last_p = &node_cell->meas;
|
||||
node_cell->cellid = cellid;
|
||||
node_cell->next = *node_cell_p;
|
||||
*node_cell_p = node_cell;
|
||||
return node_cell;
|
||||
}
|
||||
|
||||
struct node_meas *add_node_meas(struct node_cell *cell)
|
||||
{
|
||||
struct node_meas *node_meas;
|
||||
|
||||
/* append to list */
|
||||
node_meas = calloc(1, sizeof(struct node_meas));
|
||||
if (!node_meas)
|
||||
return NULL;
|
||||
node_meas->gmt = sysinfo.gmt;
|
||||
node_meas->rxlev = sysinfo.rxlev;
|
||||
if (sysinfo.ta_valid) {
|
||||
node_meas->ta_valid = 1;
|
||||
node_meas->ta = sysinfo.ta;
|
||||
}
|
||||
if (sysinfo.gps_valid) {
|
||||
node_meas->gps_valid = 1;
|
||||
node_meas->longitude = sysinfo.longitude;
|
||||
node_meas->latitude = sysinfo.latitude;
|
||||
}
|
||||
*cell->meas_last_p = node_meas;
|
||||
cell->meas_last_p = &node_meas->next;
|
||||
return node_meas;
|
||||
}
|
||||
|
||||
/* read "<ncc>,<bcc>" */
|
||||
static void read_log_bsic(char *buffer)
|
||||
{
|
||||
char *p;
|
||||
uint8_t bsic;
|
||||
|
||||
/* skip first spaces */
|
||||
while (*buffer == ' ')
|
||||
buffer++;
|
||||
|
||||
/* read ncc */
|
||||
p = buffer;
|
||||
while (*p > ' ' && *p != ',')
|
||||
p++;
|
||||
if (*p == '\0')
|
||||
return; /* no value */
|
||||
*p++ = '\0';
|
||||
bsic = atoi(buffer) << 3;
|
||||
buffer = p;
|
||||
|
||||
/* read latitude */
|
||||
bsic |= atoi(buffer);
|
||||
|
||||
sysinfo.bsic = bsic;
|
||||
}
|
||||
|
||||
/* read "<longitude> <latitude>" */
|
||||
static void read_log_pos(char *buffer, double *longitude, double *latitude,
|
||||
uint8_t *valid)
|
||||
{
|
||||
char *p;
|
||||
|
||||
/* skip first spaces */
|
||||
while (*buffer == ' ')
|
||||
buffer++;
|
||||
|
||||
/* read longitude */
|
||||
p = buffer;
|
||||
while (*p > ' ')
|
||||
p++;
|
||||
if (*p == '\0')
|
||||
return; /* no value after longitude */
|
||||
*p++ = '\0';
|
||||
*longitude = atof(buffer);
|
||||
buffer = p;
|
||||
|
||||
/* skip second spaces */
|
||||
while (*buffer == ' ')
|
||||
buffer++;
|
||||
|
||||
/* read latitude */
|
||||
*latitude = atof(buffer);
|
||||
|
||||
*valid = 1;
|
||||
}
|
||||
|
||||
/* read "<arfcn> <value> <next value> ...." */
|
||||
static void read_log_power(char *buffer)
|
||||
{
|
||||
char *p;
|
||||
int arfcn;
|
||||
|
||||
/* skip first spaces */
|
||||
while (*buffer == ' ')
|
||||
buffer++;
|
||||
|
||||
/* read arfcn */
|
||||
p = buffer;
|
||||
while (*p > ' ')
|
||||
p++;
|
||||
if (*p == '\0')
|
||||
return; /* no value after arfcn */
|
||||
*p++ = '\0';
|
||||
arfcn = atoi(buffer);
|
||||
buffer = p;
|
||||
|
||||
while (*buffer) {
|
||||
/* wrong arfcn */
|
||||
if (arfcn < 0 || arfcn > 1023)
|
||||
break;
|
||||
/* skip spaces */
|
||||
while (*buffer == ' ')
|
||||
buffer++;
|
||||
/* get value */
|
||||
p = buffer;
|
||||
while (*p > ' ')
|
||||
p++;
|
||||
/* last value */
|
||||
if (*p == '\0') {
|
||||
power.rxlev[arfcn] = atoi(buffer);
|
||||
break;
|
||||
}
|
||||
*p++ = '\0';
|
||||
power.rxlev[arfcn] = atoi(buffer);
|
||||
arfcn++;
|
||||
buffer = p;
|
||||
}
|
||||
}
|
||||
|
||||
/* read "xx xx xx xx xx...." */
|
||||
static void read_log_si(char *buffer, uint8_t *data)
|
||||
{
|
||||
uint8_t si[23];
|
||||
int i;
|
||||
|
||||
// printf("%s ", buffer);
|
||||
for (i = 0; i < 23; i++) {
|
||||
while (*buffer == ' ')
|
||||
buffer++;
|
||||
if (*buffer >= '0' && *buffer <= '9')
|
||||
si[i] = (*buffer - '0') << 4;
|
||||
else if (*buffer >= 'a' && *buffer <= 'f')
|
||||
si[i] = (*buffer - 'a' + 10) << 4;
|
||||
else if (*buffer >= 'A' && *buffer <= 'F')
|
||||
si[i] = (*buffer - 'A' + 10) << 4;
|
||||
else
|
||||
break;
|
||||
buffer++;
|
||||
if (*buffer >= '0' && *buffer <= '9')
|
||||
si[i] += *buffer - '0';
|
||||
else if (*buffer >= 'a' && *buffer <= 'f')
|
||||
si[i] += *buffer - 'a' + 10;
|
||||
else if (*buffer >= 'A' && *buffer <= 'F')
|
||||
si[i] += *buffer - 'A' + 10;
|
||||
else
|
||||
break;
|
||||
buffer++;
|
||||
// printf("%02x ", si[i]);
|
||||
}
|
||||
// printf("\n");
|
||||
|
||||
if (i == 23)
|
||||
memcpy(data, si, 23);
|
||||
}
|
||||
|
||||
/* read next record from log file */
|
||||
int read_log(FILE *infp)
|
||||
{
|
||||
static int type = LOG_TYPE_NONE, ret;
|
||||
char buffer[256];
|
||||
|
||||
memset(&sysinfo, 0, sizeof(sysinfo));
|
||||
memset(&power, 0, sizeof(power));
|
||||
memset(&power.rxlev, -128, sizeof(power.rxlev));
|
||||
|
||||
if (feof(infp))
|
||||
return LOG_TYPE_NONE;
|
||||
|
||||
while (fgets(buffer, sizeof(buffer), infp)) {
|
||||
buffer[sizeof(buffer) - 1] = 0;
|
||||
if (buffer[0])
|
||||
buffer[strlen(buffer) - 1] = '\0';
|
||||
if (buffer[0] == '[') {
|
||||
if (!strcmp(buffer, "[sysinfo]")) {
|
||||
ret = type;
|
||||
type = LOG_TYPE_SYSINFO;
|
||||
if (ret != LOG_TYPE_NONE)
|
||||
return ret;
|
||||
} else
|
||||
if (!strcmp(buffer, "[power]")) {
|
||||
ret = type;
|
||||
type = LOG_TYPE_POWER;
|
||||
if (ret != LOG_TYPE_NONE)
|
||||
return ret;
|
||||
} else {
|
||||
type = LOG_TYPE_NONE;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
switch (type) {
|
||||
case LOG_TYPE_SYSINFO:
|
||||
if (!strncmp(buffer, "arfcn ", 6))
|
||||
sysinfo.arfcn = atoi(buffer + 6);
|
||||
else if (!strncmp(buffer, "si1 ", 4))
|
||||
read_log_si(buffer + 4, sysinfo.si1);
|
||||
else if (!strncmp(buffer, "si2 ", 4))
|
||||
read_log_si(buffer + 4, sysinfo.si2);
|
||||
else if (!strncmp(buffer, "si2bis ", 7))
|
||||
read_log_si(buffer + 7, sysinfo.si2bis);
|
||||
else if (!strncmp(buffer, "si2ter ", 7))
|
||||
read_log_si(buffer + 7, sysinfo.si2ter);
|
||||
else if (!strncmp(buffer, "si3 ", 4))
|
||||
read_log_si(buffer + 4, sysinfo.si3);
|
||||
else if (!strncmp(buffer, "si4 ", 4))
|
||||
read_log_si(buffer + 4, sysinfo.si4);
|
||||
else if (!strncmp(buffer, "time ", 5))
|
||||
sysinfo.gmt = strtoul(buffer + 5, NULL, 0);
|
||||
else if (!strncmp(buffer, "position ", 9))
|
||||
read_log_pos(buffer + 9, &sysinfo.longitude,
|
||||
&sysinfo.latitude, &sysinfo.gps_valid);
|
||||
else if (!strncmp(buffer, "rxlev ", 5))
|
||||
sysinfo.rxlev =
|
||||
strtoul(buffer + 5, NULL, 0);
|
||||
else if (!strncmp(buffer, "bsic ", 5))
|
||||
read_log_bsic(buffer + 5);
|
||||
else if (!strncmp(buffer, "ta ", 3)) {
|
||||
sysinfo.ta_valid = 1;
|
||||
sysinfo.ta = atoi(buffer + 3);
|
||||
}
|
||||
break;
|
||||
case LOG_TYPE_POWER:
|
||||
if (!strncmp(buffer, "arfcn ", 6))
|
||||
read_log_power(buffer + 6);
|
||||
else if (!strncmp(buffer, "time ", 5))
|
||||
power.gmt = strtoul(buffer + 5, NULL, 0);
|
||||
else if (!strncmp(buffer, "position ", 9))
|
||||
read_log_pos(buffer + 9, &power.longitude,
|
||||
&power.latitude, &sysinfo.gps_valid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
|
||||
enum {
|
||||
LOG_TYPE_NONE = 0,
|
||||
LOG_TYPE_SYSINFO,
|
||||
LOG_TYPE_POWER,
|
||||
};
|
||||
|
||||
struct power {
|
||||
uint8_t gps_valid;
|
||||
double longitude, latitude;
|
||||
time_t gmt;
|
||||
int8_t rxlev[1024];
|
||||
};
|
||||
|
||||
struct node_power {
|
||||
struct node_power *next;
|
||||
struct power power;
|
||||
};
|
||||
|
||||
struct node_mcc {
|
||||
struct node_mcc *next;
|
||||
uint16_t mcc;
|
||||
struct node_mnc *mnc;
|
||||
};
|
||||
|
||||
struct node_mnc {
|
||||
struct node_mnc *next;
|
||||
uint16_t mnc;
|
||||
struct node_lac *lac;
|
||||
};
|
||||
|
||||
struct node_lac {
|
||||
struct node_lac *next;
|
||||
uint16_t lac;
|
||||
struct node_cell *cell;
|
||||
};
|
||||
|
||||
struct sysinfo {
|
||||
uint16_t arfcn;
|
||||
int8_t rxlev;
|
||||
uint8_t bsic;
|
||||
uint8_t gps_valid;
|
||||
double longitude, latitude;
|
||||
time_t gmt;
|
||||
uint8_t si1[23];
|
||||
uint8_t si2[23];
|
||||
uint8_t si2bis[23];
|
||||
uint8_t si2ter[23];
|
||||
uint8_t si3[23];
|
||||
uint8_t si4[23];
|
||||
uint8_t ta_valid;
|
||||
uint8_t ta;
|
||||
};
|
||||
|
||||
struct node_cell {
|
||||
struct node_cell *next;
|
||||
uint16_t cellid;
|
||||
uint8_t content; /* indicates, if sysinfo is already applied */
|
||||
struct node_meas *meas, **meas_last_p;
|
||||
struct sysinfo sysinfo;
|
||||
struct gsm48_sysinfo s;
|
||||
};
|
||||
|
||||
struct node_meas {
|
||||
struct node_meas *next;
|
||||
time_t gmt;
|
||||
int8_t rxlev;
|
||||
uint8_t gps_valid;
|
||||
double longitude, latitude;
|
||||
uint8_t ta_valid;
|
||||
uint8_t ta;
|
||||
};
|
||||
|
||||
struct node_mcc *get_node_mcc(uint16_t mcc);
|
||||
struct node_mnc *get_node_mnc(struct node_mcc *mcc, uint16_t mnc);
|
||||
struct node_lac *get_node_lac(struct node_mnc *mnc, uint16_t lac);
|
||||
struct node_cell *get_node_cell(struct node_lac *lac, uint16_t cellid);
|
||||
struct node_meas *add_node_meas(struct node_cell *cell);
|
||||
int read_log(FILE *infp);
|
||||
|
Loading…
Reference in New Issue