Pobranie współrzędnych GPS na tablecie z Windows 10

0

Koledzy, czy istnieje jakiś komponent lub inny sposób w Lazarusie, dzięki któremu pobiorę współrzędne z tabletu z wbudowanym odbiornikiem gps i wyświetlę je w aplikacji?
Tablet z Windows 10, 32 bit.

0

wyszukaj w managerze urządzeń nazwę odbiornika i poszukaj na necie opisu. Możliwe, że producent udostępnia SDK lub jakiś DLL, który uda Ci się zaimplementować jako oleobject w lazarusie

0

A jaki to jest tablet?

0

Ten konkretny to Kruger&Matz EDGE 802, aczkolwiek szukam oczywiście jakiejś uniwersalnej metody na każdy tablet z Windowsem i wbudowanym GPSem.

0

@axel234: nie wiem jak w Twoim wypadku ale ja jakiś czas temu (również tu na forum) bawiłem się z tabletem i obsługą diody led. Okazało się bowiem, że Microsoft przygotował funkcję w WinAPI do tego. Jeśli Twój tablet ma zainstalowaną jakąś aplikację do obsługi GPS to spróbuj podejrzeć processexplorerem, co faktycznie się dzieje przy odczycie współrzędnych. Możliwe, że również jest jakaś funkcja, którą wystarczy wywołać ;)

0

ILocation: https://msdn.microsoft.com/en-us/library/windows/desktop/dd464636%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396

Jest też inne (nowsze) API (Geolocation) do tego, ale tylko w aplikacjach UWP - zasadniczo niedostępne dla aplikacji Win32, chyba, że tworzysz aplikację za pomocą "Project Centennial". Główny problem polega na tym, że po prostu te moduły GPS wbudowane w tablety często prezentują dane tylko przez ILocation/Geolocation API, nie ujawniajac np. surowych danych NMEA, które by były zrozumiałe dla aplikacji zewnętrznych.

Alternatywnie: GPSReverse (https://www.turboirc.com/gps7/) jest w stanie wystawić dane z ILocation jako port szeregowy.

0

Odbiornik pewnie standardowo udostępnia dane ciągiem NMEA wysyłanym po wirtualnym porcie szeregowym :-)

0

Dzięki za podpowiedzi. Będę kombinował.

0

Wiem, że chodzi o Lazarus ale gdyby nic z tego nie wyszło może lepiej byłoby to zrobić w Delphi tam jest komponent TLocaionSensor (mam nadzieję że nie "wycięty" w darmowym Starter).

0

Tutaj ktoś w komentarzach wkleił przykład użycia ILocation w C++.
Wystarczy przerobić na Delphi.

0
Azarien napisał(a):

Tutaj ktoś w komentarzach wkleił przykład użycia ILocation w C++.
Wystarczy przerobić na Delphi.

A w przypadku interfejsów oznacza to alb samodzielną ich implementację albo import LocationApi 1.0 Type Library (przynajmniej 1.0 jest w Windows 7 a w 10 może być nowsza wersja choć nie musi). Ponieważ w Lazarus chyba nie ma (przynajmniej kiedyś nie było nie wiem jak obecnie) możliwości importu podaję wygenerowany przez Delphi kod może się przyda (choć nie ma pewności, że nie będzie wymagał przeróbek pod Lazarus)
https://4programmers.net/Pastebin/6448

0

@kAzek: raczej wiele zmieniać nie trzeba będzie. Listę uses na pewno, a co do reszty to sprawdzę później.

0

Sprawdziłem ten moduł od @kAzek i oprócz tego, że trzeba poprawić nazwy modułów na liście uses to trzeba też dodać rzutowanie z AnsiString na WideString, bo kompilator wyświetla ostrzeżenia typu:

Warning: Implicit string type conversion from "AnsiString" to "WideString"

A tak to reszta w porządku.

0

Dzięki koledzy za dyskusję i podpowiedzi.

Udało mi się uruchomić moduł od @kAzek zarówno w Delphi jak i - po poprawkach - w Lazarusie. Jeszcze nie ogarnąłem rzutowania, ale doczytam jak to zrobić.

