]> git.mxchange.org Git - flightgear.git/blob - src/Network/atc610x.cxx
YASim now supports the new fuel.nas fuel management system. It
[flightgear.git] / src / Network / atc610x.cxx
1 // atc610x.cxx -- FGFS interface to ATC 610x hardware
2 //
3 // Written by Curtis Olson, started January 2002
4 //
5 // Copyright (C) 2002  Curtis L. Olson - curt@flightgear.org
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <simgear/compiler.h>
29
30 #include <stdlib.h>             // atoi() atof() abs()
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <stdio.h>              //snprintf
35 #if defined( _MSC_VER ) || defined(__MINGW32__)
36 #  include <io.h>                 //lseek, read, write
37 #endif
38
39 #include STL_STRING
40
41 #include <plib/ul.h>
42
43 #include <simgear/debug/logstream.hxx>
44 #include <simgear/io/iochannel.hxx>
45 #include <simgear/math/sg_types.hxx>
46 #include <simgear/misc/sg_path.hxx>
47 #include <simgear/props/props.hxx>
48
49 #include <Main/fg_props.hxx>
50 #include <Main/globals.hxx>
51
52 #include "atc610x.hxx"
53
54 SG_USING_STD(string);
55
56
57 // Lock the ATC 610 hardware
58 static int ATC610xLock( int fd ) {
59     // rewind
60     lseek( fd, 0, SEEK_SET );
61
62     char tmp[2];
63     int result = read( fd, tmp, 1 );
64     if ( result != 1 ) {
65         SG_LOG( SG_IO, SG_DEBUG, "Lock failed" );
66     }
67
68     return result;
69 }
70
71
72 // Write a radios command
73 static int ATC610xRelease( int fd ) {
74     // rewind
75     lseek( fd, 0, SEEK_SET );
76
77     char tmp[2];
78     tmp[0] = tmp[1] = 0;
79     int result = write( fd, tmp, 1 );
80
81     if ( result != 1 ) {
82         SG_LOG( SG_IO, SG_DEBUG, "Release failed" );
83     }
84
85     return result;
86 }
87
88
89 // Read analog inputs
90 static void ATC610xReadAnalogInputs( int fd, unsigned char *analog_in_bytes ) {
91     // rewind
92     lseek( fd, 0, SEEK_SET );
93
94     int result = read( fd, analog_in_bytes, ATC_ANAL_IN_BYTES );
95     if ( result != ATC_ANAL_IN_BYTES ) {
96         SG_LOG( SG_IO, SG_ALERT, "Read failed" );
97         exit( -1 );
98     }
99 }
100
101
102 // Write a radios command
103 static int ATC610xSetRadios( int fd,
104                              unsigned char data[ATC_RADIO_DISPLAY_BYTES] )
105 {
106     // rewind
107     lseek( fd, 0, SEEK_SET );
108
109     int result = write( fd, data, ATC_RADIO_DISPLAY_BYTES );
110
111     if ( result != ATC_RADIO_DISPLAY_BYTES ) {
112         SG_LOG( SG_IO, SG_DEBUG, "Write failed" );
113     }
114
115     return result;
116 }
117
118
119 // Read status of last radios written to
120 static void ATC610xReadRadios( int fd, unsigned char *switch_data ) {
121     // rewind
122     lseek( fd, 0, SEEK_SET );
123
124     int result = read( fd, switch_data, ATC_RADIO_SWITCH_BYTES );
125     if ( result != ATC_RADIO_SWITCH_BYTES ) {
126         SG_LOG( SG_IO, SG_ALERT, "Read failed" );
127         exit( -1 );
128     }
129 }
130
131 // Write a stepper command
132 static int ATC610xSetStepper( int fd, unsigned char channel,
133                               unsigned char value )
134 {
135     // rewind
136     lseek( fd, 0, SEEK_SET );
137
138     // Write the value
139     unsigned char buf[3];
140     buf[0] = channel;
141     buf[1] = value;
142     buf[2] = 0;
143     int result = write( fd, buf, 2 );
144     if ( result != 2 ) {
145         SG_LOG( SG_IO, SG_INFO, "Write failed" );
146     }
147     SG_LOG( SG_IO, SG_DEBUG,
148             "Sent cmd = " << (int)channel << " value = " << (int)value );
149     return result;
150 }
151
152
153 // Read status of last stepper written to
154 static unsigned char ATC610xReadStepper( int fd ) {
155     int result;
156
157     // rewind
158     lseek( fd, 0, SEEK_SET );
159
160     // Write the value
161     unsigned char buf[2];
162     result = read( fd, buf, 1 );
163     if ( result != 1 ) {
164         SG_LOG( SG_IO, SG_ALERT, "Read failed" );
165         exit( -1 );
166     }
167     SG_LOG( SG_IO, SG_DEBUG, "Read result = " << (int)buf[0] );
168
169     return buf[0];
170 }
171
172
173 // Read switch inputs
174 static void ATC610xReadSwitches( int fd, unsigned char *switch_bytes ) {
175     // rewind
176     lseek( fd, 0, SEEK_SET );
177
178     int result = read( fd, switch_bytes, ATC_SWITCH_BYTES );
179     if ( result != ATC_SWITCH_BYTES ) {
180         SG_LOG( SG_IO, SG_ALERT, "Read failed" );
181         exit( -1 );
182     }
183 }
184
185
186 // Turn a lamp on or off
187 void ATC610xSetLamp( int fd, int channel, bool value ) {
188     // lamp channels 0-63 are written to LampPort0, channels 64-127
189     // are written to LampPort1
190
191     // bits 0-6 are the lamp address
192     // bit 7 is the value (on/off)
193
194     int result;
195
196     // Write the value
197     unsigned char buf[3];
198     buf[0] = channel;
199     buf[1] = value;
200     buf[2] = 0;
201     result = write( fd, buf, 2 );
202     if ( result != 2 ) {
203         SG_LOG( SG_IO, SG_ALERT,  "Write failed" );
204         exit( -1 );
205     }
206 }
207
208
209 void FGATC610x::init_config() {
210 #if defined( unix ) || defined( __CYGWIN__ )
211     // Next check home directory for .fgfsrc.hostname file
212     char *envp = ::getenv( "HOME" );
213     if ( envp != NULL ) {
214         SGPath atc610x_config( envp );
215         atc610x_config.append( ".fgfs-atc610x.xml" );
216         readProperties( atc610x_config.str(), globals->get_props() );
217     }
218 #endif
219 }
220
221
222 // Open and initialize ATC 610x hardware
223 bool FGATC610x::open() {
224     if ( is_enabled() ) {
225         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
226                 << "is already in use, ignoring" );
227         return false;
228     }
229
230     // This loads the config parameters generated by "simcal"
231     init_config();
232
233     SG_LOG( SG_IO, SG_ALERT,
234             "Initializing ATC 610x hardware, please wait ..." );
235
236     set_hz( 30 );               // default to processing requests @ 30Hz
237     set_enabled( true );
238
239     board = 0;                  // 610x uses a single board number = 0
240
241     snprintf( lock_file, 256, "/proc/atc610x/board%d/lock", board );
242     snprintf( analog_in_file, 256, "/proc/atc610x/board%d/analog_in", board );
243     snprintf( lamps_file, 256, "/proc/atc610x/board%d/lamps", board );
244     snprintf( radios_file, 256, "/proc/atc610x/board%d/radios", board );
245     snprintf( stepper_file, 256, "/proc/atc610x/board%d/steppers", board );
246     snprintf( switches_file, 256, "/proc/atc610x/board%d/switches", board );
247
248     /////////////////////////////////////////////////////////////////////
249     // Open the /proc files
250     /////////////////////////////////////////////////////////////////////
251
252     lock_fd = ::open( lock_file, O_RDWR );
253     if ( lock_fd == -1 ) {
254         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
255         char msg[256];
256         snprintf( msg, 256, "Error opening %s", lock_file );
257         perror( msg );
258         exit( -1 );
259     }
260
261     analog_in_fd = ::open( analog_in_file, O_RDONLY );
262     if ( analog_in_fd == -1 ) {
263         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
264         char msg[256];
265         snprintf( msg, 256, "Error opening %s", analog_in_file );
266         perror( msg );
267         exit( -1 );
268     }
269
270     lamps_fd = ::open( lamps_file, O_WRONLY );
271     if ( lamps_fd == -1 ) {
272         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
273         char msg[256];
274         snprintf( msg, 256, "Error opening %s", lamps_file );
275         perror( msg );
276         exit( -1 );
277     }
278
279     radios_fd = ::open( radios_file, O_RDWR );
280     if ( radios_fd == -1 ) {
281         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
282         char msg[256];
283         snprintf( msg, 256, "Error opening %s", radios_file );
284         perror( msg );
285         exit( -1 );
286     }
287
288     stepper_fd = ::open( stepper_file, O_RDWR );
289     if ( stepper_fd == -1 ) {
290         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
291         char msg[256];
292         snprintf( msg, 256, "Error opening %s", stepper_file );
293         perror( msg );
294         exit( -1 );
295     }
296
297     switches_fd = ::open( switches_file, O_RDONLY );
298     if ( switches_fd == -1 ) {
299         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
300         char msg[256];
301         snprintf( msg, 256, "Error opening %s", switches_file );
302         perror( msg );
303         exit( -1 );
304     }
305
306     /////////////////////////////////////////////////////////////////////
307     // Home the compass stepper motor
308     /////////////////////////////////////////////////////////////////////
309
310     SG_LOG( SG_IO, SG_ALERT,
311             "  - Homing the compass stepper motor" );
312
313     // Lock the hardware, keep trying until we succeed
314     while ( ATC610xLock( lock_fd ) <= 0 );
315
316     // Send the stepper home command
317     ATC610xSetStepper( stepper_fd, ATC_COMPASS_CH, ATC_STEPPER_HOME );
318
319     // Release the hardware
320     ATC610xRelease( lock_fd );
321
322     SG_LOG( SG_IO, SG_ALERT,
323             "  - Waiting for compass to come home." );
324
325     bool home = false;
326     int timeout = 900;          // about 30 seconds
327     timeout = 0;
328     while ( ! home && timeout > 0 ) {
329         if ( timeout % 150 == 0 ) {
330             SG_LOG( SG_IO, SG_INFO, "waiting for compass = " << timeout );
331         } else {
332             SG_LOG( SG_IO, SG_DEBUG, "Checking if compass home ..." );
333         }
334
335         while ( ATC610xLock( lock_fd ) <= 0 );
336
337         unsigned char result = ATC610xReadStepper( stepper_fd );
338         if ( result == 0 ) {
339             home = true;
340         }
341
342         ATC610xRelease( lock_fd );
343
344 #if defined( _MSC_VER )
345         ulMilliSecondSleep(33);
346 #elif defined (WIN32) && !defined(__CYGWIN__)
347         Sleep (33);
348 #else
349         usleep(33);
350 #endif
351
352         --timeout;
353     }
354
355     compass_position = 0.0;
356
357     /////////////////////////////////////////////////////////////////////
358     // Blank the radio display
359     /////////////////////////////////////////////////////////////////////
360
361     SG_LOG( SG_IO, SG_ALERT,
362             "  - Clearing the radios displays." );
363
364     // Prepair the data
365     unsigned char value = 0xff;
366     for ( int channel = 0; channel < ATC_RADIO_DISPLAY_BYTES; ++channel ) {
367         radio_display_data[channel] = value;
368     }
369
370     // Lock the hardware, keep trying until we succeed
371     while ( ATC610xLock( lock_fd ) <= 0 );
372
373     // Set radio display
374     ATC610xSetRadios( radios_fd, radio_display_data );
375
376     ATC610xRelease( lock_fd );
377
378     /////////////////////////////////////////////////////////////////////
379     // Blank the lamps
380     /////////////////////////////////////////////////////////////////////
381
382     for ( int i = 0; i < 128; ++i ) {
383         ATC610xSetLamp( lamps_fd, i, false );
384     }
385
386     /////////////////////////////////////////////////////////////////////
387     // Finished initing hardware
388     /////////////////////////////////////////////////////////////////////
389
390     SG_LOG( SG_IO, SG_ALERT,
391             "Done initializing ATC 610x hardware." );
392
393     /////////////////////////////////////////////////////////////////////
394     // Connect up to property values
395     /////////////////////////////////////////////////////////////////////
396
397     mag_compass = fgGetNode( "/instrumentation/magnetic-compass/indicated-heading-deg", true );
398
399     dme_min = fgGetNode( "/instrumentation/dme/indicated-time-min", true );
400     dme_kt = fgGetNode( "/instrumentation/dme/indicated-ground-speed-kt",
401                         true );
402     dme_nm = fgGetNode( "/instrumentation/dme/indicated-distance-nm", true );
403     dme_in_range = fgGetNode( "/instrumentation/dme/in-range", true );
404
405     adf_bus_power = fgGetNode( "/systems/electrical/outputs/adf", true );
406     dme_bus_power = fgGetNode( "/systems/electrical/outputs/dme", true );
407     navcom1_bus_power = fgGetNode( "/systems/electrical/outputs/navcom[0]",
408                                    true );
409     navcom2_bus_power = fgGetNode( "/systems/electrical/outputs/navcom[1]",
410                                    true );
411     xpdr_bus_power = fgGetNode( "/systems/electrical/outputs/transponder",
412                                  true );
413
414     navcom1_power_btn = fgGetNode( "/radios/comm[0]/inputs/power-btn", true );
415     navcom2_power_btn = fgGetNode( "/radios/comm[1]/inputs/power-btn", true );
416
417     com1_freq = fgGetNode( "/radios/comm[0]/frequencies/selected-mhz", true );
418     com1_stby_freq
419         = fgGetNode( "/radios/comm[0]/frequencies/standby-mhz", true );
420
421     com2_freq = fgGetNode( "/radios/comm[1]/frequencies/selected-mhz", true );
422     com2_stby_freq
423         = fgGetNode( "/radios/comm[1]/frequencies/standby-mhz", true );
424
425     nav1_freq = fgGetNode( "/radios/nav[0]/frequencies/selected-mhz", true );
426     nav1_stby_freq
427         = fgGetNode( "/radios/nav[0]/frequencies/standby-mhz", true );
428     nav1_obs = fgGetNode( "/radios/nav[0]/radials/selected-deg", true );
429
430     nav2_freq = fgGetNode( "/radios/nav[1]/frequencies/selected-mhz", true );
431     nav2_stby_freq
432         = fgGetNode( "/radios/nav[1]/frequencies/standby-mhz", true );
433     nav2_obs = fgGetNode( "/radios/nav[1]/radials/selected-deg", true );
434
435     adf_power_btn = fgGetNode( "/radios/kr-87/inputs/power-btn", true );
436     adf_vol = fgGetNode( "/radios/kr-87/inputs/volume", true );
437     adf_adf_btn = fgGetNode( "/radios/kr-87/inputs/adf-btn", true );
438     adf_bfo_btn = fgGetNode( "/radios/kr-87/inputs/bfo-btn", true );
439     adf_freq = fgGetNode( "/radios/kr-87/outputs/selected-khz", true );
440     adf_stby_freq = fgGetNode( "/radios/kr-87/outputs/standby-khz", true );
441     adf_stby_mode = fgGetNode( "/radios/kr-87/modes/stby", true );
442     adf_timer_mode = fgGetNode( "/radios/kr-87/modes/timer", true );
443     adf_count_mode = fgGetNode( "/radios/kr-87/modes/count", true );
444     adf_flight_timer = fgGetNode( "/radios/kr-87/outputs/flight-timer", true );
445     adf_elapsed_timer = fgGetNode( "/radios/kr-87/outputs/elapsed-timer",
446                                    true );
447     adf_ant_ann = fgGetNode( "/radios/kr-87/annunciators/ant", true );
448     adf_adf_ann = fgGetNode( "/radios/kr-87/annunciators/adf", true );
449     adf_bfo_ann = fgGetNode( "/radios/kr-87/annunciators/bfo", true );
450     adf_frq_ann = fgGetNode( "/radios/kr-87/annunciators/frq", true );
451     adf_flt_ann = fgGetNode( "/radios/kr-87/annunciators/flt", true );
452     adf_et_ann = fgGetNode( "/radios/kr-87/annunciators/et", true );
453
454     inner = fgGetNode( "/radios/marker-beacon/inner", true );
455     middle = fgGetNode( "/radios/marker-beacon/middle", true );
456     outer = fgGetNode( "/radios/marker-beacon/outer", true );
457
458     xpdr_ident_btn = fgGetNode( "/radios/kt-70/inputs/ident-btn", true );
459     xpdr_digit1 = fgGetNode( "/radios/kt-70/inputs/digit1", true );
460     xpdr_digit2 = fgGetNode( "/radios/kt-70/inputs/digit2", true );
461     xpdr_digit3 = fgGetNode( "/radios/kt-70/inputs/digit3", true );
462     xpdr_digit4 = fgGetNode( "/radios/kt-70/inputs/digit4", true );
463     xpdr_func_knob = fgGetNode( "/radios/kt-70/inputs/func-knob", true );
464     xpdr_id_code = fgGetNode( "/radios/kt-70/outputs/id-code", true );
465     xpdr_flight_level = fgGetNode( "/radios/kt-70/outputs/flight-level", true );
466     xpdr_fl_ann = fgGetNode( "/radios/kt-70/annunciators/fl", true );
467     xpdr_alt_ann = fgGetNode( "/radios/kt-70/annunciators/alt", true );
468     xpdr_gnd_ann = fgGetNode( "/radios/kt-70/annunciators/gnd", true );
469     xpdr_on_ann = fgGetNode( "/radios/kt-70/annunciators/on", true );
470     xpdr_sby_ann = fgGetNode( "/radios/kt-70/annunciators/sby", true );
471     xpdr_reply_ann = fgGetNode( "/radios/kt-70/annunciators/reply", true );
472
473     ati_bird
474       = fgGetNode( "/instrumentation/attitude-indicator/horizon-offset-deg",
475                    true );
476     alt_press = fgGetNode( "/instrumentation/altimeter/setting-inhg", true );
477     adf_hdg = fgGetNode( "/radios/kr-87/inputs/rotation-deg", true );
478     hdg_bug = fgGetNode( "/autopilot/settings/heading-bug-deg", true );
479
480     elevator_center = fgGetNode( "/input/atc610x/elevator/center", true );
481     elevator_min = fgGetNode( "/input/atc610x/elevator/min", true );
482     elevator_max = fgGetNode( "/input/atc610x/elevator/max", true );
483
484     ailerons_center = fgGetNode( "/input/atc610x/ailerons/center", true );
485     ailerons_min = fgGetNode( "/input/atc610x/ailerons/min", true );
486     ailerons_max = fgGetNode( "/input/atc610x/ailerons/max", true );
487
488     rudder_center = fgGetNode( "/input/atc610x/rudder/center", true );
489     rudder_min = fgGetNode( "/input/atc610x/rudder/min", true );
490     rudder_max = fgGetNode( "/input/atc610x/rudder/max", true );
491
492     brake_left_min = fgGetNode( "/input/atc610x/brake-left/min", true );
493     brake_left_max = fgGetNode( "/input/atc610x/brake-left/max", true );
494
495     brake_right_min = fgGetNode( "/input/atc610x/brake-right/min", true );
496     brake_right_max = fgGetNode( "/input/atc610x/brake-right/max", true );
497
498     throttle_min = fgGetNode( "/input/atc610x/throttle/min", true );
499     throttle_max = fgGetNode( "/input/atc610x/throttle/max", true );
500
501     mixture_min = fgGetNode( "/input/atc610x/mixture/min", true );
502     mixture_max = fgGetNode( "/input/atc610x/mixture/max", true );
503
504     trim_center = fgGetNode( "/input/atc610x/trim/center", true );
505     trim_min = fgGetNode( "/input/atc610x/trim/min", true );
506     trim_max = fgGetNode( "/input/atc610x/trim/max", true );
507
508     nav1vol_min = fgGetNode( "/input/atc610x/nav1vol/min", true );
509     nav1vol_max = fgGetNode( "/input/atc610x/nav1vol/max", true );
510
511     nav2vol_min = fgGetNode( "/input/atc610x/nav2vol/min", true );
512     nav2vol_max = fgGetNode( "/input/atc610x/nav2vol/max", true );
513
514     ignore_flight_controls
515         = fgGetNode( "/input/atc610x/ignore-flight-controls", true );
516
517     comm1_serviceable = fgGetNode( "/instrumentation/comm[0]/serviceable", true );
518     comm2_serviceable = fgGetNode( "/instrumentation/comm[1]/serviceable", true );
519     nav1_serviceable = fgGetNode( "/instrumentation/nav[0]/serviceable", true );
520     nav2_serviceable = fgGetNode( "/instrumentation/nav[1]/serviceable", true );
521     adf_serviceable = fgGetNode( "/instrumentation/adf/serviceable", true );
522     xpdr_serviceable = fgGetNode( "/radios/kt-70/inputs/serviceable",
523                                  true );
524     dme_serviceable = fgGetNode( "/instrumentation/dme/serviceable", true );
525
526     // default to having everything serviceable
527     comm1_serviceable->setBoolValue( true );
528     comm2_serviceable->setBoolValue( true );
529     nav1_serviceable->setBoolValue( true );
530     nav2_serviceable->setBoolValue( true );
531     adf_serviceable->setBoolValue( true );
532     xpdr_serviceable->setBoolValue( true );
533     dme_serviceable->setBoolValue( true );
534
535     return true;
536 }
537
538
539 /////////////////////////////////////////////////////////////////////
540 // Read analog inputs
541 /////////////////////////////////////////////////////////////////////
542
543 // scale a number between min and max (with center defined) to a scale
544 // from -1.0 to 1.0
545 static double scale( int center, int min, int max, int value ) {
546     // cout << center << " " << min << " " << max << " " << value << " ";
547     double result;
548     double range;
549
550     if ( value <= center ) {
551         range = center - min;
552         result = (value - center) / range;
553     } else {
554         range = max - center;
555         result = (value - center) / range;            
556     }
557
558     if ( result < -1.0 ) result = -1.0;
559     if ( result > 1.0 ) result = 1.0;
560
561     // cout << result << endl;
562
563     return result;
564 }
565
566
567 // scale a number between min and max to a scale from 0.0 to 1.0
568 static double scale( int min, int max, int value ) {
569     // cout << center << " " << min << " " << max << " " << value << " ";
570     double result;
571     double range;
572
573     range = max - min;
574     result = (value - min) / range;
575
576     if ( result < 0.0 ) result = 0.0;
577     if ( result > 1.0 ) result = 1.0;
578
579     // cout << result << endl;
580
581     return result;
582 }
583
584
585 static int tony_magic( int raw, int obs[3] ) {
586     int result = 0;
587
588     obs[0] = raw;
589
590     if ( obs[1] < 30 ) {
591         if ( obs[2] >= 68 && obs[2] < 480 ) {
592             result = -6;
593         } else if ( obs[2] >= 480 ) {
594             result = 6;
595         }
596         obs[2] = obs[1];
597         obs[1] = obs[0];
598     } else if ( obs[1] < 68 ) {
599         // do nothing
600         obs[1] = obs[0];
601     } else if ( obs[2] < 30 ) {
602         if ( obs[1] >= 68 && obs[1] < 480 ) {
603             result = 6;
604             obs[2] = obs[1];
605             obs[1] = obs[0];
606         } else if ( obs[1] >= 480 ) {
607             result = -6;
608             if ( obs[0] < obs[1] ) {
609                 obs[2] = obs[1];
610                 obs[1] = obs[0];
611             } else {
612                 obs[2] = obs[0];
613                 obs[1] = obs[0];
614             }
615         }
616     } else if ( obs[1] > 980 ) {
617         if ( obs[2] <= 956 && obs[2] > 480 ) {
618             result = 6;
619         } else if ( obs[2] <= 480 ) {
620             result = -6;
621         }
622         obs[2] = obs[1];
623         obs[1] = obs[0];
624     } else if ( obs[1] > 956 ) {
625         // do nothing
626         obs[1] = obs[0];
627     } else if ( obs[2] > 980 ) {
628         if ( obs[1] <= 956 && obs[1] > 480 ) {
629             result = -6;
630             obs[2] = obs[1];
631             obs[1] = obs[0];
632         } else if ( obs[1] <= 480 ) {
633             result = 6;
634             if ( obs[0] > obs[1] ) {
635                 obs[2] = obs[1];
636                 obs[1] = obs[0];
637             } else {
638                 obs[2] = obs[0];
639                 obs[1] = obs[0];
640             }
641         }
642     } else {
643         if ( obs[1] < 480 && obs[2] > 480 ) {
644             // crossed gap going up
645             if ( obs[0] < obs[1] ) {
646                 // caught a bogus intermediate value coming out of the gap
647                 obs[1] = obs[0];
648             }
649         } else if ( obs[1] > 480 && obs[2] < 480 ) {
650             // crossed gap going down
651             if ( obs[0] > obs[1] ) {
652                 // caught a bogus intermediate value coming out of the gap
653               obs[1] = obs[0];
654             }
655         } else if ( obs[0] > 480 && obs[1] < 480 && obs[2] < 480 ) {
656             // crossed the gap going down
657             if ( obs[1] > obs[2] ) {
658                 // caught a bogus intermediate value coming out of the gap
659                 obs[1] = obs[2];
660             }
661         } else if ( obs[0] < 480 && obs[1] > 480 && obs[2] > 480 ) {
662             // crossed the gap going up
663             if ( obs[1] < obs[2] ) {
664                 // caught a bogus intermediate value coming out of the gap
665                 obs[1] = obs[2];
666             }
667         }
668         result = obs[1] - obs[2];
669         if ( abs(result) > 400 ) {
670             // ignore
671             result = 0;
672         }
673         obs[2] = obs[1];
674         obs[1] = obs[0];
675     }
676
677     // cout << " result = " << result << endl;
678     if ( result < -500 ) { result += 1024; }
679     if ( result > 500 ) { result -= 1024; }
680
681     return result;
682 }
683
684
685 static double instr_pot_filter( double ave, double val ) {
686     if ( fabs(ave - val) < 400 || fabs(val) < fabs(ave) ) {
687         return 0.5 * ave + 0.5 * val;
688     } else {
689         return ave;
690     }
691 }
692
693
694 bool FGATC610x::do_analog_in() {
695     // Read raw data in byte form
696     ATC610xReadAnalogInputs( analog_in_fd, analog_in_bytes );
697
698     // Convert to integer values
699     for ( int channel = 0; channel < ATC_ANAL_IN_VALUES; ++channel ) {
700         unsigned char hi = analog_in_bytes[2 * channel] & 0x03;
701         unsigned char lo = analog_in_bytes[2 * channel + 1];
702         analog_in_data[channel] = hi * 256 + lo;
703
704         // printf("%02x %02x ", hi, lo );
705         // printf("%04d ", value );
706     }
707
708     float tmp;
709
710     if ( !ignore_flight_controls->getBoolValue() ) {
711         // aileron
712         tmp = scale( ailerons_center->getIntValue(),
713                      ailerons_min->getIntValue(),
714                      ailerons_max->getIntValue(), analog_in_data[0] );
715         fgSetFloat( "/controls/flight/aileron", tmp );
716         // cout << "aileron = " << analog_in_data[0] << " = " << tmp;
717
718         // elevator
719         tmp = -scale( elevator_center->getIntValue(),
720                       elevator_min->getIntValue(),
721                       elevator_max->getIntValue(), analog_in_data[5] );
722         fgSetFloat( "/controls/flight/elevator", tmp );
723         // cout << "trim = " << analog_in_data[4] << " = " << tmp;
724
725         // elevator trim
726         tmp = scale( trim_center->getIntValue(), trim_min->getIntValue(),
727                      trim_max->getIntValue(), analog_in_data[4] );
728         fgSetFloat( "/controls/flight/elevator-trim", tmp );
729         // cout << " elev = " << analog_in_data[5] << " = " << tmp << endl;
730
731         // mixture
732         tmp = scale( mixture_min->getIntValue(), mixture_max->getIntValue(),
733                      analog_in_data[6] );
734         fgSetFloat( "/controls/engines/engine[0]/mixture", tmp );
735         fgSetFloat( "/controls/engines/engine[1]/mixture", tmp );
736
737         // throttle
738         tmp = scale( throttle_min->getIntValue(), throttle_max->getIntValue(),
739                      analog_in_data[8] );
740         fgSetFloat( "/controls/engines/engine[0]/throttle", tmp );
741         fgSetFloat( "/controls/engines/engine[1]/throttle", tmp );
742         // cout << "throttle = " << tmp << endl;
743
744         if ( use_rudder ) {
745             // rudder
746             tmp = scale( rudder_center->getIntValue(),
747                          rudder_min->getIntValue(),
748                          rudder_max->getIntValue(), analog_in_data[10] );
749             fgSetFloat( "/controls/flight/rudder", -tmp );
750
751             // toe brakes
752             tmp = scale( brake_left_min->getIntValue(),
753                          brake_left_max->getIntValue(),
754                          analog_in_data[20] );
755             fgSetFloat( "/controls/gear/brake-left", tmp );
756             tmp = scale( brake_right_min->getIntValue(),
757                          brake_right_max->getIntValue(),
758                          analog_in_data[21] );
759             fgSetFloat( "/controls/gear/brake-right", tmp );
760         }
761     }
762
763     // nav1 volume
764     tmp = (float)analog_in_data[25] / 1024.0f;
765     fgSetFloat( "/radios/nav[0]/volume", tmp );
766
767     // nav2 volume
768     tmp = (float)analog_in_data[24] / 1024.0f;
769     fgSetFloat( "/radios/nav[1]/volume", tmp );
770
771     // adf volume
772     tmp = (float)analog_in_data[26] / 1024.0f;
773     fgSetFloat( "/radios/kr-87/inputs/volume", tmp );
774
775     // instrument panel pots
776     static bool first = true;
777     static int obs1[3], obs2[3], obs3[3], obs4[3], obs5[3], obs6[3];
778     static double diff1_ave = 0.0;
779     static double diff2_ave = 0.0;
780     static double diff3_ave = 0.0;
781     static double diff4_ave = 0.0;
782     static double diff5_ave = 0.0;
783     static double diff6_ave = 0.0;
784
785     if ( first ) {
786         first = false;
787         obs1[0] = obs1[1] = obs1[2] = analog_in_data[11];
788         obs2[0] = obs2[1] = obs2[2] = analog_in_data[28];
789         obs3[0] = obs3[1] = obs3[2] = analog_in_data[29];
790         obs4[0] = obs4[1] = obs4[2] = analog_in_data[30];
791         obs5[0] = obs5[1] = obs5[2] = analog_in_data[31];
792         obs6[0] = obs6[1] = obs6[2] = analog_in_data[14];
793     }
794
795     int diff1 = tony_magic( analog_in_data[11], obs1 );
796     int diff2 = tony_magic( analog_in_data[28], obs2 );
797     int diff3 = tony_magic( analog_in_data[29], obs3 );
798     int diff4 = tony_magic( analog_in_data[30], obs4 );
799     int diff5 = tony_magic( analog_in_data[31], obs5 );
800     int diff6 = tony_magic( analog_in_data[14], obs6 );
801
802     diff1_ave = instr_pot_filter( diff1_ave, diff1 );
803     diff2_ave = instr_pot_filter( diff2_ave, diff2 );
804     diff3_ave = instr_pot_filter( diff3_ave, diff3 );
805     diff4_ave = instr_pot_filter( diff4_ave, diff4 );
806     diff5_ave = instr_pot_filter( diff5_ave, diff5 );
807     diff6_ave = instr_pot_filter( diff6_ave, diff6 );
808
809     tmp = alt_press->getDoubleValue() + (diff1_ave * (0.25/888.0) );
810     if ( tmp < 27.9 ) { tmp = 27.9; }
811     if ( tmp > 31.4 ) { tmp = 31.4; }
812     fgSetFloat( "/instrumentation/altimeter/setting-inhg", tmp );
813
814     tmp = ati_bird->getDoubleValue() + (diff2_ave * (20.0/888.0) );
815     if ( tmp < -10.0 ) { tmp = -10.0; }
816     if ( tmp > 10.0 ) { tmp = 10.0; }
817     fgSetFloat( "/instrumentation/attitude-indicator/horizon-offset-deg", tmp );
818
819     tmp = nav1_obs->getDoubleValue() + (diff3_ave * (72.0/888.0) );
820     while ( tmp >= 360.0 ) { tmp -= 360.0; }
821     while ( tmp < 0.0 ) { tmp += 360.0; }
822     // cout << " obs = " << tmp << endl;
823     fgSetFloat( "/radios/nav[0]/radials/selected-deg", tmp );
824
825     tmp = nav2_obs->getDoubleValue() + (diff4_ave * (72.0/888.0) );
826     while ( tmp >= 360.0 ) { tmp -= 360.0; }
827     while ( tmp < 0.0 ) { tmp += 360.0; }
828     // cout << " obs = " << tmp << endl;
829     fgSetFloat( "/radios/nav[1]/radials/selected-deg", tmp );
830
831     tmp = adf_hdg->getDoubleValue() + (diff5_ave * (72.0/888.0) );
832     while ( tmp >= 360.0 ) { tmp -= 360.0; }
833     while ( tmp < 0.0 ) { tmp += 360.0; }
834     // cout << " obs = " << tmp << endl;
835     fgSetFloat( "/radios/kr-87/inputs/rotation-deg", tmp );
836
837     tmp = hdg_bug->getDoubleValue() + (diff6_ave * (72.0/888.0) );
838     while ( tmp >= 360.0 ) { tmp -= 360.0; }
839     while ( tmp < 0.0 ) { tmp += 360.0; }
840     // cout << " obs = " << tmp << endl;
841     fgSetFloat( "/autopilot/settings/heading-bug-deg", tmp );
842
843     return true;
844 }
845
846
847 /////////////////////////////////////////////////////////////////////
848 // Write the lights
849 /////////////////////////////////////////////////////////////////////
850
851 bool FGATC610x::do_lights() {
852
853     // Marker beacons
854     ATC610xSetLamp( lamps_fd, 4, inner->getBoolValue() );
855     ATC610xSetLamp( lamps_fd, 5, middle->getBoolValue() );
856     ATC610xSetLamp( lamps_fd, 3, outer->getBoolValue() );
857
858     // ADF annunciators
859     ATC610xSetLamp( lamps_fd, 11, adf_ant_ann->getBoolValue() ); // ANT
860     ATC610xSetLamp( lamps_fd, 12, adf_adf_ann->getBoolValue() ); // ADF
861     ATC610xSetLamp( lamps_fd, 13, adf_bfo_ann->getBoolValue() ); // BFO
862     ATC610xSetLamp( lamps_fd, 14, adf_frq_ann->getBoolValue() ); // FRQ
863     ATC610xSetLamp( lamps_fd, 15, adf_flt_ann->getBoolValue() ); // FLT
864     ATC610xSetLamp( lamps_fd, 16, adf_et_ann->getBoolValue() ); // ET
865
866     // Transponder annunciators
867     ATC610xSetLamp( lamps_fd, 17, xpdr_fl_ann->getBoolValue() ); // FL
868     ATC610xSetLamp( lamps_fd, 18, xpdr_alt_ann->getBoolValue() ); // ALT
869     ATC610xSetLamp( lamps_fd, 19, xpdr_gnd_ann->getBoolValue() ); // GND
870     ATC610xSetLamp( lamps_fd, 20, xpdr_on_ann->getBoolValue() ); // ON
871     ATC610xSetLamp( lamps_fd, 21, xpdr_sby_ann->getBoolValue() ); // SBY
872     ATC610xSetLamp( lamps_fd, 22, xpdr_reply_ann->getBoolValue() ); // R
873
874     return true;
875 }
876
877
878 /////////////////////////////////////////////////////////////////////
879 // Read radio switches 
880 /////////////////////////////////////////////////////////////////////
881
882 bool FGATC610x::do_radio_switches() {
883     double freq, coarse_freq, fine_freq, value;
884     int diff;
885
886     ATC610xReadRadios( radios_fd, radio_switch_data );
887
888     // DME Switch
889     dme_switch = (radio_switch_data[7] >> 4) & 0x03;
890     if ( dme_switch == 0 ) {
891         // off
892         fgSetInt( "/instrumentation/dme/switch-position", 0 );
893     } else if ( dme_switch == 2 ) {
894         // nav1
895         fgSetInt( "/instrumentation/dme/switch-position", 1 );
896         fgSetString( "/instrumentation/dme/frequencies/source",
897                      "/radios/nav[0]/frequencies/selected-mhz" );
898         freq = fgGetFloat( "/radios/nav[0]/frequencies/selected-mhz", true );
899         fgSetFloat( "/instrumentation/dme/frequencies/selected-mhz", freq );
900     } else if ( dme_switch == 1 ) {
901         // nav2
902         fgSetInt( "/instrumentation/dme/switch-position", 3 );
903         fgSetString( "/instrumentation/dme/frequencies/source",
904                      "/radios/nav[1]/frequencies/selected-mhz" );
905         freq = fgGetFloat( "/radios/nav[1]/frequencies/selected-mhz", true );
906         fgSetFloat( "/instrumentation/dme/frequencies/selected-mhz", freq );
907     }
908
909     // NavCom1 Power
910     fgSetBool( "/radios/comm[0]/inputs/power-btn",
911                radio_switch_data[7] & 0x01 );
912
913     if ( navcom1_has_power() && comm1_serviceable->getBoolValue() ) {
914         // Com1 Swap
915         int com1_swap = !((radio_switch_data[7] >> 1) & 0x01);
916         static int last_com1_swap;
917         if ( com1_swap && (last_com1_swap != com1_swap) ) {
918             float tmp = com1_freq->getFloatValue();
919             fgSetFloat( "/radios/comm[0]/frequencies/selected-mhz",
920                         com1_stby_freq->getFloatValue() );
921             fgSetFloat( "/radios/comm[0]/frequencies/standby-mhz", tmp );
922         }
923         last_com1_swap = com1_swap;
924     }
925
926     // NavCom2 Power
927     fgSetBool( "/radios/comm[1]/inputs/power-btn",
928                radio_switch_data[15] & 0x01 );
929
930     if ( navcom2_has_power() && comm2_serviceable->getBoolValue() ) {
931         // Com2 Swap
932         int com2_swap = !((radio_switch_data[15] >> 1) & 0x01);
933         static int last_com2_swap;
934         if ( com2_swap && (last_com2_swap != com2_swap) ) {
935             float tmp = com2_freq->getFloatValue();
936             fgSetFloat( "/radios/comm[1]/frequencies/selected-mhz",
937                         com2_stby_freq->getFloatValue() );
938             fgSetFloat( "/radios/comm[1]/frequencies/standby-mhz", tmp );
939         }
940         last_com2_swap = com2_swap;
941     }
942
943     if ( navcom1_has_power() && nav1_serviceable->getBoolValue() ) {
944         // Nav1 Swap
945         int nav1_swap = radio_switch_data[11] & 0x01;
946         static int last_nav1_swap;
947         if ( nav1_swap && (last_nav1_swap != nav1_swap) ) {
948             float tmp = nav1_freq->getFloatValue();
949             fgSetFloat( "/radios/nav[0]/frequencies/selected-mhz",
950                         nav1_stby_freq->getFloatValue() );
951             fgSetFloat( "/radios/nav[0]/frequencies/standby-mhz", tmp );
952         }
953         last_nav1_swap = nav1_swap;
954     }
955
956     if ( navcom2_has_power() && nav2_serviceable->getBoolValue() ) {
957         // Nav2 Swap
958         int nav2_swap = !(radio_switch_data[19] & 0x01);
959         static int last_nav2_swap;
960         if ( nav2_swap && (last_nav2_swap != nav2_swap) ) {
961             float tmp = nav2_freq->getFloatValue();
962             fgSetFloat( "/radios/nav[1]/frequencies/selected-mhz",
963                         nav2_stby_freq->getFloatValue() );
964             fgSetFloat( "/radios/nav[1]/frequencies/standby-mhz", tmp );
965         }
966         last_nav2_swap = nav2_swap;
967     }
968
969     if ( navcom1_has_power() && comm1_serviceable->getBoolValue() ) {
970         // Com1 Tuner
971         int com1_tuner_fine = ((radio_switch_data[5] >> 4) & 0x0f) - 1;
972         int com1_tuner_coarse = (radio_switch_data[5] & 0x0f) - 1;
973         static int last_com1_tuner_fine = com1_tuner_fine;
974         static int last_com1_tuner_coarse = com1_tuner_coarse;
975
976         freq = com1_stby_freq->getFloatValue();
977         coarse_freq = (int)freq;
978         fine_freq = (int)((freq - coarse_freq) * 40 + 0.5);
979
980         if ( com1_tuner_fine != last_com1_tuner_fine ) {
981             diff = com1_tuner_fine - last_com1_tuner_fine;
982             if ( abs(diff) > 4 ) {
983                 // roll over
984                 if ( com1_tuner_fine < last_com1_tuner_fine ) {
985                     // going up
986                     diff = 12 - last_com1_tuner_fine + com1_tuner_fine;
987                 } else {
988                     // going down
989                     diff = com1_tuner_fine - 12 - last_com1_tuner_fine;
990                 }
991             }
992             fine_freq += diff;
993         }
994         while ( fine_freq >= 40.0 ) { fine_freq -= 40.0; }
995         while ( fine_freq < 0.0 )  { fine_freq += 40.0; }
996
997         if ( com1_tuner_coarse != last_com1_tuner_coarse ) {
998             diff = com1_tuner_coarse - last_com1_tuner_coarse;
999             if ( abs(diff) > 4 ) {
1000                 // roll over
1001                 if ( com1_tuner_coarse < last_com1_tuner_coarse ) {
1002                     // going up
1003                     diff = 12 - last_com1_tuner_coarse + com1_tuner_coarse;
1004                 } else {
1005                     // going down
1006                     diff = com1_tuner_coarse - 12 - last_com1_tuner_coarse;
1007                 }
1008             }
1009             coarse_freq += diff;
1010         }
1011         if ( coarse_freq < 118.0 ) { coarse_freq += 19.0; }
1012         if ( coarse_freq > 136.0 ) { coarse_freq -= 19.0; }
1013
1014         last_com1_tuner_fine = com1_tuner_fine;
1015         last_com1_tuner_coarse = com1_tuner_coarse;
1016
1017         fgSetFloat( "/radios/comm[0]/frequencies/standby-mhz", 
1018                     coarse_freq + fine_freq / 40.0 );
1019     }
1020
1021     if ( navcom2_has_power() && comm2_serviceable->getBoolValue() ) {
1022         // Com2 Tuner
1023         int com2_tuner_fine = ((radio_switch_data[13] >> 4) & 0x0f) - 1;
1024         int com2_tuner_coarse = (radio_switch_data[13] & 0x0f) - 1;
1025         static int last_com2_tuner_fine = com2_tuner_fine;
1026         static int last_com2_tuner_coarse = com2_tuner_coarse;
1027
1028         freq = com2_stby_freq->getFloatValue();
1029         coarse_freq = (int)freq;
1030         fine_freq = (int)((freq - coarse_freq) * 40 + 0.5);
1031
1032         if ( com2_tuner_fine != last_com2_tuner_fine ) {
1033             diff = com2_tuner_fine - last_com2_tuner_fine;
1034             if ( abs(diff) > 4 ) {
1035                 // roll over
1036                 if ( com2_tuner_fine < last_com2_tuner_fine ) {
1037                     // going up
1038                     diff = 12 - last_com2_tuner_fine + com2_tuner_fine;
1039                 } else {
1040                     // going down
1041                     diff = com2_tuner_fine - 12 - last_com2_tuner_fine;
1042                 }
1043             }
1044             fine_freq += diff;
1045         }
1046         while ( fine_freq >= 40.0 ) { fine_freq -= 40.0; }
1047         while ( fine_freq < 0.0 )  { fine_freq += 40.0; }
1048
1049         if ( com2_tuner_coarse != last_com2_tuner_coarse ) {
1050             diff = com2_tuner_coarse - last_com2_tuner_coarse;
1051             if ( abs(diff) > 4 ) {
1052                 // roll over
1053                 if ( com2_tuner_coarse < last_com2_tuner_coarse ) {
1054                     // going up
1055                     diff = 12 - last_com2_tuner_coarse + com2_tuner_coarse;
1056                 } else {
1057                     // going down
1058                     diff = com2_tuner_coarse - 12 - last_com2_tuner_coarse;
1059                 }
1060             }
1061             coarse_freq += diff;
1062         }
1063         if ( coarse_freq < 118.0 ) { coarse_freq += 19.0; }
1064         if ( coarse_freq > 136.0 ) { coarse_freq -= 19.0; }
1065
1066         last_com2_tuner_fine = com2_tuner_fine;
1067         last_com2_tuner_coarse = com2_tuner_coarse;
1068
1069         fgSetFloat( "/radios/comm[1]/frequencies/standby-mhz",
1070                     coarse_freq + fine_freq / 40.0 );
1071     }
1072
1073     if ( navcom1_has_power() && nav1_serviceable->getBoolValue() ) {
1074         // Nav1 Tuner
1075         int nav1_tuner_fine = ((radio_switch_data[9] >> 4) & 0x0f) - 1;
1076         int nav1_tuner_coarse = (radio_switch_data[9] & 0x0f) - 1;
1077         static int last_nav1_tuner_fine = nav1_tuner_fine;
1078         static int last_nav1_tuner_coarse = nav1_tuner_coarse;
1079
1080         freq = nav1_stby_freq->getFloatValue();
1081         coarse_freq = (int)freq;
1082         fine_freq = (int)((freq - coarse_freq) * 20 + 0.5);
1083
1084         if ( nav1_tuner_fine != last_nav1_tuner_fine ) {
1085             diff = nav1_tuner_fine - last_nav1_tuner_fine;
1086             if ( abs(diff) > 4 ) {
1087                 // roll over
1088                 if ( nav1_tuner_fine < last_nav1_tuner_fine ) {
1089                     // going up
1090                     diff = 12 - last_nav1_tuner_fine + nav1_tuner_fine;
1091                 } else {
1092                     // going down
1093                     diff = nav1_tuner_fine - 12 - last_nav1_tuner_fine;
1094                 }
1095             }
1096             fine_freq += diff;
1097         }
1098         while ( fine_freq >= 20.0 ) { fine_freq -= 20.0; }
1099         while ( fine_freq < 0.0 )  { fine_freq += 20.0; }
1100
1101         if ( nav1_tuner_coarse != last_nav1_tuner_coarse ) {
1102             diff = nav1_tuner_coarse - last_nav1_tuner_coarse;
1103             if ( abs(diff) > 4 ) {
1104                 // roll over
1105                 if ( nav1_tuner_coarse < last_nav1_tuner_coarse ) {
1106                     // going up
1107                     diff = 12 - last_nav1_tuner_coarse + nav1_tuner_coarse;
1108                 } else {
1109                     // going down
1110                     diff = nav1_tuner_coarse - 12 - last_nav1_tuner_coarse;
1111                 }
1112             }
1113             coarse_freq += diff;
1114         }
1115         if ( coarse_freq < 108.0 ) { coarse_freq += 10.0; }
1116         if ( coarse_freq > 117.0 ) { coarse_freq -= 10.0; }
1117
1118         last_nav1_tuner_fine = nav1_tuner_fine;
1119         last_nav1_tuner_coarse = nav1_tuner_coarse;
1120
1121         fgSetFloat( "/radios/nav[0]/frequencies/standby-mhz",
1122                     coarse_freq + fine_freq / 20.0 );
1123     }
1124
1125     if ( navcom2_has_power() && nav2_serviceable->getBoolValue() ) {
1126         // Nav2 Tuner
1127         int nav2_tuner_fine = ((radio_switch_data[17] >> 4) & 0x0f) - 1;
1128         int nav2_tuner_coarse = (radio_switch_data[17] & 0x0f) - 1;
1129         static int last_nav2_tuner_fine = nav2_tuner_fine;
1130         static int last_nav2_tuner_coarse = nav2_tuner_coarse;
1131
1132         freq = nav2_stby_freq->getFloatValue();
1133         coarse_freq = (int)freq;
1134         fine_freq = (int)((freq - coarse_freq) * 20 + 0.5);
1135
1136         if ( nav2_tuner_fine != last_nav2_tuner_fine ) {
1137             diff = nav2_tuner_fine - last_nav2_tuner_fine;
1138             if ( abs(diff) > 4 ) {
1139                 // roll over
1140                 if ( nav2_tuner_fine < last_nav2_tuner_fine ) {
1141                     // going up
1142                     diff = 12 - last_nav2_tuner_fine + nav2_tuner_fine;
1143                 } else {
1144                     // going down
1145                     diff = nav2_tuner_fine - 12 - last_nav2_tuner_fine;
1146                 }
1147             }
1148             fine_freq += diff;
1149         }
1150         while ( fine_freq >= 20.0 ) { fine_freq -= 20.0; }
1151         while ( fine_freq < 0.0 )  { fine_freq += 20.0; }
1152
1153         if ( nav2_tuner_coarse != last_nav2_tuner_coarse ) {
1154             diff = nav2_tuner_coarse - last_nav2_tuner_coarse;
1155             if ( abs(diff) > 4 ) {
1156                 // roll over
1157                 if ( nav2_tuner_coarse < last_nav2_tuner_coarse ) {
1158                     // going up
1159                     diff = 12 - last_nav2_tuner_coarse + nav2_tuner_coarse;
1160                 } else {
1161                     // going down
1162                     diff = nav2_tuner_coarse - 12 - last_nav2_tuner_coarse;
1163                 }
1164             }
1165             coarse_freq += diff;
1166         }
1167         if ( coarse_freq < 108.0 ) { coarse_freq += 10.0; }
1168         if ( coarse_freq > 117.0 ) { coarse_freq -= 10.0; }
1169
1170         last_nav2_tuner_fine = nav2_tuner_fine;
1171         last_nav2_tuner_coarse = nav2_tuner_coarse;
1172
1173         fgSetFloat( "/radios/nav[1]/frequencies/standby-mhz", 
1174                     coarse_freq + fine_freq / 20.0);
1175     }
1176
1177     // ADF Tuner
1178     
1179     int adf_tuner_fine = ((radio_switch_data[21] >> 4) & 0x0f) - 1;
1180     int adf_tuner_coarse = (radio_switch_data[21] & 0x0f) - 1;
1181     static int last_adf_tuner_fine = adf_tuner_fine;
1182     static int last_adf_tuner_coarse = adf_tuner_coarse;
1183
1184     if ( adf_has_power() && adf_serviceable->getBoolValue() ) {
1185         // cout << "adf_stby_mode = " << adf_stby_mode->getIntValue() << endl;
1186         if ( adf_count_mode->getIntValue() == 2 ) {
1187             // tune count down timer
1188             value = adf_elapsed_timer->getDoubleValue();
1189         } else {
1190             // tune frequency
1191             if ( adf_stby_mode->getIntValue() == 1 ) {
1192                 value = adf_freq->getFloatValue();
1193             } else {
1194                 value = adf_stby_freq->getFloatValue();
1195             }
1196         }
1197
1198         if ( adf_tuner_fine != last_adf_tuner_fine ) {
1199             diff = adf_tuner_fine - last_adf_tuner_fine;
1200             if ( abs(diff) > 4 ) {
1201                 // roll over
1202                 if ( adf_tuner_fine < last_adf_tuner_fine ) {
1203                     // going up
1204                     diff = 12 - last_adf_tuner_fine + adf_tuner_fine;
1205                 } else {
1206                     // going down
1207                     diff = adf_tuner_fine - 12 - last_adf_tuner_fine;
1208                 }
1209             }
1210             value += diff;
1211         }
1212
1213         if ( adf_tuner_coarse != last_adf_tuner_coarse ) {
1214             diff = adf_tuner_coarse - last_adf_tuner_coarse;
1215             if ( abs(diff) > 4 ) {
1216                 // roll over
1217                 if ( adf_tuner_coarse < last_adf_tuner_coarse ) {
1218                     // going up
1219                     diff = 12 - last_adf_tuner_coarse + adf_tuner_coarse;
1220                 } else {
1221                     // going down
1222                     diff = adf_tuner_coarse - 12 - last_adf_tuner_coarse;
1223                 }
1224             }
1225             if ( adf_count_mode->getIntValue() == 2 ) {
1226                 value += 60 * diff;
1227             } else {
1228                 value += 25 * diff;
1229             }
1230         }
1231         if ( adf_count_mode->getIntValue() == 2 ) {
1232             if ( value < 0 ) { value += 3600; }
1233             if ( value > 3599 ) { value -= 3600; }
1234         } else {
1235             if ( value < 200 ) { value += 1600; }
1236             if ( value > 1799 ) { value -= 1600; }
1237         }
1238  
1239         if ( adf_count_mode->getIntValue() == 2 ) {
1240             fgSetFloat( "/radios/kr-87/outputs/elapsed-timer", value );
1241         } else {
1242             if ( adf_stby_mode->getIntValue() == 1 ) {
1243                 fgSetFloat( "/radios/kr-87/outputs/selected-khz", value );
1244             } else {
1245                 fgSetFloat( "/radios/kr-87/outputs/standby-khz", value );
1246             }
1247         }
1248     }
1249     last_adf_tuner_fine = adf_tuner_fine;
1250     last_adf_tuner_coarse = adf_tuner_coarse;
1251
1252
1253     // ADF buttons 
1254     fgSetInt( "/radios/kr-87/inputs/adf-btn",
1255               !(radio_switch_data[23] & 0x01) );
1256     fgSetInt( "/radios/kr-87/inputs/bfo-btn",
1257               !(radio_switch_data[23] >> 1 & 0x01) );
1258 #ifdef CURT_HARDWARE
1259     fgSetInt( "/radios/kr-87/inputs/frq-btn",
1260               !(radio_switch_data[23] >> 2 & 0x01) );
1261 #else
1262     fgSetInt( "/radios/kr-87/inputs/frq-btn",
1263               (radio_switch_data[23] >> 2 & 0x01) );
1264 #endif
1265     fgSetInt( "/radios/kr-87/inputs/flt-et-btn",
1266               !(radio_switch_data[23] >> 3 & 0x01) );
1267     fgSetInt( "/radios/kr-87/inputs/set-rst-btn",
1268               !(radio_switch_data[23] >> 4 & 0x01) );
1269     fgSetInt( "/radios/kr-87/inputs/power-btn",
1270               radio_switch_data[23] >> 5 & 0x01 );
1271     /* cout << "adf = " << !(radio_switch_data[23] & 0x01)
1272          << " bfo = " << !(radio_switch_data[23] >> 1 & 0x01)
1273          << " frq = " << !(radio_switch_data[23] >> 2 & 0x01)
1274          << " flt/et = " << !(radio_switch_data[23] >> 3 & 0x01)
1275          << " set/rst = " << !(radio_switch_data[23] >> 4 & 0x01)
1276          << endl; */
1277
1278     // Transponder Tuner
1279     int i;
1280     int digit_tuner[4];
1281     digit_tuner[0] = radio_switch_data[25] & 0x0f;
1282     digit_tuner[1] = ( radio_switch_data[25] >> 4 ) & 0x0f;
1283     digit_tuner[2] = radio_switch_data[29] & 0x0f;
1284     digit_tuner[3] = ( radio_switch_data[29] >> 4 ) & 0x0f;
1285
1286     static int last_digit_tuner[4];
1287     static bool first_time = true;
1288     if ( first_time ) {
1289         first_time = false;
1290         for ( i = 0; i < 4; ++i ) {
1291             last_digit_tuner[i] = digit_tuner[i];
1292         }
1293     }
1294
1295     if ( xpdr_has_power() && xpdr_serviceable->getBoolValue() ) {
1296         int id_code = xpdr_id_code->getIntValue();
1297         int digit[4];
1298         int place = 1000;
1299         for ( i = 0; i < 4; ++i ) {
1300             digit[i] = id_code / place;
1301             id_code -= digit[i] * place;
1302             place /= 10;
1303         }
1304
1305         for ( i = 0; i < 4; ++i ) {
1306             if ( digit_tuner[i] != last_digit_tuner[i] ) {
1307                 diff = digit_tuner[i] - last_digit_tuner[i];
1308                 if ( abs(diff) > 4 ) {
1309                     // roll over
1310                     if ( digit_tuner[i] < last_digit_tuner[i] ) {
1311                         // going up
1312                         diff = 16 - last_digit_tuner[i] + digit_tuner[i];
1313                     } else {
1314                         // going down
1315                         diff = digit_tuner[i] - 16 - last_digit_tuner[i];
1316                     }
1317                 }
1318                 digit[i] += diff;
1319             }
1320             while ( digit[i] >= 8 ) { digit[i] -= 8; }
1321             while ( digit[i] < 0 )  { digit[i] += 8; }
1322         }
1323
1324         fgSetInt( "/radios/kt-70/inputs/digit1", digit[0] );
1325         fgSetInt( "/radios/kt-70/inputs/digit2", digit[1] );
1326         fgSetInt( "/radios/kt-70/inputs/digit3", digit[2] );
1327         fgSetInt( "/radios/kt-70/inputs/digit4", digit[3] );
1328     }
1329     for ( i = 0; i < 4; ++i ) {
1330         last_digit_tuner[i] = digit_tuner[i];
1331     }
1332
1333     int tmp = 0;
1334     for ( i = 0; i < 5; ++i ) {
1335         if ( radio_switch_data[27] >> i & 0x01 ) {
1336             tmp = i + 1;
1337         }
1338     }
1339     fgSetInt( "/radios/kt-70/inputs/func-knob", tmp );
1340     fgSetInt( "/radios/kt-70/inputs/ident-btn",
1341               !(radio_switch_data[27] >> 5 & 0x01) );
1342
1343     // Audio panel switches
1344     fgSetInt( "/radios/nav[0]/audio-btn",
1345               (radio_switch_data[3] & 0x01) );
1346     fgSetInt( "/radios/nav[1]/audio-btn",
1347               (radio_switch_data[3] >> 2 & 0x01) );
1348     fgSetInt( "/radios/kr-87/inputs/audio-btn",
1349               (radio_switch_data[3] >> 4 & 0x01) );
1350     fgSetInt( "/radios/marker-beacon/audio-btn",
1351               (radio_switch_data[3] >> 6 & 0x01) );
1352
1353     return true;
1354 }
1355
1356
1357 /////////////////////////////////////////////////////////////////////
1358 // Update the radio display 
1359 /////////////////////////////////////////////////////////////////////
1360
1361 bool FGATC610x::do_radio_display() {
1362
1363     char digits[10];
1364     int i;
1365
1366     if ( dme_has_power() && dme_serviceable->getBoolValue() ) {
1367         if ( dme_in_range->getBoolValue() ) {
1368             // DME minutes
1369             float minutes = dme_min->getFloatValue();
1370             if ( minutes > 999 ) {
1371                 minutes = 999.0;
1372             }
1373             snprintf(digits, 7, "%03.0f", minutes);
1374             for ( i = 0; i < 6; ++i ) {
1375                 digits[i] -= '0';
1376             }
1377             radio_display_data[0] = digits[1] << 4 | digits[2];
1378             radio_display_data[1] = 0xf0 | digits[0];
1379         
1380             // DME knots
1381             float knots = dme_kt->getFloatValue();
1382             if ( knots > 999 ) {
1383                 knots = 999.0;
1384             }
1385             snprintf(digits, 7, "%03.0f", knots);
1386             for ( i = 0; i < 6; ++i ) {
1387                 digits[i] -= '0';
1388             }
1389             radio_display_data[2] = digits[1] << 4 | digits[2];
1390             radio_display_data[3] = 0xf0 | digits[0];
1391
1392             // DME distance (nm)
1393             float nm = dme_nm->getFloatValue();
1394             if ( nm > 99 ) {
1395                 nm = 99.0;
1396             }
1397             snprintf(digits, 7, "%04.1f", nm);
1398             for ( i = 0; i < 6; ++i ) {
1399                 digits[i] -= '0';
1400             }
1401             radio_display_data[4] = digits[1] << 4 | digits[3];
1402             radio_display_data[5] = 0x00 | digits[0];
1403             // the 0x00 in the upper nibble of the 6th byte of each
1404             // display turns on the decimal point
1405         } else {
1406             // out of range
1407             radio_display_data[0] = 0xbb;
1408             radio_display_data[1] = 0xfb;
1409             radio_display_data[2] = 0xbb;
1410             radio_display_data[3] = 0xfb;
1411             radio_display_data[4] = 0xbb;
1412             radio_display_data[5] = 0x0b;
1413         }
1414     } else {
1415         // blank dem display
1416         for ( i = 0; i < 6; ++i ) {
1417             radio_display_data[i] = 0xff;
1418         }
1419     }
1420
1421     if ( navcom1_has_power() && comm1_serviceable->getBoolValue() ) {
1422         // Com1 standby frequency
1423         float com1_stby = com1_stby_freq->getFloatValue();
1424         if ( fabs(com1_stby) > 999.99 ) {
1425             com1_stby = 0.0;
1426         }
1427         snprintf(digits, 7, "%06.3f", com1_stby);
1428         for ( i = 0; i < 6; ++i ) {
1429             digits[i] -= '0';
1430         }
1431         radio_display_data[6] = digits[4] << 4 | digits[5];
1432         radio_display_data[7] = digits[1] << 4 | digits[2];
1433         radio_display_data[8] = 0xf0 | digits[0];
1434
1435         // Com1 in use frequency
1436         float com1 = com1_freq->getFloatValue();
1437         if ( fabs(com1) > 999.99 ) {
1438             com1 = 0.0;
1439         }
1440         snprintf(digits, 7, "%06.3f", com1);
1441         for ( i = 0; i < 6; ++i ) {
1442             digits[i] -= '0';
1443         }
1444         radio_display_data[9] = digits[4] << 4 | digits[5];
1445         radio_display_data[10] = digits[1] << 4 | digits[2];
1446         radio_display_data[11] = 0x00 | digits[0];
1447         // the 0x00 in the upper nibble of the 6th byte of each display
1448         // turns on the decimal point
1449     } else {
1450         radio_display_data[6] = 0xff;
1451         radio_display_data[7] = 0xff;
1452         radio_display_data[8] = 0xff;
1453         radio_display_data[9] = 0xff;
1454         radio_display_data[10] = 0xff;
1455         radio_display_data[11] = 0xff;
1456     }
1457
1458     if ( navcom2_has_power() && comm2_serviceable->getBoolValue() ) {
1459         // Com2 standby frequency
1460         float com2_stby = com2_stby_freq->getFloatValue();
1461         if ( fabs(com2_stby) > 999.99 ) {
1462             com2_stby = 0.0;
1463         }
1464         snprintf(digits, 7, "%06.3f", com2_stby);
1465         for ( i = 0; i < 6; ++i ) {
1466             digits[i] -= '0';
1467         }
1468         radio_display_data[18] = digits[4] << 4 | digits[5];
1469         radio_display_data[19] = digits[1] << 4 | digits[2];
1470         radio_display_data[20] = 0xf0 | digits[0];
1471
1472         // Com2 in use frequency
1473         float com2 = com2_freq->getFloatValue();
1474         if ( fabs(com2) > 999.99 ) {
1475         com2 = 0.0;
1476         }
1477         snprintf(digits, 7, "%06.3f", com2);
1478         for ( i = 0; i < 6; ++i ) {
1479             digits[i] -= '0';
1480         }
1481         radio_display_data[21] = digits[4] << 4 | digits[5];
1482         radio_display_data[22] = digits[1] << 4 | digits[2];
1483         radio_display_data[23] = 0x00 | digits[0];
1484         // the 0x00 in the upper nibble of the 6th byte of each display
1485         // turns on the decimal point
1486     } else {
1487         radio_display_data[18] = 0xff;
1488         radio_display_data[19] = 0xff;
1489         radio_display_data[20] = 0xff;
1490         radio_display_data[21] = 0xff;
1491         radio_display_data[22] = 0xff;
1492         radio_display_data[23] = 0xff;
1493     }
1494
1495     if ( navcom1_has_power() && nav1_serviceable->getBoolValue() ) {
1496         // Nav1 standby frequency
1497         float nav1_stby = nav1_stby_freq->getFloatValue();
1498         if ( fabs(nav1_stby) > 999.99 ) {
1499         nav1_stby = 0.0;
1500         }
1501         snprintf(digits, 7, "%06.2f", nav1_stby);
1502         for ( i = 0; i < 6; ++i ) {
1503             digits[i] -= '0';
1504         }
1505         radio_display_data[12] = digits[4] << 4 | digits[5];
1506         radio_display_data[13] = digits[1] << 4 | digits[2];
1507         radio_display_data[14] = 0xf0 | digits[0];
1508
1509         // Nav1 in use frequency
1510         float nav1 = nav1_freq->getFloatValue();
1511         if ( fabs(nav1) > 999.99 ) {
1512             nav1 = 0.0;
1513         }
1514         snprintf(digits, 7, "%06.2f", nav1);
1515         for ( i = 0; i < 6; ++i ) {
1516             digits[i] -= '0';
1517         }
1518         radio_display_data[15] = digits[4] << 4 | digits[5];
1519         radio_display_data[16] = digits[1] << 4 | digits[2];
1520         radio_display_data[17] = 0x00 | digits[0];
1521         // the 0x00 in the upper nibble of the 6th byte of each display
1522         // turns on the decimal point
1523     } else {
1524         radio_display_data[12] = 0xff;
1525         radio_display_data[13] = 0xff;
1526         radio_display_data[14] = 0xff;
1527         radio_display_data[15] = 0xff;
1528         radio_display_data[16] = 0xff;
1529         radio_display_data[17] = 0xff;
1530     }
1531
1532     if ( navcom2_has_power() && nav2_serviceable->getBoolValue() ) {
1533         // Nav2 standby frequency
1534         float nav2_stby = nav2_stby_freq->getFloatValue();
1535         if ( fabs(nav2_stby) > 999.99 ) {
1536             nav2_stby = 0.0;
1537         }
1538         snprintf(digits, 7, "%06.2f", nav2_stby);
1539         for ( i = 0; i < 6; ++i ) {
1540             digits[i] -= '0';
1541         }
1542         radio_display_data[24] = digits[4] << 4 | digits[5];
1543         radio_display_data[25] = digits[1] << 4 | digits[2];
1544         radio_display_data[26] = 0xf0 | digits[0];
1545
1546         // Nav2 in use frequency
1547         float nav2 = nav2_freq->getFloatValue();
1548         if ( fabs(nav2) > 999.99 ) {
1549             nav2 = 0.0;
1550         }
1551         snprintf(digits, 7, "%06.2f", nav2);
1552         for ( i = 0; i < 6; ++i ) {
1553             digits[i] -= '0';
1554         }
1555         radio_display_data[27] = digits[4] << 4 | digits[5];
1556         radio_display_data[28] = digits[1] << 4 | digits[2];
1557         radio_display_data[29] = 0x00 | digits[0];
1558         // the 0x00 in the upper nibble of the 6th byte of each display
1559         // turns on the decimal point
1560     } else {
1561         radio_display_data[24] = 0xff;
1562         radio_display_data[25] = 0xff;
1563         radio_display_data[26] = 0xff;
1564         radio_display_data[27] = 0xff;
1565         radio_display_data[28] = 0xff;
1566         radio_display_data[29] = 0xff;
1567     }
1568
1569     // ADF standby frequency / timer
1570     if ( adf_has_power() && adf_serviceable->getBoolValue() ) {
1571         if ( adf_stby_mode->getIntValue() == 0 ) {
1572             // frequency
1573             float adf_stby = adf_stby_freq->getFloatValue();
1574             if ( fabs(adf_stby) > 1799 ) {
1575                 adf_stby = 1799;
1576             }
1577             snprintf(digits, 7, "%04.0f", adf_stby);
1578             for ( i = 0; i < 6; ++i ) {
1579                 digits[i] -= '0';
1580             }
1581             radio_display_data[30] = digits[3] << 4 | 0x0f;
1582             radio_display_data[31] = digits[1] << 4 | digits[2];
1583             if ( digits[0] == 0 ) {
1584                 radio_display_data[32] = 0xff;
1585             } else {
1586                 radio_display_data[32] = 0xf0 | digits[0];
1587             }
1588         } else {
1589             // timer
1590             double time;
1591             int hours, min, sec;
1592             if ( adf_timer_mode->getIntValue() == 0 ) {
1593                 time = adf_flight_timer->getDoubleValue();
1594             } else {
1595                 time = adf_elapsed_timer->getDoubleValue();
1596             }
1597             // cout << time << endl;
1598             hours = (int)(time / 3600.0);
1599             time -= hours * 3600.00;
1600             min = (int)(time / 60.0);
1601             time -= min * 60.0;
1602             sec = (int)time;
1603             int big, little;
1604             if ( hours > 0 ) {
1605                 big = hours;
1606                 if ( big > 99 ) {
1607                     big = 99;
1608                 }
1609                 little = min;
1610             } else {
1611                 big = min;
1612                 little = sec;
1613             }
1614             if ( big > 99 ) {
1615                 big = 99;
1616             }
1617             // cout << big << ":" << little << endl;
1618             snprintf(digits, 7, "%02d%02d", big, little);
1619             for ( i = 0; i < 6; ++i ) {
1620                 digits[i] -= '0';
1621             }
1622             radio_display_data[30] = digits[2] << 4 | digits[3];
1623             radio_display_data[31] = digits[0] << 4 | digits[1];
1624             radio_display_data[32] = 0xff;
1625         }
1626
1627         // ADF in use frequency
1628         float adf = adf_freq->getFloatValue();
1629         if ( fabs(adf) > 1799 ) {
1630             adf = 1799;
1631         }
1632         snprintf(digits, 7, "%04.0f", adf);
1633         for ( i = 0; i < 6; ++i ) {
1634             digits[i] -= '0';
1635         }
1636         radio_display_data[33] = digits[2] << 4 | digits[3];
1637         if ( digits[0] == 0 ) {
1638             radio_display_data[34] = 0xf0 | digits[1];
1639         } else {
1640             radio_display_data[34] = digits[0] << 4 | digits[1];
1641         }
1642         if ( adf_stby_mode->getIntValue() == 0 ) {
1643           radio_display_data[35] = 0xff;
1644         } else {
1645           radio_display_data[35] = 0x0f;
1646         }
1647     } else {
1648         radio_display_data[30] = 0xff;
1649         radio_display_data[31] = 0xff;
1650         radio_display_data[32] = 0xff;
1651         radio_display_data[33] = 0xff;
1652         radio_display_data[34] = 0xff;
1653         radio_display_data[35] = 0xff;
1654     }
1655     
1656     // Transponder code and flight level
1657     if ( xpdr_has_power() && xpdr_serviceable->getBoolValue() ) {
1658         if ( xpdr_func_knob->getIntValue() == 2 ) {
1659             // test mode
1660             radio_display_data[36] = 8 << 4 | 8;
1661             radio_display_data[37] = 8 << 4 | 8;
1662             radio_display_data[38] = 0xff;
1663             radio_display_data[39] = 8 << 4 | 0x0f;
1664             radio_display_data[40] = 8 << 4 | 8;
1665         } else {
1666             // other on modes
1667             int id_code = xpdr_id_code->getIntValue();
1668             int place = 1000;
1669             for ( i = 0; i < 4; ++i ) {
1670                 digits[i] = id_code / place;
1671                 id_code -= digits[i] * place;
1672                 place /= 10;
1673             }
1674             radio_display_data[36] = digits[2] << 4 | digits[3];
1675             radio_display_data[37] = digits[0] << 4 | digits[1];
1676             radio_display_data[38] = 0xff;
1677
1678             if ( xpdr_func_knob->getIntValue() == 3 ||
1679                  xpdr_func_knob->getIntValue() == 5 )
1680             {
1681                 // do flight level display
1682                 snprintf(digits, 7, "%03d", xpdr_flight_level->getIntValue() );
1683                 for ( i = 0; i < 6; ++i ) {
1684                     digits[i] -= '0';
1685                 }
1686                 radio_display_data[39] = digits[2] << 4 | 0x0f;
1687                 radio_display_data[40] = digits[0] << 4 | digits[1];
1688             } else {
1689                 // blank flight level display
1690                 radio_display_data[39] = 0xff;
1691                 radio_display_data[40] = 0xff;
1692             }
1693         }
1694     } else {
1695         // off
1696         radio_display_data[36] = 0xff;
1697         radio_display_data[37] = 0xff;
1698         radio_display_data[38] = 0xff;
1699         radio_display_data[39] = 0xff;
1700         radio_display_data[40] = 0xff;
1701     }
1702
1703     ATC610xSetRadios( radios_fd, radio_display_data );
1704
1705     return true;
1706 }
1707
1708
1709 /////////////////////////////////////////////////////////////////////
1710 // Drive the stepper motors
1711 /////////////////////////////////////////////////////////////////////
1712
1713 bool FGATC610x::do_steppers() {
1714     float diff = mag_compass->getFloatValue() - compass_position;
1715     while ( diff < -180.0 ) { diff += 360.0; }
1716     while ( diff >  180.0 ) { diff -= 360.0; }
1717
1718     int steps = (int)(diff * 4);
1719     // cout << "steps = " << steps << endl;
1720     if ( steps > 4 ) { steps = 4; }
1721     if ( steps < -4 ) { steps = -4; }
1722
1723     if ( abs(steps) > 0 ) {
1724         unsigned char cmd = 0x80;       // stepper command
1725         if ( steps > 0 ) {
1726             cmd |= 0x20;                // go up
1727         } else {
1728             cmd |= 0x00;                // go down
1729         }
1730         cmd |= abs(steps);
1731
1732         // sync compass_position with hardware position
1733         compass_position += (float)steps / 4.0;
1734
1735         ATC610xSetStepper( stepper_fd, ATC_COMPASS_CH, cmd );
1736     }
1737
1738     return true;
1739 }
1740
1741
1742 /////////////////////////////////////////////////////////////////////
1743 // Read the switch positions
1744 /////////////////////////////////////////////////////////////////////
1745
1746 // decode the packed switch data
1747 static void update_switch_matrix(
1748         int board,
1749         unsigned char switch_data[ATC_SWITCH_BYTES],
1750         int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES] )
1751 {
1752     for ( int row = 0; row < ATC_SWITCH_BYTES; ++row ) {
1753         unsigned char switches = switch_data[row];
1754
1755         for( int column = 0; column < ATC_NUM_COLS; ++column ) {
1756             switch_matrix[board][column][row] = switches & 1;
1757             switches = switches >> 1;
1758         }                       
1759     }
1760 }                     
1761
1762 bool FGATC610x::do_switches() {
1763     ATC610xReadSwitches( switches_fd, switch_data );
1764
1765     // unpack the switch data
1766     int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES];
1767     update_switch_matrix( board, switch_data, switch_matrix );
1768
1769     // master switches
1770     fgSetBool( "/controls/switches/master-bat", switch_matrix[board][5][1] );
1771     fgSetBool( "/controls/switches/master-alt", switch_matrix[board][4][1] );
1772     fgSetBool( "/controls/switches/master-avionics",
1773                switch_matrix[board][0][3] );
1774
1775     if ( !ignore_flight_controls->getBoolValue() ) {
1776         // magnetos and starter switch
1777         int magnetos = 0;
1778         bool starter = false;
1779         if ( switch_matrix[board][3][1] == 1 ) {
1780             magnetos = 3;
1781             starter = true;
1782         } else if ( switch_matrix[board][2][1] == 1 ) {
1783             magnetos = 3;
1784             starter = false;
1785         } else if ( switch_matrix[board][1][1] == 1 ) {
1786             magnetos = 2;
1787             starter = false;
1788         } else if ( switch_matrix[board][0][1] == 1 ) {
1789             magnetos = 1;
1790             starter = false;
1791         } else {
1792             magnetos = 0;
1793             starter = false;
1794         }
1795
1796         // do a bit of filtering on the magneto/starter switch and the
1797         // flap lever because these are not well debounced in hardware
1798         static int mag1, mag2, mag3;
1799         mag3 = mag2;
1800         mag2 = mag1;
1801         mag1 = magnetos;
1802         if ( mag1 == mag2 && mag2 == mag3 ) {
1803             fgSetInt( "/controls/engines/engine[0]/magnetos", magnetos );
1804         }
1805         static bool start1, start2, start3;
1806         start3 = start2;
1807         start2 = start1;
1808         start1 = starter;
1809         if ( start1 == start2 && start2 == start3 ) {
1810             fgSetBool( "/controls/engines/engine[0]/starter", starter );
1811         }
1812     }
1813
1814     // other toggle switches
1815     fgSetBool( "/controls/engines/engine[0]/fuel-pump",
1816                switch_matrix[board][0][2] );
1817     fgSetBool( "/controls/switches/flashing-beacon",
1818                switch_matrix[board][1][2] );
1819     fgSetBool( "/controls/switches/landing-light", switch_matrix[board][2][2] );
1820     fgSetBool( "/controls/switches/taxi-lights", switch_matrix[board][3][2] );
1821     fgSetBool( "/controls/switches/nav-lights",
1822                switch_matrix[board][4][2] );
1823     fgSetBool( "/controls/switches/strobe-lights", switch_matrix[board][5][2] );
1824     fgSetBool( "/controls/switches/pitot-heat", switch_matrix[board][6][2] );
1825
1826     // flaps
1827     if ( !ignore_flight_controls->getBoolValue() ) {
1828         float flaps = 0.0;
1829         if ( switch_matrix[board][6][3] ) {
1830             flaps = 1.0;
1831         } else if ( switch_matrix[board][5][3] ) {
1832             flaps = 2.0 / 3.0;
1833         } else if ( switch_matrix[board][4][3] ) {
1834             flaps = 1.0 / 3.0;
1835         } else if ( !switch_matrix[board][4][3] ) {
1836             flaps = 0.0;
1837         }
1838
1839         // do a bit of filtering on the magneto/starter switch and the
1840         // flap lever because these are not well debounced in hardware
1841         static float flap1, flap2, flap3;
1842         flap3 = flap2;
1843         flap2 = flap1;
1844         flap1 = flaps;
1845         if ( flap1 == flap2 && flap2 == flap3 ) {
1846             fgSetFloat( "/controls/flight/flaps", flaps );
1847         }
1848     }
1849
1850     // fuel selector (also filtered)
1851     int fuel = 0;
1852     if ( switch_matrix[board][2][3] ) {
1853         // both
1854         fuel = 3;
1855     } else if ( switch_matrix[board][1][3] ) {
1856         // left
1857         fuel = 1;
1858     } else if ( switch_matrix[board][3][3] ) {
1859         // right
1860         fuel = 2;
1861     } else {
1862         // fuel cutoff
1863         fuel = 0;
1864     }
1865
1866     const int max_fuel = 60;
1867     static int fuel_list[max_fuel];
1868     int i;
1869     for ( i = max_fuel - 1; i >= 0; --i ) {
1870       fuel_list[i+1] = fuel_list[i];
1871     }
1872     fuel_list[0] = fuel;
1873     bool all_same = true;
1874     for ( i = 0; i < max_fuel - 1; ++i ) {
1875       if ( fuel_list[i] != fuel_list[i+1] ) {
1876         all_same = false;
1877       }
1878     }
1879     if ( all_same ) {
1880         fgSetBool( "/controls/fuel/tank[0]/fuel_selector", (fuel & 0x01) > 0 );
1881         fgSetBool( "/controls/fuel/tank[1]/fuel_selector", (fuel & 0x02) > 0 );
1882     }
1883
1884     // circuit breakers
1885 #ifdef ATC_SUPPORT_CIRCUIT_BREAKERS_NOT_THE_DEFAULT
1886     fgSetBool( "/controls/circuit-breakers/cabin-lights-pwr",
1887                switch_matrix[board][0][0] );
1888     fgSetBool( "/controls/circuit-breakers/instr-ignition-switch",
1889                switch_matrix[board][1][0] );
1890     fgSetBool( "/controls/circuit-breakers/flaps",
1891                switch_matrix[board][2][0] );
1892     fgSetBool( "/controls/circuit-breakers/avn-bus-1",
1893                switch_matrix[board][3][0] );
1894     fgSetBool( "/controls/circuit-breakers/avn-bus-2",
1895                switch_matrix[board][4][0] );
1896     fgSetBool( "/controls/circuit-breakers/turn-coordinator",
1897                switch_matrix[board][5][0] );
1898     fgSetBool( "/controls/circuit-breakers/instrument-lights",
1899                switch_matrix[board][6][0] );
1900     fgSetBool( "/controls/circuit-breakers/annunciators",
1901                switch_matrix[board][7][0] );
1902 #else
1903     fgSetBool( "/controls/circuit-breakers/cabin-lights-pwr", true );
1904     fgSetBool( "/controls/circuit-breakers/instr-ignition-switch", true );
1905     fgSetBool( "/controls/circuit-breakers/flaps", true );
1906     fgSetBool( "/controls/circuit-breakers/avn-bus-1", true );
1907     fgSetBool( "/controls/circuit-breakers/avn-bus-2", true );
1908     fgSetBool( "/controls/circuit-breakers/turn-coordinator", true );
1909     fgSetBool( "/controls/circuit-breakers/instrument-lights", true );
1910     fgSetBool( "/controls/circuit-breakers/annunciators", true );
1911 #endif
1912
1913     if ( !ignore_flight_controls->getBoolValue() ) {
1914         fgSetDouble( "/controls/gear/brake-parking",
1915                      switch_matrix[board][7][3] );
1916     }
1917
1918     fgSetDouble( "/radios/marker-beacon/power-btn",
1919                  switch_matrix[board][6][1] );
1920
1921     return true;
1922 }
1923
1924
1925 bool FGATC610x::process() {
1926     // Lock the hardware, skip if it's not ready yet
1927     if ( ATC610xLock( lock_fd ) > 0 ) {
1928
1929         do_analog_in();
1930         do_lights();
1931         do_radio_switches();
1932         do_radio_display();
1933         do_steppers();
1934         do_switches();
1935         
1936         ATC610xRelease( lock_fd );
1937
1938         return true;
1939     } else {
1940         return false;
1941     }
1942 }
1943
1944
1945 bool FGATC610x::close() {
1946
1947     return true;
1948 }