jueves, 5 de diciembre de 2013

Indoor Positioning using BLE -> RSSI

RSSI (dBm) = -10n log10(d) + A, 
d: distance in metres 
A: received signal strength in dBm at 1 meter
n: propagation constant or path-loss exponent (Free space has n =2 for reference).

Indoor positioning systems don't bother with fading and theoretical link budget formulas. No fresnel required.
Use extensive calibration instead. you don't need to take into account the frequency of your transmission simplifying calculations. 

Relative RSSI factor between different sources may compensate for some elements of variability of absolute RSSI values to solve variations due to fading.
The result may be some information in the form of "the distance to point A is about 1.5x the distance to point B" which is enough information to infer relative location to the fix points.

Realtime tracking using RSSI:  Papers
Indoor Location Tracking using Received Signal Strength Indicator
Bluetooth Indoor Positioning
- Position Location of Remote Bluetooth Devices 
- A Presence-detection Method using RSSI of a Bluetooth Device
- A COMPREHENSIVE STUDY OF BLUETOOTH SIGNAL PARAMETERS FOR LOCALIZATION 
- Using Inquiry-based Bluetooth RSSI Probability Distributions for Indoor Positioning



- ENHANCED RSSI-BASED HIGH ACCURACY REAL-TIME USER LOCATION TRACKING SYSTEM FOR INDOOR AND OUTDOOR ENVIRONMENTS
- Outdoor Localization System Using RSSI Measurement of Wireless Sensor Network
- Accurate Distance Estimation Using Fuzzy based combined RSSI/LQI Values in an Indoor Scenario: 
Experimental Verification 
- Bluetooth Indoor Positioning using RSSI and Least Square Estimation
Bluetooth-based Indoor Proximity Sensing for Nursing Context Awareness (no device limit)

- Distance Sensing for Mini-robots: RSSI vs. TDOA 
- Experimental Analysis of RSSI-based Location Estimation in Wireless Sensor Networks
- Improving RSSI based distance estimation for 802.15.4 wireless sensor networks



 USE an IMU!:
Measure for a fair period of time and calibrate.
Use a simple kalman filter on the antenna orientation and the IMU to get a rough running... this is not very CPU or battery light.
  • Using the IMU you get a fair idea of the distance/direction of travel - and if this is over a short period of time - we assume the other 'side' is stationary. This helps a lot to get a value for 'current' orientation and 'callibrate current environment noise.
  • Likewise - do the same for rotations/position changes.
  • re-orientation of the device is a better way to get direction; and that distance is only reliable some up to some 30 to 600 seconds after a 'move' calibration' and only if the device is not too much rotated
  • in practice once needs some 4-5 'other' devices; ideally not too mobile, to keep oneself dynamically calibrated.
  • RSSI jumps wildly and randomly
  • You should retrieve your RSSI values every two seconds (any faster and you get a bunch of errors). Throw out the RSSI values that are more than a ~-40 decibel spike and use an aggregate of the past values before you declare your approximate range to the user.
  • to reduce the signal strength of the peripheral devices advertisement: use  TX Power Service . Implementing this service on your peripheral will allow you to decrease the transmit power of the device. That way you can throttle down the range that the advertisement data is visible from. We unfortunately do not however, have access to this service on an iOS device. But if you are writing your own firmware for a BLE peripheral, this is the service you want.
  • to compensate: Indoor Location Tracking using Received Signal Strength Indicator

Raspberry Pi:
Comparing BLE RSSI: iPhone 5s, Raspberry Pi A & B
Bluetooth distance sensing - phone etc without pairing
 signal strength values on my setup with a cheap bluetooth dongle

    38 - Phone touching bluetooth dongle
    25 - Phone an inch away
    10 - 6 inches away
    0 - anything from 2 feet to opposite side of room
    -3 - in next room (wall between)
    -10 - downstairs/ 2 rooms/2 walls away
1) Bluetooth stack on your Pi: sudo apt-get install --no-install-recommends bluetooth
2) You need to know the device's unique bluetooth address (BDADDR / MAC) - to get it directly the device needs to be discoverable, just for this stage: hcitool scan
3. Connect to phone: sudo rfcomm connect 0 12:34:56:78:90:00 10 >/dev/null &
4. check status level: hcitool rssi 12:34:56:78:90:00
or check it every 1/2 second on screen, so you can move the phone round (or leave it in another room) and watch the number change:  watch -n 0.5 hcitool rssi 12:34:56:78:90:00
Only step 2 (scan) needs to be done when its discoverable - but once you know the MAC address, you don't need to do that ever again.

What I found though, is that when your device goes out of range and drops the "rfcomm" connection, when it comes BACK in range again... the connection does not auto start again.

So what I needed to do is the following:

1) When the script first runs, attempt to connect to the bluetooth device:
rfcomm connect 0 12:34:56:78:90:00 10 >/dev/null &
2) After a connection attempt, check the rssi level with the following:
hcitool rssi 12:34:56:78:90:00
If you get a valid response, then the device is connected and you mark a variable to keep track of your connection state so you don't attempt to connect over and over again: btconnected=1
3) When you finally get an "error" response from checking rssi levels, mark the device "NOT connected" and attempt to connect again.