Niestety w między czasie tablet z GPSem już odjechał, bo miałem go tylko na kilka dni. W związku z tym mam pytanie, czy można jakoś zasymulować GPS, tak żeby ten moduł uznał, że odbiornik jest wbudowany w komputerze?

0

Jeszcze nie ogarnąłem rzutowania, ale doczytam jak to zrobić.

Po prostu rzutuj parametr na WideString, np.:

class function CoDispLatLongReport.CreateRemote(const MachineName: string): IDispLatLongReport;
begin
  Result := CreateRemoteComObject(WideString(MachineName), CLASS_DispLatLongReport) as IDispLatLongReport;
end;
0

Niestety w między czasie tablet z GPSem już odjechał, bo miałem go tylko na kilka dni. W związku z tym mam pytanie, czy można jakoś zasymulować GPS, tak żeby ten moduł uznał, że odbiornik jest wbudowany w komputerze?

Znowu to, do czego dawałem link jako anonim - GPSDirect. https://www.turboirc.com/gps7/ Ma możliwość symulacji urządzenia GPS. W wersji trial to jest chyba nawet za darmo na zawsze o ile dobrze pamiętam.

0

Za dobrze by było gdyby wszystko poszło jak po maśle.

Poszperałem trochę w sieci w poszukiwaniu info o LocationApiLip i znalazłem kod wyświetlający współrzędne w Editach.
Kod w Lazarusie się kompiluje, ale po wciśnięciu Buttona otrzymuję komunikat No devices detected!.

Próbowałem też z zainstalowaną i uruchomioną symulacją GPSa poleconą przez @Ktos

Gdzie dałem ciała, lub co pominąłem?

function IsLocationAPIAvailable: Boolean;
var
  loc: ILocation;
begin
  Result := False;

  try
    loc := CreateComObject(CLASS_Location) as ILocation;
    Result := True;
  except
  end;

  loc := nil;
end;  

function LocReportStatus(status: LOCATION_REPORT_STATUS): String;
begin
  Result := '';
  case status of
    REPORT_RUNNING:
      Result := 'Report received!';
    REPORT_NOT_SUPPORTED:
      Result := 'No devices detected!';
    REPORT_ERROR:
      Result := 'Report error!';
    REPORT_ACCESS_DENIED:
      Result := 'Access denied!';
    REPORT_INITIALIZING:
      Result := 'Report is initializing!';
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
type
  PILocationReport = ^ILocationReport;
var
  loc: ILocation;
  rep: ILocationReport;
  LatLongRep: ILatLongReport;
  status: LOCATION_REPORT_STATUS;
  hres: HRESULT;
  fLat, fLon: double;
begin
  if IsLocationAPIAvailable then
  begin
    loc := CreateComObject(CLASS_Location) as ILocation;
    try
      status := REPORT_NOT_SUPPORTED;
      //have permission?
                                                                            // 0 for asynchronous calls
      if loc.RequestPermissions(_RemotableHandle(nil^), IID_ILatLongReport, 1, Integer(True))  = S_OK then
      begin
        hres := loc.GetReportStatus(IID_ILatLongReport, status);
        if hres = S_OK then
        begin
          if status = REPORT_RUNNING then //report is ok?
          begin
            hres := loc.GetReport(IID_ILatLongReport, rep);
            if (hres = S_OK) and (rep<>nil) then
            begin
              hres := rep.QueryInterface(IID_ILatLongReport, LatLongRep);
              if (hres = S_OK) and (LatLongRep<>nil) then
              begin
                fLat := 0;
                fLon := 0;
                LatLongRep.GetLatitude(fLat);
                LatLongRep.GetLongitude(fLon);
                Edit1.Text := FloatToStr(fLat);
                Edit2.Text := FloatToStr(fLon);
              end;
            end;
          end
          else
            ShowMessage(LocReportStatus(status));
        end
        else
          ShowMessage(IntToStr(Integer(hres)));
      end;
    finally
      LatLongRep := nil;
      rep := nil;
      loc := nil;
    end;
  end;
end;                            
0

