]> git.mxchange.org Git - flightgear.git/blob - scripts/perl/dafif/dafift2ils.pl
Merge branch 'jmt/gps' into next
[flightgear.git] / scripts / perl / dafif / dafift2ils.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($output_file) = shift(@ARGV);
14
15 die "Usage: $0 " .
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);
20
21 my( %CODES );
22 my( %CodesByICAO );
23 my( %ILS );
24 my( %AIRPORTS );
25
26
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 );
31
32 exit;
33
34
35 ########################################################################
36 # Process DAFIFT data
37 ########################################################################
38
39 sub load_dafift() {
40     my( $arpt_file ) = shift;
41     my( $ils_file ) = shift;
42
43     my( $record );
44
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 );
52
53     # load airport file so we can lookup ICAO from internal ID
54
55     open( ARPT, "<$arpt_file" ) || die "Cannot open DAFIFT: $arpt_file\n";
56
57     <ARPT>;                          # skip header line
58
59     while ( <ARPT> ) {
60         chomp;
61         my(@F) = split(/\t/);
62         my($icao) = $F[3];
63         if ( length($icao) < 3 ) {
64             if ( length( $F[4] ) >= 3 ) {
65                 $icao = $F[4];
66             } else {
67                 $icao = "[none]";
68             }
69         }
70         $CODES{$F[0]} = $icao;
71         $CodesByICAO{$icao} = 1;
72         # print "$F[0] - $icao\n";
73     }
74
75     # Load the DAFIFT ils file
76
77     my( $last_id, $last_rwy ) = ("", "");
78
79     open( DAFIFT_ILS, "<$ils_file" ) || die "Cannot open DAFIFT: $ils_file\n";
80
81     <DAFIFT_ILS>;                   # skip header line
82
83     while ( <DAFIFT_ILS> ) {
84         chomp;
85         my @F = split(/\t/);
86         $id = $F[0];
87         $rwy = $F[1];
88
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
91
92             if ( ! $has_gs ) {
93                 ( $gs_elev, $gs_angle, $gs_lat, $gs_lon ) = ( 0, 0, 0, 0 );
94             }
95             if ( ! $has_dme ) {
96                 ( $dme_lat, $dme_lon ) = ( 0, 0 );
97             }
98             if ( ! $has_om ) {
99                 ( $om_lat, $om_lon ) = ( 0, 0 );
100             }
101             if ( ! $has_mm ) {
102                 ( $mm_lat, $mm_lon ) = ( 0, 0 );
103             }
104             if ( ! $has_im ) {
105                 ( $im_lat, $im_lon ) = ( 0, 0 );
106             }
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,
114                                   $im_lon );
115             }
116
117             $has_dme = 0;
118             $has_gs = 0;
119             $has_loc = 0;
120             $has_im = 0;
121             $has_mm = 0;
122             $has_om = 0;
123         }
124
125         $type = $F[2];
126         if ( $type eq "D" ) {
127             # DME entry
128             $has_dme = 1;
129             $dme_lon = make_dcoord( $F[16] );
130             $dme_lat = make_dcoord( $F[14] );
131             $dme_elev = $F[10];
132             if ( $dme_elev !~ m/\d/ ) {
133                 $dme_elev = "";
134             } else {
135                 $dme_elev += 0;
136             }
137             $dme_bias = $F[27];
138             # print "$id DME $dme_lon $dme_lat $dme_elev $dme_bias\n";
139         } elsif ( $type eq "G" ) {
140             # GlideSlope entry
141             $has_gs = 1;
142             $gs_lon = make_dcoord( $F[16] );
143             $gs_lat = make_dcoord( $F[14] );
144             $gs_elev = $F[10];
145             if ( $gs_elev !~ m/\d/ ) {
146                 $gs_elev = "";
147             } else {
148                 $gs_elev += 0;
149             }
150             $gs_angle = $F[7];
151             # print "$id GS $gs_lon $gs_lat $gs_elev $gs_angle\n";
152         } elsif ( $type eq "Z" ) {
153             # Localizer entry
154             $has_loc = 1;
155             $loc_lon = make_dcoord( $F[16] );
156             $loc_lat = make_dcoord( $F[14] );
157             $loc_elev = $F[10];
158             if ( $loc_elev !~ m/\d/ ) {
159                 $loc_elev = "";
160             } else {
161                 $loc_elev += 0;
162             }
163             ($loc_freq) = $F[5] =~ m/(\d\d\d\d\d\d)/;
164             $loc_freq /= 1000.0;
165             my( $magvar ) = make_dmagvar( $F[22] );
166             # print "mag var = $F[22] (" . $magvar . ")\n";
167             $loc_hdg = $F[24] + make_dmagvar( $F[22] );
168             $loc_width = $F[25];
169             $loc_id = $F[18];
170             if ( length( $loc_id ) >= 4 ) {
171                 $loc_id =~ s/^I//;
172             }
173             # print "$id LOC $loc_lon $loc_lat $loc_elev $loc_freq $loc_hdg $loc_width\n";
174         } elsif ( $type eq "I" ) {
175             # Inner marker entry
176             $has_im = 1;
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
182             $has_mm = 1;
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" ) {
187             # Outer marker entry
188             $has_om = 1;
189             $om_lon = make_dcoord( $F[16] );
190             $om_lat = make_dcoord( $F[14] );
191             # print "$id OM $om_lon $om_lat\n";
192         }
193
194         $last_id = $id;
195         $last_rwy = $rwy;
196         # printf("%-5s %10.6f %11.6f\n",  $F[0], $F[14], $F[16]);
197     }
198
199     if ( ! $has_gs ) {
200         ( $gs_elev, $gs_angle, $gs_lat, $gs_lon ) = ( 0, 0, 0, 0 );
201     }
202     if ( ! $has_dme ) {
203         ( $dme_lat, $dme_lon ) = ( 0, 0 );
204     }
205     if ( ! $has_om ) {
206         ( $om_lat, $om_lon ) = ( 0, 0 );
207     }
208     if ( ! $has_mm ) {
209         ( $mm_lat, $mm_lon ) = ( 0, 0 );
210     }
211     if ( ! $has_im ) {
212         ( $im_lat, $im_lon ) = ( 0, 0 );
213     }
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 );
221     }
222 }
223
224
225 ########################################################################
226 # Process FAA data
227 ########################################################################
228
229 sub load_faa() {
230     my( $file ) = shift;
231
232     open( FAA_ILS, "<$file" ) || die "Cannot open FAA data: $file\n";
233
234     <FAA_ILS>;                          # skip header line
235
236     while ( <FAA_ILS> ) {
237         chomp;
238
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)
257             = $_ =~
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})/;
259
260         $id = &strip_ws( $id );
261         $rwy = &strip_ws( $rwy );
262         $rwy =~ s/\/$//;
263         $rwy =~ s/\/$//;
264         $loc_id =~ s/^I-//;
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;
279
280         # my( $key );
281         # print "$id - $rwy\n";
282         # $key = $id . $rwy;
283         # print "-> $key -> $ILS{$key}\n";
284         # $key = "K" . $id . $rwy;
285         # print "-> $key -> $ILS{$key}\n";
286
287         if ( $rec_type eq "ILS1" ) {
288             if ( length( $id ) < 4 ) {
289                 if ( $CodesByICAO{"K" . $id} ) {
290                     $id = "K" . $id;
291                 }
292             }
293             if ( $ILS{$id . $rwy} ne "" ) {
294                 print "FAA updating: $id - $rwy $type\n";
295                 &update_type( $id, $rwy, $type );
296             } else {
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 );
303             }
304         }
305     }
306 }
307
308
309 ########################################################################
310 # Process FlightGear ILS data
311 ########################################################################
312
313 sub load_fgfs() {
314     my( $ils_file ) = shift;
315
316     open( FGILS, "zcat $ils_file|" ) || die "Cannot open FGFS: $ils_file\n";
317
318     <FGILS>;                          # skip header line
319
320     while ( <FGILS> ) {
321         chomp;
322         if ( ! m/\[End\]/ && length($_) > 1 ) {
323             # print "$_\n";
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+/);
328             my( $code ) = $icao;
329             $code =~ s/^K//;
330             if ( $ILS{$icao . $rwy} ne "" ) {
331                 print "FGFS: Skipping $icao - $rwy - already exists\n";
332                 # skip approaches already in FAA or DAFIFT data
333             } elsif ( length( $icao ) < 4 || $icao =~ m/^K/ ) {
334                 print "FGFS: Skipping $icao - $rwy - USA\n";
335                 # skip USA approaches not found in FAA or DAFIFT data
336             } else {
337                 print "FGFS adding: $icao $rwy\n";
338                 &safe_add_record( $icao, $rwy, $type_name, $loc_freq, $loc_id,
339                                   $loc_hdg, $loc_lat, $loc_lon, $gs_elev,
340                                   $gs_angle, $gs_lat, $gs_lon, $dme_lat,
341                                   $dme_lon, $om_lat, $om_lon, $mm_lat,
342                                   $mm_lon, $im_lat, $im_lon );
343             }
344         } else {
345             print "FGFS discarding: $_\n";
346         }
347     }
348 }
349
350
351 ########################################################################
352 # Write out the accumulated combined result
353 ########################################################################
354
355 sub write_result() {
356     my( $outfile ) = shift;
357
358     open( OUT, ">$outfile" )  || die "Cannot write to: $outfile\n";
359
360     # dump out the final results
361     print OUT "// FlightGear ILS data, generated from DAFIFT ARPT/ILS.TXT and FAA data\n";
362
363     my( $key );
364     foreach $key ( sort (keys %ILS) ) {
365         print OUT "$ILS{$key}\n";
366     }
367     print OUT "[End]\n";
368 }
369
370
371 ########################################################################
372 # Utility functions
373 ########################################################################
374
375
376 # add a record to the master list if it doesn't already exist
377
378 sub safe_add_record() {
379     my( $apt_id ) = shift;
380     my( $rwy ) = shift;
381     my( $type ) = shift;
382     my( $loc_freq ) = shift;
383     my( $loc_id ) = shift;
384     my( $loc_hdg ) = shift;
385     my( $loc_lat ) = shift;
386     my( $loc_lon ) = shift;
387     my( $gs_elev ) = shift;
388     my( $gs_angle ) = shift;
389     my( $gs_lat ) = shift;
390     my( $gs_lon ) = shift;
391     my( $dme_lat ) = shift;
392     my( $dme_lon ) = shift;
393     my( $om_lat ) = shift;
394     my( $om_lon ) = shift;
395     my( $mm_lat ) = shift;
396     my( $mm_lon ) = shift;
397     my( $im_lat ) = shift;
398     my( $im_lon ) = shift;
399
400     if ( $ILS{$apt_id . $rwy} eq "" ) {
401         # print "Safe adding (common): $apt_id - $rwy\n";
402         &update_record( $apt_id, $rwy, $type, $loc_freq, $loc_id,
403                         $loc_hdg, $loc_lat, $loc_lon, $gs_elev,
404                         $gs_angle, $gs_lat, $gs_lon, $dme_lat,
405                         $dme_lon, $om_lat, $om_lon, $mm_lat,
406                         $mm_lon, $im_lat, $im_lon );
407     }
408 }
409
410
411 # replace a record in the master list (or add it if it doesn't exist)
412
413 sub update_record() {
414     my( $apt_id ) = shift;
415     my( $rwy ) = shift;
416     my( $type ) = shift;
417     my( $loc_freq ) = shift;
418     my( $loc_id ) = shift;
419     my( $loc_hdg ) = shift;
420     my( $loc_lat ) = shift;
421     my( $loc_lon ) = shift;
422     my( $gs_elev ) = shift;
423     my( $gs_angle ) = shift;
424     my( $gs_lat ) = shift;
425     my( $gs_lon ) = shift;
426     my( $dme_lat ) = shift;
427     my( $dme_lon ) = shift;
428     my( $om_lat ) = shift;
429     my( $om_lon ) = shift;
430     my( $mm_lat ) = shift;
431     my( $mm_lon ) = shift;
432     my( $im_lat ) = shift;
433     my( $im_lon ) = shift;
434
435     my( $record );
436
437     # remap $type as needed
438     $type = &strip_ws( $type );
439     if ( $type eq "LOCALIZER" ) {
440         $type = "LOC";
441     } elsif ( $type eq "ILS/DME" ) {
442         $type = "ILS";
443     } elsif ( $type eq "SDF/DME" ) {
444         $type = "SDF";
445     } elsif ( $type eq "LOC/DME" ) {
446         $type = "ILS";
447     } elsif ( $type eq "LOC/GS" ) {
448         $type = "LOC";
449     } elsif ( $type eq "LDA/DME" ) {
450         $type = "LDA";
451     }
452
453     $record = sprintf( "%1s %-5s %-4s %-3s  %06.2f %-4s %06.2f %10.6f %11.6f ",
454                        substr( $type, 0, 1 ), $type, $apt_id, $rwy,
455                        $loc_freq, $loc_id, $loc_hdg, $loc_lat, $loc_lon );
456     $record .= sprintf( "%5d %5.2f %10.6f %11.6f ",
457                         $gs_elev, $gs_angle, $gs_lat, $gs_lon );
458     $record .= sprintf( "%10.6f %11.6f ", $dme_lat, $dme_lon );
459     $record .= sprintf( "%10.6f %11.6f ", $om_lat, $om_lon );
460     $record .= sprintf( "%10.6f %11.6f ", $mm_lat, $mm_lon );
461     $record .= sprintf( "%10.6f %11.6f ", $im_lat, $im_lon );
462
463     # print "Updating (common): $apt_id - $rwy\n";
464     $ILS{$apt_id . $rwy} = $record;
465     $AIRPORTS{$apt_id} = 1;
466 }
467
468
469 # update the $type of the record
470 sub update_type() {
471     my( $apt_id ) = shift;
472     my( $rwy ) = shift;
473     my( $new_type ) = shift;
474
475     my( $record );
476
477     if ( $ILS{$apt_id . $rwy} ne "" ) {
478         my( $type_code, $type_name, $apt_id, $rwy, $loc_freq, $loc_id,
479             $loc_hdg, $loc_lat, $loc_lon, $gs_elev, $gs_angle, $gs_lat,
480             $gs_lon, $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat, $mm_lon,
481             $im_lat, $im_lon ) = split( /\s+/, $ILS{$apt_id . $rwy} );
482         # print "Updating type: $apt_id $rwy: $type_name -> $new_type\n";
483         $type_name = $new_type;
484         &update_record( $apt_id, $rwy, $type_name, $loc_freq, $loc_id,
485                         $loc_hdg, $loc_lat, $loc_lon, $gs_elev,
486                         $gs_angle, $gs_lat, $gs_lon, $dme_lat,
487                         $dme_lon, $om_lat, $om_lon, $mm_lat,
488                         $mm_lon, $im_lat, $im_lon );
489     } else {
490         die "Error, trying to update $apt_id - $rwy which doesn't exist\n";
491     }
492 }
493
494
495 # convert a lon/lat coordinate in various formats to signed decimal
496
497 sub make_dcoord() {
498     my($coord) = shift;
499     my( $dir, $deg, $min, $sec );
500     my( $value ) = 0.0;
501
502     $coord = &strip_ws( $coord );
503
504     if ( $coord =~ m/^[WE]/ ) {
505         ( $dir, $deg, $min, $sec )
506             = $coord =~ m/^([EW])(\d\d\d)(\d\d)(\d\d\d\d)/;
507         $value = $deg + $min/60.0 + ($sec/100)/3600.0;
508         if ( $dir eq "W" ) {
509             $value = -$value;
510         }
511     } elsif ( $coord =~ m/^[NS]/ ) {
512         ( $dir, $deg, $min, $sec )
513             = $coord =~ m/^([NS])(\d\d)(\d\d)(\d\d\d\d)/;
514         $value = $deg + $min/60.0 + ($sec/100)/3600.0;
515         if ( $dir eq "S" ) {
516             $value = -$value;
517         }
518     } elsif ( $coord =~ m/[EW]$/ ) {
519         ($value, $dir) = $coord =~ m/([\d\s\.]+)([EW])/;
520         if ( $dir eq "W" ) {
521             $value = -$value;
522         }
523     } elsif ( $coord =~ m/[NS]$/ ) {
524         ($value, $dir) = $coord =~ m/([\d\s\.]+)([NS])/;
525         if ( $dir eq "S" ) {
526             $value = -$value;
527         }
528     }
529     # print "$dir $deg:$min:$sec = $value\n";
530     return $value;
531 }
532
533 # convert a magnetic variation in various formats to signed decimal
534
535 sub make_dmagvar() {
536     my( $coord ) = shift;
537     my( $value );
538
539     if ( $coord =~ m/^[EW]/ ) {
540         my( $dir, $deg, $min, $date )
541             = $coord =~ m/^([EW])(\d\d\d)(\d\d\d) (\d\d\d\d)/;
542         $value = $deg + ($min/10)/60.0;
543         if ( $dir eq "W" ) {
544             $value = -$value;
545         }
546     } elsif ( $coord =~ m/[EW]$/ ) {
547         my( $deg, $dir )
548             = $coord =~ m/^(\d\d)([EW])/;
549         $value = $deg;
550         if ( $dir eq "W" ) {
551             $value = -$value;
552         }
553     }
554     # print "$dir $deg:$min = $value\n";
555
556     return $value;
557 }
558
559 # strip white space off front and back of string
560
561 sub strip_ws() {
562     my( $string ) = shift;
563     $string =~ s/^\s+//;
564     $string =~ s/\s+$//;
565     return $string;
566 }