Paparazzi UAS  v5.8.2_stable-0-g6260b7c
Paparazzi is a free software Unmanned Aircraft System.
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
nav_survey_poly_osam.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008-2014 The Paparazzi Team
3  *
4  * This file is part of paparazzi.
5  *
6  * paparazzi is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * paparazzi is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with paparazzi; see the file COPYING. If not, write to
18  * the Free Software Foundation, 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
28 
30 #include "state.h"
31 #include "autopilot.h"
32 #include "generated/flight_plan.h"
33 
34 #ifdef DIGITAL_CAM
35 #include "modules/digital_cam/dc.h"
36 #endif
37 
38 #ifndef POLY_OSAM_DEFAULT_SIZE
39 #define POLY_OSAM_DEFAULT_SIZE 5
40 #endif
41 
42 #ifndef POLY_OSAM_DEFAULT_SWEEP
43 #define POLY_OSAM_DEFAULT_SWEEP 100
44 #endif
45 
47 #ifndef POLY_OSAM_ENTRY_RADIUS
48 #define POLY_OSAM_ENTRY_RADIUS 0
49 #endif
50 
52 #ifndef POLY_OSAM_MIN_RADIUS
53 #define POLY_OSAM_MIN_RADIUS 30
54 #endif
55 
57 #ifndef POLY_OSAM_FIRST_SWEEP_DISTANCE
58 #define POLY_OSAM_FIRST_SWEEP_DISTANCE 0
59 #endif
60 
62 #ifndef POLY_OSAM_POLYGONSIZE
63 #define POLY_OSAM_POLYGONSIZE 10
64 #endif
65 
66 #ifndef POLY_OSAM_USE_FULL_CIRCLE
67 #define POLY_OSAM_USE_FULL_CIRCLE TRUE
68 #endif
69 
73 
74 bool_t nav_survey_poly_osam_setup_towards(uint8_t FirstWP, uint8_t Size, float Sweep, int SecondWP)
75 {
76  float dx = waypoints[SecondWP].x - waypoints[FirstWP].x;
77  float dy = waypoints[SecondWP].y - waypoints[FirstWP].y;
78  if (dx == 0.0f) { dx = 0.000000001; }
79  float ang = atan(dy / dx);
80  //if values passed, use it.
81  if (Size == 0) {Size = Poly_Size;}
82  if (Sweep == 0) {Sweep = Poly_Sweep;}
83  return nav_survey_poly_osam_setup(FirstWP, Size, Sweep, DegOfRad(ang));
84 }
85 
86 struct Point2D {float x; float y;};
87 struct Line {float m; float b; float x;};
88 
89 static void TranslateAndRotateFromWorld(struct Point2D *p, float Zrot, float transX, float transY);
90 static void RotateAndTranslateToWorld(struct Point2D *p, float Zrot, float transX, float transY);
91 static void FindInterceptOfTwoLines(float *x, float *y, struct Line L1, struct Line L2);
92 static float EvaluateLineForX(float y, struct Line L);
93 
94 #define PolygonSize POLY_OSAM_POLYGONSIZE
95 #define MaxFloat 1000000000
96 #define MinFloat -1000000000
97 
98 #ifndef LINE_START_FUNCTION
99 #define LINE_START_FUNCTION {}
100 #endif
101 #ifndef LINE_STOP_FUNCTION
102 #define LINE_STOP_FUNCTION {}
103 #endif
104 
105 /************** Polygon Survey **********************************************/
106 
112 static struct Point2D SmallestCorner;
113 static struct Line Edges[PolygonSize];
114 static float EdgeMaxY[PolygonSize];
115 static float EdgeMinY[PolygonSize];
116 static float SurveyTheta;
117 static float dSweep;
118 static float SurveyRadius;
119 static struct Point2D SurveyToWP;
120 static struct Point2D SurveyFromWP;
121 static struct Point2D SurveyCircle;
124 static float SurveyCircleQdr;
125 static float MaxY;
129 
130 bool_t nav_survey_poly_osam_setup(uint8_t EntryWP, uint8_t Size, float sw, float Orientation)
131 {
132  SmallestCorner.x = 0;
133  SmallestCorner.y = 0;
134  int i = 0;
135  float ys = 0;
136  static struct Point2D EntryPoint;
137  float LeftYInt;
138  float RightYInt;
139  float temp;
140  float XIntercept1 = 0;
141  float XIntercept2 = 0;
142  float entry_distance;
143 
144  float PolySurveyEntryDistance = POLY_OSAM_FIRST_SWEEP_DISTANCE;
145  float PolySurveyEntryRadius = POLY_OSAM_ENTRY_RADIUS;
146 
147  if (PolySurveyEntryDistance == 0) {
148  entry_distance = sw / 2;
149  } else {
150  entry_distance = PolySurveyEntryDistance;
151  }
152 
153  if (PolySurveyEntryRadius == 0) {
154  EntryRadius = sw / 2;
155  } else {
156  EntryRadius = PolySurveyEntryRadius;
157  }
158 
159  SurveyTheta = RadOfDeg(Orientation);
160  PolySurveySweepNum = 0;
162 
163  SurveyEntryWP = EntryWP;
164  SurveySize = Size;
165 
166  struct Point2D Corners[PolygonSize];
167 
169 
170  if (Size == 0) {
171  return TRUE;
172  }
173 
174  //Don't initialize if Polygon is too big or if the orientation is not between 0 and 90
175  if (Size <= PolygonSize && Orientation >= -90 && Orientation <= 90) {
176  //Initialize Corners
177  for (i = 0; i < Size; i++) {
178  Corners[i].x = waypoints[i + EntryWP].x;
179  Corners[i].y = waypoints[i + EntryWP].y;
180  }
181 
182  //Rotate Corners so sweeps are parellel with x axis
183  for (i = 0; i < Size; i++) {
184  TranslateAndRotateFromWorld(&Corners[i], SurveyTheta, 0, 0);
185  }
186 
187  //Find min x and min y
188  SmallestCorner.y = Corners[0].y;
189  SmallestCorner.x = Corners[0].x;
190  for (i = 1; i < Size; i++) {
191  if (Corners[i].y < SmallestCorner.y) {
192  SmallestCorner.y = Corners[i].y;
193  }
194 
195  if (Corners[i].x < SmallestCorner.x) {
196  SmallestCorner.x = Corners[i].x;
197  }
198  }
199 
200  //Translate Corners all exist in quad #1
201  for (i = 0; i < Size; i++) {
203  }
204 
205  //Rotate and Translate Entry Point
206  EntryPoint.x = Corners[0].x;
207  EntryPoint.y = Corners[0].y;
208 
209  //Find max y
210  MaxY = Corners[0].y;
211  for (i = 1; i < Size; i++) {
212  if (Corners[i].y > MaxY) {
213  MaxY = Corners[i].y;
214  }
215  }
216 
217  //Find polygon edges
218  for (i = 0; i < Size; i++) {
219  if (i == 0)
220  if (Corners[Size - 1].x == Corners[i].x) { //Don't divide by zero!
221  Edges[i].m = MaxFloat;
222  } else {
223  Edges[i].m = ((Corners[Size - 1].y - Corners[i].y) / (Corners[Size - 1].x - Corners[i].x));
224  }
225  else if (Corners[i].x == Corners[i - 1].x) {
226  Edges[i].m = MaxFloat;
227  } else {
228  Edges[i].m = ((Corners[i].y - Corners[i - 1].y) / (Corners[i].x - Corners[i - 1].x));
229  }
230 
231  //Edges[i].m = MaxFloat;
232  Edges[i].b = (Corners[i].y - (Corners[i].x * Edges[i].m));
233  }
234 
235  //Find Min and Max y for each line
236  FindInterceptOfTwoLines(&temp, &LeftYInt, Edges[0], Edges[1]);
237  FindInterceptOfTwoLines(&temp, &RightYInt, Edges[0], Edges[Size - 1]);
238 
239  if (LeftYInt > RightYInt) {
240  EdgeMaxY[0] = LeftYInt;
241  EdgeMinY[0] = RightYInt;
242  } else {
243  EdgeMaxY[0] = RightYInt;
244  EdgeMinY[0] = LeftYInt;
245  }
246 
247  for (i = 1; i < Size - 1; i++) {
248  FindInterceptOfTwoLines(&temp, &LeftYInt, Edges[i], Edges[i + 1]);
249  FindInterceptOfTwoLines(&temp, &RightYInt, Edges[i], Edges[i - 1]);
250 
251  if (LeftYInt > RightYInt) {
252  EdgeMaxY[i] = LeftYInt;
253  EdgeMinY[i] = RightYInt;
254  } else {
255  EdgeMaxY[i] = RightYInt;
256  EdgeMinY[i] = LeftYInt;
257  }
258  }
259 
260  FindInterceptOfTwoLines(&temp, &LeftYInt, Edges[Size - 1], Edges[0]);
261  FindInterceptOfTwoLines(&temp, &RightYInt, Edges[Size - 1], Edges[Size - 2]);
262 
263  if (LeftYInt > RightYInt) {
264  EdgeMaxY[Size - 1] = LeftYInt;
265  EdgeMinY[Size - 1] = RightYInt;
266  } else {
267  EdgeMaxY[Size - 1] = RightYInt;
268  EdgeMinY[Size - 1] = LeftYInt;
269  }
270 
271  //Find amount to increment by every sweep
272  if (EntryPoint.y >= MaxY / 2) {
273  entry_distance = -entry_distance;
275  dSweep = -sw;
276  } else {
278  dSweep = sw;
279  }
280 
281  //CircleQdr tells the plane when to exit the circle
282  if (dSweep >= 0) {
283  SurveyCircleQdr = -DegOfRad(SurveyTheta);
284  } else {
285  SurveyCircleQdr = 180 - DegOfRad(SurveyTheta);
286  }
287 
288  //Find y value of the first sweep
289  ys = EntryPoint.y + entry_distance;
290 
291  //Find the edges which intercet the sweep line first
292  for (i = 0; i < SurveySize; i++) {
293  if (EdgeMinY[i] <= ys && EdgeMaxY[i] > ys) {
294  XIntercept2 = XIntercept1;
295  XIntercept1 = EvaluateLineForX(ys, Edges[i]);
296  }
297  }
298 
299  //Find point to come from and point to go to
300  if (fabs(EntryPoint.x - XIntercept2) <= fabs(EntryPoint.x - XIntercept1)) {
301  SurveyToWP.x = XIntercept1;
302  SurveyToWP.y = ys;
303 
304  SurveyFromWP.x = XIntercept2;
305  SurveyFromWP.y = ys;
306  } else {
307  SurveyToWP.x = XIntercept2;
308  SurveyToWP.y = ys;
309 
310  SurveyFromWP.x = XIntercept1;
311  SurveyFromWP.y = ys;
312  }
313 
314  //Find the direction to circle
315  if (ys > 0 && SurveyToWP.x > SurveyFromWP.x) {
317  } else if (ys < 0 && SurveyToWP.x < SurveyFromWP.x) {
319  } else {
321  }
322 
323  //Find the entry circle
325  SurveyCircle.y = EntryPoint.y + entry_distance - EntryRadius;
326 
327  //Go into entry circle state
328  CSurveyStatus = Entry;
330  }
331 
332  return FALSE;
333 }
334 
336 {
337  struct Point2D C;
338  struct Point2D ToP;
339  struct Point2D FromP;
340  float ys;
341  static struct Point2D LastPoint;
342  int i;
343  bool_t LastHalfSweep;
344  static bool_t HalfSweep = FALSE;
345  float XIntercept1 = 0;
346  float XIntercept2 = 0;
347  float DInt1 = 0;
348  float DInt2 = 0;
349  float temp;
350  float min_radius = POLY_OSAM_MIN_RADIUS;
351 
352  NavVerticalAutoThrottleMode(0); /* No pitch */
354 
355  switch (CSurveyStatus) {
356  case Entry:
357  //Rotate and translate circle point into real world
358  C.x = SurveyCircle.x;
359  C.y = SurveyCircle.y;
362 
363  //follow the circle
364  nav_circle_XY(C.x, C.y, SurveyRadius);
365 
367  && stateGetPositionUtm_f()->alt > waypoints[SurveyEntryWP].a - 10) {
369  nav_init_stage();
370  //LINE_START_FUNCTION;
371  }
372  break;
373  case Sweep:
374  LastHalfSweep = HalfSweep;
375  ToP.x = SurveyToWP.x;
376  ToP.y = SurveyToWP.y;
377  FromP.x = SurveyFromWP.x;
378  FromP.y = SurveyFromWP.y;
379 
380  //Rotate and Translate de plane position to local world
381  C.x = stateGetPositionEnu_f()->x;
382  C.y = stateGetPositionEnu_f()->y;
385 
386 #ifdef DIGITAL_CAM
387  {
388  //calc distance from line start and plane position (use only X position because y can be far due to wind or other factor)
389  float dist = FromP.x - C.x;
390 
391  // verify if plane are less than 10 meter from line start
392  if ((dc_autoshoot == DC_AUTOSHOOT_STOP) && (fabs(dist) < 10)) {
394  }
395  }
396 #endif
397 
398  //Rotate and Translate Line points into real world
401 
403  RotateAndTranslateToWorld(&FromP, SurveyTheta, 0, 0);
404 
405  //follow the line
406  nav_route_xy(FromP.x, FromP.y, ToP.x, ToP.y);
407  if (nav_approaching_xy(ToP.x, ToP.y, FromP.x, FromP.y, 0)) {
408  LastPoint.x = SurveyToWP.x;
409  LastPoint.y = SurveyToWP.y;
410 
411  if (LastPoint.y + dSweep >= MaxY || LastPoint.y + dSweep <= 0) { //Your out of the Polygon so Sweep Back or Half Sweep
412  if (LastPoint.y + (dSweep / 2) >= MaxY || LastPoint.y + (dSweep / 2) <= 0) { //Sweep back
413  dSweep = -dSweep;
414  if (LastHalfSweep) {
415  HalfSweep = FALSE;
416  ys = LastPoint.y + (dSweep);
417  } else {
418  HalfSweep = TRUE;
419  ys = LastPoint.y + (dSweep / 2);
420  }
421 
422  if (dSweep >= 0) {
423  SurveyCircleQdr = -DegOfRad(SurveyTheta);
424  } else {
425  SurveyCircleQdr = 180 - DegOfRad(SurveyTheta);
426  }
428  } else { // Half Sweep forward
429  ys = LastPoint.y + (dSweep / 2);
430 
431  if (dSweep >= 0) {
432  SurveyCircleQdr = -DegOfRad(SurveyTheta);
433  } else {
434  SurveyCircleQdr = 180 - DegOfRad(SurveyTheta);
435  }
436  HalfSweep = TRUE;
437  }
438 
439 
440  } else { // Normal sweep
441  //Find y value of the first sweep
442  HalfSweep = FALSE;
443  ys = LastPoint.y + dSweep;
444  }
445 
446  //Find the edges which intercet the sweep line first
447  for (i = 0; i < SurveySize; i++) {
448  if (EdgeMinY[i] < ys && EdgeMaxY[i] >= ys) {
449  XIntercept2 = XIntercept1;
450  XIntercept1 = EvaluateLineForX(ys, Edges[i]);
451  }
452  }
453 
454  //Find point to come from and point to go to
455  DInt1 = XIntercept1 - LastPoint.x;
456  DInt2 = XIntercept2 - LastPoint.x;
457 
458  if (DInt1 * DInt2 >= 0) {
459  if (fabs(DInt2) <= fabs(DInt1)) {
460  SurveyToWP.x = XIntercept1;
461  SurveyToWP.y = ys;
462 
463  SurveyFromWP.x = XIntercept2;
464  SurveyFromWP.y = ys;
465  } else {
466  SurveyToWP.x = XIntercept2;
467  SurveyToWP.y = ys;
468 
469  SurveyFromWP.x = XIntercept1;
470  SurveyFromWP.y = ys;
471  }
472  } else {
473  if ((SurveyToWP.x - SurveyFromWP.x) > 0 && DInt2 > 0) {
474  SurveyToWP.x = XIntercept1;
475  SurveyToWP.y = ys;
476 
477  SurveyFromWP.x = XIntercept2;
478  SurveyFromWP.y = ys;
479  } else if ((SurveyToWP.x - SurveyFromWP.x) < 0 && DInt2 < 0) {
480  SurveyToWP.x = XIntercept1;
481  SurveyToWP.y = ys;
482 
483  SurveyFromWP.x = XIntercept2;
484  SurveyFromWP.y = ys;
485  } else {
486  SurveyToWP.x = XIntercept2;
487  SurveyToWP.y = ys;
488 
489  SurveyFromWP.x = XIntercept1;
490  SurveyFromWP.y = ys;
491  }
492  }
493 
494  //Find the radius to circle
495  if (!HalfSweep || use_full_circle) {
496  temp = dSweep / 2;
497  } else {
498  temp = dSweep / 4;
499  }
500 
501  //if less than min radius
502  if (fabs(temp) < min_radius) {
503  if (temp < 0) { temp = -min_radius; } else { temp = min_radius; }
504  }
505 
506  //Find the direction to circle
507  if (ys > 0 && SurveyToWP.x > SurveyFromWP.x) {
508  SurveyRadius = temp;
509  } else if (ys < 0 && SurveyToWP.x < SurveyFromWP.x) {
510  SurveyRadius = temp;
511  } else {
512  SurveyRadius = -temp;
513  }
514 
515  //find x position to circle
516  if (fabs(LastPoint.x - SurveyToWP.x) > fabs(SurveyFromWP.x - SurveyToWP.x)) {
517  SurveyCircle.x = LastPoint.x;
518  } else {
520  }
521 
522  //y position to circle
523  SurveyCircle.y = ys - temp;
524 
525  //Go into circle state
527  nav_init_stage();
530  }
531 
532  break;
533  case SweepCircle:
534  //Rotate and translate circle point into real world
535  C.x = SurveyCircle.x;
536  C.y = SurveyCircle.y;
539 
540  //follow the circle
541  nav_circle_XY(C.x, C.y, SurveyRadius);
542 
545  nav_init_stage();
546  //LINE_START_FUNCTION;
547  }
548  break;
549  case Init:
550  return FALSE;
551  default:
552  return FALSE;
553  }
554  return TRUE;
555 }
556 
557 
558 /*
559  Translates point so (transX, transY) are (0,0) then rotates the point around z by Zrot
560 */
561 void TranslateAndRotateFromWorld(struct Point2D *p, float Zrot, float transX, float transY)
562 {
563  float temp;
564 
565  p->x = p->x - transX;
566  p->y = p->y - transY;
567 
568  temp = p->x;
569  p->x = p->x * cosf(Zrot) + p->y * sinf(Zrot);
570  p->y = -temp * sinf(Zrot) + p->y * cosf(Zrot);
571 }
572 
574 void RotateAndTranslateToWorld(struct Point2D *p, float Zrot, float transX, float transY)
575 {
576  float temp = p->x;
577 
578  p->x = p->x * cosf(Zrot) - p->y * sinf(Zrot);
579  p->y = temp * sinf(Zrot) + p->y * cosf(Zrot);
580 
581  p->x = p->x + transX;
582  p->y = p->y + transY;
583 }
584 
585 void FindInterceptOfTwoLines(float *x, float *y, struct Line L1, struct Line L2)
586 {
587  *x = ((L2.b - L1.b) / (L1.m - L2.m));
588  *y = L1.m * (*x) + L1.b;
589 }
590 
591 
592 float EvaluateLineForX(float y, struct Line L)
593 {
594  return ((y - L.b) / L.m);
595 }
unsigned short uint16_t
Definition: types.h:16
float x
Definition: common_nav.h:40
static struct EnuCoor_f * stateGetPositionEnu_f(void)
Get position in local ENU coordinates (float).
Definition: state.h:702
#define FALSE
Definition: std.h:5
#define TRUE
Definition: std.h:4
float y
Definition: common_nav.h:41
Standard Digital Camera Control Interface.
float x
in meters
dc_autoshoot_type dc_autoshoot
Definition: dc.c:62
static struct UtmCoor_f * stateGetPositionUtm_f(void)
Get position in UTM coordinates (float).
Definition: state.h:675
unsigned char uint8_t
Definition: types.h:14
API to get/set the generic vehicle states.
struct point waypoints[NB_WAYPOINT]
size == nb_waypoint, waypoint 0 is a dummy waypoint
Definition: common_nav.c:38
static float p[2][2]
float y
in meters