Android Tutorial GPS Tutorial

klaus

Expert
Licensed User
Longtime User
Das GPS ist ein wichtiges Feature vieler Android-Geräte.
Glücklicherweise ist es ziemlich einfach, mit ihm zu arbeiten.
In diesem Tutorial werden wir ein einfaches Programm erstellen, das die aktuelle Position sowie den Satelliten-Status anzeigt.

Das komplette Programm befindet sich am Ende.
Das Englische Originaltutorial.

gps1.png


Die GPS Funktionen befinden sind in der GPS Library.
Wir müssen deshalb zuerst eine Referenz zu dieser Library erstellen:
gps2.png


Es gibt drei Arten von relevanten Objekten. Das wichtigste davon ist GPS.
Das GPS Objekt verwaltet die Verbindung und die Events (Ereignisse). Das zweite Objekt ist Location.
Eine Location ist eine Struktur, die die verfügbaren Daten über einen bestimmten "fix" enthält. Die Daten umfassen die Längen-und Breitengrad-Koordinaten, die Zeit (ausgedrückt als Ticks) für dieses "fix" und andere Informationen wie Peilung, Höhe und so weiter.
Es kann vorkommen, dass nicht alle Informationen verfügbar sind (aufgrund schlechten Empfangs zum Beispiel).

Das Location Objekt umfaßt auch andere Funktionen wie die Berechnung der Entfernung und Peilung zu einem anderen Ort und Methoden, um die Koordinaten String-Formate umzuwandeln.
Normalerweise arbeitet man mit Location-Objekten die von LocationChanged Event übergeben wurden. Sie können aber auch solche Objekte selbst initialisieren (dies ist nützlich für die Berechnung der Entfernung und Peilung zwischen Standorten).

Das letzte Objekt ist GPSSatellite. Dies ist eine Struktur, die verschiedene Informationen über die derzeit bekannten Satelliten enthält. Es wird im GPSStatus Event übergeben.

Zurück zum GPS.
Das GPS-Objekt muß als Process_Global Objekt deklariert werden. Ansonsten würden jedes Mal neuen Instanzen erstellt werden wenn die Activity neu erstellt wird.

Der erste Schritt ist das Objekt zu initialisieren. Wie viele andere Initialize-Methoden erwartet diese auch einen EventName Parameter. Dies ist das Präfix für die Events, die vom GPS-Objekt ausgelöst werden.

Hier ist der komplette Code:
B4X:
Sub Process_Globals
    Dim GPS1 As GPS
End Sub
 
Sub Globals
    Dim lblLon As Label
    Dim lblLat As Label
    Dim lblSpeed As Label
    Dim lblSatellites As Label
End Sub
 
Sub Activity_Create(FirstTime As Boolean)
    If FirstTime Then
        GPS1.Initialize("GPS")
    End If
    Activity.LoadLayout("1")
End Sub
 
Sub Activity_Resume
    If GPS1.GPSEnabled = False Then
        ToastMessageShow("Please enable the GPS device.", True)
        StartActivity(GPS1.LocationSettingsIntent) 'Will open the relevant settings screen.
    Else
        GPS1.Start(0, 0) 'Listen to GPS with no filters.
    End If
End Sub
 
Sub Activity_Pause (UserClosed As Boolean)
    GPS1.Stop
End Sub
 
Sub GPS_LocationChanged (Location1 As Location)
    lblLat.Text = "Lat = " & Location1.ConvertToMinutes(Location1.Latitude)
    lblLon.Text = "Lon = " & Location1.ConvertToMinutes(Location1.Longitude)
    lblSpeed.Text = "Speed = " & Location1.Speed
End Sub
 
Sub GPS_UserEnabled (Enabled As Boolean)
    ToastMessageShow("GPS device enabled = " & Enabled, True)
End Sub
 
Sub GPS_GpsStatus (Satellites As List)
    lblSatellites.Text = "Satellites:" & CRLF
    For i = 0 To Satellites.Size - 1
        Dim Satellite As GPSSatellite
        Satellite = Satellites.Get(i)
        lblSatellites.Text = lblSatellites.Text & CRLF & Satellite.Prn & _
            " " & Satellite.Snr & " " & Satellite.UsedInFix & " " & Satellite.Azimuth _ 
            & " " & Satellite.Elevation
    Next
