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