PageRenderTime 24ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/airspace_check.pl

http://airscore.googlecode.com/
Perl | 384 lines | 252 code | 62 blank | 70 comment | 25 complexity | 6c9f70e535dd329e85b7b0f913def6f0 MD5 | raw file
Possible License(s): GPL-2.0
  1. #!/usr/bin/perl
  2. #
  3. # Check to see if a track violates airspace
  4. #
  5. # Needs to be quick/pruned somehow?
  6. # Only check if 'nearby' / every 30 seconds?
  7. #
  8. # Geoff Wong 2008
  9. #
  10. require DBD::mysql;
  11. use Math::Trig;
  12. use Data::Dumper;
  13. use POSIX qw(ceil floor);
  14. use TrackLib qw(:all);
  15. my $dbh;
  16. my $max_height = 3048; # 10000' limit in oz
  17. #
  18. # Read an abbreviated tracklog from the database
  19. #
  20. sub read_short_track
  21. {
  22. my ($traPk) = @_;
  23. my %track;
  24. my @coords;
  25. my %awards;
  26. my $ref;
  27. my $c1;
  28. $sth = $dbh->prepare("select *, floor(trlTime/60) as trlMinTime, max(trlAltitude) as maxAlt from tblTrackLog where traPk=$traPk group by floor(trlTime/60) order by trlTime");
  29. $sth->execute();
  30. while ($ref = $sth->fetchrow_hashref())
  31. {
  32. #print "Found a row: time = $ref->{'trlTime'}\n";
  33. my %coord;
  34. $coord{'dlat'} = $ref->{'trlLatDecimal'};
  35. $coord{'dlong'} = $ref->{'trlLongDecimal'};
  36. # radians
  37. $coord{'lat'} = $ref->{'trlLatDecimal'} * $pi / 180;
  38. $coord{'long'} = $ref->{'trlLongDecimal'} * $pi / 180;
  39. # alt / time / pressure?
  40. $coord{'alt'} = $ref->{'maxAlt'};
  41. $coord{'time'} = $ref->{'trlTime'};
  42. # and cartesian ..
  43. $c1 = polar2cartesian(\%coord);
  44. $coord{'cart'} = $c1;
  45. push @coords, \%coord;
  46. }
  47. $track{'coords'} = \@coords;
  48. return \%track;
  49. }
  50. sub airspace_check
  51. {
  52. my ($traPk, $airspaces) = @_;
  53. my $flight;
  54. my $violation;
  55. my $dst;
  56. $violation = 0;
  57. $flight = read_short_track($traPk);
  58. $full = $flight->{'coords'};
  59. foreach $space (@$airspaces)
  60. {
  61. print "Checking airspace ", $space->{'name'}, " with base=", $space->{'base'}, "\n";
  62. #print Dumper($space);
  63. for my $coord ( @$full )
  64. {
  65. # add dist check on centre of space too ...distance($coord, $space->{'centre'}) < $space->{'radius'}
  66. # fix: create an angle to current point .. compare with radius
  67. # and if between the angles of the "edge" points of (outer) radius
  68. #print "check ", $space->{'name'}, "\n";
  69. if ($coord->{'alt'} > 10000)
  70. {
  71. # ignore stupid values from tracklog
  72. next;
  73. }
  74. # stupid max height (10000ft) check for oz pilots
  75. if ($coord->{'alt'} > $max_height)
  76. {
  77. if ($coord->{'alt'} - $space->{'base'} > $violation)
  78. {
  79. $violation = $coord->{'alt'} - $max_height;
  80. }
  81. }
  82. if ($coord->{'alt'} > $space->{'base'})
  83. {
  84. # potential violation ..
  85. if ($space->{'shape'} eq 'circle')
  86. {
  87. $dst = distance($coord, $space->{'centre'});
  88. if ($dst < $space->{'radius'})
  89. {
  90. print "PV: alt=", $coord->{'alt'}, " dlat=", $coord->{'dlat'}, " dlong=", $coord->{'dlong'}, "\n";
  91. $violation = $coord->{'alt'} - $space->{'base'};
  92. }
  93. }
  94. elsif (in_polygon($coord, $space->{'points'}))
  95. {
  96. if ($coord->{'alt'} - $space->{'base'} > $violation)
  97. {
  98. print "PV: alt=", $coord->{'alt'}, " dlat=", $coord->{'dlat'}, " dlong=", $coord->{'dlong'}, "\n";
  99. $violation = $coord->{'alt'} - $space->{'base'};
  100. }
  101. }
  102. }
  103. }
  104. }
  105. return $violation;
  106. }
  107. #int nvert, float *vertx, float *verty, float testx, float testy)
  108. # for (i = 0, j = nvert-1; i < nvert; j = i++)
  109. # {
  110. # if ( ((verty[i]>$wpt->{'y'}) != (verty[j]>$wpt->{'y'})) &&
  111. # (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
  112. # c = !c;
  113. #
  114. # for (i = 0, j = npol-1; i < npol; j = i++) {
  115. # if ((((yp[i] <= y) && (y < yp[j])) ||
  116. # ((yp[j] <= y) && (y < yp[i]))) &&
  117. # (x < (xp[j] - xp[i]) * (y - yp[i]) / (yp[j] - yp[i]) + xp[i]))
  118. # c = !c;
  119. # }
  120. sub in_polygon
  121. {
  122. my ($wpt, $poly) = @_;
  123. my ($i, $j);
  124. my $c = 0;
  125. my $nvert;
  126. $nvert = scalar @$poly;
  127. # print "wpt=", Dumper($wpt);
  128. # print "poly=", Dumper($poly);
  129. for ($i = 0, $j = $nvert-1; $i < $nvert; $j = $i++)
  130. {
  131. # if ( (($poly->[$i]->{'cart'}->{'y'} > $wpt->{'cart'}->{'y'})
  132. # != ($poly->[$j]->{'cart'}->{'y'} > $wpt->{'cart'}->{'y'})) &&
  133. # ($wpt->{'cart'}->{'x'} < ($poly->[$j]->{'cart'}->{'x'}-$poly->[$i]->{'cart'}->{'x'}) * ($wpt->{'cart'}->{'y'} - $poly->[$i]->{'cart'}->{'y'}) /
  134. # ($poly->[$j]->{'cart'}->{'y'} - $poly->[$i]->{'cart'}->{'y'}) + $poly->[$i]->{'cart'}->{'x'}) )
  135. if (( (($poly->[$i]->{'cart'}->{'y'} <= $wpt->{'cart'}->{'y'}) &&
  136. ($wpt->{'cart'}->{'y'} < $poly->[$j]->{'cart'}->{'y'})) ||
  137. (($poly->[$j]->{'cart'}->{'y'} <= $wpt->{'cart'}->{'y'}) &&
  138. ($wpt->{'cart'}->{'y'} < $poly->[$i]->{'cart'}->{'y'}))) &&
  139. ($wpt->{'cart'}->{'x'} < ($poly->[$j]->{'cart'}->{'x'} -
  140. $poly->[$i]->{'cart'}->{'x'}) * ($wpt->{'cart'}->{'y'} -
  141. $poly->[$i]->{'cart'}->{'y'}) /
  142. ($poly->[$j]->{'cart'}->{'y'} - $poly->[$i]->{'cart'}->{'y'}) + $poly->[$i]->{'cart'}->{'x'}) )
  143. {
  144. #print "cross\n";
  145. $c++;
  146. }
  147. }
  148. return ($c % 2);
  149. }
  150. #
  151. # Don't bother with great circle for checking
  152. # Calculate angle described by a line made by two points
  153. # $p1 = origin
  154. sub cart_angle
  155. {
  156. my ($p1, $p2);
  157. my ($x,$y);
  158. $x = $p2->{'cart'}->{'x'} - $p1->{'cart'}->{'x'};
  159. $y = $p2->{'cart'}->{'y'} - $p1->{'cart'}->{'y'};
  160. return atan2($x,$y);
  161. }
  162. #
  163. # Returns a list of airspace that is centred 'near' the track
  164. # within traDistance+XXX)
  165. #
  166. sub find_nearby_airspace
  167. {
  168. my ($regPk, $dist) = @_;
  169. my @allair;
  170. my ($sth,$ref);
  171. my %nearair;
  172. my $airPk;
  173. my @points;
  174. my $centre;
  175. # Get regPk centre ...
  176. #$sth = $dbh->prepare("select * from tblAirspace A, tblAirspaceWaypoint AW where A.airCentreWP = AW.awpPk and AW.awpLatDecimal between (X,Y) and AW.awpLonDecimal between (X,Y)");
  177. # Get it all for now ...
  178. $sth = $dbh->prepare("select * from tblAirspace A, tblAirspaceWaypoint AW where A.airPk=AW.airPk order by A.airPk,AW.airOrder");
  179. $sth->execute();
  180. $airPk = 0;
  181. while ($ref = $sth->fetchrow_hashref())
  182. {
  183. my %coord;
  184. if ($ref->{'airPk'} != $airPk)
  185. {
  186. if ($airPk == 0)
  187. {
  188. $airPk = $ref->{'airPk'};
  189. }
  190. else
  191. {
  192. my @pts;
  193. my %na;
  194. @pts = @points;
  195. $nearair{'points'} = \@pts;
  196. %na = %nearair;
  197. push @allair, \%na;
  198. @points = ();
  199. %nearair = ();
  200. $airPk = $ref->{'airPk'};
  201. }
  202. }
  203. $nearair{'name'} = $ref->{'airName'};
  204. $nearair{'class'} = $ref->{'airClass'};
  205. $nearair{'base'} = $ref->{'airBase'};
  206. $nearair{'tops'} = $ref->{'airTops'};
  207. $nearair{'shape'} = $ref->{'airShape'};
  208. $nearair{'radius'} = $ref->{'airRadius'};
  209. $coord{'dlat'} = $ref->{'awpLatDecimal'};
  210. $coord{'dlong'} = $ref->{'awpLongDecimal'};
  211. $coord{'lat'} = $ref->{'awpLatDecimal'} * $pi / 180;
  212. $coord{'long'} = $ref->{'awpLongDecimal'} * $pi / 180;
  213. $coord{'cart'} = polar2cartesian(\%coord);
  214. #print "coord=",Dumper($coord), "\n";
  215. push @points, \%coord;
  216. }
  217. $nearair{'points'} = \@points;
  218. # Assuming only one point for circular regions
  219. $nearair{'centre'} = $points[0];
  220. push @allair, \%nearair;
  221. return \@allair;
  222. }
  223. sub find_task_airspace
  224. {
  225. my ($tasPk) = @_;
  226. my @allair;
  227. my ($sth,$ref);
  228. my %nearair;
  229. my $airPk;
  230. my @points;
  231. my $centre;
  232. # Get regPk centre ...
  233. #$sth = $dbh->prepare("select * from tblAirspace A, tblAirspaceWaypoint AW where A.airCentreWP = AW.awpPk and AW.awpLatDecimal between (X,Y) and AW.awpLonDecimal between (X,Y)");
  234. # Get it all for now ...
  235. $sth = $dbh->prepare("select * from tblTaskAirspace TA, tblAirspace A, tblAirspaceWaypoint AW where TA.tasPk=$tasPk and A.airPk=TA.airPk and A.airPk=AW.airPk order by A.airPk,AW.airOrder");
  236. $sth->execute();
  237. $airPk = 0;
  238. while ($ref = $sth->fetchrow_hashref())
  239. {
  240. my %coord;
  241. if ($ref->{'airPk'} != $airPk)
  242. {
  243. if ($airPk == 0)
  244. {
  245. $airPk = $ref->{'airPk'};
  246. }
  247. else
  248. {
  249. my @pts;
  250. my %na;
  251. @pts = @points;
  252. $nearair{'points'} = \@pts;
  253. %na = %nearair;
  254. push @allair, \%na;
  255. @points = ();
  256. %nearair = ();
  257. $airPk = $ref->{'airPk'};
  258. }
  259. }
  260. $nearair{'name'} = $ref->{'airName'};
  261. $nearair{'class'} = $ref->{'airClass'};
  262. $nearair{'base'} = $ref->{'airBase'};
  263. $nearair{'tops'} = $ref->{'airTops'};
  264. $nearair{'shape'} = $ref->{'airShape'};
  265. $nearair{'radius'} = $ref->{'airRadius'};
  266. $coord{'dlat'} = $ref->{'awpLatDecimal'};
  267. $coord{'dlong'} = $ref->{'awpLongDecimal'};
  268. $coord{'lat'} = $ref->{'awpLatDecimal'} * $pi / 180;
  269. $coord{'long'} = $ref->{'awpLongDecimal'} * $pi / 180;
  270. $coord{'cart'} = polar2cartesian(\%coord);
  271. #print "coord=",Dumper($coord), "\n";
  272. push @points, \%coord;
  273. }
  274. $nearair{'points'} = \@points;
  275. # Assuming only one point for circular regions
  276. $nearair{'centre'} = $points[0];
  277. push @allair, \%nearair;
  278. return \@allair;
  279. }
  280. sub get_all_tracks
  281. {
  282. my ($tasPk) = @_;
  283. my ($sth,$ref);
  284. my %ret;
  285. $sth = $dbh->prepare("select CTT.traPk, P.* from tblComTaskTrack CTT, tblTrack T, tblPilot P where CTT.traPk=T.traPk and P.pilPk=T.pilPk and CTT.tasPk=$tasPk");
  286. $sth->execute();
  287. while ($ref = $sth->fetchrow_hashref())
  288. {
  289. $ret{$ref->{'traPk'}} = $ref;
  290. }
  291. return \%ret;
  292. }
  293. #
  294. # Verify an entire task ...
  295. #
  296. my $tasPk = 0 + $ARGV[0];
  297. my $tracks;
  298. my $airspace;
  299. my $dist;
  300. my $name;
  301. $dbh = db_connect();
  302. $tracks = get_all_tracks($tasPk);
  303. #$tracks = [ 821, 823, 865 ];
  304. #$airspace = find_nearby_airspace($regPk, 100000.0);
  305. $airspace = find_task_airspace($tasPk);
  306. #print Dumper($airspace);
  307. # Go through all the tracks ..
  308. # might be more useful to print the names :-)
  309. for my $track (keys %$tracks)
  310. {
  311. print "Airspace check for track: $track\n";
  312. $dist = 0;
  313. $name = $tracks->{$track}->{'pilFirstName'} . " " . $tracks->{$track}->{'pilLastName'};
  314. if (($dist = airspace_check($track,$airspace)) > 0)
  315. {
  316. print " Maximum violation of $dist metres ($name).\n";
  317. }
  318. else
  319. {
  320. print " No violation ($name).\n";
  321. }
  322. }