]> git.mxchange.org Git - flightgear.git/blob - src/Network/ATC-Inputs.cxx
Melchior FRANZ:
[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 STL_STRING
37
38 #include <simgear/debug/logstream.hxx>
39
40 #include <Main/fg_props.hxx>
41
42 #include "ATC-Inputs.hxx"
43
44 SG_USING_STD(string);
45
46
47
48 // Constructor: The _board parameter specifies which board to
49 // reference.  Possible values are 0 or 1.  The _config_file parameter
50 // specifies the location of the input config file (xml)
51 FGATCInput::FGATCInput( const int _board, const SGPath &_config_file ) :
52     is_open(false),
53     ignore_flight_controls(NULL),
54     ignore_pedal_controls(NULL),
55     analog_in_node(NULL),
56     radio_in_node(NULL),
57     switches_node(NULL)
58 {
59     board = _board;
60     config = _config_file;
61 }
62
63
64 // Read analog inputs
65 static void ATCReadAnalogInputs( int fd, unsigned char *analog_in_bytes ) {
66 #if defined( unix ) || defined( __CYGWIN__ )
67     // rewind
68     lseek( fd, 0, SEEK_SET );
69
70     int result = read( fd, analog_in_bytes, ATC_ANAL_IN_BYTES );
71     if ( result != ATC_ANAL_IN_BYTES ) {
72         SG_LOG( SG_IO, SG_ALERT, "Read failed" );
73         exit( -1 );
74     }
75 #endif
76 }
77
78
79 // Read status of radio switches and knobs
80 static void ATCReadRadios( int fd, unsigned char *switch_data ) {
81 #if defined( unix ) || defined( __CYGWIN__ )
82     // rewind
83     lseek( fd, 0, SEEK_SET );
84
85     int result = read( fd, switch_data, ATC_RADIO_SWITCH_BYTES );
86     if ( result != ATC_RADIO_SWITCH_BYTES ) {
87         SG_LOG( SG_IO, SG_ALERT, "Read failed" );
88         exit( -1 );
89     }
90 #endif
91 }
92
93
94 // Read switch inputs
95 static void ATCReadSwitches( int fd, unsigned char *switch_bytes ) {
96 #if defined( unix ) || defined( __CYGWIN__ )
97     // rewind
98     lseek( fd, 0, SEEK_SET );
99
100     int result = read( fd, switch_bytes, ATC_SWITCH_BYTES );
101     if ( result != ATC_SWITCH_BYTES ) {
102         SG_LOG( SG_IO, SG_ALERT, "Read failed" );
103         exit( -1 );
104     }
105 #endif
106 }
107
108
109 void FGATCInput::init_config() {
110 #if defined( unix ) || defined( __CYGWIN__ )
111     if ( config.str()[0] != '/' ) {
112         // not an absolute path, prepend the standard location
113         SGPath tmp;
114         char *envp = ::getenv( "HOME" );
115         if ( envp != NULL ) {
116             tmp = envp;
117             tmp.append( ".atcflightsim" );
118             tmp.append( config.str() );
119             config = tmp;
120         }
121     }
122     readProperties( config.str(), globals->get_props() );
123 #endif
124 }
125
126
127 // Open and initialize the ATC hardware
128 bool FGATCInput::open() {
129     if ( is_open ) {
130         SG_LOG( SG_IO, SG_ALERT, "This board is already open for input! "
131                 << board );
132         return false;
133     }
134
135     // This loads the config parameters generated by "simcal"
136     init_config();
137
138     SG_LOG( SG_IO, SG_ALERT,
139             "Initializing ATC 610x hardware, please wait ..." );
140
141     snprintf( lock_file, 256, "/proc/atc610x/board%d/lock", board );
142     snprintf( analog_in_file, 256, "/proc/atc610x/board%d/analog_in", board );
143     snprintf( radios_file, 256, "/proc/atc610x/board%d/radios", board );
144     snprintf( switches_file, 256, "/proc/atc610x/board%d/switches", board );
145
146 #if defined( unix ) || defined( __CYGWIN__ )
147
148     /////////////////////////////////////////////////////////////////////
149     // Open the /proc files
150     /////////////////////////////////////////////////////////////////////
151
152     lock_fd = ::open( lock_file, O_RDWR );
153     if ( lock_fd == -1 ) {
154         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
155         char msg[256];
156         snprintf( msg, 256, "Error opening %s", lock_file );
157         perror( msg );
158         exit( -1 );
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 610x 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
229 static double scale( int center, int min, int max, int value ) {
230     // cout << center << " " << min << " " << max << " " << value << " ";
231     double result;
232     double range;
233
234     if ( value <= center ) {
235         range = center - min;
236         result = (value - center) / range;
237     } else {
238         range = max - center;
239         result = (value - center) / range;            
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             float factor = 1.0;
408             if ( cname == "channel" ) {
409                 SGPropertyNode *prop;
410                 prop = child->getChild( "name" );
411                 if ( prop != NULL ) {
412                     name = prop->getStringValue();
413                 }
414                 prop = child->getChild( "type", 0 );
415                 if ( prop != NULL ) {
416                     type = prop->getStringValue();
417                 }
418                 prop = child->getChild( "type", 1 );
419                 if ( prop != NULL ) {
420                     subtype = prop->getStringValue();
421                 }
422                 int j = 0;
423                 while ( (prop = child->getChild("prop", j)) != NULL ) {
424                     SGPropertyNode *tmp
425                         = fgGetNode( prop->getStringValue(), true );
426                     output_nodes.push_back( tmp );
427                     j++;
428                 }
429                 prop = child->getChild( "center" );
430                 if ( prop != NULL ) {
431                     center = prop->getIntValue();
432                 }
433                 prop = child->getChild( "min" );
434                 if ( prop != NULL ) {
435                     min = prop->getIntValue();
436                 }
437                 prop = child->getChild( "max" );
438                 if ( prop != NULL ) {
439                     max = prop->getIntValue();
440                 }
441                 prop = child->getChild( "factor" );
442                 if ( prop != NULL ) {
443                     factor = prop->getFloatValue();
444                 }
445
446                 // Fetch the raw value
447
448                 int raw_value = analog_in_data[index];
449
450                 // Update the target properties
451
452                 if ( type == "flight"
453                      && !ignore_flight_controls->getBoolValue() )
454                 {
455                     if ( subtype != "pedals" ||
456                          ( subtype == "pedals"
457                            && !ignore_pedal_controls->getBoolValue() ) )
458                     {
459                         // "Cook" the raw value
460                         float scaled_value = 0.0f;
461                         if ( center >= 0 ) {
462                             scaled_value = scale( center, min, max, raw_value );
463                         } else {
464                             scaled_value = scale( min, max, raw_value );
465                         }
466                         scaled_value *= factor;
467
468                         // update the property tree values
469                         for ( j = 0; j < (int)output_nodes.size(); ++j ) {
470                             output_nodes[j]->setDoubleValue( scaled_value );
471                         }
472                     }
473                 } else if ( type == "avionics-simple" ) {
474                     // "Cook" the raw value
475                     float scaled_value = 0.0f;
476                     if ( center >= 0 ) {
477                         scaled_value = scale( center, min, max, raw_value );
478                     } else {
479                         scaled_value = scale( min, max, raw_value );
480                     }
481                     scaled_value *= factor;
482
483                     // update the property tree values
484                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
485                         output_nodes[j]->setDoubleValue( scaled_value );
486                     }
487                 } else if ( type == "avionics-resolver" ) {
488                     // this type of analog input impliments a
489                     // rotational knob.  We first caclulate the amount
490                     // of knob rotation (slightly complex to work with
491                     // hardware specific goofiness) and then multiply
492                     // that amount of movement by a scaling factor,
493                     // and finally add the result to the original
494                     // value.
495
496                     bool do_init = false;
497                     float scaled_value = 0.0f;
498
499                     // fetch intermediate values from property tree
500
501                     prop = child->getChild( "is-inited", 0 );
502                     if ( prop == NULL ) {
503                         do_init = true;
504                         prop = child->getChild( "is-inited", 0, true );
505                         prop->setBoolValue( true );
506                     }
507
508                     int raw[3];
509                     for ( j = 0; j < 3; ++j ) {
510                         prop = child->getChild( "raw", j, true );
511                         if ( do_init ) {
512                             raw[j] = analog_in_data[index];
513                         } else {
514                             raw[j] = prop->getIntValue();
515                         }
516                     }
517
518                     // do Tony's magic to calculate knob movement
519                     // based on current analog input position and
520                     // historical data.
521                     int diff = tony_magic( analog_in_data[index], raw );
522
523                     // write raw intermediate values (updated by
524                     // tony_magic()) back to property tree
525                     for ( j = 0; j < 3; ++j ) {
526                         prop = child->getChild( "raw", j, true );
527                         prop->setIntValue( raw[j] );
528                     }
529
530                     // filter knob position
531                     prop = child->getChild( "diff-average", 0, true );
532                     double diff_ave = prop->getDoubleValue();
533                     diff_ave = instr_pot_filter( diff_ave, diff );
534                     prop->setDoubleValue( diff_ave );
535
536                     // calculate value adjustment in real world units
537                     scaled_value = diff_ave * factor;
538
539                     // update the property tree values
540                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
541                         float value = output_nodes[j]->getDoubleValue();
542                         value += scaled_value;
543
544                         prop = child->getChild( "min-clamp" );
545                         if ( prop != NULL ) {
546                             double min = prop->getDoubleValue();
547                             if ( value < min ) { value = min; }
548                         }
549
550                         prop = child->getChild( "max-clamp" );
551                         if ( prop != NULL ) {
552                             double max = prop->getDoubleValue();
553                             if ( value > max ) { value = max; }
554                         }
555
556                         prop = child->getChild( "compass-heading" );
557                         if ( prop != NULL ) {
558                             bool compass = prop->getBoolValue();
559                             if ( compass ) {
560                                 while ( value >= 360.0 ) { value -= 360.0; }
561                                 while ( value < 0.0 ) { value += 360.0; }
562                             }
563                         }
564
565                         output_nodes[j]->setDoubleValue( value );
566                     }
567
568                 } else {
569                     SG_LOG( SG_IO, SG_DEBUG, "Invalid channel type =  "
570                             << type );
571                 }
572             } else {
573                 SG_LOG( SG_IO, SG_DEBUG,
574                         "Input config error, expecting 'channel' but found "
575                         << cname );
576             }
577         }
578     }
579
580     return true;
581 }
582
583
584 /////////////////////////////////////////////////////////////////////
585 // Read the switch positions
586 /////////////////////////////////////////////////////////////////////
587
588 // decode the packed switch data
589 static void update_switch_matrix(
590         int board,
591         unsigned char switch_data[ATC_SWITCH_BYTES],
592         int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES] )
593 {
594     for ( int row = 0; row < ATC_SWITCH_BYTES; ++row ) {
595         unsigned char switches = switch_data[row];
596
597         for( int column = 0; column < ATC_NUM_COLS; ++column ) {
598             switch_matrix[board][column][row] = switches & 1;
599             switches = switches >> 1;
600         }                       
601     }
602 }                     
603
604 bool FGATCInput::do_switches() {
605     // Read the raw data
606     ATCReadSwitches( switches_fd, switch_data );
607
608     // unpack the switch data
609     int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES];
610     update_switch_matrix( board, switch_data, switch_matrix );
611
612     // Process the switch inputs
613     if ( switches_node != NULL ) {
614         for ( int i = 0; i < switches_node->nChildren(); ++i ) {
615             // read the next config entry from the property tree
616
617             SGPropertyNode *child = switches_node->getChild(i);
618             string cname = child->getName();
619             string name = "";
620             string type = "";
621             vector <SGPropertyNode *> output_nodes; output_nodes.clear();
622             int row = -1;
623             int col = -1;
624             float factor = 1.0;
625             int filter = -1;
626             float scaled_value = 0.0f;
627
628             // get common options
629
630             SGPropertyNode *prop;
631             prop = child->getChild( "name" );
632             if ( prop != NULL ) {
633                 name = prop->getStringValue();
634             }
635             prop = child->getChild( "type" );
636             if ( prop != NULL ) {
637                 type = prop->getStringValue();
638             }
639             int j = 0;
640             while ( (prop = child->getChild("prop", j)) != NULL ) {
641                 SGPropertyNode *tmp
642                     = fgGetNode( prop->getStringValue(), true );
643                 output_nodes.push_back( tmp );
644                 j++;
645             }
646             prop = child->getChild( "factor" );
647             if ( prop != NULL ) {
648                 factor = prop->getFloatValue();
649             }
650             prop = child->getChild( "steady-state-filter" );
651             if ( prop != NULL ) {
652                 filter = prop->getIntValue();
653             }
654
655             // handle different types of switches
656
657             if ( cname == "switch" ) {
658                 prop = child->getChild( "row" );
659                 if ( prop != NULL ) {
660                     row = prop->getIntValue();
661                 }
662                 prop = child->getChild( "col" );
663                 if ( prop != NULL ) {
664                     col = prop->getIntValue();
665                 }
666
667                 // Fetch the raw value
668                 int raw_value = switch_matrix[board][row][col];
669
670                 // Cook the value
671                 scaled_value = (float)raw_value * factor;
672
673             } else if ( cname == "combo-switch" ) {
674                 float combo_value = 0.0f;
675
676                 SGPropertyNode *pos;
677                 int k = 0;
678                 while ( (pos = child->getChild("position", k++)) != NULL ) {
679                     // read the combo position entries from the property tree
680
681                     prop = pos->getChild( "row" );
682                     if ( prop != NULL ) {
683                         row = prop->getIntValue();
684                     }
685                     prop = pos->getChild( "col" );
686                     if ( prop != NULL ) {
687                         col = prop->getIntValue();
688                     }
689                     prop = pos->getChild( "value" );
690                     if ( prop != NULL ) {
691                         combo_value = prop->getFloatValue();
692                     }
693
694                     // Fetch the raw value
695                     int raw_value = switch_matrix[board][row][col];
696                     // cout << "sm[" << board << "][" << row << "][" << col
697                     //      << "] = " << raw_value << endl;
698
699                     if ( raw_value ) {
700                         // set scaled_value to the first combo_value
701                         // that matches and jump out of loop.
702                         scaled_value = combo_value;
703                         break;
704                     }
705                 }
706
707                 // Cook the value
708                 scaled_value *= factor;
709             }
710
711             // handle filter request.  The value of the switch must be
712             // steady-state for "n" frames before the property value
713             // is updated.
714
715             bool update_prop = true;
716
717             if ( filter > 1 ) {
718                 SGPropertyNode *fv = child->getChild( "filter-value", 0, true );
719                 float filter_value = fv->getFloatValue();
720                 SGPropertyNode *fc = child->getChild( "filter-count", 0, true );
721                 int filter_count = fc->getIntValue();
722
723                 if ( fabs(scaled_value - filter_value) < 0.0001 ) {
724                     filter_count++;
725                 } else {
726                     filter_count = 0;
727                 }
728
729                 if ( filter_count < filter ) {
730                     update_prop = false;
731                 }
732
733                 fv->setFloatValue( scaled_value );
734                 fc->setIntValue( filter_count );
735             }
736
737             if ( update_prop ) {
738                 if ( type == "engine" || type == "flight" ) {
739                     if ( ! ignore_flight_controls->getBoolValue() ) {
740                         // update the property tree values
741                         for ( j = 0; j < (int)output_nodes.size(); ++j ) {
742                             output_nodes[j]->setDoubleValue( scaled_value );
743                         }
744                     }
745                 } else if ( type == "avionics" ) {
746                     // update the property tree values
747                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
748                         output_nodes[j]->setDoubleValue( scaled_value );
749                     }
750                 }
751             }
752         }
753     }
754
755     return true;
756 }
757
758
759 /////////////////////////////////////////////////////////////////////
760 // Read radio switches 
761 /////////////////////////////////////////////////////////////////////
762
763 bool FGATCInput::do_radio_switches() {
764     // Read the raw data
765     ATCReadRadios( radios_fd, radio_switch_data );
766
767     // Process the radio switch/knob inputs
768     if ( radio_in_node != NULL ) {
769         for ( int i = 0; i < radio_in_node->nChildren(); ++i ) {
770             // read the next config entry from the property tree
771
772             SGPropertyNode *child = radio_in_node->getChild(i);
773             string cname = child->getName();
774
775             if ( cname == "switch" ) {
776                 string name = "";
777                 string type = "";
778                 vector <SGPropertyNode *> output_nodes; output_nodes.clear();
779                 int byte_num = -1;
780                 int right_shift = 0;
781                 int mask = 0xff;
782                 int factor = 1;
783                 int offset = 0;
784                 bool invert = false;
785                 int scaled_value = 0;
786                 // get common options
787
788                 SGPropertyNode *prop;
789                 prop = child->getChild( "name" );
790                 if ( prop != NULL ) {
791                     name = prop->getStringValue();
792                 }
793                 prop = child->getChild( "type" );
794                 if ( prop != NULL ) {
795                     type = prop->getStringValue();
796                 }
797                 int j = 0;
798                 while ( (prop = child->getChild("prop", j)) != NULL ) {
799                     SGPropertyNode *tmp
800                         = fgGetNode( prop->getStringValue(), true );
801                     output_nodes.push_back( tmp );
802                     j++;
803                 }
804                 prop = child->getChild( "byte" );
805                 if ( prop != NULL ) {
806                     byte_num = prop->getIntValue();
807                 }
808                 prop = child->getChild( "right-shift" );
809                 if ( prop != NULL ) {
810                     right_shift = prop->getIntValue();
811                 }
812                 prop = child->getChild( "mask" );
813                 if ( prop != NULL ) {
814                     mask = prop->getIntValue();
815                 }
816                 prop = child->getChild( "factor" );
817                 if ( prop != NULL ) {
818                     factor = prop->getIntValue();
819                 }
820                 prop = child->getChild( "offset" );
821                 if ( prop != NULL ) {
822                     offset = prop->getIntValue();
823                 }
824                 prop = child->getChild( "invert" );
825                 if ( prop != NULL ) {
826                     invert = prop->getBoolValue();
827                 }
828
829                 // Fetch the raw value
830                 int raw_value
831                     = (radio_switch_data[byte_num] >> right_shift) & mask;
832
833                 // Cook the value
834                 if ( invert ) {
835                     raw_value = !raw_value;
836                 }
837                 scaled_value = raw_value * factor + offset;
838
839                 // update the property tree values
840                 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
841                     output_nodes[j]->setIntValue( scaled_value );
842                 }
843             }
844         }
845     }
846
847     return true;
848 }
849
850
851 // process the hardware inputs.  This code assumes the calling layer
852 // will lock the hardware.
853 bool FGATCInput::process() {
854     if ( !is_open ) {
855         SG_LOG( SG_IO, SG_ALERT, "This board has not been opened for input! "
856                 << board );
857         return false;
858     }
859
860     do_analog_in();
861     do_switches();
862     do_radio_switches();
863         
864     return true;
865 }
866
867
868 bool FGATCInput::close() {
869
870 #if defined( unix ) || defined( __CYGWIN__ )
871
872     int result;
873
874     result = ::close( lock_fd );
875     if ( result == -1 ) {
876         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
877         char msg[256];
878         snprintf( msg, 256, "Error closing %s", lock_file );
879         perror( msg );
880         exit( -1 );
881     }
882
883     result = ::close( analog_in_fd );
884     if ( result == -1 ) {
885         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
886         char msg[256];
887         snprintf( msg, 256, "Error closing %s", analog_in_file );
888         perror( msg );
889         exit( -1 );
890     }
891
892     result = ::close( radios_fd );
893     if ( result == -1 ) {
894         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
895         char msg[256];
896         snprintf( msg, 256, "Error closing %s", radios_file );
897         perror( msg );
898         exit( -1 );
899     }
900
901     result = ::close( switches_fd );
902     if ( result == -1 ) {
903         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
904         char msg[256];
905         snprintf( msg, 256, "Error closing %s", switches_file );
906         perror( msg );
907         exit( -1 );
908     }
909
910 #endif
911
912     return true;
913 }