3 ########################################################################
4 # Convert DAFIFT ARPT/ILS.TXT to FlightGear format.
5 ########################################################################
9 my($faa_ils_file) = shift(@ARGV);
10 my($dafift_arpt_file) = shift(@ARGV);
11 my($dafift_ils_file) = shift(@ARGV);
12 my($fgfs_ils_file) = shift(@ARGV);
13 my($output_file) = shift(@ARGV);
16 "<faa_ils_file> <dafift_arpt_file> <dafift_ils_file> <fgfs_ils_file> <output_file>\n"
17 if !defined($faa_ils_file) || !defined($dafift_arpt_file)
18 || !defined($dafift_ils_file) || !defined($fgfs_ils_file)
19 || !defined($output_file);
27 &load_dafift( $dafift_arpt_file, $dafift_ils_file );
28 &load_faa( $faa_ils_file );
29 &load_fgfs( $fgfs_ils_file );
30 &write_result( $output_file );
35 ########################################################################
37 ########################################################################
40 my( $arpt_file ) = shift;
41 my( $ils_file ) = shift;
45 my( $id, $rwy, $type );
46 my( $has_dme, $has_gs, $has_loc, $has_im, $has_mm, $has_om );
47 my( $dme_lon, $dme_lat, $dme_elev, $dme_bias );
48 my( $gs_lon, $gs_lat, $gs_elev, $gs_angle );
49 my( $loc_type, $loc_lon, $loc_lat, $loc_elev, $loc_freq, $loc_hdg,
50 $loc_width, $loc_id );
51 my( $im_lon, $im_lat, $mm_lon, $mm_lat, $om_lon, $om_lat );
53 # load airport file so we can lookup ICAO from internal ID
55 open( ARPT, "<$arpt_file" ) || die "Cannot open DAFIFT: $arpt_file\n";
57 <ARPT>; # skip header line
63 if ( length($icao) < 3 ) {
64 if ( length( $F[4] ) >= 3 ) {
70 $CODES{$F[0]} = $icao;
71 $CodesByICAO{$icao} = 1;
72 # print "$F[0] - $icao\n";
75 # Load the DAFIFT ils file
77 my( $last_id, $last_rwy ) = ("", "");
79 open( DAFIFT_ILS, "<$ils_file" ) || die "Cannot open DAFIFT: $ils_file\n";
81 <DAFIFT_ILS>; # skip header line
83 while ( <DAFIFT_ILS> ) {
89 if ( $last_id ne "" && ($last_id ne $id || $last_rwy ne $rwy) ) {
90 # just hist the start of the next record, dump the current data
93 ( $gs_elev, $gs_angle, $gs_lat, $gs_lon ) = ( 0, 0, 0, 0 );
96 ( $dme_lat, $dme_lon ) = ( 0, 0 );
99 ( $om_lat, $om_lon ) = ( 0, 0 );
102 ( $mm_lat, $mm_lon ) = ( 0, 0 );
105 ( $im_lat, $im_lon ) = ( 0, 0 );
107 if ( $ILS{$CODES{$last_id} . $last_rwy} eq "" ) {
108 print "DAFIFT adding: $CODES{$last_id} - $last_rwy\n";
109 &safe_add_record( $CODES{$last_id}, $last_rwy, "ILS",
110 $loc_freq, $loc_id, $loc_hdg, $loc_lat,
111 $loc_lon, $gs_elev, $gs_angle, $gs_lat,
112 $gs_lon, $dme_lat, $dme_lon, $om_lat,
113 $om_lon, $mm_lat, $mm_lon, $im_lat,
126 if ( $type eq "D" ) {
129 $dme_lon = make_dcoord( $F[16] );
130 $dme_lat = make_dcoord( $F[14] );
132 if ( $dme_elev !~ m/\d/ ) {
138 # print "$id DME $dme_lon $dme_lat $dme_elev $dme_bias\n";
139 } elsif ( $type eq "G" ) {
142 $gs_lon = make_dcoord( $F[16] );
143 $gs_lat = make_dcoord( $F[14] );
145 if ( $gs_elev !~ m/\d/ ) {
151 # print "$id GS $gs_lon $gs_lat $gs_elev $gs_angle\n";
152 } elsif ( $type eq "Z" ) {
155 $loc_lon = make_dcoord( $F[16] );
156 $loc_lat = make_dcoord( $F[14] );
158 if ( $loc_elev !~ m/\d/ ) {
163 ($loc_freq) = $F[5] =~ m/(\d\d\d\d\d\d)/;
165 my( $magvar ) = make_dmagvar( $F[22] );
166 # print "mag var = $F[22] (" . $magvar . ")\n";
167 $loc_hdg = $F[24] + make_dmagvar( $F[22] );
170 if ( length( $loc_id ) >= 4 ) {
173 # print "$id LOC $loc_lon $loc_lat $loc_elev $loc_freq $loc_hdg $loc_width\n";
174 } elsif ( $type eq "I" ) {
177 $im_lon = make_dcoord( $F[16] );
178 $im_lat = make_dcoord( $F[14] );
179 # print "$id IM $im_lon $im_lat\n";
180 } elsif ( $type eq "M" ) {
181 # Middle marker entry
183 $mm_lon = make_dcoord( $F[16] );
184 $mm_lat = make_dcoord( $F[14] );
185 # print "$id MM $mm_lon $mm_lat\n";
186 } elsif ( $type eq "O" ) {
189 $om_lon = make_dcoord( $F[16] );
190 $om_lat = make_dcoord( $F[14] );
191 # print "$id OM $om_lon $om_lat\n";
196 # printf("%-5s %10.6f %11.6f\n", $F[0], $F[14], $F[16]);
200 ( $gs_elev, $gs_angle, $gs_lat, $gs_lon ) = ( 0, 0, 0, 0 );
203 ( $dme_lat, $dme_lon ) = ( 0, 0 );
206 ( $om_lat, $om_lon ) = ( 0, 0 );
209 ( $mm_lat, $mm_lon ) = ( 0, 0 );
212 ( $im_lat, $im_lon ) = ( 0, 0 );
214 if ( $ILS{$CODES{$last_id} . $last_rwy} eq "" ) {
215 print "DAFIFT adding (last): $CODES{$last_id} - $last_rwy\n";
216 &safe_add_record( $CODES{$last_id}, $last_rwy, "ILS", $loc_freq,
217 $loc_id, $loc_hdg, $loc_lat, $loc_lon,
218 $gs_elev, $gs_angle, $gs_lat, $gs_lon,
219 $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat,
220 $mm_lon, $im_lat, $im_lon );
225 ########################################################################
227 ########################################################################
232 open( FAA_ILS, "<$file" ) || die "Cannot open FAA data: $file\n";
234 <FAA_ILS>; # skip header line
236 while ( <FAA_ILS> ) {
239 my ( $rec_type, $faa_id, $rwy, $type, $faa_date,
240 $faa_apt_name, $faa_city, $faa_st, $faa_state,
241 $faa_region, $id, $faa_len, $faa_wid, $faa_cat,
242 $faa_owner, $faa_operator, $faa_bearing, $faa_magvar,
243 $loc_type, $loc_id, $loc_freq, $faa_loc_latd,
244 $faa_loc_lats, $faa_loc_lond, $faa_loc_lons, $loc_width,
245 $faa_stop_dist, $faa_app_dist, $faa_gs_type, $gs_angle,
246 $faa_gs_freq, $faa_gs_latd, $faa_gs_lats, $faa_gs_lond,
247 $faa_gs_lons, $faa_gs_dist, $gs_elev, $faa_im_type,
248 $faa_im_latd, $faa_im_lats, $faa_im_lond, $faa_im_lons,
249 $faa_im_dist, $faa_mm_type, $faa_mm_id, $faa_mm_name,
250 $faa_mm_freq, $faa_mm_latd, $faa_mm_lats, $faa_mm_lond,
251 $faa_mm_lons, $faa_mm_dist, $faa_om_type, $faa_om_id,
252 $faa_om_name, $faa_om_freq, $faa_om_latd, $faa_om_lats,
253 $faa_om_lond, $faa_om_lons, $faa_om_dist,
254 $faa_om_backcourse, $faa_dme_channel, $faa_dme_latd,
255 $faa_dme_lats, $faa_dme_lond, $faa_dme_lons, $faa_dme_app_dist,
256 $faa_dme_stop_dist, $blank)
258 m/^(.{4})(.{11})(.{3})(.{10})(.{10})(.{42})(.{26})(.{2})(.{20})(.{3})(.{4})(.{5})(.{4})(.{9})(.{50})(.{50})(.{3})(.{3})(.{15})(.{5})(.{6})(.{14})(.{11})(.{14})(.{11})(.{5})(.{5})(.{6})(.{15})(.{4})(.{6})(.{14})(.{11})(.{14})(.{11})(.{6})(.{7})(.{15})(.{14})(.{11})(.{14})(.{11})(.{6})(.{15})(.{2})(.{5})(.{3})(.{14})(.{11})(.{14})(.{11})(.{6})(.{15})(.{2})(.{5})(.{3})(.{14})(.{11})(.{14})(.{11})(.{6})(.{9})(.{4})(.{14})(.{11})(.{14})(.{11})(.{6})(.{5})(.{34})/;
260 $id = &strip_ws( $id );
261 $rwy = &strip_ws( $rwy );
265 my( $loc_hdg ) = $faa_bearing + make_dmagvar($faa_magvar);
266 my( $loc_lat ) = make_dcoord($faa_loc_lats) / 3600.0;
267 my( $loc_lon ) = make_dcoord($faa_loc_lons) / 3600.0;
268 # print "$loc_lon $loc_lat $faa_loc_lons $faa_loc_lats\n";
269 my( $gs_lat ) = make_dcoord($faa_gs_lats) / 3600.0;
270 my( $gs_lon ) = make_dcoord($faa_gs_lons) / 3600.0;
271 my( $im_lat ) = make_dcoord($faa_im_lats) / 3600.0;
272 my( $im_lon ) = make_dcoord($faa_im_lons) / 3600.0;
273 my( $mm_lat ) = make_dcoord($faa_mm_lats) / 3600.0;
274 my( $mm_lon ) = make_dcoord($faa_mm_lons) / 3600.0;
275 my( $om_lat ) = make_dcoord($faa_om_lats) / 3600.0;
276 my( $om_lon ) = make_dcoord($faa_om_lons) / 3600.0;
277 my( $dme_lat ) = make_dcoord($faa_dme_lats) / 3600.0;
278 my( $dme_lon ) = make_dcoord($faa_dme_lons) / 3600.0;
281 # print "$id - $rwy\n";
283 # print "-> $key -> $ILS{$key}\n";
284 # $key = "K" . $id . $rwy;
285 # print "-> $key -> $ILS{$key}\n";
287 if ( $rec_type eq "ILS1" ) {
288 if ( length( $id ) < 4 ) {
289 if ( $CodesByICAO{"K" . $id} ) {
293 if ( $ILS{$id . $rwy} ne "" ) {
294 print "FAA updating: $id - $rwy $type\n";
295 &update_type( $id, $rwy, $type );
297 print "FAA adding: $id - $rwy\n";
298 &safe_add_record( $id, $rwy, $type, $loc_freq, $loc_id,
299 $loc_hdg, $loc_lat, $loc_lon, $gs_elev,
300 $gs_angle, $gs_lat, $gs_lon, $dme_lat,
301 $dme_lon, $om_lat, $om_lon, $mm_lat,
302 $mm_lon, $im_lat, $im_lon );
309 ########################################################################
310 # Process FlightGear ILS data
311 ########################################################################
314 my( $ils_file ) = shift;
316 open( FGILS, "zcat $ils_file|" ) || die "Cannot open FGFS: $ils_file\n";
318 <FGILS>; # skip header line
322 if ( ! m/\[End\]/ && length($_) > 1 ) {
324 my( $type_code, $type_name, $icao, $rwy, $loc_freq, $loc_id,
325 $loc_hdg, $loc_lat, $loc_lon, $gs_elev, $gs_angle, $gs_lat,
326 $gs_lon, $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat, $mm_lon,
327 $im_lat, $im_lon ) = split(/\s+/);
330 if ( $ILS{$icao . $rwy} ne "" ) {
331 # skip approaches already in FAA or DAFIFT data
332 } elsif ( $AIRPORTS{$icao} != 1 && $AIRPORTS{$code} != 1 ) {
333 # skip approaches if the FAA or DAFIFT data has any other
334 # approach already for this airport. (Avoids carrying over
335 # extraneous data if an approach was deactivated or runway
336 # numbers were changed.)
337 } elsif ( length( $icao ) < 4 || $icao =~ m/^K/ ) {
338 # skip USA approaches not found in FAA or DAFIFT data
340 print "FGFS adding: $icao $rwy\n";
341 &safe_add_record( $icao, $rwy, $type_name, $loc_freq, $loc_id,
342 $loc_hdg, $loc_lat, $loc_lon, $gs_elev,
343 $gs_angle, $gs_lat, $gs_lon, $dme_lat,
344 $dme_lon, $om_lat, $om_lon, $mm_lat,
345 $mm_lon, $im_lat, $im_lon );
348 print "FGFS discarding: $_\n";
354 ########################################################################
355 # Write out the accumulated combined result
356 ########################################################################
359 my( $outfile ) = shift;
361 open( OUT, ">$outfile" ) || die "Cannot write to: $outfile\n";
363 # dump out the final results
364 print OUT "// FlightGear ILS data, generated from DAFIFT ARPT/ILS.TXT and FAA data\n";
367 foreach $key ( sort (keys %ILS) ) {
368 print OUT "$ILS{$key}\n";
374 ########################################################################
376 ########################################################################
379 # add a record to the master list if it doesn't already exist
381 sub safe_add_record() {
382 my( $apt_id ) = shift;
385 my( $loc_freq ) = shift;
386 my( $loc_id ) = shift;
387 my( $loc_hdg ) = shift;
388 my( $loc_lat ) = shift;
389 my( $loc_lon ) = shift;
390 my( $gs_elev ) = shift;
391 my( $gs_angle ) = shift;
392 my( $gs_lat ) = shift;
393 my( $gs_lon ) = shift;
394 my( $dme_lat ) = shift;
395 my( $dme_lon ) = shift;
396 my( $om_lat ) = shift;
397 my( $om_lon ) = shift;
398 my( $mm_lat ) = shift;
399 my( $mm_lon ) = shift;
400 my( $im_lat ) = shift;
401 my( $im_lon ) = shift;
403 if ( $ILS{$apt_id . $rwy} eq "" ) {
404 # print "Safe adding (common): $apt_id - $rwy\n";
405 &update_record( $apt_id, $rwy, $type, $loc_freq, $loc_id,
406 $loc_hdg, $loc_lat, $loc_lon, $gs_elev,
407 $gs_angle, $gs_lat, $gs_lon, $dme_lat,
408 $dme_lon, $om_lat, $om_lon, $mm_lat,
409 $mm_lon, $im_lat, $im_lon );
414 # replace a record in the master list (or add it if it doesn't exist)
416 sub update_record() {
417 my( $apt_id ) = shift;
420 my( $loc_freq ) = shift;
421 my( $loc_id ) = shift;
422 my( $loc_hdg ) = shift;
423 my( $loc_lat ) = shift;
424 my( $loc_lon ) = shift;
425 my( $gs_elev ) = shift;
426 my( $gs_angle ) = shift;
427 my( $gs_lat ) = shift;
428 my( $gs_lon ) = shift;
429 my( $dme_lat ) = shift;
430 my( $dme_lon ) = shift;
431 my( $om_lat ) = shift;
432 my( $om_lon ) = shift;
433 my( $mm_lat ) = shift;
434 my( $mm_lon ) = shift;
435 my( $im_lat ) = shift;
436 my( $im_lon ) = shift;
440 # remap $type as needed
441 $type = &strip_ws( $type );
442 if ( $type eq "LOCALIZER" ) {
444 } elsif ( $type eq "ILS/DME" ) {
446 } elsif ( $type eq "SDF/DME" ) {
448 } elsif ( $type eq "LOC/DME" ) {
450 } elsif ( $type eq "LOC/GS" ) {
452 } elsif ( $type eq "LDA/DME" ) {
456 $record = sprintf( "%1s %-5s %-4s %-3s %06.2f %-4s %06.2f %10.6f %11.6f ",
457 substr( $type, 0, 1 ), $type, $apt_id, $rwy,
458 $loc_freq, $loc_id, $loc_hdg, $loc_lat, $loc_lon );
459 $record .= sprintf( "%5d %5.2f %10.6f %11.6f ",
460 $gs_elev, $gs_angle, $gs_lat, $gs_lon );
461 $record .= sprintf( "%10.6f %11.6f ", $dme_lat, $dme_lon );
462 $record .= sprintf( "%10.6f %11.6f ", $om_lat, $om_lon );
463 $record .= sprintf( "%10.6f %11.6f ", $mm_lat, $mm_lon );
464 $record .= sprintf( "%10.6f %11.6f ", $im_lat, $im_lon );
466 # print "Updating (common): $apt_id - $rwy\n";
467 $ILS{$apt_id . $rwy} = $record;
468 $AIRPORTS{$apt_id} = 1;
472 # update the $type of the record
474 my( $apt_id ) = shift;
476 my( $new_type ) = shift;
480 if ( $ILS{$apt_id . $rwy} ne "" ) {
481 my( $type_code, $type_name, $apt_id, $rwy, $loc_freq, $loc_id,
482 $loc_hdg, $loc_lat, $loc_lon, $gs_elev, $gs_angle, $gs_lat,
483 $gs_lon, $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat, $mm_lon,
484 $im_lat, $im_lon ) = split( /\s+/, $ILS{$apt_id . $rwy} );
485 # print "Updating type: $apt_id $rwy: $type_name -> $new_type\n";
486 $type_name = $new_type;
487 &update_record( $apt_id, $rwy, $type_name, $loc_freq, $loc_id,
488 $loc_hdg, $loc_lat, $loc_lon, $gs_elev,
489 $gs_angle, $gs_lat, $gs_lon, $dme_lat,
490 $dme_lon, $om_lat, $om_lon, $mm_lat,
491 $mm_lon, $im_lat, $im_lon );
493 die "Error, trying to update $apt_id - $rwy which doesn't exist\n";
498 # convert a lon/lat coordinate in various formats to signed decimal
502 my( $dir, $deg, $min, $sec );
505 $coord = &strip_ws( $coord );
507 if ( $coord =~ m/^[WE]/ ) {
508 ( $dir, $deg, $min, $sec )
509 = $coord =~ m/^([EW])(\d\d\d)(\d\d)(\d\d\d\d)/;
510 $value = $deg + $min/60.0 + ($sec/100)/3600.0;
514 } elsif ( $coord =~ m/^[NS]/ ) {
515 ( $dir, $deg, $min, $sec )
516 = $coord =~ m/^([NS])(\d\d)(\d\d)(\d\d\d\d)/;
517 $value = $deg + $min/60.0 + ($sec/100)/3600.0;
521 } elsif ( $coord =~ m/[EW]$/ ) {
522 ($value, $dir) = $coord =~ m/([\d\s\.]+)([EW])/;
526 } elsif ( $coord =~ m/[NS]$/ ) {
527 ($value, $dir) = $coord =~ m/([\d\s\.]+)([NS])/;
532 # print "$dir $deg:$min:$sec = $value\n";
536 # convert a magnetic variation in various formats to signed decimal
539 my( $coord ) = shift;
542 if ( $coord =~ m/^[EW]/ ) {
543 my( $dir, $deg, $min, $date )
544 = $coord =~ m/^([EW])(\d\d\d)(\d\d\d) (\d\d\d\d)/;
545 $value = $deg + ($min/10)/60.0;
549 } elsif ( $coord =~ m/[EW]$/ ) {
551 = $coord =~ m/^(\d\d)([EW])/;
557 # print "$dir $deg:$min = $value\n";
562 # strip white space off front and back of string
565 my( $string ) = shift;