Bilateration with iBeacons - ios

I am trying to use multiple iBeacons to track the user's location in iOS. I know this can be done (somewhat) using 3 beacons and trilateration, but I would like to do it with two (bilateration). I know that I will likely end up with two answers. Does anyone know of a simple way to accomplish this, given the (x,y) location of the beacons (relative to the room), and an averaged RSSI from each beacon?
I have this code for trilateration that I altered to objective-c from javascript:
- (CGPoint)getTrilaterationWithBeacon1:(BBBeacon *)beacon1 Beacon2:(BBBeacon *)beacon2 Beacon3:(BBBeacon *)beacon3 {
float xa = beacon1.x;
float ya = beacon1.y;
float xb = beacon2.x;
float yb = beacon2.y;
float xc = beacon3.x;
float yc = beacon3.y;
float ra = beacon1.distance;
float rb = beacon2.distance;
float rc = beacon3.distance;
float S = (pow(xc, 2.) - pow(xb, 2.) + pow(yc, 2.) - pow(yb, 2.) + pow(rb, 2.) - pow(rc, 2.)) / 2.0;
float T = (pow(xa, 2.) - pow(xb, 2.) + pow(ya, 2.) - pow(yb, 2.) + pow(rb, 2.) - pow(ra, 2.)) / 2.0;
float y = ((T * (xb - xc)) - (S * (xb - xa))) / (((ya - yb) * (xb - xc)) - ((yc - yb) * (xb - xa)));
float x = ((y * (ya - yb)) - T) / (xb - xa);
CGPoint point = CGPointMake(x, y);
return point;
}

So this is the code I ended up using, thanks to ChuckCottrill's suggestion that I look for a formula to calculate intersection of two circles. It is modified from a C version I found online here: http://paulbourke.net/geometry/circlesphere/tvoght.c
The results are somewhat inconsistent due to the inconsistency of the RSSI values returned from the iBeacons.
I will still need to add code to select the correct point somehow (it gives two results).
- (CGPoint)getBilaterationWithBeacon1:(BBBeacon *)beacon1 Beacon2:(BBBeacon *)beacon2 {
float x0 = beacon1.locationX;
float y0 = beacon1.locationY;
float r0 = beacon1.filteredDistance;
float x1 = beacon2.locationX;
float y1 = beacon2.locationY;
float r1 = beacon2.filteredDistance;
float a, dx, dy, d, h, rx, ry;
float x2, y2;
/* dx and dy are the vertical and horizontal distances between
* the circle centers.
*/
dx = x1 - x0;
dy = y1 - y0;
/* Determine the straight-line distance between the centers. */
d = sqrt((dy*dy) + (dx*dx));
/* Check for solvability. */
if (d > (r0 + r1)) {
/* no solution. circles do not intersect. */
return CGPointMake(-1, -1);
}
if (d < abs(r0 - r1)) {
/* no solution. one circle is contained in the other */
return CGPointMake(-1, -1);
}
/* 'point 2' is the point where the line through the circle
* intersection points crosses the line between the circle
* centers.
*/
/* Determine the distance from point 0 to point 2. */
a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ;
/* Determine the coordinates of point 2. */
x2 = x0 + (dx * a/d);
y2 = y0 + (dy * a/d);
/* Determine the distance from point 2 to either of the
* intersection points.
*/
h = sqrt((r0*r0) - (a*a));
/* Now determine the offsets of the intersection points from
* point 2.
*/
rx = -dy * (h/d);
ry = dx * (h/d);
/* Determine the absolute intersection points. */
float xi = x2 + rx;
float xi_prime = x2 - rx;
float yi = y2 + ry;
float yi_prime = y2 - ry;
CGPoint point1 = CGPointMake(xi, yi);
CGPoint point2 = CGPointMake(xi_prime, yi_prime);
//pick one
return point2;
}

Related

Pinching image on edges as a custom CIWarpKernel does not produces results as expected