#Global VARS:
device="XX:YY:ZZ:11:22:33"
btconnected=0
btcurrent=-1
counter=0
notconnected="0"
connected="1"
rssi=-1

#Command loop:
while [ 1 ]; do
cmdout=$(hcitool rssi $device)
btcurrent=$(echo $cmdout | grep -c "RSSI return value") 2> /dev/null
rssi=$(echo $cmdout | sed -e 's/RSSI return value: //g')

if [ $btcurrent = $notconnected ]; then
        echo "Attempting connection..."
        rfcomm connect 0 $device 1 2> /dev/null >/dev/null &
        sleep 1
fi

if [ $btcurrent = $connected ]; then
        echo "Device connected. RSSI: "$rssi
fi

if [ $btconnected -ne $btcurrent ]; then
        if [ $btcurrent -eq 0 ]; then
                echo "GONE!"
        fi
        if [ $btcurrent -eq 1 ]; then
                echo "HERE!"
        fi
        btconnected=$btcurrent
fi

sleep 1

done


----------------------------------------------------------------------------------
JAVA
http://techtitude.blogspot.com/2013/01/tutorial-to-continuously-measure.html







Wikipedia: http://en.wikipedia.org/wiki/Trilateration 

RSSI is affected by many factors like obstacles, multipath fading, antenna polarization and cross-body shielding. the range of the antenna varies with the design of the board, type of plastics, environment the board will be placed in. So it is very difficult to give an accurate location with a single antenna.

To get RSSI without disconnecting and connecting use : BluetoothGatt object on Android

In line-of-sight (no obstacles causing change in rssi) -6dB = double the distance. 
1m: -40dB 
2m: -46dB
... 16m: -64dB
Technique: Triangulation or fingerprinting, 2-3 or more devices (+ accurate positioning) 
Advertisement packages (Disable scan -> Enable scan / tell iOS CoreBluetooth to report all adv packages). Restriction iOS: In foreground mode you can do this. But in background mode you can't get all adv packages. You must connect and read RSSI to do it in the background.

Include Lat/Lon info:
You will need to account for altitude, and for the fact that the lat/lon coordinates are based on an ellipsoidal rather than a spherical reference surface. See here:en.wikipedia.org/wiki/ECEF  and here: satsleuth.com/GPS_ECEF_Datum_transformation.htm  
The first step, not really covered in the Wikipedia entry, is to convert your lat/long coordinates to Cartesian coordinates:
x0 = cos( lon0 ) * cos( lat0 ) , y0 = sin( lon0 ) * cos( lat0 ) , z0 = sin( lat0 )
x1 = cos( lon1 ) * cos( lat0 ) , y0 = sin( lon1 ) * cos( lat1 ) , z0 = sin( lat1 )
x2 = cos( lon2 ) * cos( lat0 ) , y0 = sin( lon2 ) * cos( lat2 ) , z0 = sin( lat2 )
(To keep calculations simple, I've fudged things so we are working in units of "earth radii" instead of kilometers)
For your data, I get
         p0            p1           p2
X   -0.420442596  -0.420430618  -0.42040255
Y   -0.67380418   -0.673826567  -0.673825967
Z    0.607631426   0.607614889   0.607634975
The next step, which is covered in the Wikipedia article, is to simplify the coordinates, by translating the points so p0 is at the origin, and then rotating so that p1 is on the X axis, and p2 is in the X-Y plane.
For the translation, just subtract p0 from p1 and p2:
    p0a      p1a          p2a
X   0    1.19779E-05   4.00462E-05
Y   0   -2.23864E-05  -2.17865E-05
Z   0   -1.65372E-05   3.5486E-06
The rotation isn't much harder. p1b gets (x,y) = (d,0), where d is just the distance from the origin to p1a (Pythagorean theorem)
For p2b, we need to resolve p2a into two components: one parallel to p1a (which goes on our x axis), and one perpendicular to p1a, (which goes on our y axis in the "b" coordinate system).
To do this, we need a unit vector in the direction of p1a, which is just p1a * ( 1/d ). Take the dot product of this unit vector (call it p1a_hat, if you like) with p2a, and that's the X coordinate for p2b. The Wikipedia article calls this value "I"
Now the Y coordinate is easy. The length from the origin to p2 can't change under the coordinate transformation. So calculate p2a's length using the Pythagorean theorem, then use the Pythagorean theorem "backwards" to get what the Y coordinate for p2b has to be to keep the length the same. That's the variable that Wikipedia calls "J". (Note, there's an ambiguity that I'll leave for you to figure out over whether J is positive or negative).
Now you've got the three variables d, I and J, that the Wikipedia article uses for the calculation. You can convert them back to kilometers now, by multiplying by the earth's radius. You should be able to do the rest of the calculation from here
(Incidentally, Wikipedia gives a different calculation for the coordinate transformation. I like to avoid trig where possible).

Consider the following 9 circles Points A,B,C and distances d1, d2, d3
  • Center of A, radius d1
  • Center of A, radius d2
  • Center of A, radius d3
  • Center of B, radius d1
  • Center of B, radius d2
  • Center of B, radius d3
  • Center of C, radius d1
  • Center of C, radius d2
  • Center of C, radius d3
These are your possible circles. Now we can cull these, because we know if d1 is used on A, it won't be used on B.
This makes your possible entries, where A1 means circle with center A and radius D1:
  • {A1, B2, C3}
  • {A1, B3, C2}
  • {A2, B1, C3}
  • {A2, B3, C1}
  • {A3, B1, C2}
  • {A3, B2, C1}
You should be able to convert the lat/long to X,Y,Z knowing the radius of the earth, and the distances from the curved distance along the earths crust to the straight distance, and from there you can see which of them intersect at a common point. Remember to allow for small margins of error due to float imperfection.
source: http://stackoverflow.com/questions/2813615/trilateration-using-3-latitude-and-longitude-points-and-3-distances

You can compute the intersection using spatial APIs. Examples:
  • Postgis (St_Intersection, St_buffer functions). 
  • GeoScript
  • Java Topology Suite
  • NET Topology Suite
  • GEOS

Or do the math :)

