Buchvorstellung
Buchcover

Die Natur ist unerbittlich und unveränderlich, und es ist ihr gleichgültig, ob die verborgenen Gründe und Arten ihres Handelns dem Menschen verständlich sind oder nicht.
Carl Friedrich Gauß

Plattformunabhängige Sensorkommunikation am Beispiel eines u-blox GNSS-Sensors mittels Java

Das Auslesen und Ansteuern von Sensoren und Instrumenten sind Aufgaben, die zwar nicht direkt zu den Kernkompetenzen eines Ingenieurgeodäten zählen aber mitunter auch von diesem erwartet werden. Neben der Entwicklung und Kombination von neuen Sensoren zählt die Automatisierung von (wiederkehrenden) Messprozessen wohl zu den klassischen Anwendungsbereichen. Insbesondere in der Überwachung von Objekten sind autonom messende Systeme und Datenlogger nicht mehr wegzudenken. Das Portfolio der Anbieter reicht hierbei von einfachen Wetterstationen oder GNSS-Sensoren und geht hin zur Neigungsmesstechnik oder Totalstationen. Die Kommunikation erfolgt in den meisten Fällen über die serielle Schnittstelle. Ausnahmen sind Messinstrumente, die eine hohe Messfrequenz besitzen oder eine asynchrone Kommunikation erfordern wie bspw. Lasertracker. Hier wird üblicherweise mit Sockets gearbeitet.

Aufgrund der weiten Verbreitung der seriellen Übertragung soll hier nur das Auslesen über einen seriellen COM-Port exemplarisch beschrieben werden. Da die meisten Computer heute nicht mehr über einen entsprechenden COM-Port verfügen, wird i.d.R. ein einfacher USB Schnittstellen-Wandler benötigt. Diese Wandler werden mitunter bereits direkt vom Hersteller in das Messsystem bzw. das Datenkabel integriert, sodass keine zusätzliche Peripherie erforderlich ist.


Sensoren und Bibliotheken

Als Beispielsensor soll hier eine sogenannte GNSS-Maus mit u-blox GNSS-Sensor dienen, die für wenige Euro im Handel erhältlich sind. Der GNSS-Sensor ist üblicherweise so vorkonfiguriert, dass in bestimmten Zeitintervallen die Positionsdaten über die Schnittstelle gesendet werden. Die Übertragung der Datenpakete erfolgt bei GNSS-Sensoren i.d.R. über den NMEA-Standard. Für diese Einführung soll es genügen, die Global Positioning System Fix Data (GGA) zu empfangen und für eine mögliche Weiterverarbeitung aufzubereiten. Sollte der u-blox GNSS-Sensor diese Daten nicht übermitteln, so lässt sich dies über das mitgelieferte Konfigurationstool u-center entsprechend einstellen.

Die Implementierung soll hier in der objektorientierten, plattformunabhängigen und freien Programmiersprache Java erfolgen, sodass das kompilierte Programm auf allen unterstützten Plattformen ohne erneute Kompilierung direkt lauffähig ist. Das Java-Konzept erlaubt keinen direkten Zugriff auf den COM-Port oder andere rechnerspezifische Ressourcen, sodass hierfür eine zusätzliche RxTx-Bibliothek notwendig wird. Die Realisierung der RxTx-Bibliothek für die plattformspezifischen COM-Portschnittstelle erfolgt über die JNI (Java Native Interface). JNI ermöglicht das Aufrufen von plattformspezifischen Funktionen über normierte Methoden aus einer Java-Anwendung heraus. Für die Kommunikation über den COM-Port existierte einst eine von SUN entwickelte javacomm-Bibliothek, welche später unter dem Namen RXTXcomm also OpenSource-Projekt weiterentwickelt wurde. Ein Fork dieser Bibliothek wird von Kevin Harrington unter dem Namen nrjavaserial bereitgestellt, der die notwenigen native Bibliotheken für Windows, Linux und OS X direkt im JAR-Paket bereitstellt und zur Laufzeit ins System integriert und einbindet. Da diese Bibliothek nur aus einer einzelnen JAR besteht, ist ihre Integration in eine Java-Applikation denkbar einfach, sodass wie sie hier verwenden wollen.


Kommunikation via COM-Port

