📜 ⬆️ ⬇️

Determine the location of the phone ... without GPS

Looking through the Java ME Development hub, I came across the topic of Spb Transport J2ME , where the author uses cartographic services, and one of the TODOs is GPS support (to improve usability). The problem is that the phone with a built-in GPS-receiver is a relatively small amount. I hope this post will help not only the author of that topic, but also to someone else, he himself filled a lot of cones in his time. So let's get started.

To determine the location of the user (phone, as you like), you can use several methods:
- by GPS. The method is the most accurate. Among the shortcomings: a relatively long start, consumes a lot of energy, not too many devices with a built-in receiver.
- on operator's towers. Medium in accuracy. Energy eats a little. Of the minuses: not all phones have data available.
- by IP. Least accurate. Actually this is the biggest minus.
- according to operator's CB-messages

So, we need a more or less acceptable accuracy in determining the location (for the city it is about 150-300 meters (this is somewhere 1.5-2 minutes on foot), outside the city, respectively, 2-5 km or more, as lucky) we need to cover as many devices as possible, and it would be nice to quickly update the coordinates.
The most appropriate is to determine the location through the data of the cellular operator.

Services:
To convert these networks and get the coordinates, we will need a database of cell phone towers. There are many resources in the network, I used the time-tested GoogleMaps, Yandex.Locator, location-api.com and opencellid.org (for greater accuracy and reliability, we use everything at once). plus, as a bonus, Yandex in the API has a method for determining the location based on the signal strength, but more on that later.
Each of the services has a well documented API (except GoogleMaps). API accepted parameters: MCC (country code), MNC (network operator code), LAC (cell code), CellID (tower identifier).
The API will return coordinates for this dataset.
')
Data:
Obtaining the above-mentioned data is not a trivial task, because each of the manufacturers considered it an honor to invent their own bicycle. As a result, for each brand there is a set of keys to call System.getProperty (key), which are not so easy to find.
There are also a number of other unpleasant moments. For example, on Siemens, these networks cannot be obtained without patching the firmware. SonyEricsson returns data in a HEX representation. Nokia refuses to issue LAC to uncertified (that is, almost all) midlets.

Decision:
I wrote a class sorting out known keys and retrieving network data from these keys. Then the keys were sent to the services API, the coordinates were obtained and the average value was derived, which I consider the most plausible (for the year of use I don’t recall cases of very large errors). If the phone allows you to get the signal strength, we use the Yandex bonus: we get the coordinates Taking into account the signal strength and WITHOUT, we get the delta of these values, apply it to all the results from the API, display the average result. Oddly enough, the last decision was a double-edged sword. With a uniform attenuation of the signal, the accuracy is twice as large as the usual method, but if it is a dense urban development or hilly terrain, where the signal is unevenly distributed, in this case the accuracy drops quite strongly.

As a result, it is possible to determine the location on the phones of Siemens, SonyEricsson, Samsung (for example, s5230), Huawei and others. The loading time of coordinates and addresses is about 10-15 seconds.
An example from a demo (the add-in uses the Location class, in which coordinates are determined and addresses are loaded for them)
import javax.microedition.lcdui.Display; import javax.microedition.lcdui.Form; import javax.microedition.midlet.MIDlet; import loc.*; public class HelloWorld extends MIDlet implements Runnable { Display display; Form form; public void startApp() { display = Display.getDisplay(this); form = new Form("NetMonitor"); display.setCurrent(form); new Thread(this).start(); } public void run() { //    if (!Location.reallyNull(Location.lac)) { //   Location.getData(); //  Location.getCoordinates(); //      if (!Location.reallyNull(SystemUtil.signal())) { form.append((": ") + "\n : " + String.valueOf(Location.mcc) + " \n : " + String.valueOf(Location.mnc) + " \n : " + String.valueOf(Location.lac) + " \n C: " + String.valueOf(Location.cid) + " \n : " + SystemUtil.signal() + " \n"); } // else { form.append(": " + "\n : " + String.valueOf(Location.mcc) + " \n : " + String.valueOf(Location.mnc) + " \n : " + String.valueOf(Location.lac) + " \n : " + String.valueOf(Location.cid) + " \n"); } } //    String txt = SystemUtil.nativeDigitSupport(); if (!Location.reallyNull(txt)) { form.append(txt + "\n"); } txt = SystemUtil.operatorName(); if (!Location.reallyNull(txt)) { form.append(txt + "\n"); } txt = SystemUtil.serviceProvider(); if (!Location.reallyNull(txt)) { form.append(txt + "\n"); } txt = SystemUtil.traffic(); if (!Location.reallyNull(txt)) { form.append(txt + "\n"); } txt = SystemUtil.gid1(); if (!Location.reallyNull(txt)) { form.append(txt + "\n"); } txt = SystemUtil.gid2(); if (!Location.reallyNull(txt)) { form.append(txt + "\n"); } // form.append(": ".concat(String.valueOf(Location.getStreet().concat(" \n")))); form.append(": ".concat(String.valueOf(Location.getCity().trim().concat(" \n")))); form.append(": ".concat(String.valueOf(Location.getArea().concat(" \n")))); form.append(": ".concat(String.valueOf(Location.getCountry().concat(" \n")))); form.append(": ".concat(String.valueOf(Location.getLongitude().concat(" \n")))); form.append(": ".concat(String.valueOf(Location.getLatitude().concat(" \n")))); form.append("  ..: ".concat(String.valueOf(Location.getElevation().concat("  \n")))); //   String sens = System.getProperty("microedition.sensor.version"); if (sens != null && sens.length() != 0 && !sens.equals("null")) { sens = SensorApi.getSensor(3); if (sens != null && sens.length() != 0 && !sens.equals("null") && !sens.equals("0")) { form.append("  : " + String.valueOf(sens) + " \n"); } } } public void pauseApp() { } public void destroyApp(boolean flag) { } } 



Well, the source code with the demo
goo.gl/lPkON

Source: https://habr.com/ru/post/146579/


All Articles