End Sub
Der nächste Schritt ist dem GPS mitzuteilen daß es auf Daten 'hören' soll. Das GPS kann relativ viel Akkuenergie verbrauchen. Deshalb ist es empfehlenswert, das GPS abzustellen wenn es nicht notwendig ist. Es wird empfohlen, das 'Abhören' in der Activity_Resume Routine zu starten und in der Activity_Pause Routine abzustellen.

Es kann vorkommen, dass der Benutzer das GPS ausgeschaltet hat. Aus Datenschutzgründen erlaubt das Android OS nicht das GPS programmatisch einzuschalten. Das Beste, was Sie tun können, ist den Benutzer zu fragen das GPS-Gerät wieder zu aktivieren.

Der folgende Code zeigt eine Nachricht, wenn das GPS nicht aktiviert ist und öffnet auch das GPS-Bedienfeld, damit der Benutzer nur die GPS-Option zu überprüfen braucht:
B4X:
Sub Activity_Resume
    If GPS1.GPSEnabled = False Then
        ToastMessageShow("Please enable the GPS device.", True)
        StartActivity(GPS1.LocationSettingsIntent) 'Will open the relevant settings screen.
    Else
        GPS1.Start(0, 0) 'Listen to GPS with no filters.
    End If
End Sub
Wenn das GPS aktiviert ist wir fangen an, auf Daten zu 'hören'. Die Start-Methode enthält zwei Werte, die minimale Zeit (in Millisekunden) und der minimale Abstand (Meter) zwischen den Events. Ein Event wird ausgelöst, wenn mindestens eines dieser Kriterien erfüllt ist. Dies kann helfen, Batterieenergie zu sparen.
In unserem Fall geben wir 0 ein und erhalten dafür alle "fix" Events.

Das GPS löst drei Events aus:
- GPS_LocationChanged (Location1 As Location)
Dies ist das Hauptevent. Location1 enthält alle Daten für das neue "fix".

-GPS_GpsStatus (Satellites As List)
Dieses Event zeigt Informationen über die aktuell verfügbaren Satelliten an. Beachten Sie, dass nicht alle Satelliten in der Liste tatsächlich für die Berechnung des letzten Standpunktes (fix) verwendet wurden. So ist es möglich, daß die Liste mehrere Satelliten umfassen kann, der Empfang aber trotzdem immer noch nicht gut genug ist für eine Rechnung.

- GPS_UserEnabled (Enabled As Boolean)
Dieses Event wird jedes mal ausgelöst wenn der Benutzer den GPS-Status ändert. Es wird auch nach jedem Startaufruf ausgelöst.

Das komplette Programm:

zip.gif
GPS.zip
 
Last edited:

schimanski

Well-Known Member
Licensed User
Longtime User
Hallo Klaus!

Erstmal alle Achtung und herzlichen Dank für die zahlreichen Übersetzungen, die mir das Arbeiten wesentlich erleichtern.

Ich habe eine Frage zu dem Event GPS_GpsStatus:

Gibt es eine Möglichkeit, die Anzahl der Satelliten zu kontrollieren, um rechtzeitig einen GPS-Abriss zu erkennen? Mit

B4X:
Sub GPS_GpsStatus (Satellites As List)
  Main.AnzahlSatelliten=Satellites.Size
End Sub

funktioniert es nicht. Ich bekomme trotz GPS-Abriss (Tunnel etc.) über diese Sub immer noch eine hohe Anzahl Satelliten geliefert. Offensichtlich wird dieses Event bei einem GPS-Abriss gar nicht erst ausgelöst und somit die Anzahl der Satelitten nicht mehr aktualisiert.

Danke für Deine Mühe...
 

klaus

