From 031df4b3ca2d542e77e99aed6ef728a916e5f059 Mon Sep 17 00:00:00 2001 From: localhorst Date: Mon, 25 Apr 2022 17:11:58 +0200 Subject: [PATCH] first working serial nmea parser --- app/build.gradle | 2 +- .../com/externgnss/MainActivity.java | 128 ++++++------------ .../com/externgnss/NMEAParser.java | 121 +++++++++++++++++ .../com/externgnss/SerialUSB.java | 94 ++++++------- 4 files changed, 204 insertions(+), 141 deletions(-) create mode 100644 app/src/main/java/hendrikschutter/com/externgnss/NMEAParser.java diff --git a/app/build.gradle b/app/build.gradle index f9c37ac..2f79a56 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,7 @@ android { compileSdkVersion 30 defaultConfig { applicationId "hendrikschutter.com.externgnss" - minSdkVersion 17 + minSdkVersion 19 targetSdkVersion 30 versionCode 1 versionName "1.0" diff --git a/app/src/main/java/hendrikschutter/com/externgnss/MainActivity.java b/app/src/main/java/hendrikschutter/com/externgnss/MainActivity.java index 0861c71..276d35f 100644 --- a/app/src/main/java/hendrikschutter/com/externgnss/MainActivity.java +++ b/app/src/main/java/hendrikschutter/com/externgnss/MainActivity.java @@ -2,13 +2,7 @@ package hendrikschutter.com.externgnss; import android.Manifest; import android.annotation.SuppressLint; -import android.app.PendingIntent; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbDeviceConnection; -import android.hardware.usb.UsbManager; import android.location.LocationListener; import android.location.LocationManager; import androidx.appcompat.app.AppCompatActivity; @@ -23,36 +17,31 @@ import pub.devrel.easypermissions.EasyPermissions; import static java.lang.Thread.sleep; -import com.hoho.android.usbserial.driver.UsbSerialDriver; -import com.hoho.android.usbserial.driver.UsbSerialPort; -import com.hoho.android.usbserial.driver.UsbSerialProber; - -import java.io.IOException; -import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; public class MainActivity extends AppCompatActivity { private TextView output; - private LocationManager locationManager; + private LocationManager locationManager= null; + private SerialUSB serialUSB = null; + private NMEAParser nmeaParser = null; private boolean doRun = true; private final Context cntxToastInternGNSSUpdate = this; final String[] perms = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.INTERNET,}; private static final String INTENT_ACTION_GRANT_USB = BuildConfig.APPLICATION_ID + ".GRANT_USB"; private boolean bChange = true; + private BlockingQueue rawSerialByteDataQueue = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - startLocationInit(); - - SerialUSB serialUSB = new SerialUSB(this); - - - - - + // start intern GNSS and display stats on UI + startInternGNSS(); + // start extern GNSS and display stats on UI + startExternGNSS(); } // From https://github.com/googlesamples/easypermissions @@ -63,44 +52,33 @@ public class MainActivity extends AppCompatActivity { EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); } + + private void startExternGNSS(){ + rawSerialByteDataQueue = new LinkedBlockingQueue<>(); + serialUSB = new SerialUSB(this, rawSerialByteDataQueue); + + if(serialUSB.findSerialDevice()){ + serialUSB.connect(9600, 0); + if (serialUSB.isConnected()){ + nmeaParser = new NMEAParser(); + nmeaParser.setReceiveByteStream(rawSerialByteDataQueue); + } + } + } + @AfterPermissionGranted(123) - private void startLocationInit() { + private void startInternGNSS() { if (EasyPermissions.hasPermissions(this, perms)) { // Already have permission, do the thing Toast.makeText(cntxToastInternGNSSUpdate, "Permission granted!", Toast.LENGTH_SHORT).show(); - - // Get the output UI - // output = (TextView) findViewById(R.id.interngnssLongTextView); - - // Test print - ///printlnOnView(output, "Running!"); - locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); - //List providers = locationManager.getAllProviders(); - - //for (String temp : providers) { - // System.out.println(temp); - //printProvider(temp); - //} - } else { // Do not have permissions, request them now - EasyPermissions.requestPermissions(this, "We require location access!!!", + EasyPermissions.requestPermissions(this, "We require location access!", 123, perms); } } -/* - private void printProvider(String provider) { - LocationProvider info = locationManager.getProvider(provider); - output.append("Name: " + info.getName() + "\n"); - output.append("Accuracy: " + info.getAccuracy() + "\n"); - output.append("PowerReq.: " + info.getPowerRequirement() + "\n"); - output.append("CellIDReq.: " + info.requiresCell() + "\n"); - output.append("NetworkReq.: " + info.requiresNetwork() + "\n"); - output.append("SatReq.: " + info.requiresSatellite() + "\n\n"); - } - */ private void setEditTextLabel(final EditText v, final String str) { Runnable myRun = new Runnable() { @@ -110,39 +88,6 @@ public class MainActivity extends AppCompatActivity { }; runOnUiThread(myRun); } - /* - private void printOnView(final TextView v, final String str) { - Runnable myRun = new Runnable() { - public void run() { - v.append(str); - } - }; - runOnUiThread(myRun); - } - - public void addToString(Location loc) { - //Verwendet die Variablen centerLat & centerLng - String time = loc.getTime() +""; - String latStr = Double.toString(loc.getLatitude()); - String lngStr = Double.toString(loc.getLongitude()); - String acc = loc.getAccuracy() + ""; - String numOfSat = loc.getExtras().getInt("satellites") +""; - String satStr = ""; - String distStr = ""; - // Bestimme die Distanz (Fix <-> Center) - Location locationA = new Location("Center"); - locationA.setLatitude(Double.valueOf(centerLat)); - locationA.setLongitude(Double.valueOf(centerLng)); - Location locationB = new Location("Fix"); - locationB.setLatitude(Double.valueOf(latStr)); - locationB.setLongitude(Double.valueOf(lngStr)); - distStr = locationA.distanceTo(locationB) + ""; - logString = logString + time + ";"; logString = logString + numOfSat + ";"; logString = logString + distStr + - ";"; logString = logString + acc + ";"; logString = logString + latStr + ";"; logString = logString + lngStr - + ";"; logString = logString + centerLat + ";"; logString = logString + centerLng + ";"; logString = - logString + satStr + ";"; logString = logString + "\n"; - } - */ @SuppressLint("MissingPermission") @Override @@ -185,6 +130,24 @@ public class MainActivity extends AppCompatActivity { setEditTextLabel((EditText) findViewById(R.id.interngnssAccuracyNumber), "no signal"); setEditTextLabel((EditText) findViewById(R.id.interngnssSatCountNumber),"no signal"); } + + if(serialUSB != null){ + if(serialUSB.isConnected()){ + nmeaParser.handleReceiveByteStream(); + if (nmeaParser.checkFix() == true) { + //System.out.println(parser.getLatitude() + " " + parser.getLongitude()); + //System.out.println(parser.calcDistance(fixedPosLat, fixedPosLng)); + + //nmeaParser.getLatitude(); + //nmeaParser.getLongitude(); + Log.i("ExternGNSS", "Fix! Lat: " + String.valueOf(nmeaParser.getLatitude()) + " Lon: " + String.valueOf(nmeaParser.getLongitude())); + + // distance.add(nmeaParser.calcDistance(fixedPosLat, fixedPosLng)); + // satCount.add(nmeaParser.getSatCount()); + } + } + } + runOnUiThread(new Runnable() { @Override public void run() { @@ -214,9 +177,6 @@ public class MainActivity extends AppCompatActivity { LocationListener locationListener = new LocationListener() { @Override public void onLocationChanged(android.location.Location location) { - //double latitude = location.getLatitude(); - //double longitude = location.getLongitude(); - //String msg = "New Latitude: " + latitude + " New Longitude: " + longitude; Toast.makeText(cntxToastInternGNSSUpdate, "Internal GNSS updated", Toast.LENGTH_LONG).show(); } diff --git a/app/src/main/java/hendrikschutter/com/externgnss/NMEAParser.java b/app/src/main/java/hendrikschutter/com/externgnss/NMEAParser.java new file mode 100644 index 0000000..a98311f --- /dev/null +++ b/app/src/main/java/hendrikschutter/com/externgnss/NMEAParser.java @@ -0,0 +1,121 @@ +package hendrikschutter.com.externgnss; + +import android.util.Log; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.BlockingQueue; + +public class NMEAParser { + private double latitude; + private double longitude; + private boolean fix; + private int satCount; + private BlockingQueue rawByteData = null; + + public NMEAParser() { + this.fix = false; + this.satCount = 0; + this.latitude = 0; + this.longitude = 0; + } + + public void setReceiveByteStream(BlockingQueue rawData) { + this.rawByteData = rawData; + } + + /** + * find strings in raw data and sent them to parse + */ + public void handleReceiveByteStream() { + String nmeaSentence; + ByteArrayOutputStream rawDataByteStream = new ByteArrayOutputStream(); + + //wait for sufficient data in buffer TODO don't do this block wise, losing data in edge + if (this.rawByteData.size() > 512) { + for (Iterator rawByte = this.rawByteData.iterator(); rawByte.hasNext(); ) { + rawDataByteStream.write(rawByte.next()); + rawByte.remove(); //remove this data from stream + } + ByteArrayInputStream stream = new ByteArrayInputStream(rawDataByteStream.toByteArray()); + InputStreamReader streamReader = new InputStreamReader(stream, StandardCharsets.US_ASCII); + BufferedReader bufferedReader = new BufferedReader(streamReader); + + try { + while ((nmeaSentence = bufferedReader.readLine()) != null) { + //Log.i("NMEAParser", nmeaSentence); + this.parseSentence(nmeaSentence); + } + } catch (IOException e) { + e.printStackTrace(); + } + } else { + //Log.i("NMEAParser", "wait for more data in buffer"); + } + } + + public void parseSentence(String sentence) { + if (sentence.startsWith("$GNGLL")) { + parseGNGLL(sentence); + } + if (sentence.startsWith("$GPGSV")) { + parseGPGSV(sentence); + } + } + + private void parseGNGLL(String sentence) { + if (sentence.length() >= 45) { + if (sentence.substring(44, 45).equals("A")) { + this.fix = true; + this.latitude = (Double.parseDouble(sentence.substring(9, 17)) / 60) + Double.parseDouble(sentence.substring(7, 9)); + this.longitude = (Double.parseDouble(sentence.substring(23, 30)) / 60) + Double.parseDouble(sentence.substring(20, 23)); + } else { + this.fix = false; + } + } + } + + private void parseGPGSV(String sentence) { + if (sentence.length() >= 13) { + this.satCount = (Integer.parseInt(sentence.substring(11, 13))); + } + } + + + public double calcDistance(double latitude, double longitude) { + final int R = 6371; // Radius of the earth + + double latDistance = Math.toRadians(latitude - this.latitude); + double lonDistance = Math.toRadians(longitude - this.longitude); + double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2) + + Math.cos(Math.toRadians(this.latitude)) * Math.cos(Math.toRadians(latitude)) + * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2); + double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + double distance = R * c * 1000; // convert to meters + distance = Math.pow(distance, 2); + return Math.sqrt(distance); + } + + + public double getLatitude() { + return this.latitude; + } + + public double getLongitude() { + return this.longitude; + } + + public boolean checkFix() { + return this.fix; + } + + public int getSatCount() { + return this.satCount; + } +} diff --git a/app/src/main/java/hendrikschutter/com/externgnss/SerialUSB.java b/app/src/main/java/hendrikschutter/com/externgnss/SerialUSB.java index eb69739..733dd7c 100644 --- a/app/src/main/java/hendrikschutter/com/externgnss/SerialUSB.java +++ b/app/src/main/java/hendrikschutter/com/externgnss/SerialUSB.java @@ -4,7 +4,6 @@ import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; @@ -16,69 +15,59 @@ import com.hoho.android.usbserial.driver.UsbSerialProber; import com.hoho.android.usbserial.util.SerialInputOutputManager; import java.io.IOException; +import java.util.concurrent.BlockingQueue; public class SerialUSB implements SerialInputOutputManager.Listener{ - private MainActivity mainActivity = null; - private enum UsbPermission { Unknown, Requested, Granted, Denied } - private static final String INTENT_ACTION_GRANT_USB = BuildConfig.APPLICATION_ID + ".GRANT_USB"; - private static final int WRITE_WAIT_MILLIS = 2000; - private static final int READ_WAIT_MILLIS = 2000; - private int deviceId, portNum, baudRate; - private boolean withIoManager; - private BroadcastReceiver broadcastReceiver = null; - //private final Handler mainLooper; - //private TextView receiveText; - //private ControlLines controlLines; - private SerialInputOutputManager usbIoManager; private UsbSerialPort usbSerialPort; private UsbPermission usbPermission = UsbPermission.Unknown; + private BlockingQueue dataBytesQueue = null; private boolean connected = false; - public SerialUSB(MainActivity mainActivity){ + public SerialUSB(MainActivity mainActivity, BlockingQueue dataBytesQueue){ this.mainActivity = mainActivity; + this.dataBytesQueue = dataBytesQueue; + } + public boolean isConnected(){ + return connected; + } + + public boolean findSerialDevice(){ // Find all available drivers from attached devices. UsbManager usbManager = (UsbManager) this.mainActivity.getSystemService(Context.USB_SERVICE); UsbSerialProber usbDefaultProber = UsbSerialProber.getDefaultProber(); + boolean deviceFound = false; - if (usbManager.getDeviceList().values().size() > 0){ - UsbDevice device = (UsbDevice) usbManager.getDeviceList().values().toArray()[0]; - UsbSerialDriver driver = usbDefaultProber.probeDevice(device); - if(driver == null) { - Log.i("SerialUSB", "driver is null"); - } - if(driver != null) { - Log.i("SerialUSB", "ExternGNSS: device/driver found!"); - this.deviceId = device.getDeviceId(); - this.portNum = 0; - this.baudRate = 9600; - this.withIoManager = true; - } - } - - - broadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if(INTENT_ACTION_GRANT_USB.equals(intent.getAction())) { - usbPermission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false) ? UsbPermission.Granted : UsbPermission.Denied; - connect(); - } else { - Log.i("SerialUSB", "USB permissions are not set"); - } + //check of at least one device is present + if (usbManager.getDeviceList().values().size() > 0){ + //get fist device + UsbDevice device = (UsbDevice) usbManager.getDeviceList().values().toArray()[0]; + UsbSerialDriver driver = usbDefaultProber.probeDevice(device); + if(driver == null) { + Log.e("SerialUSB", "driver is null"); } - }; - connect(); - this.mainActivity.registerReceiver(broadcastReceiver, new IntentFilter(INTENT_ACTION_GRANT_USB)); + if(driver != null) { + Log.i("SerialUSB", "ExternGNSS: device/driver found!"); + this.deviceId = device.getDeviceId(); + deviceFound = true; + } + } else + { + Log.e("SerialUSB", "no serial device connected"); + deviceFound = false; + } + return deviceFound; } - private void connect() { + public void connect(int baudRate, int portNum) { + this.baudRate = baudRate; + this.portNum = portNum; Log.i("connect", "starting to connect ..."); UsbDevice device = null; UsbManager usbManager = (UsbManager) this.mainActivity.getSystemService(Context.USB_SERVICE); @@ -90,11 +79,6 @@ public class SerialUSB implements SerialInputOutputManager.Listener{ return; } UsbSerialDriver driver = UsbSerialProber.getDefaultProber().probeDevice(device); - /* - if(driver == null) { - driver = CustomProber.getCustomProber().probeDevice(device); - } - */ if(driver == null) { Log.i("connect", "connection failed: no driver for device"); return; @@ -118,27 +102,21 @@ public class SerialUSB implements SerialInputOutputManager.Listener{ Log.i("connect", "connection failed: open failed"); return; } - try { usbSerialPort.open(usbConnection); usbSerialPort.setParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE); - if(withIoManager) { usbIoManager = new SerialInputOutputManager(usbSerialPort, this); usbIoManager.start(); - } - Log.i("connect", "connected"); connected = true; - //controlLines.start(); } catch (Exception e) { Log.i("connect", "connection failed: " + e.getMessage()); disconnect(); } } - private void disconnect() { + public void disconnect() { connected = false; - //controlLines.stop(); if(usbIoManager != null) { usbIoManager.setListener(null); usbIoManager.stop(); @@ -152,7 +130,11 @@ public class SerialUSB implements SerialInputOutputManager.Listener{ @Override public void onNewData(byte[] data) { - Log.i("connect", "received data: " + data.length); + //Log.i("connect", "received data: " + data.length); + //Log.i("connect", new String(data, StandardCharsets.US_ASCII) ); + for (byte rawByte : data) { + this.dataBytesQueue.add(rawByte); + } } @Override