From 6123bc1e237936a3ad4c8bcfc48e49e004f09975 Mon Sep 17 00:00:00 2001 From: Herbert von Broeuschmeul Date: Sun, 12 Sep 2010 22:28:11 +0200 Subject: An application for using an external bluetooth GPS on Android devices. The application starts a service, then connects to a Bluetooth device (NMEA GPS) and creates a mock GPS provider which can be used to replace the internal GPS. It's also possible to log the external GPS NMEA data in a file on the device. --- .../bluetooth/provider/BlueetoothGpsManager.java | 321 ++++++++++++++ .../bluetooth/provider/BluetoothGpsActivity.java | 144 ++++++ .../provider/BluetoothGpsProviderService.java | 304 +++++++++++++ .../android/gps/nmea/util/NmeaParser.java | 483 +++++++++++++++++++++ 4 files changed, 1252 insertions(+) create mode 100644 src/org/broeuschmeul/android/gps/bluetooth/provider/BlueetoothGpsManager.java create mode 100644 src/org/broeuschmeul/android/gps/bluetooth/provider/BluetoothGpsActivity.java create mode 100644 src/org/broeuschmeul/android/gps/bluetooth/provider/BluetoothGpsProviderService.java create mode 100644 src/org/broeuschmeul/android/gps/nmea/util/NmeaParser.java (limited to 'src') diff --git a/src/org/broeuschmeul/android/gps/bluetooth/provider/BlueetoothGpsManager.java b/src/org/broeuschmeul/android/gps/bluetooth/provider/BlueetoothGpsManager.java new file mode 100644 index 0000000..bd9464d --- /dev/null +++ b/src/org/broeuschmeul/android/gps/bluetooth/provider/BlueetoothGpsManager.java @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2010 Herbert von Broeuschmeul + * Copyright (C) 2010 BluetoothGPS4Droid Project + * + * This file is part of BluetoothGPS4Droid. + * + * BluetoothGPS4Droid 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. + * + * BluetoothGPS4Droid 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 BluetoothGPS4Droid. If not, see . + */ + +package org.broeuschmeul.android.gps.bluetooth.provider; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.broeuschmeul.android.gps.nmea.util.NmeaParser; + +import android.app.Service; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothSocket; +import android.content.Context; +import android.location.Criteria; +import android.location.LocationManager; +import android.location.LocationProvider; +import android.location.GpsStatus.NmeaListener; +import android.util.Log; +import android.util.PrintStreamPrinter; + +public class BlueetoothGpsManager { + + private class ConnectedThread extends Thread { + private final InputStream in; + private final OutputStream out; + private final PrintStream out2; + + public ConnectedThread(BluetoothSocket socket) { + InputStream tmpIn = null; + OutputStream tmpOut = null; + PrintStream tmpOut2 = null; + try { + tmpIn = socket.getInputStream(); + tmpOut = socket.getOutputStream(); + if (tmpOut != null){ + tmpOut2 = new PrintStream(tmpOut, false, "US-ASCII"); + } + } catch (IOException e) { + Log.e("BT test", "error while getting socket streams", e); + } + in = tmpIn; + out = tmpOut; + out2 = tmpOut2; + } + + public void run() { + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(in,"US-ASCII")); + String s; + while((enabled && (s = reader.readLine()) != null)){ + Log.e("BT test", "data: "+System.currentTimeMillis()+" "+s + "xxx"); + notifyNmeaSentence(s+"\r\n"); +// parser.parseNmeaSentence(s); +// // writer.println(s); +// addNMEAString(s); +// nmeaSentenceHandler.ob + } + } catch (IOException e) { + Log.e("BT test", "error while getting data", e); + } finally { + disable(); + } + } + + /** + * Write to the connected OutStream. + * @param buffer The bytes to write + */ + public void write(byte[] buffer) { + try { + out.write(buffer); + } catch (IOException e) { +// Log.e(TAG, "Exception during write", e); + } + } + /** + * Write to the connected OutStream. + * @param buffer The data to write + */ + public void write(String buffer) { + try { + out2.print(buffer); + out.flush(); + } catch (IOException e) { +// Log.e(TAG, "Exception during write", e); + } + } + } + + private Service callingService; + private BluetoothDevice gpsDevice; + private BluetoothSocket gpsSocket; + private String gpsDeviceAddress; + private NmeaParser parser = new NmeaParser(10f); + private boolean enabled = false; + private ExecutorService notificationPool; + private List nmeaListeners = Collections.synchronizedList(new LinkedList()); + private LocationManager locationManager; +// private boolean mockGpsEnabled = true; +// private String mockLocationProvider = LocationManager.GPS_PROVIDER; + private ConnectedThread connectedThread; + +// private Handler nmeaSentenceHandler = new Handler(); + + + /** + * @return true if the bluetooth GPS is enabled + */ + public boolean isEnabled() { + return enabled; + } + + public BlueetoothGpsManager(Service callingService, String deviceAddress) { + this.gpsDeviceAddress = deviceAddress; + this.callingService = callingService; + locationManager = (LocationManager)callingService.getSystemService(Context.LOCATION_SERVICE); + parser.setLocationManager(locationManager); + } + + public synchronized void enable() { + if (! enabled){ + this.enabled = true; + final BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + if (bluetoothAdapter == null) { + // Device does not support Bluetooth + Log.e("BT test", "Device does not support Bluetooth"); + } else if (!bluetoothAdapter.isEnabled()) { +// Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); +// startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); + Log.e("BT test", "Bluetooth is not enabled"); + } else { + BluetoothDevice gpsDevice = bluetoothAdapter.getRemoteDevice(gpsDeviceAddress); + if (gpsDevice == null){ + Log.e("BT test", "GPS device not found"); + } else { + Log.e("BT test", "current device: "+gpsDevice.getName() + " -- " + gpsDevice.getAddress()); + try { + gpsSocket = gpsDevice.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")); + } catch (IOException e) { + Log.e("BT test", "Error during connection", e); + } + if (gpsSocket == null){ + Log.e("BT test", "Error while establishing connection: no socket"); + } else { +//// mockGpsEnabled = locationManager.isProviderEnabled(mockLocationProvider); +// LocationProvider prov = locationManager.getProvider(mockLocationProvider); +// Log.e("BT test", "Mock power: "+prov.getPowerRequirement()+" "+prov.getAccuracy()+" "+locationManager.isProviderEnabled(mockLocationProvider)); +// locationManager.addTestProvider(mockLocationProvider, false, true,false, false, +// true, true, true, Criteria.POWER_HIGH, Criteria.ACCURACY_FINE); +//// locationManager.setTestProviderEnabled(mockLocationProvider, true); +// prov = locationManager.getProvider(mockLocationProvider); +// Log.e("BT test", "Mock power: "+prov.getPowerRequirement()+" "+prov.getAccuracy()+" "+locationManager.isProviderEnabled(mockLocationProvider)); + + Runnable connectThread = new Runnable() { + @Override + public void run() { + // Cancel discovery because it will slow down the connection + bluetoothAdapter.cancelDiscovery(); + try { + // Connect the device through the socket. This will block + // until it succeeds or throws an exception + gpsSocket.connect(); + connectedThread = new ConnectedThread(gpsSocket); + connectedThread.start(); +// String command = callingService.getString(R.string.sirf_gll_on); +// String sentence = String.format((Locale)null,"$%s*%X\r\n", command, parser.computeChecksum(command)); +// String command1 = callingService.getString(R.string.sirf_gll_off); +// String sentence1 = String.format((Locale)null,"$%s*%X\r\n", command1, parser.computeChecksum(command1)); +// String command2 = callingService.getString(R.string.sirf_vtg_off); +// String sentence2 = String.format((Locale)null,"$%s*%X\r\n", command2, parser.computeChecksum(command2)); +// try { +// Thread.sleep(5000); +// } catch (InterruptedException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// Log.e("BT test", "sending NMEA sentence: "+"$PSRF105,1*3E\r\n"); +// connectedThread.write("$PSRF105,1*3E\r\n"); +// Log.e("BT test", "sending NMEA sentence: "+sentence1); +// connectedThread.write(sentence1); +// Log.e("BT test", "sending NMEA sentence: "+sentence2); +// connectedThread.write(sentence2); + } catch (IOException connectException) { + // Unable to connect; close everything and get out + Log.e("BT test", "error while connecting to socket", connectException); + disable(); +// callingService.stopSelf(); + } + } + }; + notificationPool = Executors.newSingleThreadExecutor(); + notificationPool.execute(connectThread); +// enableMockLocationProvider(LocationManager.GPS_PROVIDER); + } + } + } + } + } + + public synchronized void disable() { + if (enabled){ + enabled = false; + if (gpsSocket != null){ + try { + gpsSocket.close(); + } catch (IOException closeException) { + Log.e("BT test", "error while closing socket", closeException); + } + } + nmeaListeners.clear(); + disableMockLocationProvider(); + notificationPool.shutdown(); +//// locationManager.setTestProviderEnabled(mockLocationProvider, mockGpsEnabled); +// LocationProvider prov = locationManager.getProvider(mockLocationProvider); +// Log.e("BT test", "Mock power: "+prov.getPowerRequirement()+" "+prov.getAccuracy()+" "+locationManager.isProviderEnabled(mockLocationProvider)); +// locationManager.clearTestProviderEnabled(mockLocationProvider); +// prov = locationManager.getProvider(mockLocationProvider); +// Log.e("BT test", "Mock power: "+prov.getPowerRequirement()+" "+prov.getAccuracy()+" "+locationManager.isProviderEnabled(mockLocationProvider)); +// locationManager.clearTestProviderStatus(mockLocationProvider); +// locationManager.removeTestProvider(mockLocationProvider); +// prov = locationManager.getProvider(mockLocationProvider); +// Log.e("BT test", "Mock power: "+prov.getPowerRequirement()+" "+prov.getAccuracy()+" "+locationManager.isProviderEnabled(mockLocationProvider)); +// Log.e("BT test", "removed mock GPS"); + + callingService.stopSelf(); + } + } + + + public void enableMockLocationProvider(String gpsName){ + if (parser != null){ + parser.enableMockLocationProvider(gpsName); + } + } + + public void disableMockLocationProvider(){ + if (parser != null){ + parser.disableMockLocationProvider(); + } + } + + /** + * @return the mockGpsEnabled + */ + public boolean isMockGpsEnabled() { + boolean mockGpsEnabled = false; + if (parser != null){ + mockGpsEnabled = parser.isMockGpsEnabled(); + } + return mockGpsEnabled; + } + /** + * @return the mockLocationProvider + */ + public String getMockLocationProvider() { + String mockLocationProvider = null; + if (parser != null){ + mockLocationProvider = parser.getMockLocationProvider(); + } + return mockLocationProvider; + } + + + public boolean addNmeaListener(NmeaListener listener){ + if (!nmeaListeners.contains(listener)){ + nmeaListeners.add(listener); + } + return true; + } + + public void removeNmeaListener(NmeaListener listener){ + nmeaListeners.remove(listener); + } + + private void notifyNmeaSentence(final String nmeaSentence){ + if (enabled){ + parser.parseNmeaSentence(nmeaSentence); + final long timestamp = System.currentTimeMillis(); + synchronized(nmeaListeners) { + for(final NmeaListener listener : nmeaListeners){ + notificationPool.execute(new Runnable(){ + @Override + public void run() { + listener.onNmeaReceived(timestamp, nmeaSentence); + } + }); + } + } + } + } +} diff --git a/src/org/broeuschmeul/android/gps/bluetooth/provider/BluetoothGpsActivity.java b/src/org/broeuschmeul/android/gps/bluetooth/provider/BluetoothGpsActivity.java new file mode 100644 index 0000000..8ce8e62 --- /dev/null +++ b/src/org/broeuschmeul/android/gps/bluetooth/provider/BluetoothGpsActivity.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2010 Herbert von Broeuschmeul + * Copyright (C) 2010 BluetoothGPS4Droid Project + * + * This file is part of BluetoothGPS4Droid. + * + * BluetoothGPS4Droid 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. + * + * BluetoothGPS4Droid 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 BluetoothGPS4Droid. If not, see . + */ + +package org.broeuschmeul.android.gps.bluetooth.provider; + +import java.util.Set; +import java.util.prefs.Preferences; +import java.util.zip.CheckedInputStream; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceActivity; +import android.preference.PreferenceManager; +import android.preference.PreferenceScreen; +import android.preference.Preference.OnPreferenceChangeListener; +import android.util.Log; + +/** + * A PreferenceActivity Class used to configure, start and stop the NMEA tracker service. + * + * @author Herbert von Broeuschmeul + * + */ +public class BluetoothGpsActivity extends PreferenceActivity implements OnPreferenceChangeListener, OnSharedPreferenceChangeListener { + private SharedPreferences sharedPref ; + private BluetoothAdapter bluetoothAdapter = null; + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.pref); + sharedPref = PreferenceManager.getDefaultSharedPreferences(this); + sharedPref.registerOnSharedPreferenceChangeListener(this); + bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + } + + /* (non-Javadoc) + * @see android.app.Activity#onResume() + */ + @Override + protected void onResume() { + this.updateDevicePreferenceList(); + super.onResume(); + } + + private void updateDevicePreferenceSummary(){ + // update bluetooth device summary + String deviceName = ""; + ListPreference prefDevices = (ListPreference)findPreference(BluetoothGpsProviderService.PREF_BLUETOOTH_DEVICE); + String deviceAddress = sharedPref.getString(BluetoothGpsProviderService.PREF_BLUETOOTH_DEVICE, null); + if (BluetoothAdapter.checkBluetoothAddress(deviceAddress)){ + deviceName = bluetoothAdapter.getRemoteDevice(deviceAddress).getName(); + } + prefDevices.setSummary(getString(R.string.pref_bluetooth_device_summary, deviceName)); + } + + private void updateDevicePreferenceList(){ + // update bluetooth device summary + updateDevicePreferenceSummary(); + // update bluetooth device list + ListPreference prefDevices = (ListPreference)findPreference(BluetoothGpsProviderService.PREF_BLUETOOTH_DEVICE); + Set pairedDevices = bluetoothAdapter.getBondedDevices(); + String[] entryValues = new String[pairedDevices.size()]; + String[] entries = new String[pairedDevices.size()]; + int i = 0; + // Loop through paired devices + for (BluetoothDevice device : pairedDevices) { + // Add the name and address to the ListPreference enties and entyValues + Log.e("BT test", "device: "+device.getName() + " -- " + device.getAddress()); + entryValues[i] = device.getAddress(); + entries[i] = device.getName(); + i++; + } + prefDevices.setEntryValues(entryValues); + prefDevices.setEntries(entries); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + sharedPref.unregisterOnSharedPreferenceChangeListener(this); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + if (BluetoothGpsProviderService.PREF_START_GPS_PROVIDER.equals(key)){ + boolean val = false; + if (val = sharedPreferences.getBoolean(key, false)){ + startService(new Intent(BluetoothGpsProviderService.ACTION_START_GPS_PROVIDER)); + } else { + startService(new Intent(BluetoothGpsProviderService.ACTION_STOP_GPS_PROVIDER)); + } + CheckBoxPreference pref = (CheckBoxPreference)findPreference(key); + if (pref.isChecked() != val){ + pref.setChecked(val); + } + } else if (BluetoothGpsProviderService.PREF_TRACK_RECORDING.equals(key)){ + boolean val = false; + if (val = sharedPreferences.getBoolean(key, false)){ + startService(new Intent(BluetoothGpsProviderService.ACTION_START_TRACK_RECORDING)); + } else { + startService(new Intent(BluetoothGpsProviderService.ACTION_STOP_TRACK_RECORDING)); + } + CheckBoxPreference pref = (CheckBoxPreference)findPreference(key); + if (pref.isChecked() != val){ + pref.setChecked(val); + } + } else if (BluetoothGpsProviderService.PREF_BLUETOOTH_DEVICE.equals(key)){ + updateDevicePreferenceSummary(); + } + } +} \ No newline at end of file diff --git a/src/org/broeuschmeul/android/gps/bluetooth/provider/BluetoothGpsProviderService.java b/src/org/broeuschmeul/android/gps/bluetooth/provider/BluetoothGpsProviderService.java new file mode 100644 index 0000000..0ab972b --- /dev/null +++ b/src/org/broeuschmeul/android/gps/bluetooth/provider/BluetoothGpsProviderService.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2010 Herbert von Broeuschmeul + * Copyright (C) 2010 BluetoothGPS4Droid Project + * + * This file is part of BluetoothGPS4Droid. + * + * BluetoothGPS4Droid 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. + * + * BluetoothGPS4Droid 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 BluetoothGPS4Droid. If not, see . + */ + +/** + * + */ +package org.broeuschmeul.android.gps.bluetooth.provider; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; + +import android.app.Notification; +import android.app.PendingIntent; +import android.app.Service; +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.location.GpsStatus.NmeaListener; +import android.os.Bundle; +import android.os.IBinder; +import android.preference.PreferenceManager; +import android.util.Config; +import android.util.Log; +import android.widget.Toast; + +/** + * A Service used to replace Android internal GPS with a bluetooth GPS and/or write GPS NMEA data in a File. + * + * @author Herbert von Broeuschmeul + * + */ +public class BluetoothGpsProviderService extends Service implements NmeaListener, LocationListener { + + public static final String ACTION_START_TRACK_RECORDING = "org.broeuschmeul.android.gps.bluetooth.tracker.nmea.intent.action.START_TRACK_RECORDING"; + public static final String ACTION_STOP_TRACK_RECORDING = "org.broeuschmeul.android.gps.bluetooth.tracker.nmea.intent.action.STOP_TRACK_RECORDING"; + public static final String ACTION_START_GPS_PROVIDER = "org.broeuschmeul.android.gps.bluetooth.provider.nmea.intent.action.START_GPS_PROVIDER"; + public static final String ACTION_STOP_GPS_PROVIDER = "org.broeuschmeul.android.gps.bluetooth.provider.nmea.intent.action.STOP_GPS_PROVIDER"; + public static final String PREF_START_GPS_PROVIDER = "startGps"; + public static final String PREF_TRACK_RECORDING = "trackRecording"; + public static final String PREF_TRACK_MIN_DISTANCE = "trackMinDistance"; + public static final String PREF_TRACK_MIN_TIME = "trackMinTime"; + public static final String PREF_TRACK_FILE_DIR = "trackFileDirectory"; + public static final String PREF_TRACK_FILE_PREFIX = "trackFilePrefix"; + public static final String PREF_BLUETOOTH_DEVICE = "bluetoothDevice"; + + private LocationManager lm; + private BlueetoothGpsManager gpsManager = null; + private PrintWriter writer; + private File trackFile; + private boolean preludeWritten = false; + private Toast toast ; + + @Override + public void onCreate() { + super.onCreate(); + toast = Toast.makeText(getApplicationContext(), "NMEA track recording... on", Toast.LENGTH_SHORT); + lm = (LocationManager)this.getSystemService(Context.LOCATION_SERVICE); + } + + @Override + public void onStart(Intent intent, int startId) { +// super.onStart(intent, startId); + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + SharedPreferences.Editor edit = sharedPreferences.edit(); +// long minTime = Long.parseLong(sharedPreferences.getString(PREF_TRACK_MIN_TIME, this.getString(R.string.defaultTrackMinTime))); +// float minDistance = Float.parseFloat(sharedPreferences.getString(PREF_TRACK_MIN_DISTANCE, this.getString(R.string.defaultTrackMinDistance))); + String deviceAddress = sharedPreferences.getString(PREF_BLUETOOTH_DEVICE, null); + if (Config.LOGD){ +// Log.d(BluetoothGpsProviderService.class.getName(), "prefs minTime: "+minTime); +// Log.d(BluetoothGpsProviderService.class.getName(), "prefs minDistance: "+minDistance); + Log.d(BluetoothGpsProviderService.class.getName(), "prefs device addr: "+deviceAddress); + } + if (ACTION_START_GPS_PROVIDER.equals(intent.getAction())){ + if (gpsManager == null){ + Notification notification = new Notification(R.drawable.icon, this.getString(R.string.foreground_gps_provider_started_notification), System.currentTimeMillis()); + Intent myIntent = new Intent(this, BluetoothGpsActivity.class); + PendingIntent myPendingIntent = PendingIntent.getActivity(this, 0, myIntent, PendingIntent.FLAG_CANCEL_CURRENT); + notification.setLatestEventInfo(getApplicationContext(), this.getString(R.string.foreground_service_started_notification_title), this.getString(R.string.foreground_gps_provider_started_notification), myPendingIntent); + startForeground(R.string.foreground_gps_provider_started_notification, notification); + if (BluetoothAdapter.checkBluetoothAddress(deviceAddress)){ + gpsManager = new BlueetoothGpsManager(this, deviceAddress); + gpsManager.enableMockLocationProvider(LocationManager.GPS_PROVIDER); + gpsManager.enable(); + if (! sharedPreferences.getBoolean(PREF_START_GPS_PROVIDER, false)){ + edit.putBoolean(PREF_START_GPS_PROVIDER,true); + edit.commit(); + } + toast.setText(this.getString(R.string.msg_gps_provider_started)); + toast.show(); + } else { +// if (! sharedPreferences.getBoolean(PREF_START_GPS_PROVIDER, true)){ +// edit.putBoolean(PREF_START_GPS_PROVIDER,false); +// edit.commit(); +// } +// toast.setText(this.getString(R.string.msg_invalid_device)); +// toast.show(); + stopSelf(); + } + } else { + toast.setText(this.getString(R.string.msg_gps_provider_already_started)); + toast.show(); + } + } else if (ACTION_START_TRACK_RECORDING.equals(intent.getAction())){ + if (trackFile == null){ + if (gpsManager != null){ + beginTrack(); + gpsManager.addNmeaListener(this); + if (! sharedPreferences.getBoolean(PREF_TRACK_RECORDING, false)){ + edit.putBoolean(PREF_TRACK_RECORDING,true); + edit.commit(); + } + toast.setText(this.getString(R.string.msg_nmea_recording_started)); + toast.show(); + } else { + endTrack(); + if ( sharedPreferences.getBoolean(PREF_TRACK_RECORDING, true)){ + edit.putBoolean(PREF_TRACK_RECORDING,false); + edit.commit(); + } +// toast.setText(this.getString(R.string.msg_device_not_started)); +// toast.show(); + } + } else { + toast.setText(this.getString(R.string.msg_nmea_recording_already_started)); + toast.show(); + } + } else if (ACTION_STOP_TRACK_RECORDING.equals(intent.getAction())){ + if (gpsManager != null){ + gpsManager.removeNmeaListener(this); + endTrack(); + toast.setText(this.getString(R.string.msg_nmea_recording_stopped)); + toast.show(); + } + if (sharedPreferences.getBoolean(PREF_TRACK_RECORDING, true)){ + edit.putBoolean(PREF_TRACK_RECORDING,false); + edit.commit(); + } + } else if (ACTION_STOP_GPS_PROVIDER.equals(intent.getAction())){ + if (sharedPreferences.getBoolean(PREF_START_GPS_PROVIDER, true)){ + edit.putBoolean(PREF_START_GPS_PROVIDER,false); + edit.commit(); + } + stopSelf(); + } + } + + /* (non-Javadoc) + * @see android.app.Service#onStartCommand(android.content.Intent, int, int) + */ + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + onStart(intent, startId); + return Service.START_NOT_STICKY; + } + + @Override + public void onDestroy() { + BlueetoothGpsManager manager = gpsManager; + gpsManager = null; + if (manager != null){ + manager.removeNmeaListener(this); + manager.disableMockLocationProvider(); + manager.disable(); + } + endTrack(); + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + SharedPreferences.Editor edit = sharedPreferences.edit(); + if (sharedPreferences.getBoolean(PREF_TRACK_RECORDING, true)){ + edit.putBoolean(PREF_TRACK_RECORDING,false); + edit.commit(); + } + if (sharedPreferences.getBoolean(PREF_START_GPS_PROVIDER, true)){ + edit.putBoolean(PREF_START_GPS_PROVIDER,false); + edit.commit(); + } +// toast.setText(this.getString(R.string.msg_nmea_recording_stopped)); +// toast.show(); + toast.setText(this.getString(R.string.msg_gps_provider_stopped)); + toast.show(); + super.onDestroy(); + } + + private void beginTrack(){ + SimpleDateFormat fmt = new SimpleDateFormat("_yyyy-MM-dd_HH-mm-ss'.nmea'"); + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + String trackDirName = sharedPreferences.getString(PREF_TRACK_FILE_DIR, this.getString(R.string.defaultTrackFileDirectory)); + String trackFilePrefix = sharedPreferences.getString(PREF_TRACK_FILE_PREFIX, this.getString(R.string.defaultTrackFilePrefix)); + trackFile = new File(trackDirName,trackFilePrefix+fmt.format(new Date())); + Log.i(BluetoothGpsProviderService.class.getName(), "Writing the prelude of the NMEA file: "+trackFile.getAbsolutePath()); + File trackDir = trackFile.getParentFile(); + try { + if ((! trackDir.mkdirs()) && (! trackDir.isDirectory())){ + Log.e(BluetoothGpsProviderService.class.getName(), "Error while creating parent dir of NMEA file: "+trackDir.getAbsolutePath()); + } + writer = new PrintWriter(new BufferedWriter(new FileWriter(trackFile))); + preludeWritten = true; + } catch (IOException e) { + Log.e(BluetoothGpsProviderService.class.getName(), "Error while writing the prelude of the NMEA file: "+trackFile.getAbsolutePath(), e); + // there was an error while writing the prelude of the NMEA file, stopping the service... + stopSelf(); +// } catch (SecurityException e) { +// Log.e(BluetoothGpsProviderService.class.getName(), "Error while writing the prelude of the NMEA file: "+trackFile.getAbsolutePath(), e); +// // there was an error while writing the prelude of the NMEA file, stopping the service... +// stopSelf(); + } + } + private void endTrack(){ + if (trackFile != null && writer != null){ + Log.i(BluetoothGpsProviderService.class.getName(), "Ending the NMEA file: "+trackFile.getAbsolutePath()); + preludeWritten = false; + writer.close(); + trackFile = null; + } + } + private void addNMEAString(String data){ + if (! preludeWritten){ + beginTrack(); + } + Log.d(BluetoothGpsProviderService.class.getName(), "Adding data in the NMEA file: "+ data); + if (trackFile != null && writer != null){ + writer.print(data); + } + } + /* (non-Javadoc) + * @see android.app.Service#onBind(android.content.Intent) + */ + @Override + public IBinder onBind(Intent intent) { + if (Config.LOGD){ + Log.d(BluetoothGpsProviderService.class.getName(), "trying access IBinder"); + } + return null; + } + + @Override + public void onLocationChanged(Location location) { + // TODO Auto-generated method stub + } + + @Override + public void onProviderDisabled(String provider) { + Log.e(BluetoothGpsProviderService.class.getName(), "The GPS has been disabled.....stopping the NMEA tracker service."); + stopSelf(); + } + + @Override + public void onProviderEnabled(String provider) { + // TODO Auto-generated method stub + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + // TODO Auto-generated method stub + } + + @Override + public void onNmeaReceived(long timestamp, String data) { + addNMEAString(data); + } + +// private void startBluetoothGps(String deviceAddress){ +// Notification notification = new Notification(R.drawable.icon, this.getString(R.string.foreground_service_started_notification), System.currentTimeMillis()); +// Intent myIntent = new Intent(this, BluetoothGpsActivity.class); +// PendingIntent myPendingIntent = PendingIntent.getActivity(this, 0, myIntent, PendingIntent.FLAG_CANCEL_CURRENT); +// notification.setLatestEventInfo(getApplicationContext(), this.getString(R.string.foreground_service_started_notification_title), this.getString(R.string.foreground_service_started_notification), myPendingIntent); +// startForeground(R.string.foreground_service_started_notification, notification); +// if (BluetoothAdapter.checkBluetoothAddress(deviceAddress)){ +// gpsManager = new BlueetoothGpsManager(this, deviceAddress); +// gpsManager.enable(); +// gpsManager.enableMockLocationProvider(LocationManager.GPS_PROVIDER); +// toast.setText(this.getString(R.string.msg_started)); +// toast.show(); +//// } else { +//// toast.setText(this.getString(R.string.msg_invalid_device)); +//// toast.show(); +// } +// } +} diff --git a/src/org/broeuschmeul/android/gps/nmea/util/NmeaParser.java b/src/org/broeuschmeul/android/gps/nmea/util/NmeaParser.java new file mode 100644 index 0000000..be4e973 --- /dev/null +++ b/src/org/broeuschmeul/android/gps/nmea/util/NmeaParser.java @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2010 Herbert von Broeuschmeul + * Copyright (C) 2010 BluetoothGPS4Droid Project + * + * This file is part of BluetoothGPS4Droid. + * + * BluetoothGPS4Droid 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. + * + * BluetoothGPS4Droid 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 BluetoothGPS4Droid. If not, see . + */ + +package org.broeuschmeul.android.gps.nmea.util; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.location.Criteria; +import android.location.Location; +import android.location.LocationManager; +import android.location.LocationProvider; +import android.os.Bundle; +import android.text.TextUtils; +import android.text.TextUtils.SimpleStringSplitter; +import android.util.Log; + +public class NmeaParser { + private String fixTime; + private long fixTimestamp; + + private boolean hasGGA = false; + private boolean hasRMC = false; + private LocationManager lm; + private float precision = 10f; + private boolean mockGpsEnabled = false; + private String mockLocationProvider = null; + + private Location fix = new Location(mockLocationProvider); + + public NmeaParser(){ + this(5f); + } + public NmeaParser(float precision){ + this.precision = precision; + } + + public void setLocationManager(LocationManager lm){ + this.lm = lm; + } + + public void enableMockLocationProvider(String gpsName){ + LocationProvider prov; + if (gpsName != null && gpsName != "" ){ + if (! gpsName.equals(mockLocationProvider)){ + disableMockLocationProvider(); + mockLocationProvider = gpsName; + } + if (! mockGpsEnabled){ + prov = lm.getProvider(mockLocationProvider); + if (prov != null){ + Log.e("BT test", "Mock provider: "+prov.getName()+" "+prov.getPowerRequirement()+" "+prov.getAccuracy()+" "+lm.isProviderEnabled(mockLocationProvider)); + } + lm.addTestProvider(mockLocationProvider, false, true,false, false, true, true, true, Criteria.POWER_HIGH, Criteria.ACCURACY_FINE); + if (! LocationManager.GPS_PROVIDER.equals(mockLocationProvider)){ + // mockGpsEnabled = locationManager.isProviderEnabled(mockLocationProvider); + lm.setTestProviderEnabled(mockLocationProvider, true); + } + mockGpsEnabled = true; + } else { + Log.e("BT test", "Mock provider already enabled: "+mockLocationProvider); + } + prov = lm.getProvider(mockLocationProvider); + if (prov != null){ + Log.e("BT test", "Mock provider: "+prov.getName()+" "+prov.getPowerRequirement()+" "+prov.getAccuracy()+" "+lm.isProviderEnabled(mockLocationProvider)); + } + } + } + + public void disableMockLocationProvider(){ + LocationProvider prov; + if (mockLocationProvider != null && mockLocationProvider != "" && mockGpsEnabled){ + prov = lm.getProvider(mockLocationProvider); + if (prov != null){ + Log.e("BT test", "Mock provider: "+prov.getName()+" "+prov.getPowerRequirement()+" "+prov.getAccuracy()+" "+lm.isProviderEnabled(mockLocationProvider)); + } + mockGpsEnabled = false; + if (! LocationManager.GPS_PROVIDER.equals(mockLocationProvider)){ + // mockGpsEnabled = locationManager.isProviderEnabled(mockLocationProvider); + lm.setTestProviderEnabled(mockLocationProvider, false); + } + // locationManager.setTestProviderEnabled(mockLocationProvider, mockGpsEnabled); + prov = lm.getProvider(mockLocationProvider); + if (prov != null){ + Log.e("BT test", "Mock provider: "+prov.getName()+" "+prov.getPowerRequirement()+" "+prov.getAccuracy()+" "+lm.isProviderEnabled(mockLocationProvider)); + } + lm.clearTestProviderEnabled(mockLocationProvider); + prov = lm.getProvider(mockLocationProvider); + if (prov != null){ + Log.e("BT test", "Mock provider: "+prov.getName()+" "+prov.getPowerRequirement()+" "+prov.getAccuracy()+" "+lm.isProviderEnabled(mockLocationProvider)); + } + lm.clearTestProviderStatus(mockLocationProvider); + lm.removeTestProvider(mockLocationProvider); + prov = lm.getProvider(mockLocationProvider); + if (prov != null){ + Log.e("BT test", "Mock provider: "+prov.getName()+" "+prov.getPowerRequirement()+" "+prov.getAccuracy()+" "+lm.isProviderEnabled(mockLocationProvider)); + } + Log.e("BT test", "removed mock GPS"); + } else { + Log.e("BT test", "Mock provider already disabled: "+mockLocationProvider); + } + mockLocationProvider = null; + mockGpsEnabled = false; + } + + /** + * @return the mockGpsEnabled + */ + public boolean isMockGpsEnabled() { + return mockGpsEnabled; + } + + /** + * @return the mockLocationProvider + */ + public String getMockLocationProvider() { + return mockLocationProvider; + } + + private void notifyFix(Location fix){ + //R.drawable.stat + fixTime = null; + hasGGA = false; + hasRMC=false; + if (fix != null){ + Log.e(this.getClass().getSimpleName(), "New Fix: "+System.currentTimeMillis()+" "+fix); + if (lm != null && mockGpsEnabled){ + lm.setTestProviderLocation(mockLocationProvider, fix); + Log.e(this.getClass().getSimpleName(), "New Fix notified to Location Manager: "+mockLocationProvider); + } + this.fix = null; + } + } + + // parse NMEA Sentence + public void parseNmeaSentence(String gpsSentence){ + Log.e("BT test", "data: "+System.currentTimeMillis()+" "+gpsSentence); + Pattern xx = Pattern.compile("\\$([^*$]*)\\*([0-9A-F][0-9A-F])?\r\n"); + Matcher m = xx.matcher(gpsSentence); + if (m.matches()){ + String sentence = m.group(1); + String checkSum = m.group(2); + Log.e("BT test", "data: "+System.currentTimeMillis()+" "+sentence+" cheksum; "+checkSum +" control: "+String.format("%X",computeChecksum(sentence))); + SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(sentence); + String command = splitter.next(); + if (command.equals("GPGGA")){ + /* $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 + + Where: + GGA Global Positioning System Fix Data + 123519 Fix taken at 12:35:19 UTC + 4807.038,N Latitude 48 deg 07.038' N + 01131.000,E Longitude 11 deg 31.000' E + 1 Fix quality: 0 = invalid + 1 = GPS fix (SPS) + 2 = DGPS fix + 3 = PPS fix + 4 = Real Time Kinematic + 5 = Float RTK + 6 = estimated (dead reckoning) (2.3 feature) + 7 = Manual input mode + 8 = Simulation mode + 08 Number of satellites being tracked + 0.9 Horizontal dilution of position + 545.4,M Altitude, Meters, above mean sea level + 46.9,M Height of geoid (mean sea level) above WGS84 + ellipsoid + (empty field) time in seconds since last DGPS update + (empty field) DGPS station ID number + *47 the checksum data, always begins with * + */ + // UTC time of fix HHmmss.S + String time = splitter.next(); + // latitude ddmm.M + String lat = splitter.next(); + // direction (N/S) + String latDir = splitter.next(); + // longitude dddmm.M + String lon = splitter.next(); + // direction (E/W) + String lonDir = splitter.next(); + /* fix quality: + 0= invalid + 1 = GPS fix (SPS) + 2 = DGPS fix + 3 = PPS fix + 4 = Real Time Kinematic + 5 = Float RTK + 6 = estimated (dead reckoning) (2.3 feature) + 7 = Manual input mode + 8 = Simulation mode + */ + String quality = splitter.next(); + // Number of satellites being tracked + String nbSat = splitter.next(); + // Horizontal dilution of position (float) + String hdop = splitter.next(); + // Altitude, Meters, above mean sea level + String alt = splitter.next(); + // Height of geoid (mean sea level) above WGS84 ellipsoid + String geoAlt = splitter.next(); + // time in seconds since last DGPS update + // DGPS station ID number + if (quality != null && !quality.equals("") && !quality.equals("0") ){ + if (! time.equals(fixTime)){ + notifyFix(fix); + fix = new Location(mockLocationProvider); + fixTime = time; + fixTimestamp = parseNmeaTime(time); + fix.setTime(fixTimestamp); + Log.e(this.getClass().getSimpleName(), "Fix: "+fix); + } + if (lat != null && !lat.equals("")){ + fix.setLatitude(parseNmeaLatitude(lat,latDir)); + } + if (lon != null && !lon.equals("")){ + fix.setLongitude(parseNmeaLongitude(lon,lonDir)); + } + if (hdop != null && !hdop.equals("")){ + fix.setAccuracy(Float.parseFloat(hdop)*precision); + } + if (alt != null && !alt.equals("")){ + fix.setAltitude(Double.parseDouble(alt)); + } + if (nbSat != null && !nbSat.equals("")){ + Bundle extras = new Bundle(); + extras.putInt("satellites", Integer.parseInt(nbSat)); + fix.setExtras(extras); + } + Log.e(this.getClass().getSimpleName(), "Fix: "+System.currentTimeMillis()+" "+fix); + hasGGA = true; + if (hasGGA && hasRMC){ + notifyFix(fix); + } + } + } else if (command.equals("GPRMC")){ + /* $GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A + + Where: + RMC Recommended Minimum sentence C + 123519 Fix taken at 12:35:19 UTC + A Status A=active or V=Void. + 4807.038,N Latitude 48 deg 07.038' N + 01131.000,E Longitude 11 deg 31.000' E + 022.4 Speed over the ground in knots + 084.4 Track angle in degrees True + 230394 Date - 23rd of March 1994 + 003.1,W Magnetic Variation + *6A The checksum data, always begins with * + */ + // UTC time of fix HHmmss.S + String time = splitter.next(); + // fix status (A/V) + String status = splitter.next(); + // latitude ddmm.M + String lat = splitter.next(); + // direction (N/S) + String latDir = splitter.next(); + // longitude dddmm.M + String lon = splitter.next(); + // direction (E/W) + String lonDir = splitter.next(); + // Speed over the ground in knots + String speed = splitter.next(); + // Track angle in degrees True + String bearing = splitter.next(); + // UTC date of fix DDMMYY + String date = splitter.next(); + // Magnetic Variation ddd.D + String magn = splitter.next(); + // Magnetic variation direction (E/W) + String magnDir = splitter.next(); + // for NMEA 0183 version 3.00 active the Mode indicator field is added + // Mode indicator, (A=autonomous, D=differential, E=Estimated, N=not valid, S=Simulator ) + if (status != null && !status.equals("") && status.equals("A") ){ + if (! time.equals(fixTime)){ + notifyFix(fix); + fix = new Location(mockLocationProvider); + fixTime = time; + fixTimestamp = parseNmeaTime(time); + fix.setTime(fixTimestamp); + Log.e(this.getClass().getSimpleName(), "Fix: "+fix); + } + if (lat != null && !lat.equals("")){ + fix.setLatitude(parseNmeaLatitude(lat,latDir)); + } + if (lon != null && !lon.equals("")){ + fix.setLongitude(parseNmeaLongitude(lon,lonDir)); + } + if (speed != null && !speed.equals("")){ + fix.setSpeed(parseNmeaSpeed(speed, "N")); + } + if (bearing != null && !bearing.equals("")){ + fix.setBearing(Float.parseFloat(bearing)); + } + Log.e(this.getClass().getSimpleName(), "Fix: "+System.currentTimeMillis()+" "+fix); + hasRMC = true; + if (hasGGA && hasRMC){ + notifyFix(fix); + } + } + } else if (command.equals("GPGSA")){ + /* $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39 + + Where: + GSA Satellite status + A Auto selection of 2D or 3D fix (M = manual) + 3 3D fix - values include: 1 = no fix + 2 = 2D fix + 3 = 3D fix + 04,05... PRNs of satellites used for fix (space for 12) + 2.5 PDOP (Position dilution of precision) + 1.3 Horizontal dilution of precision (HDOP) + 2.1 Vertical dilution of precision (VDOP) + *39 the checksum data, always begins with * + */ + // mode : A Auto selection of 2D or 3D fix / M = manual + String mode = splitter.next(); + // fix type : 1 - no fix / 2 - 2D / 3 - 3D + String fixType = splitter.next(); + // discard PRNs of satellites used for fix (space for 12) + for (int i=0 ; ((i<12)&&(! "1".equals(fixType))) ; i++){ + splitter.next(); + } + // Position dilution of precision (float) + String pdop = splitter.next(); + // Horizontal dilution of precision (float) + String hdop = splitter.next(); + // Vertical dilution of precision (float) + String vdop = splitter.next(); + } else if (command.equals("GPVTG")){ + /* $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48 + + where: + VTG Track made good and ground speed + 054.7,T True track made good (degrees) + 034.4,M Magnetic track made good + 005.5,N Ground speed, knots + 010.2,K Ground speed, Kilometers per hour + *48 Checksum + */ + // Track angle in degrees True + String bearing = splitter.next(); + // T + splitter.next(); + // Magnetic track made good + String magn = splitter.next(); + // M + splitter.next(); + // Speed over the ground in knots + String speedKnots = splitter.next(); + // N + splitter.next(); + // Speed over the ground in Kilometers per hour + String speedKm = splitter.next(); + // K + splitter.next(); + // for NMEA 0183 version 3.00 active the Mode indicator field is added + // Mode indicator, (A=autonomous, D=differential, E=Estimated, N=not valid, S=Simulator ) + } else if (command.equals("GPGLL")){ + /* $GPGLL,4916.45,N,12311.12,W,225444,A,*1D + + Where: + GLL Geographic position, Latitude and Longitude + 4916.46,N Latitude 49 deg. 16.45 min. North + 12311.12,W Longitude 123 deg. 11.12 min. West + 225444 Fix taken at 22:54:44 UTC + A Data Active or V (void) + *iD checksum data + */ + // latitude ddmm.M + String lat = splitter.next(); + // direction (N/S) + String latDir = splitter.next(); + // longitude dddmm.M + String lon = splitter.next(); + // direction (E/W) + String lonDir = splitter.next(); + // UTC time of fix HHmmss.S + String time = splitter.next(); + // fix status (A/V) + String status = splitter.next(); + // for NMEA 0183 version 3.00 active the Mode indicator field is added + // Mode indicator, (A=autonomous, D=differential, E=Estimated, N=not valid, S=Simulator ) + } + } + } + + public double parseNmeaLatitude(String lat,String orientation){ + double latitude = 0.0; + if (lat != null && orientation != null && !lat.equals("") && !orientation.equals("")){ + double temp1 = Double.parseDouble(lat); + double temp2 = Math.floor(temp1/100); + double temp3 = (temp1/100 - temp2)/0.6; + if (orientation.equals("S")){ + latitude = -(temp2+temp3); + } else if (orientation.equals("N")){ + latitude = (temp2+temp3); + } + } + return latitude; + } + public double parseNmeaLongitude(String lon,String orientation){ + double longitude = 0.0; + if (lon != null && orientation != null && !lon.equals("") && !orientation.equals("")){ + double temp1 = Double.parseDouble(lon); + double temp2 = Math.floor(temp1/100); + double temp3 = (temp1/100 - temp2)/0.6; + if (orientation.equals("W")){ + longitude = -(temp2+temp3); + } else if (orientation.equals("E")){ + longitude = (temp2+temp3); + } + } + return longitude; + } + public float parseNmeaSpeed(String speed,String metric){ + float meterSpeed = 0.0f; + if (speed != null && metric != null && !speed.equals("") && !metric.equals("")){ + float temp1 = Float.parseFloat(speed)/3.6f; + if (metric.equals("K")){ + meterSpeed = temp1; + } else if (metric.equals("N")){ + meterSpeed = temp1*1.852f; + } + } + return meterSpeed; + } + public long parseNmeaTime(String time){ + long timestamp = 0; + SimpleDateFormat fmt = new SimpleDateFormat("HHmmss.S"); + fmt.setTimeZone(TimeZone.getTimeZone("GMT")); + try { + if (time != null && time != null){ + long now = System.currentTimeMillis(); + long today = now - (now %86400000L); + long temp1; + temp1 = fmt.parse(time).getTime(); + long temp2 = today+temp1; + // + if (temp2 - now > 43200000L) { + timestamp = temp2 - 86400000L; + } else if (now - temp2 > 43200000L){ + timestamp = temp2 + 86400000L; + } else { + timestamp = temp2; + } + } + } catch (ParseException e) { + // TODO Auto-generated catch block + Log.e(this.getClass().getSimpleName(), "Error while parsing NMEA time", e); + } + return timestamp; + } + public byte computeChecksum(String s){ + byte checksum = 0; + for (char c : s.toCharArray()){ + checksum ^= (byte)c; + } + return checksum; + } +} -- cgit v1.1