Expert
Licensed User
Longtime User
Ich befürchte dass es nicht möglich ist.
Denn die Satellitenliste kann
- mehr Satelliten enthalten als was für die letzte Rechnung benutzt wurden
- zum Anderen, kann es auch sein dass die Liste eine Anzahl von Satelliten enthält die aber doch nicht genügend sind für einen guten Empfang und einer Rechnung.

Habe leider selbst keine Erfahrung mit GPS in Android.

Beste Grüsse.
 

spunky

New Member
wie lassen sich die Koordinaten in Gauß/Krüger umwandeln um z.B. eine Fläche zu berechnen? oder kann man die Fläche auch mit Hilfe der normalen Koordinaten ermitteln? MfG.
 

Stefan080159

Member
Licensed User
Longtime User
Guten Abend,
ich möchte mit folgendem abgewandelten Code aus Eurem
Toturial den Radius eines Bogens berechnen.

B4X:
Sub GPS_LocationChanged (Location1 As Location)
 
    lblLat.Text = "Lat = " & Location1.ConvertToMinutes(Location1.Latitude)
    lblLon.Text = "Lon = " & Location1.ConvertToMinutes(Location1.Longitude)
    lblSpeed.Text = "Speed = " & Location1.Speed
    Zähler =  Zähler+1
    lblZähler.Text = Zähler
    If lblZähler.Text =1.0 Then
    x1 = Location1.ConvertToMinutes(Location1.Latitude)
    y1 = Location1.ConvertToMinutes(Location1.Longitude)
   
    End If
    If lblZähler.Text =2.0 Then
    x2 = Location1.ConvertToMinutes(Location1.Latitude)
    y2 = Location1.ConvertToMinutes(Location1.Longitude)
   
    End If
    If lblZähler.Text =3.0 Then
    x3 = Location1.ConvertToMinutes(Location1.Latitude)
    y3 = Location1.ConvertToMinutes(Location1.Longitude)   
   
    a = (y3-y1)*(x2-x1)-(y2-y1)*(x3-x1)
    b = x3*x3-x1*x1+y3*y3-y1*y1
    c = x2*x2-x1+y2*y2-y1*y1
    d = (b*(x2-x1)-c*(x3-x1))/2/a
    e = (c-2*d*(y2-y1))/2/(x2-x1)
    f = Sqrt(((x1-3)*(x1-e))+((y1-d)*(y1-d)))*10000
   
    lblErgebniss.Text = f
    Zähler= 0
    End If
 
End Sub

ich kann aber mit dem Format aus
Location1.ConvertToMinutes(Location1.Latitude) nicht rechnen.
Gibt es eine Möglichkeit, den Wert Dezimal auszugeben?

schönen Abend noch.
Stefan Schäfer
 

klaus

Expert
Licensed User
Longtime User
Ersetzte
B4X:
x1 = Location1.ConvertToMinutes(Location1.Latitude)
y1 = Location1.ConvertToMinutes(Location1.Longitude)
durch
B4X:
x1 = Location1.Latitude
y1 = Location1.Longitude
Die anderen Zeilen natürlich auch.
Du musst auch folgende Zeilen am Anfang der Routine einfügen.
B4X:
Sub GPS_LocationChanged (Location1 As Location)
    Dim x1, y1, x2, y2, x3, y3 As Double
    Dim a, b, c, d, e, f As Double

Die 'Originalwerte' sind Dezimalwerte !
In dem Tutorialcode wurden sie zur Anzeige umgewandelt.
 

Stefan080159

Member
Licensed User
Longtime User
wobei das Ergebnis irgendwie jenseits von gut und böse ist...
Da die Koordinaten ziemlich wild "springen" (schlechtes Signal
im Haus) ist natürlich keine brauchbare Berechnung zu erwarten.
Aber im freien bei kontinuierlicher Positionsveränderung sollte doch
aber ein akzeptabler Wert zu erwarten sein. Oder IST der Interval von
B4X:
Sub GPS_LocationChanged (Location1 As Location)
viel zu eng, um eine brauchbare Berechnung zu erreichen?
Gruß Stefan
 

klaus

