1 // ATC-Inputs.hxx -- Translate ATC hardware inputs to FGFS properties
2 //
3 // Written by Curtis Olson, started November 2004.
4 //
5 // Copyright (C) 2004  Curtis L. Olson - http://www.flightgear.org/~curt
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22 
23 
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27 
28 #include <simgear/compiler.h>
29 
30 #if defined( unix ) || defined( __CYGWIN__ )
31 #  include <sys/types.h>
32 #  include <sys/stat.h>
33 #  include <fcntl.h>
34 #  include <stdlib.h>
35 #  include <unistd.h>
36 #  include <istream>
37 #endif
38 
39 #include <errno.h>
40 #include <cmath>
41 #include <cstdio>
42 
43 #include <string>
44 
45 #include <simgear/debug/logstream.hxx>
46 #include <simgear/misc/sg_path.hxx>
47 #include <simgear/props/props_io.hxx>
48 
49 #include <Main/fg_props.hxx>
50 
51 #include "ATC-Inputs.hxx"
52 
53 using std::string;
54 using std::vector;
55 
56 
57 // Constructor: The _board parameter specifies which board to
58 // reference.  Possible values are 0 or 1.  The _config_file parameter
59 // specifies the location of the input config file (xml)
FGATCInput(const int _board,const SGPath & _config_file)60 FGATCInput::FGATCInput( const int _board, const SGPath &_config_file ) :
61     is_open(false),
62     ignore_flight_controls(NULL),
63     ignore_pedal_controls(NULL),
64     analog_in_node(NULL),
65     radio_in_node(NULL),
66     switches_node(NULL)
67 {
68     board = _board;
69     config = _config_file;
70 }
71 
72 
73 // Read analog inputs
ATCReadAnalogInputs(int fd,unsigned char * analog_in_bytes)74 static void ATCReadAnalogInputs( int fd, unsigned char *analog_in_bytes ) {
75 #if defined( unix ) || defined( __CYGWIN__ )
76     // rewind
77     lseek( fd, 0, SEEK_SET );
78 
79     int result = read( fd, analog_in_bytes, ATC_ANAL_IN_BYTES );
80     if ( result != ATC_ANAL_IN_BYTES ) {
81 	SG_LOG( SG_IO, SG_ALERT, "Read failed" );
82 	exit( -1 );
83     }
84 #endif
85 }
86 
87 
88 // Read status of radio switches and knobs
ATCReadRadios(int fd,unsigned char * switch_data)89 static void ATCReadRadios( int fd, unsigned char *switch_data ) {
90 #if defined( unix ) || defined( __CYGWIN__ )
91     // rewind
92     lseek( fd, 0, SEEK_SET );
93 
94     int result = read( fd, switch_data, ATC_RADIO_SWITCH_BYTES );
95     if ( result != ATC_RADIO_SWITCH_BYTES ) {
96 	SG_LOG( SG_IO, SG_ALERT, "Read failed" );
97 	exit( -1 );
98     }
99 #endif
100 }
101 
102 
103 // Read switch inputs
ATCReadSwitches(int fd,unsigned char * switch_bytes)104 static void ATCReadSwitches( int fd, unsigned char *switch_bytes ) {
105 #if defined( unix ) || defined( __CYGWIN__ )
106     // rewind
107     lseek( fd, 0, SEEK_SET );
108 
109     int result = read( fd, switch_bytes, ATC_SWITCH_BYTES );
110     if ( result != ATC_SWITCH_BYTES ) {
111 	SG_LOG( SG_IO, SG_ALERT, "Read failed" );
112 	exit( -1 );
113     }
114 #endif
115 }
116 
117 
init_config()118 void FGATCInput::init_config() {
119 #if defined( unix ) || defined( __CYGWIN__ )
120     if ( !config.isAbsolute() ) {
121         // not an absolute path, prepend the standard location
122         SGPath tmp = SGPath::home();
123         tmp.append( ".atcflightsim" );
124         tmp.append( config.utf8Str() );
125     }
126     readProperties( config, globals->get_props() );
127 #endif
128 }
129 
130 
131 // Open and initialize the ATC hardware
open()132 bool FGATCInput::open() {
133     if ( is_open ) {
134 	SG_LOG( SG_IO, SG_ALERT, "This board is already open for input! "
135                 << board );
136 	return false;
137     }
138 
139     // This loads the config parameters generated by "simcal"
140     init_config();
141 
142     SG_LOG( SG_IO, SG_ALERT,
143 	    "Initializing ATC input hardware, please wait ..." );
144 
145     snprintf( analog_in_file, 256,
146               "/proc/atcflightsim/board%d/analog_in", board );
147     snprintf( radios_file, 256,
148               "/proc/atcflightsim/board%d/radios", board );
149     snprintf( switches_file, 256,
150               "/proc/atcflightsim/board%d/switches", board );
151 
152 #if defined( unix ) || defined( __CYGWIN__ )
153 
154     /////////////////////////////////////////////////////////////////////
155     // Open the /proc files
156     /////////////////////////////////////////////////////////////////////
157 
158     analog_in_fd = ::open( analog_in_file, O_RDONLY );
159     if ( analog_in_fd == -1 ) {
160 	SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
161 	char msg[300];
162 	snprintf( msg, 300, "Error opening %s", analog_in_file );
163 	perror( msg );
164 	exit( -1 );
165     }
166 
167     radios_fd = ::open( radios_file, O_RDWR );
168     if ( radios_fd == -1 ) {
169 	SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
170 	char msg[300];
171 	snprintf( msg, 300, "Error opening %s", radios_file );
172 	perror( msg );
173 	exit( -1 );
174     }
175 
176     switches_fd = ::open( switches_file, O_RDONLY );
177     if ( switches_fd == -1 ) {
178 	SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
179 	char msg[300];
180 	snprintf( msg, 300, "Error opening %s", switches_file );
181 	perror( msg );
182 	exit( -1 );
183     }
184 
185 #endif
186 
187     /////////////////////////////////////////////////////////////////////
188     // Finished initing hardware
189     /////////////////////////////////////////////////////////////////////
190 
191     SG_LOG( SG_IO, SG_ALERT,
192 	    "Done initializing ATC input hardware." );
193 
194     is_open = true;
195 
196     /////////////////////////////////////////////////////////////////////
197     // Connect up to property values
198     /////////////////////////////////////////////////////////////////////
199 
200     ignore_flight_controls
201         = fgGetNode( "/input/atcsim/ignore-flight-controls", true );
202     ignore_pedal_controls
203         = fgGetNode( "/input/atcsim/ignore-pedal-controls", true );
204 
205     char base_name[256];
206 
207     snprintf( base_name, 256, "/input/atc-board[%d]/analog-in", board );
208     analog_in_node = fgGetNode( base_name );
209 
210     snprintf( base_name, 256, "/input/atc-board[%d]/radio-switches", board );
211     radio_in_node = fgGetNode( base_name );
212 
213     snprintf( base_name, 256, "/input/atc-board[%d]/switches", board );
214     switches_node = fgGetNode( base_name );
215 
216     return true;
217 }
218 
219 
220 /////////////////////////////////////////////////////////////////////
221 // Read analog inputs
222 /////////////////////////////////////////////////////////////////////
223 
224 // scale a number between min and max (with center defined) to a scale
225 // from -1.0 to 1.0.  The deadband value is symmetric, so specifying
226 // '1' will give you a deadband of +/-1
scale(int center,int deadband,int min,int max,int value)227 static double scale( int center, int deadband, int min, int max, int value ) {
228     // cout << center << " " << min << " " << max << " " << value << " ";
229     double result;
230     double range;
231 
232     if ( value <= (center - deadband) ) {
233         range = (center - deadband) - min;
234         result = (value - (center - deadband)) / range;
235     } else if ( value >= (center + deadband) ) {
236         range = max - (center + deadband);
237         result = (value - (center + deadband)) / range;
238     } else {
239         result = 0.0;
240     }
241 
242     if ( result < -1.0 ) result = -1.0;
243     if ( result > 1.0 ) result = 1.0;
244 
245     // cout << result << endl;
246 
247     return result;
248 }
249 
250 
251 // scale a number between min and max to a scale from 0.0 to 1.0
scale(int min,int max,int value)252 static double scale( int min, int max, int value ) {
253     // cout << center << " " << min << " " << max << " " << value << " ";
254     double result;
255     double range;
256 
257     range = max - min;
258     result = (value - min) / range;
259 
260     if ( result < 0.0 ) result = 0.0;
261     if ( result > 1.0 ) result = 1.0;
262 
263     // cout << result << endl;
264 
265     return result;
266 }
267 
268 
clamp(double min,double max,double value)269 static double clamp( double min, double max, double value ) {
270     double result = value;
271 
272     if ( result < min ) result = min;
273     if ( result > max ) result = max;
274 
275     // cout << result << endl;
276 
277     return result;
278 }
279 
280 
tony_magic(int raw,int obs[3])281 static int tony_magic( int raw, int obs[3] ) {
282     int result = 0;
283 
284     obs[0] = raw;
285 
286     if ( obs[1] < 30 ) {
287         if ( obs[2] >= 68 && obs[2] < 480 ) {
288             result = -6;
289         } else if ( obs[2] >= 480 ) {
290             result = 6;
291         }
292         obs[2] = obs[1];
293         obs[1] = obs[0];
294     } else if ( obs[1] < 68 ) {
295         // do nothing
296         obs[1] = obs[0];
297     } else if ( obs[2] < 30 ) {
298         if ( obs[1] >= 68 && obs[1] < 480 ) {
299             result = 6;
300 	    obs[2] = obs[1];
301 	    obs[1] = obs[0];
302         } else if ( obs[1] >= 480 ) {
303 	    result = -6;
304             if ( obs[0] < obs[1] ) {
305 		obs[2] = obs[1];
306 		obs[1] = obs[0];
307 	    } else {
308 	        obs[2] = obs[0];
309 		obs[1] = obs[0];
310 	    }
311         }
312     } else if ( obs[1] > 980 ) {
313         if ( obs[2] <= 956 && obs[2] > 480 ) {
314             result = 6;
315         } else if ( obs[2] <= 480 ) {
316             result = -6;
317         }
318         obs[2] = obs[1];
319         obs[1] = obs[0];
320     } else if ( obs[1] > 956 ) {
321         // do nothing
322         obs[1] = obs[0];
323     } else if ( obs[2] > 980 ) {
324         if ( obs[1] <= 956 && obs[1] > 480 ) {
325             result = -6;
326 	    obs[2] = obs[1];
327 	    obs[1] = obs[0];
328         } else if ( obs[1] <= 480 ) {
329 	    result = 6;
330             if ( obs[0] > obs[1] ) {
331 		obs[2] = obs[1];
332 		obs[1] = obs[0];
333 	    } else {
334 		obs[2] = obs[0];
335 		obs[1] = obs[0];
336 	    }
337         }
338     } else {
339         if ( obs[1] < 480 && obs[2] > 480 ) {
340 	    // crossed gap going up
341 	    if ( obs[0] < obs[1] ) {
342 	        // caught a bogus intermediate value coming out of the gap
343 	        obs[1] = obs[0];
344 	    }
345 	} else if ( obs[1] > 480 && obs[2] < 480 ) {
346 	    // crossed gap going down
347 	    if ( obs[0] > obs[1] ) {
348 	        // caught a bogus intermediate value coming out of the gap
349 	      obs[1] = obs[0];
350 	    }
351 	} else if ( obs[0] > 480 && obs[1] < 480 && obs[2] < 480 ) {
352             // crossed the gap going down
353 	    if ( obs[1] > obs[2] ) {
354 	        // caught a bogus intermediate value coming out of the gap
355 	        obs[1] = obs[2];
356 	    }
357 	} else if ( obs[0] < 480 && obs[1] > 480 && obs[2] > 480 ) {
358             // crossed the gap going up
359 	    if ( obs[1] < obs[2] ) {
360 	        // caught a bogus intermediate value coming out of the gap
361 	        obs[1] = obs[2];
362 	    }
363 	}
364         result = obs[1] - obs[2];
365         if ( abs(result) > 400 ) {
366             // ignore
367             result = 0;
368         }
369         obs[2] = obs[1];
370         obs[1] = obs[0];
371     }
372 
373     // cout << " result = " << result << endl;
374     if ( result < -500 ) { result += 1024; }
375     if ( result > 500 ) { result -= 1024; }
376 
377     return result;
378 }
379 
380 
instr_pot_filter(double ave,double val)381 static double instr_pot_filter( double ave, double val ) {
382     if ( fabs(ave - val) < 400 || fabs(val) < fabs(ave) ) {
383         return 0.5 * ave + 0.5 * val;
384     } else {
385         return ave;
386     }
387 }
388 
389 
do_analog_in()390 bool FGATCInput::do_analog_in() {
391     // Read raw data in byte form
392     ATCReadAnalogInputs( analog_in_fd, analog_in_bytes );
393 
394     // Convert to integer values
395     for ( int channel = 0; channel < ATC_ANAL_IN_VALUES; ++channel ) {
396 	unsigned char hi = analog_in_bytes[2 * channel] & 0x03;
397 	unsigned char lo = analog_in_bytes[2 * channel + 1];
398 	analog_in_data[channel] = hi * 256 + lo;
399 
400 	// printf("%02x %02x ", hi, lo );
401 	// printf("%04d ", value );
402     }
403 
404     // Process analog inputs
405     if ( analog_in_node != NULL ) {
406         for ( int i = 0; i < analog_in_node->nChildren(); ++i ) {
407             // read the next config entry from the property tree
408 
409             SGPropertyNode *child = analog_in_node->getChild(i);
410             string cname = child->getName();
411             int index = child->getIndex();
412             string name = "";
413             string type = "";
414             string subtype = "";
415             vector <SGPropertyNode *> output_nodes;
416             int center = -1;
417             int min = 0;
418             int max = 1023;
419 	    int deadband = 0;
420 	    int hysteresis = 0;
421             float offset = 0.0;
422             float factor = 1.0;
423             if ( cname == "channel" ) {
424                 SGPropertyNode *prop;
425                 prop = child->getChild( "name" );
426                 if ( prop != NULL ) {
427                     name = prop->getStringValue();
428                 }
429                 prop = child->getChild( "type", 0 );
430                 if ( prop != NULL ) {
431                     type = prop->getStringValue();
432                 }
433                 prop = child->getChild( "type", 1 );
434                 if ( prop != NULL ) {
435                     subtype = prop->getStringValue();
436                 }
437                 int j = 0;
438                 while ( (prop = child->getChild("prop", j)) != NULL ) {
439                     SGPropertyNode *tmp
440                         = fgGetNode( prop->getStringValue(), true );
441                     output_nodes.push_back( tmp );
442                     j++;
443                 }
444                 prop = child->getChild( "center" );
445                 if ( prop != NULL ) {
446                     center = prop->getIntValue();
447                 }
448                 prop = child->getChild( "min" );
449                 if ( prop != NULL ) {
450                     min = prop->getIntValue();
451                 }
452                 prop = child->getChild( "max" );
453                 if ( prop != NULL ) {
454                     max = prop->getIntValue();
455                 }
456                 prop = child->getChild( "deadband" );
457                 if ( prop != NULL ) {
458                     deadband = prop->getIntValue();
459                 }
460                 prop = child->getChild( "hysteresis" );
461                 if ( prop != NULL ) {
462                     hysteresis = prop->getIntValue();
463                 }
464                 prop = child->getChild( "offset" );
465                 if ( prop != NULL ) {
466                     offset = prop->getFloatValue();
467                 }
468                 prop = child->getChild( "factor" );
469                 if ( prop != NULL ) {
470                     factor = prop->getFloatValue();
471                 }
472 
473                 // Fetch the raw value
474 
475                 int raw_value = analog_in_data[index];
476 
477                 // Update the target properties
478 
479                 if ( type == "flight"
480                      && !ignore_flight_controls->getBoolValue() )
481                 {
482                     if ( subtype != "pedals" ||
483                          ( subtype == "pedals"
484                            && !ignore_pedal_controls->getBoolValue() ) )
485                     {
486                         // "Cook" the raw value
487                         float scaled_value = 0.0f;
488 
489 			if ( hysteresis > 0 ) {
490 			    int last_raw_value = 0;
491 			    prop = child->getChild( "last-raw-value", 0, true );
492 			    last_raw_value = prop->getIntValue();
493 
494 			    if ( abs(raw_value - last_raw_value) < hysteresis )
495 			    {
496 				// not enough movement stay put
497 				raw_value = last_raw_value;
498 			    } else {
499 				// update last raw value
500 				prop->setIntValue( raw_value );
501 			    }
502 			}
503 
504                         if ( center >= 0 ) {
505                             scaled_value = scale( center, deadband,
506 						  min, max, raw_value );
507                         } else {
508                             scaled_value = scale( min, max, raw_value );
509                         }
510                         scaled_value *= factor;
511                         scaled_value += offset;
512 
513                         // final sanity clamp
514                         if ( center >= 0 ) {
515                             scaled_value = clamp( -1.0, 1.0, scaled_value );
516                         } else {
517                             scaled_value = clamp( 0.0, 1.0, scaled_value );
518                         }
519 
520                         // update the property tree values
521                         for ( j = 0; j < (int)output_nodes.size(); ++j ) {
522                             output_nodes[j]->setDoubleValue( scaled_value );
523                         }
524                     }
525                 } else if ( type == "avionics-simple" ) {
526                     // "Cook" the raw value
527                     float scaled_value = 0.0f;
528                     if ( center >= 0 ) {
529                         scaled_value = scale( center, deadband,
530 					      min, max, raw_value );
531                     } else {
532                         scaled_value = scale( min, max, raw_value );
533                     }
534                     scaled_value *= factor;
535                     scaled_value += offset;
536 
537                     // final sanity clamp
538                     if ( center >= 0 ) {
539                         scaled_value = clamp( -1.0, 1.0, scaled_value );
540                     } else {
541                         scaled_value = clamp( 0.0, 1.0, scaled_value );
542                     }
543 
544                     // update the property tree values
545                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
546                         output_nodes[j]->setDoubleValue( scaled_value );
547                     }
548                 } else if ( type == "avionics-resolver" ) {
549                     // this type of analog input impliments a
550                     // rotational knob.  We first caclulate the amount
551                     // of knob rotation (slightly complex to work with
552                     // hardware specific goofiness) and then multiply
553                     // that amount of movement by a scaling factor,
554                     // and finally add the result to the original
555                     // value.
556 
557                     bool do_init = false;
558                     float scaled_value = 0.0f;
559 
560                     // fetch intermediate values from property tree
561 
562                     prop = child->getChild( "is-inited", 0 );
563                     if ( prop == NULL ) {
564                         do_init = true;
565                         prop = child->getChild( "is-inited", 0, true );
566                         prop->setBoolValue( true );
567                     }
568 
569                     int raw[3];
570                     for ( j = 0; j < 3; ++j ) {
571                         prop = child->getChild( "raw", j, true );
572                         if ( do_init ) {
573                             raw[j] = analog_in_data[index];
574                         } else {
575                             raw[j] = prop->getIntValue();
576                         }
577                     }
578 
579                     // do Tony's magic to calculate knob movement
580                     // based on current analog input position and
581                     // historical data.
582                     int diff = tony_magic( analog_in_data[index], raw );
583 
584                     // write raw intermediate values (updated by
585                     // tony_magic()) back to property tree
586                     for ( j = 0; j < 3; ++j ) {
587                         prop = child->getChild( "raw", j, true );
588                         prop->setIntValue( raw[j] );
589                     }
590 
591                     // filter knob position
592                     prop = child->getChild( "diff-average", 0, true );
593                     double diff_ave = prop->getDoubleValue();
594                     diff_ave = instr_pot_filter( diff_ave, diff );
595                     prop->setDoubleValue( diff_ave );
596 
597                     // calculate value adjustment in real world units
598                     scaled_value = diff_ave * factor;
599 
600                     // update the property tree values
601                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
602                         float value = output_nodes[j]->getDoubleValue();
603                         value += scaled_value;
604 
605                         prop = child->getChild( "min-clamp" );
606                         if ( prop != NULL ) {
607                             double min = prop->getDoubleValue();
608                             if ( value < min ) { value = min; }
609                         }
610 
611                         prop = child->getChild( "max-clamp" );
612                         if ( prop != NULL ) {
613                             double max = prop->getDoubleValue();
614                             if ( value > max ) { value = max; }
615                         }
616 
617                         prop = child->getChild( "compass-heading" );
618                         if ( prop != NULL ) {
619                             bool compass = prop->getBoolValue();
620                             if ( compass ) {
621                                 while ( value >= 360.0 ) { value -= 360.0; }
622                                 while ( value < 0.0 ) { value += 360.0; }
623                             }
624                         }
625 
626                         output_nodes[j]->setDoubleValue( value );
627                     }
628 
629                 } else {
630                     SG_LOG( SG_IO, SG_DEBUG, "Invalid channel type =  "
631                             << type );
632                 }
633             } else {
634                 SG_LOG( SG_IO, SG_DEBUG,
635                         "Input config error, expecting 'channel' but found "
636                         << cname );
637             }
638         }
639     }
640 
641     return true;
642 }
643 
644 
645 /////////////////////////////////////////////////////////////////////
646 // Read the switch positions
647 /////////////////////////////////////////////////////////////////////
648 
649 // decode the packed switch data
update_switch_matrix(int board,unsigned char switch_data[ATC_SWITCH_BYTES],int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES])650 static void update_switch_matrix(
651         int board,
652 	unsigned char switch_data[ATC_SWITCH_BYTES],
653 	int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES] )
654 {
655     for ( int row = 0; row < ATC_SWITCH_BYTES; ++row ) {
656 	unsigned char switches = switch_data[row];
657 
658 	for( int column = 0; column < ATC_NUM_COLS; ++column ) {
659             if ( row < 8 ) {
660                 switch_matrix[board][column][row] = switches & 1;
661             } else {
662                 switch_matrix[board][row-8][8+column] = switches & 1;
663             }
664             switches = switches >> 1;
665         }
666     }
667 }
668 
do_switches()669 bool FGATCInput::do_switches() {
670     // Read the raw data
671     ATCReadSwitches( switches_fd, switch_data );
672 
673     // unpack the switch data
674     int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES];
675     update_switch_matrix( board, switch_data, switch_matrix );
676 
677     // Process the switch inputs
678     if ( switches_node != NULL ) {
679         for ( int i = 0; i < switches_node->nChildren(); ++i ) {
680             // read the next config entry from the property tree
681 
682             SGPropertyNode *child = switches_node->getChild(i);
683             string cname = child->getName();
684             string name = "";
685             string type = "";
686             vector <SGPropertyNode *> output_nodes;
687             int row = -1;
688             int col = -1;
689             float factor = 1.0;
690             int filter = -1;
691             float scaled_value = 0.0f;
692 	    bool invert = false;
693 
694             // get common options
695 
696             SGPropertyNode *prop;
697             prop = child->getChild( "name" );
698             if ( prop != NULL ) {
699                 name = prop->getStringValue();
700             }
701             prop = child->getChild( "type" );
702             if ( prop != NULL ) {
703                 type = prop->getStringValue();
704             }
705             int j = 0;
706             while ( (prop = child->getChild("prop", j)) != NULL ) {
707                 SGPropertyNode *tmp
708                     = fgGetNode( prop->getStringValue(), true );
709                 output_nodes.push_back( tmp );
710                 j++;
711             }
712             prop = child->getChild( "factor" );
713             if ( prop != NULL ) {
714                 factor = prop->getFloatValue();
715             }
716             prop = child->getChild( "invert" );
717             if ( prop != NULL ) {
718                 invert = prop->getBoolValue();
719             }
720             prop = child->getChild( "steady-state-filter" );
721             if ( prop != NULL ) {
722                 filter = prop->getIntValue();
723             }
724 
725             // handle different types of switches
726 
727             if ( cname == "switch" ) {
728                 prop = child->getChild( "row" );
729                 if ( prop != NULL ) {
730                     row = prop->getIntValue();
731                 }
732                 prop = child->getChild( "col" );
733                 if ( prop != NULL ) {
734                     col = prop->getIntValue();
735                 }
736 
737                 // Fetch the raw value
738                 int raw_value = switch_matrix[board][row][col];
739 
740 		// Invert
741 		if ( invert ) {
742 		    raw_value = !raw_value;
743 		}
744 
745                 // Cook the value
746                 scaled_value = (float)raw_value * factor;
747 
748             } else if ( cname == "combo-switch" ) {
749                 float combo_value = 0.0f;
750 
751                 SGPropertyNode *pos;
752                 int k = 0;
753                 while ( (pos = child->getChild("position", k++)) != NULL ) {
754                     // read the combo position entries from the property tree
755 
756                     prop = pos->getChild( "row" );
757                     if ( prop != NULL ) {
758                         row = prop->getIntValue();
759                     }
760                     prop = pos->getChild( "col" );
761                     if ( prop != NULL ) {
762                         col = prop->getIntValue();
763                     }
764                     prop = pos->getChild( "value" );
765                     if ( prop != NULL ) {
766                         combo_value = prop->getFloatValue();
767                     }
768 
769                     // Fetch the raw value
770                     int raw_value = switch_matrix[board][row][col];
771                     // cout << "sm[" << board << "][" << row << "][" << col
772                     //      << "] = " << raw_value << endl;
773 
774                     if ( raw_value ) {
775                         // set scaled_value to the first combo_value
776                         // that matches and jump out of loop.
777                         scaled_value = combo_value;
778                         break;
779                     }
780                 }
781 
782                 // Cook the value
783                 scaled_value *= factor;
784             } else if ( cname == "additive-switch" ) {
785                 float additive_value = 0.0f;
786 		float increment = 0.0f;
787 
788                 SGPropertyNode *pos;
789                 int k = 0;
790                 while ( (pos = child->getChild("position", k++)) != NULL ) {
791                     // read the combo position entries from the property tree
792 
793                     prop = pos->getChild( "row" );
794                     if ( prop != NULL ) {
795                         row = prop->getIntValue();
796                     }
797                     prop = pos->getChild( "col" );
798                     if ( prop != NULL ) {
799                         col = prop->getIntValue();
800                     }
801                     prop = pos->getChild( "value" );
802                     if ( prop != NULL ) {
803                         increment = prop->getFloatValue();
804                     }
805 
806                     // Fetch the raw value
807                     int raw_value = switch_matrix[board][row][col];
808                     // cout << "sm[" << board << "][" << row << "][" << col
809                     //      << "] = " << raw_value << endl;
810 
811                     if ( raw_value ) {
812                         // set scaled_value to the first combo_value
813                         // that matches and jump out of loop.
814                         additive_value += increment;
815                     }
816                 }
817 
818                 // Cook the value
819                 scaled_value = additive_value * factor;
820             }
821 
822             // handle filter request.  The value of the switch must be
823             // steady-state for "n" frames before the property value
824             // is updated.
825 
826             bool update_prop = true;
827 
828             if ( filter > 1 ) {
829                 SGPropertyNode *fv = child->getChild( "filter-value", 0, true );
830                 float filter_value = fv->getFloatValue();
831                 SGPropertyNode *fc = child->getChild( "filter-count", 0, true );
832                 int filter_count = fc->getIntValue();
833 
834                 if ( fabs(scaled_value - filter_value) < 0.0001 ) {
835                     filter_count++;
836                 } else {
837                     filter_count = 0;
838                 }
839 
840                 if ( filter_count < filter ) {
841                     update_prop = false;
842                 }
843 
844                 fv->setFloatValue( scaled_value );
845                 fc->setIntValue( filter_count );
846             }
847 
848             if ( update_prop ) {
849                 if ( type == "engine" || type == "flight" ) {
850                     if ( ! ignore_flight_controls->getBoolValue() ) {
851                         // update the property tree values
852                         for ( j = 0; j < (int)output_nodes.size(); ++j ) {
853                             output_nodes[j]->setDoubleValue( scaled_value );
854                         }
855                     }
856                 } else if ( type == "avionics" ) {
857                     // update the property tree values
858                     for ( j = 0; j < (int)output_nodes.size(); ++j ) {
859                         output_nodes[j]->setDoubleValue( scaled_value );
860                     }
861                 }
862             }
863         }
864     }
865 
866     return true;
867 }
868 
869 
870 /////////////////////////////////////////////////////////////////////
871 // Read radio switches
872 /////////////////////////////////////////////////////////////////////
873 
do_radio_switches()874 bool FGATCInput::do_radio_switches() {
875     // Read the raw data
876     ATCReadRadios( radios_fd, radio_switch_data );
877 
878     // Process the radio switch/knob inputs
879     if ( radio_in_node != NULL ) {
880         for ( int i = 0; i < radio_in_node->nChildren(); ++i ) {
881             // read the next config entry from the property tree
882 
883             SGPropertyNode *child = radio_in_node->getChild(i);
884             string cname = child->getName();
885 
886             if ( cname == "switch" ) {
887                 string name = "";
888                 string type = "";
889                 vector <SGPropertyNode *> output_nodes;
890                 int byte_num = -1;
891                 int right_shift = 0;
892                 int mask = 0xff;
893                 int factor = 1;
894                 int offset = 0;
895                 bool invert = false;
896                 int scaled_value = 0;
897                 // get common options
898 
899                 SGPropertyNode *prop;
900                 prop = child->getChild( "name" );
901                 if ( prop != NULL ) {
902                     name = prop->getStringValue();
903                 }
904                 prop = child->getChild( "type" );
905                 if ( prop != NULL ) {
906                     type = prop->getStringValue();
907                 }
908                 int j = 0;
909                 while ( (prop = child->getChild("prop", j)) != NULL ) {
910                     SGPropertyNode *tmp
911                         = fgGetNode( prop->getStringValue(), true );
912                     output_nodes.push_back( tmp );
913                     j++;
914                 }
915                 prop = child->getChild( "byte" );
916                 if ( prop != NULL ) {
917                     byte_num = prop->getIntValue();
918                 }
919                 prop = child->getChild( "right-shift" );
920                 if ( prop != NULL ) {
921                     right_shift = prop->getIntValue();
922                 }
923                 prop = child->getChild( "mask" );
924                 if ( prop != NULL ) {
925                     mask = prop->getIntValue();
926                 }
927                 prop = child->getChild( "factor" );
928                 if ( prop != NULL ) {
929                     factor = prop->getIntValue();
930                 }
931                 prop = child->getChild( "offset" );
932                 if ( prop != NULL ) {
933                     offset = prop->getIntValue();
934                 }
935                 prop = child->getChild( "invert" );
936                 if ( prop != NULL ) {
937                     invert = prop->getBoolValue();
938                 }
939 
940                 // Fetch the raw value
941                 int raw_value
942                     = (radio_switch_data[byte_num] >> right_shift) & mask;
943 
944                 // Cook the value
945                 if ( invert ) {
946                     raw_value = !raw_value;
947                 }
948                 scaled_value = raw_value * factor + offset;
949 
950                 // update the property tree values
951                 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
952                     output_nodes[j]->setIntValue( scaled_value );
953                 }
954             }
955         }
956     }
957 
958     return true;
959 }
960 
961 
962 // process the hardware inputs.  This code assumes the calling layer
963 // will lock the hardware.
process()964 bool FGATCInput::process() {
965     if ( !is_open ) {
966 	SG_LOG( SG_IO, SG_ALERT, "This board has not been opened for input! "
967                 << board );
968 	return false;
969     }
970 
971     do_analog_in();
972     do_switches();
973     do_radio_switches();
974 
975     return true;
976 }
977 
978 
close()979 bool FGATCInput::close() {
980 
981 #if defined( unix ) || defined( __CYGWIN__ )
982 
983     if ( !is_open ) {
984         return true;
985     }
986 
987     int result;
988 
989     result = ::close( analog_in_fd );
990     if ( result == -1 ) {
991 	SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
992 	char msg[300];
993 	snprintf( msg, 300, "Error closing %s", analog_in_file );
994 	perror( msg );
995 	exit( -1 );
996     }
997 
998     result = ::close( radios_fd );
999     if ( result == -1 ) {
1000 	SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
1001 	char msg[300];
1002 	snprintf( msg, 300, "Error closing %s", radios_file );
1003 	perror( msg );
1004 	exit( -1 );
1005     }
1006 
1007     result = ::close( switches_fd );
1008     if ( result == -1 ) {
1009 	SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
1010 	char msg[300];
1011 	snprintf( msg, 300, "Error closing %s", switches_file );
1012 	perror( msg );
1013 	exit( -1 );
1014     }
1015 
1016 #endif
1017 
1018     return true;
1019 }
1020