wifiList = app.lightWifiManager.getScanResults();
+
+ try {
+ app.scanAPs.setText("AP: " + wifiList.size());
+ } catch (NullPointerException e) {
+ Log.e(TAG, "wifiList was null");
+ }
+ // Show strongest mac
+ app.textViewMessage1.setText(
+ "Strongest AP:" + getStrongestListeningMac(app.currentScanListGet()));
+
+ app.currentScanListClear();
+ LogRecord lr = null;
+
+ // If we receive results, add them to latest scan list
+ if (wifiList != null && !wifiList.isEmpty()) {
+ for (int i = 0; i < wifiList.size(); i++) {
+
+ if (isntProblematic(wifiList.get(i).BSSID)) {
+ lr = new LogRecord(wifiList.get(i).BSSID, wifiList.get(i).level);
+ app.currentScanListAdd(lr);
+ }
+ }
+ }
+
+ //save positions
+ if (WalkSimulatorClassGenerator.recordingRoute) {
+ WalkSimulatorClassGenerator.addNewPosition(app.currentScanListGet());
+ }
+
+ app.setBgProgressOff();
+
+ // If user is in trackme mode
+ // get partial rmap and then localize
+ if (app.isTrackmeEnabled() || app.isFindmeEnabled()) {
+ Intent intent = new Intent();
+ intent.setAction(App.BROADCAST_RECEIVER_GET_PARTIAL_RMAP);
+ app.sendBroadcast(intent);
+
+ // If findme was running, disable it and stop service
+ if (app.isFindmeEnabled() && !app.alwaysScan) {
+ app.stopService(new Intent(app, LocalizationService.class));
+ }
+ }
+ }
+
+ /**
+ * Returns true if Access Point isnt problematic Problematic: there is not data for it in our
+ * Server's database
+ *
+ * @return true if mac isnt problematic
+ */
+ private boolean isntProblematic(String mac) {
+ for (int i = 0; i < app.problematicAPs.size(); i++) {
+ if (mac.equals(app.problematicAPs.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/Preferences.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/Preferences.java
new file mode 100755
index 0000000..8ca36e4
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/Preferences.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+
+package cy.ac.ucy.cs.tvm;
+
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.provider.MediaStore.MediaColumns;
+import android.util.Log;
+import android.view.MenuItem;
+
+public class Preferences extends PreferenceActivity
+ implements SharedPreferences.OnSharedPreferenceChangeListener {
+
+ private static final String TAG = PreferenceActivity.class.getSimpleName();
+ int toggleCnt = 0;
+ boolean toggleDone = false;
+ private App app;
+
+ /** Build preference menu when the activity is first created. */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+
+ super.onCreate(savedInstanceState);
+
+ app = (App) getApplication();
+
+ getPreferenceManager().setSharedPreferencesName(getString(R.string.preferences));
+
+ addPreferencesFromResource(R.xml.preferences);
+ getPreferenceManager()
+ .getSharedPreferences()
+ .registerOnSharedPreferenceChangeListener(this);
+ }
+
+ /** Clear RAM Cache */
+ private void showClearRamCacheDialog() {
+
+ Builder builder = new Builder(this);
+ builder.setTitle("Clear parsed radiomaps");
+
+ builder.setMessage(
+ "Are you sure you want to delete all parsed radiomaps?\nNOTE: Application will reload!")
+ .setCancelable(true)
+ .setPositiveButton(
+ "Yes",
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ app.showToast("Restarting to take effect..");
+ Log.i(TAG, "Cleared RAM cache");
+
+ app.rmapCache.clearRamCache();
+ app.restartApp();
+ }
+ })
+ .setNegativeButton(
+ "No",
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+
+ AlertDialog alertDialog = builder.create();
+ alertDialog.show();
+ }
+
+ private void showClearSDCacheDialog() {
+
+ Builder builder = new Builder(this);
+ builder.setTitle("Clear stored radiomaps");
+
+ builder.setMessage(
+ "Are you sure you want to delete all radiomaps from your device's storage?")
+ .setCancelable(true)
+ .setPositiveButton(
+ "Yes",
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ app.rmapCache.clearSDCache();
+ }
+ })
+ .setNegativeButton(
+ "No",
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+
+ AlertDialog alertDialog = builder.create();
+ alertDialog.show();
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(
+ PreferenceScreen preferenceScreen, final Preference preference) {
+
+ try {
+ // Clear SD Cache
+ if (preference.getKey().equals(getString(R.string.pref_key_clear_sd_card_cache))) {
+
+ showClearSDCacheDialog();
+
+ } else if (preference.getKey().equals(getString(R.string.pref_key_k_anonymity))) {
+
+ App.showKAnonymityPickerDialog(this, App.MINIMUM_ANONYMITY, App.MAXIMUM_ANONYMITY);
+
+ } else if (preference.getKey().equals(getString(R.string.pref_key_clear_ram_cache))) {
+
+ showClearRamCacheDialog();
+
+ } else if (preference
+ .getKey()
+ .equals(getString(R.string.pref_key_developer_mode_toggle))) {
+ ++toggleCnt;
+
+ if (toggleCnt == 1) {
+ app.showToast("Are you a developer?");
+ }
+ if (toggleCnt > 4 && !toggleDone) {
+
+ app.showToast("You are now a developer!");
+ // Enable developer preference
+ Preference devPref =
+ findPreference(getString(R.string.pref_key_developer_mode));
+ devPref.setEnabled(true);
+ Preference realSim =
+ findPreference(getString(R.string.pref_key_real_simulation));
+ realSim.setEnabled(true);
+ toggleDone = true;
+ }
+ }
+ } catch (NullPointerException e) {
+ // do noth:
+ // some preferences dont have key (they are categories)
+ }
+ return false;
+ }
+
+ public String getRealPathFromURI(Uri contentUri) {
+ String[] proj = {MediaColumns.DATA};
+ Cursor cursor = managedQuery(contentUri, proj, null, null, null);
+ int column_index = cursor.getColumnIndexOrThrow(MediaColumns.DATA);
+ cursor.moveToFirst();
+ return cursor.getString(column_index);
+ }
+
+ /** Sets up a listener when the preference activity is resumed. */
+ @Override
+ protected void onResume() {
+ super.onResume();
+ // Set up a listener whenever a key changes
+ getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onBackPressed() {
+ super.onBackPressed();
+ this.finish();
+ }
+
+ /** Unregisters the listener before the preference activity is destroyed. */
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ // Unregister the listener whenever a key changes
+ getPreferenceManager()
+ .getSharedPreferences()
+ .unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ /** Unregisters the listener when a preference activity is paused. */
+ @Override
+ protected void onPause() {
+ super.onPause();
+ // Unregister the listener whenever a key changes
+ getPreferenceScreen()
+ .getSharedPreferences()
+ .unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * Do extra modifications when preferences change
+ *
+ * @see android.content.SharedPreferences.OnSharedPreferenceChangeListener#
+ * onSharedPreferenceChanged(android.content.SharedPreferences,
+ * java.lang.String)
+ */
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences preference, String preferenceKey) {
+
+ // Change SD Card caching status
+ if (preferenceKey.equals(getString(R.string.pref_key_sd_card_cache_status))) {
+
+ app.cacheSDcard = preference.getBoolean(preferenceKey, false);
+ Log.i(TAG, "SC card cache: " + app.cacheSDcard);
+
+ }
+ // Change RAM cache status
+ else if (preferenceKey.equals(getString(R.string.pref_key_ram_cache_status))) {
+
+ app.cacheRam = preference.getBoolean(preferenceKey, false);
+ Log.i(TAG, "RAM cache: " + app.cacheRam);
+
+ }
+ // Change localization algorithm
+ else if (preferenceKey.endsWith(getString(R.string.pref_key_localization_algorithm))) {
+ String algo =
+ preference.getString(getString(R.string.pref_key_localization_algorithm), "");
+ app.setLocalizationAlgorithm(algo);
+ Log.i(TAG, "Loc algo: " + app.localizationAlgorithm.toString());
+
+ }
+
+ // Change anonymity algorithm
+ else if (preferenceKey.endsWith(getString(R.string.pref_key_anonymity_algorithm))) {
+ String algo =
+ preference.getString(getString(R.string.pref_key_anonymity_algorithm), "");
+ app.setAnonymityAlgorithm(algo);
+ app.firstBloomDone = false;
+ Log.i(TAG, "Anon algo: " + app.anonymityAlgorithm.toString());
+
+ }
+ // Developer mode
+ else if (preferenceKey.equals(getString(R.string.pref_key_developer_mode))) {
+
+ app.developerMode = preference.getBoolean(preferenceKey, false);
+ Log.i(TAG, "Dev mode: " + app.developerMode);
+
+ }
+
+ // Real simulation mode
+ else if (preferenceKey.equals(getString(R.string.pref_key_real_simulation))) {
+
+ app.realSimulation = preference.getBoolean(preferenceKey, false);
+ Log.i(TAG, "Real simulation: " + app.realSimulation);
+
+ if (app.realSimulation) {
+ app.menuItemTrackMe.setEnabled(false);
+ app.menuItemFindMe.setEnabled(false);
+ } else {
+ app.menuItemTrackMe.setEnabled(true);
+ app.menuItemFindMe.setEnabled(true);
+ }
+
+ }
+ // Draw routes
+ else if (preferenceKey.equals(getString(R.string.pref_key_draw_routes))) {
+
+ app.drawRoutes = preference.getBoolean(preferenceKey, true);
+ Log.i(TAG, "Draw routes: " + app.drawRoutes);
+
+ }
+ // Always scan mode
+ else if (preferenceKey.equals(getString(R.string.pref_key_always_scan))) {
+
+ app.alwaysScan = preference.getBoolean(preferenceKey, false);
+
+ if (!app.isTrackmeEnabled()) {
+ app.stopLocalizationService();
+ }
+ Log.i(TAG, "Always scan: " + app.alwaysScan);
+
+ }
+ // K parameter
+ else if (preferenceKey.equals(getString(R.string.pref_key_k_parameter))) {
+
+ app.kParameter = Integer.parseInt(preference.getString(preferenceKey, "0"));
+ Log.i(TAG, "K parameter: " + app.kParameter);
+
+ }
+
+ // Samples interval
+ else if (preferenceKey.equals(getString(R.string.pref_key_samples_interval))) {
+
+ app.wifiScanInterval = Integer.parseInt(preference.getString(preferenceKey, "1000"));
+ Log.i(TAG, "Samples Interval: " + app.wifiScanInterval);
+
+ }
+ // Ram cache slots
+ else if (preferenceKey.equals(getString(R.string.pref_key_ram_cache_slots))) {
+
+ app.cacheRamSlots = Integer.parseInt(preference.getString(preferenceKey, "0"));
+ Log.i(TAG, "Ram slots: " + app.cacheRamSlots);
+
+ // Ram cleared
+ app.rmapCache.clearRamCache();
+ app.showToast("Ram cache cleared");
+ Log.i(TAG, "Cleared RAM cache");
+
+ }
+ // Datasets
+ else if (preferenceKey.equals(getString(R.string.pref_key_datasets))) {
+
+ app.datasetInUse = Integer.parseInt(preference.getString(preferenceKey, "0"));
+
+ Log.i(TAG, "Dataset in use: " + app.cacheRamSlots);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ this.finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/cache/RMapCache.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/cache/RMapCache.java
new file mode 100755
index 0000000..018a0be
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/cache/RMapCache.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+
+package cy.ac.ucy.cs.tvm.cache;
+
+import android.content.Context;
+import android.util.Log;
+import cy.ac.ucy.cs.tvm.App;
+import cy.ac.ucy.cs.tvm.AsyncTaskHttpExecutor;
+import cy.ac.ucy.cs.tvm.tvm.RadioMap;
+import java.io.File;
+import java.util.ArrayList;
+
+/**
+ * Radioamp cache: we cache the radiomaps either on SD card, so we don't need to download them each
+ * time we want to localize in a particular area that we visited before, and in RAM, so we don't
+ * have to parse the radiomap each time before localization.
+ *
+ * There are preferences that control both RAM and SD card caching.
+ */
+public class RMapCache {
+
+ private static String TAG = RMapCache.class.getSimpleName();
+
+ /** Radiomaps Cache. When disabled only 1st place is used */
+ public ArrayList radiomapCache;
+
+ /** Access point names of radiomap cache */
+ public ArrayList radiomapCacheAPs;
+
+ Context context;
+ App app;
+
+ /** SD Card directory */
+ File sdCardDir;
+
+ /** Cache directory: sdDir/cache/ */
+ File cacheDir;
+
+ public enum CacheType {
+ RAM,
+ SD,
+ MISS,
+ undefined
+ }
+
+ public static class CacheData {
+
+ public CacheType type = CacheType.undefined;
+ public RadioMap currentRmap = null;
+
+ /** Radiomap of Fake APs that 'is' currently in use */
+ public ArrayList currentFakeRmaps = new ArrayList();
+
+ /** Radiomap that is in SD cache */
+ public File currentRmapFile = null;
+
+ /** Current MAC address of APs range that is used for localization */
+ public String currentMac = null;
+
+ public CacheData(App app) {
+ // Create the fake empty radiomaps
+ for (int i = 0; i < app.getkAnonymity_m1(); i++) {
+ currentFakeRmaps.add(new RadioMap(app));
+ }
+ }
+
+ public void recycleFakeRmaps() {
+ for (int i = 0; i < currentFakeRmaps.size(); i++) currentFakeRmaps.get(i).recycle();
+ }
+ }
+
+ public RMapCache(Context context) {
+
+ this.radiomapCache = new ArrayList();
+ this.context = context;
+ this.app = (App) context.getApplicationContext();
+
+ this.radiomapCache = new ArrayList();
+ this.radiomapCacheAPs = new ArrayList();
+
+ // Create SD card directory
+ if (android.os.Environment.getExternalStorageState()
+ .equals(android.os.Environment.MEDIA_MOUNTED))
+ sdCardDir =
+ new File(
+ android.os.Environment.getExternalStorageDirectory(),
+ "Android/data/airplace/");
+ else sdCardDir = context.getCacheDir();
+ if (!sdCardDir.exists()) sdCardDir.mkdirs();
+
+ // Create cache directory
+ // Find the dir to save cached images
+ if (android.os.Environment.getExternalStorageState()
+ .equals(android.os.Environment.MEDIA_MOUNTED))
+ cacheDir =
+ new File(
+ android.os.Environment.getExternalStorageDirectory(),
+ "Android/data/airplace/cache/");
+ else cacheDir = context.getCacheDir();
+ if (!cacheDir.exists()) cacheDir.mkdirs();
+ }
+
+ /** Gets radiomap from server, and then cache it to SD Card and Download ram */
+ public void cacheRadiomapFile(CacheData cacheData) {
+
+ String[] parameters = {"", cacheData.currentMac};
+
+ new AsyncTaskHttpExecutor(
+ app, App.URL_VCENTER, App.AsyncTaskType.checkVpn, parameters, null)
+ .execute();
+ }
+
+ /** Returns directory name of where radiomap should be located */
+ public void fillCacheData(ArrayList macs) {
+ String cacheDirS = getCacheDirectory();
+ String filename;
+ int strongestMacIndex = -1;
+ int ramCacheIndex = -1;
+ File file = null;
+
+ boolean found = false;
+
+ // IF RAM caching is enabled
+ if (app.cacheRam) {
+
+ // Find strongest mac that is RAM cached
+ // For all the listening APs
+ for (int i = 0; i < macs.size(); i++) {
+
+ // And the RAM cached APs
+ for (int j = 0; j < radiomapCacheAPs.size(); j++) {
+ if (radiomapCacheAPs.get(j).equals(macs.get(i))) {
+ strongestMacIndex = i;
+ ramCacheIndex = j;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ // Ram cache hit - Save it to current rmap cache
+ if (found) {
+ // Set RAM cache type, radiomap cache, and strongest index
+ app.cacheData.type = CacheType.RAM;
+ app.cacheData.currentRmap = this.radiomapCache.get(ramCacheIndex);
+ app.cacheData.currentMac = macs.get(strongestMacIndex);
+
+ return;
+ }
+ }
+
+ if (app.cacheSDcard) {
+ // Find strongest mac that is SD cached
+ for (int i = 0; i < macs.size(); i++) {
+
+ filename = cacheDirS + "/" + macs.get(i);
+ file = new File(filename);
+
+ // Check if filename exists
+ if (file.exists()) {
+ // Found file
+ strongestMacIndex = i;
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ // Set SD cache type, radiomap cache, and strongest
+ // index
+ app.cacheData.type = CacheType.SD;
+ app.cacheData.currentRmapFile = file;
+ app.cacheData.currentMac = macs.get(strongestMacIndex);
+
+ return;
+ }
+ }
+
+ // Cache MISS
+ // if no cache files found, use the strongest mac
+ app.cacheData.currentMac = macs.get(0);
+
+ // where file will SD cached
+ app.cacheData.currentRmapFile = new File(cacheDirS + "/" + app.cacheData.currentMac);
+ app.cacheData.type = CacheType.MISS;
+
+ return;
+ }
+
+ /** Find out if problematic mac doesnt already exists */
+ public boolean isProblematicMac(String problematicMac) {
+ for (int i = 0; i < app.problematicAPs.size(); i++) {
+ if (app.problematicAPs.get(i).equals(problematicMac)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Clears all caches */
+ public void clearSDCache() {
+ File[] files = cacheDir.listFiles();
+
+ if (files == null) return;
+
+ // Cache already empty
+ if (cacheDir.listFiles().length == 0) {
+ app.showToast("Cache was already empty");
+
+ return;
+ }
+
+ for (File f : files) f.delete();
+
+ if (cacheDir.listFiles().length != 0) {
+ // Cache cleared
+ app.showToast("Something went wrong. Clear cache manually");
+
+ } else {
+ Log.i(TAG, "Cleared SD Card cache");
+ }
+ }
+
+ public void clearRamCache() {
+ this.radiomapCacheAPs.clear();
+ this.radiomapCache.clear();
+ }
+
+ /** @return caches directory */
+ public String getCacheDirectory() {
+ return cacheDir.toString() + "/";
+ }
+
+ /**
+ * FUTURE remove and leave only the above. notice the extra slash!
+ *
+ * @return caches directory
+ */
+ public String getSDCardDirectory() {
+ return cacheDir.toString();
+ }
+
+ /**
+ * Creates a new slot in RAM for parsed rmap, and returns it. If RAM cache limit is reached,
+ * then it removes the LRU rmap.
+ */
+ public RadioMap ramCacheParsedRadiomap(String macAddress) {
+
+ RadioMap result;
+
+ // Cache is not full yet
+ if (radiomapCache.size() < app.cacheRamSlots) {
+ // Create new rmap
+ result = new RadioMap(app);
+ // Cache radiomap + its MAC address
+ radiomapCache.add(result);
+ radiomapCacheAPs.add(macAddress);
+ }
+ // Cache is full
+ else {
+
+ // Radiomap to use is the slot last in cache
+ result = radiomapCache.get(app.cacheRamSlots - 1);
+
+ //Remove it from cache & macs too
+ radiomapCache.remove(app.cacheRamSlots - 1);
+ radiomapCacheAPs.remove(app.cacheRamSlots - 1);
+
+ result.recycle();
+
+ //Add it again in first place
+ radiomapCache.add(result);
+ radiomapCacheAPs.add(macAddress);
+ }
+ return result;
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/map/MapUtilities.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/map/MapUtilities.java
new file mode 100644
index 0000000..5d9f619
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/map/MapUtilities.java
@@ -0,0 +1,554 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+
+package cy.ac.ucy.cs.tvm.map;
+
+import static cy.ac.ucy.cs.tvm.MainActivity.localized;
+import static cy.ac.ucy.cs.tvm.MainActivity.tiltHandler;
+import static cy.ac.ucy.cs.tvm.tvm.CoarseLocation.REQUEST_PERMISSION_COARSE_LOCATION;
+import static cy.ac.ucy.cs.tvm.tvm.CoarseLocation.REQUEST_PERMISSION_FINE_LOCATION;
+
+import android.Manifest;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.location.Address;
+import android.location.Geocoder;
+import android.location.Location;
+import android.os.Build;
+import android.support.v4.app.ActivityCompat;
+import com.google.android.gms.maps.CameraUpdateFactory;
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.model.BitmapDescriptor;
+import com.google.android.gms.maps.model.BitmapDescriptorFactory;
+import com.google.android.gms.maps.model.CameraPosition;
+import com.google.android.gms.maps.model.GroundOverlay;
+import com.google.android.gms.maps.model.GroundOverlayOptions;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.Marker;
+import com.google.android.gms.maps.model.MarkerOptions;
+import com.google.android.gms.maps.model.Polyline;
+import com.google.android.gms.maps.model.PolylineOptions;
+import cy.ac.ucy.cs.tvm.App;
+import cy.ac.ucy.cs.tvm.MainActivity;
+import cy.ac.ucy.cs.tvm.R;
+import cy.ac.ucy.cs.tvm.tvm.CoarseLocation;
+import cy.ac.ucy.cs.tvm.tvm.LocalizationAlgorithms;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains everything related to the GoogleMap instance and GeoLocation API. adding/removing
+ * markers, drawing routes, calculating dinstances, fancy animations, etc.
+ */
+public class MapUtilities {
+
+ private static final String TAG = MapUtilities.class.getSimpleName();
+ private static final int MEDIUM_ZOOM = 14;
+ private static final int MIN_ZOOM = 5;
+ public static int MAX_ZOOM = 20;
+ public static boolean liveTilt = true;
+ public static boolean touchingScreen = false;
+ public static boolean cameraMoving = false;
+ public static GoogleMap googleMap;
+ public static RotatingMarker realPositionMarker;
+ public static boolean gotCoarseLocation = false;
+ public static double realRouteDistance = 0;
+ public static GroundOverlay ucyBuildingGroundOverlay;
+ public static ArrayList fakeLocationMarkers;
+ public static ArrayList currentFakeLocationMarkers;
+ public static ArrayList loggerSavedSpots;
+ private static double[] fakeRouteLengths;
+ /** Real routes on googleMap */
+ static ArrayList realRoutePolyline = new ArrayList();
+
+ static CameraPosition previousCameraPosition;
+
+ /** Center the googleMap in coarse location. Run on application init */
+ public static void centerMapInCoarseLocation(App app) {
+
+ // Show the marker in the center of the world!
+ realPositionMarker = new RotatingMarker(googleMap, app);
+ realPositionMarker.addToMap(new LatLng(0, 0));
+
+ //Try to get coarse location from Google's Location Services using WiFi
+ CoarseLocation.LocationResult locationResult =
+ new CoarseLocation.LocationResult() {
+
+ @Override
+ public void gotLocation(Location location) {
+ gotCoarseLocation = true;
+
+ try {
+ // Zoom googleMap to the location
+ double gotLat = location.getLatitude();
+ double gotLon = location.getLongitude();
+
+ previousCameraPosition = googleMap.getCameraPosition();
+
+ fancyMoveToInitialLocation(
+ new LatLng(gotLat, gotLon),
+ 500,
+ (int) previousCameraPosition.zoom,
+ MEDIUM_ZOOM);
+
+ //move to coarse location
+ realPositionMarker.moveMarker(new LatLng(gotLat, gotLon));
+
+ } catch (NullPointerException e) {
+ //failed to get coarse location
+ }
+ }
+ };
+
+ CoarseLocation myLocation = new CoarseLocation();
+ myLocation.getLocation(app.getApplicationContext(), locationResult);
+ }
+
+ private static void fancyMoveToInitialLocation(
+ final LatLng ll, final int time, final int zoomFrom, final int zoomTo) {
+
+ // Zoom googleMap to coarse location
+ googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(ll, zoomFrom));
+
+ tiltHandler.postDelayed(
+ new Runnable() {
+
+ @Override
+ public void run() {
+ cameraMoving = true;
+ tiltHandler.removeCallbacks(MainActivity.reEnableTil);
+
+ // Zoom in, animating the camera.
+ googleMap.animateCamera(
+ CameraUpdateFactory.zoomTo(zoomTo),
+ time,
+ new GoogleMap.CancelableCallback() {
+
+ @Override
+ public void onFinish() {
+ cameraMoving = false;
+ }
+
+ @Override
+ public void onCancel() {
+ cameraMoving = false;
+ }
+ });
+ }
+ },
+ 1000);
+ }
+
+ /** Show marker according to user movements on googleMap */
+ public static void updateMap(App app) {
+
+ // Re-center googleMap
+ if (!localized) {
+ moveMapFromLocationToLocation(app.currentCoordinates, 200, 1000, MIN_ZOOM, MAX_ZOOM);
+ }
+
+ try {
+
+ realPositionMarker.moveMarker(app.currentCoordinates);
+ // if previous coordinates exist, draw real route
+ if (app.drawRoutes && app.previousCoordinates.latitude != 0) {
+
+ // Clear latest previous point from routes
+ for (int i = 0; i < realRoutePolyline.size(); i++) {
+ LatLng prev = realRoutePolyline.get(i).getPoints().get(0);
+
+ if (prev.equals(app.previousCoordinates)) {
+ realRoutePolyline.get(i).setVisible(false);
+ }
+ }
+
+ // Calculate route dinstance
+ realRouteDistance +=
+ LocalizationAlgorithms.distance(
+ app.previousCoordinates, app.currentCoordinates);
+
+ realPositionMarker.updateDistance((int) realRouteDistance);
+
+ // Real route polyline
+ realRoutePolyline.add(
+ googleMap.addPolyline(
+ new PolylineOptions()
+ .add(app.previousCoordinates, app.currentCoordinates)
+ .width(5)
+ .color(
+ app.getResources()
+ .getColor(android.R.color.holo_blue_dark))
+ .geodesic(true)));
+
+ int fakesSize = app.fakeMatchedLocations.size();
+
+ if (fakesSize >= 2) {
+
+ ArrayList curFakeLocs = app.fakeMatchedLocations.get(fakesSize - 1);
+
+ ArrayList prevFakeLocs = app.fakeMatchedLocations.get(fakesSize - 2);
+
+ int size =
+ curFakeLocs.size() < prevFakeLocs.size()
+ ? curFakeLocs.size()
+ : prevFakeLocs.size();
+
+ for (int i = 0; i < curFakeLocs.size(); i++) {
+ LatLng prevFakeLoc = prevFakeLocs.get(i);
+ LatLng curFakeLoc = curFakeLocs.get(i);
+
+ // Draw last 2 locs
+ googleMap.addPolyline(
+ new PolylineOptions()
+ .add(prevFakeLoc, curFakeLoc)
+ .width(5)
+ .color(Color.RED)
+ .geodesic(true));
+ }
+ }
+ }
+
+ } catch (NullPointerException e) {
+ // Noth - ignore first localization line draw
+ }
+
+ localized = true;
+ }
+
+ /** Fancy animation to move the map between two points */
+ public static synchronized void moveMapFromLocationToLocation(
+ final LatLng ll,
+ final int timeZoomOut,
+ final int timeZoomIn,
+ final int zoomOutTo,
+ final int zoomInTo) {
+
+ cameraMoving = true;
+ tiltHandler.removeCallbacks(MainActivity.reEnableTil);
+
+ previousCameraPosition = googleMap.getCameraPosition();
+
+ if ((int) previousCameraPosition.zoom > zoomOutTo) {
+
+ // Zoom out camera
+ googleMap.animateCamera(
+ CameraUpdateFactory.zoomTo(zoomOutTo),
+ timeZoomOut,
+ new GoogleMap.CancelableCallback() {
+
+ @Override
+ public void onFinish() {
+ // Zoom googleMap to coarse location
+ googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(ll, zoomOutTo));
+
+ // Zoom in, animating the camera.
+ googleMap.animateCamera(
+ CameraUpdateFactory.zoomTo(zoomInTo),
+ timeZoomIn,
+ new GoogleMap.CancelableCallback() {
+
+ @Override
+ public void onFinish() {
+ cameraMoving = false;
+ }
+
+ @Override
+ public void onCancel() {
+ cameraMoving = false;
+ }
+ });
+ }
+
+ @Override
+ public void onCancel() {
+ cameraMoving = false;
+ }
+ });
+ } else {
+ // Zoom googleMap to coarse location
+ googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(ll, zoomOutTo));
+
+ // Zoom in, animating the camera.
+ googleMap.animateCamera(
+ CameraUpdateFactory.zoomTo(zoomInTo),
+ timeZoomIn,
+ new GoogleMap.CancelableCallback() {
+
+ @Override
+ public void onFinish() {
+ cameraMoving = false;
+ }
+
+ @Override
+ public void onCancel() {
+ cameraMoving = false;
+ }
+ });
+ }
+ }
+
+ private static synchronized void moveMapToNewCoordinate(LatLng ll, int timeZoomOut) {
+ cameraMoving = true;
+ tiltHandler.removeCallbacks(MainActivity.reEnableTil);
+
+ googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(ll, timeZoomOut));
+ googleMap.animateCamera(
+ CameraUpdateFactory.zoomTo(MAX_ZOOM),
+ 1,
+ new GoogleMap.CancelableCallback() {
+
+ @Override
+ public void onFinish() {
+ cameraMoving = false;
+ }
+
+ @Override
+ public void onCancel() {
+ cameraMoving = false;
+ }
+ });
+ }
+
+ private static synchronized void moveMapToNewCoordinate(
+ LatLng ll, int timeZoom, int zoomLevel) {
+
+ cameraMoving = true;
+ tiltHandler.removeCallbacks(MainActivity.reEnableTil);
+
+ previousCameraPosition = googleMap.getCameraPosition();
+
+ // Zoom googleMap to coarse location
+ googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(ll, previousCameraPosition.zoom));
+
+ googleMap.animateCamera(
+ CameraUpdateFactory.zoomTo(zoomLevel),
+ timeZoom,
+ new GoogleMap.CancelableCallback() {
+
+ @Override
+ public void onFinish() {
+ cameraMoving = false;
+ }
+
+ @Override
+ public void onCancel() {
+ cameraMoving = false;
+ }
+ });
+ }
+
+ /** Zoom out camera, and then zoom in */
+ public static synchronized void cameraZoom(int zoomLevel, int time) {
+
+ cameraMoving = true;
+ tiltHandler.removeCallbacks(MainActivity.reEnableTil);
+
+ previousCameraPosition = googleMap.getCameraPosition();
+
+ // Zoom googleMap to coarse location
+ googleMap.moveCamera(
+ CameraUpdateFactory.newLatLngZoom(
+ previousCameraPosition.target, previousCameraPosition.zoom));
+
+ googleMap.animateCamera(
+ CameraUpdateFactory.zoomTo(zoomLevel),
+ time,
+ new GoogleMap.CancelableCallback() {
+
+ @Override
+ public void onFinish() {
+ cameraMoving = false;
+ }
+
+ @Override
+ public void onCancel() {
+ cameraMoving = false;
+ }
+ });
+ }
+
+ public static synchronized void tiltCamera(float nadir) {
+ if (googleMap == null) return; // map not ready yet
+
+ nadir = (int) (nadir / 1.5);
+ if (nadir < 0) nadir *= -1;
+ if (nadir > 45) nadir = 45;
+ previousCameraPosition = googleMap.getCameraPosition();
+
+ // INFO bearing is the horizontal
+ CameraPosition currentPlace =
+ new CameraPosition.Builder()
+ .tilt(nadir)
+ .target(previousCameraPosition.target)
+ .zoom(previousCameraPosition.zoom)
+ .bearing(previousCameraPosition.bearing)
+ .build();
+ googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(currentPlace));
+ }
+
+ @TargetApi(Build.VERSION_CODES.M)
+ public static void initializeMap(Context ctx, App app) {
+ MapUtilities.googleMap.getUiSettings().setZoomControlsEnabled(false);
+ MapUtilities.googleMap.setOnMarkerClickListener(
+ new GoogleMap.OnMarkerClickListener() {
+ @Override
+ public boolean onMarkerClick(Marker marker) {
+ LatLng to = marker.getPosition();
+ moveMapFromLocationToLocation(to, 50, 100, MEDIUM_ZOOM, 22);
+ return false;
+ }
+ });
+ googleMap.clear(); // remove any routes, markers, etc
+
+ googleMap.getUiSettings().setTiltGesturesEnabled(false);
+ googleMap.setPadding(5, 200, 0, 0);
+
+ app.layoutTopMessages.setBackgroundColor(ctx.getColor(R.color.teal600));
+
+ BitmapDescriptor image =
+ BitmapDescriptorFactory.fromBitmap(
+ BitmapFactory.decodeResource(
+ app.getResources(), R.drawable.cs_floor2)); // get an image.
+
+ // Ucy building
+ ucyBuildingGroundOverlay =
+ googleMap.addGroundOverlay(
+ new GroundOverlayOptions()
+ .image(image)
+ .anchor(0, 0)
+ .bearing(51.3f)
+ .transparency(0.4f)
+ .position(App.CS_UCY_0_0, 76.5f, 39.75f));
+
+ if (ActivityCompat.checkSelfPermission(
+ (Activity) ctx, Manifest.permission.ACCESS_FINE_LOCATION)
+ != PackageManager.PERMISSION_GRANTED
+ && ActivityCompat.checkSelfPermission(
+ (Activity) ctx, Manifest.permission.ACCESS_COARSE_LOCATION)
+ != PackageManager.PERMISSION_GRANTED) {
+
+ ActivityCompat.requestPermissions(
+ (Activity) ctx,
+ new String[] {Manifest.permission.ACCESS_COARSE_LOCATION},
+ REQUEST_PERMISSION_COARSE_LOCATION);
+ ActivityCompat.requestPermissions(
+ (Activity) ctx,
+ new String[] {Manifest.permission.ACCESS_FINE_LOCATION},
+ REQUEST_PERMISSION_FINE_LOCATION);
+ } else {
+ centerMapInCoarseLocation(app);
+ }
+ }
+
+ public static void unveilMatches(App app) {
+ // Clear previous markers
+ for (int i = 0; i < currentFakeLocationMarkers.size(); i++) {
+ currentFakeLocationMarkers.get(i).remove();
+ }
+ currentFakeLocationMarkers.clear();
+
+ if (app.fakeMatchedLocations.size() >= 1) {
+
+ // Calculate fake route lengths
+ fakeRouteLengths = new double[app.fakeMatchedLocations.get(0).size()];
+
+ for (int j = 0; j < app.fakeMatchedLocations.size() - 1; j++) {
+ ArrayList cur = app.fakeMatchedLocations.get(j);
+ ArrayList next = app.fakeMatchedLocations.get(j + 1);
+
+ for (int i = 0; i < fakeRouteLengths.length; i++) {
+ fakeRouteLengths[i] += LocalizationAlgorithms.distance(cur.get(i), next.get(i));
+ }
+ }
+
+ ArrayList tmpLatestPoints =
+ app.fakeMatchedLocations.get(app.fakeMatchedLocations.size() - 1);
+
+ // Show new fake position markers
+ for (int i = 0; i < tmpLatestPoints.size(); i++) {
+
+ Geocoder geocoder = new Geocoder(app);
+ List address = null;
+ try {
+ address =
+ geocoder.getFromLocation(
+ tmpLatestPoints.get(i).latitude,
+ tmpLatestPoints.get(i).longitude,
+ 1);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ String addressString = "";
+
+ ArrayList tmpLengths = new ArrayList();
+ try {
+
+ String locality = address.get(0).getLocality();
+ String countryName = address.get(0).getCountryName();
+
+ if (locality == null) locality = "";
+ if (countryName == null) countryName = "";
+
+ addressString += ": " + locality + ", " + countryName;
+
+ // init lens
+ for (int j = 0; j < app.fakeMatchedLocations.get(0).size(); j++) {
+ tmpLengths.add("0");
+ }
+
+ for (int j = 0; j < fakeRouteLengths.length; j++) {
+
+ int len = (int) fakeRouteLengths[j];
+ String res = "";
+ if (len > 1000) {
+ len /= 1000;
+ res = len + " km";
+ } else {
+ res = len + " m";
+ }
+
+ tmpLengths.set(j, res);
+ }
+
+ currentFakeLocationMarkers.add(
+ googleMap.addMarker(
+ new MarkerOptions()
+ .position(tmpLatestPoints.get(i))
+ .anchor(0.5f, 0.5f)
+ .title("Route distance: " + tmpLengths.get(i))
+ .snippet("Fake location" + addressString)
+ .icon(
+ BitmapDescriptorFactory.fromResource(
+ R.drawable.vm_red_dot_obscured_on))));
+ } catch (Exception e) {
+ // Noth
+ }
+ }
+ }
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/map/RotatingMarker.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/map/RotatingMarker.java
new file mode 100755
index 0000000..664de02
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/map/RotatingMarker.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+
+package cy.ac.ucy.cs.tvm.map;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.location.Address;
+import android.location.Geocoder;
+import android.os.Looper;
+import android.util.Log;
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.model.BitmapDescriptorFactory;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.Marker;
+import com.google.android.gms.maps.model.MarkerOptions;
+import cy.ac.ucy.cs.tvm.App;
+import cy.ac.ucy.cs.tvm.R;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Just a fancy rotating marker put on the GoogleMap to show the current position */
+public class RotatingMarker extends android.support.v4.app.FragmentActivity {
+
+ private static final String TAG = RotatingMarker.class.getSimpleName();
+ private static final int TOTAL_DEGREES = 360;
+ private static final int NAV_ICON = R.drawable.direction_pointer;
+ App mApp;
+ private GoogleMap mMap;
+ private Bitmap mOriginalBitmap;
+ /** Main dot */
+ private Marker mMainMarker;
+ /** Show the direction */
+ private List mPointerMarkers;
+
+ /**
+ * A blinking marker, with a custom frequency and fps.
+ *
+ * @param map - the GoogleMap instance to which the marker is attached
+ * @param app - the frequency of the blinking in milliseconds
+ */
+ public RotatingMarker(GoogleMap map, App app) {
+ this.mApp = app;
+ this.mMap = map;
+
+ // get the bitmap icon
+ mOriginalBitmap = BitmapFactory.decodeResource(app.getResources(), NAV_ICON);
+ }
+
+ /**
+ * Add markers to map
+ *
+ * @throws IllegalStateException not in ui thread
+ */
+ public void addToMap(LatLng position) throws IllegalStateException {
+ checkIfUiThread();
+ if (mPointerMarkers != null) {
+ Log.w(TAG, "Marker was already added.");
+ return;
+ }
+
+ Geocoder geocoder = new Geocoder(mApp);
+ List address = null;
+ try {
+ address = geocoder.getFromLocation(position.latitude, position.longitude, 1);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ String addressString = "";
+
+ try {
+ addressString +=
+ ": " + address.get(0).getLocality() + ", " + address.get(0).getCountryName();
+ } catch (Exception e) {
+ // Noth
+ }
+
+ //Add the main marker
+ MarkerOptions markerOptions =
+ new MarkerOptions()
+ .position(position)
+ .anchor(0.5f, 0.5f)
+ .title("Route Distance: 0")
+ .snippet("Real location" + addressString)
+ .icon(BitmapDescriptorFactory.fromResource(R.drawable.blue_dot_no_shadow));
+ this.mMainMarker = mMap.addMarker(markerOptions);
+ this.mMainMarker.setVisible(true);
+
+ // Add the pointer markers
+ mPointerMarkers = new ArrayList();
+ for (int i = 0; i < TOTAL_DEGREES; i++) {
+ mPointerMarkers.add(
+ createNewPointerMarker(generateRotatedBitmap(mOriginalBitmap, i), position));
+ }
+ }
+
+ /**
+ * Removes the marker from the map. It could free up a lot of memory, so use this when you don't
+ * need the marker anymore.
+ *
+ * @throws IllegalStateException - if it isn't called form the UI thread
+ */
+ public void removeMarker() throws IllegalStateException {
+ checkIfUiThread();
+ removeMarkers();
+ }
+
+ /** Moves the marker to a new position, in sync with the rotating. */
+ public void moveMarker(LatLng newPosition) {
+ moveMarkers(newPosition);
+ }
+
+ private void removeMarkers() {
+ if (mPointerMarkers == null) {
+ return;
+ }
+
+ for (Marker marker : mPointerMarkers) {
+ marker.remove();
+ }
+ mPointerMarkers = null;
+ }
+
+ private void moveMarkers(final LatLng newPosition) {
+
+ mMainMarker.setPosition(newPosition);
+
+ for (Marker marker : mPointerMarkers) {
+ marker.setPosition(newPosition);
+ }
+ }
+
+ private void changeMarkerRotation(final int currentID, final int previousID) {
+ mPointerMarkers.get(currentID).setVisible(true);
+ mPointerMarkers.get(previousID).setVisible(false);
+ }
+
+ /** Rotate marker according to heading */
+ public void rotateMarker(int currentHeading, int previousHeading) {
+ if (currentHeading == previousHeading) return;
+
+ // Swap bitmaps
+ changeMarkerRotation(currentHeading, previousHeading);
+ }
+
+ /** Create a new pointer marker */
+ private Marker createNewPointerMarker(Bitmap bitmap, LatLng position) {
+ MarkerOptions markerOptions =
+ new MarkerOptions()
+ .position(position)
+ .anchor(0.5f, 0.5f)
+ .icon(BitmapDescriptorFactory.fromBitmap(bitmap));
+ Marker marker = mMap.addMarker(markerOptions);
+ marker.setVisible(false);
+ return marker;
+ }
+
+ /** Generate a new bitmat according to rotation */
+ private Bitmap generateRotatedBitmap(Bitmap source, int rotation) {
+
+ Bitmap targetBitmap =
+ Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(targetBitmap);
+ Matrix matrix = new Matrix();
+ matrix.setRotate(rotation, source.getWidth() / 2, source.getHeight() / 2);
+ canvas.drawBitmap(source, matrix, new Paint());
+
+ return targetBitmap;
+ }
+
+ private void checkIfUiThread() throws IllegalStateException {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ throw new IllegalStateException("This call has to be made from the UI thread.");
+ }
+ }
+
+ /** Update distance of main dot in humar readable format */
+ public void updateDistance(int realRouteDistance) {
+ String res = "";
+ if (realRouteDistance > 1000) {
+ realRouteDistance /= 1000;
+ res = realRouteDistance + " km";
+ } else {
+ res = realRouteDistance + " m";
+ }
+
+ mMainMarker.setTitle("Route Distance: " + res);
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/CoarseLocation.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/CoarseLocation.java
new file mode 100755
index 0000000..774af80
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/CoarseLocation.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+
+package cy.ac.ucy.cs.tvm.tvm;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.ActivityCompat;
+import java.util.Timer;
+
+/** Getting Coarse Location readings of the user */
+public class CoarseLocation {
+
+ public static final int REQUEST_PERMISSION_COARSE_LOCATION = 1;
+ public static final int REQUEST_PERMISSION_FINE_LOCATION = 2;
+
+ Timer timer1;
+ LocationManager lm;
+ LocationResult locationResult;
+ boolean network_enabled = false;
+
+ public boolean getLocation(Context context, LocationResult result) {
+ //I use LocationResult callback class to pass location value from MyLocation to user code.
+ locationResult = result;
+
+ // permissions not acquired yet
+ if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
+ != PackageManager.PERMISSION_GRANTED
+ && ActivityCompat.checkSelfPermission(
+ context, Manifest.permission.ACCESS_COARSE_LOCATION)
+ != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+
+ if (lm == null) lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+
+ //exceptions will be thrown if provider is not permitted.
+ try {
+ network_enabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
+ } catch (Exception ex) {
+ }
+
+ //don't start listeners if no provider is enabled
+ if (!network_enabled) return false;
+
+ if (network_enabled) {
+ lm.requestLocationUpdates(
+ LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);
+ }
+
+ timer1 = new Timer();
+
+ Handler mainHandler = new Handler(context.getMainLooper());
+
+ Runnable myRunnable =
+ new Runnable() {
+
+ @Override
+ public void run() {
+ lm.removeUpdates(locationListenerGps);
+ lm.removeUpdates(locationListenerNetwork);
+
+ Location net_loc = null, gps_loc = null;
+
+ if (network_enabled)
+ net_loc = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
+
+ //if there are both values use the latest one
+ if (gps_loc != null && net_loc != null) {
+ if (gps_loc.getTime() > net_loc.getTime())
+ locationResult.gotLocation(gps_loc);
+ else locationResult.gotLocation(net_loc);
+ return;
+ }
+
+ if (gps_loc != null) {
+ locationResult.gotLocation(gps_loc);
+ return;
+ }
+ if (net_loc != null) {
+ locationResult.gotLocation(net_loc);
+ return;
+ }
+ locationResult.gotLocation(null);
+ }
+ };
+
+ mainHandler.post(myRunnable);
+
+ return true;
+ }
+
+ LocationListener locationListenerGps =
+ new LocationListener() {
+
+ public void onLocationChanged(Location location) {
+ timer1.cancel();
+ locationResult.gotLocation(location);
+ lm.removeUpdates(this);
+ lm.removeUpdates(locationListenerNetwork);
+ }
+
+ public void onProviderDisabled(String provider) {}
+
+ public void onProviderEnabled(String provider) {}
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {}
+ };
+
+ LocationListener locationListenerNetwork =
+ new LocationListener() {
+ public void onLocationChanged(Location location) {
+ timer1.cancel();
+ locationResult.gotLocation(location);
+ lm.removeUpdates(this);
+ lm.removeUpdates(locationListenerGps);
+ }
+
+ public void onProviderDisabled(String provider) {}
+
+ public void onProviderEnabled(String provider) {}
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {}
+ };
+
+ public abstract static class LocationResult {
+ public abstract void gotLocation(Location location);
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/DownloadingSettings.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/DownloadingSettings.java
new file mode 100755
index 0000000..8b2fc3d
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/DownloadingSettings.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+
+package cy.ac.ucy.cs.tvm.tvm;
+
+import android.os.Handler;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.Socket;
+
+/** Settings of the communicatin to the HBase server */
+public class DownloadingSettings extends Thread {
+
+ private final String IP;
+ private final String PORT;
+ private final String filename_radiomap_download;
+ private final String folder_path; // radiomap download location
+
+ private String errMsg;
+ private final Handler handler;
+
+ public DownloadingSettings(
+ String IP, String PORT, String folder_path, String filename, Handler handler) {
+ this.filename_radiomap_download = filename;
+ this.PORT = PORT;
+ this.IP = IP;
+ this.folder_path = folder_path;
+ this.handler = handler;
+ }
+
+ public String getErrMsg() {
+ return errMsg;
+ }
+
+ /** Establishes a connection on IP/PORT and download radiomap */
+ public void run() {
+
+ Socket connection = null;
+ FileOutputStream fos = null;
+ File root = new File(folder_path);
+
+ String radiomap_mean = filename_radiomap_download;
+ String rbf_weights = radiomap_mean + "-rbf-weights";
+ String parameters = radiomap_mean + "-parameters";
+
+ try {
+
+ connection = new Socket(IP, Integer.parseInt(PORT));
+
+ // path is writable
+ if (root.canWrite()) {
+ fos = new FileOutputStream(new File(root, radiomap_mean), false);
+ } else {
+ errMsg =
+ "Directory: "
+ + root.getAbsolutePath()
+ + " is not writable.\nYou may need an external memory card";
+ handler.sendEmptyMessage(-2);
+ return;
+ }
+
+ PrintWriter out = new PrintWriter(connection.getOutputStream(), true);
+ BufferedReader in =
+ new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ String inputLine, outputLine;
+
+ inputLine = in.readLine();
+
+ if (!inputLine.equalsIgnoreCase("+OK READY")) {
+ fos.close();
+ out.close();
+ in.close();
+ connection.close();
+ errMsg = "Server not ready.\nTry again later.";
+ handler.sendEmptyMessage(-2);
+ return;
+ }
+
+ outputLine = "GET radiomap";
+ out.println(outputLine);
+ inputLine = in.readLine();
+
+ if (!inputLine.startsWith("RADIOMAP")) {
+ fos.close();
+ out.close();
+ in.close();
+ connection.close();
+ errMsg = inputLine + "";
+ handler.sendEmptyMessage(-2);
+ return;
+ }
+
+ inputLine = inputLine.replaceFirst("RADIOMAP ", "");
+ fos.write((inputLine + "\n").getBytes());
+
+ // Get files: radiomap, parameters and rbf weights
+ while ((inputLine = in.readLine()) != null) {
+ if (inputLine.compareTo("null") == 0 || inputLine.startsWith("CORRUPTED")) break;
+
+ if (inputLine.equalsIgnoreCase("PARAMETERS")) {
+ fos = new FileOutputStream(new File(root, parameters), false);
+ } else if (inputLine.equalsIgnoreCase("RBF_WEIGHTS")) {
+ fos = new FileOutputStream(new File(root, rbf_weights), false);
+ } else {
+ fos.write((inputLine + "\n").getBytes());
+ }
+ }
+
+ fos.close();
+ out.close();
+ in.close();
+ connection.close();
+
+ errMsg = null;
+ handler.sendEmptyMessage(-2);
+
+ } catch (Exception e) {
+ errMsg = "Error: " + e.getMessage();
+ handler.sendEmptyMessage(-2);
+ }
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/Heading.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/Heading.java
new file mode 100755
index 0000000..20f1666
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/Heading.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: J. Metochi jmetoc01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+
+package cy.ac.ucy.cs.tvm.tvm;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import cy.ac.ucy.cs.tvm.MainActivity;
+
+/** Heading of the device: where the device is pointing at. */
+public class Heading implements SensorEventListener {
+
+ private SensorManager mSensorManager = null;
+ public static float azimuth;
+ private static float nadir;
+ private Sensor orientation;
+
+ public Heading(Context context) {
+ mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ }
+
+ public static float getNadir() {
+ if (nadir < 0) return -nadir;
+ return nadir;
+ }
+
+ public void pause() {
+ mSensorManager.unregisterListener(this);
+ }
+
+ public void resume() {
+ registerSensors();
+ }
+
+ private void registerSensors() {
+ orientation = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
+
+ if (orientation == null) {
+ return;
+ }
+
+ if (orientation != null) {
+ mSensorManager.registerListener(this, orientation, SensorManager.SENSOR_DELAY_GAME);
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor arg0, int arg1) {}
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+
+ azimuth = event.values[0];
+ nadir = event.values[1];
+ MainActivity.updateDeveloperInfoLabels();
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LightWifiManager.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LightWifiManager.java
new file mode 100755
index 0000000..4a78eed
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LightWifiManager.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+package cy.ac.ucy.cs.tvm.tvm;
+
+import android.content.Context;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import android.util.Log;
+
+import java.util.List;
+
+import cy.ac.ucy.cs.tvm.App;
+
+/**
+ * An interface to the Wifi hardware of the device:
+ *
+ *
+ * - starts wifi
+ *
- stops wifi
+ *
- returns wifi scan results
+ *
+ */
+public class LightWifiManager {
+
+ public static final String TAG = LightWifiManager.class.getSimpleName();
+ App app;
+ WifiManager mainWifi;
+
+ public LightWifiManager(App app) {
+ this.app = app;
+
+ mainWifi = (WifiManager) app.getSystemService(Context.WIFI_SERVICE);
+ app.wasWifiEnabled = mainWifi.isWifiEnabled();
+ }
+
+ public void start() {
+ enableWifi();
+ }
+
+ public void stop() {
+ disableWifi();
+ }
+
+ public void scanArea() {
+ mainWifi.startScan();
+ }
+
+ private void disableWifi() {
+ if (!app.wasWifiEnabled) {
+ Log.i(TAG, "Disabling wifi");
+ if (mainWifi.isWifiEnabled())
+ if (mainWifi.getWifiState() != WifiManager.WIFI_STATE_DISABLING)
+ mainWifi.setWifiEnabled(false);
+ }
+ }
+
+ private void enableWifi() {
+ if (!mainWifi.isWifiEnabled())
+ if (mainWifi.getWifiState() != WifiManager.WIFI_STATE_ENABLING)
+ mainWifi.setWifiEnabled(true);
+ }
+
+ public List getScanResults() {
+ return mainWifi.getScanResults();
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LocDistance.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LocDistance.java
new file mode 100755
index 0000000..0df2531
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LocDistance.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+package cy.ac.ucy.cs.tvm.tvm;
+
+/** A DAO class. */
+public class LocDistance {
+
+ private double distance;
+ private String location;
+
+ public LocDistance(double distance, String location) {
+ this.distance = distance;
+ this.location = location;
+ }
+
+ public double getDistance() {
+ return distance;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LocalizationAlgorithms.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LocalizationAlgorithms.java
new file mode 100755
index 0000000..c546ce3
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LocalizationAlgorithms.java
@@ -0,0 +1,666 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+
+package cy.ac.ucy.cs.tvm.tvm;
+
+import com.google.android.gms.maps.model.LatLng;
+
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.util.Log;
+import android.view.View;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+
+import cy.ac.ucy.cs.tvm.App;
+import cy.ac.ucy.cs.tvm.MainActivity;
+import cy.ac.ucy.cs.tvm.cache.RMapCache;
+import cy.ac.ucy.cs.tvm.tvm.simulation.TestTVM;
+
+import static cy.ac.ucy.cs.tvm.map.MapUtilities.updateMap;
+
+/**
+ * Localization algorithms:
+ *
+ *
+ * - K Nearest Neighbor (KNN)
+ *
- Weighted K Nearest Neighbor (WKNN)
+ *
- Minimum Measure Square Error (MMSE)
+ *
- Weighted Minimum Measure Square Error (WMMSE)
+ *
+ */
+public class LocalizationAlgorithms {
+
+ private static final String TAG = LocalizationAlgorithms.class.getSimpleName();
+ private App app;
+
+ public LocalizationAlgorithms(App app) {
+ this.app = app;
+ }
+
+ /**
+ * Calculates the Euclidean distance between the currently observed RSS values and the RSS
+ * values for a specific location.
+ *
+ * @param l1 RSS values of a location in radiomap
+ * @param l2 RSS values currently observed
+ * @return The Euclidean distance, or MIN_VALUE for error
+ */
+ private static double calculateEuclideanDistance(ArrayList l1, ArrayList l2) {
+
+ double finalResult = 0, v1, v2, temp;
+ String str;
+
+ for (int i = 0; i < l1.size(); ++i) {
+ try {
+ str = l1.get(i);
+ v1 = Double.valueOf(str.trim()).doubleValue();
+ str = l2.get(i);
+ v2 = Double.valueOf(str.trim()).doubleValue();
+ } catch (Exception e) {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ // do the procedure
+ temp = v1 - v2;
+ temp *= temp;
+
+ // do the procedure
+ finalResult += temp;
+ }
+
+ return ((double) Math.sqrt(finalResult));
+ }
+
+ /**
+ * Calculates the Euclidean distance between the currently observed RSS values and the RSS
+ * values for a specific location.
+ *
+ * @param l1 RSS values of a location in radiomap
+ * @param l2 RSS values currently observed
+ * @return The Euclidean distance, or MIN_VALUE for error
+ */
+ public static double calculateEuclideanDistanceDouble1(LatLng l1, LatLng l2) {
+
+ double finalResult = 0, temp;
+
+ temp = l1.latitude - l2.latitude;
+ temp *= temp;
+ finalResult += temp;
+
+ temp = l1.longitude - l2.longitude;
+ temp *= temp;
+ finalResult += temp;
+
+ return (Math.sqrt(finalResult));
+ }
+
+ /**
+ * Calculates the Probability of the user being in the currently observed RSS values and the RSS
+ * values for a specific location.
+ *
+ * @param l1 RSS values of a location in radiomap
+ * @param l2 RSS values currently observed
+ * @return The Probability for this location, or MIN_VALUE for error
+ */
+ public static double calculateProbability(
+ ArrayList l1, ArrayList l2, double sGreek) {
+
+ double finalResult = 1, v1, v2, temp;
+ String str;
+
+ for (int i = 0; i < l1.size(); ++i) {
+
+ try {
+ str = l1.get(i);
+ v1 = Double.valueOf(str.trim()).doubleValue();
+ str = l2.get(i);
+ v2 = Double.valueOf(str.trim()).doubleValue();
+ } catch (Exception e) {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ temp = v1 - v2;
+ temp *= temp;
+ temp = -temp;
+ temp /= (double) (sGreek * sGreek);
+ temp = (double) Math.exp(temp);
+ finalResult *= temp;
+ }
+ return finalResult;
+ }
+
+ /**
+ * Reads the parameters from the file FUTURE check this
+ *
+ * @param file the file of radiomap, to read parameters
+ * @param algorithm_choice choice of several algorithms
+ * @return The parameter for the algorithm
+ */
+ private static String readParameter(File file, int algorithm_choice) {
+
+ String line;
+ BufferedReader reader = null;
+ FileReader fr = null;
+
+ String parameter = null;
+
+ try {
+ fr = new FileReader(file.getAbsolutePath() + "-parameters");
+ reader = new BufferedReader(fr);
+
+ while ((line = reader.readLine()) != null) {
+
+ /* Ignore the labels */
+ if (line.startsWith("#") || line.trim().equals("")) {
+ continue;
+ }
+
+ /* Split fields */
+ String[] temp = line.split(":");
+
+ /* The file may be corrupted so ignore reading it */
+ if (temp.length != 2) {
+ return null;
+ }
+
+ if (algorithm_choice == 0 && temp[0].equals("NaN")) {
+ parameter = temp[1];
+ break;
+ } else if (algorithm_choice == 1 && temp[0].equals("KNN")) {
+ parameter = temp[1];
+ break;
+ } else if (algorithm_choice == 2 && temp[0].equals("WKNN")) {
+ parameter = temp[1];
+ break;
+ } else if (algorithm_choice == 3 && temp[0].equals("MAP")) {
+ parameter = temp[1];
+ break;
+ } else if (algorithm_choice == 4 && temp[0].equals("MMSE")) {
+ parameter = temp[1];
+ break;
+ }
+ }
+ fr.close();
+ reader.close();
+ } catch (Exception e) {
+ return null;
+ }
+
+ return parameter;
+ }
+
+ /** Dinstance between two locs in */
+ public static double distance(LatLng StartP, LatLng EndP) {
+ double lat1 = StartP.latitude;
+ double lat2 = EndP.latitude;
+ double lon1 = StartP.longitude;
+ double lon2 = EndP.longitude;
+ double dLat = Math.toRadians(lat2 - lat1);
+ double dLon = Math.toRadians(lon2 - lon1);
+ double a =
+ Math.sin(dLat / 2) * Math.sin(dLat / 2)
+ + Math.cos(Math.toRadians(lat1))
+ * Math.cos(Math.toRadians(lat2))
+ * Math.sin(dLon / 2)
+ * Math.sin(dLon / 2);
+ double c = 2 * Math.asin(Math.sqrt(a));
+ return 6366000 * c;
+ }
+
+ /**
+ * Calculate users location
+ *
+ * @param latestScanList the current scan list of APs
+ * @param radioMap the constructed Radio Map
+ * @param heading choice of several algorithms
+ * @return the location of user
+ */
+ public boolean calculateRealLocation(
+ ArrayList latestScanList,
+ RadioMap radioMap,
+ int heading,
+ App.LocalizationAlgorithm algorithm) {
+
+ int i, j, notFoundCounter = 0;
+ ArrayList MacAdressList = radioMap.getmacAddresses();
+ ArrayList Observed_RSS_Values = new ArrayList();
+ LogRecord temp_LR;
+ boolean found;
+
+ // Check which mac addresses of radio map, we are currently listening.
+ for (i = 0; i < MacAdressList.size(); ++i) {
+
+ found = false;
+ for (j = 0; j < latestScanList.size(); ++j) {
+
+ temp_LR = latestScanList.get(j);
+
+ // MAC Address Matched
+ if (MacAdressList.get(i).compareTo(temp_LR.getBssid()) == 0) {
+
+ Observed_RSS_Values.add(String.valueOf(temp_LR.getRss()));
+ found = true;
+ break;
+ }
+ }
+ // A MAC Address is missing so we place a small value, NaN value
+ // if (j == latestScanList.size()){
+ if (!found) {
+ Observed_RSS_Values.add(String.valueOf(App.NanValueUsed));
+ ++notFoundCounter;
+ }
+ }
+
+ if (notFoundCounter == MacAdressList.size()) {
+ return false;
+ }
+
+ Log.i(TAG, "Algorithm used: " + algorithm.toString());
+ int param = 4;
+ boolean result = false;
+
+ switch (algorithm) {
+ case KNN:
+ result = KNN_WKNN_Algorithm(radioMap, Observed_RSS_Values, param, heading, false);
+ break;
+ case WKNN:
+ result = KNN_WKNN_Algorithm(radioMap, Observed_RSS_Values, param, heading, true);
+ break;
+ case MMSE:
+ result = MAP_MMSE_Algorithm(radioMap, Observed_RSS_Values, param, heading, false);
+ break;
+ case WMMSE:
+ result = MAP_MMSE_Algorithm(radioMap, Observed_RSS_Values, param, heading, true);
+ break;
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates user location based on Weighted/Not Weighted K Nearest Neighbor (KNN) Algorithm
+ *
+ * @param RMap The radio map structure
+ * @param Observed_RSS_Values RSS values currently observed
+ * @param isWeighted To be weighted or not
+ * @return The estimated user location
+ */
+ private boolean KNN_WKNN_Algorithm(
+ RadioMap RMap,
+ ArrayList Observed_RSS_Values,
+ int parameter,
+ int heading,
+ boolean isWeighted) {
+
+ ArrayList RSS_Values;
+ double curResult = 0;
+ ArrayList LocDistance_Results_List = new ArrayList();
+ boolean foundLocation = false;
+ int K = parameter;
+
+ // Find appropriate hashmap
+ HashMap> hasmap = RMap.getlocationRssHashMap(heading);
+
+ // Construct a list with locations-distances pairs for currently
+ // observed RSS values
+ for (String location : hasmap.keySet()) {
+ RSS_Values = hasmap.get(location);
+ curResult = calculateEuclideanDistance(RSS_Values, Observed_RSS_Values);
+
+ if (curResult == Double.NEGATIVE_INFINITY) {
+ Log.e(TAG, "Negative infinity curRes: " + curResult);
+ return false;
+ }
+
+ LocDistance_Results_List.add(0, new LocDistance(curResult, location));
+ }
+
+ // Sort locations-distances pairs based on minimum distances
+ Collections.sort(
+ LocDistance_Results_List,
+ new Comparator() {
+
+ public int compare(LocDistance gd1, LocDistance gd2) {
+ return (gd1.getDistance() > gd2.getDistance()
+ ? 1
+ : (gd1.getDistance() == gd2.getDistance() ? 0 : -1));
+ }
+ });
+
+ if (!isWeighted) {
+ foundLocation = calculateAverageKDistanceLocations(LocDistance_Results_List, K);
+ } else {
+ foundLocation = calculateWeightedAverageKDistanceLocations(LocDistance_Results_List, K);
+ }
+
+ return foundLocation;
+ }
+
+ /**
+ * Calculates user location based on Probabilistic Maximum A Posteriori (MAP) Algorithm or
+ * Probabilistic Minimum Mean Square Error (MMSE) Algorithm
+ *
+ * @param RMap The radio map structure
+ * @param Observed_RSS_Values RSS values currently observed
+ * @param isWeighted To be weighted or not
+ * @return The estimated user location
+ */
+ private boolean MAP_MMSE_Algorithm(
+ RadioMap RMap,
+ ArrayList Observed_RSS_Values,
+ int parameter,
+ int heading,
+ boolean isWeighted) {
+
+ ArrayList RSS_Values;
+ double curResult = 0.0d;
+ boolean foundLocation = false;
+ double highestProbability = Double.NEGATIVE_INFINITY;
+ ArrayList LocDistance_Results_List = new ArrayList();
+ double sGreek = parameter;
+
+ // Find appropriate hashmap
+ HashMap> hasmap = RMap.getlocationRssHashMap(heading);
+
+ // Find the location of user with the highest probability
+ for (String location : hasmap.keySet()) {
+
+ RSS_Values = hasmap.get(location);
+ curResult = calculateProbability(RSS_Values, Observed_RSS_Values, sGreek);
+
+ if (curResult == Double.NEGATIVE_INFINITY) return false;
+ else if (curResult > highestProbability) {
+ highestProbability = curResult;
+
+ String[] loc = location.split(" ");
+ app.currentCoordinates =
+ new LatLng(Double.parseDouble(loc[0]), Double.parseDouble(loc[1]));
+
+ return true;
+ }
+
+ if (isWeighted) LocDistance_Results_List.add(0, new LocDistance(curResult, location));
+ }
+
+ if (isWeighted)
+ foundLocation = calculateWeightedAverageProbabilityLocations(LocDistance_Results_List);
+
+ return foundLocation;
+ }
+
+ /**
+ * Calculates the Average of the K locations that have the shortest distances D
+ *
+ * @param LocDistance_Results_List Locations-Distances pairs sorted by distance
+ * @param K The number of locations used
+ * @return The estimated user location, or null for error
+ */
+ private boolean calculateAverageKDistanceLocations(
+ ArrayList LocDistance_Results_List, int K) {
+
+ double sumX = 0.0f;
+ double sumY = 0.0f;
+
+ String[] LocationArray = new String[2];
+ double x, y;
+
+ int K_Min = K < LocDistance_Results_List.size() ? K : LocDistance_Results_List.size();
+
+ // Calculate the sum of X and Y
+ for (int i = 0; i < K_Min; ++i) {
+ LocationArray = LocDistance_Results_List.get(i).getLocation().split(" ");
+
+ try {
+ x = Double.valueOf(LocationArray[0].trim()).doubleValue();
+ y = Double.valueOf(LocationArray[1].trim()).doubleValue();
+ } catch (Exception e) {
+ return false;
+ }
+
+ sumX += x;
+ sumY += y;
+ }
+
+ // Calculate the average
+ sumX /= K_Min;
+ sumY /= K_Min;
+
+ // Save results
+ app.currentCoordinates = new LatLng(sumX, sumY);
+
+ return true;
+ }
+
+ /**
+ * Calculates the Weighted Average of the K locations that have the shortest distances D
+ *
+ * @param LocDistance_Results_List Locations-Distances pairs sorted by distance
+ * @param K The number of locations used
+ * @return The estimated user location, or null for error
+ */
+ public boolean calculateWeightedAverageKDistanceLocations(
+ ArrayList LocDistance_Results_List, int K) {
+
+ double LocationWeight = 0.0f;
+ double sumWeights = 0.0f;
+ double WeightedSumX = 0.0f;
+ double WeightedSumY = 0.0f;
+
+ String[] LocationArray = new String[2];
+ double x, y;
+
+ int K_Min = K < LocDistance_Results_List.size() ? K : LocDistance_Results_List.size();
+
+ // Calculate the weighted sum of X and Y
+ for (int i = 0; i < K_Min; ++i) {
+
+ LocationWeight = 1 / LocDistance_Results_List.get(i).getDistance();
+ LocationArray = LocDistance_Results_List.get(i).getLocation().split(" ");
+
+ try {
+ x = Double.valueOf(LocationArray[0].trim()).doubleValue();
+ y = Double.valueOf(LocationArray[1].trim()).doubleValue();
+ } catch (Exception e) {
+ Log.e(TAG, "Localization exception: " + e.getMessage());
+ return false;
+ }
+
+ sumWeights += LocationWeight;
+ WeightedSumX += LocationWeight * x;
+ WeightedSumY += LocationWeight * y;
+ }
+
+ WeightedSumX /= sumWeights;
+ WeightedSumY /= sumWeights;
+
+ app.currentCoordinates = new LatLng(WeightedSumX, WeightedSumY);
+
+ return true;
+ }
+
+ /**
+ * Calculates the Weighted Average over ALL locations where the weights are the Normalized
+ * Probabilities
+ *
+ * @param LocDistance_Results_List Locations-Probability pairs
+ * @return The estimated user location, or null for error
+ */
+ public boolean calculateWeightedAverageProbabilityLocations(
+ ArrayList LocDistance_Results_List) {
+
+ double sumProbabilities = 0.0f;
+ double WeightedSumX = 0.0f;
+ double WeightedSumY = 0.0f;
+ double NP;
+ double x, y;
+ String[] LocationArray = new String[2];
+
+ // Calculate the sum of all probabilities
+ for (int i = 0; i < LocDistance_Results_List.size(); ++i)
+ sumProbabilities += LocDistance_Results_List.get(i).getDistance();
+
+ // Calculate the weighted (Normalized Probabilities) sum of X and Y
+ for (int i = 0; i < LocDistance_Results_List.size(); ++i) {
+ LocationArray = LocDistance_Results_List.get(i).getLocation().split(" ");
+
+ try {
+ x = Double.valueOf(LocationArray[0].trim()).doubleValue();
+ y = Double.valueOf(LocationArray[1].trim()).doubleValue();
+ } catch (Exception e) {
+ return false;
+ }
+
+ NP = LocDistance_Results_List.get(i).getDistance() / sumProbabilities;
+
+ WeightedSumX += (x * NP);
+ WeightedSumY += (y * NP);
+ }
+
+ app.currentCoordinates = new LatLng(WeightedSumX, WeightedSumY);
+
+ return true;
+ }
+
+ public static class AsyncTaskFindLocation extends AsyncTask {
+
+ Long startTime;
+ App app;
+ int heading;
+ private boolean anotherProgress = false;
+
+ public AsyncTaskFindLocation(App app, int heading) {
+ this.app = app;
+ this.heading = heading;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ if (app.isInBgProgress()) {
+ anotherProgress = true;
+
+ return;
+ }
+
+ app.setBgProgressOn();
+
+ app.progressBarBgTasks.setVisibility(View.VISIBLE);
+ app.textViewMessage4.setText("Finding location..");
+ Log.i(TAG, "Finding location..");
+
+ startTime = System.currentTimeMillis();
+
+ super.onPreExecute();
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ if (anotherProgress) {
+ Log.i(TAG, "Process(localize) not runned: another process was already running");
+ return false;
+ }
+
+ boolean foundLocation =
+ app.algorithms.calculateRealLocation(
+ app.currentScanListGet(),
+ app.cacheData.currentRmap,
+ heading,
+ app.localizationAlgorithm);
+
+ if (!foundLocation) return false;
+
+ return true;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ super.onPostExecute(result);
+
+ if (anotherProgress) return;
+
+ app.setFindmeOff(); // Disable findme
+ app.setBgProgressOff(); // this process finished
+
+ Long endTime = System.currentTimeMillis();
+
+ float totalTime = (float) ((endTime - startTime) / 1000.0);
+ String totalTimeS = String.format("%.2f", totalTime);
+
+ app.progressBarBgTasks.setVisibility(View.INVISIBLE);
+
+ // Successfully found location
+ if (result == true) {
+ app.textViewMessage1.setText("Success: located");
+
+ Log.i(TAG, "Success: located. Time: " + totalTimeS);
+ app.textViewMessage4.setText("Found location: " + totalTimeS);
+
+ if (app.runningExperiment) {
+ TestTVM.currentTime.addLocaliseTime(totalTime);
+ }
+
+ // Clear fault mac addresses
+ app.problematicAPs.clear();
+
+ // If TVM1+ algorithms are enabled
+ // and user successfully localized,
+ // now the fake macs will calculated with TVM1/TVM2 methods
+ // If it was cache miss: in ram+sd cases the firstBloom isnt done yet!
+ if ((app.isTVMenabled() || app.isTVM0())
+ && app.cacheData.type.equals(RMapCache.CacheType.MISS)) {
+ app.firstBloomDone = true;
+
+ // If we have >2 matches, show unveil button - CHECK !
+ app.buttonUnveilMatches.setVisibility(View.VISIBLE);
+ }
+
+ MainActivity.updateDeveloperInfoLabels();
+ updateMap(app);
+
+ if (app.runningExperiment) {
+ // Send broadcast to move to next position
+ // Send broadcast to get partial radiomap
+ Intent intent = new Intent();
+ intent.setAction(App.BROADCAST_EXPERIMENT_LOCALISE_INSTANCE);
+ app.sendBroadcast(intent);
+ }
+
+ // Failed to find location
+ } else {
+ app.textViewMessage1.setText("Out of range.");
+ app.textViewMessage4.setText("Failure.");
+ Log.e(TAG, "Out of range. ");
+
+ if (app.runningExperiment) {
+ Log.i(TAG, "LocalizationAlgorithms: Continue Experiment");
+ TestTVM.continueExperiment();
+ }
+ }
+ }
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LocalizationService.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LocalizationService.java
new file mode 100755
index 0000000..0c96ccc
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LocalizationService.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+package cy.ac.ucy.cs.tvm.tvm;
+
+import android.app.Service;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.WifiManager;
+import android.os.IBinder;
+import android.util.Log;
+import cy.ac.ucy.cs.tvm.App;
+import cy.ac.ucy.cs.tvm.MultiBroadcastReceiver;
+
+/**
+ * Localization Service:
+ *
+ *
+ * - Collect listening AP
+ *
- Received broadcast when listening AP are ready
+ *
- Get partial radiomap of strongest cached mac according to enabled caches, and anonymous
+ * algorithm
+ *
- Parse partial radiomap
+ *
- Localize user according to localization algorithm
+ *
+ */
+public class LocalizationService extends Service {
+
+ private static final String TAG = LocalizationService.class.getSimpleName();
+
+ LocalizationThread localizationThread;
+ public static boolean isRunning = false;
+ App app;
+ public static MultiBroadcastReceiver receiver;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ localizationThread = new LocalizationThread();
+
+ app = (App) getApplication();
+ receiver = new MultiBroadcastReceiver();
+ }
+
+ @Override
+ public synchronized void onStart(Intent intent, int startId) {
+ super.onStart(intent, startId);
+
+ // Start user locator
+ if (!isRunning) {
+ isRunning = true;
+ localizationThread.start();
+ Log.i(TAG, "Localization Service started");
+ }
+ }
+
+ @Override
+ public synchronized void onDestroy() {
+ super.onDestroy();
+
+ // Kill locator service
+ if (isRunning) localizationThread.interrupt();
+
+ try {
+ unregisterReceiver(receiver);
+
+ } catch (Exception e) {
+ Log.e(TAG, "Problem unregister receiver: " + e.getMessage());
+ }
+
+ Log.i(TAG, "Localization Service Stopped");
+ }
+
+ public class LocalizationThread extends Thread {
+ @Override
+ public void run() {
+
+ registerReceiver(receiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+
+ while (isRunning) { // wifi scanner runs
+ try {
+ // Get radiomap
+ if (!app.isInBgProgress()) {
+ // Send broadcast to scan for nearest AP's
+ app.lightWifiManager.scanArea();
+ }
+ Thread.sleep(app.wifiScanInterval);
+ } catch (InterruptedException e) {
+ // Thread interrupted
+ isRunning = false;
+ }
+ }
+ }
+ }
+
+ /** @return true if WifiBgService is running otherwise false */
+ public boolean isRunning() {
+ return isRunning;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LogRecord.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LogRecord.java
new file mode 100755
index 0000000..fe67437
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/LogRecord.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+
+package cy.ac.ucy.cs.tvm.tvm;
+
+/**
+ * A log record consists of:
+ *
+ *
+ * - coordinates (latitude, longitude)
+ *
- heading of the device
+ *
- rss and bssid values of the Access Points (APs)
+ *
- the timestamp when the above values were observed
+ *
+ */
+public class LogRecord {
+
+ private long ts; // timestamp
+ private double lng; // longitude
+ private double lat; // latitude
+ private float heading;
+ private String bssid;
+ private int rss;
+
+ public LogRecord(String bssid, int rss) {
+ super();
+ this.bssid = bssid;
+ this.rss = rss;
+ }
+
+ public LogRecord(long ts, double lat, double lng, float heading, String bssid, int rss) {
+ super();
+ this.ts = ts;
+ this.lng = lng;
+ this.lat = lat;
+ this.heading = heading;
+ this.bssid = bssid;
+ this.rss = rss;
+ }
+
+ public String toString() {
+ String str =
+ String.valueOf(ts)
+ + " "
+ + String.valueOf(lat)
+ + " "
+ + String.valueOf(lng)
+ + " "
+ + String.valueOf(heading)
+ + " "
+ + String.valueOf(bssid)
+ + " "
+ + String.valueOf(rss)
+ + "\n";
+ return str;
+ }
+
+ public String getBssid() {
+ return bssid;
+ }
+
+ public int getRss() {
+ return rss;
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/RadioMap.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/RadioMap.java
new file mode 100755
index 0000000..b551cfc
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/RadioMap.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+package cy.ac.ucy.cs.tvm.tvm;
+
+import android.content.Context;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import cy.ac.ucy.cs.tvm.App;
+
+/**
+ * Radiomap consists of:
+ *
+ *
+ * - the raw file that it was created from
+ *
- a list of the all mac addresses
+ *
- a hashmap between the mac addresses and the RSS values
+ *
+ */
+public class RadioMap {
+
+ private static final String TAG = RadioMap.class.getSimpleName();
+ private File rmapFile = null;
+ private ArrayList macAddresses = null;
+
+ /** Table with four hashmaps. for 0, 90, 180, 270 degrees */
+ private ArrayList>> locationRssHashMapTbl = null;
+
+ private ArrayList orderList = null;
+
+ public RadioMap(Context c) {
+ // MAC Addresses
+ macAddresses = new ArrayList();
+
+ // Create the hasmap table container
+ locationRssHashMapTbl = new ArrayList>>();
+
+ // Table with degrees hashmaps
+ for (int i = 0; i < App.MAX_DEGREES; i++) {
+ // Create the hashmaps
+ locationRssHashMapTbl.add(new HashMap>());
+ }
+
+ orderList = new ArrayList();
+ }
+
+ /**
+ * Getter of MAC Address list in file order
+ *
+ * @return the list of MAC Addresses
+ */
+ public ArrayList getmacAddresses() {
+ return macAddresses;
+ }
+
+ public ArrayList>> getlocationRssHashMapTbl() {
+ return locationRssHashMapTbl;
+ }
+
+ /**
+ * Getter of Location list in file order
+ *
+ * @return the Location list
+ */
+ public ArrayList getorderList() {
+ return orderList;
+ }
+
+ /**
+ * Getter of radio map mean filename
+ *
+ * @return the filename of radiomap mean used
+ */
+ public File getradiomapMeanFile() {
+ return this.rmapFile;
+ }
+
+ /**
+ * @param heading
+ * @return
+ */
+ public HashMap> getlocationRssHashMap(int heading) {
+ int degreeIndex = findTableIndex(heading + "");
+
+ return locationRssHashMapTbl.get(degreeIndex);
+ }
+
+ /** Recycles this radiomap for re-use with other rmap data */
+ public void recycle() {
+ rmapFile = null;
+ macAddresses.clear();
+ orderList.clear();
+
+ // Clear degree hashmaps
+ for (int i = 0; i < App.MAX_DEGREES; i++) {
+ // Create the hashmaps
+ locationRssHashMapTbl.get(i).clear();
+ }
+ }
+
+ /**
+ * @param headingStr heading number in a string format
+ * @return HashMap according to degrees given
+ */
+ public HashMap> getlocationRssHashMap(String headingStr) {
+ return getlocationRssHashMap(Integer.parseInt(headingStr));
+ }
+
+ /** Returns the index where the line must inserted, according to the degrees */
+ public static int findTableIndex(String string) {
+ int degrees = Integer.parseInt(string);
+ return findTableIndex(degrees);
+ }
+
+ /**
+ * Calculate the index of the HashMap according to degrees
+ *
+ * @param degrees given to calculate HashMap Index
+ */
+ public static int findTableIndex(int degrees) {
+
+ if ((degrees >= 315 && degrees <= 360) || degrees < 45) {
+ return 0;
+ } else if (degrees >= 45 && degrees < 135) {
+ return 1;
+ } else if (degrees >= 135 && degrees < 225) {
+ return 2;
+ } else if (degrees >= 225 && degrees < 315) {
+ return 3;
+ } else return -1;
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/WeightRecord.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/WeightRecord.java
new file mode 100755
index 0000000..bf81ace
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/WeightRecord.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+package cy.ac.ucy.cs.tvm.tvm;
+
+/** A DAO class that is used by the Weighted variants of KNN and MMSE */
+public class WeightRecord {
+ private float wx;
+ private float wy;
+
+ public WeightRecord(float weightX, float weightY) {
+ this.wx = weightX;
+ this.wy = weightY;
+ }
+
+ public float getWeightX() {
+ return wx;
+ }
+
+ public float getWeightY() {
+ return wy;
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/simulation/TestTVM.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/simulation/TestTVM.java
new file mode 100644
index 0000000..0d9d635
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/simulation/TestTVM.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+
+package cy.ac.ucy.cs.tvm.tvm.simulation;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.util.Log;
+import cy.ac.ucy.cs.tvm.App;
+import cy.ac.ucy.cs.tvm.AsyncTaskHttpExecutor;
+import cy.ac.ucy.cs.tvm.MainActivity;
+import java.util.ArrayList;
+
+/** The experimental series used for the TVM paper. */
+public class TestTVM {
+
+ private static final String TAG = TestTVM.class.getSimpleName();
+ public static ArrayList positions;
+ public static App sApp;
+ public static MainActivity sMainActivity;
+ public static ArrayList times = new ArrayList();
+ public static Test_DAO currentTime;
+
+ private static WalkSimulator walkSimulator;
+ public static final int EXP_ID_SERIES_1 = 1;
+ public static final int EXP_ID_WALKING_SIMULATOR = 0;
+
+ public static void FinalizeWalkingSimulator(App app) {
+ app.showToast("Walking simulator finished.");
+ app.currentSimulationPosition = 0;
+ app.buttonWalkingSimulator.setText("" + app.currentSimulationPosition);
+ App.menuItemTrackMe.setEnabled(true);
+ App.menuItemFindMe.setEnabled(true);
+ }
+
+ public static void InitWalkingSimulator(MainActivity mainActivity, App app) {
+ app.currentSimulationPosition = 0;
+ TestTVM.sMainActivity = mainActivity;
+ TestTVM.sApp = app;
+ App.menuItemTrackMe.setEnabled(false);
+ App.menuItemFindMe.setEnabled(false);
+
+ walkSimulator = new WalkAtUcy_8(app);
+ currentTime = new Test_DAO(sApp, EXP_ID_WALKING_SIMULATOR, walkSimulator.routeName);
+
+ app.singleStepSimulation = true;
+ }
+
+ /** K Anon 3,5,7,10 in dataset 1 */
+ public static void RunExperiment(MainActivity mainActivity, App app) {
+ InitWalkingSimulator(mainActivity, app);
+
+ sApp.showToast("Real navigation disabled.\nRunning experiment 1/TVM");
+ startExperiment();
+ }
+
+ private static void startExperiment() {
+ sApp.initExperimentSeries1();
+
+ sApp.singleStepSimulation = false;
+
+ // INFO this is how we change rootes
+ // eg 1 route simulateWalking = new SimulateMovement_UCY1(sApp);
+ walkSimulator = new WalkAtUcy_300(sApp);
+
+ // create and save current timings
+ currentTime = new Test_DAO(sApp, EXP_ID_SERIES_1, walkSimulator.routeName);
+ times.add(currentTime);
+
+ sApp.showToast("K anonymity: " + sApp.getkAnonymity() + " Dataset: " + sApp.datasetInUse);
+
+ //Save initial values
+ currentTime.position = sApp.currentSimulationPosition;
+
+ // start realAutoSimulation// Fake N location
+ walkSimulator.simulatePosition(sApp.currentSimulationPosition);
+ }
+
+ /** */
+ public static void continueExperiment() {
+
+ // single stepping handles position increases elsewhere
+ if (!sApp.singleStepSimulation) {
+ sApp.currentSimulationPosition++;
+ }
+ sApp.showToast(
+ "Position : " + sApp.currentSimulationPosition + "/" + walkSimulator.max_positions);
+ Log.e(
+ TAG,
+ "Position : " + sApp.currentSimulationPosition + "/" + walkSimulator.max_positions);
+
+ if (sApp.currentSimulationPosition > walkSimulator.max_positions) {
+ // Submit results
+ String parameters[] = new String[1];
+ String params = currentTime.getHttpGetRequestParameters();
+ Log.e(TAG, "Parameters: " + params);
+ parameters[0] = params;
+
+ // Save the data to server
+ new AsyncTaskHttpExecutor(
+ sApp,
+ App.getExperimentsURL(),
+ App.AsyncTaskType.saveExperimentData,
+ parameters,
+ null)
+ .execute();
+
+ sApp.runningExperiment = false; //experiment finished!
+
+ Intent i;
+ PackageManager manager = sMainActivity.getPackageManager();
+ i = manager.getLaunchIntentForPackage(App.PACKAGE_POWER_TUTOR);
+ i.addCategory(Intent.CATEGORY_LAUNCHER);
+ sMainActivity.startActivity(i);
+
+ sApp.showToast("Press Stop Profiler, and then\n Save log\nApplication now stops!");
+ sMainActivity.finish(); //terminate this activity! (so no battery is wasted!)
+ return;
+ }
+
+ if (!sApp.singleStepSimulation) { // continue
+ walkSimulator.simulatePosition(sApp.currentSimulationPosition);
+ }
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/simulation/Test_DAO.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/simulation/Test_DAO.java
new file mode 100644
index 0000000..7eb737e
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/simulation/Test_DAO.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+
+package cy.ac.ucy.cs.tvm.tvm.simulation;
+
+import android.os.Build;
+import cy.ac.ucy.cs.tvm.App;
+import java.util.ArrayList;
+
+/** DAO class used for tests */
+public class Test_DAO {
+
+ private ArrayList mTimeDownload = new ArrayList();
+ private ArrayList mTimeParse = new ArrayList();
+ private ArrayList mTimeLocalise = new ArrayList();
+ private ArrayList mNumberOfMessages = new ArrayList();
+ private final String deviceUsed;
+ private int mDatasetUsed;
+ private App app;
+ int position; // of current user
+
+ int experimentSeries = -1;
+ String anonymityAlgorithm;
+ String route;
+
+ public Test_DAO(App app, int series, String routeName) {
+ this.app = app;
+ this.deviceUsed = getDeviceName();
+ this.mDatasetUsed = app.datasetInUse;
+ this.experimentSeries = series;
+ anonymityAlgorithm = app.anonymityAlgorithm.toString();
+ this.route = routeName;
+ }
+
+ public void addMessagesNumber(int msgs) {
+ this.mNumberOfMessages.add(msgs);
+ }
+
+ public void addLocaliseTime(float time) {
+ this.mTimeLocalise.add(time);
+ }
+
+ public void addDownloadTime(float time) {
+ this.mTimeDownload.add(time);
+ }
+
+ public void addParseTime(float time) {
+ this.mTimeParse.add(time);
+ }
+
+ private String buildMessagesStr() {
+ String result = "";
+
+ for (float l : mNumberOfMessages) {
+ result += l + ",";
+ }
+ if (!result.equals("")) result = result.substring(0, result.length() - 2);
+
+ return result;
+ }
+
+ private String buildTimesDownloadStr() {
+ String result = "";
+
+ for (float l : mTimeDownload) {
+ result += l + ",";
+ }
+ if (!result.equals("")) result = result.substring(0, result.length() - 2);
+
+ return result;
+ }
+
+ private String buildTimesLocalizeStr() {
+ String result = "";
+
+ for (float l : mTimeLocalise) {
+ result += l + ",";
+ }
+
+ if (!result.equals("")) result = result.substring(0, result.length() - 2);
+
+ return result;
+ }
+
+ private String buildTimesParseStr() {
+ String result = "";
+
+ for (float l : mTimeParse) {
+ result += l + ",";
+ }
+ if (!result.equals("")) result = result.substring(0, result.length() - 2);
+
+ return result;
+ }
+
+ private float getLocaliseTimesSum() {
+ float result = -1;
+
+ for (float l : mTimeLocalise) {
+ result += l;
+ }
+
+ return result;
+ }
+
+ private float getDownloadTimesSum() {
+ float result = -1;
+
+ for (float l : mTimeDownload) {
+ result += l;
+ }
+
+ return result;
+ }
+
+ private float getParseTimesSum() {
+ float result = -1;
+
+ for (float l : mTimeParse) {
+ result += l;
+ }
+ return result;
+ }
+
+ private int getTotalMessages() {
+ int result = -1;
+
+ for (int l : mNumberOfMessages) {
+ result += l;
+ }
+ return result;
+ }
+
+ /** Return the total times for download, parse and localize */
+ private float getTotalTimes() {
+ return (getDownloadTimesSum() + getLocaliseTimesSum() + getParseTimesSum());
+ }
+
+ /** @return */
+ public String getHttpGetRequestParameters() {
+
+ return "experimentseries="
+ + experimentSeries
+ + "&device="
+ + deviceUsed
+ + "&dataset="
+ + mDatasetUsed
+ + "&kanonymity="
+ + app.getkAnonymity()
+ + "&algorithm="
+ + anonymityAlgorithm
+ + "&route="
+ + route
+ + "&messagesTotal="
+ + getTotalMessages()
+ + "&timeTotal="
+ + getTotalTimes()
+ + "&timeDownloadTotal="
+ + getDownloadTimesSum()
+ + "&timeParseTotal="
+ + getParseTimesSum()
+ + "&timeLocaliseTotal="
+ + getLocaliseTimesSum()
+ + "&messages="
+ + buildMessagesStr()
+ + "&timelocalize="
+ + buildTimesLocalizeStr()
+ + "&timedownload="
+ + buildTimesDownloadStr()
+ + "&timeparse="
+ + buildTimesParseStr();
+ }
+
+ /** get device name */
+ public String getDeviceName() {
+
+ String manufacturer = Build.MANUFACTURER;
+ String model = Build.MODEL;
+ if (model.startsWith(manufacturer)) {
+ return capitalize(model);
+ } else {
+ return (capitalize(manufacturer) + "_" + model).replace(" ", "");
+ }
+ }
+
+ /** helper for device name */
+ private String capitalize(String s) {
+ if (s == null || s.length() == 0) {
+ return "";
+ }
+ char first = s.charAt(0);
+ if (Character.isUpperCase(first)) {
+ return s;
+ } else {
+ return Character.toUpperCase(first) + s.substring(1);
+ }
+ }
+}
diff --git a/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/simulation/WalkAtUcy_100.java b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/simulation/WalkAtUcy_100.java
new file mode 100644
index 0000000..85fca9e
--- /dev/null
+++ b/Client/app/src/main/java/cy/ac/ucy/cs/tvm/tvm/simulation/WalkAtUcy_100.java
@@ -0,0 +1,3722 @@
+/*
+ * Copyright (c) 2013, Data Management Systems Lab (DMSL), University of Cyprus.
+ *
+ * Author: P. Mpeis pmpeis01@cs.ucy.ac.cy (University of Cyprus)
+ *
+ * Project supervisors: A. Konstantinides, D. Zeinalipour-Yazti (University of Cyprus)
+ *
+ * This file is part of TVM.
+ * TVM 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 .
+ */
+
+/**
+ * INFO: Class automatically generated by WalkSimulatorClassGenerator to simulate a real
+ * localization scenario that was recorded at CS UCY Campus.
+ */
+package cy.ac.ucy.cs.tvm.tvm.simulation;
+
+import cy.ac.ucy.cs.tvm.App;
+import cy.ac.ucy.cs.tvm.tvm.LogRecord;
+import java.util.ArrayList;
+
+/**
+ * INFO: Class automatically generated by WalkSimulatorClassGenerator. Simulating 100 positions at
+ * Computer Science dept. @ UCY Positions has real recorded data.
+ */
+public class WalkAtUcy_100 extends WalkSimulator {
+
+ public static final int MAX_POSITIONS = 100;
+
+ public WalkAtUcy_100(App app) {
+ super("UCY_route100", app, MAX_POSITIONS);
+ }
+
+ public ArrayList getPosition(int num) {
+
+ switch (num) {
+ case 0:
+ return getLogRecordUcyPosition0();
+ case 1:
+ return getLogRecordUcyPosition1();
+ case 2:
+ return getLogRecordUcyPosition2();
+ case 3:
+ return getLogRecordUcyPosition3();
+ case 4:
+ return getLogRecordUcyPosition4();
+ case 5:
+ return getLogRecordUcyPosition5();
+ case 6:
+ return getLogRecordUcyPosition6();
+ case 7:
+ return getLogRecordUcyPosition7();
+ case 8:
+ return getLogRecordUcyPosition8();
+ case 9:
+ return getLogRecordUcyPosition9();
+ case 10:
+ return getLogRecordUcyPosition10();
+ case 11:
+ return getLogRecordUcyPosition11();
+ case 12:
+ return getLogRecordUcyPosition12();
+ case 13:
+ return getLogRecordUcyPosition13();
+ case 14:
+ return getLogRecordUcyPosition14();
+ case 15:
+ return getLogRecordUcyPosition15();
+ case 16:
+ return getLogRecordUcyPosition16();
+ case 17:
+ return getLogRecordUcyPosition17();
+ case 18:
+ return getLogRecordUcyPosition18();
+ case 19:
+ return getLogRecordUcyPosition19();
+ case 20:
+ return getLogRecordUcyPosition20();
+ case 21:
+ return getLogRecordUcyPosition21();
+ case 22:
+ return getLogRecordUcyPosition22();
+ case 23:
+ return getLogRecordUcyPosition23();
+ case 24:
+ return getLogRecordUcyPosition24();
+ case 25:
+ return getLogRecordUcyPosition25();
+ case 26:
+ return getLogRecordUcyPosition26();
+ case 27:
+ return getLogRecordUcyPosition27();
+ case 28:
+ return getLogRecordUcyPosition28();
+ case 29:
+ return getLogRecordUcyPosition29();
+ case 30:
+ return getLogRecordUcyPosition30();
+ case 31:
+ return getLogRecordUcyPosition31();
+ case 32:
+ return getLogRecordUcyPosition32();
+ case 33:
+ return getLogRecordUcyPosition33();
+ case 34:
+ return getLogRecordUcyPosition34();
+ case 35:
+ return getLogRecordUcyPosition35();
+ case 36:
+ return getLogRecordUcyPosition36();
+ case 37:
+ return getLogRecordUcyPosition37();
+ case 38:
+ return getLogRecordUcyPosition38();
+ case 39:
+ return getLogRecordUcyPosition39();
+ case 40:
+ return getLogRecordUcyPosition40();
+ case 41:
+ return getLogRecordUcyPosition41();
+ case 42:
+ return getLogRecordUcyPosition42();
+ case 43:
+ return getLogRecordUcyPosition43();
+ case 44:
+ return getLogRecordUcyPosition44();
+ case 45:
+ return getLogRecordUcyPosition45();
+ case 46:
+ return getLogRecordUcyPosition46();
+ case 47:
+ return getLogRecordUcyPosition47();
+ case 48:
+ return getLogRecordUcyPosition48();
+ case 49:
+ return getLogRecordUcyPosition49();
+ case 50:
+ return getLogRecordUcyPosition50();
+ case 51:
+ return getLogRecordUcyPosition51();
+ case 52:
+ return getLogRecordUcyPosition52();
+ case 53:
+ return getLogRecordUcyPosition53();
+ case 54:
+ return getLogRecordUcyPosition54();
+ case 55:
+ return getLogRecordUcyPosition55();
+ case 56:
+ return getLogRecordUcyPosition56();
+ case 57:
+ return getLogRecordUcyPosition57();
+ case 58:
+ return getLogRecordUcyPosition58();
+ case 59:
+ return getLogRecordUcyPosition59();
+ case 60:
+ return getLogRecordUcyPosition60();
+ case 61:
+ return getLogRecordUcyPosition61();
+ case 62:
+ return getLogRecordUcyPosition62();
+ case 63:
+ return getLogRecordUcyPosition63();
+ case 64:
+ return getLogRecordUcyPosition64();
+ case 65:
+ return getLogRecordUcyPosition65();
+ case 66:
+ return getLogRecordUcyPosition66();
+ case 67:
+ return getLogRecordUcyPosition67();
+ case 68:
+ return getLogRecordUcyPosition68();
+ case 69:
+ return getLogRecordUcyPosition69();
+ case 70:
+ return getLogRecordUcyPosition70();
+ case 71:
+ return getLogRecordUcyPosition71();
+ case 72:
+ return getLogRecordUcyPosition72();
+ case 73:
+ return getLogRecordUcyPosition73();
+ case 74:
+ return getLogRecordUcyPosition74();
+ case 75:
+ return getLogRecordUcyPosition75();
+ case 76:
+ return getLogRecordUcyPosition76();
+ case 77:
+ return getLogRecordUcyPosition77();
+ case 78:
+ return getLogRecordUcyPosition78();
+ case 79:
+ return getLogRecordUcyPosition79();
+ case 80:
+ return getLogRecordUcyPosition80();
+ case 81:
+ return getLogRecordUcyPosition81();
+ case 82:
+ return getLogRecordUcyPosition82();
+ case 83:
+ return getLogRecordUcyPosition83();
+ case 84:
+ return getLogRecordUcyPosition84();
+ case 85:
+ return getLogRecordUcyPosition85();
+ case 86:
+ return getLogRecordUcyPosition86();
+ case 87:
+ return getLogRecordUcyPosition87();
+ case 88:
+ return getLogRecordUcyPosition88();
+ case 89:
+ return getLogRecordUcyPosition89();
+ case 90:
+ return getLogRecordUcyPosition90();
+ case 91:
+ return getLogRecordUcyPosition91();
+ case 92:
+ return getLogRecordUcyPosition92();
+ case 93:
+ return getLogRecordUcyPosition93();
+ case 94:
+ return getLogRecordUcyPosition94();
+ case 95:
+ return getLogRecordUcyPosition95();
+ case 96:
+ return getLogRecordUcyPosition96();
+ case 97:
+ return getLogRecordUcyPosition97();
+ case 98:
+ return getLogRecordUcyPosition98();
+ case 99:
+ return getLogRecordUcyPosition99();
+ case 100:
+ return getLogRecordUcyPosition100();
+ default:
+ break;
+ }
+
+ return null;
+ }
+
+ public ArrayList getLogRecordUcyPosition0() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -89));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -80));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -88));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -75));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -80));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -80));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -69));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -71));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -79));
+ result.add(new LogRecord("24:b6:57:b4:f6:20", -86));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -69));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -79));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -81));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -72));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -79));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -72));
+ result.add(new LogRecord("00:0e:84:4b:0b:aa", -91));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -90));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -86));
+ result.add(new LogRecord("d4:d7:48:b6:3e:b0", -84));
+ result.add(new LogRecord("20:aa:4b:55:05:31", -89));
+ result.add(new LogRecord("00:0e:84:4b:0b:86", -92));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -90));
+ result.add(new LogRecord("00:0b:fd:4a:71:ce", -89));
+ result.add(new LogRecord("00:0e:84:4b:0b:80", -89));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition1() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -91));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -80));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -88));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -72));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -82));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -80));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -73));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -73));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -72));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -62));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -73));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -82));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -73));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -81));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -78));
+ result.add(new LogRecord("00:0e:84:4b:0b:aa", -91));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -90));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -86));
+ result.add(new LogRecord("d4:d7:48:b6:3e:b0", -84));
+ result.add(new LogRecord("20:aa:4b:55:05:31", -89));
+ result.add(new LogRecord("00:0e:84:4b:0b:86", -92));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -90));
+ result.add(new LogRecord("00:0b:fd:4a:71:ce", -86));
+ result.add(new LogRecord("00:0e:84:4b:0b:80", -89));
+ result.add(new LogRecord("00:0e:84:4b:0b:b5", -92));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition2() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -91));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -78));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -88));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -77));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -71));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -84));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -71));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -71));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -77));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -66));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -85));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -71));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -71));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -75));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -74));
+ result.add(new LogRecord("00:0e:84:4b:0b:aa", -91));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -90));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -86));
+ result.add(new LogRecord("d4:d7:48:b6:3e:b0", -84));
+ result.add(new LogRecord("20:aa:4b:55:05:31", -89));
+ result.add(new LogRecord("00:0e:84:4b:0b:86", -92));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -90));
+ result.add(new LogRecord("00:0b:fd:4a:71:ce", -86));
+ result.add(new LogRecord("00:0e:84:4b:0b:80", -89));
+ result.add(new LogRecord("00:0e:84:4b:0b:b5", -92));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition3() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -92));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -78));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -87));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -77));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -71));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -84));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -71));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -71));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -67));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -66));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -70));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -71));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -71));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -75));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -74));
+ result.add(new LogRecord("00:0e:84:4b:0b:aa", -91));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -86));
+ result.add(new LogRecord("d4:d7:48:b6:3e:b0", -84));
+ result.add(new LogRecord("20:aa:4b:55:05:31", -89));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -90));
+ result.add(new LogRecord("00:0b:fd:4a:71:ce", -86));
+ result.add(new LogRecord("00:0e:84:4b:0b:80", -89));
+ result.add(new LogRecord("00:0e:84:4b:0b:b5", -92));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition4() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -92));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -74));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -83));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -73));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -78));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -74));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -73));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -73));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -70));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -69));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -71));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -78));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -73));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -80));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -81));
+ result.add(new LogRecord("00:0e:84:4b:0b:aa", -91));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -86));
+ result.add(new LogRecord("d4:d7:48:b6:3e:b0", -84));
+ result.add(new LogRecord("20:aa:4b:55:05:31", -89));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -90));
+ result.add(new LogRecord("00:0b:fd:4a:71:ce", -86));
+ result.add(new LogRecord("00:0e:84:4b:0b:80", -89));
+ result.add(new LogRecord("00:0e:84:4b:0b:b5", -92));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition5() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -92));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -81));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -84));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -68));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -85));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -83));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -72));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -72));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -67));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -57));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -67));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -85));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -71));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -85));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -86));
+ result.add(new LogRecord("00:0e:84:4b:0b:aa", -91));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -86));
+ result.add(new LogRecord("20:aa:4b:55:05:31", -89));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -90));
+ result.add(new LogRecord("00:0b:fd:4a:71:ce", -86));
+ result.add(new LogRecord("00:0e:84:4b:0b:80", -89));
+ result.add(new LogRecord("00:0e:84:4b:0b:b5", -92));
+ result.add(new LogRecord("24:b6:57:b4:f6:20", -77));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -89));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition6() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -87));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -75));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -84));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -79));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -85));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -83));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -72));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -72));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -67));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -57));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -77));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -85));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -71));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -84));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -74));
+ result.add(new LogRecord("00:0e:84:4b:0b:aa", -91));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -86));
+ result.add(new LogRecord("20:aa:4b:55:05:31", -89));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -90));
+ result.add(new LogRecord("00:0b:fd:4a:71:ce", -86));
+ result.add(new LogRecord("00:0e:84:4b:0b:80", -89));
+ result.add(new LogRecord("00:0e:84:4b:0b:b5", -92));
+ result.add(new LogRecord("24:b6:57:b4:f6:20", -77));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -89));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition7() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -87));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -62));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -76));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -74));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -75));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -74));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -68));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -67));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -74));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -71));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -74));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -74));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -68));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -70));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -71));
+ result.add(new LogRecord("00:0e:84:4b:0b:aa", -91));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -86));
+ result.add(new LogRecord("20:aa:4b:55:05:31", -89));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -90));
+ result.add(new LogRecord("00:0b:fd:4a:71:ce", -86));
+ result.add(new LogRecord("00:0e:84:4b:0b:80", -89));
+ result.add(new LogRecord("00:0e:84:4b:0b:b5", -92));
+ result.add(new LogRecord("24:b6:57:b4:f6:20", -77));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -89));
+ result.add(new LogRecord("00:0e:84:4b:0a:f1", -92));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition8() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -87));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", 0));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -74));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -70));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -66));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -66));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -68));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -59));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -73));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", 0));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -73));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -66));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -60));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -69));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -69));
+ result.add(new LogRecord("00:0e:84:4b:0b:aa", -91));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -86));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -80));
+ result.add(new LogRecord("00:0b:fd:4a:71:ce", -86));
+ result.add(new LogRecord("00:0e:84:4b:0b:80", -89));
+ result.add(new LogRecord("00:0e:84:4b:0b:b5", -92));
+ result.add(new LogRecord("24:b6:57:b4:f6:20", -77));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -89));
+ result.add(new LogRecord("00:0e:84:4b:0a:f1", -92));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition9() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -86));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", 0));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -73));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -72));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -66));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -66));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -86));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -80));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -73));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -56));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -73));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -74));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -60));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -69));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -69));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -90));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -80));
+ result.add(new LogRecord("24:b6:57:b4:f6:20", -77));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -92));
+ result.add(new LogRecord("00:0e:84:4b:0a:f1", -92));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition10() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -86));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -44));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -51));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -76));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -82));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -82));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -72));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -72));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -75));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -72));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -75));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -82));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -64));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -69));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -69));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -90));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -78));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -92));
+ result.add(new LogRecord("00:0e:84:4b:0a:f1", -91));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition11() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -77));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -55));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -62));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -81));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -71));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -71));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -77));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -77));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -75));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -75));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -81));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -71));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -72));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -70));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -70));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -90));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -78));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -92));
+ result.add(new LogRecord("00:0e:84:4b:0a:f1", -93));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition12() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -77));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -55));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -62));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -81));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -71));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -71));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -77));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -77));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -75));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -75));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -73));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -76));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -72));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -70));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -70));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -90));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -78));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -92));
+ result.add(new LogRecord("00:0e:84:4b:0a:f1", -89));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition13() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -77));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -66));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -66));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -77));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -69));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -74));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -71));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -70));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -77));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -75));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -77));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -66));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -69));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -70));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -69));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -90));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -78));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -92));
+ result.add(new LogRecord("00:0e:84:4b:0a:f1", -89));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition14() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -77));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -73));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -79));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -77));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -74));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -73));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -71));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -68));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -78));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -75));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -80));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -72));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -70));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -77));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -66));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -90));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -78));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -92));
+ result.add(new LogRecord("00:0e:84:4b:0a:f1", -90));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition15() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -68));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -73));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -79));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -77));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -74));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -73));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -70));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -68));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -78));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -75));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -76));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -72));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -70));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -77));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -66));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -90));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -78));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -91));
+ result.add(new LogRecord("00:0e:84:4b:0a:f1", -90));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition16() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -68));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -70));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -80));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -73));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -82));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -81));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -79));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -74));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -73));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -15));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -72));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -81));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -74));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -77));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -77));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -90));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -91));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -91));
+ result.add(new LogRecord("00:0e:84:4b:0a:f1", -90));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition17() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -68));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -71));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -83));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -84));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -80));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -85));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -82));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -80));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -84));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -63));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -83));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -78));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -82));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -77));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -77));
+ result.add(new LogRecord("00:0e:84:4b:0b:9f", -90));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -18));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -91));
+ result.add(new LogRecord("00:0e:84:4b:0a:f1", -90));
+ result.add(new LogRecord("d4:d7:48:b6:3e:b0", -85));
+ result.add(new LogRecord("00:0e:84:4b:0b:b5", -92));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition18() {
+ ArrayList result = new ArrayList();
+
+ result.add(new LogRecord("00:25:9c:99:90:d4", -72));
+ result.add(new LogRecord("d4:d7:48:b0:8f:40", -71));
+ result.add(new LogRecord("d4:d7:48:7d:b5:10", -83));
+ result.add(new LogRecord("00:1f:6c:a8:e9:10", -84));
+ result.add(new LogRecord("00:1d:45:51:38:a0", -80));
+ result.add(new LogRecord("00:1d:45:51:38:a2", -85));
+ result.add(new LogRecord("00:3a:98:2a:65:30", -82));
+ result.add(new LogRecord("00:3a:98:2a:65:32", -80));
+ result.add(new LogRecord("00:1f:6c:a8:e9:12", -88));
+ result.add(new LogRecord("00:0b:fd:4a:71:a2", -63));
+ result.add(new LogRecord("00:1f:6c:a8:e9:11", -83));
+ result.add(new LogRecord("00:1d:45:51:38:a1", -78));
+ result.add(new LogRecord("00:3a:98:2a:65:31", -82));
+ result.add(new LogRecord("00:3a:98:2a:65:82", -77));
+ result.add(new LogRecord("00:3a:98:2a:65:81", -77));
+ result.add(new LogRecord("00:0b:fd:f3:ab:56", -18));
+ result.add(new LogRecord("00:0e:84:4b:0b:02", -91));
+ result.add(new LogRecord("00:0e:84:4b:0a:f1", -90));
+ result.add(new LogRecord("d4:d7:48:b6:3e:b0", -85));
+ result.add(new LogRecord("00:0e:84:4b:0b:b5", -92));
+
+ return result;
+ }
+
+ public ArrayList getLogRecordUcyPosition19() {
+ ArrayList result = new ArrayList