Android Question Mock Location doesn't accept Lat/Lon

Discussion in 'Android Questions' started by Fusseldieb, Jul 29, 2019.

  1. Fusseldieb

    Fusseldieb Active Member Licensed User

    Hi everyone,
    I've tried to use the mock-locationprovider-lib and successfully simulated a location with it (after setting the target API down to 16 because of a deprecated library function which doesn't set a specific value that's required on newer API's, but anyways), but I'm struggling to set a specific Lat/Lon value to it.

    If I take the example that is:
    Code:
    latitude="5234.1421"        'lat. as decimal_degrees
    ns="N"                    'north-south = NORTH
    longitude="01311.7996"        'long. as decimal_degrees
    we = "E"                    'west-east = EAST
    altitude="84.0"                'alt. as meter     
    speed="0.0"                'speed as km/h
    bearing="75.0"            'cuorse as degrees
    accuracy="2.0"            'accuracy of horizontal position fix in relation to used sattelites position as meter
    it simulated a location somewhere in Germany, which is totally fine.

    Now I've tried to put Lat/Lon values there -16.3000000 and -48.9300000 but the library doesn't like that and spits an error out that
    I've tried to omit the negative sign and use ns="s" and we="w" but it still compained. Etc etc.

    I'm trying for literal days now and have no clue on how to convert Lat/Lon to this format (it's not even normal decimal degress, it's something else).

    Any help is welcome
     
  2. DonManfred

    DonManfred Expert Licensed User

    Your values (-16.3000000 and -48.9300000) doesn´t match the error.
     
  3. Fusseldieb

    Fusseldieb Active Member Licensed User

    That's exactly where I'm stuck. I'm feeding these exact values in and it gives me the 0.6.000000 error. I'm not using double dots anywhere, that's the library messing things up. I think it's trying to "parse" something, but since it's in the wrong format, it does weird things like these.

    The code is practically unedited, I've just edited the locations and it messed everything up, parsing double dotted floats and stuff.

    I'm using the wrong format, but which one is the correct? How do I reach the correct one?
     
  4. DonManfred

    DonManfred Expert Licensed User

    possible. But as the source is not available we just can guess.
     
  5. Fusseldieb

    Fusseldieb Active Member Licensed User

  6. DonManfred

    DonManfred Expert Licensed User

    1. Use File-Export as zip. The file would be much smaller and can be uploaded here instead of volafile.org
    2. The source of the mockprovider lib is not available. Your mentioned calculations are probably done here.
     
    MarkusR likes this.
  7. Fusseldieb

    Fusseldieb Active Member Licensed User

    Sorry about the .rar, here's the zip.

    Is there any other way to archieve mock locations via B4A? This library archieves it using Reflection, as it seems, but I have no ideas on how to do it. Any help would be super!

    EDIT: Decompiled the library using a cool tool I just found and it actually worked (javadecompilers.com)!

    Code:
    package esolutions4you.B4A.moclocationprovider;

    import android.content.Context;
    import android.location.Location;
    import android.location.LocationManager;
    import anywheresoftware.b4a.BA.Author;
    import anywheresoftware.b4a.BA.ShortName;
    import anywheresoftware.b4a.BA.Version;


    @BA.ShortName(
    "moclocationprovider")
    @BA.Author(
    "bitstra")
    @BA.Version(
    1.0F)
    public class moclocationprovider
    {
      
    public moclocationprovider() {}
     
      public static void enableMockProvider(Context ctx)
      {
        LocationManager mLocationManager = (LocationManager)ctx.getSystemService("location");
     
        mLocationManager.addTestProvider(
          "gps",
          false,
          false,
          false,
          false,
          false,
          false,
          false,
          3,
          1);
     
        mLocationManager.setTestProviderEnabled("gps", true);
        mLocationManager.setTestProviderStatus(
          "gps",
          2,
          null,
          System.currentTimeMillis());
      }

      public static void disableMockProvider(Context ctx)
      {
        LocationManager mLocationManager = (LocationManager)ctx.getSystemService("location");
     
        if (mLocationManager.getProvider("gps") != null)
        {
          mLocationManager.clearTestProviderEnabled("gps");
          mLocationManager.clearTestProviderStatus("gps");
          mLocationManager.clearTestProviderLocation("gps");
          mLocationManager.removeTestProvider("gps");
        }
      }
     
      public static void publishMockLocation(String latitude, String ns, String longitude, String we, double altitude, float speed, float bearing, float accuracy, Context ctx)
      {
        LocationManager mLocationManager = (LocationManager)ctx.getSystemService("location");
     
        String lat_deg = latitude.substring(0, 2);
        String lat_min1 = latitude.substring(2, 4);
        String lat_min2 = latitude.substring(5);
        String lat_min3 = "0." + lat_min1 + lat_min2;
        float lat_dec = Float.parseFloat(lat_min3) / 0.6F;
        float lat_val = Float.parseFloat(lat_deg) + lat_dec;

        if (!ns.equals("N"))
        {

          lat_val *= -1.0F;
        }

        String lon_deg = longitude.substring(0, 3);
        String lon_min1 = longitude.substring(3, 5);
        String lon_min2 = longitude.substring(6);
        String lon_min3 = "0." + lon_min1 + lon_min2;
        float lon_dec = Float.parseFloat(lon_min3) / 0.6F;
        float lon_val = Float.parseFloat(lon_deg) + lon_dec;

        if (!we.equals("E"))
        {

          lon_val *= -1.0F;
        }

        Location newLocation = new Location("gps");
     
        newLocation.setLatitude(lat_val);
        newLocation.setLongitude(lon_val);
        newLocation.setAltitude(altitude);
        newLocation.setSpeed(speed);
        newLocation.setBearing(bearing);
        newLocation.setTime(System.currentTimeMillis());
        newLocation.setAccuracy(accuracy);
     
        mLocationManager.setTestProviderEnabled("gps", true);
        mLocationManager.setTestProviderStatus(
          "gps",
          2,
          null,
          System.currentTimeMillis());
     
        mLocationManager.setTestProviderLocation(
          "gps",
          newLocation);
      }
    }
    The decompiled version might help sorting out the problem, but for today, it's enough for me. It's already 02:57 in the morning and I am gonna work today (07:00), so... yay... ouch.

    EDIT2: 03:16, who needs sleep anyways? Progress? Yes.
    I've analysed the source a bit and figured out that it uses a rather specific format. It must match exactly that format, or else it fails. Very dirty code, but whatever.
    Code:
    // Given "Latitude"5234.1421 from demo code
    String lat_deg = latitude.substring(02); // 52
    String lat_min1 = latitude.substring(24); // 34
    String lat_min2 = latitude.substring(5); // 1421
    String lat_min3 = "0." + lat_min1 + lat_min2; // 0. & 34 & 1421
    float lat_dec = Float.parseFloat(lat_min3) / 
    0.6F; // (0.569035‬)
    float lat_val = Float.parseFloat(lat_deg) + lat_dec; // (
    52 + 0.569035‬) = 52.569035
    Running through this piece of code full of substrings and parseFloats, it produces a valid latitude, with negative sign, if "N" doesn't exist in the variable ns.

    Now I will try to reverse it. I'll report.
     

    Attached Files:

    Last edited: Jul 29, 2019
  8. moster67

    moster67 Expert Licensed User

    Just a sidenote, even though the wrapper is perhaps not longer maintained, you should not publish the decompiled source code unless you have permission from the author.
     
    BillMeyer and DonManfred like this.
  9. emexes

    emexes Well-Known Member Licensed User

    I was going to point out that the longitude and latitude measurement look like they're in a weird decimal-scaled-minutes format, eg:

    dddmm.mmmm where ddd = degrees, mm.mmmm = minutes (to 4 decimal places)

    It can't be:

    dddmm.sshh where sshh is seconds and hundredths-of-a-second, because the longitude that worked would then be 79 seconds whereas valid ss should be 00 to 59

    but you said:
    which half-sinks that theory, except that you've used lowercase compass letters, and the working sample used uppercase.

    The ".93" in your not-working "48.9300000" should be ok, if the format around the decimal point is indeed mm.mmmm.

    Perhaps it is the extra "000" on the end that is confusing the library; maybe it only accepts 4 digits after the point and no more. The leading "0" of "01311.7996" slightly confirms that the library is expecting a specific number of digits, although this clue is tempered by the "5234.1421" not having a leading "0", so... hmm...
    My approach would be to slowly change the working longitude and latitude, digit by digit and step by step, towards the location that you are aiming for. Every now and again, one of your changes will cause an error, eg , perhaps when you move from 17900.0000 to 18000.0000...

    Uh, I forgot to mention, your scale looks wrong too, you didn't multiply degrees by 100. Like, you're using ddd.dddd whereas it looks like it should be dddmm.mmmm so give that a burl.


    What could possibly go wrong?!?!

    :)
     
    Last edited: Jul 29, 2019
  10. emexes

    emexes Well-Known Member Licensed User

    Looks like you're seven steps ahead of me. Or maybe not, given that I possibly reached the finish line in the previous post. But I was impressed by the decompilation... it's like: you ran the marathon, I cheated and took the bus! :-/

    That code makes it clear that the latitude has only two digits for degrees (since the max number is 90 degrees from the equator) whereas the longitude is three digits (since it takes 180 degrees to get around to the opposite side of the world). So that explains why the example latitude was missing the leading "0".

    Code:
    String lat_deg = latitude.substring(02);    'two digits latitude 0..90 degrees
    String lat_min1 = latitude.substring(24);    'two-plus-
    String lat_min2 = latitude.substring(5);    'the-rest-of-it digits of minutes
    String lat_min3 = "0." + lat_min1 + lat_min2;    'divided by 100, so that eg 12.34 minutes is now 0.1234
    float lat_dec = Float.parseFloat(lat_min3) / 0.6F;    'convert minutes to fraction-of-degree by dividing by 60 (effectively: minutes is already divided by 100)
    float lat_val = Float.parseFloat(lat_deg) + lat_dec;    'add degrees and minutes (fraction-of-degree) to get single decimal degrees 0.0 to 90.0 (well, perhaps to 99.9... but let's not push our luck)

    if (!ns.equals("N")) {    'ewk - case sensitive :-(
        lat_val *= -1.0F;
    }

    String lon_deg = longitude.substring(0, 3);    'three digits longitude 0..180 degrees
    String lon_min1 = longitude.substring(3, 5);    'remainder is as per latitude calculation  
    String lon_min2 = longitude.substring(6);
    String lon_min3 = "0." + lon_min1 + lon_min2;
    float lon_dec = Float.parseFloat(lon_min3) / 0.6F;
    float lon_val = Float.parseFloat(lon_deg) + lon_dec;

    if (!we.equals("E")) {    'including this bit - ewk again 
        lon_val *= -1.0F;
    }
    so it would seem that if you passed your example location of -16.3000000 and -48.9300000 as "1680.000" "S" and "4855.480" "E" then you'd be at home and hosed.

    In Anápolis?
     
  11. Fusseldieb

    Fusseldieb Active Member Licensed User

    Exactly. You saw everything which I did. Good eye :)

    And thanks for everyone at helping too!

    I continued digging into the code and made two functions which produce exactly the values that this library needs! It took me an hour (I guess). One hour less sleep too, whatever.

    Here it is:
    Code:
    Sub ConvertLatLon(lat As Float,lng As Float) As String
        
    Dim latResult As String
        
    Dim lngResult As String
        
    Dim dmsResult As String

        
    If(lat >=0Then
            latResult = 
    "N"
        
    Else
            latResult = 
    "S"
        
    End If

        latResult = latResult & GetDMS(lat)

        
    If(lng >=0Then
            lngResult = 
    "E"
        
    Else
            lngResult = 
    "W"
        
    End If
        
    If (lng < 100Then
            lngResult = lngResult & 
    "0" ' Remember subString 0,3? This is him now. Feel old yet?
        End If
     
        lngResult = lngResult & GetDMS(lng)

        dmsResult = latResult & 
    " " & lngResult

        
    Return dmsResult
    End Sub

    Sub GetDMS(val As Float) As String

        
    Dim valDeg As Int
        
    Dim valMin As Float
        
    Dim valSec As Float
        
    Dim result As String

        val = 
    Abs(val)

        valDeg = 
    Floor(val)
        result = valDeg

        valMin = 
    Floor((val - valDeg) * 60)
        result = result & valMin & 
    "."

        valSec = 
    Round((val - valDeg - valMin / 60) * 3600 * 1000' Original code had /1000 after that, but I removed it, because it would produce double dots.
        result = result & valSec

        
    Return result
    End Sub
    Adapted from: http://en.marnoto.com/2014/04/converter-coordenadas-gps.html

    But I have one problem: When I input the lat/lon coordinates into the function "ConvertLatLon(lat,lon)", the library parses it almost right. The position shows up in my city, but not in my street. In fact, not even close. It appears +-200m off. The coordinates that I gave to the function is exactly at my door, but the mock location is 200m off.

    My guess is that Java (or B4A?) does some math operations differently and it looses accuracy? Or maybe it's my fault and I use wrong data types? If someone could shed me some light into it, it would be great!
     
    Last edited: Jul 29, 2019
  12. emexes

    emexes Well-Known Member Licensed User

    The coordinates you gave were to 0.01 of a degree, and a degree (of latitude) is ~111 km so coordinates are to nearest 1.11 km thus would expect to be up to +/- 550 metres from your door.

    edit: sorry, forgot two dimensions = +/- 700 metres

    ps: but unless you're planning on throwing a party, probably best not to publicise your "address"... perhaps pin the nearest pub or café instead ;-)
     
    Last edited: Jul 29, 2019
  13. Fusseldieb

    Fusseldieb Active Member Licensed User

    Hmm, something's off.

    I receive from my USB GPS the following lat:
    It has 6 decimal places, which, according to Wikipedia is
    upload_2019-7-29_5-10-3.png
    This is roughly 1 meter, which is enough. But I am 200m off, and it's 200m steady! It's not jumping aroung... I think my formula is crooked.

    Oh, hahaha. Don't worry. I haven't yet posted my accurate home position. And even if, you must find me first :)
     
  14. emexes

    emexes Well-Known Member Licensed User

    Consumer GPS accurate to +/- 2 metres on a good day, +/- 10 metres on a bad day. Depends on cloud and tree cover, rain, and number and spread of satellites in sky.
    True. But I wouldn't go trusting that last digit.

    They've thrown it in because it is better to provide information that is roughly correct, than to withhold it because it might not be.
     
  15. Fusseldieb

    Fusseldieb Active Member Licensed User

    I am sure that the received position is correct, I can check it manually on any site that lets me input my lat/lot. It appears right in front of my street. But when I convert it, it's 200m off.

    That's strange.

    EDIT: I've checked again. Lat/Lon is 100% correct and appears at my location.
     
  16. emexes

    emexes Well-Known Member Licensed User

    Am I getting closer to the Party House?

    upload_2019-7-29_18-21-2.png
     
  17. Fusseldieb

    Fusseldieb Active Member Licensed User

    Ahahaha... Yes, you're inside a 50-100m radius.
     
  18. emexes

    emexes Well-Known Member Licensed User

    So we're good? Or are we chasing down some oddball thing like your GPS being set to a different coordinate system eg not the usual WGS-84?
     
  19. Fusseldieb

    Fusseldieb Active Member Licensed User

    My coordinates are correct, yes, but the "formula" makes it loose accuracy up to 200m. And I need to run it through it to feed the library.
    I'm still trying to figure out why.
     
  20. emexes

    emexes Well-Known Member Licensed User

    From memory, continents are moving (in different directions!) at 1-5 cm/year, so since 1984 that'd be like things have moved by a metre or so. Not sure how they cope with that with regards to property boundaries and suchlike. But there must be some system in place, like there is on maps where they note how much magnetic north is moving per year.
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice