]> git.mxchange.org Git - flightgear.git/blob - src/Network/ATC-Inputs.cxx
Use OSG polytope intersector to fill ground cache
[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             float offset = 0.0;
424             float factor = 1.0;
425             if ( cname == "channel" ) {
426                 SGPropertyNode *prop;
427                 prop = child->getChild( "name" );
428                 if ( prop != NULL ) {
429                     name = prop->getStringValue();
430                 }
431                 prop = child->getChild( "type", 0 );
432                 if ( prop != NULL ) {
433                     type = prop->getStringValue();
434                 }
435                 prop = child->getChild( "type", 1 );
436                 if ( prop != NULL ) {
437                     subtype = prop->getStringValue();
438                 }
439                 int j = 0;
440                 while ( (prop = child->getChild("prop", j)) != NULL ) {
441                     SGPropertyNode *tmp
442                         = fgGetNode( prop->getStringValue(), true );
443                     output_nodes.push_back( tmp );
444                     j++;
445                 }
446                 prop = child->getChild( "center" );
447                 if ( prop != NULL ) {
448                     center = prop->getIntValue();
449                 }
450                 prop = child->getChild( "min" );
451                 if ( prop != NULL ) {
452                     min = prop->getIntValue();
453                 }
454                 prop = child->getChild( "max" );
455                 if ( prop != NULL ) {
456                     max = prop->getIntValue();
457                 }
458                 prop = child->getChild( "deadband" );
459                 if ( prop != NULL ) {
460                     deadband = prop->getIntValue();
461                 }
462                 prop = child->getChild( "offset" );
463                 if ( prop != NULL ) {
464                     offset = prop->getFloatValue();
465                 }
466                 prop = child->getChild( "factor" );
467                 if ( prop != NULL ) {
468                     factor = prop->getFloatValue();
469                 }
470
471                 // Fetch the raw value
472
473                 int raw_value = analog_in_data[index];
474
475                 // Update the target properties
476
477                 if ( type == "flight"
478                      && !ignore_flight_controls->getBoolValue() )
479                 {
480                     if ( subtype != "pedals" ||
481                          ( subtype == "pedals"
482                            && !ignore_pedal_controls->getBoolValue() ) )
483                     {
484                         // "Cook" the raw value
485                         float scaled_value = 0.0f;
486                         if ( center >= 0 ) {
487                             scaled_value = scale( center, deadband,
488                                                   min, max, raw_value );
489                         } else {
490                             scaled_value = scale( min, max, raw_value );
491                         }
492                         scaled_value *= factor;
493                         scaled_value += offset;
494
495                         // final sanity clamp
496                         if ( center >= 0 ) {
497                             scaled_value = clamp( -1.0, 1.0, scaled_value );
498                         } else {
499                             scaled_value = clamp( 0.0, 1.0, scaled_value );
500                         }
501
502                         // update the property tree values
503                         for ( j = 0; j < (int)output_nodes.size(); ++j ) {
504                             output_nodes[j]->setDoubleValue( scaled_value );
505                         }
506                     }
507                 } else if ( type == "avionics-simple" ) {
508                     // "Cook" the raw value
509                     float scaled_value = 0.0f;
510                     if ( center >= 0 ) {
511                         scaled_value = scale( center, deadband,
512                                               min, max, raw_value );
513                     } else {
514                         scaled_value = scale( min, max, raw_value );
515                     }
516                     scaled_value *= factor;
517                     scaled_value += offset;
518
519                     // final sanity clamp
520                     if ( center >= 0 ) {
521                         scaled_value = clamp( -1.0, 1.0, scaled_value );
522                     } else {
523                         scaled_value = clamp( 0.0, 1.0, scaled_value );
524                     }
525
526                     // update the property tree values
527                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
528                         output_nodes[j]->setDoubleValue( scaled_value );
529                     }
530                 } else if ( type == "avionics-resolver" ) {
531                     // this type of analog input impliments a
532                     // rotational knob.  We first caclulate the amount
533                     // of knob rotation (slightly complex to work with
534                     // hardware specific goofiness) and then multiply
535                     // that amount of movement by a scaling factor,
536                     // and finally add the result to the original
537                     // value.
538
539                     bool do_init = false;
540                     float scaled_value = 0.0f;
541
542                     // fetch intermediate values from property tree
543
544                     prop = child->getChild( "is-inited", 0 );
545                     if ( prop == NULL ) {
546                         do_init = true;
547                         prop = child->getChild( "is-inited", 0, true );
548                         prop->setBoolValue( true );
549                     }
550
551                     int raw[3];
552                     for ( j = 0; j < 3; ++j ) {
553                         prop = child->getChild( "raw", j, true );
554                         if ( do_init ) {
555                             raw[j] = analog_in_data[index];
556                         } else {
557                             raw[j] = prop->getIntValue();
558                         }
559                     }
560
561                     // do Tony's magic to calculate knob movement
562                     // based on current analog input position and
563                     // historical data.
564                     int diff = tony_magic( analog_in_data[index], raw );
565
566                     // write raw intermediate values (updated by
567                     // tony_magic()) back to property tree
568                     for ( j = 0; j < 3; ++j ) {
569                         prop = child->getChild( "raw", j, true );
570                         prop->setIntValue( raw[j] );
571                     }
572
573                     // filter knob position
574                     prop = child->getChild( "diff-average", 0, true );
575                     double diff_ave = prop->getDoubleValue();
576                     diff_ave = instr_pot_filter( diff_ave, diff );
577                     prop->setDoubleValue( diff_ave );
578
579                     // calculate value adjustment in real world units
580                     scaled_value = diff_ave * factor;
581
582                     // update the property tree values
583                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
584                         float value = output_nodes[j]->getDoubleValue();
585                         value += scaled_value;
586
587                         prop = child->getChild( "min-clamp" );
588                         if ( prop != NULL ) {
589                             double min = prop->getDoubleValue();
590                             if ( value < min ) { value = min; }
591                         }
592
593                         prop = child->getChild( "max-clamp" );
594                         if ( prop != NULL ) {
595                             double max = prop->getDoubleValue();
596                             if ( value > max ) { value = max; }
597                         }
598
599                         prop = child->getChild( "compass-heading" );
600                         if ( prop != NULL ) {
601                             bool compass = prop->getBoolValue();
602                             if ( compass ) {
603                                 while ( value >= 360.0 ) { value -= 360.0; }
604                                 while ( value < 0.0 ) { value += 360.0; }
605                             }
606                         }
607
608                         output_nodes[j]->setDoubleValue( value );
609                     }
610
611                 } else {
612                     SG_LOG( SG_IO, SG_DEBUG, "Invalid channel type =  "
613                             << type );
614                 }
615             } else {
616                 SG_LOG( SG_IO, SG_DEBUG,
617                         "Input config error, expecting 'channel' but found "
618                         << cname );
619             }
620         }
621     }
622
623     return true;
624 }
625
626
627 /////////////////////////////////////////////////////////////////////
628 // Read the switch positions
629 /////////////////////////////////////////////////////////////////////
630
631 // decode the packed switch data
632 static void update_switch_matrix(
633         int board,
634         unsigned char switch_data[ATC_SWITCH_BYTES],
635         int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES] )
636 {
637     for ( int row = 0; row < ATC_SWITCH_BYTES; ++row ) {
638         unsigned char switches = switch_data[row];
639
640         for( int column = 0; column < ATC_NUM_COLS; ++column ) {
641             switch_matrix[board][column][row] = switches & 1;
642             switches = switches >> 1;
643         }                       
644     }
645 }                     
646
647 bool FGATCInput::do_switches() {
648     // Read the raw data
649     ATCReadSwitches( switches_fd, switch_data );
650
651     // unpack the switch data
652     int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES];
653     update_switch_matrix( board, switch_data, switch_matrix );
654
655     // Process the switch inputs
656     if ( switches_node != NULL ) {
657         for ( int i = 0; i < switches_node->nChildren(); ++i ) {
658             // read the next config entry from the property tree
659
660             SGPropertyNode *child = switches_node->getChild(i);
661             string cname = child->getName();
662             string name = "";
663             string type = "";
664             vector <SGPropertyNode *> output_nodes;
665             int row = -1;
666             int col = -1;
667             float factor = 1.0;
668             int filter = -1;
669             float scaled_value = 0.0f;
670             bool invert = false;
671
672             // get common options
673
674             SGPropertyNode *prop;
675             prop = child->getChild( "name" );
676             if ( prop != NULL ) {
677                 name = prop->getStringValue();
678             }
679             prop = child->getChild( "type" );
680             if ( prop != NULL ) {
681                 type = prop->getStringValue();
682             }
683             int j = 0;
684             while ( (prop = child->getChild("prop", j)) != NULL ) {
685                 SGPropertyNode *tmp
686                     = fgGetNode( prop->getStringValue(), true );
687                 output_nodes.push_back( tmp );
688                 j++;
689             }
690             prop = child->getChild( "factor" );
691             if ( prop != NULL ) {
692                 factor = prop->getFloatValue();
693             }
694             prop = child->getChild( "invert" );
695             if ( prop != NULL ) {
696                 invert = prop->getBoolValue();
697             }
698             prop = child->getChild( "steady-state-filter" );
699             if ( prop != NULL ) {
700                 filter = prop->getIntValue();
701             }
702
703             // handle different types of switches
704
705             if ( cname == "switch" ) {
706                 prop = child->getChild( "row" );
707                 if ( prop != NULL ) {
708                     row = prop->getIntValue();
709                 }
710                 prop = child->getChild( "col" );
711                 if ( prop != NULL ) {
712                     col = prop->getIntValue();
713                 }
714
715                 // Fetch the raw value
716                 int raw_value = switch_matrix[board][row][col];
717
718                 // Invert
719                 if ( invert ) {
720                     raw_value = !raw_value;
721                 }
722
723                 // Cook the value
724                 scaled_value = (float)raw_value * factor;
725
726             } else if ( cname == "combo-switch" ) {
727                 float combo_value = 0.0f;
728
729                 SGPropertyNode *pos;
730                 int k = 0;
731                 while ( (pos = child->getChild("position", k++)) != NULL ) {
732                     // read the combo position entries from the property tree
733
734                     prop = pos->getChild( "row" );
735                     if ( prop != NULL ) {
736                         row = prop->getIntValue();
737                     }
738                     prop = pos->getChild( "col" );
739                     if ( prop != NULL ) {
740                         col = prop->getIntValue();
741                     }
742                     prop = pos->getChild( "value" );
743                     if ( prop != NULL ) {
744                         combo_value = prop->getFloatValue();
745                     }
746
747                     // Fetch the raw value
748                     int raw_value = switch_matrix[board][row][col];
749                     // cout << "sm[" << board << "][" << row << "][" << col
750                     //      << "] = " << raw_value << endl;
751
752                     if ( raw_value ) {
753                         // set scaled_value to the first combo_value
754                         // that matches and jump out of loop.
755                         scaled_value = combo_value;
756                         break;
757                     }
758                 }
759
760                 // Cook the value
761                 scaled_value *= factor;
762             } else if ( cname == "additive-switch" ) {
763                 float additive_value = 0.0f;
764                 float increment = 0.0f;
765
766                 SGPropertyNode *pos;
767                 int k = 0;
768                 while ( (pos = child->getChild("position", k++)) != NULL ) {
769                     // read the combo position entries from the property tree
770
771                     prop = pos->getChild( "row" );
772                     if ( prop != NULL ) {
773                         row = prop->getIntValue();
774                     }
775                     prop = pos->getChild( "col" );
776                     if ( prop != NULL ) {
777                         col = prop->getIntValue();
778                     }
779                     prop = pos->getChild( "value" );
780                     if ( prop != NULL ) {
781                         increment = prop->getFloatValue();
782                     }
783
784                     // Fetch the raw value
785                     int raw_value = switch_matrix[board][row][col];
786                     // cout << "sm[" << board << "][" << row << "][" << col
787                     //      << "] = " << raw_value << endl;
788
789                     if ( raw_value ) {
790                         // set scaled_value to the first combo_value
791                         // that matches and jump out of loop.
792                         additive_value += increment;
793                     }
794                 }
795
796                 // Cook the value
797                 scaled_value = additive_value * factor;
798             }
799
800             // handle filter request.  The value of the switch must be
801             // steady-state for "n" frames before the property value
802             // is updated.
803
804             bool update_prop = true;
805
806             if ( filter > 1 ) {
807                 SGPropertyNode *fv = child->getChild( "filter-value", 0, true );
808                 float filter_value = fv->getFloatValue();
809                 SGPropertyNode *fc = child->getChild( "filter-count", 0, true );
810                 int filter_count = fc->getIntValue();
811
812                 if ( fabs(scaled_value - filter_value) < 0.0001 ) {
813                     filter_count++;
814                 } else {
815                     filter_count = 0;
816                 }
817
818                 if ( filter_count < filter ) {
819                     update_prop = false;
820                 }
821
822                 fv->setFloatValue( scaled_value );
823                 fc->setIntValue( filter_count );
824             }
825
826             if ( update_prop ) {
827                 if ( type == "engine" || type == "flight" ) {
828                     if ( ! ignore_flight_controls->getBoolValue() ) {
829                         // update the property tree values
830                         for ( j = 0; j < (int)output_nodes.size(); ++j ) {
831                             output_nodes[j]->setDoubleValue( scaled_value );
832                         }
833                     }
834                 } else if ( type == "avionics" ) {
835                     // update the property tree values
836                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
837                         output_nodes[j]->setDoubleValue( scaled_value );
838                     }
839                 }
840             }
841         }
842     }
843
844     return true;
845 }
846
847
848 /////////////////////////////////////////////////////////////////////
849 // Read radio switches 
850 /////////////////////////////////////////////////////////////////////
851
852 bool FGATCInput::do_radio_switches() {
853     // Read the raw data
854     ATCReadRadios( radios_fd, radio_switch_data );
855
856     // Process the radio switch/knob inputs
857     if ( radio_in_node != NULL ) {
858         for ( int i = 0; i < radio_in_node->nChildren(); ++i ) {
859             // read the next config entry from the property tree
860
861             SGPropertyNode *child = radio_in_node->getChild(i);
862             string cname = child->getName();
863
864             if ( cname == "switch" ) {
865                 string name = "";
866                 string type = "";
867                 vector <SGPropertyNode *> output_nodes;
868                 int byte_num = -1;
869                 int right_shift = 0;
870                 int mask = 0xff;
871                 int factor = 1;
872                 int offset = 0;
873                 bool invert = false;
874                 int scaled_value = 0;
875                 // get common options
876
877                 SGPropertyNode *prop;
878                 prop = child->getChild( "name" );
879                 if ( prop != NULL ) {
880                     name = prop->getStringValue();
881                 }
882                 prop = child->getChild( "type" );
883                 if ( prop != NULL ) {
884                     type = prop->getStringValue();
885                 }
886                 int j = 0;
887                 while ( (prop = child->getChild("prop", j)) != NULL ) {
888                     SGPropertyNode *tmp
889                         = fgGetNode( prop->getStringValue(), true );
890                     output_nodes.push_back( tmp );
891                     j++;
892                 }
893                 prop = child->getChild( "byte" );
894                 if ( prop != NULL ) {
895                     byte_num = prop->getIntValue();
896                 }
897                 prop = child->getChild( "right-shift" );
898                 if ( prop != NULL ) {
899                     right_shift = prop->getIntValue();
900                 }
901                 prop = child->getChild( "mask" );
902                 if ( prop != NULL ) {
903                     mask = prop->getIntValue();
904                 }
905                 prop = child->getChild( "factor" );
906                 if ( prop != NULL ) {
907                     factor = prop->getIntValue();
908                 }
909                 prop = child->getChild( "offset" );
910                 if ( prop != NULL ) {
911                     offset = prop->getIntValue();
912                 }
913                 prop = child->getChild( "invert" );
914                 if ( prop != NULL ) {
915                     invert = prop->getBoolValue();
916                 }
917
918                 // Fetch the raw value
919                 int raw_value
920                     = (radio_switch_data[byte_num] >> right_shift) & mask;
921
922                 // Cook the value
923                 if ( invert ) {
924                     raw_value = !raw_value;
925                 }
926                 scaled_value = raw_value * factor + offset;
927
928                 // update the property tree values
929                 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
930                     output_nodes[j]->setIntValue( scaled_value );
931                 }
932             }
933         }
934     }
935
936     return true;
937 }
938
939
940 // process the hardware inputs.  This code assumes the calling layer
941 // will lock the hardware.
942 bool FGATCInput::process() {
943     if ( !is_open ) {
944         SG_LOG( SG_IO, SG_ALERT, "This board has not been opened for input! "
945                 << board );
946         return false;
947     }
948
949     do_analog_in();
950     do_switches();
951     do_radio_switches();
952         
953     return true;
954 }
955
956
957 bool FGATCInput::close() {
958
959 #if defined( unix ) || defined( __CYGWIN__ )
960
961     if ( !is_open ) {
962         return true;
963     }
964
965     int result;
966
967     result = ::close( analog_in_fd );
968     if ( result == -1 ) {
969         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
970         char msg[256];
971         snprintf( msg, 256, "Error closing %s", analog_in_file );
972         perror( msg );
973         exit( -1 );
974     }
975
976     result = ::close( radios_fd );
977     if ( result == -1 ) {
978         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
979         char msg[256];
980         snprintf( msg, 256, "Error closing %s", radios_file );
981         perror( msg );
982         exit( -1 );
983     }
984
985     result = ::close( switches_fd );
986     if ( result == -1 ) {
987         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
988         char msg[256];
989         snprintf( msg, 256, "Error closing %s", switches_file );
990         perror( msg );
991         exit( -1 );
992     }
993
994 #endif
995
996     return true;
997 }