4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "CirclingComputer.hpp"
25 #include "NMEA/MoreData.hpp"
26 #include "NMEA/Derived.hpp"
27 #include "Settings.hpp"
28 #include "Math/LowPassFilter.hpp"
29 #include "Util/Clamp.hpp"
31 static constexpr fixed
MIN_TURN_RATE(4);
32 static constexpr fixed
CRUISE_CLIMB_SWITCH(15);
33 static constexpr fixed
CLIMB_CRUISE_SWITCH(10);
36 CirclingComputer::Reset()
38 turn_rate_delta_time
.Reset();
39 turning_delta_time
.Reset();
40 percent_delta_time
.Reset();
46 CirclingComputer::ResetStats()
48 min_altitude
= fixed(0);
52 CirclingComputer::TurnRate(CirclingInfo
&circling_info
,
53 const NMEAInfo
&basic
,
54 const FlyingState
&flight
)
56 if (!basic
.time_available
|| !flight
.flying
) {
57 circling_info
.turn_rate
= fixed(0);
58 circling_info
.turn_rate_heading
= fixed(0);
59 circling_info
.turn_rate_smoothed
= fixed(0);
63 const fixed dt
= turn_rate_delta_time
.Update(basic
.time
,
64 fixed_third
, fixed(10));
66 circling_info
.turn_rate
= fixed(0);
67 circling_info
.turn_rate_heading
= fixed(0);
68 circling_info
.turn_rate_smoothed
= fixed(0);
73 circling_info
.turn_rate
=
74 (basic
.track
- last_track
).AsDelta().Degrees() / dt
;
75 circling_info
.turn_rate_heading
=
76 (basic
.attitude
.heading
- last_heading
).AsDelta().Degrees() / dt
;
78 // JMW limit rate to 50 deg per second otherwise a big spike
79 // will cause spurious lock on circling for a long time
80 fixed turn_rate
= Clamp(circling_info
.turn_rate
, fixed(-50), fixed(50));
82 // Make the turn rate more smooth using the LowPassFilter
83 turn_rate
= LowPassFilter(circling_info
.turn_rate_smoothed
,
84 turn_rate
, fixed(0.3));
85 circling_info
.turn_rate_smoothed
= turn_rate
;
88 last_track
= basic
.track
;
89 last_heading
= basic
.attitude
.heading
;
93 CirclingComputer::Turning(CirclingInfo
&circling_info
,
94 const MoreData
&basic
,
95 const FlyingState
&flight
,
96 const CirclingSettings
&settings
)
98 // You can't be circling unless you're flying
99 if (!basic
.time_available
|| !flight
.flying
)
102 const fixed dt
= turning_delta_time
.Update(basic
.time
,
107 circling_info
.turning
=
108 fabs(circling_info
.turn_rate_smoothed
) >= MIN_TURN_RATE
;
110 // Force cruise or climb mode if external device says so
111 bool force_cruise
= false;
112 bool force_circling
= false;
113 if (settings
.external_trigger_cruise_enabled
&& !basic
.gps
.replay
) {
114 switch (basic
.switch_state
.flight_mode
) {
115 case SwitchState::FlightMode::UNKNOWN
:
118 case SwitchState::FlightMode::CIRCLING
:
119 force_circling
= true;
122 case SwitchState::FlightMode::CRUISE
:
128 switch (circling_info
.turn_mode
) {
129 case CirclingMode::CRUISE
:
130 // If (in cruise mode and beginning of circling detected)
131 if (circling_info
.turning
|| force_circling
) {
132 // Remember the start values of the turn
133 turn_start_time
= basic
.time
;
134 turn_start_location
= basic
.location
;
135 turn_start_altitude
= basic
.nav_altitude
;
136 turn_start_energy_height
= basic
.energy_height
;
137 circling_info
.turn_mode
= CirclingMode::POSSIBLE_CLIMB
;
142 case CirclingMode::POSSIBLE_CLIMB
:
144 circling_info
.turn_mode
= CirclingMode::CRUISE
;
147 if (circling_info
.turning
|| force_circling
) {
148 if (((basic
.time
- turn_start_time
) > CRUISE_CLIMB_SWITCH
)
150 // yes, we are certain now that we are circling
151 circling_info
.circling
= true;
153 // JMW Transition to climb
154 circling_info
.turn_mode
= CirclingMode::CLIMB
;
156 // Remember the start values of the climbing period
157 circling_info
.climb_start_location
= turn_start_location
;
158 circling_info
.climb_start_altitude
= turn_start_altitude
;
159 circling_info
.climb_start_altitude_te
=
160 turn_start_altitude
+ turn_start_energy_height
;
161 circling_info
.climb_start_time
= turn_start_time
;
164 // nope, not turning, so go back to cruise
165 circling_info
.turn_mode
= CirclingMode::CRUISE
;
169 case CirclingMode::CLIMB
:
170 if (!circling_info
.turning
|| force_cruise
) {
171 // Remember the end values of the turn
172 turn_start_time
= basic
.time
;
173 turn_start_location
= basic
.location
;
174 turn_start_altitude
= basic
.nav_altitude
;
175 turn_start_energy_height
= basic
.energy_height
;
177 // JMW Transition to cruise, due to not properly turning
178 circling_info
.turn_mode
= CirclingMode::POSSIBLE_CRUISE
;
183 case CirclingMode::POSSIBLE_CRUISE
:
184 if (force_circling
) {
185 circling_info
.turn_mode
= CirclingMode::CLIMB
;
189 if (!circling_info
.turning
|| force_cruise
) {
190 if (basic
.time
- turn_start_time
> CLIMB_CRUISE_SWITCH
|| force_cruise
) {
191 // yes, we are certain now that we are cruising again
192 circling_info
.circling
= false;
194 // Transition to cruise
195 circling_info
.turn_mode
= CirclingMode::CRUISE
;
196 circling_info
.cruise_start_location
= turn_start_location
;
197 circling_info
.cruise_start_altitude
= turn_start_altitude
;
198 circling_info
.cruise_start_altitude_te
=
199 turn_start_altitude
+ turn_start_energy_height
;
200 circling_info
.cruise_start_time
= turn_start_time
;
203 // nope, we are circling again
204 // JMW Transition back to climb, because we are turning again
205 circling_info
.turn_mode
= CirclingMode::CLIMB
;
212 CirclingComputer::PercentCircling(const MoreData
&basic
,
213 CirclingInfo
&circling_info
)
215 if (!basic
.time_available
)
218 // JMW circling % only when really circling,
219 // to prevent bad stats due to flap switches and dolphin soaring
221 const fixed dt
= percent_delta_time
.Update(basic
.time
,
227 if (circling_info
.circling
&& circling_info
.turning
) {
228 // Add one second to the circling time
229 // timeCircling += (Basic->Time-LastTime);
230 circling_info
.time_climb
+= dt
;
232 // Add the Vario signal to the total climb height
233 circling_info
.total_height_gain
+= basic
.gps_vario
;
235 // Add one second to the cruise time
236 // timeCruising += (Basic->Time-LastTime);
237 circling_info
.time_cruise
+= dt
;
240 // Calculate the circling percentage
241 if (circling_info
.time_cruise
+ circling_info
.time_climb
> fixed(1))
242 circling_info
.circling_percentage
= 100 * circling_info
.time_climb
/
243 (circling_info
.time_cruise
+ circling_info
.time_climb
);
245 circling_info
.circling_percentage
= fixed(-1);
249 CirclingComputer::MaxHeightGain(const MoreData
&basic
,
250 const FlyingState
&flight
,
251 CirclingInfo
&circling_info
)
253 if (!basic
.NavAltitudeAvailable() || !flight
.flying
)
256 if (positive(min_altitude
)) {
257 fixed height_gain
= basic
.nav_altitude
- min_altitude
;
258 circling_info
.max_height_gain
=
259 std::max(height_gain
, circling_info
.max_height_gain
);
261 min_altitude
= basic
.nav_altitude
;
264 min_altitude
= std::min(basic
.nav_altitude
, min_altitude
);