1 // ATC-Inputs.hxx -- Translate ATC hardware inputs to FGFS properties
3 // Written by Curtis Olson, started November 2004.
5 // Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
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.
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.
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.
28 #include <simgear/compiler.h>
30 #if defined( unix ) || defined( __CYGWIN__ )
31 # include <sys/types.h>
32 # include <sys/stat.h>
38 #include <simgear/debug/logstream.hxx>
40 #include <Main/fg_props.hxx>
42 #include "ATC-Inputs.hxx"
48 // Constructor: The _board parameter specifies which board to
49 // reference. Possible values are 0 or 1. The _config_file parameter
50 // specifies the location of the input config file (xml)
51 FGATCInput::FGATCInput( const int _board, const SGPath &_config_file ) :
53 ignore_flight_controls(NULL),
54 ignore_pedal_controls(NULL),
60 config = _config_file;
65 static void ATCReadAnalogInputs( int fd, unsigned char *analog_in_bytes ) {
67 lseek( fd, 0, SEEK_SET );
69 int result = read( fd, analog_in_bytes, ATC_ANAL_IN_BYTES );
70 if ( result != ATC_ANAL_IN_BYTES ) {
71 SG_LOG( SG_IO, SG_ALERT, "Read failed" );
77 // Read status of radio switches and knobs
78 static void ATCReadRadios( int fd, unsigned char *switch_data ) {
80 lseek( fd, 0, SEEK_SET );
82 int result = read( fd, switch_data, ATC_RADIO_SWITCH_BYTES );
83 if ( result != ATC_RADIO_SWITCH_BYTES ) {
84 SG_LOG( SG_IO, SG_ALERT, "Read failed" );
91 static void ATCReadSwitches( int fd, unsigned char *switch_bytes ) {
93 lseek( fd, 0, SEEK_SET );
95 int result = read( fd, switch_bytes, ATC_SWITCH_BYTES );
96 if ( result != ATC_SWITCH_BYTES ) {
97 SG_LOG( SG_IO, SG_ALERT, "Read failed" );
103 void FGATCInput::init_config() {
104 #if defined( unix ) || defined( __CYGWIN__ )
105 if ( config.str()[0] != '/' ) {
106 // not an absolute path, prepend the standard location
108 char *envp = ::getenv( "HOME" );
109 if ( envp != NULL ) {
111 tmp.append( ".atcflightsim" );
112 tmp.append( config.str() );
116 readProperties( config.str(), globals->get_props() );
121 // Open and initialize the ATC hardware
122 bool FGATCInput::open() {
124 SG_LOG( SG_IO, SG_ALERT, "This board is already open for input! "
129 // This loads the config parameters generated by "simcal"
132 SG_LOG( SG_IO, SG_ALERT,
133 "Initializing ATC 610x hardware, please wait ..." );
135 snprintf( lock_file, 256, "/proc/atc610x/board%d/lock", board );
136 snprintf( analog_in_file, 256, "/proc/atc610x/board%d/analog_in", board );
137 snprintf( radios_file, 256, "/proc/atc610x/board%d/radios", board );
138 snprintf( switches_file, 256, "/proc/atc610x/board%d/switches", board );
140 #if defined( unix ) || defined( __CYGWIN__ )
142 /////////////////////////////////////////////////////////////////////
143 // Open the /proc files
144 /////////////////////////////////////////////////////////////////////
146 lock_fd = ::open( lock_file, O_RDWR );
147 if ( lock_fd == -1 ) {
148 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
150 snprintf( msg, 256, "Error opening %s", lock_file );
155 analog_in_fd = ::open( analog_in_file, O_RDONLY );
156 if ( analog_in_fd == -1 ) {
157 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
159 snprintf( msg, 256, "Error opening %s", analog_in_file );
164 radios_fd = ::open( radios_file, O_RDWR );
165 if ( radios_fd == -1 ) {
166 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
168 snprintf( msg, 256, "Error opening %s", radios_file );
173 switches_fd = ::open( switches_file, O_RDONLY );
174 if ( switches_fd == -1 ) {
175 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
177 snprintf( msg, 256, "Error opening %s", switches_file );
184 /////////////////////////////////////////////////////////////////////
185 // Finished initing hardware
186 /////////////////////////////////////////////////////////////////////
188 SG_LOG( SG_IO, SG_ALERT,
189 "Done initializing ATC 610x hardware." );
193 /////////////////////////////////////////////////////////////////////
194 // Connect up to property values
195 /////////////////////////////////////////////////////////////////////
197 ignore_flight_controls
198 = fgGetNode( "/input/atcsim/ignore-flight-controls", true );
199 ignore_pedal_controls
200 = fgGetNode( "/input/atcsim/ignore-pedal-controls", true );
204 snprintf( base_name, 256, "/input/atc-board[%d]/analog-in", board );
205 analog_in_node = fgGetNode( base_name );
207 snprintf( base_name, 256, "/input/atc-board[%d]/radio-switches", board );
208 radio_in_node = fgGetNode( base_name );
210 snprintf( base_name, 256, "/input/atc-board[%d]/switches", board );
211 switches_node = fgGetNode( base_name );
217 /////////////////////////////////////////////////////////////////////
218 // Read analog inputs
219 /////////////////////////////////////////////////////////////////////
221 // scale a number between min and max (with center defined) to a scale
223 static double scale( int center, int min, int max, int value ) {
224 // cout << center << " " << min << " " << max << " " << value << " ";
228 if ( value <= center ) {
229 range = center - min;
230 result = (value - center) / range;
232 range = max - center;
233 result = (value - center) / range;
236 if ( result < -1.0 ) result = -1.0;
237 if ( result > 1.0 ) result = 1.0;
239 // cout << result << endl;
245 // scale a number between min and max to a scale from 0.0 to 1.0
246 static double scale( int min, int max, int value ) {
247 // cout << center << " " << min << " " << max << " " << value << " ";
252 result = (value - min) / range;
254 if ( result < 0.0 ) result = 0.0;
255 if ( result > 1.0 ) result = 1.0;
257 // cout << result << endl;
263 static int tony_magic( int raw, int obs[3] ) {
269 if ( obs[2] >= 68 && obs[2] < 480 ) {
271 } else if ( obs[2] >= 480 ) {
276 } else if ( obs[1] < 68 ) {
279 } else if ( obs[2] < 30 ) {
280 if ( obs[1] >= 68 && obs[1] < 480 ) {
284 } else if ( obs[1] >= 480 ) {
286 if ( obs[0] < obs[1] ) {
294 } else if ( obs[1] > 980 ) {
295 if ( obs[2] <= 956 && obs[2] > 480 ) {
297 } else if ( obs[2] <= 480 ) {
302 } else if ( obs[1] > 956 ) {
305 } else if ( obs[2] > 980 ) {
306 if ( obs[1] <= 956 && obs[1] > 480 ) {
310 } else if ( obs[1] <= 480 ) {
312 if ( obs[0] > obs[1] ) {
321 if ( obs[1] < 480 && obs[2] > 480 ) {
322 // crossed gap going up
323 if ( obs[0] < obs[1] ) {
324 // caught a bogus intermediate value coming out of the gap
327 } else if ( obs[1] > 480 && obs[2] < 480 ) {
328 // crossed gap going down
329 if ( obs[0] > obs[1] ) {
330 // caught a bogus intermediate value coming out of the gap
333 } else if ( obs[0] > 480 && obs[1] < 480 && obs[2] < 480 ) {
334 // crossed the gap going down
335 if ( obs[1] > obs[2] ) {
336 // caught a bogus intermediate value coming out of the gap
339 } else if ( obs[0] < 480 && obs[1] > 480 && obs[2] > 480 ) {
340 // crossed the gap going up
341 if ( obs[1] < obs[2] ) {
342 // caught a bogus intermediate value coming out of the gap
346 result = obs[1] - obs[2];
347 if ( abs(result) > 400 ) {
355 // cout << " result = " << result << endl;
356 if ( result < -500 ) { result += 1024; }
357 if ( result > 500 ) { result -= 1024; }
363 static double instr_pot_filter( double ave, double val ) {
364 if ( fabs(ave - val) < 400 || fabs(val) < fabs(ave) ) {
365 return 0.5 * ave + 0.5 * val;
372 bool FGATCInput::do_analog_in() {
373 // Read raw data in byte form
374 ATCReadAnalogInputs( analog_in_fd, analog_in_bytes );
376 // Convert to integer values
377 for ( int channel = 0; channel < ATC_ANAL_IN_VALUES; ++channel ) {
378 unsigned char hi = analog_in_bytes[2 * channel] & 0x03;
379 unsigned char lo = analog_in_bytes[2 * channel + 1];
380 analog_in_data[channel] = hi * 256 + lo;
382 // printf("%02x %02x ", hi, lo );
383 // printf("%04d ", value );
386 // Process analog inputs
387 if ( analog_in_node != NULL ) {
388 for ( int i = 0; i < analog_in_node->nChildren(); ++i ) {
389 // read the next config entry from the property tree
391 SGPropertyNode *child = analog_in_node->getChild(i);
392 string cname = child->getName();
393 int index = child->getIndex();
397 vector <SGPropertyNode *> output_nodes; output_nodes.clear();
402 if ( cname == "channel" ) {
403 SGPropertyNode *prop;
404 prop = child->getChild( "name" );
405 if ( prop != NULL ) {
406 name = prop->getStringValue();
408 prop = child->getChild( "type", 0 );
409 if ( prop != NULL ) {
410 type = prop->getStringValue();
412 prop = child->getChild( "type", 1 );
413 if ( prop != NULL ) {
414 subtype = prop->getStringValue();
417 while ( (prop = child->getChild("prop", j)) != NULL ) {
419 = fgGetNode( prop->getStringValue(), true );
420 output_nodes.push_back( tmp );
423 prop = child->getChild( "center" );
424 if ( prop != NULL ) {
425 center = prop->getIntValue();
427 prop = child->getChild( "min" );
428 if ( prop != NULL ) {
429 min = prop->getIntValue();
431 prop = child->getChild( "max" );
432 if ( prop != NULL ) {
433 max = prop->getIntValue();
435 prop = child->getChild( "factor" );
436 if ( prop != NULL ) {
437 factor = prop->getFloatValue();
440 // Fetch the raw value
442 int raw_value = analog_in_data[index];
444 // Update the target properties
446 if ( type == "flight"
447 && !ignore_flight_controls->getBoolValue() )
449 if ( subtype != "pedals" ||
450 ( subtype == "pedals"
451 && !ignore_pedal_controls->getBoolValue() ) )
453 // "Cook" the raw value
454 float scaled_value = 0.0f;
456 scaled_value = scale( center, min, max, raw_value );
458 scaled_value = scale( min, max, raw_value );
460 scaled_value *= factor;
462 // update the property tree values
463 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
464 output_nodes[j]->setDoubleValue( scaled_value );
467 } else if ( type == "avionics-simple" ) {
468 // "Cook" the raw value
469 float scaled_value = 0.0f;
471 scaled_value = scale( center, min, max, raw_value );
473 scaled_value = scale( min, max, raw_value );
475 scaled_value *= factor;
477 // update the property tree values
478 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
479 output_nodes[j]->setDoubleValue( scaled_value );
481 } else if ( type == "avionics-resolver" ) {
482 // this type of analog input impliments a
483 // rotational knob. We first caclulate the amount
484 // of knob rotation (slightly complex to work with
485 // hardware specific goofiness) and then multiply
486 // that amount of movement by a scaling factor,
487 // and finally add the result to the original
490 bool do_init = false;
491 float scaled_value = 0.0f;
493 // fetch intermediate values from property tree
495 prop = child->getChild( "is-inited", 0 );
496 if ( prop == NULL ) {
498 prop = child->getChild( "is-inited", 0, true );
499 prop->setBoolValue( true );
503 for ( j = 0; j < 3; ++j ) {
504 prop = child->getChild( "raw", j, true );
506 raw[j] = analog_in_data[index];
508 raw[j] = prop->getIntValue();
512 // do Tony's magic to calculate knob movement
513 // based on current analog input position and
515 int diff = tony_magic( analog_in_data[index], raw );
517 // write raw intermediate values (updated by
518 // tony_magic()) back to property tree
519 for ( j = 0; j < 3; ++j ) {
520 prop = child->getChild( "raw", j, true );
521 prop->setIntValue( raw[j] );
524 // filter knob position
525 prop = child->getChild( "diff-average", 0, true );
526 double diff_ave = prop->getDoubleValue();
527 diff_ave = instr_pot_filter( diff_ave, diff );
528 prop->setDoubleValue( diff_ave );
530 // calculate value adjustment in real world units
531 scaled_value = diff_ave * factor;
533 // update the property tree values
534 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
535 float value = output_nodes[j]->getDoubleValue();
536 value += scaled_value;
538 prop = child->getChild( "min-clamp" );
539 if ( prop != NULL ) {
540 double min = prop->getDoubleValue();
541 if ( value < min ) { value = min; }
544 prop = child->getChild( "max-clamp" );
545 if ( prop != NULL ) {
546 double max = prop->getDoubleValue();
547 if ( value > max ) { value = max; }
550 prop = child->getChild( "compass-heading" );
551 if ( prop != NULL ) {
552 bool compass = prop->getBoolValue();
554 while ( value >= 360.0 ) { value -= 360.0; }
555 while ( value < 0.0 ) { value += 360.0; }
559 output_nodes[j]->setDoubleValue( value );
563 SG_LOG( SG_IO, SG_DEBUG, "Invalid channel type = "
567 SG_LOG( SG_IO, SG_DEBUG,
568 "Input config error, expecting 'channel' but found "
578 /////////////////////////////////////////////////////////////////////
579 // Read the switch positions
580 /////////////////////////////////////////////////////////////////////
582 // decode the packed switch data
583 static void update_switch_matrix(
585 unsigned char switch_data[ATC_SWITCH_BYTES],
586 int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES] )
588 for ( int row = 0; row < ATC_SWITCH_BYTES; ++row ) {
589 unsigned char switches = switch_data[row];
591 for( int column = 0; column < ATC_NUM_COLS; ++column ) {
592 switch_matrix[board][column][row] = switches & 1;
593 switches = switches >> 1;
598 bool FGATCInput::do_switches() {
600 ATCReadSwitches( switches_fd, switch_data );
602 // unpack the switch data
603 int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES];
604 update_switch_matrix( board, switch_data, switch_matrix );
606 // Process the switch inputs
607 if ( switches_node != NULL ) {
608 for ( int i = 0; i < switches_node->nChildren(); ++i ) {
609 // read the next config entry from the property tree
611 SGPropertyNode *child = switches_node->getChild(i);
612 string cname = child->getName();
615 vector <SGPropertyNode *> output_nodes; output_nodes.clear();
620 float scaled_value = 0.0f;
622 // get common options
624 SGPropertyNode *prop;
625 prop = child->getChild( "name" );
626 if ( prop != NULL ) {
627 name = prop->getStringValue();
629 prop = child->getChild( "type" );
630 if ( prop != NULL ) {
631 type = prop->getStringValue();
634 while ( (prop = child->getChild("prop", j)) != NULL ) {
636 = fgGetNode( prop->getStringValue(), true );
637 output_nodes.push_back( tmp );
640 prop = child->getChild( "factor" );
641 if ( prop != NULL ) {
642 factor = prop->getFloatValue();
644 prop = child->getChild( "steady-state-filter" );
645 if ( prop != NULL ) {
646 filter = prop->getIntValue();
649 // handle different types of switches
651 if ( cname == "switch" ) {
652 prop = child->getChild( "row" );
653 if ( prop != NULL ) {
654 row = prop->getIntValue();
656 prop = child->getChild( "col" );
657 if ( prop != NULL ) {
658 col = prop->getIntValue();
661 // Fetch the raw value
662 int raw_value = switch_matrix[board][row][col];
665 scaled_value = (float)raw_value * factor;
667 } else if ( cname == "combo-switch" ) {
668 float combo_value = 0.0f;
672 while ( (pos = child->getChild("position", k++)) != NULL ) {
673 // read the combo position entries from the property tree
675 prop = pos->getChild( "row" );
676 if ( prop != NULL ) {
677 row = prop->getIntValue();
679 prop = pos->getChild( "col" );
680 if ( prop != NULL ) {
681 col = prop->getIntValue();
683 prop = pos->getChild( "value" );
684 if ( prop != NULL ) {
685 combo_value = prop->getFloatValue();
688 // Fetch the raw value
689 int raw_value = switch_matrix[board][row][col];
690 // cout << "sm[" << board << "][" << row << "][" << col
691 // << "] = " << raw_value << endl;
694 // set scaled_value to the first combo_value
695 // that matches and jump out of loop.
696 scaled_value = combo_value;
702 scaled_value *= factor;
705 // handle filter request. The value of the switch must be
706 // steady-state for "n" frames before the property value
709 bool update_prop = true;
712 SGPropertyNode *fv = child->getChild( "filter-value", 0, true );
713 float filter_value = fv->getFloatValue();
714 SGPropertyNode *fc = child->getChild( "filter-count", 0, true );
715 int filter_count = fc->getIntValue();
717 if ( fabs(scaled_value - filter_value) < 0.0001 ) {
723 if ( filter_count < filter ) {
727 fv->setFloatValue( scaled_value );
728 fc->setIntValue( filter_count );
732 if ( type == "engine" || type == "flight" ) {
733 if ( ! ignore_flight_controls->getBoolValue() ) {
734 // update the property tree values
735 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
736 output_nodes[j]->setDoubleValue( scaled_value );
739 } else if ( type == "avionics" ) {
740 // update the property tree values
741 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
742 output_nodes[j]->setDoubleValue( scaled_value );
753 /////////////////////////////////////////////////////////////////////
754 // Read radio switches
755 /////////////////////////////////////////////////////////////////////
757 bool FGATCInput::do_radio_switches() {
759 ATCReadRadios( radios_fd, radio_switch_data );
761 // Process the radio switch/knob inputs
762 if ( radio_in_node != NULL ) {
763 for ( int i = 0; i < radio_in_node->nChildren(); ++i ) {
764 // read the next config entry from the property tree
766 SGPropertyNode *child = radio_in_node->getChild(i);
767 string cname = child->getName();
769 if ( cname == "switch" ) {
772 vector <SGPropertyNode *> output_nodes; output_nodes.clear();
779 int scaled_value = 0;
780 // get common options
782 SGPropertyNode *prop;
783 prop = child->getChild( "name" );
784 if ( prop != NULL ) {
785 name = prop->getStringValue();
787 prop = child->getChild( "type" );
788 if ( prop != NULL ) {
789 type = prop->getStringValue();
792 while ( (prop = child->getChild("prop", j)) != NULL ) {
794 = fgGetNode( prop->getStringValue(), true );
795 output_nodes.push_back( tmp );
798 prop = child->getChild( "byte" );
799 if ( prop != NULL ) {
800 byte_num = prop->getIntValue();
802 prop = child->getChild( "right-shift" );
803 if ( prop != NULL ) {
804 right_shift = prop->getIntValue();
806 prop = child->getChild( "mask" );
807 if ( prop != NULL ) {
808 mask = prop->getIntValue();
810 prop = child->getChild( "factor" );
811 if ( prop != NULL ) {
812 factor = prop->getIntValue();
814 prop = child->getChild( "offset" );
815 if ( prop != NULL ) {
816 offset = prop->getIntValue();
818 prop = child->getChild( "invert" );
819 if ( prop != NULL ) {
820 invert = prop->getBoolValue();
823 // Fetch the raw value
825 = (radio_switch_data[byte_num] >> right_shift) & mask;
829 raw_value = !raw_value;
831 scaled_value = raw_value * factor + offset;
833 // update the property tree values
834 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
835 output_nodes[j]->setIntValue( scaled_value );
845 // process the hardware inputs. This code assumes the calling layer
846 // will lock the hardware.
847 bool FGATCInput::process() {
849 SG_LOG( SG_IO, SG_ALERT, "This board has not been opened for input! "
862 bool FGATCInput::close() {
864 #if defined( unix ) || defined( __CYGWIN__ )
868 result = ::close( lock_fd );
869 if ( result == -1 ) {
870 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
872 snprintf( msg, 256, "Error closing %s", lock_file );
877 result = ::close( analog_in_fd );
878 if ( result == -1 ) {
879 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
881 snprintf( msg, 256, "Error closing %s", analog_in_file );
886 result = ::close( radios_fd );
887 if ( result == -1 ) {
888 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
890 snprintf( msg, 256, "Error closing %s", radios_file );
895 result = ::close( switches_fd );
896 if ( result == -1 ) {
897 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
899 snprintf( msg, 256, "Error closing %s", switches_file );