]> git.mxchange.org Git - flightgear.git/blob - src/Network/ATC-Inputs.cxx
Canvas: Add new element type map for geo mapping.
[flightgear.git] / src / Network / ATC-Inputs.cxx
1 // ATC-Inputs.hxx -- Translate ATC hardware inputs to FGFS properties
2 //
3 // Written by Curtis Olson, started November 2004.
4 //
5 // Copyright (C) 2004  Curtis L. Olson - http://www.flightgear.org/~curt
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <simgear/compiler.h>
29
30 #if defined( unix ) || defined( __CYGWIN__ )
31 #  include <sys/types.h>
32 #  include <sys/stat.h>
33 #  include <fcntl.h>
34 #  include <stdlib.h>
35 #  include <unistd.h>
36 #  include <istream>
37 #endif
38
39 #include <errno.h>
40 #include <math.h>
41
42 #include <string>
43
44 #include <simgear/debug/logstream.hxx>
45 #include <simgear/misc/sg_path.hxx>
46 #include <simgear/props/props_io.hxx>
47
48 #include <Main/fg_props.hxx>
49
50 #include "ATC-Inputs.hxx"
51
52 using std::string;
53 using std::vector;
54
55
56 // Constructor: The _board parameter specifies which board to
57 // reference.  Possible values are 0 or 1.  The _config_file parameter
58 // specifies the location of the input config file (xml)
59 FGATCInput::FGATCInput( const int _board, const SGPath &_config_file ) :
60     is_open(false),
61     ignore_flight_controls(NULL),
62     ignore_pedal_controls(NULL),
63     analog_in_node(NULL),
64     radio_in_node(NULL),
65     switches_node(NULL)
66 {
67     board = _board;
68     config = _config_file;
69 }
70
71
72 // Read analog inputs
73 static void ATCReadAnalogInputs( int fd, unsigned char *analog_in_bytes ) {
74 #if defined( unix ) || defined( __CYGWIN__ )
75     // rewind
76     lseek( fd, 0, SEEK_SET );
77
78     int result = read( fd, analog_in_bytes, ATC_ANAL_IN_BYTES );
79     if ( result != ATC_ANAL_IN_BYTES ) {
80         SG_LOG( SG_IO, SG_ALERT, "Read failed" );
81         exit( -1 );
82     }
83 #endif
84 }
85
86
87 // Read status of radio switches and knobs
88 static void ATCReadRadios( int fd, unsigned char *switch_data ) {
89 #if defined( unix ) || defined( __CYGWIN__ )
90     // rewind
91     lseek( fd, 0, SEEK_SET );
92
93     int result = read( fd, switch_data, ATC_RADIO_SWITCH_BYTES );
94     if ( result != ATC_RADIO_SWITCH_BYTES ) {
95         SG_LOG( SG_IO, SG_ALERT, "Read failed" );
96         exit( -1 );
97     }
98 #endif
99 }
100
101
102 // Read switch inputs
103 static void ATCReadSwitches( int fd, unsigned char *switch_bytes ) {
104 #if defined( unix ) || defined( __CYGWIN__ )
105     // rewind
106     lseek( fd, 0, SEEK_SET );
107
108     int result = read( fd, switch_bytes, ATC_SWITCH_BYTES );
109     if ( result != ATC_SWITCH_BYTES ) {
110         SG_LOG( SG_IO, SG_ALERT, "Read failed" );
111         exit( -1 );
112     }
113 #endif
114 }
115
116
117 void FGATCInput::init_config() {
118 #if defined( unix ) || defined( __CYGWIN__ )
119     if ( config.str()[0] != '/' ) {
120         // not an absolute path, prepend the standard location
121         SGPath tmp;
122         char *envp = ::getenv( "HOME" );
123         if ( envp != NULL ) {
124             tmp = envp;
125             tmp.append( ".atcflightsim" );
126             tmp.append( config.str() );
127             config = tmp;
128         }
129     }
130     readProperties( config.str(), globals->get_props() );
131 #endif
132 }
133
134
135 // Open and initialize the ATC hardware
136 bool FGATCInput::open() {
137     if ( is_open ) {
138         SG_LOG( SG_IO, SG_ALERT, "This board is already open for input! "
139                 << board );
140         return false;
141     }
142
143     // This loads the config parameters generated by "simcal"
144     init_config();
145
146     SG_LOG( SG_IO, SG_ALERT,
147             "Initializing ATC input hardware, please wait ..." );
148
149     snprintf( analog_in_file, 256,
150               "/proc/atcflightsim/board%d/analog_in", board );
151     snprintf( radios_file, 256,
152               "/proc/atcflightsim/board%d/radios", board );
153     snprintf( switches_file, 256,
154               "/proc/atcflightsim/board%d/switches", board );
155
156 #if defined( unix ) || defined( __CYGWIN__ )
157
158     /////////////////////////////////////////////////////////////////////
159     // Open the /proc files
160     /////////////////////////////////////////////////////////////////////
161
162     analog_in_fd = ::open( analog_in_file, O_RDONLY );
163     if ( analog_in_fd == -1 ) {
164         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
165         char msg[256];
166         snprintf( msg, 256, "Error opening %s", analog_in_file );
167         perror( msg );
168         exit( -1 );
169     }
170
171     radios_fd = ::open( radios_file, O_RDWR );
172     if ( radios_fd == -1 ) {
173         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
174         char msg[256];
175         snprintf( msg, 256, "Error opening %s", radios_file );
176         perror( msg );
177         exit( -1 );
178     }
179
180     switches_fd = ::open( switches_file, O_RDONLY );
181     if ( switches_fd == -1 ) {
182         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
183         char msg[256];
184         snprintf( msg, 256, "Error opening %s", switches_file );
185         perror( msg );
186         exit( -1 );
187     }
188
189 #endif
190
191     /////////////////////////////////////////////////////////////////////
192     // Finished initing hardware
193     /////////////////////////////////////////////////////////////////////
194
195     SG_LOG( SG_IO, SG_ALERT,
196             "Done initializing ATC input hardware." );
197
198     is_open = true;
199
200     /////////////////////////////////////////////////////////////////////
201     // Connect up to property values
202     /////////////////////////////////////////////////////////////////////
203
204     ignore_flight_controls
205         = fgGetNode( "/input/atcsim/ignore-flight-controls", true );
206     ignore_pedal_controls
207         = fgGetNode( "/input/atcsim/ignore-pedal-controls", true );
208
209     char base_name[256];
210
211     snprintf( base_name, 256, "/input/atc-board[%d]/analog-in", board );
212     analog_in_node = fgGetNode( base_name );
213
214     snprintf( base_name, 256, "/input/atc-board[%d]/radio-switches", board );
215     radio_in_node = fgGetNode( base_name );
216
217     snprintf( base_name, 256, "/input/atc-board[%d]/switches", board );
218     switches_node = fgGetNode( base_name );
219
220     return true;
221 }
222
223
224 /////////////////////////////////////////////////////////////////////
225 // Read analog inputs
226 /////////////////////////////////////////////////////////////////////
227
228 // scale a number between min and max (with center defined) to a scale
229 // from -1.0 to 1.0.  The deadband value is symmetric, so specifying
230 // '1' will give you a deadband of +/-1
231 static double scale( int center, int deadband, int min, int max, int value ) {
232     // cout << center << " " << min << " " << max << " " << value << " ";
233     double result;
234     double range;
235
236     if ( value <= (center - deadband) ) {
237         range = (center - deadband) - min;
238         result = (value - (center - deadband)) / range;
239     } else if ( value >= (center + deadband) ) {
240         range = max - (center + deadband);
241         result = (value - (center + deadband)) / range;            
242     } else {
243         result = 0.0;
244     }
245
246     if ( result < -1.0 ) result = -1.0;
247     if ( result > 1.0 ) result = 1.0;
248
249     // cout << result << endl;
250
251     return result;
252 }
253
254
255 // scale a number between min and max to a scale from 0.0 to 1.0
256 static double scale( int min, int max, int value ) {
257     // cout << center << " " << min << " " << max << " " << value << " ";
258     double result;
259     double range;
260
261     range = max - min;
262     result = (value - min) / range;
263
264     if ( result < 0.0 ) result = 0.0;
265     if ( result > 1.0 ) result = 1.0;
266
267     // cout << result << endl;
268
269     return result;
270 }
271
272
273 static double clamp( double min, double max, double value ) {
274     double result = value;
275
276     if ( result < min ) result = min;
277     if ( result > max ) result = max;
278
279     // cout << result << endl;
280
281     return result;   
282 }
283
284
285 static int tony_magic( int raw, int obs[3] ) {
286     int result = 0;
287
288     obs[0] = raw;
289
290     if ( obs[1] < 30 ) {
291         if ( obs[2] >= 68 && obs[2] < 480 ) {
292             result = -6;
293         } else if ( obs[2] >= 480 ) {
294             result = 6;
295         }
296         obs[2] = obs[1];
297         obs[1] = obs[0];
298     } else if ( obs[1] < 68 ) {
299         // do nothing
300         obs[1] = obs[0];
301     } else if ( obs[2] < 30 ) {
302         if ( obs[1] >= 68 && obs[1] < 480 ) {
303             result = 6;
304             obs[2] = obs[1];
305             obs[1] = obs[0];
306         } else if ( obs[1] >= 480 ) {
307             result = -6;
308             if ( obs[0] < obs[1] ) {
309                 obs[2] = obs[1];
310                 obs[1] = obs[0];
311             } else {
312                 obs[2] = obs[0];
313                 obs[1] = obs[0];
314             }
315         }
316     } else if ( obs[1] > 980 ) {
317         if ( obs[2] <= 956 && obs[2] > 480 ) {
318             result = 6;
319         } else if ( obs[2] <= 480 ) {
320             result = -6;
321         }
322         obs[2] = obs[1];
323         obs[1] = obs[0];
324     } else if ( obs[1] > 956 ) {
325         // do nothing
326         obs[1] = obs[0];
327     } else if ( obs[2] > 980 ) {
328         if ( obs[1] <= 956 && obs[1] > 480 ) {
329             result = -6;
330             obs[2] = obs[1];
331             obs[1] = obs[0];
332         } else if ( obs[1] <= 480 ) {
333             result = 6;
334             if ( obs[0] > obs[1] ) {
335                 obs[2] = obs[1];
336                 obs[1] = obs[0];
337             } else {
338                 obs[2] = obs[0];
339                 obs[1] = obs[0];
340             }
341         }
342     } else {
343         if ( obs[1] < 480 && obs[2] > 480 ) {
344             // crossed gap going up
345             if ( obs[0] < obs[1] ) {
346                 // caught a bogus intermediate value coming out of the gap
347                 obs[1] = obs[0];
348             }
349         } else if ( obs[1] > 480 && obs[2] < 480 ) {
350             // crossed gap going down
351             if ( obs[0] > obs[1] ) {
352                 // caught a bogus intermediate value coming out of the gap
353               obs[1] = obs[0];
354             }
355         } else if ( obs[0] > 480 && obs[1] < 480 && obs[2] < 480 ) {
356             // crossed the gap going down
357             if ( obs[1] > obs[2] ) {
358                 // caught a bogus intermediate value coming out of the gap
359                 obs[1] = obs[2];
360             }
361         } else if ( obs[0] < 480 && obs[1] > 480 && obs[2] > 480 ) {
362             // crossed the gap going up
363             if ( obs[1] < obs[2] ) {
364                 // caught a bogus intermediate value coming out of the gap
365                 obs[1] = obs[2];
366             }
367         }
368         result = obs[1] - obs[2];
369         if ( abs(result) > 400 ) {
370             // ignore
371             result = 0;
372         }
373         obs[2] = obs[1];
374         obs[1] = obs[0];
375     }
376
377     // cout << " result = " << result << endl;
378     if ( result < -500 ) { result += 1024; }
379     if ( result > 500 ) { result -= 1024; }
380
381     return result;
382 }
383
384
385 static double instr_pot_filter( double ave, double val ) {
386     if ( fabs(ave - val) < 400 || fabs(val) < fabs(ave) ) {
387         return 0.5 * ave + 0.5 * val;
388     } else {
389         return ave;
390     }
391 }
392
393
394 bool FGATCInput::do_analog_in() {
395     // Read raw data in byte form
396     ATCReadAnalogInputs( analog_in_fd, analog_in_bytes );
397
398     // Convert to integer values
399     for ( int channel = 0; channel < ATC_ANAL_IN_VALUES; ++channel ) {
400         unsigned char hi = analog_in_bytes[2 * channel] & 0x03;
401         unsigned char lo = analog_in_bytes[2 * channel + 1];
402         analog_in_data[channel] = hi * 256 + lo;
403
404         // printf("%02x %02x ", hi, lo );
405         // printf("%04d ", value );
406     }
407
408     // Process analog inputs
409     if ( analog_in_node != NULL ) {
410         for ( int i = 0; i < analog_in_node->nChildren(); ++i ) {
411             // read the next config entry from the property tree
412
413             SGPropertyNode *child = analog_in_node->getChild(i);
414             string cname = child->getName();
415             int index = child->getIndex();
416             string name = "";
417             string type = "";
418             string subtype = "";
419             vector <SGPropertyNode *> output_nodes;
420             int center = -1;
421             int min = 0;
422             int max = 1023;
423             int deadband = 0;
424             int hysteresis = 0;
425             float offset = 0.0;
426             float factor = 1.0;
427             if ( cname == "channel" ) {
428                 SGPropertyNode *prop;
429                 prop = child->getChild( "name" );
430                 if ( prop != NULL ) {
431                     name = prop->getStringValue();
432                 }
433                 prop = child->getChild( "type", 0 );
434                 if ( prop != NULL ) {
435                     type = prop->getStringValue();
436                 }
437                 prop = child->getChild( "type", 1 );
438                 if ( prop != NULL ) {
439                     subtype = prop->getStringValue();
440                 }
441                 int j = 0;
442                 while ( (prop = child->getChild("prop", j)) != NULL ) {
443                     SGPropertyNode *tmp
444                         = fgGetNode( prop->getStringValue(), true );
445                     output_nodes.push_back( tmp );
446                     j++;
447                 }
448                 prop = child->getChild( "center" );
449                 if ( prop != NULL ) {
450                     center = prop->getIntValue();
451                 }
452                 prop = child->getChild( "min" );
453                 if ( prop != NULL ) {
454                     min = prop->getIntValue();
455                 }
456                 prop = child->getChild( "max" );
457                 if ( prop != NULL ) {
458                     max = prop->getIntValue();
459                 }
460                 prop = child->getChild( "deadband" );
461                 if ( prop != NULL ) {
462                     deadband = prop->getIntValue();
463                 }
464                 prop = child->getChild( "hysteresis" );
465                 if ( prop != NULL ) {
466                     hysteresis = prop->getIntValue();
467                 }
468                 prop = child->getChild( "offset" );
469                 if ( prop != NULL ) {
470                     offset = prop->getFloatValue();
471                 }
472                 prop = child->getChild( "factor" );
473                 if ( prop != NULL ) {
474                     factor = prop->getFloatValue();
475                 }
476
477                 // Fetch the raw value
478
479                 int raw_value = analog_in_data[index];
480
481                 // Update the target properties
482
483                 if ( type == "flight"
484                      && !ignore_flight_controls->getBoolValue() )
485                 {
486                     if ( subtype != "pedals" ||
487                          ( subtype == "pedals"
488                            && !ignore_pedal_controls->getBoolValue() ) )
489                     {
490                         // "Cook" the raw value
491                         float scaled_value = 0.0f;
492
493                         if ( hysteresis > 0 ) {
494                             int last_raw_value = 0;
495                             prop = child->getChild( "last-raw-value", 0, true );
496                             last_raw_value = prop->getIntValue();
497
498                             if ( abs(raw_value - last_raw_value) < hysteresis )
499                             {
500                                 // not enough movement stay put
501                                 raw_value = last_raw_value;
502                             } else {
503                                 // update last raw value
504                                 prop->setIntValue( raw_value );
505                             }
506                         }
507
508                         if ( center >= 0 ) {
509                             scaled_value = scale( center, deadband,
510                                                   min, max, raw_value );
511                         } else {
512                             scaled_value = scale( min, max, raw_value );
513                         }
514                         scaled_value *= factor;
515                         scaled_value += offset;
516
517                         // final sanity clamp
518                         if ( center >= 0 ) {
519                             scaled_value = clamp( -1.0, 1.0, scaled_value );
520                         } else {
521                             scaled_value = clamp( 0.0, 1.0, scaled_value );
522                         }
523
524                         // update the property tree values
525                         for ( j = 0; j < (int)output_nodes.size(); ++j ) {
526                             output_nodes[j]->setDoubleValue( scaled_value );
527                         }
528                     }
529                 } else if ( type == "avionics-simple" ) {
530                     // "Cook" the raw value
531                     float scaled_value = 0.0f;
532                     if ( center >= 0 ) {
533                         scaled_value = scale( center, deadband,
534                                               min, max, raw_value );
535                     } else {
536                         scaled_value = scale( min, max, raw_value );
537                     }
538                     scaled_value *= factor;
539                     scaled_value += offset;
540
541                     // final sanity clamp
542                     if ( center >= 0 ) {
543                         scaled_value = clamp( -1.0, 1.0, scaled_value );
544                     } else {
545                         scaled_value = clamp( 0.0, 1.0, scaled_value );
546                     }
547
548                     // update the property tree values
549                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
550                         output_nodes[j]->setDoubleValue( scaled_value );
551                     }
552                 } else if ( type == "avionics-resolver" ) {
553                     // this type of analog input impliments a
554                     // rotational knob.  We first caclulate the amount
555                     // of knob rotation (slightly complex to work with
556                     // hardware specific goofiness) and then multiply
557                     // that amount of movement by a scaling factor,
558                     // and finally add the result to the original
559                     // value.
560
561                     bool do_init = false;
562                     float scaled_value = 0.0f;
563
564                     // fetch intermediate values from property tree
565
566                     prop = child->getChild( "is-inited", 0 );
567                     if ( prop == NULL ) {
568                         do_init = true;
569                         prop = child->getChild( "is-inited", 0, true );
570                         prop->setBoolValue( true );
571                     }
572
573                     int raw[3];
574                     for ( j = 0; j < 3; ++j ) {
575                         prop = child->getChild( "raw", j, true );
576                         if ( do_init ) {
577                             raw[j] = analog_in_data[index];
578                         } else {
579                             raw[j] = prop->getIntValue();
580                         }
581                     }
582
583                     // do Tony's magic to calculate knob movement
584                     // based on current analog input position and
585                     // historical data.
586                     int diff = tony_magic( analog_in_data[index], raw );
587
588                     // write raw intermediate values (updated by
589                     // tony_magic()) back to property tree
590                     for ( j = 0; j < 3; ++j ) {
591                         prop = child->getChild( "raw", j, true );
592                         prop->setIntValue( raw[j] );
593                     }
594
595                     // filter knob position
596                     prop = child->getChild( "diff-average", 0, true );
597                     double diff_ave = prop->getDoubleValue();
598                     diff_ave = instr_pot_filter( diff_ave, diff );
599                     prop->setDoubleValue( diff_ave );
600
601                     // calculate value adjustment in real world units
602                     scaled_value = diff_ave * factor;
603
604                     // update the property tree values
605                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
606                         float value = output_nodes[j]->getDoubleValue();
607                         value += scaled_value;
608
609                         prop = child->getChild( "min-clamp" );
610                         if ( prop != NULL ) {
611                             double min = prop->getDoubleValue();
612                             if ( value < min ) { value = min; }
613                         }
614
615                         prop = child->getChild( "max-clamp" );
616                         if ( prop != NULL ) {
617                             double max = prop->getDoubleValue();
618                             if ( value > max ) { value = max; }
619                         }
620
621                         prop = child->getChild( "compass-heading" );
622                         if ( prop != NULL ) {
623                             bool compass = prop->getBoolValue();
624                             if ( compass ) {
625                                 while ( value >= 360.0 ) { value -= 360.0; }
626                                 while ( value < 0.0 ) { value += 360.0; }
627                             }
628                         }
629
630                         output_nodes[j]->setDoubleValue( value );
631                     }
632
633                 } else {
634                     SG_LOG( SG_IO, SG_DEBUG, "Invalid channel type =  "
635                             << type );
636                 }
637             } else {
638                 SG_LOG( SG_IO, SG_DEBUG,
639                         "Input config error, expecting 'channel' but found "
640                         << cname );
641             }
642         }
643     }
644
645     return true;
646 }
647
648
649 /////////////////////////////////////////////////////////////////////
650 // Read the switch positions
651 /////////////////////////////////////////////////////////////////////
652
653 // decode the packed switch data
654 static void update_switch_matrix(
655         int board,
656         unsigned char switch_data[ATC_SWITCH_BYTES],
657         int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES] )
658 {
659     for ( int row = 0; row < ATC_SWITCH_BYTES; ++row ) {
660         unsigned char switches = switch_data[row];
661
662         for( int column = 0; column < ATC_NUM_COLS; ++column ) {
663             if ( row < 8 ) {
664                 switch_matrix[board][column][row] = switches & 1;
665             } else {
666                 switch_matrix[board][row-8][8+column] = switches & 1;
667             }
668             switches = switches >> 1;
669         }
670     }
671 }                     
672
673 bool FGATCInput::do_switches() {
674     // Read the raw data
675     ATCReadSwitches( switches_fd, switch_data );
676
677     // unpack the switch data
678     int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES];
679     update_switch_matrix( board, switch_data, switch_matrix );
680
681     // Process the switch inputs
682     if ( switches_node != NULL ) {
683         for ( int i = 0; i < switches_node->nChildren(); ++i ) {
684             // read the next config entry from the property tree
685
686             SGPropertyNode *child = switches_node->getChild(i);
687             string cname = child->getName();
688             string name = "";
689             string type = "";
690             vector <SGPropertyNode *> output_nodes;
691             int row = -1;
692             int col = -1;
693             float factor = 1.0;
694             int filter = -1;
695             float scaled_value = 0.0f;
696             bool invert = false;
697
698             // get common options
699
700             SGPropertyNode *prop;
701             prop = child->getChild( "name" );
702             if ( prop != NULL ) {
703                 name = prop->getStringValue();
704             }
705             prop = child->getChild( "type" );
706             if ( prop != NULL ) {
707                 type = prop->getStringValue();
708             }
709             int j = 0;
710             while ( (prop = child->getChild("prop", j)) != NULL ) {
711                 SGPropertyNode *tmp
712                     = fgGetNode( prop->getStringValue(), true );
713                 output_nodes.push_back( tmp );
714                 j++;
715             }
716             prop = child->getChild( "factor" );
717             if ( prop != NULL ) {
718                 factor = prop->getFloatValue();
719             }
720             prop = child->getChild( "invert" );
721             if ( prop != NULL ) {
722                 invert = prop->getBoolValue();
723             }
724             prop = child->getChild( "steady-state-filter" );
725             if ( prop != NULL ) {
726                 filter = prop->getIntValue();
727             }
728
729             // handle different types of switches
730
731             if ( cname == "switch" ) {
732                 prop = child->getChild( "row" );
733                 if ( prop != NULL ) {
734                     row = prop->getIntValue();
735                 }
736                 prop = child->getChild( "col" );
737                 if ( prop != NULL ) {
738                     col = prop->getIntValue();
739                 }
740
741                 // Fetch the raw value
742                 int raw_value = switch_matrix[board][row][col];
743
744                 // Invert
745                 if ( invert ) {
746                     raw_value = !raw_value;
747                 }
748
749                 // Cook the value
750                 scaled_value = (float)raw_value * factor;
751
752             } else if ( cname == "combo-switch" ) {
753                 float combo_value = 0.0f;
754
755                 SGPropertyNode *pos;
756                 int k = 0;
757                 while ( (pos = child->getChild("position", k++)) != NULL ) {
758                     // read the combo position entries from the property tree
759
760                     prop = pos->getChild( "row" );
761                     if ( prop != NULL ) {
762                         row = prop->getIntValue();
763                     }
764                     prop = pos->getChild( "col" );
765                     if ( prop != NULL ) {
766                         col = prop->getIntValue();
767                     }
768                     prop = pos->getChild( "value" );
769                     if ( prop != NULL ) {
770                         combo_value = prop->getFloatValue();
771                     }
772
773                     // Fetch the raw value
774                     int raw_value = switch_matrix[board][row][col];
775                     // cout << "sm[" << board << "][" << row << "][" << col
776                     //      << "] = " << raw_value << endl;
777
778                     if ( raw_value ) {
779                         // set scaled_value to the first combo_value
780                         // that matches and jump out of loop.
781                         scaled_value = combo_value;
782                         break;
783                     }
784                 }
785
786                 // Cook the value
787                 scaled_value *= factor;
788             } else if ( cname == "additive-switch" ) {
789                 float additive_value = 0.0f;
790                 float increment = 0.0f;
791
792                 SGPropertyNode *pos;
793                 int k = 0;
794                 while ( (pos = child->getChild("position", k++)) != NULL ) {
795                     // read the combo position entries from the property tree
796
797                     prop = pos->getChild( "row" );
798                     if ( prop != NULL ) {
799                         row = prop->getIntValue();
800                     }
801                     prop = pos->getChild( "col" );
802                     if ( prop != NULL ) {
803                         col = prop->getIntValue();
804                     }
805                     prop = pos->getChild( "value" );
806                     if ( prop != NULL ) {
807                         increment = prop->getFloatValue();
808                     }
809
810                     // Fetch the raw value
811                     int raw_value = switch_matrix[board][row][col];
812                     // cout << "sm[" << board << "][" << row << "][" << col
813                     //      << "] = " << raw_value << endl;
814
815                     if ( raw_value ) {
816                         // set scaled_value to the first combo_value
817                         // that matches and jump out of loop.
818                         additive_value += increment;
819                     }
820                 }
821
822                 // Cook the value
823                 scaled_value = additive_value * factor;
824             }
825
826             // handle filter request.  The value of the switch must be
827             // steady-state for "n" frames before the property value
828             // is updated.
829
830             bool update_prop = true;
831
832             if ( filter > 1 ) {
833                 SGPropertyNode *fv = child->getChild( "filter-value", 0, true );
834                 float filter_value = fv->getFloatValue();
835                 SGPropertyNode *fc = child->getChild( "filter-count", 0, true );
836                 int filter_count = fc->getIntValue();
837
838                 if ( fabs(scaled_value - filter_value) < 0.0001 ) {
839                     filter_count++;
840                 } else {
841                     filter_count = 0;
842                 }
843
844                 if ( filter_count < filter ) {
845                     update_prop = false;
846                 }
847
848                 fv->setFloatValue( scaled_value );
849                 fc->setIntValue( filter_count );
850             }
851
852             if ( update_prop ) {
853                 if ( type == "engine" || type == "flight" ) {
854                     if ( ! ignore_flight_controls->getBoolValue() ) {
855                         // update the property tree values
856                         for ( j = 0; j < (int)output_nodes.size(); ++j ) {
857                             output_nodes[j]->setDoubleValue( scaled_value );
858                         }
859                     }
860                 } else if ( type == "avionics" ) {
861                     // update the property tree values
862                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
863                         output_nodes[j]->setDoubleValue( scaled_value );
864                     }
865                 }
866             }
867         }
868     }
869
870     return true;
871 }
872
873
874 /////////////////////////////////////////////////////////////////////
875 // Read radio switches 
876 /////////////////////////////////////////////////////////////////////
877
878 bool FGATCInput::do_radio_switches() {
879     // Read the raw data
880     ATCReadRadios( radios_fd, radio_switch_data );
881
882     // Process the radio switch/knob inputs
883     if ( radio_in_node != NULL ) {
884         for ( int i = 0; i < radio_in_node->nChildren(); ++i ) {
885             // read the next config entry from the property tree
886
887             SGPropertyNode *child = radio_in_node->getChild(i);
888             string cname = child->getName();
889
890             if ( cname == "switch" ) {
891                 string name = "";
892                 string type = "";
893                 vector <SGPropertyNode *> output_nodes;
894                 int byte_num = -1;
895                 int right_shift = 0;
896                 int mask = 0xff;
897                 int factor = 1;
898                 int offset = 0;
899                 bool invert = false;
900                 int scaled_value = 0;
901                 // get common options
902
903                 SGPropertyNode *prop;
904                 prop = child->getChild( "name" );
905                 if ( prop != NULL ) {
906                     name = prop->getStringValue();
907                 }
908                 prop = child->getChild( "type" );
909                 if ( prop != NULL ) {
910                     type = prop->getStringValue();
911                 }
912                 int j = 0;
913                 while ( (prop = child->getChild("prop", j)) != NULL ) {
914                     SGPropertyNode *tmp
915                         = fgGetNode( prop->getStringValue(), true );
916                     output_nodes.push_back( tmp );
917                     j++;
918                 }
919                 prop = child->getChild( "byte" );
920                 if ( prop != NULL ) {
921                     byte_num = prop->getIntValue();
922                 }
923                 prop = child->getChild( "right-shift" );
924                 if ( prop != NULL ) {
925                     right_shift = prop->getIntValue();
926                 }
927                 prop = child->getChild( "mask" );
928                 if ( prop != NULL ) {
929                     mask = prop->getIntValue();
930                 }
931                 prop = child->getChild( "factor" );
932                 if ( prop != NULL ) {
933                     factor = prop->getIntValue();
934                 }
935                 prop = child->getChild( "offset" );
936                 if ( prop != NULL ) {
937                     offset = prop->getIntValue();
938                 }
939                 prop = child->getChild( "invert" );
940                 if ( prop != NULL ) {
941                     invert = prop->getBoolValue();
942                 }
943
944                 // Fetch the raw value
945                 int raw_value
946                     = (radio_switch_data[byte_num] >> right_shift) & mask;
947
948                 // Cook the value
949                 if ( invert ) {
950                     raw_value = !raw_value;
951                 }
952                 scaled_value = raw_value * factor + offset;
953
954                 // update the property tree values
955                 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
956                     output_nodes[j]->setIntValue( scaled_value );
957                 }
958             }
959         }
960     }
961
962     return true;
963 }
964
965
966 // process the hardware inputs.  This code assumes the calling layer
967 // will lock the hardware.
968 bool FGATCInput::process() {
969     if ( !is_open ) {
970         SG_LOG( SG_IO, SG_ALERT, "This board has not been opened for input! "
971                 << board );
972         return false;
973     }
974
975     do_analog_in();
976     do_switches();
977     do_radio_switches();
978         
979     return true;
980 }
981
982
983 bool FGATCInput::close() {
984
985 #if defined( unix ) || defined( __CYGWIN__ )
986
987     if ( !is_open ) {
988         return true;
989     }
990
991     int result;
992
993     result = ::close( analog_in_fd );
994     if ( result == -1 ) {
995         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
996         char msg[256];
997         snprintf( msg, 256, "Error closing %s", analog_in_file );
998         perror( msg );
999         exit( -1 );
1000     }
1001
1002     result = ::close( radios_fd );
1003     if ( result == -1 ) {
1004         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
1005         char msg[256];
1006         snprintf( msg, 256, "Error closing %s", radios_file );
1007         perror( msg );
1008         exit( -1 );
1009     }
1010
1011     result = ::close( switches_fd );
1012     if ( result == -1 ) {
1013         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
1014         char msg[256];
1015         snprintf( msg, 256, "Error closing %s", switches_file );
1016         perror( msg );
1017         exit( -1 );
1018     }
1019
1020 #endif
1021
1022     return true;
1023 }