I have developed a custom CIWarpKernel which is the transposition of my Numbers model design as follows:
The cell for positive y is: SIN($A2×PI()÷2)×(−COS(B$1×PI())×($A2+1)÷$W$3+SIN($A2 ×PI()÷2)) where $A2 is y, B$1 is x and $W$3 is the center stretching factor.
The code is as follows:
kernel vec2 panoramaDistortion (vec2 center) {
float pi = 3.141592653589793;
vec2 t = destCoord();
float x = t.x / center.x - 1.0; // x ∈ -1...1
float y = t.y / center.y - 1.0; // y ∈ -1...1
float rx = x;
float delta = 50.0;
float siny = sin(y * pi / 2.0);
// See my model in Numbers: Aladdin PanoramPinch CIKernel file where $a2 = x and b$1 = y
// sin($a2×pi()÷2)×(−cos(b$1×pi())×($a2+1)÷$w$3+sin($a2 ×pi()÷$w$5))
// sin(y×pi()÷2)×(−cos(x×pi())×(y+1)÷$w$3+sin(y×pi()÷2))
// sin($a22×pi()÷2)×(−cos(b$1×pi())×abs(1−$a22)÷$w$3−sin($a22 ×pi()÷$w$5))
float ry =
y >= 0.0 ?
siny * (-cos(x * pi) * (y + 1.0) / delta + siny)
: siny * (-cos(x * pi) * (1.0 - y) / delta - siny)
;
return vec2(center.x * (rx + 1.0), center.y * (ry + 1.0));
}
The issue I have is that, though the transposition is 100% exact, I don't get the same result I have in my model. See the resulting warping of tiled-image:
How it comes I have the dilatation at the center y=0 and at the upper- and bottom- sides?
For information, my tiled-image is as follows:

Convert cv::Vec4f line to cv::Vec2f

I have a pair of Cartesian coordinates that represent a line in an image. I would like to convert this line to polar form and draw it over the image.
e.g
cv::Vec4f line {10,20,60,70};
float x1 = line[0];
float y1 = line[1];
float x2 = line[2];
float y2 = line[3];
I want this line to be represented in cv::Vec2f form(rho,theta).
Taking care of rho & theta with all possible slopes.
Given are the image dimensions :: w and h;
w = image.cols
h = image.rows
How can I achieve this.
N.B: We can also assume that the line can be an extended one running across the image.
for (size_t i = 0; i < lines.size(); i++)
{
int x1 = lines[i][0];
int y1 = lines[i][1];
int x2 = lines[i][2];
int y2 = lines[i][3];
float d = sqrt(((y1-y2)*(y1-y2)) + ((x2-x1)*(x2-x1)) );
float rho = (y1*x2 - y2*x1)/d;
float theta = atan2(x2 - x1,y1-y2) ;
if(rho < 0){
theta *= -1;
rho *= -1;
}
linv2f.push_back(cv::Vec2f(rho,theta));
}
The above approach doesnt give me results when I plot the lines I dont get the lines that are overlapping their original vec4f form.
I use this to convert vec2f to vec4f for testing :
cv::Vec4f cvtVec2fLine(const cv::Vec2f& data, const cv::Mat& img)
{
float const rho = data[0];
float const theta = data[1];
cv::Point pt1,pt2;
if((theta < CV_PI/4. || theta > 3. * CV_PI/4.)){
pt1 = cv::Point(rho / std::cos(theta), 0);
pt2 = cv::Point( (rho - img.rows * std::sin(theta))/std::cos(theta), img.rows);
}else {
pt1 = cv::Point(0, rho / std::sin(theta));
pt2 = cv::Point(img.cols, (rho - img.cols * std::cos(theta))/std::sin(theta));
}
cv::Vec4f l;
l[0] = pt1.x;
l[1] = pt1.y;
l[2] = pt2.x;
l[3] = pt2.y;
return l;
}
rho-theta equation has form
x * Cos(Theta) + y * Sin(Theta) - Rho = 0
We want to represent equation 'by two points' into rho-theta form (page 92 in pdf here). If we have
x * A + y * B - C = 0
and need coefficients in trigonometric form, we can divide all equation by magnitude of (A,B) coefficient vector.
D = Length(A,B) = Math.Hypot(A,B)
x * A/D + y * B/D - C/D = 0
note that (A/D)^2 + (B/D)^2 = 1 - basic trigonometric equality, so we can consider A/D and B/D as cosine and sine of some angle theta.
Your line equation is
(y-y1) * (x2-x1) - (x-x1) * (y2-y1) = 0
or
x * (y1-y2) + y * (x2-x1) - (y1 * x2 - y2 * x1) = 0
let
D = Sqrt((y1-y2)^2 + (x2-x1)^2)
so
Theta = ArcTan2(x2-x1, y1-y2)
Rho = (y1 * x2 - y2 * x1) / D
edited
If Rho is negative, change sign of Rho and shift Theta by Pi
Example:
x1=1,y1=0, x2=0,y2=1
Theta = atan2(-1,-1)=-3*Pi/4
D=Sqrt(2)
Rho=-Sqrt(2)/2 negative =>
Rho = Sqrt(2)/2
Theta = Pi/4
Back substitutuon - find points of intersection with axes
0 * Sqrt(2)/2 + y0 * Sqrt(2)/2 - Sqrt(2)/2 = 0
x=0 y=1
x0 * Sqrt(2)/2 + 0 * Sqrt(2)/2 - Sqrt(2)/2 = 0
x=1 y=0