Die Kommunikation über den COM-Port läuft im Prinzip für alle Sensoren nach demselben Schema. Wir benötigen den Portnamen (z.B. COM3) und die Verbindungseinstellungen (Baudrate, Anzahl der Datenbits, Anzahl der Stopbits und die Parität). Die Verbindungseinstellungen müssen mit denen des Sensors übereinstimmen. Für den u-blox GNSS-Sensor können diese über u-center angezeigt werden, bei anderen Sensoren hilft ein Blick ins Handbuch. Der Port lässt sich bspw. über den Geräte-Manager unter Windows ermitteln.

Die statische Methode getPortIdentifier(‹Name›) der Klasse CommPortIdentifier erzeugt anhand des Port-Namens ein CommPortIdentifier-Objekt, welches eine Methode open(‹Applikationsname›, ‹Timeout›) bereitstellt, die ein Objekt vom Typ CommPort zurückliefert. Zur Kommunikation werden vom CommPort (bzw. vom SerialPort) ein InputStream und ein OutputStream bereitgestellt. Da der GNSS-Sensor permanent Daten über die Schnittstelle sendet, wird nur der InputStream benötigt. Dieser kann über getInputStream() referenziert werden. Das Hinzufügen eines SerialPortEventListener ermöglicht es, über das Empfangen von Daten informiert zu werden, diese ggf. zu parsen und weiterzuverarbeiten, sofern notifyOnDataAvailable gesetzt ist. Im untenstehenden Beispielcode nutzen wir einen eigenen Listener vom Typ SerialReader, die die empfangen Daten auswertet.


String portName = "COM3";
int baudRate = 9600;
int dataBits = SerialPort.DATABITS_8;
int stopBits = SerialPort.STOPBITS_1;
int parity   = SerialPort.PARITY_NONE;

SerialPort serialPort = null;
InputStream inputStream = null;

CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(portName);
CommPort commPort = portId.open("u-blox-App", 2000);
if (commPort instanceof SerialPort) {
  serialPort = (SerialPort)commPort;
  serialPort.setSerialPortParams(baudRate, dataBits, stopBits, parity);
  inputStream = serialPort.getInputStream();
  serialPort.removeEventListener();
  serialPort.addEventListener(new SerialReader());
  serialPort.notifyOnDataAvailable(true);
}

Neben dem Öffnen eines Ports existiert natürlich auch ein entsprechendes Pendant zum Schließen. Dieses sollte am Ende aufgerufen werden, sodass andere Applikationen auf den Port zugreifen können. Neben dem Port sollten auch die verwendeten Streams geschlossen werden.


inputStream.close();
serialort.close();

Identifizierung, Validierung und Zerlegung der empfangenen GGA-Pakete

Das Global Positioning System Fix Data-Paket enthält u.a. Informationen zur Empfangszeit, Position und Genauigkeit. Jedes Paket beginnt mit einem $ und endet mit einem Wagenrücklauf ‹CR› mit anschließenden Zeilenvorschub ‹LF›.


$GPGGA,HHMMSS.ss,BBBB.BBBB,b,LLLLL.LLLL,l,Q,NN,D.D,H.H,h,G.G,g,A.A,RRRR*PP‹CR›‹LF›

SymbolBedeutung
HHMMSS.ss UTC-Zeit
BBBB.BBBB geographische Breite in Grad und Minuten (ddmm.mmmmmm)
b Ausrichtung des Breitengrades (N=Nord oder S=Süd)
LLLLL.LLLL geographische Länge in Grad und Minuten (dddmm.mmmmmm)
l Ausrichtung des Längengrades (E=East oder W=West)
Q GNSS-Qualität:
  • 0 für ungültig
  • 1 für GPS fix
  • 2 für DGPS fix
NN Satellitenanzahl (00 − 12)
D.D Horizontal DOP (Dilution of Precision)
H.H Orthometrische Höhe
h Einheit der Antennenhöhe (Meter)
G.G Geoid-Undulation
g Einheit der Undulation (Meter)
A.A Alter des DGPS-Datensatzes
RRRR DGPS-Referenzstation
PP Prüfsumme

Da neben den hier relevanten GGA-Paketen ggf. weitere Pakete übermittelt werden, muss zunächst eine Identifizierung der gewünschten Daten erfolgen. Hierfür gibt es verschiedene Möglichkeiten. Eine Möglichkeit ist die Verwendung eines regulären Ausdrucks. Reguläre Ausdrücke erlauben das Auffinden von Zeichenketten anhand eines bestimmten Musters. Für das GGA-Paket ergibt sich eine mögliche Musterdefinition aus dem Start- und Endzeichen sowie der GGA-Zeichenfolge. Ferner enthält das Paket keine Whitespaces. Ein einfacher Ausdruck wäre demnach:


