]> git.mxchange.org Git - flightgear.git/blob - src/Network/ATC-Inputs.cxx
IRIX fixes.
[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 int tony_magic( int raw, int obs[3] ) {
272     int result = 0;
273
274     obs[0] = raw;
275
276     if ( obs[1] < 30 ) {
277         if ( obs[2] >= 68 && obs[2] < 480 ) {
278             result = -6;
279         } else if ( obs[2] >= 480 ) {
280             result = 6;
281         }
282         obs[2] = obs[1];
283         obs[1] = obs[0];
284     } else if ( obs[1] < 68 ) {
285         // do nothing
286         obs[1] = obs[0];
287     } else if ( obs[2] < 30 ) {
288         if ( obs[1] >= 68 && obs[1] < 480 ) {
289             result = 6;
290             obs[2] = obs[1];
291             obs[1] = obs[0];
292         } else if ( obs[1] >= 480 ) {
293             result = -6;
294             if ( obs[0] < obs[1] ) {
295                 obs[2] = obs[1];
296                 obs[1] = obs[0];
297             } else {
298                 obs[2] = obs[0];
299                 obs[1] = obs[0];
300             }
301         }
302     } else if ( obs[1] > 980 ) {
303         if ( obs[2] <= 956 && obs[2] > 480 ) {
304             result = 6;
305         } else if ( obs[2] <= 480 ) {
306             result = -6;
307         }
308         obs[2] = obs[1];
309         obs[1] = obs[0];
310     } else if ( obs[1] > 956 ) {
311         // do nothing
312         obs[1] = obs[0];
313     } else if ( obs[2] > 980 ) {
314         if ( obs[1] <= 956 && obs[1] > 480 ) {
315             result = -6;
316             obs[2] = obs[1];
317             obs[1] = obs[0];
318         } else if ( obs[1] <= 480 ) {
319             result = 6;
320             if ( obs[0] > obs[1] ) {
321                 obs[2] = obs[1];
322                 obs[1] = obs[0];
323             } else {
324                 obs[2] = obs[0];
325                 obs[1] = obs[0];
326             }
327         }
328     } else {
329         if ( obs[1] < 480 && obs[2] > 480 ) {
330             // crossed gap going up
331             if ( obs[0] < obs[1] ) {
332                 // caught a bogus intermediate value coming out of the gap
333                 obs[1] = obs[0];
334             }
335         } else if ( obs[1] > 480 && obs[2] < 480 ) {
336             // crossed gap going down
337             if ( obs[0] > obs[1] ) {
338                 // caught a bogus intermediate value coming out of the gap
339               obs[1] = obs[0];
340             }
341         } else if ( obs[0] > 480 && obs[1] < 480 && obs[2] < 480 ) {
342             // crossed the gap going down
343             if ( obs[1] > obs[2] ) {
344                 // caught a bogus intermediate value coming out of the gap
345                 obs[1] = obs[2];
346             }
347         } else if ( obs[0] < 480 && obs[1] > 480 && obs[2] > 480 ) {
348             // crossed the gap going up
349             if ( obs[1] < obs[2] ) {
350                 // caught a bogus intermediate value coming out of the gap
351                 obs[1] = obs[2];
352             }
353         }
354         result = obs[1] - obs[2];
355         if ( abs(result) > 400 ) {
356             // ignore
357             result = 0;
358         }
359         obs[2] = obs[1];
360         obs[1] = obs[0];
361     }
362
363     // cout << " result = " << result << endl;
364     if ( result < -500 ) { result += 1024; }
365     if ( result > 500 ) { result -= 1024; }
366
367     return result;
368 }
369
370
371 static double instr_pot_filter( double ave, double val ) {
372     if ( fabs(ave - val) < 400 || fabs(val) < fabs(ave) ) {
373         return 0.5 * ave + 0.5 * val;
374     } else {
375         return ave;
376     }
377 }
378
379
380 bool FGATCInput::do_analog_in() {
381     // Read raw data in byte form
382     ATCReadAnalogInputs( analog_in_fd, analog_in_bytes );
383
384     // Convert to integer values
385     for ( int channel = 0; channel < ATC_ANAL_IN_VALUES; ++channel ) {
386         unsigned char hi = analog_in_bytes[2 * channel] & 0x03;
387         unsigned char lo = analog_in_bytes[2 * channel + 1];
388         analog_in_data[channel] = hi * 256 + lo;
389
390         // printf("%02x %02x ", hi, lo );
391         // printf("%04d ", value );
392     }
393
394     // Process analog inputs
395     if ( analog_in_node != NULL ) {
396         for ( int i = 0; i < analog_in_node->nChildren(); ++i ) {
397             // read the next config entry from the property tree
398
399             SGPropertyNode *child = analog_in_node->getChild(i);
400             string cname = child->getName();
401             int index = child->getIndex();
402             string name = "";
403             string type = "";
404             string subtype = "";
405             vector <SGPropertyNode *> output_nodes; output_nodes.clear();
406             int center = -1;
407             int min = 0;
408             int max = 1023;
409             int deadband = 0;
410             float factor = 1.0;
411             if ( cname == "channel" ) {
412                 SGPropertyNode *prop;
413                 prop = child->getChild( "name" );
414                 if ( prop != NULL ) {
415                     name = prop->getStringValue();
416                 }
417                 prop = child->getChild( "type", 0 );
418                 if ( prop != NULL ) {
419                     type = prop->getStringValue();
420                 }
421                 prop = child->getChild( "type", 1 );
422                 if ( prop != NULL ) {
423                     subtype = prop->getStringValue();
424                 }
425                 int j = 0;
426                 while ( (prop = child->getChild("prop", j)) != NULL ) {
427                     SGPropertyNode *tmp
428                         = fgGetNode( prop->getStringValue(), true );
429                     output_nodes.push_back( tmp );
430                     j++;
431                 }
432                 prop = child->getChild( "center" );
433                 if ( prop != NULL ) {
434                     center = prop->getIntValue();
435                 }
436                 prop = child->getChild( "min" );
437                 if ( prop != NULL ) {
438                     min = prop->getIntValue();
439                 }
440                 prop = child->getChild( "max" );
441                 if ( prop != NULL ) {
442                     max = prop->getIntValue();
443                 }
444                 prop = child->getChild( "deadband" );
445                 if ( prop != NULL ) {
446                     deadband = prop->getIntValue();
447                 }
448                 prop = child->getChild( "factor" );
449                 if ( prop != NULL ) {
450                     factor = prop->getFloatValue();
451                 }
452
453                 // Fetch the raw value
454
455                 int raw_value = analog_in_data[index];
456
457                 // Update the target properties
458
459                 if ( type == "flight"
460                      && !ignore_flight_controls->getBoolValue() )
461                 {
462                     if ( subtype != "pedals" ||
463                          ( subtype == "pedals"
464                            && !ignore_pedal_controls->getBoolValue() ) )
465                     {
466                         // "Cook" the raw value
467                         float scaled_value = 0.0f;
468                         if ( center >= 0 ) {
469                             scaled_value = scale( center, deadband,
470                                                   min, max, raw_value );
471                         } else {
472                             scaled_value = scale( min, max, raw_value );
473                         }
474                         scaled_value *= factor;
475
476                         // update the property tree values
477                         for ( j = 0; j < (int)output_nodes.size(); ++j ) {
478                             output_nodes[j]->setDoubleValue( scaled_value );
479                         }
480                     }
481                 } else if ( type == "avionics-simple" ) {
482                     // "Cook" the raw value
483                     float scaled_value = 0.0f;
484                     if ( center >= 0 ) {
485                         scaled_value = scale( center, deadband,
486                                               min, max, raw_value );
487                     } else {
488                         scaled_value = scale( min, max, raw_value );
489                     }
490                     scaled_value *= factor;
491
492                     // update the property tree values
493                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
494                         output_nodes[j]->setDoubleValue( scaled_value );
495                     }
496                 } else if ( type == "avionics-resolver" ) {
497                     // this type of analog input impliments a
498                     // rotational knob.  We first caclulate the amount
499                     // of knob rotation (slightly complex to work with
500                     // hardware specific goofiness) and then multiply
501                     // that amount of movement by a scaling factor,
502                     // and finally add the result to the original
503                     // value.
504
505                     bool do_init = false;
506                     float scaled_value = 0.0f;
507
508                     // fetch intermediate values from property tree
509
510                     prop = child->getChild( "is-inited", 0 );
511                     if ( prop == NULL ) {
512                         do_init = true;
513                         prop = child->getChild( "is-inited", 0, true );
514                         prop->setBoolValue( true );
515                     }
516
517                     int raw[3];
518                     for ( j = 0; j < 3; ++j ) {
519                         prop = child->getChild( "raw", j, true );
520                         if ( do_init ) {
521                             raw[j] = analog_in_data[index];
522                         } else {
523                             raw[j] = prop->getIntValue();
524                         }
525                     }
526
527                     // do Tony's magic to calculate knob movement
528                     // based on current analog input position and
529                     // historical data.
530                     int diff = tony_magic( analog_in_data[index], raw );
531
532                     // write raw intermediate values (updated by
533                     // tony_magic()) back to property tree
534                     for ( j = 0; j < 3; ++j ) {
535                         prop = child->getChild( "raw", j, true );
536                         prop->setIntValue( raw[j] );
537                     }
538
539                     // filter knob position
540                     prop = child->getChild( "diff-average", 0, true );
541                     double diff_ave = prop->getDoubleValue();
542                     diff_ave = instr_pot_filter( diff_ave, diff );
543                     prop->setDoubleValue( diff_ave );
544
545                     // calculate value adjustment in real world units
546                     scaled_value = diff_ave * factor;
547
548                     // update the property tree values
549                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
550                         float value = output_nodes[j]->getDoubleValue();
551                         value += scaled_value;
552
553                         prop = child->getChild( "min-clamp" );
554                         if ( prop != NULL ) {
555                             double min = prop->getDoubleValue();
556                             if ( value < min ) { value = min; }
557                         }
558
559                         prop = child->getChild( "max-clamp" );
560                         if ( prop != NULL ) {
561                             double max = prop->getDoubleValue();
562                             if ( value > max ) { value = max; }
563                         }
564
565                         prop = child->getChild( "compass-heading" );
566                         if ( prop != NULL ) {
567                             bool compass = prop->getBoolValue();
568                             if ( compass ) {
569                                 while ( value >= 360.0 ) { value -= 360.0; }
570                                 while ( value < 0.0 ) { value += 360.0; }
571                             }
572                         }
573
574                         output_nodes[j]->setDoubleValue( value );
575                     }
576
577                 } else {
578                     SG_LOG( SG_IO, SG_DEBUG, "Invalid channel type =  "
579                             << type );
580                 }
581             } else {
582                 SG_LOG( SG_IO, SG_DEBUG,
583                         "Input config error, expecting 'channel' but found "
584                         << cname );
585             }
586         }
587     }
588
589     return true;
590 }
591
592
593 /////////////////////////////////////////////////////////////////////
594 // Read the switch positions
595 /////////////////////////////////////////////////////////////////////
596
597 // decode the packed switch data
598 static void update_switch_matrix(
599         int board,
600         unsigned char switch_data[ATC_SWITCH_BYTES],
601         int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES] )
602 {
603     for ( int row = 0; row < ATC_SWITCH_BYTES; ++row ) {
604         unsigned char switches = switch_data[row];
605
606         for( int column = 0; column < ATC_NUM_COLS; ++column ) {
607             switch_matrix[board][column][row] = switches & 1;
608             switches = switches >> 1;
609         }                       
610     }
611 }                     
612
613 bool FGATCInput::do_switches() {
614     // Read the raw data
615     ATCReadSwitches( switches_fd, switch_data );
616
617     // unpack the switch data
618     int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES];
619     update_switch_matrix( board, switch_data, switch_matrix );
620
621     // Process the switch inputs
622     if ( switches_node != NULL ) {
623         for ( int i = 0; i < switches_node->nChildren(); ++i ) {
624             // read the next config entry from the property tree
625
626             SGPropertyNode *child = switches_node->getChild(i);
627             string cname = child->getName();
628             string name = "";
629             string type = "";
630             vector <SGPropertyNode *> output_nodes; output_nodes.clear();
631             int row = -1;
632             int col = -1;
633             float factor = 1.0;
634             int filter = -1;
635             float scaled_value = 0.0f;
636             bool invert = false;
637
638             // get common options
639
640             SGPropertyNode *prop;
641             prop = child->getChild( "name" );
642             if ( prop != NULL ) {
643                 name = prop->getStringValue();
644             }
645             prop = child->getChild( "type" );
646             if ( prop != NULL ) {
647                 type = prop->getStringValue();
648             }
649             int j = 0;
650             while ( (prop = child->getChild("prop", j)) != NULL ) {
651                 SGPropertyNode *tmp
652                     = fgGetNode( prop->getStringValue(), true );
653                 output_nodes.push_back( tmp );
654                 j++;
655             }
656             prop = child->getChild( "factor" );
657             if ( prop != NULL ) {
658                 factor = prop->getFloatValue();
659             }
660             prop = child->getChild( "invert" );
661             if ( prop != NULL ) {
662                 invert = prop->getBoolValue();
663             }
664             prop = child->getChild( "steady-state-filter" );
665             if ( prop != NULL ) {
666                 filter = prop->getIntValue();
667             }
668
669             // handle different types of switches
670
671             if ( cname == "switch" ) {
672                 prop = child->getChild( "row" );
673                 if ( prop != NULL ) {
674                     row = prop->getIntValue();
675                 }
676                 prop = child->getChild( "col" );
677                 if ( prop != NULL ) {
678                     col = prop->getIntValue();
679                 }
680
681                 // Fetch the raw value
682                 int raw_value = switch_matrix[board][row][col];
683
684                 // Invert
685                 if ( invert ) {
686                     raw_value = !raw_value;
687                 }
688
689                 // Cook the value
690                 scaled_value = (float)raw_value * factor;
691
692             } else if ( cname == "combo-switch" ) {
693                 float combo_value = 0.0f;
694
695                 SGPropertyNode *pos;
696                 int k = 0;
697                 while ( (pos = child->getChild("position", k++)) != NULL ) {
698                     // read the combo position entries from the property tree
699
700                     prop = pos->getChild( "row" );
701                     if ( prop != NULL ) {
702                         row = prop->getIntValue();
703                     }
704                     prop = pos->getChild( "col" );
705                     if ( prop != NULL ) {
706                         col = prop->getIntValue();
707                     }
708                     prop = pos->getChild( "value" );
709                     if ( prop != NULL ) {
710                         combo_value = prop->getFloatValue();
711                     }
712
713                     // Fetch the raw value
714                     int raw_value = switch_matrix[board][row][col];
715                     // cout << "sm[" << board << "][" << row << "][" << col
716                     //      << "] = " << raw_value << endl;
717
718                     if ( raw_value ) {
719                         // set scaled_value to the first combo_value
720                         // that matches and jump out of loop.
721                         scaled_value = combo_value;
722                         break;
723                     }
724                 }
725
726                 // Cook the value
727                 scaled_value *= factor;
728             } else if ( cname == "additive-switch" ) {
729                 float additive_value = 0.0f;
730                 float increment = 0.0f;
731
732                 SGPropertyNode *pos;
733                 int k = 0;
734                 while ( (pos = child->getChild("position", k++)) != NULL ) {
735                     // read the combo position entries from the property tree
736
737                     prop = pos->getChild( "row" );
738                     if ( prop != NULL ) {
739                         row = prop->getIntValue();
740                     }
741                     prop = pos->getChild( "col" );
742                     if ( prop != NULL ) {
743                         col = prop->getIntValue();
744                     }
745                     prop = pos->getChild( "value" );
746                     if ( prop != NULL ) {
747                         increment = prop->getFloatValue();
748                     }
749
750                     // Fetch the raw value
751                     int raw_value = switch_matrix[board][row][col];
752                     // cout << "sm[" << board << "][" << row << "][" << col
753                     //      << "] = " << raw_value << endl;
754
755                     if ( raw_value ) {
756                         // set scaled_value to the first combo_value
757                         // that matches and jump out of loop.
758                         additive_value += increment;
759                     }
760                 }
761
762                 // Cook the value
763                 scaled_value = additive_value * factor;
764             }
765
766             // handle filter request.  The value of the switch must be
767             // steady-state for "n" frames before the property value
768             // is updated.
769
770             bool update_prop = true;
771
772             if ( filter > 1 ) {
773                 SGPropertyNode *fv = child->getChild( "filter-value", 0, true );
774                 float filter_value = fv->getFloatValue();
775                 SGPropertyNode *fc = child->getChild( "filter-count", 0, true );
776                 int filter_count = fc->getIntValue();
777
778                 if ( fabs(scaled_value - filter_value) < 0.0001 ) {
779                     filter_count++;
780                 } else {
781                     filter_count = 0;
782                 }
783
784                 if ( filter_count < filter ) {
785                     update_prop = false;
786                 }
787
788                 fv->setFloatValue( scaled_value );
789                 fc->setIntValue( filter_count );
790             }
791
792             if ( update_prop ) {
793                 if ( type == "engine" || type == "flight" ) {
794                     if ( ! ignore_flight_controls->getBoolValue() ) {
795                         // update the property tree values
796                         for ( j = 0; j < (int)output_nodes.size(); ++j ) {
797                             output_nodes[j]->setDoubleValue( scaled_value );
798                         }
799                     }
800                 } else if ( type == "avionics" ) {
801                     // update the property tree values
802                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
803                         output_nodes[j]->setDoubleValue( scaled_value );
804                     }
805                 }
806             }
807         }
808     }
809
810     return true;
811 }
812
813
814 /////////////////////////////////////////////////////////////////////
815 // Read radio switches 
816 /////////////////////////////////////////////////////////////////////
817
818 bool FGATCInput::do_radio_switches() {
819     // Read the raw data
820     ATCReadRadios( radios_fd, radio_switch_data );
821
822     // Process the radio switch/knob inputs
823     if ( radio_in_node != NULL ) {
824         for ( int i = 0; i < radio_in_node->nChildren(); ++i ) {
825             // read the next config entry from the property tree
826
827             SGPropertyNode *child = radio_in_node->getChild(i);
828             string cname = child->getName();
829
830             if ( cname == "switch" ) {
831                 string name = "";
832                 string type = "";
833                 vector <SGPropertyNode *> output_nodes; output_nodes.clear();
834                 int byte_num = -1;
835                 int right_shift = 0;
836                 int mask = 0xff;
837                 int factor = 1;
838                 int offset = 0;
839                 bool invert = false;
840                 int scaled_value = 0;
841                 // get common options
842
843                 SGPropertyNode *prop;
844                 prop = child->getChild( "name" );
845                 if ( prop != NULL ) {
846                     name = prop->getStringValue();
847                 }
848                 prop = child->getChild( "type" );
849                 if ( prop != NULL ) {
850                     type = prop->getStringValue();
851                 }
852                 int j = 0;
853                 while ( (prop = child->getChild("prop", j)) != NULL ) {
854                     SGPropertyNode *tmp
855                         = fgGetNode( prop->getStringValue(), true );
856                     output_nodes.push_back( tmp );
857                     j++;
858                 }
859                 prop = child->getChild( "byte" );
860                 if ( prop != NULL ) {
861                     byte_num = prop->getIntValue();
862                 }
863                 prop = child->getChild( "right-shift" );
864                 if ( prop != NULL ) {
865                     right_shift = prop->getIntValue();
866                 }
867                 prop = child->getChild( "mask" );
868                 if ( prop != NULL ) {
869                     mask = prop->getIntValue();
870                 }
871                 prop = child->getChild( "factor" );
872                 if ( prop != NULL ) {
873                     factor = prop->getIntValue();
874                 }
875                 prop = child->getChild( "offset" );
876                 if ( prop != NULL ) {
877                     offset = prop->getIntValue();
878                 }
879                 prop = child->getChild( "invert" );
880                 if ( prop != NULL ) {
881                     invert = prop->getBoolValue();
882                 }
883
884                 // Fetch the raw value
885                 int raw_value
886                     = (radio_switch_data[byte_num] >> right_shift) & mask;
887
888                 // Cook the value
889                 if ( invert ) {
890                     raw_value = !raw_value;
891                 }
892                 scaled_value = raw_value * factor + offset;
893
894                 // update the property tree values
895                 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
896                     output_nodes[j]->setIntValue( scaled_value );
897                 }
898             }
899         }
900     }
901
902     return true;
903 }
904
905
906 // process the hardware inputs.  This code assumes the calling layer
907 // will lock the hardware.
908 bool FGATCInput::process() {
909     if ( !is_open ) {
910         SG_LOG( SG_IO, SG_ALERT, "This board has not been opened for input! "
911                 << board );
912         return false;
913     }
914
915     do_analog_in();
916     do_switches();
917     do_radio_switches();
918         
919     return true;
920 }
921
922
923 bool FGATCInput::close() {
924
925 #if defined( unix ) || defined( __CYGWIN__ )
926
927     if ( !is_open ) {
928         return true;
929     }
930
931     int result;
932
933     result = ::close( analog_in_fd );
934     if ( result == -1 ) {
935         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
936         char msg[256];
937         snprintf( msg, 256, "Error closing %s", analog_in_file );
938         perror( msg );
939         exit( -1 );
940     }
941
942     result = ::close( radios_fd );
943     if ( result == -1 ) {
944         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
945         char msg[256];
946         snprintf( msg, 256, "Error closing %s", radios_file );
947         perror( msg );
948         exit( -1 );
949     }
950
951     result = ::close( switches_fd );
952     if ( result == -1 ) {
953         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
954         char msg[256];
955         snprintf( msg, 256, "Error closing %s", switches_file );
956         perror( msg );
957         exit( -1 );
958     }
959
960 #endif
961
962     return true;
963 }