Calculating the angle to a location

I have an image of an arrow that behaves like a compass to a specific location. Sometimes it works, and other times it's mirrored. So if I was facing east and the location is directly east of me, it should point up, but sometimes it points down.
#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI))
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)heading
{
// update direction of arrow
CGFloat degrees = [self p_calculateAngleBetween:_myLocation
and:_otherLocation];
CGFloat rads = (degrees - heading.trueHeading) * M_PI / 180;
CGAffineTransform tr = CGAffineTransformIdentity;
tr = CGAffineTransformConcat(tr, CGAffineTransformMakeRotation(rads) );
[_directionArrowView setTransform:tr];
}
-(CGFloat) p_calculateAngleBetween:(CLLocationCoordinate2D)coords0 and:(CLLocationCoordinate2D)coords1 {
double x = 0, y = 0 , deg = 0, deltaLon = 0;
deltaLon = coords1.longitude - coords0.longitude;
y = sin(deltaLon) * cos(coords1.latitude);
x = cos(coords0.latitude) * sin(coords1.latitude) - sin(coords0.latitude) * cos(coords1.latitude) * cos(deltaLon);
deg = RADIANS_TO_DEGREES(atan2(y, x));
if(deg < 0)
{
deg = -deg;
}
else
{
deg = 360 - deg;
}
return deg;
}
Is this the correct way to calculate my angle with another location? Or am I missing a step? Being the arrow points directly in the opposite direction sometimes, my assumption is it's an issue with my math.
To calculate radians from x & y:
double r = atan(y/x);
if (x<0)
r = M_PI + r;
else if (x>0 && y<0)
r = 2 * M_PI + r;
There is not issue of dividing by 0 when X is zero because the atan function handles this correctly:
If the argument is positive infinity (negative infinity), +pi/2 (-pi/2) is returned.

Calculate distance from a lat/lon coord to a street