Pattern GGA_PATTERN = Pattern.compile("(?s).*(\\$G[N|P]GGA\\S+\\s).*$");

Ein beliebiges Zeichen wird durch einen einfachen Punkt . repräsentiert. Sollen auch Zeilenumbrüche berücksichtigt werden, so lassen sich diese durch (?s) aktivieren. Ein Sternchen bedeutet, dass der voranstehende Ausdruck beliebig oft, insbesondere auch keinmal, vorkommen muss. Der erste Teil des Ausdrucks (?s).* erweitert somit den Wertebereich eines beliebigen Zeichens um den Zeilenumbruch und passt auf alle Zeichenketten, die aus keinem oder unendlich vielen Zeichen bestehen. Durch den nachfolgenden Ausdruck (\\$G[N|P]GGA\\S+\\s) wird das Matching eingegrenzt. Die zuvor beliebige Zeichenkette muss nun wahlweise von einem $GPGGA oder $GNGGA gefolgt werden. Da das $-Zeichen ein spezielles Zeichen innerhalb eines regulären Ausdrucks ist, muss es durch einen Rückstrich maskiert werden. Da auch der Rückstrich in Java eine besondere Bedeutung hat, muss dieser ebenfalls maskiert werden. Hierdurch ergibt sich die o.g. Doppelmaskierung, die u.U. in anderen Programmiersprachen entfallen kann. Die Daten innerhalb eines GGA-Pakets bestehen nur aus Zeichen, die keine Whitespaces wie bspw. Leerzeichen oder Tabulatoren enthalten, sodass der Inhalt des GGA-Pakets mit \S+ erfasst werden kann. Das Pluszeichen bedeutet, dass beliebig viele (aber mindestens ein) Nicht-Whitespace-Zeichen folgen müssen. Das Endzeichen eines Pakets ist eine Zeichensequenz bestehend aus Wagenrücklauf ‹CR› und Zeilenvorschub ‹LF›. Sowohl Wagenrücklauf als auch Zeilenvorschub sind Whitespaces und lassen sich mit \s identifizieren und beschreiben das Ende des Pakets. Da unter Umständen weitere Pakete bereits übermittelt wurden, insbesondere auch GGA-Pakete, schließen wir den Ausdruck mit .*$ ab. Hierdurch erhalten wir nur das letzte übermittelte GGA-Paket.


String msg="$GPGGA,153933.00,5011.28964,N,00844.51181,E,1,08,1.08,103.7,M,47.5,M,,*50";
Matcher matcher = GGA_PATTERN.matcher(msg);
if (matcher.find()) {
  lastPosition = exploidGGAResponse(matcher.group(1));
}

Die runden Klammern um den Ausdruck (\\$G[N|P]GGA\\S+\\s) definieren eine Gruppe, auf dessen Inhalt zugegriffen werden kann. Die Verwendung des regulären Ausdrucks erfüllt somit zwei Aufgaben. Zum einen wird der gewünschte GGA-Datensatz mit der Methode matcher.find() identifiziert und zum anderen wird dieser Datensatz durch die Gruppierung (matcher.group(1)) aus der Zeichenkette extrahiert, sodass dieser nach unseren Vorstellungen zerlegt werden kann. Die Zerlegung sei hier durch die Methode exploidGGAResponse(‹GGA-Paket›) angedeutet, die zur Weiterverarbeitung der Daten aufgerufen wird.

Die Identifizierung des gewünschten GGA-Pakets wäre damit möglich. Um zu prüfen, ob das Paket vollständig korrekt übertragen wurde, befindet sich am Ende des Pakets eine sogenannte Prüfsumme. Die übermittelte Summe muss sich aus der XOR-Verknüpfung aller Daten-Bytes zwischen dem $-Zeichen und dem Stern ergeben.


boolean checkMessage(String msg) {
  int dollarToken = msg.indexOf("$");
  int starToken = msg.indexOf('*');

  if (dollarToken < 0 || starToken < 0 || starToken + 1 == msg.length())
    return false;
  
  int checksum = 0;
  for (int i = dollarToken + 1; i < starToken; i++)
    checksum ^= msg.charAt(i);

  try {
    int refSum = Integer.parseInt(msg.substring(starToken + 1).trim(), 16);
    return checksum == refSum;
  } catch (Exception e) {
    e.printStackTrace();
  }
  return false;
}

