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>
41 #include <simgear/debug/logstream.hxx>
42 #include <simgear/misc/sg_path.hxx>
44 #include <Main/fg_props.hxx>
46 #include "ATC-Inputs.hxx"
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 ) :
57 ignore_flight_controls(NULL),
58 ignore_pedal_controls(NULL),
64 config = _config_file;
69 static void ATCReadAnalogInputs( int fd, unsigned char *analog_in_bytes ) {
70 #if defined( unix ) || defined( __CYGWIN__ )
72 lseek( fd, 0, SEEK_SET );
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" );
83 // Read status of radio switches and knobs
84 static void ATCReadRadios( int fd, unsigned char *switch_data ) {
85 #if defined( unix ) || defined( __CYGWIN__ )
87 lseek( fd, 0, SEEK_SET );
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" );
99 static void ATCReadSwitches( int fd, unsigned char *switch_bytes ) {
100 #if defined( unix ) || defined( __CYGWIN__ )
102 lseek( fd, 0, SEEK_SET );
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" );
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
118 char *envp = ::getenv( "HOME" );
119 if ( envp != NULL ) {
121 tmp.append( ".atcflightsim" );
122 tmp.append( config.str() );
126 readProperties( config.str(), globals->get_props() );
131 // Open and initialize the ATC hardware
132 bool FGATCInput::open() {
134 SG_LOG( SG_IO, SG_ALERT, "This board is already open for input! "
139 // This loads the config parameters generated by "simcal"
142 SG_LOG( SG_IO, SG_ALERT,
143 "Initializing ATC input hardware, please wait ..." );
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 );
152 #if defined( unix ) || defined( __CYGWIN__ )
154 /////////////////////////////////////////////////////////////////////
155 // Open the /proc files
156 /////////////////////////////////////////////////////////////////////
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 );
162 snprintf( msg, 256, "Error opening %s", analog_in_file );
167 radios_fd = ::open( radios_file, O_RDWR );
168 if ( radios_fd == -1 ) {
169 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
171 snprintf( msg, 256, "Error opening %s", radios_file );
176 switches_fd = ::open( switches_file, O_RDONLY );
177 if ( switches_fd == -1 ) {
178 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
180 snprintf( msg, 256, "Error opening %s", switches_file );
187 /////////////////////////////////////////////////////////////////////
188 // Finished initing hardware
189 /////////////////////////////////////////////////////////////////////
191 SG_LOG( SG_IO, SG_ALERT,
192 "Done initializing ATC input hardware." );
196 /////////////////////////////////////////////////////////////////////
197 // Connect up to property values
198 /////////////////////////////////////////////////////////////////////
200 ignore_flight_controls
201 = fgGetNode( "/input/atcsim/ignore-flight-controls", true );
202 ignore_pedal_controls
203 = fgGetNode( "/input/atcsim/ignore-pedal-controls", true );
207 snprintf( base_name, 256, "/input/atc-board[%d]/analog-in", board );
208 analog_in_node = fgGetNode( base_name );
210 snprintf( base_name, 256, "/input/atc-board[%d]/radio-switches", board );
211 radio_in_node = fgGetNode( base_name );
213 snprintf( base_name, 256, "/input/atc-board[%d]/switches", board );
214 switches_node = fgGetNode( base_name );
220 /////////////////////////////////////////////////////////////////////
221 // Read analog inputs
222 /////////////////////////////////////////////////////////////////////
224 // scale a number between min and max (with center defined) to a scale
226 static double scale( int center, int deadband, int min, int max, int value ) {
227 // cout << center << " " << min << " " << max << " " << value << " ";
231 if ( value <= (center - deadband) ) {
232 range = (center - deadband) - min;
233 result = (value - (center - deadband)) / range;
235 range = max - (center + deadband);
236 result = (value - (center + deadband)) / range;
239 if ( result < -1.0 ) result = -1.0;
240 if ( result > 1.0 ) result = 1.0;
242 // cout << result << endl;
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 << " ";
255 result = (value - min) / range;
257 if ( result < 0.0 ) result = 0.0;
258 if ( result > 1.0 ) result = 1.0;
260 // cout << result << endl;
266 static int tony_magic( int raw, int obs[3] ) {
272 if ( obs[2] >= 68 && obs[2] < 480 ) {
274 } else if ( obs[2] >= 480 ) {
279 } else if ( obs[1] < 68 ) {
282 } else if ( obs[2] < 30 ) {
283 if ( obs[1] >= 68 && obs[1] < 480 ) {
287 } else if ( obs[1] >= 480 ) {
289 if ( obs[0] < obs[1] ) {
297 } else if ( obs[1] > 980 ) {
298 if ( obs[2] <= 956 && obs[2] > 480 ) {
300 } else if ( obs[2] <= 480 ) {
305 } else if ( obs[1] > 956 ) {
308 } else if ( obs[2] > 980 ) {
309 if ( obs[1] <= 956 && obs[1] > 480 ) {
313 } else if ( obs[1] <= 480 ) {
315 if ( obs[0] > obs[1] ) {
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
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
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
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
349 result = obs[1] - obs[2];
350 if ( abs(result) > 400 ) {
358 // cout << " result = " << result << endl;
359 if ( result < -500 ) { result += 1024; }
360 if ( result > 500 ) { result -= 1024; }
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;
375 bool FGATCInput::do_analog_in() {
376 // Read raw data in byte form
377 ATCReadAnalogInputs( analog_in_fd, analog_in_bytes );
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;
385 // printf("%02x %02x ", hi, lo );
386 // printf("%04d ", value );
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
394 SGPropertyNode *child = analog_in_node->getChild(i);
395 string cname = child->getName();
396 int index = child->getIndex();
400 vector <SGPropertyNode *> output_nodes; output_nodes.clear();
406 if ( cname == "channel" ) {
407 SGPropertyNode *prop;
408 prop = child->getChild( "name" );
409 if ( prop != NULL ) {
410 name = prop->getStringValue();
412 prop = child->getChild( "type", 0 );
413 if ( prop != NULL ) {
414 type = prop->getStringValue();
416 prop = child->getChild( "type", 1 );
417 if ( prop != NULL ) {
418 subtype = prop->getStringValue();
421 while ( (prop = child->getChild("prop", j)) != NULL ) {
423 = fgGetNode( prop->getStringValue(), true );
424 output_nodes.push_back( tmp );
427 prop = child->getChild( "center" );
428 if ( prop != NULL ) {
429 center = prop->getIntValue();
431 prop = child->getChild( "min" );
432 if ( prop != NULL ) {
433 min = prop->getIntValue();
435 prop = child->getChild( "max" );
436 if ( prop != NULL ) {
437 max = prop->getIntValue();
439 prop = child->getChild( "deadband" );
440 if ( prop != NULL ) {
441 deadband = prop->getIntValue();
443 prop = child->getChild( "factor" );
444 if ( prop != NULL ) {
445 factor = prop->getFloatValue();
448 // Fetch the raw value
450 int raw_value = analog_in_data[index];
452 // Update the target properties
454 if ( type == "flight"
455 && !ignore_flight_controls->getBoolValue() )
457 if ( subtype != "pedals" ||
458 ( subtype == "pedals"
459 && !ignore_pedal_controls->getBoolValue() ) )
461 // "Cook" the raw value
462 float scaled_value = 0.0f;
464 scaled_value = scale( center, deadband,
465 min, max, raw_value );
467 scaled_value = scale( min, max, raw_value );
469 scaled_value *= factor;
471 // update the property tree values
472 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
473 output_nodes[j]->setDoubleValue( scaled_value );
476 } else if ( type == "avionics-simple" ) {
477 // "Cook" the raw value
478 float scaled_value = 0.0f;
480 scaled_value = scale( center, deadband,
481 min, max, raw_value );
483 scaled_value = scale( min, max, raw_value );
485 scaled_value *= factor;
487 // update the property tree values
488 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
489 output_nodes[j]->setDoubleValue( scaled_value );
491 } else if ( type == "avionics-resolver" ) {
492 // this type of analog input impliments a
493 // rotational knob. We first caclulate the amount
494 // of knob rotation (slightly complex to work with
495 // hardware specific goofiness) and then multiply
496 // that amount of movement by a scaling factor,
497 // and finally add the result to the original
500 bool do_init = false;
501 float scaled_value = 0.0f;
503 // fetch intermediate values from property tree
505 prop = child->getChild( "is-inited", 0 );
506 if ( prop == NULL ) {
508 prop = child->getChild( "is-inited", 0, true );
509 prop->setBoolValue( true );
513 for ( j = 0; j < 3; ++j ) {
514 prop = child->getChild( "raw", j, true );
516 raw[j] = analog_in_data[index];
518 raw[j] = prop->getIntValue();
522 // do Tony's magic to calculate knob movement
523 // based on current analog input position and
525 int diff = tony_magic( analog_in_data[index], raw );
527 // write raw intermediate values (updated by
528 // tony_magic()) back to property tree
529 for ( j = 0; j < 3; ++j ) {
530 prop = child->getChild( "raw", j, true );
531 prop->setIntValue( raw[j] );
534 // filter knob position
535 prop = child->getChild( "diff-average", 0, true );
536 double diff_ave = prop->getDoubleValue();
537 diff_ave = instr_pot_filter( diff_ave, diff );
538 prop->setDoubleValue( diff_ave );
540 // calculate value adjustment in real world units
541 scaled_value = diff_ave * factor;
543 // update the property tree values
544 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
545 float value = output_nodes[j]->getDoubleValue();
546 value += scaled_value;
548 prop = child->getChild( "min-clamp" );
549 if ( prop != NULL ) {
550 double min = prop->getDoubleValue();
551 if ( value < min ) { value = min; }
554 prop = child->getChild( "max-clamp" );
555 if ( prop != NULL ) {
556 double max = prop->getDoubleValue();
557 if ( value > max ) { value = max; }
560 prop = child->getChild( "compass-heading" );
561 if ( prop != NULL ) {
562 bool compass = prop->getBoolValue();
564 while ( value >= 360.0 ) { value -= 360.0; }
565 while ( value < 0.0 ) { value += 360.0; }
569 output_nodes[j]->setDoubleValue( value );
573 SG_LOG( SG_IO, SG_DEBUG, "Invalid channel type = "
577 SG_LOG( SG_IO, SG_DEBUG,
578 "Input config error, expecting 'channel' but found "
588 /////////////////////////////////////////////////////////////////////
589 // Read the switch positions
590 /////////////////////////////////////////////////////////////////////
592 // decode the packed switch data
593 static void update_switch_matrix(
595 unsigned char switch_data[ATC_SWITCH_BYTES],
596 int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES] )
598 for ( int row = 0; row < ATC_SWITCH_BYTES; ++row ) {
599 unsigned char switches = switch_data[row];
601 for( int column = 0; column < ATC_NUM_COLS; ++column ) {
602 switch_matrix[board][column][row] = switches & 1;
603 switches = switches >> 1;
608 bool FGATCInput::do_switches() {
610 ATCReadSwitches( switches_fd, switch_data );
612 // unpack the switch data
613 int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES];
614 update_switch_matrix( board, switch_data, switch_matrix );
616 // Process the switch inputs
617 if ( switches_node != NULL ) {
618 for ( int i = 0; i < switches_node->nChildren(); ++i ) {
619 // read the next config entry from the property tree
621 SGPropertyNode *child = switches_node->getChild(i);
622 string cname = child->getName();
625 vector <SGPropertyNode *> output_nodes; output_nodes.clear();
630 float scaled_value = 0.0f;
633 // get common options
635 SGPropertyNode *prop;
636 prop = child->getChild( "name" );
637 if ( prop != NULL ) {
638 name = prop->getStringValue();
640 prop = child->getChild( "type" );
641 if ( prop != NULL ) {
642 type = prop->getStringValue();
645 while ( (prop = child->getChild("prop", j)) != NULL ) {
647 = fgGetNode( prop->getStringValue(), true );
648 output_nodes.push_back( tmp );
651 prop = child->getChild( "factor" );
652 if ( prop != NULL ) {
653 factor = prop->getFloatValue();
655 prop = child->getChild( "invert" );
656 if ( prop != NULL ) {
657 invert = prop->getBoolValue();
659 prop = child->getChild( "steady-state-filter" );
660 if ( prop != NULL ) {
661 filter = prop->getIntValue();
664 // handle different types of switches
666 if ( cname == "switch" ) {
667 prop = child->getChild( "row" );
668 if ( prop != NULL ) {
669 row = prop->getIntValue();
671 prop = child->getChild( "col" );
672 if ( prop != NULL ) {
673 col = prop->getIntValue();
676 // Fetch the raw value
677 int raw_value = switch_matrix[board][row][col];
681 raw_value = !raw_value;
685 scaled_value = (float)raw_value * factor;
687 } else if ( cname == "combo-switch" ) {
688 float combo_value = 0.0f;
692 while ( (pos = child->getChild("position", k++)) != NULL ) {
693 // read the combo position entries from the property tree
695 prop = pos->getChild( "row" );
696 if ( prop != NULL ) {
697 row = prop->getIntValue();
699 prop = pos->getChild( "col" );
700 if ( prop != NULL ) {
701 col = prop->getIntValue();
703 prop = pos->getChild( "value" );
704 if ( prop != NULL ) {
705 combo_value = prop->getFloatValue();
708 // Fetch the raw value
709 int raw_value = switch_matrix[board][row][col];
710 // cout << "sm[" << board << "][" << row << "][" << col
711 // << "] = " << raw_value << endl;
714 // set scaled_value to the first combo_value
715 // that matches and jump out of loop.
716 scaled_value = combo_value;
722 scaled_value *= factor;
723 } else if ( cname == "additive-switch" ) {
724 float additive_value = 0.0f;
725 float increment = 0.0f;
729 while ( (pos = child->getChild("position", k++)) != NULL ) {
730 // read the combo position entries from the property tree
732 prop = pos->getChild( "row" );
733 if ( prop != NULL ) {
734 row = prop->getIntValue();
736 prop = pos->getChild( "col" );
737 if ( prop != NULL ) {
738 col = prop->getIntValue();
740 prop = pos->getChild( "value" );
741 if ( prop != NULL ) {
742 increment = prop->getFloatValue();
745 // Fetch the raw value
746 int raw_value = switch_matrix[board][row][col];
747 // cout << "sm[" << board << "][" << row << "][" << col
748 // << "] = " << raw_value << endl;
751 // set scaled_value to the first combo_value
752 // that matches and jump out of loop.
753 additive_value += increment;
758 scaled_value = additive_value * factor;
761 // handle filter request. The value of the switch must be
762 // steady-state for "n" frames before the property value
765 bool update_prop = true;
768 SGPropertyNode *fv = child->getChild( "filter-value", 0, true );
769 float filter_value = fv->getFloatValue();
770 SGPropertyNode *fc = child->getChild( "filter-count", 0, true );
771 int filter_count = fc->getIntValue();
773 if ( fabs(scaled_value - filter_value) < 0.0001 ) {
779 if ( filter_count < filter ) {
783 fv->setFloatValue( scaled_value );
784 fc->setIntValue( filter_count );
788 if ( type == "engine" || type == "flight" ) {
789 if ( ! ignore_flight_controls->getBoolValue() ) {
790 // update the property tree values
791 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
792 output_nodes[j]->setDoubleValue( scaled_value );
795 } else if ( type == "avionics" ) {
796 // update the property tree values
797 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
798 output_nodes[j]->setDoubleValue( scaled_value );
809 /////////////////////////////////////////////////////////////////////
810 // Read radio switches
811 /////////////////////////////////////////////////////////////////////
813 bool FGATCInput::do_radio_switches() {
815 ATCReadRadios( radios_fd, radio_switch_data );
817 // Process the radio switch/knob inputs
818 if ( radio_in_node != NULL ) {
819 for ( int i = 0; i < radio_in_node->nChildren(); ++i ) {
820 // read the next config entry from the property tree
822 SGPropertyNode *child = radio_in_node->getChild(i);
823 string cname = child->getName();
825 if ( cname == "switch" ) {
828 vector <SGPropertyNode *> output_nodes; output_nodes.clear();
835 int scaled_value = 0;
836 // get common options
838 SGPropertyNode *prop;
839 prop = child->getChild( "name" );
840 if ( prop != NULL ) {
841 name = prop->getStringValue();
843 prop = child->getChild( "type" );
844 if ( prop != NULL ) {
845 type = prop->getStringValue();
848 while ( (prop = child->getChild("prop", j)) != NULL ) {
850 = fgGetNode( prop->getStringValue(), true );
851 output_nodes.push_back( tmp );
854 prop = child->getChild( "byte" );
855 if ( prop != NULL ) {
856 byte_num = prop->getIntValue();
858 prop = child->getChild( "right-shift" );
859 if ( prop != NULL ) {
860 right_shift = prop->getIntValue();
862 prop = child->getChild( "mask" );
863 if ( prop != NULL ) {
864 mask = prop->getIntValue();
866 prop = child->getChild( "factor" );
867 if ( prop != NULL ) {
868 factor = prop->getIntValue();
870 prop = child->getChild( "offset" );
871 if ( prop != NULL ) {
872 offset = prop->getIntValue();
874 prop = child->getChild( "invert" );
875 if ( prop != NULL ) {
876 invert = prop->getBoolValue();
879 // Fetch the raw value
881 = (radio_switch_data[byte_num] >> right_shift) & mask;
885 raw_value = !raw_value;
887 scaled_value = raw_value * factor + offset;
889 // update the property tree values
890 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
891 output_nodes[j]->setIntValue( scaled_value );
901 // process the hardware inputs. This code assumes the calling layer
902 // will lock the hardware.
903 bool FGATCInput::process() {
905 SG_LOG( SG_IO, SG_ALERT, "This board has not been opened for input! "
918 bool FGATCInput::close() {
920 #if defined( unix ) || defined( __CYGWIN__ )
928 result = ::close( analog_in_fd );
929 if ( result == -1 ) {
930 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
932 snprintf( msg, 256, "Error closing %s", analog_in_file );
937 result = ::close( radios_fd );
938 if ( result == -1 ) {
939 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
941 snprintf( msg, 256, "Error closing %s", radios_file );
946 result = ::close( switches_fd );
947 if ( result == -1 ) {
948 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
950 snprintf( msg, 256, "Error closing %s", switches_file );