]> git.mxchange.org Git - flightgear.git/blobdiff - src/Network/atc610x.cxx
Fixed some stupidity.
[flightgear.git] / src / Network / atc610x.cxx
index ae0c3bcee1c256f84763e88ff61a77cd2322cb78..edec4630cfc9da6abda40a8794cba7ac13dfe192 100644 (file)
@@ -44,6 +44,7 @@
 #include <simgear/io/iochannel.hxx>
 #include <simgear/math/sg_types.hxx>
 #include <simgear/misc/props.hxx>
+#include <simgear/misc/sg_path.hxx>
 
 #include <Main/fg_props.hxx>
 #include <Main/globals.hxx>
 
 SG_USING_STD(string);
 
-#if defined( _MSC_VER ) || defined(__MINGW32__)
-#  define snprintf _snprintf
-#endif
-
 // Lock the ATC 610 hardware
 static int ATC610xLock( int fd ) {
     // rewind
@@ -185,6 +182,42 @@ static void ATC610xReadSwitches( int fd, unsigned char *switch_bytes ) {
 }
 
 
+// Turn a lamp on or off
+void ATC610xSetLamp( int fd, int channel, bool value ) {
+    // lamp channels 0-63 are written to LampPort0, channels 64-127
+    // are written to LampPort1
+
+    // bits 0-6 are the lamp address
+    // bit 7 is the value (on/off)
+
+    int result;
+
+    // Write the value
+    unsigned char buf[3];
+    buf[0] = channel;
+    buf[1] = value;
+    buf[2] = 0;
+    result = write( fd, buf, 2 );
+    if ( result != 2 ) {
+        SG_LOG( SG_IO, SG_ALERT,  "Write failed" );
+        exit( -1 );
+    }
+}
+
+
+void FGATC610x::init_config() {
+#if defined( unix ) || defined( __CYGWIN__ )
+    // Next check home directory for .fgfsrc.hostname file
+    char *envp = ::getenv( "HOME" );
+    if ( envp != NULL ) {
+        SGPath atc610x_config( envp );
+        atc610x_config.append( ".fgfs-atc610x.xml" );
+        readProperties( atc610x_config.str(), globals->get_props() );
+    }
+#endif
+}
+
+
 // Open and initialize ATC 610x hardware
 bool FGATC610x::open() {
     if ( is_enabled() ) {
@@ -193,6 +226,9 @@ bool FGATC610x::open() {
        return false;
     }
 
+    // This loads the config parameters generated by "simcal"
+    init_config();
+
     SG_LOG( SG_IO, SG_ALERT,
            "Initializing ATC 610x hardware, please wait ..." );
 
@@ -203,6 +239,7 @@ bool FGATC610x::open() {
 
     snprintf( lock_file, 256, "/proc/atc610x/board%d/lock", board );
     snprintf( analog_in_file, 256, "/proc/atc610x/board%d/analog_in", board );
+    snprintf( lamps_file, 256, "/proc/atc610x/board%d/lamps", board );
     snprintf( radios_file, 256, "/proc/atc610x/board%d/radios", board );
     snprintf( stepper_file, 256, "/proc/atc610x/board%d/steppers", board );
     snprintf( switches_file, 256, "/proc/atc610x/board%d/switches", board );
@@ -229,6 +266,15 @@ bool FGATC610x::open() {
        exit( -1 );
     }
 
+    lamps_fd = ::open( lamps_file, O_WRONLY );
+    if ( lamps_fd == -1 ) {
+       SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
+       char msg[256];
+       snprintf( msg, 256, "Error opening %s", lamps_file );
+       perror( msg );
+       exit( -1 );
+    }
+
     radios_fd = ::open( radios_file, O_RDWR );
     if ( radios_fd == -1 ) {
        SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
@@ -272,9 +318,17 @@ bool FGATC610x::open() {
     // Release the hardware
     ATC610xRelease( lock_fd );
 
+    SG_LOG( SG_IO, SG_ALERT,
+           "  - Waiting for compass to come home." );
+
     bool home = false;
-    while ( ! home ) {
-       SG_LOG( SG_IO, SG_DEBUG, "Checking if compass home ..." );
+    int timeout = 900;          // about 30 seconds
+    while ( ! home && timeout > 0 ) {
+        if ( timeout % 150 == 0 ) {
+            SG_LOG( SG_IO, SG_INFO, "waiting for compass = " << timeout );
+        } else {
+            SG_LOG( SG_IO, SG_DEBUG, "Checking if compass home ..." );
+        }
 
        while ( ATC610xLock( lock_fd ) <= 0 );
 
@@ -292,6 +346,8 @@ bool FGATC610x::open() {
 #else
        usleep(33);
 #endif
+
+        --timeout;
     }
 
     compass_position = 0.0;
@@ -317,6 +373,14 @@ bool FGATC610x::open() {
 
     ATC610xRelease( lock_fd );
 
+    /////////////////////////////////////////////////////////////////////
+    // Blank the lamps
+    /////////////////////////////////////////////////////////////////////
+
+    for ( int i = 0; i < 128; ++i ) {
+        ATC610xSetLamp( lamps_fd, i, false );
+    }
+
     /////////////////////////////////////////////////////////////////////
     // Finished initing hardware
     /////////////////////////////////////////////////////////////////////
@@ -349,8 +413,71 @@ bool FGATC610x::open() {
     nav2_stby_freq
        = fgGetNode( "/radios/nav[1]/frequencies/standby-mhz", true );
 
-    adf_freq = fgGetNode( "/radios/adf/frequencies/selected-khz", true );
-    adf_stby_freq = fgGetNode( "/radios/adf/frequencies/standby-khz", true );
+    adf_power = fgGetNode( "/radios/kr-87/inputs/power-btn", true );
+    adf_vol = fgGetNode( "/radios/kr-87/inputs/volume", true );
+    adf_adf_btn = fgGetNode( "/radios/kr-87/inputs/adf-btn", true );
+    adf_bfo_btn = fgGetNode( "/radios/kr-87/inputs/bfo-btn", true );
+    adf_freq = fgGetNode( "/radios/kr-87/outputs/selected-khz", true );
+    adf_stby_freq = fgGetNode( "/radios/kr-87/outputs/standby-khz", true );
+    adf_stby_mode = fgGetNode( "/radios/kr-87/modes/stby", true );
+    adf_timer_mode = fgGetNode( "/radios/kr-87/modes/timer", true );
+    adf_count_mode = fgGetNode( "/radios/kr-87/modes/count", true );
+    adf_flight_timer = fgGetNode( "/radios/kr-87/outputs/flight-timer", true );
+    adf_elapsed_timer = fgGetNode( "/radios/kr-87/outputs/elapsed-timer",
+                                   true );
+    adf_ant_ann = fgGetNode( "/radios/kr-87/annunciators/ant", true );
+    adf_adf_ann = fgGetNode( "/radios/kr-87/annunciators/adf", true );
+    adf_bfo_ann = fgGetNode( "/radios/kr-87/annunciators/bfo", true );
+    adf_frq_ann = fgGetNode( "/radios/kr-87/annunciators/frq", true );
+    adf_flt_ann = fgGetNode( "/radios/kr-87/annunciators/flt", true );
+    adf_et_ann = fgGetNode( "/radios/kr-87/annunciators/et", true );
+
+    inner = fgGetNode( "/radios/marker-beacon/inner", true );
+    middle = fgGetNode( "/radios/marker-beacon/middle", true );
+    outer = fgGetNode( "/radios/marker-beacon/outer", true );
+
+    xpdr_ident_btn = fgGetNode( "/radios/kt-70/inputs/ident-btn", true );
+    xpdr_digit1 = fgGetNode( "/radios/kt-70/inputs/digit1", true );
+    xpdr_digit2 = fgGetNode( "/radios/kt-70/inputs/digit2", true );
+    xpdr_digit3 = fgGetNode( "/radios/kt-70/inputs/digit3", true );
+    xpdr_digit4 = fgGetNode( "/radios/kt-70/inputs/digit4", true );
+    xpdr_func_knob = fgGetNode( "/radios/kt-70/inputs/func-knob", true );
+    xpdr_id_code = fgGetNode( "/radios/kt-70/outputs/id-code", true );
+    xpdr_flight_level = fgGetNode( "/radios/kt-70/outputs/flight-level", true );
+    xpdr_fl_ann = fgGetNode( "/radios/kt-70/annunciators/fl", true );
+    xpdr_alt_ann = fgGetNode( "/radios/kt-70/annunciators/alt", true );
+    xpdr_gnd_ann = fgGetNode( "/radios/kt-70/annunciators/gnd", true );
+    xpdr_on_ann = fgGetNode( "/radios/kt-70/annunciators/on", true );
+    xpdr_sby_ann = fgGetNode( "/radios/kt-70/annunciators/sby", true );
+    xpdr_reply_ann = fgGetNode( "/radios/kt-70/annunciators/reply", true );
+
+    elevator_center = fgGetNode( "/input/atc610x/elevator/center", 0 );
+    elevator_min = fgGetNode( "/input/atc610x/elevator/min", 0 );
+    elevator_max = fgGetNode( "/input/atc610x/elevator/max", 0 );
+
+    ailerons_center = fgGetNode( "/input/atc610x/ailerons/center", 0 );
+    ailerons_min = fgGetNode( "/input/atc610x/ailerons/min", 0 );
+    ailerons_max = fgGetNode( "/input/atc610x/ailerons/max", 0 );
+
+    rudder_center = fgGetNode( "/input/atc610x/rudder/center", 0 );
+    rudder_min = fgGetNode( "/input/atc610x/rudder/min", 0 );
+    rudder_max = fgGetNode( "/input/atc610x/rudder/max", 0 );
+
+    throttle_min = fgGetNode( "/input/atc610x/throttle/min", 0 );
+    throttle_max = fgGetNode( "/input/atc610x/throttle/max", 0 );
+
+    mixture_min = fgGetNode( "/input/atc610x/mixture/min", 0 );
+    mixture_max = fgGetNode( "/input/atc610x/mixture/max", 0 );
+
+    trim_center = fgGetNode( "/input/atc610x/trim/center", 0 );
+    trim_min = fgGetNode( "/input/atc610x/trim/min", 0 );
+    trim_max = fgGetNode( "/input/atc610x/trim/max", 0 );
+
+    nav1vol_min = fgGetNode( "/input/atc610x/nav1vol/min", 0 );
+    nav1vol_max = fgGetNode( "/input/atc610x/nav1vol/max", 0 );
+
+    nav2vol_min = fgGetNode( "/input/atc610x/nav2vol/min", 0 );
+    nav2vol_max = fgGetNode( "/input/atc610x/nav2vol/max", 0 );
 
     return true;
 }
@@ -363,6 +490,49 @@ bool FGATC610x::open() {
 #define ATC_AILERON_CENTER 535
 #define ATC_ELEVATOR_TRIM_CENTER 512
 #define ATC_ELEVATOR_CENTER 543
+#define ATC_RUDDER_CENTER 519
+
+// scale a number between min and max (with center defined) to a scale
+// from -1.0 to 1.0
+static double scale( int center, int min, int max, int value ) {
+    // cout << center << " " << min << " " << max << " " << value << " ";
+    double result;
+    double range;
+
+    if ( value <= center ) {
+        range = center - min;
+        result = (value - center) / range;
+    } else {
+        range = max - center;
+        result = (value - center) / range;            
+    }
+
+    if ( result < -1.0 ) result = -1.0;
+    if ( result > 1.0 ) result = 1.0;
+
+    // cout << result << endl;
+
+    return result;
+}
+
+
+// scale a number between min and max to a scale from 0.0 to 1.0
+static double scale( int min, int max, int value ) {
+    // cout << center << " " << min << " " << max << " " << value << " ";
+    double result;
+    double range;
+
+    range = max - min;
+    result = (value - min) / range;
+
+    if ( result < 0.0 ) result = 0.0;
+    if ( result > 1.0 ) result = 1.0;
+
+    // cout << result << endl;
+
+    return result;
+}
+
 
 bool FGATC610x::do_analog_in() {
     // Read raw data in byte form
@@ -381,27 +551,41 @@ bool FGATC610x::do_analog_in() {
     float tmp, tmp1, tmp2;
 
     // aileron
-    tmp = (float)(analog_in_data[0] - ATC_AILERON_CENTER) / 256.0f;
+    tmp = scale( ailerons_center->getIntValue(), ailerons_min->getIntValue(),
+                 ailerons_max->getIntValue(), analog_in_data[0] );
     fgSetFloat( "/controls/aileron", tmp );
     // cout << "aileron = " << analog_in_data[0] << " = " << tmp;
 
     // elevator
-    tmp = (float)(analog_in_data[4] - ATC_ELEVATOR_TRIM_CENTER) / 512.0f;
-    fgSetFloat( "/controls/elevator-trim", tmp );
+    tmp = -scale( elevator_center->getIntValue(), elevator_min->getIntValue(),
+                  elevator_max->getIntValue(), analog_in_data[5] );
+    fgSetFloat( "/controls/elevator", tmp );
     // cout << "trim = " << analog_in_data[4] << " = " << tmp;
 
-    // trim
-    tmp = (float)(ATC_ELEVATOR_CENTER - analog_in_data[5]) / 100.0f;
-    fgSetFloat( "/controls/elevator", tmp );
+    // elevator trim
+    tmp = scale( trim_center->getIntValue(), trim_min->getIntValue(),
+                 trim_max->getIntValue(), analog_in_data[4] );
+    fgSetFloat( "/controls/elevator-trim", tmp );
     // cout << " elev = " << analog_in_data[5] << " = " << tmp << endl;
 
     // mixture
-    tmp = (float)analog_in_data[7] / 680.0f;
+    tmp = scale( mixture_min->getIntValue(), mixture_max->getIntValue(),
+                 analog_in_data[7] );
     fgSetFloat( "/controls/mixture[0]", tmp );
+    fgSetFloat( "/controls/mixture[1]", tmp );
 
     // throttle
-    tmp = (float)analog_in_data[8] / 690.0f;
+    tmp = scale( mixture_min->getIntValue(), mixture_max->getIntValue(),
+                 analog_in_data[8] );
     fgSetFloat( "/controls/throttle[0]", tmp );
+    fgSetFloat( "/controls/throttle[1]", tmp );
+
+#if 0
+    // rudder
+    tmp = scale( rudder_center->getIntValue(), rudder_min->getIntValue(),
+                 rudder_max->getIntValue(), analog_in_data[10] );
+    fgSetFloat( "/controls/rudder", tmp );
+#endif
 
     // nav1 volume
     tmp = (float)analog_in_data[25] / 1024.0f;
@@ -413,7 +597,7 @@ bool FGATC610x::do_analog_in() {
 
     // adf volume
     tmp = (float)analog_in_data[26] / 1024.0f;
-    fgSetFloat( "/radios/adf/volume", tmp );
+    fgSetFloat( "/radios/kr-87/inputs/volume", tmp );
 
     // nav2 obs tuner
     tmp = (float)analog_in_data[29] * 360.0f / 1024.0f;
@@ -428,12 +612,44 @@ bool FGATC610x::do_analog_in() {
 }
 
 
+/////////////////////////////////////////////////////////////////////
+// Write the lights
+/////////////////////////////////////////////////////////////////////
+
+bool FGATC610x::do_lights() {
+
+    // Marker beacons
+    ATC610xSetLamp( lamps_fd, 4, inner->getBoolValue() );
+    ATC610xSetLamp( lamps_fd, 5, middle->getBoolValue() );
+    ATC610xSetLamp( lamps_fd, 3, outer->getBoolValue() );
+
+    // ADF annunciators
+    ATC610xSetLamp( lamps_fd, 11, adf_ant_ann->getBoolValue() ); // ANT
+    ATC610xSetLamp( lamps_fd, 12, adf_adf_ann->getBoolValue() ); // ADF
+    ATC610xSetLamp( lamps_fd, 13, adf_bfo_ann->getBoolValue() ); // BFO
+    ATC610xSetLamp( lamps_fd, 14, adf_frq_ann->getBoolValue() ); // FRQ
+    ATC610xSetLamp( lamps_fd, 15, adf_flt_ann->getBoolValue() ); // FLT
+    ATC610xSetLamp( lamps_fd, 16, adf_et_ann->getBoolValue() ); // ET
+
+    // Transponder annunciators
+    ATC610xSetLamp( lamps_fd, 17, xpdr_fl_ann->getBoolValue() ); // FL
+    ATC610xSetLamp( lamps_fd, 18, xpdr_alt_ann->getBoolValue() ); // ALT
+    ATC610xSetLamp( lamps_fd, 19, xpdr_gnd_ann->getBoolValue() ); // GND
+    ATC610xSetLamp( lamps_fd, 20, xpdr_on_ann->getBoolValue() ); // ON
+    ATC610xSetLamp( lamps_fd, 21, xpdr_sby_ann->getBoolValue() ); // SBY
+    ATC610xSetLamp( lamps_fd, 22, xpdr_reply_ann->getBoolValue() ); // R
+
+    return true;
+}
+
+
 /////////////////////////////////////////////////////////////////////
 // Read radio switches 
 /////////////////////////////////////////////////////////////////////
 
 bool FGATC610x::do_radio_switches() {
-    float freq, inc;
+    double freq, coarse_freq, fine_freq, value;
+    int diff;
 
     ATC610xReadRadios( radios_fd, radio_switch_data );
 
@@ -450,6 +666,10 @@ bool FGATC610x::do_radio_switches() {
        fgSetInt( "/radios/dme/switch-position", 3 );
     }
 
+    // Com1 Power
+    fgSetBool( "/radios/comm[0]/inputs/power-btn",
+               radio_switch_data[7] & 0x01 );
+
     // Com1 Swap
     int com1_swap = !((radio_switch_data[7] >> 1) & 0x01);
     static int last_com1_swap;
@@ -461,6 +681,10 @@ bool FGATC610x::do_radio_switches() {
     }
     last_com1_swap = com1_swap;
 
+    // Com2 Power
+    fgSetBool( "/radios/comm[1]/inputs/power-btn",
+               radio_switch_data[15] & 0x01 );
+
     // Com2 Swap
     int com2_swap = !((radio_switch_data[15] >> 1) & 0x01);
     static int last_com2_swap;
@@ -495,211 +719,358 @@ bool FGATC610x::do_radio_switches() {
     last_nav2_swap = nav2_swap;
 
     // Com1 Tuner
-    int com1_tuner_fine = (radio_switch_data[5] >> 4) & 0x0f;
-    int com1_tuner_course = radio_switch_data[5] & 0x0f;
-    // cout << "com1 = " << com1_tuner_fine << " " << com1_tuner_course << endl;
+    int com1_tuner_fine = ((radio_switch_data[5] >> 4) & 0x0f) - 1;
+    int com1_tuner_coarse = (radio_switch_data[5] & 0x0f) - 1;
     static int last_com1_tuner_fine = com1_tuner_fine;
-    static int last_com1_tuner_course = com1_tuner_course;
-    inc = 0.0;
+    static int last_com1_tuner_coarse = com1_tuner_coarse;
+
+    freq = com1_stby_freq->getFloatValue();
+    coarse_freq = (int)freq;
+    fine_freq = (int)((freq - coarse_freq) * 40 + 0.5);
+
     if ( com1_tuner_fine != last_com1_tuner_fine ) {
-       if ( com1_tuner_fine == 0x0c && last_com1_tuner_fine == 0x01 ) {
-           inc = -0.025;
-       } else if ( com1_tuner_fine == 0x01 && last_com1_tuner_fine == 0x0c ) {
-           inc = -0.025;
-       } else if ( com1_tuner_fine > last_com1_tuner_fine ) {
-           inc = 0.025;
-       } else {
-           inc = -0.025;
-       }
-    }
-    if ( com1_tuner_course != last_com1_tuner_course ) {
-       if ( com1_tuner_course == 0x0c && last_com1_tuner_course == 0x01 ) {
-           inc = -1.0;
-       } else if ( com1_tuner_course == 0x01
-                   && last_com1_tuner_course == 0x0c ) {
-           inc = -1.0;
-       } else if ( com1_tuner_course > last_com1_tuner_course ) {
-           inc = 1.0;
-       } else {
-           inc = -1.0;
-       }
-    }
+        diff = com1_tuner_fine - last_com1_tuner_fine;
+        if ( abs(diff) > 4 ) {
+            // roll over
+            if ( com1_tuner_fine < last_com1_tuner_fine ) {
+                // going up
+                diff = 12 - last_com1_tuner_fine + com1_tuner_fine;
+            } else {
+                // going down
+                diff = com1_tuner_fine - 12 - last_com1_tuner_fine;
+            }
+        }
+        fine_freq += diff;
+    }
+    while ( fine_freq >= 40.0 ) { fine_freq -= 40.0; }
+    while ( fine_freq < 0.0 )  { fine_freq += 40.0; }
+
+    if ( com1_tuner_coarse != last_com1_tuner_coarse ) {
+        diff = com1_tuner_coarse - last_com1_tuner_coarse;
+        if ( abs(diff) > 4 ) {
+            // roll over
+            if ( com1_tuner_coarse < last_com1_tuner_coarse ) {
+                // going up
+                diff = 12 - last_com1_tuner_coarse + com1_tuner_coarse;
+            } else {
+                // going down
+                diff = com1_tuner_coarse - 12 - last_com1_tuner_coarse;
+            }
+        }
+        coarse_freq += diff;
+    }
+    if ( coarse_freq < 118.0 ) { coarse_freq += 19.0; }
+    if ( coarse_freq > 136.0 ) { coarse_freq -= 19.0; }
+
     last_com1_tuner_fine = com1_tuner_fine;
-    last_com1_tuner_course = com1_tuner_course;
+    last_com1_tuner_coarse = com1_tuner_coarse;
 
-    freq = com1_stby_freq->getFloatValue() + inc;
-    if ( freq < 0.0 ) {
-       freq = 140.0;
-    }
-    if ( freq > 140.0 ) {
-       freq = 0.0;
-    }
-    fgSetFloat( "/radios/comm[0]/frequencies/standby-mhz", freq );
+    fgSetFloat( "/radios/comm[0]/frequencies/standby-mhz", 
+                coarse_freq + fine_freq / 40.0 );
 
     // Com2 Tuner
-    int com2_tuner_fine = (radio_switch_data[13] >> 4) & 0x0f;
-    int com2_tuner_course = radio_switch_data[13] & 0x0f;
+    int com2_tuner_fine = ((radio_switch_data[13] >> 4) & 0x0f) - 1;
+    int com2_tuner_coarse = (radio_switch_data[13] & 0x0f) - 1;
     static int last_com2_tuner_fine = com2_tuner_fine;
-    static int last_com2_tuner_course = com2_tuner_course;
-    inc = 0.0;
+    static int last_com2_tuner_coarse = com2_tuner_coarse;
+
+    freq = com2_stby_freq->getFloatValue();
+    coarse_freq = (int)freq;
+    fine_freq = (int)((freq - coarse_freq) * 40 + 0.5);
+
     if ( com2_tuner_fine != last_com2_tuner_fine ) {
-       if ( com2_tuner_fine == 0x0c && last_com2_tuner_fine == 0x01 ) {
-           inc = -0.025;
-       } else if ( com2_tuner_fine == 0x01 && last_com2_tuner_fine == 0x0c ) {
-           inc = -0.025;
-       } else if ( com2_tuner_fine > last_com2_tuner_fine ) {
-           inc = 0.025;
-       } else {
-           inc = -0.025;
-       }
-    }
-    if ( com2_tuner_course != last_com2_tuner_course ) {
-       if ( com2_tuner_course == 0x0c && last_com2_tuner_course == 0x01 ) {
-           inc = -1.0;
-       } else if ( com2_tuner_course == 0x01
-                   && last_com2_tuner_course == 0x0c ) {
-           inc = -1.0;
-       } else if ( com2_tuner_course > last_com2_tuner_course ) {
-           inc = 1.0;
-       } else {
-           inc = -1.0;
-       }
-    }
+        diff = com2_tuner_fine - last_com2_tuner_fine;
+        if ( abs(diff) > 4 ) {
+            // roll over
+            if ( com2_tuner_fine < last_com2_tuner_fine ) {
+                // going up
+                diff = 12 - last_com2_tuner_fine + com2_tuner_fine;
+            } else {
+                // going down
+                diff = com2_tuner_fine - 12 - last_com2_tuner_fine;
+            }
+        }
+        fine_freq += diff;
+    }
+    while ( fine_freq >= 40.0 ) { fine_freq -= 40.0; }
+    while ( fine_freq < 0.0 )  { fine_freq += 40.0; }
+
+    if ( com2_tuner_coarse != last_com2_tuner_coarse ) {
+        diff = com2_tuner_coarse - last_com2_tuner_coarse;
+        if ( abs(diff) > 4 ) {
+            // roll over
+            if ( com2_tuner_coarse < last_com2_tuner_coarse ) {
+                // going up
+                diff = 12 - last_com2_tuner_coarse + com2_tuner_coarse;
+            } else {
+                // going down
+                diff = com2_tuner_coarse - 12 - last_com2_tuner_coarse;
+            }
+        }
+        coarse_freq += diff;
+    }
+    if ( coarse_freq < 118.0 ) { coarse_freq += 19.0; }
+    if ( coarse_freq > 136.0 ) { coarse_freq -= 19.0; }
+
     last_com2_tuner_fine = com2_tuner_fine;
-    last_com2_tuner_course = com2_tuner_course;
+    last_com2_tuner_coarse = com2_tuner_coarse;
 
-    freq = com2_stby_freq->getFloatValue() + inc;
-    if ( freq < 0.0 ) {
-       freq = 140.0;
-    }
-    if ( freq > 140.0 ) {
-       freq = 0.0;
-    }
-    fgSetFloat( "/radios/comm[1]/frequencies/standby-mhz", freq );
+    fgSetFloat( "/radios/comm[1]/frequencies/standby-mhz",
+                coarse_freq + fine_freq / 40.0 );
 
     // Nav1 Tuner
-    int nav1_tuner_fine = (radio_switch_data[9] >> 4) & 0x0f;
-    int nav1_tuner_course = radio_switch_data[9] & 0x0f;
+    int nav1_tuner_fine = ((radio_switch_data[9] >> 4) & 0x0f) - 1;
+    int nav1_tuner_coarse = (radio_switch_data[9] & 0x0f) - 1;
     static int last_nav1_tuner_fine = nav1_tuner_fine;
-    static int last_nav1_tuner_course = nav1_tuner_course;
-    inc = 0.0;
+    static int last_nav1_tuner_coarse = nav1_tuner_coarse;
+
+    freq = nav1_stby_freq->getFloatValue();
+    coarse_freq = (int)freq;
+    fine_freq = (int)((freq - coarse_freq) * 20 + 0.5);
+
     if ( nav1_tuner_fine != last_nav1_tuner_fine ) {
-       if ( nav1_tuner_fine == 0x0c && last_nav1_tuner_fine == 0x01 ) {
-           inc = -0.05;
-       } else if ( nav1_tuner_fine == 0x01 && last_nav1_tuner_fine == 0x0c ) {
-           inc = -0.05;
-       } else if ( nav1_tuner_fine > last_nav1_tuner_fine ) {
-           inc = 0.05;
-       } else {
-           inc = -0.05;
-       }
-    }
-    if ( nav1_tuner_course != last_nav1_tuner_course ) {
-       if ( nav1_tuner_course == 0x0c && last_nav1_tuner_course == 0x01 ) {
-           inc = -1.0;
-       } else if ( nav1_tuner_course == 0x01
-                   && last_nav1_tuner_course == 0x0c ) {
-           inc = -1.0;
-       } else if ( nav1_tuner_course > last_nav1_tuner_course ) {
-           inc = 1.0;
-       } else {
-           inc = -1.0;
-       }
-    }
+        diff = nav1_tuner_fine - last_nav1_tuner_fine;
+        if ( abs(diff) > 4 ) {
+            // roll over
+            if ( nav1_tuner_fine < last_nav1_tuner_fine ) {
+                // going up
+                diff = 12 - last_nav1_tuner_fine + nav1_tuner_fine;
+            } else {
+                // going down
+                diff = nav1_tuner_fine - 12 - last_nav1_tuner_fine;
+            }
+        }
+        fine_freq += diff;
+    }
+    while ( fine_freq >= 20.0 ) { fine_freq -= 20.0; }
+    while ( fine_freq < 0.0 )  { fine_freq += 20.0; }
+
+    if ( nav1_tuner_coarse != last_nav1_tuner_coarse ) {
+        diff = nav1_tuner_coarse - last_nav1_tuner_coarse;
+        if ( abs(diff) > 4 ) {
+            // roll over
+            if ( nav1_tuner_coarse < last_nav1_tuner_coarse ) {
+                // going up
+                diff = 12 - last_nav1_tuner_coarse + nav1_tuner_coarse;
+            } else {
+                // going down
+                diff = nav1_tuner_coarse - 12 - last_nav1_tuner_coarse;
+            }
+        }
+        coarse_freq += diff;
+    }
+    if ( coarse_freq < 108.0 ) { coarse_freq += 10.0; }
+    if ( coarse_freq > 117.0 ) { coarse_freq -= 10.0; }
+
     last_nav1_tuner_fine = nav1_tuner_fine;
-    last_nav1_tuner_course = nav1_tuner_course;
+    last_nav1_tuner_coarse = nav1_tuner_coarse;
 
-    freq = nav1_stby_freq->getFloatValue() + inc;
-    if ( freq < 108.0 ) {
-       freq = 117.95;
-    }
-    if ( freq > 117.95 ) {
-       freq = 108.0;
-    }
-    fgSetFloat( "/radios/nav[0]/frequencies/standby-mhz", freq );
+    fgSetFloat( "/radios/nav[0]/frequencies/standby-mhz",
+                coarse_freq + fine_freq / 20.0 );
 
     // Nav2 Tuner
-    int nav2_tuner_fine = (radio_switch_data[17] >> 4) & 0x0f;
-    int nav2_tuner_course = radio_switch_data[17] & 0x0f;
+    int nav2_tuner_fine = ((radio_switch_data[17] >> 4) & 0x0f) - 1;
+    int nav2_tuner_coarse = (radio_switch_data[17] & 0x0f) - 1;
     static int last_nav2_tuner_fine = nav2_tuner_fine;
-    static int last_nav2_tuner_course = nav2_tuner_course;
-    inc = 0.0;
+    static int last_nav2_tuner_coarse = nav2_tuner_coarse;
+
+    freq = nav2_stby_freq->getFloatValue();
+    coarse_freq = (int)freq;
+    fine_freq = (int)((freq - coarse_freq) * 20 + 0.5);
+
     if ( nav2_tuner_fine != last_nav2_tuner_fine ) {
-       if ( nav2_tuner_fine == 0x0c && last_nav2_tuner_fine == 0x01 ) {
-           inc = -0.05;
-       } else if ( nav2_tuner_fine == 0x01 && last_nav2_tuner_fine == 0x0c ) {
-           inc = -0.05;
-       } else if ( nav2_tuner_fine > last_nav2_tuner_fine ) {
-           inc = 0.05;
-       } else {
-           inc = -0.05;
-       }
-    }
-    if ( nav2_tuner_course != last_nav2_tuner_course ) {
-       if ( nav2_tuner_course == 0x0c && last_nav2_tuner_course == 0x01 ) {
-           inc = -1.0;
-       } else if ( nav2_tuner_course == 0x01
-                   && last_nav2_tuner_course == 0x0c ) {
-           inc = -1.0;
-       } else if ( nav2_tuner_course > last_nav2_tuner_course ) {
-           inc = 1.0;
-       } else {
-           inc = -1.0;
-       }
-    }
+        diff = nav2_tuner_fine - last_nav2_tuner_fine;
+        if ( abs(diff) > 4 ) {
+            // roll over
+            if ( nav2_tuner_fine < last_nav2_tuner_fine ) {
+                // going up
+                diff = 12 - last_nav2_tuner_fine + nav2_tuner_fine;
+            } else {
+                // going down
+                diff = nav2_tuner_fine - 12 - last_nav2_tuner_fine;
+            }
+        }
+        fine_freq += diff;
+    }
+    while ( fine_freq >= 20.0 ) { fine_freq -= 20.0; }
+    while ( fine_freq < 0.0 )  { fine_freq += 20.0; }
+
+    if ( nav2_tuner_coarse != last_nav2_tuner_coarse ) {
+        diff = nav2_tuner_coarse - last_nav2_tuner_coarse;
+        if ( abs(diff) > 4 ) {
+            // roll over
+            if ( nav2_tuner_coarse < last_nav2_tuner_coarse ) {
+                // going up
+                diff = 12 - last_nav2_tuner_coarse + nav2_tuner_coarse;
+            } else {
+                // going down
+                diff = nav2_tuner_coarse - 12 - last_nav2_tuner_coarse;
+            }
+        }
+        coarse_freq += diff;
+    }
+    if ( coarse_freq < 108.0 ) { coarse_freq += 10.0; }
+    if ( coarse_freq > 117.0 ) { coarse_freq -= 10.0; }
+
     last_nav2_tuner_fine = nav2_tuner_fine;
-    last_nav2_tuner_course = nav2_tuner_course;
+    last_nav2_tuner_coarse = nav2_tuner_coarse;
 
-    freq = nav2_stby_freq->getFloatValue() + inc;
-    if ( freq < 108.0 ) {
-       freq = 117.95;
-    }
-    if ( freq > 117.95 ) {
-       freq = 108.0;
-    }
-    fgSetFloat( "/radios/nav[1]/frequencies/standby-mhz", freq );
+    fgSetFloat( "/radios/nav[1]/frequencies/standby-mhz", 
+                coarse_freq + fine_freq / 20.0);
 
     // ADF Tuner
-    int adf_tuner_fine = (radio_switch_data[21] >> 4) & 0x0f;
-    int adf_tuner_course = radio_switch_data[21] & 0x0f;
-    // cout << "adf = " << adf_tuner_fine << " " << adf_tuner_course << endl;
+    int adf_tuner_fine = ((radio_switch_data[21] >> 4) & 0x0f) - 1;
+    int adf_tuner_coarse = (radio_switch_data[21] & 0x0f) - 1;
     static int last_adf_tuner_fine = adf_tuner_fine;
-    static int last_adf_tuner_course = adf_tuner_course;
-    inc = 0.0;
-    if ( adf_tuner_fine != last_adf_tuner_fine ) {
-       if ( adf_tuner_fine == 0x0c && last_adf_tuner_fine == 0x01 ) {
-           inc = -1.0;
-       } else if ( adf_tuner_fine == 0x01 && last_adf_tuner_fine == 0x0c ) {
-           inc = -1.0;
-       } else if ( adf_tuner_fine > last_adf_tuner_fine ) {
-           inc = 1.0;
-       } else {
-           inc = -1.0;
-       }
+    static int last_adf_tuner_coarse = adf_tuner_coarse;
+
+    // cout << "adf_stby_mode = " << adf_stby_mode->getIntValue() << endl;
+    if ( adf_count_mode->getIntValue() == 2 ) {
+        // tune count down timer
+        value = adf_elapsed_timer->getDoubleValue();
+    } else {
+        // tune frequency
+        if ( adf_stby_mode->getIntValue() == 1 ) {
+            value = adf_freq->getFloatValue();
+        } else {
+            value = adf_stby_freq->getFloatValue();
+        }
     }
-    if ( adf_tuner_course != last_adf_tuner_course ) {
-       if ( adf_tuner_course == 0x0c && last_adf_tuner_course == 0x01 ) {
-           inc = -25.0;
-       } else if ( adf_tuner_course == 0x01
-                   && last_adf_tuner_course == 0x0c ) {
-           inc = -25.0;
-       } else if ( adf_tuner_course > last_adf_tuner_course ) {
-           inc = 25.0;
-       } else {
-           inc = -25.0;
-       }
+
+    if ( adf_tuner_fine != last_adf_tuner_fine ) {
+        diff = adf_tuner_fine - last_adf_tuner_fine;
+        if ( abs(diff) > 4 ) {
+            // roll over
+            if ( adf_tuner_fine < last_adf_tuner_fine ) {
+                // going up
+                diff = 12 - last_adf_tuner_fine + adf_tuner_fine;
+            } else {
+                // going down
+                diff = adf_tuner_fine - 12 - last_adf_tuner_fine;
+            }
+        }
+        value += diff;
+    }
+
+    if ( adf_tuner_coarse != last_adf_tuner_coarse ) {
+        diff = adf_tuner_coarse - last_adf_tuner_coarse;
+        if ( abs(diff) > 4 ) {
+            // roll over
+            if ( adf_tuner_coarse < last_adf_tuner_coarse ) {
+                // going up
+                diff = 12 - last_adf_tuner_coarse + adf_tuner_coarse;
+            } else {
+                // going down
+                diff = adf_tuner_coarse - 12 - last_adf_tuner_coarse;
+            }
+        }
+        if ( adf_count_mode->getIntValue() == 2 ) {
+            value += 60 * diff;
+        } else {
+            value += 25 * diff;
+        }
+    }
+    if ( adf_count_mode->getIntValue() == 2 ) {
+        if ( value < 0 ) { value += 3600; }
+        if ( value > 3599 ) { value -= 3600; }
+    } else {
+        if ( value < 200 ) { value += 1600; }
+        if ( value > 1799 ) { value -= 1600; }
     }
     last_adf_tuner_fine = adf_tuner_fine;
-    last_adf_tuner_course = adf_tuner_course;
+    last_adf_tuner_coarse = adf_tuner_coarse;
 
-    freq = adf_freq->getFloatValue() + inc;
-    if ( freq < 100.0 ) {
-       freq = 1299;
-    }
-    if ( freq > 1299 ) {
-       freq = 100.0;
-    }
-    fgSetFloat( "/radios/adf/frequencies/selected-khz", freq );
+    if ( adf_count_mode->getIntValue() == 2 ) {
+        fgSetFloat( "/radios/kr-87/outputs/elapsed-timer", value );
+    } else {
+        if ( adf_stby_mode->getIntValue() == 1 ) {
+            fgSetFloat( "/radios/kr-87/outputs/selected-khz", value );
+        } else {
+            fgSetFloat( "/radios/kr-87/outputs/standby-khz", value );
+        }
+    }
+
+    // ADF buttons 
+    fgSetInt( "/radios/kr-87/inputs/adf-btn",
+              !(radio_switch_data[23] & 0x01) );
+    fgSetInt( "/radios/kr-87/inputs/bfo-btn",
+              !(radio_switch_data[23] >> 1 & 0x01) );
+    fgSetInt( "/radios/kr-87/inputs/frq-btn",
+              (radio_switch_data[23] >> 2 & 0x01) );
+    fgSetInt( "/radios/kr-87/inputs/flt-et-btn",
+             !(radio_switch_data[23] >> 3 & 0x01) );
+    fgSetInt( "/radios/kr-87/inputs/set-rst-btn",
+              !(radio_switch_data[23] >> 4 & 0x01) );
+    fgSetInt( "/radios/kr-87/inputs/power-btn",
+              radio_switch_data[23] >> 5 & 0x01 );
+    /* cout << "adf = " << !(radio_switch_data[23] & 0x01)
+         << " bfo = " << !(radio_switch_data[23] >> 1 & 0x01)
+         << " stby = " << !(radio_switch_data[23] >> 2 & 0x01)
+         << " timer = " << !(radio_switch_data[23] >> 3 & 0x01)
+         << " set/rst = " << !(radio_switch_data[23] >> 4 & 0x01)
+         << endl; */
+
+    // Transponder Tuner
+    int i;
+    int digit_tuner[4];
+
+    digit_tuner[0] = radio_switch_data[25] & 0x0f;
+    digit_tuner[1] = ( radio_switch_data[25] >> 4 ) & 0x0f;
+    digit_tuner[2] = radio_switch_data[29] & 0x0f;
+    digit_tuner[3] = ( radio_switch_data[29] >> 4 ) & 0x0f;
+    static bool first_time = true;
+    static int last_digit_tuner[4];
+    if ( first_time ) {
+        first_time = false;
+        for ( i = 0; i < 4; ++i ) {
+            last_digit_tuner[i] = digit_tuner[i];
+        }
+    }
+
+    int id_code = xpdr_id_code->getIntValue();
+    int digit[4];
+    int place = 1000;
+    for ( i = 0; i < 4; ++i ) {
+        digit[i] = id_code / place;
+        id_code -= digit[i] * place;
+        place /= 10;
+    }
+
+    for ( i = 0; i < 4; ++i ) {
+        if ( digit_tuner[i] != last_digit_tuner[i] ) {
+            diff = digit_tuner[i] - last_digit_tuner[i];
+            if ( abs(diff) > 4 ) {
+                // roll over
+                if ( digit_tuner[i] < last_digit_tuner[i] ) {
+                    // going up
+                    diff = 15 - last_digit_tuner[i] + digit_tuner[i];
+                } else {
+                    // going down
+                    diff = digit_tuner[i] - 15 - last_digit_tuner[i];
+                }
+            }
+            digit[i] += diff;
+        }
+        while ( digit[i] >= 8 ) { digit[i] -= 8; }
+        while ( digit[i] < 0 )  { digit[i] += 8; }
+        last_digit_tuner[i] = digit_tuner[i];
+    }
+
+    fgSetInt( "/radios/kt-70/inputs/digit1", digit[0] );
+    fgSetInt( "/radios/kt-70/inputs/digit2", digit[1] );
+    fgSetInt( "/radios/kt-70/inputs/digit3", digit[2] );
+    fgSetInt( "/radios/kt-70/inputs/digit4", digit[3] );
+
+    int tmp = 0;
+    for ( i = 0; i < 5; ++i ) {
+        if ( radio_switch_data[27] >> i & 0x01 ) {
+            tmp = i + 1;
+        }
+    }
+    fgSetInt( "/radios/kt-70/inputs/func-knob", tmp );
+    fgSetInt( "/radios/kt-70/inputs/ident-btn",
+              !(radio_switch_data[27] >> 5 & 0x01) );
 
     return true;
 }
@@ -720,7 +1091,7 @@ bool FGATC610x::do_radio_display() {
        if ( minutes > 999 ) {
            minutes = 999.0;
        }
-       sprintf(digits, "%03.0f", minutes);
+       snprintf(digits, 7, "%03.0f", minutes);
        for ( i = 0; i < 6; ++i ) {
            digits[i] -= '0';
        }
@@ -732,7 +1103,7 @@ bool FGATC610x::do_radio_display() {
        if ( knots > 999 ) {
            knots = 999.0;
        }
-       sprintf(digits, "%03.0f", knots);
+       snprintf(digits, 7, "%03.0f", knots);
        for ( i = 0; i < 6; ++i ) {
            digits[i] -= '0';
        }
@@ -744,7 +1115,7 @@ bool FGATC610x::do_radio_display() {
        if ( nm > 99 ) {
            nm = 99.0;
        }
-       sprintf(digits, "%04.1f", nm);
+       snprintf(digits, 7, "%04.1f", nm);
        for ( i = 0; i < 6; ++i ) {
            digits[i] -= '0';
        }
@@ -764,7 +1135,7 @@ bool FGATC610x::do_radio_display() {
     if ( fabs(com1_stby) > 999.99 ) {
        com1_stby = 0.0;
     }
-    sprintf(digits, "%06.3f", com1_stby);
+    snprintf(digits, 7, "%06.3f", com1_stby);
     for ( i = 0; i < 6; ++i ) {
        digits[i] -= '0';
     }
@@ -777,7 +1148,7 @@ bool FGATC610x::do_radio_display() {
     if ( fabs(com1) > 999.99 ) {
        com1 = 0.0;
     }
-    sprintf(digits, "%06.3f", com1);
+    snprintf(digits, 7, "%06.3f", com1);
     for ( i = 0; i < 6; ++i ) {
        digits[i] -= '0';
     }
@@ -792,7 +1163,7 @@ bool FGATC610x::do_radio_display() {
     if ( fabs(com2_stby) > 999.99 ) {
        com2_stby = 0.0;
     }
-    sprintf(digits, "%06.3f", com2_stby);
+    snprintf(digits, 7, "%06.3f", com2_stby);
     for ( i = 0; i < 6; ++i ) {
        digits[i] -= '0';
     }
@@ -805,7 +1176,7 @@ bool FGATC610x::do_radio_display() {
     if ( fabs(com2) > 999.99 ) {
        com2 = 0.0;
     }
-    sprintf(digits, "%06.3f", com2);
+    snprintf(digits, 7, "%06.3f", com2);
     for ( i = 0; i < 6; ++i ) {
        digits[i] -= '0';
     }
@@ -820,7 +1191,7 @@ bool FGATC610x::do_radio_display() {
     if ( fabs(nav1_stby) > 999.99 ) {
        nav1_stby = 0.0;
     }
-    sprintf(digits, "%06.2f", nav1_stby);
+    snprintf(digits, 7, "%06.2f", nav1_stby);
     for ( i = 0; i < 6; ++i ) {
        digits[i] -= '0';
     }
@@ -833,7 +1204,7 @@ bool FGATC610x::do_radio_display() {
     if ( fabs(nav1) > 999.99 ) {
        nav1 = 0.0;
     }
-    sprintf(digits, "%06.2f", nav1);
+    snprintf(digits, 7, "%06.2f", nav1);
     for ( i = 0; i < 6; ++i ) {
        digits[i] -= '0';
     }
@@ -848,7 +1219,7 @@ bool FGATC610x::do_radio_display() {
     if ( fabs(nav2_stby) > 999.99 ) {
        nav2_stby = 0.0;
     }
-    sprintf(digits, "%06.2f", nav2_stby);
+    snprintf(digits, 7, "%06.2f", nav2_stby);
     for ( i = 0; i < 6; ++i ) {
        digits[i] -= '0';
     }
@@ -861,7 +1232,7 @@ bool FGATC610x::do_radio_display() {
     if ( fabs(nav2) > 999.99 ) {
        nav2 = 0.0;
     }
-    sprintf(digits, "%06.2f", nav2);
+    snprintf(digits, 7, "%06.2f", nav2);
     for ( i = 0; i < 6; ++i ) {
        digits[i] -= '0';
     }
@@ -871,29 +1242,131 @@ bool FGATC610x::do_radio_display() {
     // the 0x00 in the upper nibble of the 6th byte of each display
     // turns on the decimal point
 
-    // ADF standby frequency
-    float adf_stby = adf_stby_freq->getFloatValue();
-    if ( fabs(adf_stby) > 999.99 ) {
-       adf_stby = 0.0;
-    }
-    sprintf(digits, "%03.0f", adf_stby);
-    for ( i = 0; i < 6; ++i ) {
-       digits[i] -= '0';
-    }
-    radio_display_data[30] = digits[2] << 4 | 0x0f;
-    radio_display_data[31] = digits[0] << 4 | digits[1];
-
-    // ADF in use frequency
-    float adf = adf_freq->getFloatValue();
-    if ( fabs(adf) > 999.99 ) {
-       adf = 0.0;
-    }
-    sprintf(digits, "%03.0f", adf);
-    for ( i = 0; i < 6; ++i ) {
-       digits[i] -= '0';
+    // ADF standby frequency / timer
+    if ( adf_power->getBoolValue() ) {
+        if ( adf_stby_mode->getIntValue() == 0 ) {
+            // frequency
+            float adf_stby = adf_stby_freq->getFloatValue();
+            if ( fabs(adf_stby) > 1799 ) {
+                adf_stby = 1799;
+            }
+            snprintf(digits, 7, "%04.0f", adf_stby);
+            for ( i = 0; i < 6; ++i ) {
+                digits[i] -= '0';
+            }
+            radio_display_data[30] = digits[3] << 4 | 0x0f;
+            radio_display_data[31] = digits[1] << 4 | digits[2];
+            if ( digits[0] == 0 ) {
+                radio_display_data[32] = 0xff;
+            } else {
+                radio_display_data[32] = 0xf0 | digits[0];
+            }
+        } else {
+            // timer
+            double time;
+            int hours, min, sec;
+            if ( adf_timer_mode->getIntValue() == 0 ) {
+                time = adf_flight_timer->getDoubleValue();
+            } else {
+                time = adf_elapsed_timer->getDoubleValue();
+            }
+            // cout << time << endl;
+            hours = (int)(time / 3600.0);
+            time -= hours * 3600.00;
+            min = (int)(time / 60.0);
+            time -= min * 60.0;
+            sec = (int)time;
+            int big, little;
+            if ( hours > 0 ) {
+                big = hours;
+                if ( big > 99 ) {
+                    big = 99;
+                }
+                little = min;
+            } else {
+                big = min;
+                little = sec;
+            }
+            if ( big > 99 ) {
+                big = 99;
+            }
+            // cout << big << ":" << little << endl;
+            snprintf(digits, 7, "%02d%02d", big, little);
+            for ( i = 0; i < 6; ++i ) {
+                digits[i] -= '0';
+            }
+            radio_display_data[30] = digits[3] << 4 | 0x0f;
+            radio_display_data[31] = digits[1] << 4 | digits[2];
+            radio_display_data[32] = 0xf0 | digits[0];
+        }
+
+        // ADF in use frequency
+        float adf = adf_freq->getFloatValue();
+        if ( fabs(adf) > 1799 ) {
+            adf = 1799;
+        }
+        snprintf(digits, 7, "%04.0f", adf);
+        for ( i = 0; i < 6; ++i ) {
+            digits[i] -= '0';
+        }
+        radio_display_data[33] = digits[2] << 4 | digits[3];
+        if ( digits[0] == 0 ) {
+            radio_display_data[34] = 0xf0 | digits[1];
+        } else {
+            radio_display_data[34] = digits[0] << 4 | digits[1];
+        }
+    } else {
+        radio_display_data[30] = 0xff;
+        radio_display_data[31] = 0xff;
+        radio_display_data[32] = 0xff;
+        radio_display_data[33] = 0xff;
+        radio_display_data[34] = 0xff;
+    }
+    
+    // Transponder code and flight level
+    if ( xpdr_func_knob->getIntValue() == 2 ) {
+        // test mode
+        radio_display_data[36] = 8 << 4 | 8;
+        radio_display_data[37] = 8 << 4 | 8;
+        radio_display_data[38] = 0xff;
+        radio_display_data[39] = 8 << 4 | 0x0f;
+        radio_display_data[40] = 8 << 4 | 8;
+    } else if ( xpdr_func_knob->getIntValue() > 0 ) {
+        // other on modes
+        int id_code = xpdr_id_code->getIntValue();
+        int place = 1000;
+        for ( i = 0; i < 4; ++i ) {
+            digits[i] = id_code / place;
+            id_code -= digits[i] * place;
+            place /= 10;
+        }
+        radio_display_data[36] = digits[2] << 4 | digits[3];
+        radio_display_data[37] = digits[0] << 4 | digits[1];
+        radio_display_data[38] = 0xff;
+
+        if ( xpdr_func_knob->getIntValue() == 3 ||
+             xpdr_func_knob->getIntValue() == 5 )
+        {
+            // do flight level display
+            snprintf(digits, 7, "%03d", xpdr_flight_level->getIntValue() );
+            for ( i = 0; i < 6; ++i ) {
+                digits[i] -= '0';
+            }
+           radio_display_data[39] = digits[2] << 4 | 0x0f;
+           radio_display_data[40] = digits[0] << 4 | digits[1];
+        } else {
+            // blank flight level display
+            radio_display_data[39] = 0xff;
+            radio_display_data[40] = 0xff;
+        }
+    } else {
+        // off
+        radio_display_data[36] = 0xff;
+        radio_display_data[37] = 0xff;
+        radio_display_data[38] = 0xff;
+        radio_display_data[39] = 0xff;
+        radio_display_data[40] = 0xff;
     }
-    radio_display_data[33] = digits[1] << 4 | digits[2];
-    radio_display_data[34] = 0xf0 | digits[0];
 
     ATC610xSetRadios( radios_fd, radio_display_data );
 
@@ -962,32 +1435,87 @@ bool FGATC610x::do_switches() {
     update_switch_matrix( board, switch_data, switch_matrix );
 
     // magnetos and starter switch
+    int magnetos = 0;
+    bool starter = false;
     if ( switch_matrix[board][3][1] == 1 ) {
-       fgSetInt( "/controls/magnetos[0]", 3 );
-       fgSetBool( "/controls/starter[0]", true );
+       magnetos = 3;
+       starter = true;
     } else if ( switch_matrix[board][2][1] == 1 ) {
-       fgSetInt( "/controls/magnetos[0]", 3 );
-       fgSetBool( "/controls/starter[0]", false );
+       magnetos = 3;
+       starter = false;
     } else if ( switch_matrix[board][1][1] == 1 ) {
-       fgSetInt( "/controls/magnetos[0]", 2 );
-       fgSetBool( "/controls/starter[0]", false );
+       magnetos = 2;
+       starter = false;
     } else if ( switch_matrix[board][0][1] == 1 ) {
-       fgSetInt( "/controls/magnetos[0]", 1 );
-       fgSetBool( "/controls/starter[0]", false );
+       magnetos = 1;
+       starter = false;
     } else {
-       fgSetInt( "/controls/magnetos[0]", 0 );
-       fgSetBool( "/controls/starter[0]", false );
+       magnetos = 0;
+       starter = false;
+    }
+
+    // do a bit of filtering on the magneto/starter switch and the
+    // flap lever because these are not well debounced in hardware
+    static int mag1, mag2, mag3;
+    mag3 = mag2;
+    mag2 = mag1;
+    mag1 = magnetos;
+    if ( mag1 == mag2 && mag2 == mag3 ) {
+        fgSetInt( "/controls/magnetos[0]", magnetos );
+    }
+    static bool start1, start2, start3;
+    start3 = start2;
+    start2 = start1;
+    start1 = starter;
+    if ( start1 == start2 && start2 == start3 ) {
+        fgSetBool( "/controls/starter[0]", starter );
     }
 
     // flaps
-    if ( switch_matrix[board][6][3] == 1 ) {
-       fgSetFloat( "/controls/flaps", 1.0 );
-    } else if ( switch_matrix[board][5][3] == 1 ) {
-       fgSetFloat( "/controls/flaps", 0.66 );
-    } else if ( switch_matrix[board][4][3] == 1 ) {
-       fgSetFloat( "/controls/flaps", 0.33 );
-    } else if ( switch_matrix[board][4][3] == 0 ) {
-       fgSetFloat( "/controls/flaps", 0.0 );
+    float flaps = 0.0;
+    if ( switch_matrix[board][6][3] ) {
+       flaps = 1.0;
+    } else if ( switch_matrix[board][5][3] ) {
+       flaps = 2.0 / 3.0;
+    } else if ( switch_matrix[board][4][3] ) {
+       flaps = 1.0 / 3.0;
+    } else if ( !switch_matrix[board][4][3] ) {
+       flaps = 0.0;
+    }
+
+    // do a bit of filtering on the magneto/starter switch and the
+    // flap lever because these are not well debounced in hardware
+    static float flap1, flap2, flap3;
+    flap3 = flap2;
+    flap2 = flap1;
+    flap1 = flaps;
+    if ( flap1 == flap2 && flap2 == flap3 ) {
+        fgSetFloat( "/controls/flaps", flaps );
+    }
+
+    // fuel selector (also filtered)
+    int fuel = 0;
+    if ( switch_matrix[board][2][3] ) {
+        // both
+        fuel = 3;
+    } else if ( switch_matrix[board][1][3] ) {
+        // left
+        fuel = 1;
+    } else if ( switch_matrix[board][3][3] ) {
+        // right
+        fuel = 2;
+    } else {
+        // fuel cutoff
+        fuel = 0;
+    }
+
+    static int fuel1, fuel2, fuel3;
+    fuel3 = fuel2;
+    fuel2 = fuel1;
+    fuel1 = fuel;
+    if ( fuel1 == fuel2 && fuel2 == fuel3 ) {
+        fgSetBool( "/controls/fuel-selector[0]", (fuel & 0x01) > 0 );
+        fgSetBool( "/controls/fuel-selector[1]", (fuel & 0x02) > 0 );
     }
 
     return true;
@@ -995,11 +1523,11 @@ bool FGATC610x::do_switches() {
 
 
 bool FGATC610x::process() {
-
     // Lock the hardware, skip if it's not ready yet
     if ( ATC610xLock( lock_fd ) > 0 ) {
 
        do_analog_in();
+       do_lights();
        do_radio_switches();
        do_radio_display();
        do_steppers();