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