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`

jueves, 28 de noviembre de 2013

Pi + arduino + ifttt

source: http://dparkinson.blogspot.com/2013/05/using-raspberrypi-arduino-and-ifttt-to.html



With the particularly wet weather we’re having this summer, my garage has developed a flood.

 

There is what I believe to be a soakaway drain  which is used to drain the water from the garage roof when it’s raining.
Over the last few weeks, I’ve walked into the garage to see quite a flood around the drain without any indication of how it it is happening, even whilst it’s still raining and water is going into the drain, it’s not clear how it is flooding.
I have therefore decided to set up the Arduino as a water sensor and hooked that up to the Raspberry Pi to provide the notifications, since I don’t have a wireless shield for my Arduino. This way, I can hopefully get an early warning of the flood without having to keep checking every few minutes.
This post just outlines how I’ve achieved that. Most of this is quite convoluted and probably overkill for the requirement, but I have these items around and it's all pretty simple to do.

Overall Architecture

 

At a high level, the Arduino senses the flood and the raspberry pi sends me a tweet. In addition, since I wouldn’t necessarily see the tweet immediately, I’ve also set it up to email “If This Then That”  which will in turn send me a text message (sms) that I’ll get immediately.

Arduino as a Water Sensor

 

Strictly speaking, this isn’t really a sensor. I’m essentially using the water to complete a circuit between a couple of nails and when I get a circuit, I’m reading the analog input to determine whether there is a flood or not.
The analog input will give me a value of 1023 for a direct connection between the two nails and a value of 0 when no circuit is made. When water completes the circuit, it will provide a value of between 400 and 700 depending on how far apart the nails are.
The circuit and the code for this is therefore very simple, as outlined below.
 

int moistureSensor = 0; //analog 0
int ledPin = 9;
int moistureValue;

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  moistureValue = analogRead(moistureSensor);
  if ((moistureValue < 700) && (moistureValue > 400)) {
    analogWrite(ledPin, 255); 
    Serial.println("Moisture Detected");
  } else {
    analogWrite(ledPin, 0);
  }
  delay(500);
 
}
This will also light an LED on pin 9 as a visual indication too.

Setting up a Twitter for notifications

To get the notifications, I’ve set up a twitter account for the house which can then tweet that a flood has occurred.
You could just use any twitter account, the important thing is to set up an “application” in twitter so that you can use Python on the Raspberry Pi to tweet.
Go to dev.twitter.com/apps  and log in with your account. Once logged in, you can click the “Create a new application” button and follow the steps.
 
Once you’ve created an application, you then need to get the consumer key, the consumer secret and an access token secret from the application details tab which you’ll need for the Python script coming up next. You create the access token by clicking the “Create my access token” button.
They have lots of rules too, so please make sure you follow them!

Setting up the Raspberry Pi

At this point, I’m assuming you already have a working Raspberry Pi, so I’ll just outline the main steps I performed to get up and running.
So that I could load the arduino sketches on from the Raspberry Pi, I installed the arduino package and added the pi user to the tty and dial out groups for access to the serial port:
sudo apt-get update
sudo apt-get install arduino
sudo usermod -a -G tty pi
sudo usermod -a -G dialout pi
Then I added the serial package for accessing the Arduino via serial port in the script:
sudo apt-get install python-serial python3-serial
To enable the twitter integration, I’m using the python-twitter  wrapper. To get this running I installed pip for the python package management:
sudo apt-get install python-setuptools 
sudo easy_install pip
Then installed the appropriate components:
sudo pip install simplejson
sudo pip install OAuth2
sudo pip install HTTPLib2
sudo pip install python-twitter
Then I created a short python script to read the serial port and detect the “Moisture Detected” string I’m outputting from the Arduino script. It probably isn’t that robust, but should serve the purpose for this.
def SendTweet():
    '''
    Method to send a tweet
    '''
    print "Water has been detected -- sending tweet"
    api = twitter.Api(consumer_key=my_consumer_key,
        consumer_secret=my_consumer_secret,
        access_token_key=my_access_token_key,
        access_token_secret=my_access_token_secret)
    statustext = 'Test Tweet : Water Detected on %s' % time.asctime()
    status = api.PostUpdate(statustext)
    print status.text

import serial
import time
import twitter

my_consumer_key = 'YOUR_CONSUMER_KEY'
my_consumer_secret = 'YOUR_CONSUMER_SECRET'
my_access_token_key = 'YOUR_ACCESS_TOKEN_KEY'
my_access_token_secret = 'YOUR_ACCESS_TOKEN_SECRET'

port = "/dev/ttyACM0"
ser = serial.Serial(port, 9600)
ser.flushInput()

print "Listening to arduino, waiting for detection..."
timer = time.time()
tweetdelay = 120 # in seconds

while True:
    if (ser.inWaiting() > 0):
        input = ser.readline()
        if "Moisture Detected" in input:
            if ((time.time() - timer) > tweetdelay):
               timer = time.time() #reset timer ready for next loop
               SendTweet()
        elif ((time.time() - timer) > tweetdelay):
            timer = time.time()

Adding SMS notifications

At the moment, IFTTT doesn’t have any triggers for Twitter, so in order to get an SMS notification, I can send an email from my google account to IFTTT which in turn will send me a text message.
To do this, I just added the following to the SendTweet method:
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(username, password)
server.sendmail(fromaddr, toaddr, msg)
server.quit()
And the following to the main script: 
fromaddr = 'YOUR_GMAIL_ADDRESS_HERE'
toaddr = 'trigger@ifttt.com'
header = 'To:' + toaddr + '\n' + 'From: fromaddr' + '\n' + 'Subject:Garage is flooded. \n'
msg = header + 'Garage is flooded'
username = 'YOUR_GMAIL_ACCOUNT@gmail.com'
password = 'YOUR_GMAIL_PASSWORD'
Of note is that the email wasn't accepted without the "From:" in the header, which makes sense I guess as this is what IFTTT is checking for I suppose.

 
That's it.
Now, if the garage floods, I'll be able to get there immediately to see how on earth it's happening.  As a follow up, I could also incorporate the motion sensor that I've posted about before, along with a web cam in case I'm not in the house somewhere.

Github Node.js + BLE + Pi


- Node.js wiring tool: http://nodered.org/

- GPIO Raspberry Pi: http://wiringpi.com/

- https://sourcegraph.com/sandeepmistry

JavaScript403 ✱12
JavaScript255 
JavaScript245 
JavaScript92 ✱4
JavaScript91 
JavaScript72 
JavaScript70 
JavaScript68 
JavaScript56 
JavaScript54 
JavaScript36 
JavaScript32 
C++
C++
C++
JavaScript
Objective-C
JavaScript
C++
Objective-C
Arduino
C++