/* * XCTrack - XContest Live Tracking client for J2ME devices * Copyright (C) 2009 Petr Chromec <petr@xcontest.org> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package org.xcontest.xctrack.widget; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import org.xcontest.live.Earth; import org.xcontest.xctrack.info.InfoCenter; import org.xcontest.xctrack.info.LocationInfo; import org.xcontest.xctrack.info.LocationInfoResult; import org.xcontest.xctrack.widget.settings.DataSourceSettings; class AveragingItem { AveragingItem(double age, int idx) { this.age = age; this.idx = idx; } double age; int idx; } public final class WidgetInfo { private static LocationInfoResult _loc = new LocationInfoResult(); private static boolean _averagingChanged = false; private static Hashtable _locationAveraging = new Hashtable(); private static Hashtable _altitudeAveraging = new Hashtable(); private static double[] _locationAges; private static double[] _historyLocations; private static double[] _altitudeAges; private static double[] _historyAltitudes; private static double[] _averageSpeed; private static double[] _averageHeading; private static double[] _averageVario; public static void update() { LocationInfo li = InfoCenter.getInstance().getLocationInfo(); if (_averagingChanged) { Vector ages = new Vector(); Enumeration els; int nages; els = _locationAveraging.elements(); while (els.hasMoreElements()) { AveragingItem it = (AveragingItem)els.nextElement(); Double ot = new Double(it.age); it.idx = ages.indexOf(ot); if (it.idx < 0) { it.idx = ages.size(); ages.addElement(ot); } } nages = ages.size(); _locationAges = new double[nages]; for (int i = 0; i < nages; i ++) _locationAges[i] = ((Double)ages.elementAt(i)).doubleValue(); _averageSpeed = new double[nages]; _averageHeading = new double[nages]; _historyLocations = new double[4*nages]; ages.removeAllElements(); els = _altitudeAveraging.elements(); while (els.hasMoreElements()) { AveragingItem it = (AveragingItem)els.nextElement(); Double ot = new Double(it.age); it.idx = ages.indexOf(ot); if (it.idx < 0) { it.idx = ages.size(); ages.addElement(ot); } } nages = ages.size(); _altitudeAges = new double[nages]; for (int i = 0; i < nages; i ++) _altitudeAges[i] = ((Double)ages.elementAt(i)).doubleValue(); _averageVario = new double[nages]; _historyAltitudes = new double[2*nages]; li.getHistory().reset(_locationAges,_altitudeAges); _averagingChanged = false; } li.computeLocation(_loc); // == null for the first run - the history requests has to be setup first at the first-time paint() of widgets double currentSpeed = (_loc.hasTwoPoints && _loc.age < 20) ? _loc.speed : Double.NaN; double currentHeading = (_loc.hasTwoPoints && _loc.age < 20) ? _loc.heading : Double.NaN; double currentVario = (_loc.hasVerticalSpeed && _loc.age < 20) ? _loc.verticalSpeed : Double.NaN; if (_locationAges != null) { li.getHistory().get(_loc.time, _historyLocations, _historyAltitudes); int n = _locationAges.length; for (int i = 0; i < n; i ++) { if (Double.isNaN(_historyLocations[4*i])) { _averageHeading[i] = currentHeading; _averageSpeed[i] = currentSpeed; } else { _averageSpeed[i] = Earth.getDistance(_historyLocations[4*i], _historyLocations[4*i+1], _historyLocations[4*i+2], _historyLocations[4*i+3])/_locationAges[i]; _averageHeading[i] = Earth.getAngle(_historyLocations[4*i], _historyLocations[4*i+1], _historyLocations[4*i+2], _historyLocations[4*i+3]); } } n = _altitudeAges.length; for (int i = 0; i < n; i ++) { if (Double.isNaN(_historyAltitudes[2*i])) { _averageVario[i] = currentVario; } else { _averageVario[i] = (_historyAltitudes[2*i+1]-_historyAltitudes[2*i])/_altitudeAges[i]; } } } } public static LocationInfoResult getLocation() { return _loc; } // synchronize? ... all methods here are called from one thread (WidgetPage.paint), no serialization needed (yet) public static double getAverageVario(Object settings, double age) { if (age > 0) { if (_altitudeAveraging.containsKey(settings)) { AveragingItem it = (AveragingItem)_altitudeAveraging.get(settings); if (it.age == age) { return _averageVario[it.idx]; } else { it.age = age; _averagingChanged = true; return Double.NaN; } } else { _altitudeAveraging.put(settings, new AveragingItem(age,0)); _averagingChanged = true; return Double.NaN; } } else { if (_altitudeAveraging.containsKey(settings)) { _altitudeAveraging.remove(settings); _averagingChanged = true; } return (_loc.hasVerticalSpeed && _loc.age < 20) ? _loc.verticalSpeed : Double.NaN; } } // synchronize? ... all methods here are called from one thread (WidgetPage.paint), no serialization needed (yet) private static double getAverageSpeedHeading(Object settings, double age, boolean isSpeed) { if (age > 0) { if (_locationAveraging.containsKey(settings)) { AveragingItem it = (AveragingItem)_locationAveraging.get(settings); if (it.age == age) { return isSpeed ? _averageSpeed[it.idx] : _averageHeading[it.idx]; } else { it.age = age; _averagingChanged = true; return Double.NaN; } } else { _locationAveraging.put(settings, new AveragingItem(age,0)); _averagingChanged = true; return Double.NaN; } } else { if (_locationAveraging.containsKey(settings)) { _locationAveraging.remove(settings); _averagingChanged = true; } return (_loc.hasTwoPoints && _loc.age < 20) ? (isSpeed ? _loc.speed : _loc.heading) : Double.NaN; } } public static double getAverageSpeed(Object settings, double age) { return getAverageSpeedHeading(settings,age,true); } public static double getAverageHeading(Object settings, double age) { return getAverageSpeedHeading(settings,age,false); } public static double getSpeed(DataSourceSettings.Data d) { if (d.gpsSource) return _loc.gpsSpeed; else return getAverageSpeed(d,d.averaging); } public static double getHeading(DataSourceSettings.Data d) { if (d.gpsSource) return _loc.gpsHeading; else return getAverageHeading(d,d.averaging); } public static double getVario(DataSourceSettings.Data d) { return getAverageVario(d,d.averaging); } }