Expert
Licensed User
Longtime User
Weche Werte hast Du in GPS1.Start eingegeben.
Wenn Du GPS1.Start(0, 0) hast wird Sub GPS_LocationChanged sobald sich die Stellung geändert hat.
Aus so nahen Stellungswerten den Radius des Bogens zu rechnen kommt mir unrealistisch vor, sehr wahrscheinlich mit grossen Schwankungen was Du ja auch bemerkt hast.
Was Du versuchen könntest ist eine minimale Distanz in GPS1.Start einfügen damit GPS_LocationChanged nur aufgerufen wird wenn der Unterschied zwischen zwei Stellungen grösser ist diese minimale Distanz ist. Habs zwar nie versucht.
 

Stefan080159

Member
Licensed User
Longtime User
Hallo Klaus,
habe es jetzt erst mal manuell über 'nen Button gemacht. Bin den ganzen Nachmittag im Garten
und im Gelände mit dem Tab rumgerannt...
Die Werte sind Ok. Button 3 mal gedrückt für x1/y1; x2/y2; x3/y3.
Dass Event sollte sich aber bestimmt auch über die Distanz auslösen lassen.

B4X:
Sub Button1_Click
Dim a, b, c, d, e, f, g
Dim x1, y1, x2, y2, x3, y3
 
If Button1.Text = "Start" Then
Button1.Text = "X1"
 
lblx1.Text = x
lbly1.Text = y
Else If Button1.Text = "X1" Then
Button1.Text = "X2"
 
lblx2.Text = x
lbly2.Text = y
Else If Button1.Text = "X2" Then
Button1.Text = "X3 & Ergebniss"
 
lblx3.Text = x
lbly3.Text = y
 
x1 = lblx1.Text
y1 = lbly1.Text
x2 = lblx2.Text
y2 = lbly2.Text
x3 = lblx3.Text
y3 = lbly3.Text
 
a =((x3*x3-x1*x1+y3*y3-y1*y1)*(x2-x1)-(x2*x2-x1*x1+y2*y2-y1*y1)*(x3-x1))
b=2*((y3-y1)*(x2-x1)-(y2-y1)*(x3-x1))
c=a/b
d=((x2*x2-x1*x1)+(y2*y2-y1*y1)-2*c*(y2-y1))
e=2*(x2-x1)
f=d/e
g=Sqrt(((x1-f)*(x1-f))+((y1-c)*(y1-c)))*100000
 
lblErgebniss.Text = g
Else If Button1.Text = "X3 & Ergebniss" Then
 
Button1.Text = "Start"
lblx1.Text = 0
lbly1.Text = 0
lblx2.Text = 0
lbly2.Text = 0
lblx3.Text = 0
lbly3.Text = 0
lblErgebniss.Text = 0
End If
End Sub

Gruß Stefan
 

Stefan080159

Member
Licensed User
Longtime User
Hallo Klaus,
habe es jetzt erst mal manuell über 'nen Button gemacht. Bin den ganzen Nachmittag im Garten
und im Gelände mit dem Tab rumgerannt...
Die Werte sind Ok. Button 3 mal gedrückt für x1/y1; x2/y2; x3/y3.
Dass Event sollte sich aber bestimmt auch über die Distanz auslösen lassen.

B4X:
Sub Button1_Click
Dim a, b, c, d, e, f, g
Dim x1, y1, x2, y2, x3, y3
 
If Button1.Text = "Start" Then
Button1.Text = "X1"
 
lblx1.Text = x
lbly1.Text = y
Else If Button1.Text = "X1" Then
Button1.Text = "X2"
 
lblx2.Text = x
lbly2.Text = y
Else If Button1.Text = "X2" Then
Button1.Text = "X3 & Ergebniss"
 
lblx3.Text = x
lbly3.Text = y
 
x1 = lblx1.Text
y1 = lbly1.Text
x2 = lblx2.Text
y2 = lbly2.Text
x3 = lblx3.Text
y3 = lbly3.Text
 