I have the coordinates for a street, for example:
CLLocationCoordinate2D street[3];
street[0] = CLLocationCoordinate2DMake(-17.3521, 145.5898);
street[1] = CLLocationCoordinate2DMake(-17.3518, 145.5910);
street[2] = CLLocationCoordinate2DMake(-17.3515, 145.5917);
And a location that is fairly close to the street (about 60 meters):
CLLocationCoordinate2D location = CLLocationCoordinate2DMake(-17.3525, 145.5911);
How can I calculate the distance between the location and position along the street's path?
I'm not looking for the distance to the closest point in the array, I want the distance to the closest position in between the points.
EDIT
It's easier to describe my question with a picture:
street is the three red dots
location is the blue dot
I want to calculate the length of the yellow line in meters.
Take a look at this site: link.
It shows different types of distance measuring with latitude and longitude coordinates and even some code examples (in javascript).
If you have the find the crow distance between two locations, make CLLocation object of two coords, then
CLLocationDistance meters = [newLocation distanceFromLocation:oldLocation];
and if you have find the actual road distance divide the two coords to several coords in straight line and find the distance and add them up.
Here is my attempt to solve this, but I'm not sure if it's the best way?
// http://www.cprogramto.com/c-program-to-find-shortest-distance-between-point-and-line-segment/
double FindDistanceToSegment(double x1, double y1, double x2, double y2, double pointX, double pointY)
{
double diffX = x2 - x1;
float diffY = y2 - y1;
if ((diffX == 0) && (diffY == 0))
{
diffX = pointX - x1;
diffY = pointY - y1;
return sqrt(diffX * diffX + diffY * diffY);
}
float t = ((pointX - x1) * diffX + (pointY - y1) * diffY) / (diffX * diffX + diffY * diffY);
if (t < 0)
{
//point is nearest to the first point i.e x1 and y1
diffX = pointX - x1;
diffY = pointY - y1;
}
else if (t > 1)
{
//point is nearest to the end point i.e x2 and y2
diffX = pointX - x2;
diffY = pointY - y2;
}
else
{
//if perpendicular line intersect the line segment.
diffX = pointX - (x1 + t * diffX);
diffY = pointY - (y1 + t * diffY);
}
//returning shortest distance
return sqrt(diffX * diffX + diffY * diffY);
}
-
CLLocationCoordinate2D street[3];
street[0] = CLLocationCoordinate2DMake(-17.3521, 145.5898);
street[1] = CLLocationCoordinate2DMake(-17.3518, 145.5910);
street[2] = CLLocationCoordinate2DMake(-17.3515, 145.5917);
CLLocationCoordinate2D location = CLLocationCoordinate2DMake(-17.3525, 145.5911);
CLLocationDegrees distanceDegrees = CGFLOAT_MAX;
for (NSUInteger nodeIndex = 1; nodeIndex < 3; nodeIndex++) {
CLLocationCoordinate2D nodeCoord = street[nodeIndex];
CLLocationCoordinate2D prevNodeCoord = street[nodeIndex - 1];
CLLocationDegrees distanceToCurrent = FindDistanceToSegment(prevNodeCoord.longitude, prevNodeCoord.latitude, nodeCoord.longitude, nodeCoord.latitude, location.longitude, location.latitude);
if (distanceToCurrent < distanceDegrees)
distanceDegrees = distanceToCurrent;
}
CLLocationDistance distance = distanceDegrees * 111111; // 1.0 degree is approximately 111,111 meters
NSLog(#"%f", distance); // 78.15 meters

iOS Core Data To Query Against Geospatial Data Set

I'm using the Core Data Framework to manage a set of accounts which also include geospatial (GPS) coordinate data for each account. How can I query against this data based on position of the device to get a list of accounts within x feet and list them in order of distance?
to get you started, here's a method i use in my iOS app that returns the distance in meters between two CLLocationCoordinate2D locations, assuming the Google Spherical Mercator Projection (if you want to use another projection, you can specify the appropriate flattening ratio value (f) and semi-major axis value (a). if you want the forward and backward azimuth values between the coordinates, you can uncomment and return the faz and baz values along with the distance by defining your own struct. this method can be used to add the distance to each of your 'account' objects and the current location being reported by your CLLocationManager object, then you could easily sort and filter an array of account objects based on their distances.
based on code by Gerald Evenden located here: http://article.gmane.org/gmane.comp.gis.proj-4.devel/3478
#define PI 3.141592653589793238462643
#define EPS 5e-14
#define DEG_TO_RAD 0.0174532925199432958
// returns the geodesic distance in meters between two coordinates based on the google spherical mercator projection.
- (int) geodesicDistanceFromCoordinate: (CLLocationCoordinate2D) fromCoord toCoordinate: (CLLocationCoordinate2D) toCoord {
double c, d, e, r, x, y, sa, cx, cy, cz, sx, sy, c2a, cu1, cu2, su1, tu1, tu2, ts, phi1, lam1, phi2, lam2, f, baz, faz, s, a;
phi1 = fromCoord.latitude * DEG_TO_RAD;
lam1 = fromCoord.longitude * DEG_TO_RAD;
phi2 = toCoord.latitude * DEG_TO_RAD;
lam2 = toCoord.longitude * DEG_TO_RAD;
f = 0; //google's spherical mercator projection has no flattening
a = 6378137; //earth's axis in meters used in google's projection
r = 1. - f;
tu1 = r * tan(phi1);
tu2 = r * tan(phi2);
cu1 = 1. / sqrt(tu1 * tu1 + 1.);
su1 = cu1 * tu1;
cu2 = 1. / sqrt(tu2 * tu2 + 1.);
ts = cu1 * cu2;
baz = ts * tu2;
faz = baz * tu1;
x = lam2 - lam1;
do {
sx = sin(x);
cx = cos(x);
tu1 = cu2 * sx;
tu2 = baz - su1 * cu2 * cx;
sy = sqrt(tu1 * tu1 + tu2 * tu2);
cy = ts * cx + faz;
y = atan2(sy, cy);
sa = ts * sx / sy;
c2a = -sa * sa + 1.;
cz = faz + faz;
if (c2a > 0.)
cz = -cz / c2a + cy;
e = cz * cz * 2. - 1.;
c = ((c2a * -3. + 4.) * f + 4.) * c2a * f / 16.;
d = x;
x = ((e * cy * c + cz) * sy * c + y) * sa;
x = (1. - c) * x * f + lam2 - lam1;
} while (fabs(d - x) > EPS);
//forward azimuth faz = atan2(tu1, tu2);
//backward azimuth baz = atan2(cu1 * sx, baz * cx - su1 * cu2) + PI;
x = sqrt((1. / r / r - 1.) * c2a + 1.) + 1.;
x = (x - 2.) / x;
c = (x * x / 4. + 1.) / (1. - x);
d = (x * .375 * x - 1.) * x;
s = ((((sy * sy * 4. - 3.) * (1. - e - e) * cz * d / 6. - e * cy) * d / 4. + cz) * sy * d + y) * c * r;
return (int)(s * a);
}

Resources