I'm a newbie to Android.
How do I get the GSM signal Strength in terms of percentage (1 - 100%)?
The user who asked should have provided more information or feedback. That said...
The question is not trivial at all: since it's a scale in decibels it's not linear and thus smaller changes have a greater impact when the signal is low, while bigger changes are less important when the value is high. That's why I'm sorry to say that all the other answers will be getting inaccurate values that would not match the one displayed on the phone.
Assuming you already have a SignalStrength object (if not, there's another nice answer that shows how to do it), in Marshmallow it's solved with the method getGsmLevel() (there are also methods for all other signals and even combined) that returns a linearized scale 0-4. You can check the source code from the class SignalStrength.
/**
* Get gsm as level 0..4
*
* #hide
*/
public int getGsmLevel() {
int level;
// ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
// asu = 0 (-113dB or less) is very weak
// signal, its better to show 0 bars to the user in such cases.
// asu = 99 is a special case, where the signal strength is unknown.
int asu = getGsmSignalStrength();
if (asu <= 2 || asu == 99) level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
else if (asu >= 12) level = SIGNAL_STRENGTH_GREAT;
else if (asu >= 8) level = SIGNAL_STRENGTH_GOOD;
else if (asu >= 5) level = SIGNAL_STRENGTH_MODERATE;
else level = SIGNAL_STRENGTH_POOR;
if (DBG) log("getGsmLevel=" + level);
return level;
}
Having a 0-100% scale it's not significative because it's a small granularity for this matter, that's why it's more commonly used a 0-4 range and in this method it's already linearized. If not in Marshmallow, just adapt this method to receive the object as a value. If you'd really need a 0-100 range for some reason you should use a dB to linear conversion function, but I'm unaware of the gain factor in GSM signals.
public class MyActivity extends Activity {
public static final int UNKNOW_CODE = 99;
int MAX_SIGNAL_DBM_VALUE = 31;
TelephonyManager tel;
MyPhoneStateListener myPhoneStateListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view = getLayoutInflater().inflate(R.layout.activity_about, null);
setContentView(view);
myPhoneStateListener = new MyPhoneStateListener();
tel = (TelephonyManager) PpsApplication.getAppContext().getSystemService(Context.TELEPHONY_SERVICE);
tel.listen(myPhoneStateListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
private class MyPhoneStateListener extends PhoneStateListener {
/* Get the Signal strength from the provider, each tiome there is an update */
#Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
super.onSignalStrengthsChanged(signalStrength);
if (null != signalStrength && signalStrength.getGsmSignalStrength() != UNKNOW_CODE) {
int signalStrengthPercent = calculateSignalStrengthInPercent(signalStrength.getGsmSignalStrength());
viewModel.setSignalStrengthString(IntegerHelper.getString(signalStrengthPercent));
}
}
}
private int calculateSignalStrengthInPercent(int signalStrength) {
return (int) ((float) signalStrength / MAX_SIGNAL_DBM_VALUE * 100);
}
}
Be aware that .getGsmSignalStrength(); now only returns bars: 0-5,99
The actual values are now hidden. You can still get to them using reflection:
int strength=signalStrength.getGsmSignalStrength();//number of bars not ASU
Log.v("Mobile","BARS: "+strength);
try{//Actual signal strength is hidden
Class classFromName = Class.forName(SignalStrength.class.getName());
java.lang.reflect.Method method = classFromName.getDeclaredMethod("getAsuLevel");//getDbm
strength = (int) method.invoke(signalStrength);
}catch (Exception ex){Log.v("Mobile","cant retreive");}
if (strength == 99 ) { Log.v("Mobile", "ERROR! GSM signal strength not available!");return;}//99 = Unknown
if (strength == 255) { Log.v("Mobile", "ERROR! UMTS signal strength not available!");return;}//255 = Unknown
The above example is for ASU only, which seems to work better than Dbm. After you get the ASU value, you can then dump it into percentage:
Log.v("Mobile","ASU: "+strength);
//Data.mobile_signal=strength*20;//Number of bars 0-5
//Data.mobile_signal = 100-((strength-113)/62);//GSM DBM
Data.mobile_signal =(int)((double)strength*100/31);//GSM ASU
Data.mobile_signal =(int)((double)strength*100/91);//UMTS ASU
Log.v("Mobile","Set GSM signal from "+strength+" to "+Data.mobile_signal);
As a reminder, this is for when you have a GSM signal not a CDMA signal. Use TelephonyManager.getPhoneType(); to determine which:
1=GSM, 2=CDMA, 3=SIP
BUT WAIT! This says I only have a 50% signal strength yet I have 5 bars! This is wrong!
Well, not exactly. Unless your phone is right in front of the transmitter, it's probably not going to be 100%. But 50% signal is about 100% quality. So from here you have to get creative.
A better solution is to use:
signalStrength.level
This returns a 0 to 4 value that you should be familiar with from your android device. Think of it as the number of bars of signal. This is compatible across GSM and CDMA.
Related
I have an app which shows the current signal strength, in many phones it is working, but when I test it on a Huawei phone, it is always 0, at the same time the signal bars are full in that huawei phone. what is the problem? is it related to android version? is it related to permissions? i have this:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
It's not always zero. The way you are getting the signal strength is wrong.
In some of the phones, it could be zero may be the removal of api.
Just try this code if it works for you.
String gsmSignalStrength=null;
String ssignal = asu.toString();
String[] parts = ssignal.split(" ");
int currentStrength = asu.getGsmSignalStrength();
if (currentStrength <= 0) {
gsmSignalStrength= String
.valueOf(Integer.parseInt(parts[3]));
} else {
gsmSignalStrength= String.valueOf(-113 + 2 * asu.getGsmSignalStrength());
}
Log.d(TAG,"Signal Strength : "+gsmSignalStrength);
This will works only for GSM ;
The parts array elements are described as
parts[1] = GsmSignalStrength
parts[3] = CDMADBM
parts[8] = LteSignalStrength
parts[9] = LteRsrp
parts[10] = LteRsrq
parts[13] = gsm|lte
So you can put some condition to extract value from this array.
I using this code for get wifi signal set.(BSSID, SSID, RSSI.. etc.).
public void sortScanResult()
{
wifiScan.scanWifiSignal();
scanedResults = new ArrayList<ScanResult>(wifiScan.getScanResults());
Comparator<ScanResult> comparator = new Comparator<ScanResult>() {
#Override
public int compare(ScanResult lhs, ScanResult rhs) {
return (lhs.level > rhs.level ? -1 : (lhs.level == rhs.level ? 0 : 1));
}
};
Collections.sort(scanedResults, comparator); //sorting result
if (scanedResults.size() > 10) {
int resultSize = scanedResults.size();
for (int index = resultSize - 1; ; index--) {
scanedResults.remove(index);
if (scanedResults.size() == 10) break;
}
}
}
but the scanResults of this code return RSSI level by INT.
I want get rssi level value by float for precision because I use for indoor navigation.(Finger Print).
Is there no way to get RSSI level value by float?
ps. sorry, I'm poor at English.
Even if it were in float, that doesn't imply greater accuracy as float is 32bit same as int.
But anyway it's just not available at the hardware level:
Vendors and chipset makers provide their own accuracy, granularity.
Cisco Systems cards have a RSSI_Max value of 100 and will report 101 different power levels, where the RSSI value is 0 to 100. Another popular Wi-Fi chipset is made by Atheros. An Atheros based card will return an RSSI value of 0 to 127 (0x7f) with 128 (0x80) indicating an invalid value.
So that's what you have to work with, around 7 bits.
P.S. you won't have much luck with your approach according to the wiki article:
RSSI doesn't always provide measurements that are sufficiently accurate to properly determine the location.
using Bluetooth Low Energy (BLE) scan on Android, I noticed that sometimes RSSI values are incorrect.
My code simply calls the start scan function:
mBluetoothAdapter.startLeScan(mLeScanCallback);
and then I read results in the callback and save results in a file:
private static BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
String objScanRec = bytesToHex(scanRecord);
outStr = rssi + ";" + objScanRec + ";" + device.getName() + ";" + beaconLocation + ";\n";
try {
Raw_log.write(outStr);
Raw_log.flush();
} catch (IOException e) {
e.printStackTrace();
}
// }
}
};
the problem is that I read positive RSSI values, also if the beacon is at a fixed distance.
E.g. I have the beacon 30 cm from the phone (or smartwatch) I read a values around -45 which are realistic, but also values around +80 or +100 (which are not realistic) those values are around 20% of measurements.
Is there something that I'm missing?
thanks
thanks for your help I figured out it's a problem related only to Samsung Gear Live. I came up with this s solution:
if(rssi > 0){
rssi = rssi - 128;
}
I've tested the solution and it works fine. (e.g. the obtained positive values after correction are now similar to negative values, for example I read
-44 -45 -43 84 82
that after correctio become:
-44 -45 -43 -44 -46)
This is definitely not normal. I have never seen a rssi value in that callback be positive. Typical values are from -30 to -120.
I suspect there is something wrong with the way the data are written out to the log, or read back. What happens if you just do a regular Log.d(TAG, "rssi="+rssi); Do you ever see positive values? If so, can you share an excerpt, along with the device model you are using to detect and the device you are detecting?
I'm using a GPS provider and LocationListener.onLocationChanged(Location location) to receive location fixes.
Documentation says, that Location.getExtras() contains next key/value pair:
satellites - the number of satellites used to derive the fix
but on practice I'm getting an empty extra object - there is no any data there.
Does it means that I'm getting the A-GPS fixes and not GPS?
To get the number of satellites used by the GPS engine you need to implement android.location.GpsStatus.Listener and implement its method onGpsStatusChanged().
Example...
public void onGpsStatusChanged(int event) {
int satellites = 0;
int satellitesInFix = 0;
int timetofix = locationManager.getGpsStatus(null).getTimeToFirstFix();
Log.i(TAG, "Time to first fix = " + timetofix);
for (GpsSatellite sat : locationManager.getGpsStatus(null).getSatellites()) {
if(sat.usedInFix()) {
satellitesInFix++;
}
satellites++;
}
Log.i(TAG, satellites + " Used In Last Fix ("+satellitesInFix+")");
}
I use Location.getExtras().getInt("satellites"), and it give the number of satellites in use.
Since Android API 24 GpsStatus is deprecated and one should use GnssStatus. Let us have an activity or a service processing Gps data and a LocationManager already created.
private GnssStatus.Callback gnssCallback;
public void initCallbacks() {
....
gnssCallback = new GnssStatus.Callback() {
#Override
public void onSatelliteStatusChanged(#NonNull GnssStatus status) {
final int satelliteCount = status.getSatelliteCount();
int usedCount = 0;
for (int i = 0; i < satelliteCount; ++i)
if (status.usedInFix(i))
++usedCount;
Log.d("MyServiceTag", "satellites count = " + satelliteCount + ", used = " + usedCount);
}
};
locationManager.registerGnssStatusCallback(gnssCallback, new Handler(Looper.myLooper()));
....
}
public void deinitCallbacks() {
....
locationManager.unregisterGnssStatusCallback(gnssCallback);
....
}
initCallbacks() should be called after locationManager initialization. deinitCallbacks() should be called when information on the number of satellites is no longer needed, e.g. in onDestroy(). GnssStatus.getSatelliteCount() returns total number of known satellites, GnssStatus.usedInFix(int i) tells whether i-th satellite had been used in the most actual location capture.
Nope it means that your phone manufacturer decided not to implement this. (Or you could be using the NETWORK_PROVIDER which does not use satellites)
Use a NmeaListener and parse the sentences to know the number of satellites visible or used.
I can obtain the phone number from an incoming call or from a sms message. unfortunately, in case of the SMS there might be the country code in it. So, basically I need to obtain the plain phone number, without country code, in order to compare it with existing numbers in Contacts.
If you want to compare phone numbers you can always use the
PhoneNumberUtils.compare(number1, number2);
or
PhoneNumberUtils.compare(context, number1, number2);
Then you don't have to worry about the country code, it will just compare the numbers from the reversed order and see if they match (enough for callerID purposes at least).
fast untested approach (AFAIK phone numbers have 10 digits):
// As I said, AFAIK phone numbers have 10 digits... (at least here in Mexico this is true)
int digits = 10;
// the char + is always at first.
int plus_sign_pos = 0;
// Always send the number to this function to remove the first n digits (+1,+52, +520, etc)
private String removeCountryCode(String number) {
if (hasCountryCode(number)) {
// +52 for MEX +526441122345, 13-10 = 3, so we need to remove 3 characters
int country_digits = number.length() - digits;
number = number.substring(country_digits);
}
return number;
}
// Every country code starts with + right?
private boolean hasCountryCode(String number) {
return number.charAt(plus_sign_pos) == '+'; // Didn't String had contains() method?...
}
then you just call these functions