O ile wiem to Lazarus ma debugger więc zawsze można wykonać program krok po kroku i sprawdzić co poszło nie tak.

  1. Podstawowe pytanie czy w ogóle pokazał się monit o włączenie urządzenia?
  2. W jakim trybie pracujesz tzn. czy łączysz się przez Wi-Fi ze smartfonem lub iphone posiadającym GPS czy Simulation (bez fizycznego urządzenia ale darmowa licencja)?

Jeżeli masz fizyczne urządzenie to to musi ono błyskawicznie "łapać" sygnał GPS bo w kodzie nie ma żadnego oczekiwania (na stronie z której brałeś ten kod poprawka jest niżej).

0

Ad 1. Po kliknięciu buttona pojawiło się jedynie info, że ta opcja wymaga włączonych usług lokalizacyjnych i czy chcę je teraz włączyć.
Ad 2. Próbowałem zarówno opcji Simulation jak i z połączonym (prawidłowo) smartfonem.

Wywołanie ILocation Query w GpsComplete daje odpowiedź "No ILocation Available".

0

To (najlepiej na początek na Simulation) użyj debuggera i zobacz na czym się wywala pewnie na tym

if loc.RequestPermissions(_RemotableHandle(nil^), IID_ILatLongReport, 1, Integer(True))  = S_OK then

bo to wywołanie się nie powiedzie jeżeli zgoda już została udzielona (musiałbyś za każdym razem odhaczać zgodę).
Jeżeli masz włączoną zgodę chyba można tymczasowo to wykomentować i sprawdzać co się dalej stanie powinno zwrócić to co wywołanie ILocation Query z menu Tests GPSComplete.

0

Wykomentowałem.
Wywala się na tej linii:

hres := rep.QueryInterface(IID_ILatLongReport, LatLongRep);

Jak uruchomię wykomentowaną aplikację, dostaję komunikat:

Project raised exception class 'External: SIGSEGV'.
0

U mnie działa taki kod:

function IsLocationAPIAvailable: Boolean;
var
  loc: ILocation;
begin
  Result := False;

  try
    loc := CreateComObject(CLASS_Location) as ILocation;
    Result := True;
  except
  end;

  loc := nil;
end;

function LocReportStatus(status: LOCATION_REPORT_STATUS): String;
begin
  Result := '';
  case status of
    REPORT_RUNNING:
      Result := 'Report received!';
    REPORT_NOT_SUPPORTED:
      Result := 'No devices detected!';
    REPORT_ERROR:
      Result := 'Report error!';
    REPORT_ACCESS_DENIED:
      Result := 'Access denied!';
    REPORT_INITIALIZING:
      Result := 'Report is initializing!';
  end;
end;

function WaitForReport(loc: ILocation; reportType: TGUID): Boolean;
var
  status: LOCATION_REPORT_STATUS;
begin
  Result := False;


  while (loc.GetReportStatus(reportType, status) = S_OK) do
  begin
    if status = REPORT_RUNNING then //report ready!
    begin
      Result := True;
      Exit;
    end;

    if status <> REPORT_INITIALIZING then //wait for report!
    begin
      //error occured
      Exit;
    end;
    Sleep(200);
  end;
end;

procedure TForm1.Button3Click(Sender: TObject);
type
  PILocationReport = ^ILocationReport;
var
  loc: ILocation;
  rep: ILocationReport;
  LatLongRep: ILatLongReport;
  status: LOCATION_REPORT_STATUS;
  hres: HRESULT;
  fLat, fLon: double;