a =((x3*x3-x1*x1+y3*y3-y1*y1)*(x2-x1)-(x2*x2-x1*x1+y2*y2-y1*y1)*(x3-x1))
b=2*((y3-y1)*(x2-x1)-(y2-y1)*(x3-x1))
c=a/b
d=((x2*x2-x1*x1)+(y2*y2-y1*y1)-2*c*(y2-y1))
e=2*(x2-x1)
f=d/e
g=Sqrt(((x1-f)*(x1-f))+((y1-c)*(y1-c)))*100000
 
lblErgebniss.Text = g
Else If Button1.Text = "X3 & Ergebniss" Then
 
Button1.Text = "Start"
lblx1.Text = 0
lbly1.Text = 0
lblx2.Text = 0
lbly2.Text = 0
lblx3.Text = 0
lbly3.Text = 0
lblErgebniss.Text = 0
End If
End Sub

Gruß Stefan
 

Stefan080159

Member
Licensed User
Longtime User
ach so, noch eine Frage. Den Befehl "Format" aus VB6 schein B4a nicht zu kennen.
Wie kann ich das Ausgabeformat dahingehend beeinflussen?

Gruß Stefan
 

klaus

Expert
Licensed User
Longtime User
Es gibt zwei NumberFormat funktionen:
NumberFormat(Number As Double, MinimumIntegers As Int, MaximumFractions As Int)
und
NumberFormat2(Number As Double, MinimumIntegers As Int, MaximumFractions As Int, MinimumFractions As Int, GroupingUsed As Boolean)
 

xxxxDennisxxxx

New Member
Hallo,
Ich habe ein Problem und ich kriege es nicht Gelöst!

B4X:
For i = 0 To Satellites.Size - 1
  Dim Satellite As GPSSatellite
  Satellite = Satellites.Get(i)
  lblSatellites.Text = lblSatellites.Text & CRLF & Satellite.Prn & _
  " " & Satellite.Snr & " " & Satellite.UsedInFix

Jetzt will ich den Satellite.Snr für jeden satelliten einzeln Anzeigen.
Label1. = Satelit 1.Snr
Label2. = Satellit 2.Snr
Label3. = satellit 3.Snr.

Ich kriege es einfach nicht hin!

Helft mir mal Bitte.

Tschüss
 

klaus

Expert
Licensed User
Longtime User
Wo und wie sind Label1, Label2 usw definiert ?
Du könntest diese dynamisch im Code einfügen.
B4X:
Dim lblHeight = 30dip As Int
For i = 0 To Satellites.Size - 1
  Dim Satellite As GPSSatellite
  Dim lbl As Label
  lbl.Initialize("")
  Satellite = Satellites.Get(i)
  Activity.AddView(lbl, 0, i * lblHeight, 100&x, lblHeight)
  lbl.Text = Satellite.Prn & " " & Satellite.Snr & " " & Satellite.UsedInFix
Activity.AddView könnte auch Panel1.AddView sein je nach dem wo Du die Labels anfügen willst.
Nicht getestet.
 

xxxxDennisxxxx

New Member
B4X:
lblSatellites.Text = "Satellites:" & CRLF
  For i = 0 To Satellites.Size - 1
  Dim Satellite As GPSSatellite
  Satellite = Satellites.Get(i)
  lblSatellites.Text = Satellites.Snr

Also wenn man die App mit diesem Code startet bekommt man bei lblSatellites.Text logischerweise immer nur die Empfangsqualität eines einzigen Satelliten oder eben alle in einem einzigen String...
Die Labels sind einfach im Designer eingefügt.

Ich möchte aber sozusagen die Sateliten aus dieser Schleife extrahieren und alles in einzelne Strings laden.
Also statt alle Satelitten in einem String will ich die daten in enzelnen Strings anzeigen.

Ich hoffe es ist verständlich
 

TAK

Member
Licensed User
Longtime User
Hallo, habe eine eher allgemeine Frage z. B. zum GPS_LocationChanged (Location1 As Location).
Warum kann ich viele solche Events nicht in Librarys finden? Wo kann man sie nachschlagen? Ohne so ein Beispiel würde ich nämlich nicht mal wissen, dass es so eine Funktion gibt.

Gruß
 
Top