Eine Prüfung kann vor dem Aufruf von exploidGGAResponse(‹GGA-Paket›) oder direkt am Anfang dieser Methode erfolgen.


Aufbau der inneren Klasse SerialReader

Nachdem exemplarisch gezeigt wurde, wie eine Verbindung über den COM-Port aufgebaut wird und mit welchem Werkzeug sich die empfangenen Daten verarbeitet lassen, fehlt nur noch die Klasse SerialReader, die uns über empfangene Daten informieren soll. Die Klasse SerialReader leiten wir vom Interface SerialPortEventListener ab, sodass wir die Methode serialEvent(‹SerialPortEvent›) überschreiben müssen. Da uns nur interessiert, ob Daten empfangen wurden, reicht es, ausschließlich SerialPortEvents zu berücksichtigen, die vom Event-Typ DATA_AVAILABLE sind. Das Auslesen der Daten und Speichern in einem String erfolgt über den InputStream.


byte[] readBuffer = new byte[0xFFFF];
int readBytes;
String msg = new String();
while (inputStream.available() › 0) {
  readBytes = inputStream.read(readBuffer);
  msg = msg + new String(readBuffer, 0, readBytes);
}

Die im String msg abgespeicherten Daten können nun bzgl. des Vorhandenseins von mindestens einem GGA-Datenblock mittels regulären Ausdrucks untersucht werden.


Vollständiges Beispiel

Für den zerlegten GGA-Datenblock soll hier ein eigenes Objekt verwendet werden, welches in der Methode exploidGGAResponse(‹GGA-Paket›) erzeugt wird. Das GGA-Objekt ist demnach nur ein Container, welcher einen Teil der GGA-Daten enthält. Über entsprechende Getter-Methoden kann auf den Inhalt des Containerobjekts zurückgegriffen werden.


package com.derletztekick.geodesy.instrument.gnss;

import java.util.Date;

public class GNSSPosition {
  public enum GeographicDirectionLatitude {
    NORTH, SOUTH;

    public static GeographicDirectionLatitude getGeographicDirectionLatitudeByChar(
        char c) {
      return c == 'N' || c == 'n' ? NORTH : SOUTH;
    }

    public static GeographicDirectionLatitude getGeographicDirectionLatitudeByChar(
        String s) {
      return getGeographicDirectionLatitudeByChar(s.charAt(0));
    }
  }

  public enum GeographicDirectionLongitude {
    EAST, WEST;

    public static GeographicDirectionLongitude getGeographicDirectionLongitudeByChar(
        char c) {
      return c == 'E' || c == 'e' ? EAST : WEST;
    }

    public static GeographicDirectionLongitude getGeographicDirectionLongitudeByChar(
        String s) {
      return getGeographicDirectionLongitudeByChar(s.charAt(0));
    }
  }

  private double longitude, latitude, altitude, geoidalSeparation, hdop;
  private int numberOfSatellites;
  private GeographicDirectionLatitude oriLat;
  private GeographicDirectionLongitude oriLon;
  private Date receivedDate, systemDate;

  public GNSSPosition(GeographicDirectionLatitude oriLat, double latitude,
      GNSSPosition.GeographicDirectionLongitude oriLon, double longitude, double altitude,
      double geoidalSeparation, double hdop, int numberOfSatellites, Date receivedDate, 
      Date systemDate) {
    this.longitude = longitude;
    this.latitude = latitude;
    this.altitude = altitude;
    this.geoidalSeparation = geoidalSeparation;
    this.hdop = hdop;
    this.numberOfSatellites = numberOfSatellites;
    this.oriLat = oriLat;
    this.oriLon = oriLon;
    this.receivedDate = receivedDate;
    this.systemDate = systemDate;
  }

  public double getLongitude() {
    return longitude;
  }

  public double getLatitude() {
    return latitude;
  }

  public double getAltitude() {
    return altitude;
  }

  public double getGeoidalSeparation() {
    return geoidalSeparation;
  }

  public double getHeight() {
    return geoidalSeparation + altitude;
  }

  public double getHorizontalDilutionOfPrecision() {
    return hdop;
  }

  public int numberOfSatellites() {
    return this.numberOfSatellites;
  }

