/* @file Trilateration.java
*
* @author marco corvi
* @date nov 2016
*
* @brief TopoDroid centerline computation: trilateration
* --------------------------------------------------------
* Copyright This sowftare is distributed under GPL-3.0 or later
* See the file COPYING.
* --------------------------------------------------------
*/
package com.topodroid.DistoX;
import java.util.ArrayList;
import java.util.List;
public class Trilateration
{
ArrayList<TrilaterationLeg> legs;
ArrayList<TrilaterationPoint> points;
double err0;
int iter;
TrilaterationPoint getPoint( String n )
{
for ( TrilaterationPoint p : points ) if ( n.equals(p.name) ) return p;
return null;
}
Trilateration( TriCluster cl )
{
legs = new ArrayList<TrilaterationLeg>();
points = new ArrayList<TrilaterationPoint>();
// populate
for ( String n : cl.stations ) {
points.add( new TrilaterationPoint( n ) );
}
for ( TriShot sh : cl.shots ) {
TrilaterationPoint p1 = getPoint( sh.from );
TrilaterationPoint p2 = getPoint( sh.to );
legs.add( new TrilaterationLeg( sh, p1, p2 ) );
}
// initialize points
initialize();
// and minimize
minimize( 0.01, 0.20, 10000 );
}
void initialize()
{
double d, a;
boolean repeat = true;
legs.get(0).pi.used = true;
while ( repeat ) {
repeat = false;
for ( TrilaterationLeg leg : legs ) {
if ( ! leg.used ) {
TrilaterationPoint pi = leg.pi;
TrilaterationPoint pj = leg.pj;
d = leg.d;
a = leg.a * Math.PI / 180.0;
if ( pi.used && ! pj.used ) {
pj.used = true;
pj.x = pi.x + d * Math.sin( a );
pj.y = pi.y + d * Math.cos( a );
leg.used = true;
repeat = true;
} else if ( pj.used && ! pi.used ) {
pi.used = true;
pi.x = pj.x - d * Math.sin( a );
pi.y = pj.y - d * Math.cos( a );
leg.used = true;
repeat = true;
}
}
}
}
}
private void clearPointsDelta()
{
for ( TrilaterationPoint p : points ) { p.dx = p.dy = 0; }
}
private void addPointsDelta( double delta )
{
for ( TrilaterationPoint p : points ) {
p.x += p.dx * delta;
p.y += p.dy * delta;
}
}
private double distance1( TrilaterationPoint p1, TrilaterationPoint p2 )
{
return Math.sqrt( (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) );
}
private double computeError2( )
{
double error = 0;
for ( TrilaterationLeg l : legs ) {
double d = ( distance1( l.pi, l.pj ) - l.d );
error += d * d;
}
return error;
}
// eps: error for one point [m] 0.001
// delta: initial delta [m] 0.10
// iter_max: max iterations 100000
void minimize( double eps, double delta, int iter_max )
{
eps *= points.size(); // 1 mm per point
err0 = computeError2();
int n_pts = points.size();
for ( iter =0 ; iter < iter_max; ++ iter ) {
double err3, e;
for ( int m=1; m<n_pts; ++m ) { // compute derivatives D Error / DPx
TrilaterationPoint p = points.get( m );
if ( m > 1 ) {
p.x += delta;
e = err0 - computeError2();
p.x -= delta;
p.dx = e / ( 1 + e * e );
}
p.y += delta;
e = err0 - computeError2();
p.y -= delta;
p.dy = e / ( 1 + e * e );
}
addPointsDelta( delta );
err3 = computeError2();
if ( err3 >= err0 ) {
addPointsDelta( -delta );
delta = delta / 2;
} else {
err0 = err3;
}
clearPointsDelta();
if ( err0 < eps || delta < 0.00000001 ) break;
}
}
}