/* * ShareNav - Copyright (c) 2007 Harald Mueller james22 at users dot sourceforge dot net * - Copyright (c) 2008 Kai Krueger apmonkey at users dot sourceforge dot net * - Copyright (c) 2008 sk750 at users dot sourceforge dot net * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * See Copying */ package net.sharenav.gps.location; import java.io.IOException; import java.io.OutputStream; import java.util.Vector; //#if polish.api.locationapi import javax.microedition.location.Criteria; import javax.microedition.location.Location; import javax.microedition.location.LocationException; import javax.microedition.location.LocationListener; import javax.microedition.location.LocationProvider; import javax.microedition.location.QualifiedCoordinates; //#endif import net.sharenav.sharenav.data.Position; import net.sharenav.util.Logger; import net.sharenav.util.StringTokenizer; import de.enough.polish.util.Locale; /** * This class implements a location producer which uses the JSR-179 API * to get the device's current position. * This API is supported by most phones with an internal GPS receiver. */ public class Jsr179Input //#if polish.api.locationapi implements LocationListener, LocationMsgProducer //#endif { private final static Logger logger = Logger.getInstance(Jsr179Input.class, Logger.TRACE); //#if polish.api.locationapi private LocationProvider locationProvider = null; private final LocationMsgReceiverList receiverList; private NmeaMessage smsg; Position pos = new Position(0f, 0f, 0f, 0f, 0f, 0, System.currentTimeMillis()); private int numSatellites = 0; private OutputStream rawDataLogger; public Jsr179Input() { this.receiverList = new LocationMsgReceiverList(); } public boolean init(LocationMsgReceiver receiver) { logger.info("Start Jsr179 LocationProvider"); this.receiverList.addReceiver(receiver); // We may be able to get some additional information such as the number // of satellites form the NMEA string smsg = new NmeaMessage(receiverList); createLocationProvider(); if (locationProvider != null) { return true; } else { return false; } } public boolean activate(LocationMsgReceiver receiver) { // FIXME move activation code (code to enable continuos location feed) here return true; } public boolean deactivate(LocationMsgReceiver receiver) { return true; } /** * Initializes LocationProvider uses default criteria * * @throws Exception */ void createLocationProvider() { logger.trace("enter createLocationProvider()"); if (locationProvider == null) { // try out different locationprovider criteria combinations, the // ones with maximum features first for (int i = 0; i <= 3; i++) { try { Criteria criteria = new Criteria(); switch (i) { case 0: criteria.setAltitudeRequired(true); criteria.setSpeedAndCourseRequired(true); break; case 1: criteria.setSpeedAndCourseRequired(true); break; case 2: criteria.setAltitudeRequired(true); } locationProvider = LocationProvider.getInstance(criteria); if (locationProvider != null) { logger.info("Chosen location provider:" + locationProvider); break; // we are using this criteria } } catch (LocationException le) { logger.info("LocationProvider criteria not fitting: " + i); locationProvider = null; } catch (Exception e) { logger.exception(Locale.get("jsr179input.unexpectedExceptioninLocProv")/*unexpected exception while probing LocationProvider criteria.*/,e); } } if (locationProvider != null) { try { locationProvider.setLocationListener(this, 1, -1, -1); } catch (Exception e) { receiverList.receiveStatus(LocationMsgReceiver.STATUS_SECEX, 0); locationProvider = null; } if (locationProvider != null) { updateSolution(locationProvider.getState()); } } else { receiverList.locationDecoderEnd(Locale.get("jsr179input.NoJSR179Provider")/*no JSR179 Provider*/); //#debug info logger.info("Cannot create LocationProvider for criteria."); } } //#debug logger.trace("exit createLocationProvider()"); } public void locationUpdated(LocationProvider provider, Location location) { locationUpdated(provider, location, false); } public void locationUpdated(LocationProvider provider, Location location, boolean lastKnown) { //#debug info logger.info("updateLocation: " + location); if (location == null) { return; } numSatellites = 0; //#debug debug logger.debug("received Location: " + location.getLocationMethod()); String nmeaString = location .getExtraInfo("application/X-jsr179-location-nmea"); //nmeaString = // "$GPGSA,A,3,32,23,31,11,20,,,,,,,,2.8,2.6,1.0*3D$GPGSV,3,1,10,20,71,071,38,23,60,168,41,17,42,251,25,11,36,148,37*73$GPGSV,3,2,10,04,29,300,16,13,26,197,,31,21,054,47,32,10,074,38*70$GPGSV,3,3,10,12,04,339,17,05,01,353,15*75"; // ; //#debug info logger.info("Using extra NMEA info in JSR179: " + nmeaString); // FIXME combine to one, duplicated in Jsr179Input and AndroidLocationInput if (nmeaString != null) { if (rawDataLogger != null) { try { rawDataLogger.write(nmeaString.getBytes()); rawDataLogger.flush(); } catch (IOException ioe) { logger.exception(Locale.get("jsr179input.CouldNotWriteGPSLog")/*Could not write raw GPS log*/, ioe); } } Vector messages = StringTokenizer.getVector(nmeaString, "$"); if (messages != null) { for (int i = 0; i < messages.size(); i++) { String nmeaMessage = (String) messages.elementAt(i); if (nmeaMessage == null) { continue; } if (nmeaMessage.startsWith("$")) { // Cut off $GP from the start nmeaMessage = nmeaMessage.substring(3); } else if (nmeaMessage.startsWith("GP")) { // Cut off GP from the start nmeaMessage = nmeaMessage.substring(2); } int delimiterIdx = nmeaMessage.indexOf("*"); if (delimiterIdx > 0) { // remove the checksum nmeaMessage = nmeaMessage.substring(0, delimiterIdx); } delimiterIdx = nmeaMessage.indexOf(" "); if (delimiterIdx > 0) { // remove trailing whitespace because some mobiles like HTC Touch Diamond 2 with JavaFX // receive NMEA sentences terminated by a space instead of a star followed by the checksum nmeaMessage = nmeaMessage.substring(0, delimiterIdx); } //#debug info logger.info("Decoding: " + nmeaMessage); if ((nmeaMessage != null) && (nmeaMessage.length() > 5)) { smsg.decodeMessage(nmeaMessage, false, true); // get *DOP from the message pos.pdop = smsg.getPosition().pdop; pos.hdop = smsg.getPosition().hdop; pos.vdop = smsg.getPosition().vdop; numSatellites = smsg.getMAllSatellites(); } } } } if (location.isValid()) { /* e.g. SE C702 only receives from getExtraInfo() $GPGSV sentences, * therefore use On as solution when it's still set to NoFix though the location is valid */ if (receiverList.getCurrentStatus() == LocationMsgReceiver.STATUS_NOFIX) { receiverList.receiveStatus(LocationMsgReceiver.STATUS_ON, 0); } QualifiedCoordinates coordinates = location.getQualifiedCoordinates(); pos.latitude = (float) coordinates.getLatitude(); pos.longitude = (float) coordinates.getLongitude(); pos.altitude = coordinates.getAltitude(); pos.course = location.getCourse(); pos.speed = location.getSpeed(); pos.timeMillis = location.getTimestamp(); pos.accuracy = coordinates.getHorizontalAccuracy(); if (lastKnown) { pos.type = Position.TYPE_GPS_LASTKNOWN; } else { pos.type = Position.TYPE_GPS; } receiverList.receivePosition(pos); } else { if (receiverList != null) { receiverList.receiveStatus(LocationMsgReceiver.STATUS_NOFIX, numSatellites); } } // logger.trace("exit locationUpdated(provider,location)"); } public void providerStateChanged(LocationProvider lprov, int state) { //#debug info logger.info("providerStateChanged(" + lprov + "," + state + ")"); updateSolution(state); } public void close() { //#debug logger.trace("enter close()"); // if (locationProvider != null){ // locationProvider.setLocationListener(null, -1, -1, -1); // } if (locationProvider != null) { /* locationProvider.setLocationListener(null, 0, 0, 0) tends to freeze Samsung S8000 frequently when stopping GPS * was replaced by locationProvider.reset() until 2010-10-21. * So if other mobile devices have issues with locationProvider.reset(), * please document this here * - Aparently Gps is left on, on at least Nokia S40 phones and SE G705, without * locationProvider.setLocationListener(null, 0, 0, 0) - hopefully both reset() * and setLocationListener(null... will work on all phones. */ locationProvider.reset(); locationProvider.setLocationListener(null, 0, 0, 0); } locationProvider = null; receiverList.locationDecoderEnd(); //#debug logger.trace("exit close()"); } private void updateSolution(int state) { logger.info("Update Solution"); locationUpdated(locationProvider, LocationProvider.getLastKnownLocation(), true); if (state == LocationProvider.AVAILABLE) { if (receiverList != null) { receiverList.receiveStatus(LocationMsgReceiver.STATUS_NOFIX, 0); } } //#if polish.android // FIXME when j2mepolish is fixed; 2010-06 j2mepolish gives OUT_OF_SERVICE // even when waiting for a fix, so we don't want to switch GPS off here //#else if (state == LocationProvider.OUT_OF_SERVICE) { if (receiverList != null) { receiverList.receiveStatus(LocationMsgReceiver.STATUS_OFF, 0); receiverList.receiveMessage(Locale.get("jsr179input.ProviderStopped")/*provider stopped*/); } } //#endif //#if polish.android if (state == LocationProvider.TEMPORARILY_UNAVAILABLE || state == LocationProvider.OUT_OF_SERVICE) { //#else if (state == LocationProvider.TEMPORARILY_UNAVAILABLE) { //#endif /** * Even though the receiver is temporarily un-available, * we still need to receive updates periodically, as some * implementations will otherwise not reacquire the GPS signal. * So setting setLocationListener to 0 interval, which should have given * you all the status changes, does not work. */ if (receiverList != null) { receiverList.receiveStatus(LocationMsgReceiver.STATUS_NOFIX, 0); } } } public void triggerPositionUpdate() { } public void triggerLastKnownPositionUpdate() { locationUpdated(locationProvider, LocationProvider.getLastKnownLocation(), true); } public void disableRawLogging() { if (rawDataLogger != null) { try { rawDataLogger.close(); } catch (IOException e) { logger.exception(Locale.get("jsr179input.CouldntCloseRawGpsLogger")/*Couldnt close raw gps logger*/, e); } rawDataLogger = null; } } public void enableRawLogging(OutputStream os) { rawDataLogger = os; } public void addLocationMsgReceiver(LocationMsgReceiver receiver) { receiverList.addReceiver(receiver); } public boolean removeLocationMsgReceiver(LocationMsgReceiver receiver) { return receiverList.removeReceiver(receiver); } //#endif }