  public Date getReceivedDate() {
    return receivedDate;
  }

  public Date getSystemDate() {
    return systemDate;
  }

  public GeographicDirectionLatitude getGeographicDirectionLatitude() {
    return oriLat;
  }
  
  public GeographicDirectionLongitude getGeographicDirectionLongitude() {
    return oriLon;
  }
}


Die Klasse für unseren GNSS-Sensor inklusive main-Methode könnte wie folgt aussehen:


package com.derletztekick.geodesy.instrument.gnss.ublox;

import java.io.IOException;
import java.io.InputStream;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import com.derletztekick.geodesy.instrument.gnss.GNSSPosition;

public class TEPUB353 {
 private final static String GGA_REG_EXP = "(?s).*(\\$G[N|P]GGA\\S+\\s).*$";
 private final static Pattern GGA_PATTERN = Pattern.compile(GGA_REG_EXP);

 private class SerialReader implements SerialPortEventListener {
  private String responseMessageGGA = "";
  private Calendar systemDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));

  @Override
  public synchronized void serialEvent(SerialPortEvent event) {
   if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
    byte[] readBuffer = new byte[0xFFFF];

    try {
     int readBytes;
     String resStr = new String();
     while (inputStream.available() > 0) {
      readBytes = inputStream.read(readBuffer);
      resStr = new String(readBuffer, 0, readBytes);
      this.responseMessageGGA += resStr;
     }

     this.systemDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));

     Matcher matcher = GGA_PATTERN.matcher(this.responseMessageGGA);
     if (matcher != null && matcher.find() && matcher.groupCount() > 0) {
      lastPosition = exploidGGAResponse(matcher.group(1));
      this.responseMessageGGA = "";
     }
    } catch (IOException e) {
     this.responseMessageGGA = "";
     System.err.println("Fehler beim Datenempfang!");
     e.printStackTrace();
    }
   }
  }

  private boolean checkMessage(String msg) {
   int dollarToken = msg.indexOf("$");
   int starToken = msg.indexOf('*');

   if (dollarToken < 0 || starToken < 0 || starToken + 1 == msg.length())
    return false;

   int checksum = 0;
   for (int i = dollarToken + 1; i < starToken; i++)
    checksum ^= msg.charAt(i);

   try {
    int refSum = Integer.parseInt(msg.substring(starToken + 1).trim(), 16);
    return checksum == refSum;
   } catch (Exception e) {
    e.printStackTrace();
   }
   return false;
  }

  private GNSSPosition exploidGGAResponse(String ggaMsg) {
   GNSSPosition gnssPosition = null;

   if (this.checkMessage(ggaMsg)) {
    Calendar receivedDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));

    String data[] = ggaMsg.split(",");

    if (data.length >= 15) {
     int hh = Integer.parseInt(data[1].substring(0, 2), 10);
     int mm = Integer.parseInt(data[1].substring(2, 4), 10);
     int ss = Integer.parseInt(data[1].substring(4, 6), 10);
     int SS = Integer.parseInt(data[1].substring(7, data[1].length()), 10);

     receivedDate.set(Calendar.HOUR_OF_DAY, hh);
     receivedDate.set(Calendar.MINUTE, mm);
     receivedDate.set(Calendar.SECOND, ss);
     receivedDate.set(Calendar.MILLISECOND, SS);

     double latitude = Integer.parseInt(data[2].substring(0, 2), 10)
       + Double.parseDouble(data[2].substring(2, data[2].length())) / 60.0;
     char ns = data[3].charAt(0);
     double longitude = Integer.parseInt(data[4].substring(0, 3), 10)
       + Double.parseDouble(data[4].substring(3, data[4].length())) / 60.0;
     char ew = data[5].charAt(0);

     int numberOfSatellites = Integer.parseInt(data[7], 10);
     double hdop = Double.parseDouble(data[8]);

     double altitude = Double.parseDouble(data[9]);
     double geoidalSeparation = Double.parseDouble(data[11]);

     gnssPosition = new GNSSPosition(
       GNSSPosition.GeographicDirectionLatitude.getGeographicDirectionLatitudeByChar(ns), 
       latitude,
       GNSSPosition.GeographicDirectionLongitude.getGeographicDirectionLongitudeByChar(ew),
       longitude, 
       altitude, 
       geoidalSeparation, 
       hdop, 
       numberOfSatellites,
       new Date(receivedDate.getTimeInMillis()), 
       new Date(systemDate.getTimeInMillis()));
    }
   }
   return gnssPosition;
  }
 }

 private GNSSPosition lastPosition;
 private int baudRate = 9600;
 private int dataBits = SerialPort.DATABITS_8;
 private int stopBits = SerialPort.STOPBITS_1;
 private int parity = SerialPort.PARITY_NONE;

 private CommPortIdentifier portId;
 private SerialPort serialPort;
 private InputStream inputStream;

 public void setCommPortIdentifier(CommPortIdentifier portId) {
  this.portId = portId;
 }

 public boolean connect() {
  try {
   if (this.portId == null || this.baudRate % 1200 != 0
     || this.dataBits < 5 || this.dataBits > 8
     || this.stopBits < 1 || this.stopBits > 2
	 || this.parity < 0 || this.parity > 2) {
    System.err.println("Fehler, invalide Verbindungseinstellungen!");
    return false;
   }

   if (this.portId.isCurrentlyOwned()) {
    System.err.println("Fehler, PORT " + this.portId.getName() + " wird "
      + "bereits verwendet von " + portId.getCurrentOwner() + "!");
    return false;
   }

   CommPort commPort = this.portId.open(this.getClass().getSimpleName(), 2000);
   if (commPort instanceof SerialPort) {
    this.serialPort = (SerialPort) commPort;

    this.serialPort.setSerialPortParams(
	  this.baudRate, 
	  this.dataBits, 
	  this.stopBits, 
	  this.parity
	);

    this.inputStream = this.serialPort.getInputStream();

    this.serialPort.removeEventListener();
    this.serialPort.addEventListener(new SerialReader());
    this.serialPort.notifyOnDataAvailable(true);
   } else {
    System.err.println("Fehler, PORT " + this.portId.getName() + " ist kein SerialPort!");
    return false;
   }
  } catch (Exception e) {
   e.printStackTrace();
   return false;
  }
  return true;
 }

 public boolean disconnect() {
  try {
   if (inputStream != null)
    inputStream.close();
  } catch (IOException e) {
   e.printStackTrace();
  }
  try {
   if (serialPort != null) {
    synchronized (this) {
     serialPort.removeEventListener();
     serialPort.addEventListener(null);
     serialPort.close();
    }
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
  return true;
 }

 public GNSSPosition getLastPosition() {
  return lastPosition;
 }

 public static void main(String args[]) throws Exception {
  TEPUB353 usbLogger = new TEPUB353();
  try {
   usbLogger.setCommPortIdentifier(
    CommPortIdentifier.getPortIdentifier(args[0])
   );

   usbLogger.connect();
   String format = "%5d %9.5f  %9.5f  %9.4f%n";

   int i = 0;
   while (i++ < 25) {
    Thread.sleep(500);
    GNSSPosition pos = usbLogger.getLastPosition();

    if (pos != null)
     System.out.print(String.format(
       Locale.ENGLISH, 
       format, i,
       pos.getLatitude(),
       pos.getLongitude(), 
       pos.getHeight()));
   }
  } finally {
   usbLogger.disconnect();
  }
 }
}


Programm ausführen

Konsolenausgabe der empfangen u-blox GNSS-Daten vom seriellen COM-Port
Konsolenausgabe der empfangen u-blox GNSS-Daten vom seriellen COM-Port

Das erzeugte und compilierte Java-Programm kann nun direkt auf der Konsole ausgeführt werden. Ausgehend davon, dass die nrjavaserial-Bibliothek im lib-Verzeichnis liegt, sieht der Aufruf unter Windows wie folgt aus

java -cp lib/*;*; com.derletztekick.geodesy.instrument.gnss.ublox.TEPUB353 COM11

Über die Flag -cp wird der sogenannte Klassenpfad (CLASSPATH) gesetzt. Im gezeigten Code wird zum einen das aktuelle Verzeichnis und zum anderen das lib-Verzeichnis zum CLASSPATH hinzugefügt. Der COM-Port, hier COM11, wurde als Argument der erstellten Applikation übergeben.

Der u-blox Sensor liefert die Daten mit einer Frequenz von 1 Hz. Die Ausgabe erfolgt hier beispielhaft auf der Konsole unter Berücksichtigung des Nyquist-Abtasttheorems mit 2 Hz.


Quellen

Verwendete Literatur, die nicht der Bibliothek entnommen ist:

16.04.2017 von Michael Lösler