1 #include <marnav/geo/cpa.hpp>
2 #include <marnav/math/vector.hpp>
8 /// @brief Computes the CPA (closest point of approach)
9 /// and TCPA (time to closest point of approach).
11 /// @param[in] vessel1 Telemetry about vessel 1.
12 /// @param[in] vessel2 Telemetry about vessel 2.
13 /// @return Position of the two vessels at the closest approach
14 /// and the time until the closest approach is reached. Also included
15 /// in the result, does a CPA exist or not. If both vessels
16 /// go in the same direction and are parallel to eachother,
17 /// no CPA exists at all.
18 /// - Position of vessel 1 at closest point of approach
19 /// - Position of vessel 2 at closest point of approach
21 /// - CPA exists (true) or not (false)
23 /// @note This function calculates the CPA/TCPA on a plane (not sphere, not geoid).
24 /// This is precise enough for vessels in intresting vincinity. The farther away
25 /// the vessels are, the less precise the calculation gets.
29 /// Position of vessels and in function of time: @f{eqnarray*}{
30 /// V_1(t) &=& V_1(0) + t \cdot \vec{u} \\
31 /// V_2(t) &=& V_2(0) + t \cdot \vec{v}
33 /// with polar coordinates @f{eqnarray*}{
34 /// \vec{u} = (sog_1, \phi cog_1) \\
35 /// \vec{v} = (sog_2, \phi cog_2)
38 /// - @f${sog}@f$ : speed over ground
39 /// - @f${cog}@f$ : course over ground in degrees
41 /// The distance between the two vessels as function of time: @f[
42 /// \vec{d}_t = V_1(t) - V_2(t)
44 /// with @f{eqnarray*}{
45 /// \vec{d}_0 &=& V_1(0) - V_2(0) \\
46 /// \vec{d}_t &=& V_1(t) - V_2(t) = V_1(0) + t \cdot \vec{u} - V_2(0) + t \cdot \vec{v} \\
47 /// &=& V_1(0) - V_2(0) + t \cdot \left( \vec{u} - \vec{v} \right) \\
48 /// &=& \vec{d}_0 + t \cdot \left( \vec{u} - \vec{v} \right)
50 /// Closest point of approach is reached if the distance @f$\vec{d}_t@f$ is minimal:
52 /// || \vec{d}_t ||^2 &=& \vec{d}_t \cdot \vec{d}_t \\
53 /// &=& \left( \vec{d}_0 + t \cdot \left( \vec{u} - \vec{v} \right) \right)^2 \\
54 /// &=& {\vec{d}_0}^2 + 2\cdot\vec{d}_0\cdot t\cdot\left(\vec{u}-\vec{v}\right)
55 /// + t^2\cdot\left(\vec{u}-\vec{v}\right)^2
57 /// The minimum is reached, when the first derivation becomes zero. The derivation is:
59 /// \frac{\partial}{\partial t}|| \vec{d}_t ||^2 &=&
60 /// 2\cdot\vec{d}_0\cdot\left(\vec{u}-\vec{v}\right)
61 /// + 2\cdot t\cdot\left(\vec{u}-\vec{v}\right)^2
63 /// Solving for @f$t@f$:
65 /// 0 &=& 2\cdot\vec{d}_0\cdot\left(\vec{u}-\vec{v}\right)
66 /// + 2\cdot t\cdot\left(\vec{u}-\vec{v}\right)^2 \\
67 /// -2\cdot\vec{d}_0\cdot\left(\vec{u}-\vec{v}\right) &=& 2\cdot t\cdot\left(\vec{u}-\vec{v}\right)^2 \\
68 /// \frac{-2\cdot\vec{d}_0\cdot\left(\vec{u}-\vec{v}\right)}{2\cdot\left(\vec{u}-\vec{v}\right)^2} &=& t \\
69 /// t_{CPA} &=& \frac{-\vec{d}_0\cdot\left(\vec{u}-\vec{v}\right)}{\left(\vec{u}-\vec{v}\right)^2}
71 /// If the denominator @f$\left(\vec{u}-\vec{v}\right)^2@f$ is zero, there will never be a CPA.
72 /// If @f$t_{CPA}@f$ is less than zero, it means CPA was reached in the past.
74 /// The location of the vessels at closest approach:
76 /// V_1(t_{CPA}) &=& V_1(0) + t_{CPA} \cdot \vec{u} \\
77 /// V_2(t_{CPA}) &=& V_2(0) + t_{CPA} \cdot \vec{v}
82 /// const vessel vessel1 = {{0.0, 1.0}, 1.0, 90.0}; // being west, going east with 1kn
83 /// const vessel vessel2 = {{-1.0, 0.0}, 1.0, 0.0}; // being south, going north with 1k
87 /// std::chrono::seconds t_cpa;
89 /// std::tie(p1, p2, t_cpa, cpa_exists) = cpa(vessel1, vessel2);
92 /// const double d = abs(distance_sphere(p1, p2));
93 /// if ((d < 3.0 * 1852.0) || (t_cpa < std::chrono::minutes{10})) {
99 std::tuple
<position
, position
, std::chrono::seconds
, bool> cpa(
100 const vessel
& vessel1
, const vessel
& vessel2
)
104 const vec2 v1_0
{-vessel1
.pos
.lon(), vessel1
.pos
.lat()};
105 const vec2 v2_0
{-vessel2
.pos
.lon(), vessel2
.pos
.lat()};
107 const vec2 u
= vec2::make_from_polar(vessel1
.sog
, 90.0 - vessel1
.cog
);
108 const vec2 v
= vec2::make_from_polar(vessel2
.sog
, 90.0 - vessel2
.cog
);
110 const vec2 d0
= v1_0
- v2_0
;
111 const vec2 t
= u
- v
;
112 const double den
= t
* t
;
113 if (std::abs(den
) < 1e-7)
114 return std::make_tuple
<position
, position
, std::chrono::seconds
>(
115 position
{0.0, 0.0}, position
{0.0, 0.0}, std::chrono::seconds
{0}, false);
117 const double t_cpa
= (-1.0 * (d0
* t
)) / den
;
119 const vec2 v1_t
= v1_0
+ t_cpa
* u
;
120 const vec2 v2_t
= v2_0
+ t_cpa
* v
;
122 // we need to convert the time, because the units were nautical miles and knots.
123 // there are 60nm per degree and 1kn is 1nm per hour or 3600 seconds.
124 const std::chrono::duration
<double> t_cpa_seconds
{t_cpa
* 60 * 3600};
126 return std::make_tuple
<position
, position
, std::chrono::seconds
>(
127 position
{v1_t
[1], -v1_t
[0]}, position
{v2_t
[1], -v2_t
[0]},
128 std::chrono::duration_cast
<std::chrono::seconds
>(t_cpa_seconds
), true);