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