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