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 ) {
66 #if defined( unix ) || defined( __CYGWIN__ )
68 lseek( fd, 0, SEEK_SET );
70 int result = read( fd, analog_in_bytes, ATC_ANAL_IN_BYTES );
71 if ( result != ATC_ANAL_IN_BYTES ) {
72 SG_LOG( SG_IO, SG_ALERT, "Read failed" );
79 // Read status of radio switches and knobs
80 static void ATCReadRadios( int fd, unsigned char *switch_data ) {
81 #if defined( unix ) || defined( __CYGWIN__ )
83 lseek( fd, 0, SEEK_SET );
85 int result = read( fd, switch_data, ATC_RADIO_SWITCH_BYTES );
86 if ( result != ATC_RADIO_SWITCH_BYTES ) {
87 SG_LOG( SG_IO, SG_ALERT, "Read failed" );
95 static void ATCReadSwitches( int fd, unsigned char *switch_bytes ) {
96 #if defined( unix ) || defined( __CYGWIN__ )
98 lseek( fd, 0, SEEK_SET );
100 int result = read( fd, switch_bytes, ATC_SWITCH_BYTES );
101 if ( result != ATC_SWITCH_BYTES ) {
102 SG_LOG( SG_IO, SG_ALERT, "Read failed" );
109 void FGATCInput::init_config() {
110 #if defined( unix ) || defined( __CYGWIN__ )
111 if ( config.str()[0] != '/' ) {
112 // not an absolute path, prepend the standard location
114 char *envp = ::getenv( "HOME" );
115 if ( envp != NULL ) {
117 tmp.append( ".atcflightsim" );
118 tmp.append( config.str() );
122 readProperties( config.str(), globals->get_props() );
127 // Open and initialize the ATC hardware
128 bool FGATCInput::open() {
130 SG_LOG( SG_IO, SG_ALERT, "This board is already open for input! "
135 // This loads the config parameters generated by "simcal"
138 SG_LOG( SG_IO, SG_ALERT,
139 "Initializing ATC input hardware, please wait ..." );
141 snprintf( analog_in_file, 256,
142 "/proc/atcflightsim/board%d/analog_in", board );
143 snprintf( radios_file, 256,
144 "/proc/atcflightsim/board%d/radios", board );
145 snprintf( switches_file, 256,
146 "/proc/atcflightsim/board%d/switches", board );
148 #if defined( unix ) || defined( __CYGWIN__ )
150 /////////////////////////////////////////////////////////////////////
151 // Open the /proc files
152 /////////////////////////////////////////////////////////////////////
154 analog_in_fd = ::open( analog_in_file, O_RDONLY );
155 if ( analog_in_fd == -1 ) {
156 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
158 snprintf( msg, 256, "Error opening %s", analog_in_file );
163 radios_fd = ::open( radios_file, O_RDWR );
164 if ( radios_fd == -1 ) {
165 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
167 snprintf( msg, 256, "Error opening %s", radios_file );
172 switches_fd = ::open( switches_file, O_RDONLY );
173 if ( switches_fd == -1 ) {
174 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
176 snprintf( msg, 256, "Error opening %s", switches_file );
183 /////////////////////////////////////////////////////////////////////
184 // Finished initing hardware
185 /////////////////////////////////////////////////////////////////////
187 SG_LOG( SG_IO, SG_ALERT,
188 "Done initializing ATC input hardware." );
192 /////////////////////////////////////////////////////////////////////
193 // Connect up to property values
194 /////////////////////////////////////////////////////////////////////
196 ignore_flight_controls
197 = fgGetNode( "/input/atcsim/ignore-flight-controls", true );
198 ignore_pedal_controls
199 = fgGetNode( "/input/atcsim/ignore-pedal-controls", true );
203 snprintf( base_name, 256, "/input/atc-board[%d]/analog-in", board );
204 analog_in_node = fgGetNode( base_name );
206 snprintf( base_name, 256, "/input/atc-board[%d]/radio-switches", board );
207 radio_in_node = fgGetNode( base_name );
209 snprintf( base_name, 256, "/input/atc-board[%d]/switches", board );
210 switches_node = fgGetNode( base_name );
216 /////////////////////////////////////////////////////////////////////
217 // Read analog inputs
218 /////////////////////////////////////////////////////////////////////
220 // scale a number between min and max (with center defined) to a scale
222 static double scale( int center, int min, int max, int value ) {
223 // cout << center << " " << min << " " << max << " " << value << " ";
227 if ( value <= center ) {
228 range = center - min;
229 result = (value - center) / range;
231 range = max - center;
232 result = (value - center) / range;
235 if ( result < -1.0 ) result = -1.0;
236 if ( result > 1.0 ) result = 1.0;
238 // cout << result << endl;
244 // scale a number between min and max to a scale from 0.0 to 1.0
245 static double scale( int min, int max, int value ) {
246 // cout << center << " " << min << " " << max << " " << value << " ";
251 result = (value - min) / range;
253 if ( result < 0.0 ) result = 0.0;
254 if ( result > 1.0 ) result = 1.0;
256 // cout << result << endl;
262 static int tony_magic( int raw, int obs[3] ) {
268 if ( obs[2] >= 68 && obs[2] < 480 ) {
270 } else if ( obs[2] >= 480 ) {
275 } else if ( obs[1] < 68 ) {
278 } else if ( obs[2] < 30 ) {
279 if ( obs[1] >= 68 && obs[1] < 480 ) {
283 } else if ( obs[1] >= 480 ) {
285 if ( obs[0] < obs[1] ) {
293 } else if ( obs[1] > 980 ) {
294 if ( obs[2] <= 956 && obs[2] > 480 ) {
296 } else if ( obs[2] <= 480 ) {
301 } else if ( obs[1] > 956 ) {
304 } else if ( obs[2] > 980 ) {
305 if ( obs[1] <= 956 && obs[1] > 480 ) {
309 } else if ( obs[1] <= 480 ) {
311 if ( obs[0] > obs[1] ) {
320 if ( obs[1] < 480 && obs[2] > 480 ) {
321 // crossed gap going up
322 if ( obs[0] < obs[1] ) {
323 // caught a bogus intermediate value coming out of the gap
326 } else if ( obs[1] > 480 && obs[2] < 480 ) {
327 // crossed gap going down
328 if ( obs[0] > obs[1] ) {
329 // caught a bogus intermediate value coming out of the gap
332 } else if ( obs[0] > 480 && obs[1] < 480 && obs[2] < 480 ) {
333 // crossed the gap going down
334 if ( obs[1] > obs[2] ) {
335 // caught a bogus intermediate value coming out of the gap
338 } else if ( obs[0] < 480 && obs[1] > 480 && obs[2] > 480 ) {
339 // crossed the gap going up
340 if ( obs[1] < obs[2] ) {
341 // caught a bogus intermediate value coming out of the gap
345 result = obs[1] - obs[2];
346 if ( abs(result) > 400 ) {
354 // cout << " result = " << result << endl;
355 if ( result < -500 ) { result += 1024; }
356 if ( result > 500 ) { result -= 1024; }
362 static double instr_pot_filter( double ave, double val ) {
363 if ( fabs(ave - val) < 400 || fabs(val) < fabs(ave) ) {
364 return 0.5 * ave + 0.5 * val;
371 bool FGATCInput::do_analog_in() {
372 // Read raw data in byte form
373 ATCReadAnalogInputs( analog_in_fd, analog_in_bytes );
375 // Convert to integer values
376 for ( int channel = 0; channel < ATC_ANAL_IN_VALUES; ++channel ) {
377 unsigned char hi = analog_in_bytes[2 * channel] & 0x03;
378 unsigned char lo = analog_in_bytes[2 * channel + 1];
379 analog_in_data[channel] = hi * 256 + lo;
381 // printf("%02x %02x ", hi, lo );
382 // printf("%04d ", value );
385 // Process analog inputs
386 if ( analog_in_node != NULL ) {
387 for ( int i = 0; i < analog_in_node->nChildren(); ++i ) {
388 // read the next config entry from the property tree
390 SGPropertyNode *child = analog_in_node->getChild(i);
391 string cname = child->getName();
392 int index = child->getIndex();
396 vector <SGPropertyNode *> output_nodes; output_nodes.clear();
401 if ( cname == "channel" ) {
402 SGPropertyNode *prop;
403 prop = child->getChild( "name" );
404 if ( prop != NULL ) {
405 name = prop->getStringValue();
407 prop = child->getChild( "type", 0 );
408 if ( prop != NULL ) {
409 type = prop->getStringValue();
411 prop = child->getChild( "type", 1 );
412 if ( prop != NULL ) {
413 subtype = prop->getStringValue();
416 while ( (prop = child->getChild("prop", j)) != NULL ) {
418 = fgGetNode( prop->getStringValue(), true );
419 output_nodes.push_back( tmp );
422 prop = child->getChild( "center" );
423 if ( prop != NULL ) {
424 center = prop->getIntValue();
426 prop = child->getChild( "min" );
427 if ( prop != NULL ) {
428 min = prop->getIntValue();
430 prop = child->getChild( "max" );
431 if ( prop != NULL ) {
432 max = prop->getIntValue();
434 prop = child->getChild( "factor" );
435 if ( prop != NULL ) {
436 factor = prop->getFloatValue();
439 // Fetch the raw value
441 int raw_value = analog_in_data[index];
443 // Update the target properties
445 if ( type == "flight"
446 && !ignore_flight_controls->getBoolValue() )
448 if ( subtype != "pedals" ||
449 ( subtype == "pedals"
450 && !ignore_pedal_controls->getBoolValue() ) )
452 // "Cook" the raw value
453 float scaled_value = 0.0f;
455 scaled_value = scale( center, min, max, raw_value );
457 scaled_value = scale( min, max, raw_value );
459 scaled_value *= factor;
461 // update the property tree values
462 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
463 output_nodes[j]->setDoubleValue( scaled_value );
466 } else if ( type == "avionics-simple" ) {
467 // "Cook" the raw value
468 float scaled_value = 0.0f;
470 scaled_value = scale( center, min, max, raw_value );
472 scaled_value = scale( min, max, raw_value );
474 scaled_value *= factor;
476 // update the property tree values
477 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
478 output_nodes[j]->setDoubleValue( scaled_value );
480 } else if ( type == "avionics-resolver" ) {
481 // this type of analog input impliments a
482 // rotational knob. We first caclulate the amount
483 // of knob rotation (slightly complex to work with
484 // hardware specific goofiness) and then multiply
485 // that amount of movement by a scaling factor,
486 // and finally add the result to the original
489 bool do_init = false;
490 float scaled_value = 0.0f;
492 // fetch intermediate values from property tree
494 prop = child->getChild( "is-inited", 0 );
495 if ( prop == NULL ) {
497 prop = child->getChild( "is-inited", 0, true );
498 prop->setBoolValue( true );
502 for ( j = 0; j < 3; ++j ) {
503 prop = child->getChild( "raw", j, true );
505 raw[j] = analog_in_data[index];
507 raw[j] = prop->getIntValue();
511 // do Tony's magic to calculate knob movement
512 // based on current analog input position and
514 int diff = tony_magic( analog_in_data[index], raw );
516 // write raw intermediate values (updated by
517 // tony_magic()) back to property tree
518 for ( j = 0; j < 3; ++j ) {
519 prop = child->getChild( "raw", j, true );
520 prop->setIntValue( raw[j] );
523 // filter knob position
524 prop = child->getChild( "diff-average", 0, true );
525 double diff_ave = prop->getDoubleValue();
526 diff_ave = instr_pot_filter( diff_ave, diff );
527 prop->setDoubleValue( diff_ave );
529 // calculate value adjustment in real world units
530 scaled_value = diff_ave * factor;
532 // update the property tree values
533 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
534 float value = output_nodes[j]->getDoubleValue();
535 value += scaled_value;
537 prop = child->getChild( "min-clamp" );
538 if ( prop != NULL ) {
539 double min = prop->getDoubleValue();
540 if ( value < min ) { value = min; }
543 prop = child->getChild( "max-clamp" );
544 if ( prop != NULL ) {
545 double max = prop->getDoubleValue();
546 if ( value > max ) { value = max; }
549 prop = child->getChild( "compass-heading" );
550 if ( prop != NULL ) {
551 bool compass = prop->getBoolValue();
553 while ( value >= 360.0 ) { value -= 360.0; }
554 while ( value < 0.0 ) { value += 360.0; }
558 output_nodes[j]->setDoubleValue( value );
562 SG_LOG( SG_IO, SG_DEBUG, "Invalid channel type = "
566 SG_LOG( SG_IO, SG_DEBUG,
567 "Input config error, expecting 'channel' but found "
577 /////////////////////////////////////////////////////////////////////
578 // Read the switch positions
579 /////////////////////////////////////////////////////////////////////
581 // decode the packed switch data
582 static void update_switch_matrix(
584 unsigned char switch_data[ATC_SWITCH_BYTES],
585 int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES] )
587 for ( int row = 0; row < ATC_SWITCH_BYTES; ++row ) {
588 unsigned char switches = switch_data[row];
590 for( int column = 0; column < ATC_NUM_COLS; ++column ) {
591 switch_matrix[board][column][row] = switches & 1;
592 switches = switches >> 1;
597 bool FGATCInput::do_switches() {
599 ATCReadSwitches( switches_fd, switch_data );
601 // unpack the switch data
602 int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES];
603 update_switch_matrix( board, switch_data, switch_matrix );
605 // Process the switch inputs
606 if ( switches_node != NULL ) {
607 for ( int i = 0; i < switches_node->nChildren(); ++i ) {
608 // read the next config entry from the property tree
610 SGPropertyNode *child = switches_node->getChild(i);
611 string cname = child->getName();
614 vector <SGPropertyNode *> output_nodes; output_nodes.clear();
619 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( "invert" );
645 if ( prop != NULL ) {
646 invert = prop->getBoolValue();
648 prop = child->getChild( "steady-state-filter" );
649 if ( prop != NULL ) {
650 filter = prop->getIntValue();
653 // handle different types of switches
655 if ( cname == "switch" ) {
656 prop = child->getChild( "row" );
657 if ( prop != NULL ) {
658 row = prop->getIntValue();
660 prop = child->getChild( "col" );
661 if ( prop != NULL ) {
662 col = prop->getIntValue();
665 // Fetch the raw value
666 int raw_value = switch_matrix[board][row][col];
670 raw_value = !raw_value;
674 scaled_value = (float)raw_value * factor;
676 } else if ( cname == "combo-switch" ) {
677 float combo_value = 0.0f;
681 while ( (pos = child->getChild("position", k++)) != NULL ) {
682 // read the combo position entries from the property tree
684 prop = pos->getChild( "row" );
685 if ( prop != NULL ) {
686 row = prop->getIntValue();
688 prop = pos->getChild( "col" );
689 if ( prop != NULL ) {
690 col = prop->getIntValue();
692 prop = pos->getChild( "value" );
693 if ( prop != NULL ) {
694 combo_value = prop->getFloatValue();
697 // Fetch the raw value
698 int raw_value = switch_matrix[board][row][col];
699 // cout << "sm[" << board << "][" << row << "][" << col
700 // << "] = " << raw_value << endl;
703 // set scaled_value to the first combo_value
704 // that matches and jump out of loop.
705 scaled_value = combo_value;
711 scaled_value *= factor;
712 } else if ( cname == "additive-switch" ) {
713 float additive_value = 0.0f;
714 float increment = 0.0f;
718 while ( (pos = child->getChild("position", k++)) != NULL ) {
719 // read the combo position entries from the property tree
721 prop = pos->getChild( "row" );
722 if ( prop != NULL ) {
723 row = prop->getIntValue();
725 prop = pos->getChild( "col" );
726 if ( prop != NULL ) {
727 col = prop->getIntValue();
729 prop = pos->getChild( "value" );
730 if ( prop != NULL ) {
731 increment = prop->getFloatValue();
734 // Fetch the raw value
735 int raw_value = switch_matrix[board][row][col];
736 // cout << "sm[" << board << "][" << row << "][" << col
737 // << "] = " << raw_value << endl;
740 // set scaled_value to the first combo_value
741 // that matches and jump out of loop.
742 additive_value += increment;
747 scaled_value = additive_value * factor;
750 // handle filter request. The value of the switch must be
751 // steady-state for "n" frames before the property value
754 bool update_prop = true;
757 SGPropertyNode *fv = child->getChild( "filter-value", 0, true );
758 float filter_value = fv->getFloatValue();
759 SGPropertyNode *fc = child->getChild( "filter-count", 0, true );
760 int filter_count = fc->getIntValue();
762 if ( fabs(scaled_value - filter_value) < 0.0001 ) {
768 if ( filter_count < filter ) {
772 fv->setFloatValue( scaled_value );
773 fc->setIntValue( filter_count );
777 if ( type == "engine" || type == "flight" ) {
778 if ( ! ignore_flight_controls->getBoolValue() ) {
779 // update the property tree values
780 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
781 output_nodes[j]->setDoubleValue( scaled_value );
784 } else if ( type == "avionics" ) {
785 // update the property tree values
786 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
787 output_nodes[j]->setDoubleValue( scaled_value );
798 /////////////////////////////////////////////////////////////////////
799 // Read radio switches
800 /////////////////////////////////////////////////////////////////////
802 bool FGATCInput::do_radio_switches() {
804 ATCReadRadios( radios_fd, radio_switch_data );
806 // Process the radio switch/knob inputs
807 if ( radio_in_node != NULL ) {
808 for ( int i = 0; i < radio_in_node->nChildren(); ++i ) {
809 // read the next config entry from the property tree
811 SGPropertyNode *child = radio_in_node->getChild(i);
812 string cname = child->getName();
814 if ( cname == "switch" ) {
817 vector <SGPropertyNode *> output_nodes; output_nodes.clear();
824 int scaled_value = 0;
825 // get common options
827 SGPropertyNode *prop;
828 prop = child->getChild( "name" );
829 if ( prop != NULL ) {
830 name = prop->getStringValue();
832 prop = child->getChild( "type" );
833 if ( prop != NULL ) {
834 type = prop->getStringValue();
837 while ( (prop = child->getChild("prop", j)) != NULL ) {
839 = fgGetNode( prop->getStringValue(), true );
840 output_nodes.push_back( tmp );
843 prop = child->getChild( "byte" );
844 if ( prop != NULL ) {
845 byte_num = prop->getIntValue();
847 prop = child->getChild( "right-shift" );
848 if ( prop != NULL ) {
849 right_shift = prop->getIntValue();
851 prop = child->getChild( "mask" );
852 if ( prop != NULL ) {
853 mask = prop->getIntValue();
855 prop = child->getChild( "factor" );
856 if ( prop != NULL ) {
857 factor = prop->getIntValue();
859 prop = child->getChild( "offset" );
860 if ( prop != NULL ) {
861 offset = prop->getIntValue();
863 prop = child->getChild( "invert" );
864 if ( prop != NULL ) {
865 invert = prop->getBoolValue();
868 // Fetch the raw value
870 = (radio_switch_data[byte_num] >> right_shift) & mask;
874 raw_value = !raw_value;
876 scaled_value = raw_value * factor + offset;
878 // update the property tree values
879 for ( j = 0; j < (int)output_nodes.size(); ++j ) {
880 output_nodes[j]->setIntValue( scaled_value );
890 // process the hardware inputs. This code assumes the calling layer
891 // will lock the hardware.
892 bool FGATCInput::process() {
894 SG_LOG( SG_IO, SG_ALERT, "This board has not been opened for input! "
907 bool FGATCInput::close() {
909 #if defined( unix ) || defined( __CYGWIN__ )
917 result = ::close( analog_in_fd );
918 if ( result == -1 ) {
919 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
921 snprintf( msg, 256, "Error closing %s", analog_in_file );
926 result = ::close( radios_fd );
927 if ( result == -1 ) {
928 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
930 snprintf( msg, 256, "Error closing %s", radios_file );
935 result = ::close( switches_fd );
936 if ( result == -1 ) {
937 SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
939 snprintf( msg, 256, "Error closing %s", switches_file );