# Closest Point of Approach (CPA)

This form calculates the Closest Point of Approach or CPA of two vessels.
```
Other vessel speed Kts
Other vessel course
Other vessel range Nm
Bearing from you to other vessel

CPA calculation
If the resulting CPA is zero, that
means a collision will occur.
www.chartsandtides.co.uk

/* all speeds in knots, courses & bearings in degrees, range in nmi */

function CPABearing(r1,b1,r2,b2)
{
var DTR = Math.PI / 180;
return Math.atan((r2*Math.cos(DTR*b2) - r1*Math.cos(DTR*b1))/
(r1*Math.sin(DTR*b1) - r2*Math.sin(DTR*b2)));
}

function Dist(r1,a1,r2,a2)
{
var DTR = Math.PI / 180;
var x1,x2,y1,y2;
x1 = r1 * Math.cos(a1*DTR);
x2 = r2 * Math.cos(a2*DTR);
y1 = r1 * Math.sin(a1*DTR);
y2 = r2 * Math.sin(a2*DTR);
return Math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
}

function CalcRB(f)
{
var DTR = Math.PI / 180;
var bearing = CPABearing(f.range1.value,f.bearing1.value,f.range2.value,f.bearing2.value);
var range = f.range2.value * Math.cos(bearing - f.bearing2.value*DTR);
var distance = Dist(f.range1.value,f.bearing1.value,f.range2.value,f.bearing2.value);
var speed = distance/(f.time2.value - f.time1.value)*60;
f.bearing.value = bearing + " degrees";
f.range.value = range + " nmi";
f.speed.value = speed + " knots";
}

/* this func ported from Java code at http://www.ai.sri.com/geovrml/rhumbline/ */
function CPA(speed1,course1,speed2,course2,range,bearing)
{
var DTR = Math.PI / 180;
var x,y,xVel,yVel,dot,a,b,cpa;

x = range * Math.cos(DTR*bearing);
y = range * Math.sin(DTR*bearing);
xVel = speed2 * Math.cos(DTR*course2) - speed1 * Math.cos(DTR*course1);
yVel = speed2 * Math.sin(DTR*course2) - speed1 * Math.sin(DTR*course1);
dot = x * xVel + y * yVel;
if (dot >= 0.0) return "No further closure.";
a = xVel * xVel + yVel * yVel;
b = 2 * dot;
if (Math.abs(a) < 0.0001 || Math.abs(b) > 24 * Math.abs(a)) return "CPA > 12";
cpa = range * range - ((b*b)/(4*a));
if (cpa <= 0.0) return "0 nm in " + 60*(-b/(2*a)) + " minutes";
cpa = Math.sqrt(cpa);
return "CPA = " + cpa + " nm in " + 60*(-b/(2*a)) + " minutes";
}

function CalcSC(form)
{
form.cpa.value = CPA(form.speed1.value,form.course1.value,
form.speed2.value,form.course2.value,
form.range.value,form.bearing.value);
}

```