The math on the wikipedia page isn't too bad, just need to covert your geodetic coordinates to the cartesian ECEF, which can be found here. the a/x +h terms can be replaced by the authalic sphere radius, if you aren't using an ellipsoid.
I plotted the points in ArcMap, buffered them to the distances specified, ran intersect to on the buffers, and then captured the vertex of intersection to get the solutions. Your proposed output is the point in green. I calculated the value in the callout box, which is about 3 meters of what ArcMap gave for solution derived from the intersect.

Python:
from math import *
from numpy import *

#assuming elevation = 0
earthR = 6371
LatA = 37.418436
LonA = -121.963477
DistA = 0.265710701754
LatB = 37.417243
LonB = -121.961889
DistB = 0.234592423446
LatC = 37.418692
LonC = -121.960194
DistC = 0.0548954278262

#using authalic sphere
#if using an ellipsoid this step is slightly different
#Convert geodetic Lat/Long to ECEF xyz
#   1. Convert Lat/Long to radians
#   2. Convert Lat/Long(radians) to ECEF
xA = earthR *(math.cos(math.radians(LatA)) * math.cos(math.radians(LonA)))
yA = earthR *(math.cos(math.radians(LatA)) * math.sin(math.radians(LonA)))
zA = earthR *(math.sin(math.radians(LatA)))

xB = earthR *(math.cos(math.radians(LatB)) * math.cos(math.radians(LonB)))
yB = earthR *(math.cos(math.radians(LatB)) * math.sin(math.radians(LonB)))
zB = earthR *(math.sin(math.radians(LatB)))

xC = earthR *(math.cos(math.radians(LatC)) * math.cos(math.radians(LonC)))
yC = earthR *(math.cos(math.radians(LatC)) * math.sin(math.radians(LonC)))
zC = earthR *(math.sin(math.radians(LatC)))

P1 = array([xA, yA, zA])
P2 = array([xB, yB, zB])
P3 = array([xC, yC, zC])

#from wikipedia
#transform to get circle 1 at origin
#transform to get circle 2 on x axis
ex = (P2 - P1)/(numpy.linalg.norm(P2 - P1))
i = dot(ex, P3 - P1)
ey = (P3 - P1 - i*ex)/(numpy.linalg.norm(P3 - P1 - i*ex))
ez = numpy.cross(ex,ey)
d = numpy.linalg.norm(P2 - P1)
j = dot(ey, P3 - P1)

#from wikipedia
#plug and chug using above values
x = (pow(DistA,2) - pow(DistB,2) + pow(d,2))/(2*d)
y = ((pow(DistA,2) - pow(DistC,2) + pow(i,2) + pow(j,2))/(2*j)) - ((i/j)*x)

# only one case shown here
z = sqrt(pow(DistA,2) - pow(x,2) - pow(y,2))

#triPt is an array with ECEF x,y,z of trilateration point
triPt = P1 + x*ex + y*ey + z*ez

#convert back to lat/long from ECEF
#convert to degrees
lat = math.degrees(math.asin(triPt[2] / earthR))
lon = math.degrees(math.atan2(triPt[1],triPt[0]))

print lat, lon`

2 comentarios:

  1. Hey man !
    I'm actually trying to do the same BUT i want to measure the RSSI on the Pi not on the Smartphone. Did you ever try this ? I'm getting strange RSSI values. From +10 to -25. Also they do not really react on movements. For example: if I'm 1 meter away its rssi=0 but for 5 meters its 0 as well. Any ideas ?

    ResponderEliminar
  2. hai
    can i get the library for numpy in javascript
    i want to write the above code in javascript, if anyone did please share with me,

    ResponderEliminar