]> git.mxchange.org Git - flightgear.git/blob - scripts/perl/dafif/build_ils.pl
Merge branch 'jmt/gps' into next
[flightgear.git] / scripts / perl / dafif / build_ils.pl
1 #!/usr/bin/perl
2
3 ########################################################################
4 # Convert DAFIFT ARPT/ILS.TXT to FlightGear format.
5 ########################################################################
6
7 use strict;
8
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($fgfs_apt_file) = shift(@ARGV);
14 my($output_file) = shift(@ARGV);
15
16 my($calc_loc)
17     = "/home/curt/projects/FlightGear-0.9/source/src/Airports/calc_loc";
18
19 die "Usage: $0 " .
20     "<faa_ils_file> <dafift_arpt_file> <dafift_ils_file> <xplane_ils_file> <fgfs_apt_file> <output_file>\n"
21     if !defined($faa_ils_file) || !defined($dafift_arpt_file)
22        || !defined($dafift_ils_file) || !defined($fgfs_ils_file)
23        || !defined($fgfs_apt_file) || !defined($output_file);
24
25 my( %CODES );
26 my( %CodesByICAO );
27 my( %Elevations );
28 my( %ILS );
29 my( %AIRPORTS );
30 my( %RUNWAYS );
31
32
33 &load_dafift( $dafift_arpt_file, $dafift_ils_file );
34 &load_faa( $faa_ils_file );
35 &load_fgfs( $fgfs_ils_file );
36 &load_fgfs_airports( $fgfs_apt_file );
37 &fix_localizer();
38 &fudge_missing_gs_elev();
39
40 &write_result( $output_file );
41
42 exit;
43
44
45 ########################################################################
46 # Process DAFIFT data
47 ########################################################################
48
49 sub load_dafift() {
50     my( $arpt_file ) = shift;
51     my( $ils_file ) = shift;
52
53     my( $record );
54
55     my( $id, $rwy, $type );
56     my( $has_dme, $has_gs, $has_loc, $has_im, $has_mm, $has_om );
57     my( $dme_lon, $dme_lat, $dme_elev, $dme_bias );
58     my( $gs_lon, $gs_lat, $gs_elev, $gs_angle );
59     my( $loc_type, $loc_lon, $loc_lat, $loc_elev, $loc_freq, $loc_hdg,
60         $loc_width, $loc_id );
61     my( $im_lon, $im_lat, $mm_lon, $mm_lat, $om_lon, $om_lat );
62
63     # load airport file so we can lookup ICAO from internal ID
64
65     open( ARPT, "<$arpt_file" ) || die "Cannot open DAFIFT: $arpt_file\n";
66
67     <ARPT>;                          # skip header line
68
69     while ( <ARPT> ) {
70         chomp;
71         my(@F) = split(/\t/);
72         my($icao) = $F[3];
73         if ( length($icao) < 3 ) {
74             if ( length( $F[4] ) >= 3 ) {
75                 $icao = $F[4];
76             } else {
77                 $icao = "[none]";
78             }
79         }
80         $CODES{$F[0]} = $icao;
81         $CodesByICAO{$icao} = 1;
82         # print "$F[0] - $icao\n";
83         $Elevations{$icao} = $F[11];
84         # print "$icao $F[11]\n";
85     }
86
87     # Load the DAFIFT ils file
88
89     my( $last_id, $last_rwy ) = ("", "");
90
91     open( DAFIFT_ILS, "<$ils_file" ) || die "Cannot open DAFIFT: $ils_file\n";
92
93     <DAFIFT_ILS>;                   # skip header line
94
95     while ( <DAFIFT_ILS> ) {
96         chomp;
97         my @F = split(/\t/);
98         $id = $F[0];
99         $rwy = $F[1];
100
101         if ( $last_id ne "" && ($last_id ne $id || $last_rwy ne $rwy) ) {
102             # just hist the start of the next record, dump the current data
103
104             if ( ! $has_gs ) {
105                 ( $gs_elev, $gs_angle, $gs_lat, $gs_lon ) = ( 0, 0, 0, 0 );
106             }
107             if ( ! $has_dme ) {
108                 ( $dme_lat, $dme_lon ) = ( 0, 0 );
109             }
110             if ( ! $has_om ) {
111                 ( $om_lat, $om_lon ) = ( 0, 0 );
112             }
113             if ( ! $has_mm ) {
114                 ( $mm_lat, $mm_lon ) = ( 0, 0 );
115             }
116             if ( ! $has_im ) {
117                 ( $im_lat, $im_lon ) = ( 0, 0 );
118             }
119             if ( $ILS{$CODES{$last_id} . $last_rwy} eq "" ) {
120                 print "DAFIFT adding: $CODES{$last_id} - $last_rwy\n";
121                 &safe_add_record( $CODES{$last_id}, $last_rwy, "ILS", 
122                                   $loc_freq, $loc_id, $loc_hdg, $loc_lat,
123                                   $loc_lon, $gs_elev, $gs_angle, $gs_lat,
124                                   $gs_lon, $dme_lat, $dme_lon, $om_lat,
125                                   $om_lon, $mm_lat, $mm_lon, $im_lat,
126                                   $im_lon );
127             }
128
129             $has_dme = 0;
130             $has_gs = 0;
131             $has_loc = 0;
132             $has_im = 0;
133             $has_mm = 0;
134             $has_om = 0;
135         }
136
137         $type = $F[2];
138         if ( $type eq "D" ) {
139             # DME entry
140             $has_dme = 1;
141             $dme_lon = make_dcoord( $F[16] );
142             $dme_lat = make_dcoord( $F[14] );
143             $dme_elev = $F[10];
144             if ( $dme_elev !~ m/\d/ ) {
145                 $dme_elev = "";
146             } else {
147                 $dme_elev += 0;
148             }
149             $dme_bias = $F[27];
150             # print "$id DME $dme_lon $dme_lat $dme_elev $dme_bias\n";
151         } elsif ( $type eq "G" ) {
152             # GlideSlope entry
153             $has_gs = 1;
154             $gs_lon = make_dcoord( $F[16] );
155             $gs_lat = make_dcoord( $F[14] );
156             $gs_elev = $F[10];
157             if ( $gs_elev !~ m/\d/ ) {
158                 $gs_elev = "";
159             } else {
160                 $gs_elev += 0;
161             }
162             $gs_angle = $F[7];
163             # print "$id GS $gs_lon $gs_lat $gs_elev $gs_angle\n";
164         } elsif ( $type eq "Z" ) {
165             # Localizer entry
166             $has_loc = 1;
167             $loc_lon = make_dcoord( $F[16] );
168             $loc_lat = make_dcoord( $F[14] );
169             $loc_elev = $F[10];
170             if ( $loc_elev !~ m/\d/ ) {
171                 $loc_elev = "";
172             } else {
173                 $loc_elev += 0;
174             }
175             ($loc_freq) = $F[5] =~ m/(\d\d\d\d\d\d)/;
176             $loc_freq /= 1000.0;
177             my( $magvar ) = make_dmagvar( $F[22] );
178             # print "mag var = $F[22] (" . $magvar . ")\n";
179             $loc_hdg = $F[24] + make_dmagvar( $F[22] );
180             $loc_width = $F[25];
181             $loc_id = $F[18];
182             if ( length( $loc_id ) >= 4 ) {
183                 $loc_id =~ s/^I//;
184             }
185             # print "$id LOC $loc_lon $loc_lat $loc_elev $loc_freq $loc_hdg $loc_width\n";
186         } elsif ( $type eq "I" ) {
187             # Inner marker entry
188             $has_im = 1;
189             $im_lon = make_dcoord( $F[16] );
190             $im_lat = make_dcoord( $F[14] );
191             # print "$id IM $im_lon $im_lat\n";
192         } elsif ( $type eq "M" ) {
193             # Middle marker entry
194             $has_mm = 1;
195             $mm_lon = make_dcoord( $F[16] );
196             $mm_lat = make_dcoord( $F[14] );
197             # print "$id MM $mm_lon $mm_lat\n";
198         } elsif ( $type eq "O" ) {
199             # Outer marker entry
200             $has_om = 1;
201             $om_lon = make_dcoord( $F[16] );
202             $om_lat = make_dcoord( $F[14] );
203             # print "$id OM $om_lon $om_lat\n";
204         }
205
206         $last_id = $id;
207         $last_rwy = $rwy;
208         # printf("%-5s %10.6f %11.6f\n",  $F[0], $F[14], $F[16]);
209     }
210
211     if ( ! $has_gs ) {
212         ( $gs_elev, $gs_angle, $gs_lat, $gs_lon ) = ( 0, 0, 0, 0 );
213     }
214     if ( ! $has_dme ) {
215         ( $dme_lat, $dme_lon ) = ( 0, 0 );
216     }
217     if ( ! $has_om ) {
218         ( $om_lat, $om_lon ) = ( 0, 0 );
219     }
220     if ( ! $has_mm ) {
221         ( $mm_lat, $mm_lon ) = ( 0, 0 );
222     }
223     if ( ! $has_im ) {
224         ( $im_lat, $im_lon ) = ( 0, 0 );
225     }
226     if ( $ILS{$CODES{$last_id} . $last_rwy} eq "" ) {
227         print "DAFIFT adding (last): $CODES{$last_id} - $last_rwy\n";
228         &safe_add_record( $CODES{$last_id}, $last_rwy, "ILS", $loc_freq,
229                           $loc_id, $loc_hdg, $loc_lat, $loc_lon,
230                           $gs_elev, $gs_angle, $gs_lat, $gs_lon,
231                           $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat,
232                           $mm_lon, $im_lat, $im_lon );
233     }
234 }
235
236
237 ########################################################################
238 # Process FAA data
239 ########################################################################
240
241 sub load_faa() {
242     my( $file ) = shift;
243
244     open( FAA_ILS, "<$file" ) || die "Cannot open FAA data: $file\n";
245
246     <FAA_ILS>;                          # skip header line
247
248     while ( <FAA_ILS> ) {
249         chomp;
250
251         my ( $rec_type, $faa_id, $rwy, $type, $faa_date,
252              $faa_apt_name, $faa_city, $faa_st, $faa_state,
253              $faa_region, $id, $faa_len, $faa_wid, $faa_cat,
254              $faa_owner, $faa_operator, $faa_bearing, $faa_magvar,
255              $loc_type, $loc_id, $loc_freq, $faa_loc_latd,
256              $faa_loc_lats, $faa_loc_lond, $faa_loc_lons, $loc_width,
257              $faa_stop_dist, $faa_app_dist, $faa_gs_type, $gs_angle,
258              $faa_gs_freq, $faa_gs_latd, $faa_gs_lats, $faa_gs_lond,
259              $faa_gs_lons, $faa_gs_dist, $gs_elev, $faa_im_type,
260              $faa_im_latd, $faa_im_lats, $faa_im_lond, $faa_im_lons,
261              $faa_im_dist, $faa_mm_type, $faa_mm_id, $faa_mm_name,
262              $faa_mm_freq, $faa_mm_latd, $faa_mm_lats, $faa_mm_lond,
263              $faa_mm_lons, $faa_mm_dist, $faa_om_type, $faa_om_id,
264              $faa_om_name, $faa_om_freq, $faa_om_latd, $faa_om_lats,
265              $faa_om_lond, $faa_om_lons, $faa_om_dist,
266              $faa_om_backcourse, $faa_dme_channel, $faa_dme_latd,
267              $faa_dme_lats, $faa_dme_lond, $faa_dme_lons, $faa_dme_app_dist,
268              $faa_dme_stop_dist, $blank)
269             = $_ =~
270             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})/;
271
272         $id = &strip_ws( $id );
273         $rwy = &strip_ws( $rwy );
274         $rwy =~ s/\/$//;
275         $rwy =~ s/\/$//;
276         $loc_id =~ s/^I-//;
277         my( $loc_hdg ) = $faa_bearing + make_dmagvar($faa_magvar);
278         my( $loc_lat ) = make_dcoord($faa_loc_lats) / 3600.0;
279         my( $loc_lon ) = make_dcoord($faa_loc_lons) / 3600.0;
280         # print "$loc_lon $loc_lat $faa_loc_lons $faa_loc_lats\n";
281         my( $gs_lat ) = make_dcoord($faa_gs_lats) / 3600.0;
282         my( $gs_lon ) = make_dcoord($faa_gs_lons) / 3600.0;
283         my( $im_lat ) = make_dcoord($faa_im_lats) / 3600.0;
284         my( $im_lon ) = make_dcoord($faa_im_lons) / 3600.0;
285         my( $mm_lat ) = make_dcoord($faa_mm_lats) / 3600.0;
286         my( $mm_lon ) = make_dcoord($faa_mm_lons) / 3600.0;
287         my( $om_lat ) = make_dcoord($faa_om_lats) / 3600.0;
288         my( $om_lon ) = make_dcoord($faa_om_lons) / 3600.0;
289         my( $dme_lat ) = make_dcoord($faa_dme_lats) / 3600.0;
290         my( $dme_lon ) = make_dcoord($faa_dme_lons) / 3600.0;
291
292         # my( $key );
293         # print "$id - $rwy\n";
294         # $key = $id . $rwy;
295         # print "-> $key -> $ILS{$key}\n";
296         # $key = "K" . $id . $rwy;
297         # print "-> $key -> $ILS{$key}\n";
298
299         if ( $rec_type eq "ILS1" ) {
300             if ( length( $id ) < 4 ) {
301                 if ( $CodesByICAO{"K" . $id} ) {
302                     $id = "K" . $id;
303                 }
304             }
305             if ( $ILS{$id . $rwy} ne "" ) {
306                 print "FAA updating: $id - $rwy $type\n";
307                 &update_type( $id, $rwy, $type );
308                 if ( $gs_elev > 0 ) {
309                     &maybe_update_gs_elev( $id . $rwy, $gs_elev );
310                 }
311             } else {
312                 print "FAA adding: $id - $rwy\n";
313                 &safe_add_record( $id, $rwy, $type, $loc_freq, $loc_id,
314                                   $loc_hdg, $loc_lat, $loc_lon, $gs_elev,
315                                   $gs_angle, $gs_lat, $gs_lon, $dme_lat,
316                                   $dme_lon, $om_lat, $om_lon, $mm_lat,
317                                   $mm_lon, $im_lat, $im_lon );
318             }
319         }
320     }
321 }
322
323
324 ########################################################################
325 # Process FlightGear ILS data
326 ########################################################################
327
328 sub load_fgfs() {
329     my( $ils_file ) = shift;
330
331     open( FGILS, "zcat $ils_file|" ) || die "Cannot open FGFS: $ils_file\n";
332
333     <FGILS>;                          # skip header line
334
335     while ( <FGILS> ) {
336         chomp;
337         if ( ! m/\[End\]/ && length($_) > 1 ) {
338             # print "$_\n";
339             my( $type_code, $type_name, $icao, $rwy, $loc_freq, $loc_id,
340                 $loc_hdg, $loc_lat, $loc_lon, $gs_elev, $gs_angle, $gs_lat,
341                 $gs_lon, $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat, $mm_lon,
342                 $im_lat, $im_lon ) = split(/\s+/);
343             my( $code ) = $icao;
344             $code =~ s/^K//;
345             if ( $ILS{$icao . $rwy} ne "" ) {
346                 print "FGFS: Skipping $icao - $rwy - already exists\n";
347                 # skip approaches already in FAA or DAFIFT data
348             } elsif ( length( $icao ) < 4 || $icao =~ m/^K/ ) {
349                 print "FGFS: Skipping $icao - $rwy - USA\n";
350                 # skip USA approaches not found in FAA or DAFIFT data
351             } else {
352                 print "FGFS adding: $icao $rwy\n";
353                 &safe_add_record( $icao, $rwy, $type_name, $loc_freq, $loc_id,
354                                   $loc_hdg, $loc_lat, $loc_lon, $gs_elev,
355                                   $gs_angle, $gs_lat, $gs_lon, $dme_lat,
356                                   $dme_lon, $om_lat, $om_lon, $mm_lat,
357                                   $mm_lon, $im_lat, $im_lon );
358             }
359         } else {
360             print "FGFS discarding: $_\n";
361         }
362     }
363 }
364
365
366 ########################################################################
367 # Load the x-plane/flightgear airport/runway information
368 ########################################################################
369
370 sub load_fgfs_airports() {
371     my( $infile ) = shift;
372
373     my( $id );
374
375     open( IN, "zcat $infile|" ) || die "Cannot open: $infile\n";
376
377     <IN>;                       # skip header line
378
379     while ( <IN> ) {
380         chomp;
381         my(@F) = split(/\s+/);
382         if ( $F[0] eq "A" ) {
383             $id = $F[1];
384         } elsif ( $F[0] eq "R" ) {
385             my($recip) = &rwy_recip( $F[1] );
386             $RUNWAYS{ $id . $F[1] } = "FOR $_";
387             $RUNWAYS{ $id . $recip } = "REV $_";
388         }
389     }
390 }
391
392
393 ########################################################################
394 # Run through the final ils list and adjust the heading to match the runway
395 ########################################################################
396
397 sub fix_localizer() {
398     my( $key );
399     foreach $key ( sort (keys %ILS) ) {
400         my(@F) = split( /\s+/, $ILS{$key} );
401         print "FIXING: $key $F[2] $F[3] $F[4]\n";
402         if ( $RUNWAYS{$key} ) {
403             my(@G) = split( /\s+/, $RUNWAYS{$key} );
404             print "  LocPos = $F[7] $F[8]\n";
405             print "  RwyInfo = $G[0] $G[3] $G[4] $G[5] $G[6] $G[7]\n";
406             my($cmd)
407                 = "$calc_loc $F[7] $F[8] $G[0] $G[3] $G[4] $G[5] $G[6] $G[7]";
408             open( CALC, "$cmd |" ) || die "Cannot open $calc_loc\n";
409             my( $j, $lat, $lon );
410             while ( <CALC> ) {
411                 if ( m/New localizer/ ) {
412                     ($j, $j, $j, $lat, $lon) = split( /\s+/ );
413                 }
414             }
415             if ( $G[0] eq "REV" ) {
416                 $G[5] += 180.0;
417                 if ( $G[5] >= 360.0 ) {
418                     $G[5] -= 360.0;
419                 }
420             }
421             &update_loc( $F[2], $F[3], $lat, $lon, $G[5]);
422         }
423     }
424 }
425
426
427 ########################################################################
428 # Run through the final ils list and fixup any missing glide slope elevations
429 # to match that of the field elevation.
430 ########################################################################
431
432 sub fudge_missing_gs_elev() {
433     my( $key );
434     foreach $key ( sort (keys %ILS) ) {
435         my(@F) = split( /\s+/, $ILS{$key} );
436         if ( $F[9] <= 0 ) {            
437             print "FUDGING GS: $key $F[2] $F[3] $F[4] - $F[9]\n";
438             &maybe_update_gs_elev( $key, $Elevations{$F[2]});
439         }
440     }
441 }
442
443
444 ########################################################################
445 # Write out the accumulated combined result
446 ########################################################################
447
448 sub write_result() {
449     my( $outfile ) = shift;
450
451     open( OUT, ">$outfile" )  || die "Cannot write to: $outfile\n";
452
453     # dump out the final results
454     print OUT "// FlightGear ILS data, generated from DAFIFT ARPT/ILS.TXT and FAA data\n";
455
456     my( $key );
457     foreach $key ( sort (keys %ILS) ) {
458         print OUT "$ILS{$key}\n";
459     }
460     print OUT "[End]\n";
461 }
462
463
464 ########################################################################
465 # Utility functions
466 ########################################################################
467
468
469 # add a record to the master list if it doesn't already exist
470
471 sub safe_add_record() {
472     my( $apt_id ) = shift;
473     my( $rwy ) = shift;
474     my( $type ) = shift;
475     my( $loc_freq ) = shift;
476     my( $loc_id ) = shift;
477     my( $loc_hdg ) = shift;
478     my( $loc_lat ) = shift;
479     my( $loc_lon ) = shift;
480     my( $gs_elev ) = shift;
481     my( $gs_angle ) = shift;
482     my( $gs_lat ) = shift;
483     my( $gs_lon ) = shift;
484     my( $dme_lat ) = shift;
485     my( $dme_lon ) = shift;
486     my( $om_lat ) = shift;
487     my( $om_lon ) = shift;
488     my( $mm_lat ) = shift;
489     my( $mm_lon ) = shift;
490     my( $im_lat ) = shift;
491     my( $im_lon ) = shift;
492
493     if ( $ILS{$apt_id . $rwy} eq "" ) {
494         # print "Safe adding (common): $apt_id - $rwy\n";
495         &update_record( $apt_id, $rwy, $type, $loc_freq, $loc_id,
496                         $loc_hdg, $loc_lat, $loc_lon, $gs_elev,
497                         $gs_angle, $gs_lat, $gs_lon, $dme_lat,
498                         $dme_lon, $om_lat, $om_lon, $mm_lat,
499                         $mm_lon, $im_lat, $im_lon );
500     }
501 }
502
503
504 # replace a record in the master list (or add it if it doesn't exist)
505
506 sub update_record() {
507     my( $apt_id ) = shift;
508     my( $rwy ) = shift;
509     my( $type ) = shift;
510     my( $loc_freq ) = shift;
511     my( $loc_id ) = shift;
512     my( $loc_hdg ) = shift;
513     my( $loc_lat ) = shift;
514     my( $loc_lon ) = shift;
515     my( $gs_elev ) = shift;
516     my( $gs_angle ) = shift;
517     my( $gs_lat ) = shift;
518     my( $gs_lon ) = shift;
519     my( $dme_lat ) = shift;
520     my( $dme_lon ) = shift;
521     my( $om_lat ) = shift;
522     my( $om_lon ) = shift;
523     my( $mm_lat ) = shift;
524     my( $mm_lon ) = shift;
525     my( $im_lat ) = shift;
526     my( $im_lon ) = shift;
527
528     my( $record );
529
530     # remap $type as needed
531     $type = &strip_ws( $type );
532     if ( $type eq "LOCALIZER" ) {
533         $type = "LOC";
534     } elsif ( $type eq "ILS/DME" ) {
535         $type = "ILS";
536     } elsif ( $type eq "SDF/DME" ) {
537         $type = "SDF";
538     } elsif ( $type eq "LOC/DME" ) {
539         $type = "ILS";
540     } elsif ( $type eq "LOC/GS" ) {
541         $type = "LOC";
542     } elsif ( $type eq "LDA/DME" ) {
543         $type = "LDA";
544     }
545
546     $record = sprintf( "%1s %-5s %-4s %-3s  %06.2f %-4s %06.2f %10.6f %11.6f ",
547                        substr( $type, 0, 1 ), $type, $apt_id, $rwy,
548                        $loc_freq, $loc_id, $loc_hdg, $loc_lat, $loc_lon );
549     $record .= sprintf( "%5d %5.2f %10.6f %11.6f ",
550                         $gs_elev, $gs_angle, $gs_lat, $gs_lon );
551     $record .= sprintf( "%10.6f %11.6f ", $dme_lat, $dme_lon );
552     $record .= sprintf( "%10.6f %11.6f ", $om_lat, $om_lon );
553     $record .= sprintf( "%10.6f %11.6f ", $mm_lat, $mm_lon );
554     $record .= sprintf( "%10.6f %11.6f ", $im_lat, $im_lon );
555
556     # print "Updating (common): $apt_id - $rwy\n";
557     $ILS{$apt_id . $rwy} = $record;
558     $AIRPORTS{$apt_id} = 1;
559 }
560
561
562 # update the $type of the record
563 sub update_type() {
564     my( $apt_id ) = shift;
565     my( $rwy ) = shift;
566     my( $new_type ) = shift;
567
568     my( $record );
569
570     if ( $ILS{$apt_id . $rwy} ne "" ) {
571         my( $type_code, $type_name, $apt_id, $rwy, $loc_freq, $loc_id,
572             $loc_hdg, $loc_lat, $loc_lon, $gs_elev, $gs_angle, $gs_lat,
573             $gs_lon, $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat, $mm_lon,
574             $im_lat, $im_lon ) = split( /\s+/, $ILS{$apt_id . $rwy} );
575         # print "Updating type: $apt_id $rwy: $type_name -> $new_type\n";
576         $type_name = $new_type;
577         &update_record( $apt_id, $rwy, $type_name, $loc_freq, $loc_id,
578                         $loc_hdg, $loc_lat, $loc_lon, $gs_elev,
579                         $gs_angle, $gs_lat, $gs_lon, $dme_lat,
580                         $dme_lon, $om_lat, $om_lon, $mm_lat,
581                         $mm_lon, $im_lat, $im_lon );
582     } else {
583         die "Error, trying to update $apt_id - $rwy which doesn't exist\n";
584     }
585 }
586
587
588 # update the glide slope elevation of the record (but only if it is 0)
589 sub maybe_update_gs_elev() {
590     my( $key ) = shift;
591     my( $new_gs_elev ) = shift;
592
593     my( $record );
594
595     if ( $ILS{$key} ne "" ) {
596         my( $type_code, $type_name, $apt_id, $rwy, $loc_freq, $loc_id,
597             $loc_hdg, $loc_lat, $loc_lon, $gs_elev, $gs_angle, $gs_lat,
598             $gs_lon, $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat, $mm_lon,
599             $im_lat, $im_lon ) = split( /\s+/, $ILS{$key} );
600         if ( $gs_elev == 0 ) {
601             print "Updating gs elev: $apt_id $rwy: $gs_elev -> $new_gs_elev\n";
602             $gs_elev = $new_gs_elev;
603             &update_record( $apt_id, $rwy, $type_name, $loc_freq, $loc_id,
604                             $loc_hdg, $loc_lat, $loc_lon, $gs_elev,
605                             $gs_angle, $gs_lat, $gs_lon, $dme_lat,
606                             $dme_lon, $om_lat, $om_lon, $mm_lat,
607                             $mm_lon, $im_lat, $im_lon );
608         }
609     } else {
610         die "Error, trying to update $key which doesn't exist\n";
611     }
612 }
613
614
615 # update the localizer position of the record
616 sub update_loc() {
617     my( $apt_id ) = shift;
618     my( $rwy ) = shift;
619     my( $nloc_lat ) = shift;
620     my( $nloc_lon ) = shift;
621     my( $nloc_hdg ) = shift;
622
623     my( $record );
624
625     if ( $ILS{$apt_id . $rwy} ne "" ) {
626         my( $type_code, $type_name, $apt_id, $rwy, $loc_freq, $loc_id,
627             $loc_hdg, $loc_lat, $loc_lon, $gs_elev, $gs_angle, $gs_lat,
628             $gs_lon, $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat, $mm_lon,
629             $im_lat, $im_lon ) = split( /\s+/, $ILS{$apt_id . $rwy} );
630         print "    Old pos = " . $loc_lat . "," . $loc_lon . " " . $loc_hdg . "\n";
631         print "    New pos = " . $nloc_lat . "," . $nloc_lon . " " . $nloc_hdg . "\n";
632         $loc_lat = $nloc_lat;
633         $loc_lon = $nloc_lon;
634         $loc_hdg = $nloc_hdg;
635         &update_record( $apt_id, $rwy, $type_name, $loc_freq, $loc_id,
636                         $loc_hdg, $loc_lat, $loc_lon, $gs_elev,
637                         $gs_angle, $gs_lat, $gs_lon, $dme_lat,
638                         $dme_lon, $om_lat, $om_lon, $mm_lat,
639                         $mm_lon, $im_lat, $im_lon );
640     } else {
641         die "Error, trying to update $apt_id - $rwy which doesn't exist\n";
642     }
643 }
644
645
646 # convert a lon/lat coordinate in various formats to signed decimal
647
648 sub make_dcoord() {
649     my($coord) = shift;
650     my( $dir, $deg, $min, $sec );
651     my( $value ) = 0.0;
652
653     $coord = &strip_ws( $coord );
654
655     if ( $coord =~ m/^[WE]/ ) {
656         ( $dir, $deg, $min, $sec )
657             = $coord =~ m/^([EW])(\d\d\d)(\d\d)(\d\d\d\d)/;
658         $value = $deg + $min/60.0 + ($sec/100)/3600.0;
659         if ( $dir eq "W" ) {
660             $value = -$value;
661         }
662     } elsif ( $coord =~ m/^[NS]/ ) {
663         ( $dir, $deg, $min, $sec )
664             = $coord =~ m/^([NS])(\d\d)(\d\d)(\d\d\d\d)/;
665         $value = $deg + $min/60.0 + ($sec/100)/3600.0;
666         if ( $dir eq "S" ) {
667             $value = -$value;
668         }
669     } elsif ( $coord =~ m/[EW]$/ ) {
670         ($value, $dir) = $coord =~ m/([\d\s\.]+)([EW])/;
671         if ( $dir eq "W" ) {
672             $value = -$value;
673         }
674     } elsif ( $coord =~ m/[NS]$/ ) {
675         ($value, $dir) = $coord =~ m/([\d\s\.]+)([NS])/;
676         if ( $dir eq "S" ) {
677             $value = -$value;
678         }
679     }
680     # print "$dir $deg:$min:$sec = $value\n";
681     return $value;
682 }
683
684 # convert a magnetic variation in various formats to signed decimal
685
686 sub make_dmagvar() {
687     my( $coord ) = shift;
688     my( $value );
689
690     if ( $coord =~ m/^[EW]/ ) {
691         my( $dir, $deg, $min, $date )
692             = $coord =~ m/^([EW])(\d\d\d)(\d\d\d) (\d\d\d\d)/;
693         $value = $deg + ($min/10)/60.0;
694         if ( $dir eq "W" ) {
695             $value = -$value;
696         }
697     } elsif ( $coord =~ m/[EW]$/ ) {
698         my( $deg, $dir )
699             = $coord =~ m/^(\d\d)([EW])/;
700         $value = $deg;
701         if ( $dir eq "W" ) {
702             $value = -$value;
703         }
704     }
705     # print "$dir $deg:$min = $value\n";
706
707     return $value;
708 }
709
710 # strip white space off front and back of string
711
712 sub strip_ws() {
713     my( $string ) = shift;
714     $string =~ s/^\s+//;
715     $string =~ s/\s+$//;
716     return $string;
717 }
718
719
720 # return the reciprical runway number
721 sub rwy_recip() {
722     my( $input ) = shift;
723
724     my($num, $letter);
725
726     if ( length($input) == 3 ) {
727         ($num, $letter) = $input =~ m/(\d\d)(.)/;
728     } elsif ( length($input) == 2 ) {
729         $num = $input;
730         $letter = "";
731     } else {
732         $num = "";
733         $letter = $input;
734     }
735     # print "RWY: $num - $letter <==> ";
736
737     if ( $num ne "" ) {
738         $num += 18;
739         if ( $num > 35 ) {
740             $num -= 36;
741         }
742         if ( $num < 10 ) {
743             $num = "0$num";
744         }
745     }
746
747     if ( $letter eq "R" ) {
748         $letter = "L";
749     } elsif ( $letter eq "L" ) {
750         $letter = "R";
751     } elsif ( $letter eq "C" ) {
752         $letter = "C";
753     } elsif ( $letter eq "N" ) {
754         $letter = "S";
755     } elsif ( $letter eq "S" ) {
756         $letter = "N";
757     } elsif ( $letter eq "E" ) {
758         $letter = "W";
759     } elsif ( $letter eq "W" ) {
760         $letter = "E";
761     }
762     # print "$num - $letter\n";
763
764     return "$num$letter";
765 }