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