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