begin
  if IsLocationAPIAvailable then
  begin
    loc := CreateComObject(CLASS_Location) as ILocation;
    try
      status := REPORT_NOT_SUPPORTED;
      //have permission?
                                                                            // 0 for asynchronous calls
      //if loc.RequestPermissions(_RemotableHandle(nil^), IID_ILatLongReport, 1, Integer(True))  = S_OK then

      loc.RequestPermissions(_RemotableHandle(nil^), IID_ILatLongReport, 1, Integer(True));
      if True then //tak na chwile zamiast sprawdzac czy RequestPermissions = S_OK
      begin
        if WaitForReport(loc, IID_ILatLongReport) then
        begin
          hres := loc.GetReportStatus(IID_ILatLongReport, status);
          if hres = S_OK then
          begin
            if status = REPORT_RUNNING then //report is ok?
            begin
              hres := loc.GetReport(IID_ILatLongReport, rep);
              if (hres = S_OK) and (rep<>nil) then
              begin
                hres := rep.QueryInterface(IID_ILatLongReport, LatLongRep);
                if (hres = S_OK) and (LatLongRep<>nil) then
                begin
                  fLat := 0;
                  fLon := 0;
                  LatLongRep.GetLatitude(fLat);
                  LatLongRep.GetLongitude(fLon);
                  Edit1.Text := FloatToStr(fLat);
                  Edit2.Text := FloatToStr(fLon);
                end;
              end;
            end
            else
              ShowMessage(LocReportStatus(status));
          end
          else
            ShowMessage(IntToStr(Integer(hres)));
        end;
      end;
    finally
      LatLongRep := nil;
      rep := nil;
      loc := nil;
    end;
  end;
end;

initialization
  CoInitializeEx(nil, COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE);

finalization
  CoUninitialize;
0

No i oczywiście nie działa. Cały czas sprawdzałem to pod Delphi 2007.

Ten warunek

if WaitForReport(loc, IID_ILatLongReport) then

nie jest spełniany. Bez niego pojawia się: No devices found.

Przed chwilą wrzuciłem ten kod do Lazarusa i pojawił się błąd w liniach:

initialization
  CoInitializeEx(nil, COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE);

finalization
  CoUninitialize;  

(no idetifier found).

Czy w Lazarusie powinienem coś dodatkowo zadeklarować? W Delphi przechodzi, ale zależy mi żeby docelowo korzystać z tego kodu w Lazarusie.

EDIT: W Lazarusie, do uses dodałem ActiveX, to już błąd w initialization nie występuje, ale za to pojawia się teraz w tej linii:

if loc.RequestPermissions(_RemotableHandle(nil^), IID_ILatLongReport, 1, Integer(True))  = S_OK then  

A komunikat to:
unit1.pas(108,55) Error: Call by var for arg no. 1 has to match exactly: Got "ACTIVEX._RemotableHandle" expected "LOCATIONAPILIB._RemotableHandle"

0

Tak u mnie wygląda ekran GpsComplete.

0

Chyba znalazłem odpowiedź:
https://naughter.wordpress.com/2015/05/24/changes-in-the-windows-10-sdk-compared-to-windows-8-1-part-two/

A konkretnie mam na myśli fragment ze zrzutu w załączniku.

1
axel234 napisał(a):

Chyba znalazłem odpowiedź:
https://naughter.wordpress.com/2015/05/24/changes-in-the-windows-10-sdk-compared-to-windows-8-1-part-two/

A konkretnie mam na myśli fragment ze zrzutu w załączniku.

Raczej bez znaczenia, w WinAPI jest mnóstwo rzeczy które są “deprecated” a wciąż działają.
Np. takie DirectDraw jest “deprecated” od 17 lat, a używam go z powodzeniem w Windows 10.

Gdyby ILocation miało przestać działać, to już CoCreateInstance by zwracało błąd.

0

Ja tu podejrzewam grubszą sprawę typu brak uprawnień jak wiadomo w nowszych systemach nawet do założenia niektórych hooków potrzeba nie wiadomo czego nawet podpisu cyfrowego aplikacji być może w 10 takie jest wymaganie odnośnie dostępu do lokalizacji ale to by było trochę głupie. Najbardziej zastanawia mnie fakt że w przypadku GpsComplete system raz zapytał o zgodę na dostęp a w testowym programie nie ma zamiaru (na win 7 pyta jeżeli takiej zgody jeszcze nie ma).
GpsComplete ma podpis cyfrowy i być może dlatego działa (choć równie dobrze ten podpis potrzebny jest tylko do instalacji sterowników ale może do tego i do tego).

1 użytkowników online, w tym zalogowanych: 0, gości: 1