Paparazzi UAS  v5.10_stable-5-g83a0da5-dirty
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 
70 // use half sweep at the end of polygon
71 #ifndef POLY_OSAM_HALF_SWEEP_ENABLED
72 #define POLY_OSAM_HALF_SWEEP_ENABLED TRUE
73 #endif
74 
80 
81 bool nav_survey_poly_osam_setup_towards(uint8_t FirstWP, uint8_t Size, float Sweep, int SecondWP)
82 {
83  float dx = waypoints[SecondWP].x - waypoints[FirstWP].x;
84  float dy = waypoints[SecondWP].y - waypoints[FirstWP].y;
85  if (dx == 0.0f) { dx = 0.000000001; }
86  float ang = atan(dy / dx);
87  //if values passed, use it.
88  if (Size == 0) {Size = Poly_Size;}
89  if (Sweep == 0) {Sweep = Poly_Sweep;}
90  return nav_survey_poly_osam_setup(FirstWP, Size, Sweep, DegOfRad(ang));
91 }
92 
93 struct Point2D {float x; float y;};
94 struct Line {float m; float b; float x;};
95 
96 static void TranslateAndRotateFromWorld(struct Point2D *p, float Zrot, float transX, float transY);
97 static void RotateAndTranslateToWorld(struct Point2D *p, float Zrot, float transX, float transY);
98 static void FindInterceptOfTwoLines(float *x, float *y, struct Line L1, struct Line L2);
99 static float EvaluateLineForX(float y, struct Line L);
100 
102 {
103  if (rst) {
105  Reset_Sweep = FALSE;
106  }
107 }
108 
109 #define PolygonSize POLY_OSAM_POLYGONSIZE
110 #define MaxFloat 1000000000
111 #define MinFloat -1000000000
112 
113 #ifndef LINE_START_FUNCTION
114 #define LINE_START_FUNCTION {}
115 #endif
116 #ifndef LINE_STOP_FUNCTION
117 #define LINE_STOP_FUNCTION {}
118 #endif
119 
120 /************** Polygon Survey **********************************************/
121 
127 static struct Point2D SmallestCorner;
128 static struct Line Edges[PolygonSize];
129 static float EdgeMaxY[PolygonSize];
130 static float EdgeMinY[PolygonSize];
131 static float SurveyTheta;
132 static float dSweep;
133 static float SurveyRadius;
134 static struct Point2D SurveyToWP;
135 static struct Point2D SurveyFromWP;
136 static struct Point2D SurveyCircle;
139 static float SurveyCircleQdr;
140 static float MaxY;
144 
145 bool nav_survey_poly_osam_setup(uint8_t EntryWP, uint8_t Size, float sw, float Orientation)
146 {
147  SmallestCorner.x = 0;
148  SmallestCorner.y = 0;
149  int i = 0;
150  float ys = 0;
151  static struct Point2D EntryPoint;
152  float LeftYInt;
153  float RightYInt;
154  float temp;
155  float XIntercept1 = 0;
156  float XIntercept2 = 0;
157  float entry_distance;
158 
159  float PolySurveyEntryDistance = POLY_OSAM_FIRST_SWEEP_DISTANCE;
160  float PolySurveyEntryRadius = POLY_OSAM_ENTRY_RADIUS;
161 
162  if (PolySurveyEntryDistance == 0) {
163  entry_distance = sw / 2;
164  } else {
165  entry_distance = PolySurveyEntryDistance;
166  }
167 
168  if (PolySurveyEntryRadius == 0) {
169  EntryRadius = sw / 2;
170  } else {
171  EntryRadius = PolySurveyEntryRadius;
172  }
173 
174  SurveyTheta = RadOfDeg(Orientation);
175  PolySurveySweepNum = 0;
177 
178  SurveyEntryWP = EntryWP;
179  SurveySize = Size;
180 
181  struct Point2D Corners[PolygonSize];
182 
184 
185  if (Size == 0) {
186  return true;
187  }
188 
189  //Don't initialize if Polygon is too big or if the orientation is not between 0 and 90
190  if (Size <= PolygonSize && Orientation >= -90 && Orientation <= 90) {
191  //Initialize Corners
192  for (i = 0; i < Size; i++) {
193  Corners[i].x = waypoints[i + EntryWP].x;
194  Corners[i].y = waypoints[i + EntryWP].y;
195  }
196 
197  //Rotate Corners so sweeps are parellel with x axis
198  for (i = 0; i < Size; i++) {
199  TranslateAndRotateFromWorld(&Corners[i], SurveyTheta, 0, 0);
200  }
201 
202  //Find min x and min y
203  SmallestCorner.y = Corners[0].y;
204  SmallestCorner.x = Corners[0].x;
205  for (i = 1; i < Size; i++) {
206  if (Corners[i].y < SmallestCorner.y) {
207  SmallestCorner.y = Corners[i].y;
208  }
209 
210  if (Corners[i].x < SmallestCorner.x) {
211  SmallestCorner.x = Corners[i].x;
212  }
213  }
214 
215  //Translate Corners all exist in quad #1
216  for (i = 0; i < Size; i++) {
218  }
219 
220  //Rotate and Translate Entry Point
221  EntryPoint.x = Corners[0].x;
222  EntryPoint.y = Corners[0].y;
223 
224  //Find max y
225  MaxY = Corners[0].y;
226  for (i = 1; i < Size; i++) {
227  if (Corners[i].y > MaxY) {
228  MaxY = Corners[i].y;
229  }
230  }
231 
232  //Find polygon edges
233  for (i = 0; i < Size; i++) {
234  if (i == 0)
235  if (Corners[Size - 1].x == Corners[i].x) { //Don't divide by zero!
236  Edges[i].m = MaxFloat;
237  } else {
238  Edges[i].m = ((Corners[Size - 1].y - Corners[i].y) / (Corners[Size - 1].x - Corners[i].x));
239  }
240  else if (Corners[i].x == Corners[i - 1].x) {
241  Edges[i].m = MaxFloat;
242  } else {
243  Edges[i].m = ((Corners[i].y - Corners[i - 1].y) / (Corners[i].x - Corners[i - 1].x));
244  }
245 
246  //Edges[i].m = MaxFloat;
247  Edges[i].b = (Corners[i].y - (Corners[i].x * Edges[i].m));
248  }
249 
250  //Find Min and Max y for each line
251  FindInterceptOfTwoLines(&temp, &LeftYInt, Edges[0], Edges[1]);
252  FindInterceptOfTwoLines(&temp, &RightYInt, Edges[0], Edges[Size - 1]);
253 
254  if (LeftYInt > RightYInt) {
255  EdgeMaxY[0] = LeftYInt;
256  EdgeMinY[0] = RightYInt;
257  } else {
258  EdgeMaxY[0] = RightYInt;
259  EdgeMinY[0] = LeftYInt;
260  }
261 
262  for (i = 1; i < Size - 1; i++) {
263  FindInterceptOfTwoLines(&temp, &LeftYInt, Edges[i], Edges[i + 1]);
264  FindInterceptOfTwoLines(&temp, &RightYInt, Edges[i], Edges[i - 1]);
265 
266  if (LeftYInt > RightYInt) {
267  EdgeMaxY[i] = LeftYInt;
268  EdgeMinY[i] = RightYInt;
269  } else {
270  EdgeMaxY[i] = RightYInt;
271  EdgeMinY[i] = LeftYInt;
272  }
273  }
274 
275  FindInterceptOfTwoLines(&temp, &LeftYInt, Edges[Size - 1], Edges[0]);
276  FindInterceptOfTwoLines(&temp, &RightYInt, Edges[Size - 1], Edges[Size - 2]);
277 
278  if (LeftYInt > RightYInt) {
279  EdgeMaxY[Size - 1] = LeftYInt;
280  EdgeMinY[Size - 1] = RightYInt;
281  } else {
282  EdgeMaxY[Size - 1] = RightYInt;
283  EdgeMinY[Size - 1] = LeftYInt;
284  }
285 
286  //Find amount to increment by every sweep
287  if (EntryPoint.y >= MaxY / 2) {
288  entry_distance = -entry_distance;
290  dSweep = -sw;
291  } else {
292  dSweep = sw;
293  }
294 
295  //CircleQdr tells the plane when to exit the circle
296  if (dSweep >= 0) {
297  SurveyCircleQdr = -DegOfRad(SurveyTheta);
298  } else {
299  SurveyCircleQdr = 180 - DegOfRad(SurveyTheta);
300  }
301 
302  //Find y value of the first sweep
303  ys = EntryPoint.y + entry_distance;
304 
305  //Find the edges which intercet the sweep line first
306  for (i = 0; i < SurveySize; i++) {
307  if (EdgeMinY[i] <= ys && EdgeMaxY[i] > ys) {
308  XIntercept2 = XIntercept1;
309  XIntercept1 = EvaluateLineForX(ys, Edges[i]);
310  }
311  }
312 
313  //Find point to come from and point to go to
314  if (fabs(EntryPoint.x - XIntercept2) <= fabs(EntryPoint.x - XIntercept1)) {
315  SurveyToWP.x = XIntercept1;
316  SurveyToWP.y = ys;
317 
318  SurveyFromWP.x = XIntercept2;
319  SurveyFromWP.y = ys;
320  } else {
321  SurveyToWP.x = XIntercept2;
322  SurveyToWP.y = ys;
323 
324  SurveyFromWP.x = XIntercept1;
325  SurveyFromWP.y = ys;
326  }
327 
328  //Find the direction to circle
329  if (ys > 0 && SurveyToWP.x > SurveyFromWP.x) {
331  } else if (ys < 0 && SurveyToWP.x < SurveyFromWP.x) {
333  } else {
335  }
336 
337  //Find the entry circle
339  SurveyCircle.y = EntryPoint.y + entry_distance - EntryRadius;
340 
341  //Go into entry circle state
342  CSurveyStatus = Entry;
344  }
345 
346  return false;
347 }
348 
350 {
351  struct Point2D C;
352  struct Point2D ToP;
353  struct Point2D FromP;
354  float ys;
355  static struct Point2D LastPoint;
356  int i;
357  bool LastHalfSweep;
358  static bool HalfSweep = false;
359  float XIntercept1 = 0;
360  float XIntercept2 = 0;
361  float DInt1 = 0;
362  float DInt2 = 0;
363  float temp;
364  float min_radius = POLY_OSAM_MIN_RADIUS;
365 
366  NavVerticalAutoThrottleMode(0); /* No pitch */
368 
369  switch (CSurveyStatus) {
370  case Entry:
371  //Rotate and translate circle point into real world
372  C.x = SurveyCircle.x;
373  C.y = SurveyCircle.y;
376 
377  //follow the circle
378  nav_circle_XY(C.x, C.y, SurveyRadius);
379 
381  && stateGetPositionUtm_f()->alt > waypoints[SurveyEntryWP].a - 10) {
383  nav_init_stage();
384  //LINE_START_FUNCTION;
385  }
386  break;
387  case Sweep:
388  LastHalfSweep = HalfSweep;
389  ToP.x = SurveyToWP.x;
390  ToP.y = SurveyToWP.y;
391  FromP.x = SurveyFromWP.x;
392  FromP.y = SurveyFromWP.y;
393 
394  //Rotate and Translate de plane position to local world
395  C.x = stateGetPositionEnu_f()->x;
396  C.y = stateGetPositionEnu_f()->y;
399 
400 #ifdef DIGITAL_CAM
401  {
402  //calc distance from line start and plane position (use only X position because y can be far due to wind or other factor)
403  float dist = FromP.x - C.x;
404 
405  // verify if plane are less than 10 meter from line start
406  if ((dc_autoshoot == DC_AUTOSHOOT_STOP) && (fabs(dist) < 10)) {
408  }
409  }
410 #endif
411 
412  //Rotate and Translate Line points into real world
415 
417  RotateAndTranslateToWorld(&FromP, SurveyTheta, 0, 0);
418 
419  //follow the line
420  nav_route_xy(FromP.x, FromP.y, ToP.x, ToP.y);
421  if (nav_approaching_xy(ToP.x, ToP.y, FromP.x, FromP.y, 0)) {
422  LastPoint.x = SurveyToWP.x;
423  LastPoint.y = SurveyToWP.y;
424 
425  if (LastPoint.y + dSweep >= MaxY || LastPoint.y + dSweep <= 0) { //Your out of the Polygon so Sweep Back or Half Sweep
426 
427  if (LastPoint.y + (dSweep / 2) >= MaxY || LastPoint.y + (dSweep / 2) <= 0 || !Half_Sweep_Enabled) { //Sweep back
428  dSweep = -dSweep;
429  if (LastHalfSweep) {
430  HalfSweep = false;
431  ys = LastPoint.y + (dSweep);
432  } else {
433  HalfSweep = true;
434  ys = LastPoint.y + (dSweep / 2);
435  }
436 
437  if (dSweep >= 0) {
438  SurveyCircleQdr = -DegOfRad(SurveyTheta);
439  } else {
440  SurveyCircleQdr = 180 - DegOfRad(SurveyTheta);
441  }
443  } else { // Half Sweep forward
444  ys = LastPoint.y + (dSweep / 2);
445 
446  if (dSweep >= 0) {
447  SurveyCircleQdr = -DegOfRad(SurveyTheta);
448  } else {
449  SurveyCircleQdr = 180 - DegOfRad(SurveyTheta);
450  }
451  HalfSweep = true;
452  }
453 
454 
455  } else { // Normal sweep
456  //Find y value of the first sweep
457  HalfSweep = false;
458  ys = LastPoint.y + dSweep;
459  }
460 
461  //Find the edges which intercet the sweep line first
462  for (i = 0; i < SurveySize; i++) {
463  if (EdgeMinY[i] < ys && EdgeMaxY[i] >= ys) {
464  XIntercept2 = XIntercept1;
465  XIntercept1 = EvaluateLineForX(ys, Edges[i]);
466  }
467  }
468 
469  //Find point to come from and point to go to
470  DInt1 = XIntercept1 - LastPoint.x;
471  DInt2 = XIntercept2 - LastPoint.x;
472 
473  if (DInt1 * DInt2 >= 0) {
474  if (fabs(DInt2) <= fabs(DInt1)) {
475  SurveyToWP.x = XIntercept1;
476  SurveyToWP.y = ys;
477 
478  SurveyFromWP.x = XIntercept2;
479  SurveyFromWP.y = ys;
480  } else {
481  SurveyToWP.x = XIntercept2;
482  SurveyToWP.y = ys;
483 
484  SurveyFromWP.x = XIntercept1;
485  SurveyFromWP.y = ys;
486  }
487  } else {
488  if ((SurveyToWP.x - SurveyFromWP.x) > 0 && DInt2 > 0) {
489  SurveyToWP.x = XIntercept1;
490  SurveyToWP.y = ys;
491 
492  SurveyFromWP.x = XIntercept2;
493  SurveyFromWP.y = ys;
494  } else if ((SurveyToWP.x - SurveyFromWP.x) < 0 && DInt2 < 0) {
495  SurveyToWP.x = XIntercept1;
496  SurveyToWP.y = ys;
497 
498  SurveyFromWP.x = XIntercept2;
499  SurveyFromWP.y = ys;
500  } else {
501  SurveyToWP.x = XIntercept2;
502  SurveyToWP.y = ys;
503 
504  SurveyFromWP.x = XIntercept1;
505  SurveyFromWP.y = ys;
506  }
507  }
508 
509  //Find the radius to circle
510  if (!HalfSweep || use_full_circle) {
511  temp = dSweep / 2;
512  } else {
513  temp = dSweep / 4;
514  }
515 
516  //if less than min radius
517  if (fabs(temp) < min_radius) {
518  if (temp < 0) { temp = -min_radius; } else { temp = min_radius; }
519  }
520 
521  //Find the direction to circle
522  if (ys > 0 && SurveyToWP.x > SurveyFromWP.x) {
523  SurveyRadius = temp;
524  } else if (ys < 0 && SurveyToWP.x < SurveyFromWP.x) {
525  SurveyRadius = temp;
526  } else {
527  SurveyRadius = -temp;
528  }
529 
530  //find x position to circle
531  if (fabs(LastPoint.x - SurveyToWP.x) > fabs(SurveyFromWP.x - SurveyToWP.x)) {
532  SurveyCircle.x = LastPoint.x;
533  } else {
535  }
536 
537  //y position to circle
538  SurveyCircle.y = ys - temp;
539 
540  //Go into circle state
542  nav_init_stage();
545  }
546 
547  break;
548  case SweepCircle:
549  //Rotate and translate circle point into real world
550  C.x = SurveyCircle.x;
551  C.y = SurveyCircle.y;
554 
555  //follow the circle
556  nav_circle_XY(C.x, C.y, SurveyRadius);
557 
560  nav_init_stage();
561  //LINE_START_FUNCTION;
562  }
563  break;
564  case Init:
565  return false;
566  default:
567  return false;
568  }
569  return true;
570 }
571 
572 
573 /*
574  Translates point so (transX, transY) are (0,0) then rotates the point around z by Zrot
575 */
576 void TranslateAndRotateFromWorld(struct Point2D *p, float Zrot, float transX, float transY)
577 {
578  float temp;
579 
580  p->x = p->x - transX;
581  p->y = p->y - transY;
582 
583  temp = p->x;
584  p->x = p->x * cosf(Zrot) + p->y * sinf(Zrot);
585  p->y = -temp * sinf(Zrot) + p->y * cosf(Zrot);
586 }
587 
589 void RotateAndTranslateToWorld(struct Point2D *p, float Zrot, float transX, float transY)
590 {
591  float temp = p->x;
592 
593  p->x = p->x * cosf(Zrot) - p->y * sinf(Zrot);
594  p->y = temp * sinf(Zrot) + p->y * cosf(Zrot);
595 
596  p->x = p->x + transX;
597  p->y = p->y + transY;
598 }
599 
600 void FindInterceptOfTwoLines(float *x, float *y, struct Line L1, struct Line L2)
601 {
602  *x = ((L2.b - L1.b) / (L1.m - L2.m));
603  *y = L1.m * (*x) + L1.b;
604 }
605 
606 
607 float EvaluateLineForX(float y, struct Line L)
608 {
609  return ((y - L.b) / L.m);
610 }
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:713
#define FALSE
Definition: std.h:5
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:686
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