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