first working serial nmea parser

This commit is contained in:
Hendrik Schutter 2022-04-25 17:11:58 +02:00
parent 17b8ba745b
commit 031df4b3ca
4 changed files with 204 additions and 141 deletions

View File

@ -4,7 +4,7 @@ android {
compileSdkVersion 30
defaultConfig {
applicationId "hendrikschutter.com.externgnss"
minSdkVersion 17
minSdkVersion 19
targetSdkVersion 30
versionCode 1
versionName "1.0"

View File

@ -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<Byte> 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<String> 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();
}

View File

@ -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<Byte> rawByteData = null;
public NMEAParser() {
this.fix = false;
this.satCount = 0;
this.latitude = 0;
this.longitude = 0;
}
public void setReceiveByteStream(BlockingQueue<Byte> 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<Byte> 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;
}
}

View File

@ -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<Byte> dataBytesQueue = null;
private boolean connected = false;
public SerialUSB(MainActivity mainActivity){
public SerialUSB(MainActivity mainActivity, BlockingQueue<Byte> 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