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