Android Question Mock Location doesn't accept Lat/Lon

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:
B4X:
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
java.lang.NumberFormatException: Invalid float: "0.6.000000"
at java.lang.StringToReal.invalidReal(StringToReal.java:63)
at java.lang.StringToReal.initialParse(StringToReal.java:160)
at java.lang.StringToReal.parseFloat(StringToReal.java:323)
at java.lang.Float.parseFloat(Float.java:306)
at esolutions4you.B4A.moclocationprovider.moclocationprovider.publishMockLocation(moclocationprovider.java:95)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
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
 

Fusseldieb

Active Member
Licensed User
Your values (-16.3000000 and -48.9300000) doesn´t match the error.
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?
 

Fusseldieb

Active Member
Licensed User
Sorry about the .rar, here's the zip.

2. The source of the mockprovider lib is not available. Your mentioned calculations are probably done here.
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)!

B4X:
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.
B4X:
// Given "Latitude": 5234.1421 from demo code
String lat_deg = latitude.substring(0, 2); // 52
String lat_min1 = latitude.substring(2, 4); // 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.
 

Attachments

Last edited:

emexes

Well-Known Member
Licensed User
If I take the example that is:
B4X:
latitude="5234.1421"        'lat. as decimal_degrees
ns="N"                    'north-south = NORTH
longitude="01311.7996"        'long. as decimal_degrees
we = "E"                    'west-east = EAST
Now I've tried to put Lat/Lon values there -16.3000000 and -48.9300000
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:
I've tried to omit the negative sign and use ns="s" and we="w" but it still compained.
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...
Any help is welcome
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:

emexes

Well-Known Member
Licensed User
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.
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".

B4X:
String lat_deg = latitude.substring(0, 2);    'two digits latitude 0..90 degrees
String lat_min1 = latitude.substring(2, 4);    '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?
 

Fusseldieb

Active Member
Licensed User
In Anápolis?
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:
B4X:
Sub ConvertLatLon(lat As Float,lng As Float) As String
    Dim latResult As String
    Dim lngResult As String
    Dim dmsResult As String

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

    latResult = latResult & GetDMS(lat)

    If(lng >=0) Then
        lngResult = "E"
    Else
        lngResult = "W"
    End If
    If (lng < 100) Then
        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:

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:

Fusseldieb

Active Member
Licensed User
The coordinates you gave were only 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
Hmm, something's off.

I receive from my USB GPS the following lat:
-16.296451
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.

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 ;-)
Oh, hahaha. Don't worry. I haven't yet posted my accurate home position. And even if, you must find me first :)
 

emexes

Well-Known Member
Licensed User
I receive from my USB GPS the following lat:
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.
It has 6 decimal places
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.
 

Fusseldieb

Active 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.
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.
 

emexes

Well-Known Member
Licensed User
EDIT: I've checked again. Lat/Lon is 100% correct and appears at my location.
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?
 

Fusseldieb

Active 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?
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.
 

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.
 
Top