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 /* This library was originally imported from Cumulus
25 http://kflog.org/cumulus/ */
27 #include "Wind/CirclingWind.hpp"
28 #include "Math/Constants.h"
29 #include "LogFile.hpp"
30 #include "NMEA/MoreData.hpp"
31 #include "NMEA/Derived.hpp"
41 Currently, the wind is being analyzed by finding the minimum and the maximum
42 groundspeeds measured while flying a circle. The direction of the wind is taken
43 to be the direction in wich the speed reaches it's maximum value, the speed
44 is half the difference between the maximum and the minimum speeds measured.
45 A quality parameter, based on the number of circles allready flown (the first
46 circles are taken to be less accurate) and the angle between the headings at
47 minimum and maximum speeds, is calculated in order to be able to weigh the
48 resulting measurement.
50 There are other options for determining the windspeed. You could for instance
51 add all the vectors in a circle, and take the resuling vector as the windspeed.
52 This is a more complex method, but because it is based on more heading/speed
53 measurements by the GPS, it is probably more accurate. If equiped with
54 instruments that pass along airspeed, the calculations can be compensated for
55 changes in airspeed, resulting in better measurements. We are now assuming
56 the pilot flies in perfect circles with constant airspeed, wich is of course
57 not a safe assumption.
58 The quality indication we are calculation can also be approched differently,
59 by calculating how constant the speed in the circle would be if corrected for
60 the windspeed we just derived. The more constant, the better. This is again
61 more CPU intensive, but may produce better results.
63 Some of the errors made here will be averaged-out by the WindStore, wich keeps
64 a number of windmeasurements and calculates a weighted average based on quality.
70 last_track_available
.Clear();
71 last_ground_speed_available
.Clear();
75 last_track
= Angle::Zero();
80 CirclingWind::NewSample(const MoreData
&info
)
83 // only work if we are in active mode
86 if (last_track_available
.FixTimeWarp(info
.track_available
) ||
87 last_ground_speed_available
.FixTimeWarp(info
.ground_speed_available
))
88 /* time warp: start from scratch */
91 if (!info
.track_available
.Modified(last_track_available
) ||
92 !info
.ground_speed_available
.Modified(last_ground_speed_available
))
93 /* no updated GPS fix */
96 last_track_available
= info
.track_available
;
97 last_ground_speed_available
= info
.ground_speed_available
;
102 int diff
= (int)(info
.track
- last_track
).AsDelta().AbsoluteDegrees();
104 last_track
= info
.track
;
106 const bool fullCircle
= circle_deg
>= 360;
111 circle_count
++; //increase the number of circles flown (used
112 //to determine the quality)
115 curVector
= Vector(SpeedVector(info
.track
, info
.ground_speed
));
117 if (!samples
.full()) {
118 Sample
&sample
= samples
.append();
119 sample
.v
= curVector
;
120 sample
.time
= info
.clock
;
121 sample
.mag
= info
.ground_speed
;
123 // TODO code: give error, too many wind samples
124 // or use circular buffer
127 if (first
|| (info
.ground_speed
< min_vector
.Magnitude()))
128 min_vector
= curVector
;
130 if (first
|| (info
.ground_speed
> max_vector
.Magnitude()))
131 max_vector
= curVector
;
134 if (fullCircle
) { //we have completed a full circle!
136 // calculate the wind for this circle, only if it is valid
139 // should set each vector to average
141 min_vector
= max_vector
= Vector((max_vector
.x
- min_vector
.x
) / 2,
142 (max_vector
.y
- min_vector
.y
) / 2);
154 CirclingWind::NewFlightMode(const DerivedInfo
&derived
)
156 // we are inactive by default
159 // reset the circlecounter for each flightmode
160 // change. The important thing to measure is the
161 // number of turns in this thermal only.
166 // we are not circling? Exit function!
167 if (!derived
.circling
)
170 // initialize analyser-parameters
177 CirclingWind::CalcWind()
182 // reject if average time step greater than 2.0 seconds
183 if ((samples
.last().time
- samples
[0].time
) / (samples
.size() - 1) > fixed(2))
188 for (unsigned i
= 0; i
< samples
.size(); i
++)
189 av
+= samples
[i
].mag
;
191 av
/= samples
.size();
193 // find zero time for times above average
196 fixed rthismax
= fixed(0);
197 fixed rthismin
= fixed(0);
201 for (unsigned j
= 0; j
< samples
.size(); j
++) {
204 for (unsigned i
= 0; i
< samples
.size(); i
++) {
208 ithis
= (i
+ j
) % samples
.size();
211 if (idiff
> samples
.size() / 2)
212 idiff
= samples
.size() - idiff
;
214 rthisp
+= (samples
[ithis
].mag
) * idiff
;
217 if ((rthisp
< rthismax
) || (jmax
== -1)) {
222 if ((rthisp
> rthismin
) || (jmin
== -1)) {
228 // jmax is the point where most wind samples are below
229 max_vector
= samples
[jmax
].v
;
231 // jmin is the point where most wind samples are above
232 min_vector
= samples
[jmin
].v
;
234 // attempt to fit cycloid
236 fixed mag
= Half(samples
[jmax
].mag
- samples
[jmin
].mag
);
237 fixed rthis
= fixed(0);
239 const Angle step
= Angle::FullCircle() / samples
.size();
240 Angle angle
= step
* jmax
;
241 for (unsigned i
= 0; i
< samples
.size(); i
++, angle
+= step
) {
242 const auto sc
= angle
.SinCos();
243 fixed wx
= sc
.second
, wy
= sc
.first
;
246 fixed cmag
= SmallHypot(wx
, wy
) - samples
[i
].mag
;
250 rthis
/= samples
.size();
256 quality
= 5 - iround(rthis
/ mag
* 3);
258 quality
= 5 - iround(rthis
);
260 if (circle_count
< 3)
262 if (circle_count
< 2)
264 if (circle_count
< 1)
267 quality
= min(quality
, 5); //5 is maximum quality, make sure we honour that.
270 //measurment quality too low
273 Vector
a(-mag
* max_vector
.x
/ samples
[jmax
].mag
,
274 -mag
* max_vector
.y
/ samples
[jmax
].mag
);
276 if (a
.SquareMagnitude() >= fixed(30 * 30))
277 // limit to reasonable values (60 knots), reject otherwise
280 return Result(quality
, a
);