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