Skip to content

Instantly share code, notes, and snippets.

@richard-flosi
Last active April 12, 2021 04:09
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save richard-flosi/8c4ade400188d1c91e8964ff3211d091 to your computer and use it in GitHub Desktop.
Save richard-flosi/8c4ade400188d1c91e8964ff3211d091 to your computer and use it in GitHub Desktop.
JavaScript Trilateration
// Created by Derrick Cohodas (dav-)
// Based on the Python example by StackExchange user wwnick from http://gis.stackexchange.com/a/415/41129
// Requires the Mathjs library - http://mathjs.org/
var math = require('mathjs')
/**
* Represents a coordinate with a distance
* @param {Number} lat Latitude
* @param {Number} lon Longitude
* @param {Number} dist Distance from [lat, lon] to some point in kilometers
*/
var Beacon = function(lat, lon, dist) {
this.lat = lat
this.lon = lon
this.dist = dist
return this
}
/**
* Perform a trilateration calculation to determine a location
* based on 3 beacons and their respective distances (in kilometers) to the desired point.
*
* @param {Array} beacons Array of 3 Beacon objects
* @return {Array} Array of the format [latitude, longitude]
*/
var trilaterate = function(beacons) {
// #assuming elevation = 0
var earthR = 6371e3
, rad = function(deg) {
return deg * (math.pi/180)
}
, deg = function(rad) {
return rad * (180/math.pi)
}
// #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
var P1 = [ earthR *(math.cos(rad(beacons[0].lat)) * math.cos(rad(beacons[0].lon)))
, earthR *(math.cos(rad(beacons[0].lat)) * math.sin(rad(beacons[0].lon)))
, earthR *(math.sin(rad(beacons[0].lat)))
]
var P2 = [ earthR *(math.cos(rad(beacons[1].lat)) * math.cos(rad(beacons[1].lon)))
, earthR *(math.cos(rad(beacons[1].lat)) * math.sin(rad(beacons[1].lon)))
, earthR *(math.sin(rad(beacons[1].lat)))
]
var P3 = [ earthR *(math.cos(rad(beacons[2].lat)) * math.cos(rad(beacons[2].lon)))
, earthR *(math.cos(rad(beacons[2].lat)) * math.sin(rad(beacons[2].lon)))
, earthR *(math.sin(rad(beacons[2].lat)))
]
// #from wikipedia
// #transform to get circle 1 at origin
// #transform to get circle 2 on x axis
var ex = math.divide(math.subtract(P2, P1), math.norm( math.subtract(P2, P1) ))
var i = math.dot(ex, math.subtract(P3, P1) )
var ey = math.divide(
math.subtract(
math.subtract(P3, P1),
math.multiply(i, ex)
),
math.norm(
math.subtract(
math.subtract(P3, P1),
math.multiply(i, ex)
)
)
)
var ez = math.cross(ex, ey)
var d = math.norm(math.subtract(P2, P1))
var j = math.dot(ey, math.subtract(P3, P1))
// #from wikipedia
// #plug and chug using above values
var x = (math.pow(beacons[0].dist, 2) - math.pow(beacons[1].dist,2) + math.pow(d,2))/(2*d)
var y = ((math.pow(beacons[0].dist, 2) - math.pow(beacons[2].dist,2) + math.pow(i,2) + math.pow(j,2))/(2*j)) - ((i/j)*x)
// # only one case shown here
//
// I was having problems with the number in the radical being negative,
// so I took the absolute value. Not sure if this is always going to work
var z = math.sqrt( math.abs(math.pow(beacons[0].dist, 2) - math.pow(x, 2) - math.pow(y, 2)) )
// #triPt is an array with ECEF x,y,z of trilateration point
var triPt = math.add(
math.add(
math.add(P1,
math.multiply(x, ex)
),
math.multiply(y, ey)
),
math.multiply(z, ez)
)
// #convert back to lat/long from ECEF
// #convert to degrees
var lat = deg(math.asin(math.divide(triPt[2], earthR)))
var lon = deg(math.atan2(triPt[1], triPt[0]))
return [lat, lon]
}
var beacons = [ new Beacon(35.000000, -120.000000, 189.419265289145)
, new Beacon(35.000005, -120.000010, 189.420325082156)
, new Beacon(35.000000, -120.000020, 189.420689733286)
]
console.log(trilaterate(beacons))
@cfh463
Copy link

cfh463 commented Mar 27, 2018

Did you have much luck finding a solution that works well to calculate trilateration in JS

@seco35
Copy link

seco35 commented Oct 4, 2018

We'd used this one, works perfect. You've an error with the earth radius though. It's in kilometers, it should be in meters. Instead of using var earthR = 6371 on line 32 use -> var earthR = 6371e3
and it works like charm!

Thank you so much for sharing this. It saved us a lot of work :)

@richard-flosi
Copy link
Author

@seco35 Thanks! I've updated line 32 accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment