summaryrefslogtreecommitdiffstats
path: root/src/host
diff options
context:
space:
mode:
authorAndreas.Eversberg <jolly@eversberg.eu>2010-10-24 14:12:58 +0000
committerAndreas.Eversberg <jolly@eversberg.eu>2010-10-24 14:12:58 +0000
commita8ac4bb2dffe2dfe8030ad9f34ec10a4f9684d54 (patch)
treed384bb6d5a3b626f60d26ea452790b0ea7454801 /src/host
parent462015a91e3e64d41fedf91aec71c50cacf8e14c (diff)
Introducing "gsmmap" to convert SYSTEM INFORMATION log into a KML map
Diffstat (limited to 'src/host')
-rw-r--r--src/host/gsmmap/.gitignore35
-rw-r--r--src/host/gsmmap/Makefile.am18
-rw-r--r--src/host/gsmmap/configure.ac25
-rw-r--r--src/host/gsmmap/geo.c47
-rw-r--r--src/host/gsmmap/geo.h12
-rwxr-xr-xsrc/host/gsmmap/git-version-gen151
-rw-r--r--src/host/gsmmap/gsmmap.c646
-rw-r--r--src/host/gsmmap/locate.c182
-rw-r--r--src/host/gsmmap/locate.h8
-rw-r--r--src/host/gsmmap/log.c377
-rw-r--r--src/host/gsmmap/log.h80
11 files changed, 1581 insertions, 0 deletions
diff --git a/src/host/gsmmap/.gitignore b/src/host/gsmmap/.gitignore
new file mode 100644
index 00000000..661fd133
--- /dev/null
+++ b/src/host/gsmmap/.gitignore
@@ -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
diff --git a/src/host/gsmmap/Makefile.am b/src/host/gsmmap/Makefile.am
new file mode 100644
index 00000000..d866d02e
--- /dev/null
+++ b/src/host/gsmmap/Makefile.am
@@ -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
+
diff --git a/src/host/gsmmap/configure.ac b/src/host/gsmmap/configure.ac
new file mode 100644
index 00000000..45a00e2e
--- /dev/null
+++ b/src/host/gsmmap/configure.ac
@@ -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)
diff --git a/src/host/gsmmap/geo.c b/src/host/gsmmap/geo.c
new file mode 100644
index 00000000..3fb9b7a2
--- /dev/null
+++ b/src/host/gsmmap/geo.c
@@ -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);
+}
+
diff --git a/src/host/gsmmap/geo.h b/src/host/gsmmap/geo.h
new file mode 100644
index 00000000..25e26cba
--- /dev/null
+++ b/src/host/gsmmap/geo.h
@@ -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);
+
diff --git a/src/host/gsmmap/git-version-gen b/src/host/gsmmap/git-version-gen
new file mode 100755
index 00000000..652fac68
--- /dev/null
+++ b/src/host/gsmmap/git-version-gen
@@ -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:
diff --git a/src/host/gsmmap/gsmmap.c b/src/host/gsmmap/gsmmap.c
new file mode 100644
index 00000000..01f3670c
--- /dev/null
+++ b/src/host/gsmmap/gsmmap.c
@@ -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;
+}
diff --git a/src/host/gsmmap/locate.c b/src/host/gsmmap/locate.c
new file mode 100644
index 00000000..ed0ac931
--- /dev/null
+++ b/src/host/gsmmap/locate.c
@@ -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;
+}
diff --git a/src/host/gsmmap/locate.h b/src/host/gsmmap/locate.h
new file mode 100644
index 00000000..26133452
--- /dev/null
+++ b/src/host/gsmmap/locate.h
@@ -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);
+
diff --git a/src/host/gsmmap/log.c b/src/host/gsmmap/log.c
new file mode 100644
index 00000000..c2db801e
--- /dev/null
+++ b/src/host/gsmmap/log.c
@@ -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;
+}
+
diff --git a/src/host/gsmmap/log.h b/src/host/gsmmap/log.h
new file mode 100644
index 00000000..d1520101
--- /dev/null
+++ b/src/host/gsmmap/log.h
@@ -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);
+