]> git.mxchange.org Git - flightgear.git/blob - src/Network/atc610x.cxx
Add support for KR 87 annunciators.
[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/props.hxx>
47
48 #include <Main/fg_props.hxx>
49 #include <Main/globals.hxx>
50
51 #include "atc610x.hxx"
52
53 SG_USING_STD(string);
54
55 // Lock the ATC 610 hardware
56 static int ATC610xLock( int fd ) {
57     // rewind
58     lseek( fd, 0, SEEK_SET );
59
60     char tmp[2];
61     int result = read( fd, tmp, 1 );
62     if ( result != 1 ) {
63         SG_LOG( SG_IO, SG_DEBUG, "Lock failed" );
64     }
65
66     return result;
67 }
68
69
70 // Write a radios command
71 static int ATC610xRelease( int fd ) {
72     // rewind
73     lseek( fd, 0, SEEK_SET );
74
75     char tmp[2];
76     tmp[0] = tmp[1] = 0;
77     int result = write( fd, tmp, 1 );
78
79     if ( result != 1 ) {
80         SG_LOG( SG_IO, SG_DEBUG, "Release failed" );
81     }
82
83     return result;
84 }
85
86
87 // Read analog inputs
88 static void ATC610xReadAnalogInputs( int fd, unsigned char *analog_in_bytes ) {
89     // rewind
90     lseek( fd, 0, SEEK_SET );
91
92     int result = read( fd, analog_in_bytes, ATC_ANAL_IN_BYTES );
93     if ( result != ATC_ANAL_IN_BYTES ) {
94         SG_LOG( SG_IO, SG_ALERT, "Read failed" );
95         exit( -1 );
96     }
97 }
98
99
100 // Write a radios command
101 static int ATC610xSetRadios( int fd,
102                              unsigned char data[ATC_RADIO_DISPLAY_BYTES] )
103 {
104     // rewind
105     lseek( fd, 0, SEEK_SET );
106
107     int result = write( fd, data, ATC_RADIO_DISPLAY_BYTES );
108
109     if ( result != ATC_RADIO_DISPLAY_BYTES ) {
110         SG_LOG( SG_IO, SG_DEBUG, "Write failed" );
111     }
112
113     return result;
114 }
115
116
117 // Read status of last radios written to
118 static void ATC610xReadRadios( int fd, unsigned char *switch_data ) {
119     // rewind
120     lseek( fd, 0, SEEK_SET );
121
122     int result = read( fd, switch_data, ATC_RADIO_SWITCH_BYTES );
123     if ( result != ATC_RADIO_SWITCH_BYTES ) {
124         SG_LOG( SG_IO, SG_ALERT, "Read failed" );
125         exit( -1 );
126     }
127 }
128
129 // Write a stepper command
130 static int ATC610xSetStepper( int fd, unsigned char channel,
131                               unsigned char value )
132 {
133     // rewind
134     lseek( fd, 0, SEEK_SET );
135
136     // Write the value
137     unsigned char buf[3];
138     buf[0] = channel;
139     buf[1] = value;
140     buf[2] = 0;
141     int result = write( fd, buf, 2 );
142     if ( result != 2 ) {
143         SG_LOG( SG_IO, SG_INFO, "Write failed" );
144     }
145     SG_LOG( SG_IO, SG_DEBUG,
146             "Sent cmd = " << (int)channel << " value = " << (int)value );
147     return result;
148 }
149
150
151 // Read status of last stepper written to
152 static unsigned char ATC610xReadStepper( int fd ) {
153     int result;
154
155     // rewind
156     lseek( fd, 0, SEEK_SET );
157
158     // Write the value
159     unsigned char buf[2];
160     result = read( fd, buf, 1 );
161     if ( result != 1 ) {
162         SG_LOG( SG_IO, SG_ALERT, "Read failed" );
163         exit( -1 );
164     }
165     SG_LOG( SG_IO, SG_DEBUG, "Read result = " << (int)buf[0] );
166
167     return buf[0];
168 }
169
170
171 // Read switch inputs
172 static void ATC610xReadSwitches( int fd, unsigned char *switch_bytes ) {
173     // rewind
174     lseek( fd, 0, SEEK_SET );
175
176     int result = read( fd, switch_bytes, ATC_SWITCH_BYTES );
177     if ( result != ATC_SWITCH_BYTES ) {
178         SG_LOG( SG_IO, SG_ALERT, "Read failed" );
179         exit( -1 );
180     }
181 }
182
183
184 // Turn a lamp on or off
185 void ATC610xSetLamp( int fd, int channel, bool value ) {
186     // lamp channels 0-63 are written to LampPort0, channels 64-127
187     // are written to LampPort1
188
189     // bits 0-6 are the lamp address
190     // bit 7 is the value (on/off)
191
192     int result;
193
194     // Write the value
195     unsigned char buf[3];
196     buf[0] = channel;
197     buf[1] = value;
198     buf[2] = 0;
199     result = write( fd, buf, 2 );
200     if ( result != 2 ) {
201         SG_LOG( SG_IO, SG_ALERT,  "Write failed" );
202         exit( -1 );
203     }
204 }
205
206
207 // Open and initialize ATC 610x hardware
208 bool FGATC610x::open() {
209     if ( is_enabled() ) {
210         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
211                 << "is already in use, ignoring" );
212         return false;
213     }
214
215     SG_LOG( SG_IO, SG_ALERT,
216             "Initializing ATC 610x hardware, please wait ..." );
217
218     set_hz( 30 );               // default to processing requests @ 30Hz
219     set_enabled( true );
220
221     board = 0;                  // 610x uses a single board number = 0
222
223     snprintf( lock_file, 256, "/proc/atc610x/board%d/lock", board );
224     snprintf( analog_in_file, 256, "/proc/atc610x/board%d/analog_in", board );
225     snprintf( lamps_file, 256, "/proc/atc610x/board%d/lamps", board );
226     snprintf( radios_file, 256, "/proc/atc610x/board%d/radios", board );
227     snprintf( stepper_file, 256, "/proc/atc610x/board%d/steppers", board );
228     snprintf( switches_file, 256, "/proc/atc610x/board%d/switches", board );
229
230     /////////////////////////////////////////////////////////////////////
231     // Open the /proc files
232     /////////////////////////////////////////////////////////////////////
233
234     lock_fd = ::open( lock_file, O_RDWR );
235     if ( lock_fd == -1 ) {
236         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
237         char msg[256];
238         snprintf( msg, 256, "Error opening %s", lock_file );
239         perror( msg );
240         exit( -1 );
241     }
242
243     analog_in_fd = ::open( analog_in_file, O_RDONLY );
244     if ( analog_in_fd == -1 ) {
245         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
246         char msg[256];
247         snprintf( msg, 256, "Error opening %s", analog_in_file );
248         perror( msg );
249         exit( -1 );
250     }
251
252     lamps_fd = ::open( lamps_file, O_WRONLY );
253     if ( lamps_fd == -1 ) {
254         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
255         char msg[256];
256         snprintf( msg, 256, "Error opening %s", lamps_file );
257         perror( msg );
258         exit( -1 );
259     }
260
261     radios_fd = ::open( radios_file, O_RDWR );
262     if ( radios_fd == -1 ) {
263         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
264         char msg[256];
265         snprintf( msg, 256, "Error opening %s", radios_file );
266         perror( msg );
267         exit( -1 );
268     }
269
270     stepper_fd = ::open( stepper_file, O_RDWR );
271     if ( stepper_fd == -1 ) {
272         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
273         char msg[256];
274         snprintf( msg, 256, "Error opening %s", stepper_file );
275         perror( msg );
276         exit( -1 );
277     }
278
279     switches_fd = ::open( switches_file, O_RDONLY );
280     if ( switches_fd == -1 ) {
281         SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
282         char msg[256];
283         snprintf( msg, 256, "Error opening %s", switches_file );
284         perror( msg );
285         exit( -1 );
286     }
287
288     /////////////////////////////////////////////////////////////////////
289     // Home the compass stepper motor
290     /////////////////////////////////////////////////////////////////////
291
292     SG_LOG( SG_IO, SG_ALERT,
293             "  - Homing the compass stepper motor" );
294
295     // Lock the hardware, keep trying until we succeed
296     while ( ATC610xLock( lock_fd ) <= 0 );
297
298     // Send the stepper home command
299     ATC610xSetStepper( stepper_fd, ATC_COMPASS_CH, ATC_STEPPER_HOME );
300
301     // Release the hardware
302     ATC610xRelease( lock_fd );
303
304     SG_LOG( SG_IO, SG_ALERT,
305             "  - Waiting for compass to come home." );
306
307     bool home = false;
308     int timeout = 900;          // about 30 seconds
309     while ( ! home && timeout > 0 ) {
310         if ( timeout % 150 == 0 ) {
311             SG_LOG( SG_IO, SG_INFO, "waiting for compass = " << timeout );
312         } else {
313             SG_LOG( SG_IO, SG_DEBUG, "Checking if compass home ..." );
314         }
315
316         while ( ATC610xLock( lock_fd ) <= 0 );
317
318         unsigned char result = ATC610xReadStepper( stepper_fd );
319         if ( result == 0 ) {
320             home = true;
321         }
322
323         ATC610xRelease( lock_fd );
324
325 #if defined( _MSC_VER )
326         ulMilliSecondSleep(33);
327 #elif defined (WIN32) && !defined(__CYGWIN__)
328         Sleep (33);
329 #else
330         usleep(33);
331 #endif
332
333         --timeout;
334     }
335
336     compass_position = 0.0;
337
338     /////////////////////////////////////////////////////////////////////
339     // Blank the radio display
340     /////////////////////////////////////////////////////////////////////
341
342     SG_LOG( SG_IO, SG_ALERT,
343             "  - Clearing the radios displays." );
344
345     // Prepair the data
346     unsigned char value = 0xff;
347     for ( int channel = 0; channel < ATC_RADIO_DISPLAY_BYTES; ++channel ) {
348         radio_display_data[channel] = value;
349     }
350
351     // Lock the hardware, keep trying until we succeed
352     while ( ATC610xLock( lock_fd ) <= 0 );
353
354     // Set radio display
355     ATC610xSetRadios( radios_fd, radio_display_data );
356
357     ATC610xRelease( lock_fd );
358
359     /////////////////////////////////////////////////////////////////////
360     // Blank the lamps
361     /////////////////////////////////////////////////////////////////////
362
363     for ( int i = 0; i < 128; ++i ) {
364         ATC610xSetLamp( lamps_fd, i, false );
365     }
366
367     /////////////////////////////////////////////////////////////////////
368     // Finished initing hardware
369     /////////////////////////////////////////////////////////////////////
370
371     SG_LOG( SG_IO, SG_ALERT,
372             "Done initializing ATC 610x hardware." );
373
374     /////////////////////////////////////////////////////////////////////
375     // Connect up to property values
376     /////////////////////////////////////////////////////////////////////
377
378     mag_compass = fgGetNode( "/steam/mag-compass-deg", true );
379
380     dme_min = fgGetNode( "/radios/dme/ete-min", true );
381     dme_kt = fgGetNode( "/radios/dme/speed-kt", true );
382     dme_nm = fgGetNode( "/radios/dme/distance-nm", true );
383
384     com1_freq = fgGetNode( "/radios/comm[0]/frequencies/selected-mhz", true );
385     com1_stby_freq
386         = fgGetNode( "/radios/comm[0]/frequencies/standby-mhz", true );
387     com2_freq = fgGetNode( "/radios/comm[1]/frequencies/selected-mhz", true );
388     com2_stby_freq
389         = fgGetNode( "/radios/comm[1]/frequencies/standby-mhz", true );
390
391     nav1_freq = fgGetNode( "/radios/nav[0]/frequencies/selected-mhz", true );
392     nav1_stby_freq
393         = fgGetNode( "/radios/nav[0]/frequencies/standby-mhz", true );
394
395     nav2_freq = fgGetNode( "/radios/nav[1]/frequencies/selected-mhz", true );
396     nav2_stby_freq
397         = fgGetNode( "/radios/nav[1]/frequencies/standby-mhz", true );
398
399     adf_on_off_vol = fgGetNode( "/radios/adf/on-off-volume", true );
400     adf_adf_btn = fgGetNode( "/radios/adf/adf-btn", true );
401     adf_bfo_btn = fgGetNode( "/radios/adf/bfo-btn", true );
402     adf_freq = fgGetNode( "/radios/adf/frequencies/selected-khz", true );
403     adf_stby_freq = fgGetNode( "/radios/adf/frequencies/standby-khz", true );
404     adf_stby_mode = fgGetNode( "/radios/adf/stby-mode", true );
405     adf_timer_mode = fgGetNode( "/radios/adf/timer-mode", true );
406     adf_count_mode = fgGetNode( "/radios/adf/count-mode", true );
407     adf_flight_timer = fgGetNode( "/radios/adf/flight-timer", true );
408     adf_elapsed_timer = fgGetNode( "/radios/adf/elapsed-timer", true );
409
410     inner = fgGetNode( "/radios/marker-beacon/inner", true );
411     middle = fgGetNode( "/radios/marker-beacon/middle", true );
412     outer = fgGetNode( "/radios/marker-beacon/outer", true );
413
414     return true;
415 }
416
417
418 /////////////////////////////////////////////////////////////////////
419 // Read analog inputs
420 /////////////////////////////////////////////////////////////////////
421
422 #define ATC_AILERON_CENTER 535
423 #define ATC_ELEVATOR_TRIM_CENTER 512
424 #define ATC_ELEVATOR_CENTER 543
425
426 bool FGATC610x::do_analog_in() {
427     // Read raw data in byte form
428     ATC610xReadAnalogInputs( analog_in_fd, analog_in_bytes );
429
430     // Convert to integer values
431     for ( int channel = 0; channel < ATC_ANAL_IN_VALUES; ++channel ) {
432         unsigned char hi = analog_in_bytes[2 * channel] & 0x03;
433         unsigned char lo = analog_in_bytes[2 * channel + 1];
434         analog_in_data[channel] = hi * 256 + lo;
435
436         // printf("%02x %02x ", hi, lo );
437         // printf("%04d ", value );
438     }
439
440     float tmp, tmp1, tmp2;
441
442     // aileron
443     tmp = (float)(analog_in_data[0] - ATC_AILERON_CENTER) / 256.0f;
444     fgSetFloat( "/controls/aileron", tmp );
445     // cout << "aileron = " << analog_in_data[0] << " = " << tmp;
446
447     // elevator
448     tmp = (float)(analog_in_data[4] - ATC_ELEVATOR_TRIM_CENTER) / 512.0f;
449     fgSetFloat( "/controls/elevator-trim", tmp );
450     // cout << "trim = " << analog_in_data[4] << " = " << tmp;
451
452     // trim
453     tmp = (float)(ATC_ELEVATOR_CENTER - analog_in_data[5]) / 100.0f;
454     fgSetFloat( "/controls/elevator", tmp );
455     // cout << " elev = " << analog_in_data[5] << " = " << tmp << endl;
456
457     // mixture
458     tmp = (float)analog_in_data[7] / 680.0f;
459     fgSetFloat( "/controls/mixture[0]", tmp );
460
461     // throttle
462     tmp = (float)analog_in_data[8] / 690.0f;
463     fgSetFloat( "/controls/throttle[0]", tmp );
464
465     // nav1 volume
466     tmp = (float)analog_in_data[25] / 1024.0f;
467     fgSetFloat( "/radios/nav[0]/volume", tmp );
468
469     // nav2 volume
470     tmp = (float)analog_in_data[24] / 1024.0f;
471     fgSetFloat( "/radios/nav[1]/volume", tmp );
472
473     // adf volume
474     tmp = (float)analog_in_data[26] / 1024.0f;
475     fgSetFloat( "/radios/adf/on-off-volume", tmp );
476
477     // nav2 obs tuner
478     tmp = (float)analog_in_data[29] * 360.0f / 1024.0f;
479     fgSetFloat( "/radios/nav[1]/radials/selected-deg", tmp );
480
481     // nav1 obs tuner
482     tmp1 = (float)analog_in_data[30] * 360.0f / 1024.0f;
483     tmp2 = (float)analog_in_data[31] * 360.0f / 1024.0f;
484     fgSetFloat( "/radios/nav[0]/radials/selected-deg", tmp1 );
485
486     return true;
487 }
488
489
490 /////////////////////////////////////////////////////////////////////
491 // Write the lights
492 /////////////////////////////////////////////////////////////////////
493
494 bool FGATC610x::do_lights( double dt ) {
495
496     // Marker beacons
497     ATC610xSetLamp( lamps_fd, 4, inner->getBoolValue() );
498     ATC610xSetLamp( lamps_fd, 5, middle->getBoolValue() );
499     ATC610xSetLamp( lamps_fd, 3, outer->getBoolValue() );
500
501     // ADF annunciators
502     if ( adf_on_off_vol->getDoubleValue() >= 0.01 ) {
503         ATC610xSetLamp( lamps_fd, 11, !adf_adf_btn->getBoolValue() ); // ANT
504         ATC610xSetLamp( lamps_fd, 12, adf_adf_btn->getBoolValue() ); // ADF
505         ATC610xSetLamp( lamps_fd, 13, adf_bfo_btn->getBoolValue() ); // BFO
506         ATC610xSetLamp( lamps_fd, 14, !adf_stby_mode->getBoolValue() ); // FRQ
507         ATC610xSetLamp( lamps_fd, 15, adf_stby_mode->getBoolValue() &&
508                         !adf_timer_mode->getBoolValue() ); // FLT
509
510         // ET needs to blink when we are in ET set countdown time
511         if ( adf_count_mode->getIntValue() < 2 ) {
512             ATC610xSetLamp( lamps_fd, 16, adf_stby_mode->getBoolValue() &&
513                             adf_timer_mode->getBoolValue() ); // ET
514         } else {
515             et_flash_time += dt;
516             if ( et_flash && et_flash_time > 0.5 ) {
517                 et_flash = false;
518                 et_flash_time -= 0.5;
519             } else if ( !et_flash && et_flash_time > 0.2 ) {
520                 et_flash = true;
521                 et_flash_time -= 0.2;
522             }
523             ATC610xSetLamp( lamps_fd, 16, et_flash ); // ET
524         }
525     } else {
526         ATC610xSetLamp( lamps_fd, 11, false ); // ANT
527         ATC610xSetLamp( lamps_fd, 12, false ); // ADF
528         ATC610xSetLamp( lamps_fd, 13, false ); // BFO
529         ATC610xSetLamp( lamps_fd, 14, false ); // FRQ
530         ATC610xSetLamp( lamps_fd, 15, false ); // FLT
531         ATC610xSetLamp( lamps_fd, 16, false ); // ET
532     }
533
534     return true;
535 }
536
537
538 /////////////////////////////////////////////////////////////////////
539 // Read radio switches 
540 /////////////////////////////////////////////////////////////////////
541
542 bool FGATC610x::do_radio_switches() {
543     double freq, coarse_freq, fine_freq, value;
544     int diff;
545
546     ATC610xReadRadios( radios_fd, radio_switch_data );
547
548     // DME Switch
549     dme_switch = (radio_switch_data[7] >> 4) & 0x03;
550     if ( dme_switch == 0 ) {
551         // off
552         fgSetInt( "/radios/dme/switch-position", 0 );
553     } else if ( dme_switch == 2 ) {
554         // nav1
555         fgSetInt( "/radios/dme/switch-position", 1 );
556     } else if ( dme_switch == 1 ) {
557         // nav2
558         fgSetInt( "/radios/dme/switch-position", 3 );
559     }
560
561     // Com1 Swap
562     int com1_swap = !((radio_switch_data[7] >> 1) & 0x01);
563     static int last_com1_swap;
564     if ( com1_swap && (last_com1_swap != com1_swap) ) {
565         float tmp = com1_freq->getFloatValue();
566         fgSetFloat( "/radios/comm[0]/frequencies/selected-mhz",
567                    com1_stby_freq->getFloatValue() );
568         fgSetFloat( "/radios/comm[0]/frequencies/standby-mhz", tmp );
569     }
570     last_com1_swap = com1_swap;
571
572     // Com2 Swap
573     int com2_swap = !((radio_switch_data[15] >> 1) & 0x01);
574     static int last_com2_swap;
575     if ( com2_swap && (last_com2_swap != com2_swap) ) {
576         float tmp = com2_freq->getFloatValue();
577         fgSetFloat( "/radios/comm[1]/frequencies/selected-mhz",
578                    com2_stby_freq->getFloatValue() );
579         fgSetFloat( "/radios/comm[1]/frequencies/standby-mhz", tmp );
580     }
581     last_com2_swap = com2_swap;
582
583     // Nav1 Swap
584     int nav1_swap = radio_switch_data[11] & 0x01;
585     static int last_nav1_swap;
586     if ( nav1_swap && (last_nav1_swap != nav1_swap) ) {
587         float tmp = nav1_freq->getFloatValue();
588         fgSetFloat( "/radios/nav[0]/freqencies/selected-mhz",
589                    nav1_stby_freq->getFloatValue() );
590         fgSetFloat( "/radios/nav[0]/frequencies/standby-mhz", tmp );
591     }
592     last_nav1_swap = nav1_swap;
593
594     // Nav2 Swap
595     int nav2_swap = !(radio_switch_data[19] & 0x01);
596     static int last_nav2_swap;
597     if ( nav2_swap && (last_nav2_swap != nav2_swap) ) {
598         float tmp = nav2_freq->getFloatValue();
599         fgSetFloat( "/radios/nav[1]/frequencies/selected-mhz",
600                    nav2_stby_freq->getFloatValue() );
601         fgSetFloat( "/radios/nav[1]/frequencies/standby-mhz", tmp );
602     }
603     last_nav2_swap = nav2_swap;
604
605     // Com1 Tuner
606     int com1_tuner_fine = ((radio_switch_data[5] >> 4) & 0x0f) - 1;
607     int com1_tuner_coarse = (radio_switch_data[5] & 0x0f) - 1;
608     static int last_com1_tuner_fine = com1_tuner_fine;
609     static int last_com1_tuner_coarse = com1_tuner_coarse;
610
611     freq = com1_stby_freq->getFloatValue();
612     coarse_freq = (int)freq;
613     fine_freq = (int)((freq - coarse_freq) * 40 + 0.5);
614
615     if ( com1_tuner_fine != last_com1_tuner_fine ) {
616         diff = com1_tuner_fine - last_com1_tuner_fine;
617         if ( abs(diff) > 4 ) {
618             // roll over
619             if ( com1_tuner_fine < last_com1_tuner_fine ) {
620                 // going up
621                 diff = 12 - last_com1_tuner_fine + com1_tuner_fine;
622             } else {
623                 // going down
624                 diff = com1_tuner_fine - 12 - last_com1_tuner_fine;
625             }
626         }
627         fine_freq += diff;
628     }
629     while ( fine_freq >= 40.0 ) { fine_freq -= 40.0; }
630     while ( fine_freq < 0.0 )  { fine_freq += 40.0; }
631
632     if ( com1_tuner_coarse != last_com1_tuner_coarse ) {
633         diff = com1_tuner_coarse - last_com1_tuner_coarse;
634         if ( abs(diff) > 4 ) {
635             // roll over
636             if ( com1_tuner_coarse < last_com1_tuner_coarse ) {
637                 // going up
638                 diff = 12 - last_com1_tuner_coarse + com1_tuner_coarse;
639             } else {
640                 // going down
641                 diff = com1_tuner_coarse - 12 - last_com1_tuner_coarse;
642             }
643         }
644         coarse_freq += diff;
645     }
646     if ( coarse_freq < 118.0 ) { coarse_freq += 19.0; }
647     if ( coarse_freq > 136.0 ) { coarse_freq -= 19.0; }
648
649     last_com1_tuner_fine = com1_tuner_fine;
650     last_com1_tuner_coarse = com1_tuner_coarse;
651
652     fgSetFloat( "/radios/comm[0]/frequencies/standby-mhz", 
653                 coarse_freq + fine_freq / 40.0 );
654
655     // Com2 Tuner
656     int com2_tuner_fine = ((radio_switch_data[13] >> 4) & 0x0f) - 1;
657     int com2_tuner_coarse = (radio_switch_data[13] & 0x0f) - 1;
658     static int last_com2_tuner_fine = com2_tuner_fine;
659     static int last_com2_tuner_coarse = com2_tuner_coarse;
660
661     freq = com2_stby_freq->getFloatValue();
662     coarse_freq = (int)freq;
663     fine_freq = (int)((freq - coarse_freq) * 40 + 0.5);
664
665     if ( com2_tuner_fine != last_com2_tuner_fine ) {
666         diff = com2_tuner_fine - last_com2_tuner_fine;
667         if ( abs(diff) > 4 ) {
668             // roll over
669             if ( com2_tuner_fine < last_com2_tuner_fine ) {
670                 // going up
671                 diff = 12 - last_com2_tuner_fine + com2_tuner_fine;
672             } else {
673                 // going down
674                 diff = com2_tuner_fine - 12 - last_com2_tuner_fine;
675             }
676         }
677         fine_freq += diff;
678     }
679     while ( fine_freq >= 40.0 ) { fine_freq -= 40.0; }
680     while ( fine_freq < 0.0 )  { fine_freq += 40.0; }
681
682     if ( com2_tuner_coarse != last_com2_tuner_coarse ) {
683         diff = com2_tuner_coarse - last_com2_tuner_coarse;
684         if ( abs(diff) > 4 ) {
685             // roll over
686             if ( com2_tuner_coarse < last_com2_tuner_coarse ) {
687                 // going up
688                 diff = 12 - last_com2_tuner_coarse + com2_tuner_coarse;
689             } else {
690                 // going down
691                 diff = com2_tuner_coarse - 12 - last_com2_tuner_coarse;
692             }
693         }
694         coarse_freq += diff;
695     }
696     if ( coarse_freq < 118.0 ) { coarse_freq += 19.0; }
697     if ( coarse_freq > 136.0 ) { coarse_freq -= 19.0; }
698
699     last_com2_tuner_fine = com2_tuner_fine;
700     last_com2_tuner_coarse = com2_tuner_coarse;
701
702     fgSetFloat( "/radios/comm[1]/frequencies/standby-mhz",
703                 coarse_freq + fine_freq / 40.0 );
704
705     // Nav1 Tuner
706     int nav1_tuner_fine = ((radio_switch_data[9] >> 4) & 0x0f) - 1;
707     int nav1_tuner_coarse = (radio_switch_data[9] & 0x0f) - 1;
708     static int last_nav1_tuner_fine = nav1_tuner_fine;
709     static int last_nav1_tuner_coarse = nav1_tuner_coarse;
710
711     freq = nav1_stby_freq->getFloatValue();
712     coarse_freq = (int)freq;
713     fine_freq = (int)((freq - coarse_freq) * 20 + 0.5);
714
715     if ( nav1_tuner_fine != last_nav1_tuner_fine ) {
716         diff = nav1_tuner_fine - last_nav1_tuner_fine;
717         if ( abs(diff) > 4 ) {
718             // roll over
719             if ( nav1_tuner_fine < last_nav1_tuner_fine ) {
720                 // going up
721                 diff = 12 - last_nav1_tuner_fine + nav1_tuner_fine;
722             } else {
723                 // going down
724                 diff = nav1_tuner_fine - 12 - last_nav1_tuner_fine;
725             }
726         }
727         fine_freq += diff;
728     }
729     while ( fine_freq >= 20.0 ) { fine_freq -= 20.0; }
730     while ( fine_freq < 0.0 )  { fine_freq += 20.0; }
731
732     if ( nav1_tuner_coarse != last_nav1_tuner_coarse ) {
733         diff = nav1_tuner_coarse - last_nav1_tuner_coarse;
734         if ( abs(diff) > 4 ) {
735             // roll over
736             if ( nav1_tuner_coarse < last_nav1_tuner_coarse ) {
737                 // going up
738                 diff = 12 - last_nav1_tuner_coarse + nav1_tuner_coarse;
739             } else {
740                 // going down
741                 diff = nav1_tuner_coarse - 12 - last_nav1_tuner_coarse;
742             }
743         }
744         coarse_freq += diff;
745     }
746     if ( coarse_freq < 108.0 ) { coarse_freq += 10.0; }
747     if ( coarse_freq > 117.0 ) { coarse_freq -= 10.0; }
748
749     last_nav1_tuner_fine = nav1_tuner_fine;
750     last_nav1_tuner_coarse = nav1_tuner_coarse;
751
752     fgSetFloat( "/radios/nav[0]/frequencies/standby-mhz",
753                 coarse_freq + fine_freq / 20.0 );
754
755     // Nav2 Tuner
756     int nav2_tuner_fine = ((radio_switch_data[17] >> 4) & 0x0f) - 1;
757     int nav2_tuner_coarse = (radio_switch_data[17] & 0x0f) - 1;
758     static int last_nav2_tuner_fine = nav2_tuner_fine;
759     static int last_nav2_tuner_coarse = nav2_tuner_coarse;
760
761     freq = nav2_stby_freq->getFloatValue();
762     coarse_freq = (int)freq;
763     fine_freq = (int)((freq - coarse_freq) * 20 + 0.5);
764
765     if ( nav2_tuner_fine != last_nav2_tuner_fine ) {
766         diff = nav2_tuner_fine - last_nav2_tuner_fine;
767         if ( abs(diff) > 4 ) {
768             // roll over
769             if ( nav2_tuner_fine < last_nav2_tuner_fine ) {
770                 // going up
771                 diff = 12 - last_nav2_tuner_fine + nav2_tuner_fine;
772             } else {
773                 // going down
774                 diff = nav2_tuner_fine - 12 - last_nav2_tuner_fine;
775             }
776         }
777         fine_freq += diff;
778     }
779     while ( fine_freq >= 20.0 ) { fine_freq -= 20.0; }
780     while ( fine_freq < 0.0 )  { fine_freq += 20.0; }
781
782     if ( nav2_tuner_coarse != last_nav2_tuner_coarse ) {
783         diff = nav2_tuner_coarse - last_nav2_tuner_coarse;
784         if ( abs(diff) > 4 ) {
785             // roll over
786             if ( nav2_tuner_coarse < last_nav2_tuner_coarse ) {
787                 // going up
788                 diff = 12 - last_nav2_tuner_coarse + nav2_tuner_coarse;
789             } else {
790                 // going down
791                 diff = nav2_tuner_coarse - 12 - last_nav2_tuner_coarse;
792             }
793         }
794         coarse_freq += diff;
795     }
796     if ( coarse_freq < 108.0 ) { coarse_freq += 10.0; }
797     if ( coarse_freq > 117.0 ) { coarse_freq -= 10.0; }
798
799     last_nav2_tuner_fine = nav2_tuner_fine;
800     last_nav2_tuner_coarse = nav2_tuner_coarse;
801
802     fgSetFloat( "/radios/nav[1]/frequencies/standby-mhz", 
803                 coarse_freq + fine_freq / 20.0);
804
805     // ADF Tuner
806     int adf_tuner_fine = ((radio_switch_data[21] >> 4) & 0x0f) - 1;
807     int adf_tuner_coarse = (radio_switch_data[21] & 0x0f) - 1;
808     static int last_adf_tuner_fine = adf_tuner_fine;
809     static int last_adf_tuner_coarse = adf_tuner_coarse;
810
811     if ( adf_count_mode->getIntValue() == 2 ) {
812         // tune count down timer
813         value = adf_elapsed_timer->getDoubleValue();
814     } else {
815         // tune frequency
816         if ( adf_stby_mode->getIntValue() == 1 ) {
817             value = adf_freq->getFloatValue();
818         } else {
819             value = adf_stby_freq->getFloatValue();
820         }
821     }
822
823     if ( adf_tuner_fine != last_adf_tuner_fine ) {
824         diff = adf_tuner_fine - last_adf_tuner_fine;
825         if ( abs(diff) > 4 ) {
826             // roll over
827             if ( adf_tuner_fine < last_adf_tuner_fine ) {
828                 // going up
829                 diff = 12 - last_adf_tuner_fine + adf_tuner_fine;
830             } else {
831                 // going down
832                 diff = adf_tuner_fine - 12 - last_adf_tuner_fine;
833             }
834         }
835         value += diff;
836     }
837
838     if ( adf_tuner_coarse != last_adf_tuner_coarse ) {
839         diff = adf_tuner_coarse - last_adf_tuner_coarse;
840         if ( abs(diff) > 4 ) {
841             // roll over
842             if ( adf_tuner_coarse < last_adf_tuner_coarse ) {
843                 // going up
844                 diff = 12 - last_adf_tuner_coarse + adf_tuner_coarse;
845             } else {
846                 // going down
847                 diff = adf_tuner_coarse - 12 - last_adf_tuner_coarse;
848             }
849         }
850         if ( adf_count_mode->getIntValue() == 2 ) {
851             value += 60 * diff;
852         } else {
853             value += 25 * diff;
854         }
855     }
856     if ( adf_count_mode->getIntValue() == 2 ) {
857         if ( value < 0 ) { value += 3600; }
858         if ( value > 3599 ) { value -= 3600; }
859     } else {
860         if ( value < 200 ) { value += 1600; }
861         if ( value > 1799 ) { value -= 1600; }
862     }
863  
864     last_adf_tuner_fine = adf_tuner_fine;
865     last_adf_tuner_coarse = adf_tuner_coarse;
866
867     if ( adf_count_mode->getIntValue() == 2 ) {
868         fgSetFloat( "/radios/adf/elapsed-timer", value );
869     } else {
870         if ( adf_stby_mode->getIntValue() == 1 ) {
871             fgSetFloat( "/radios/adf/frequencies/selected-khz", value );
872         } else {
873             fgSetFloat( "/radios/adf/frequencies/standby-khz", value );
874         }
875     }
876
877     // ADF Modes 
878     fgSetInt( "/radios/adf/adf-btn", !(radio_switch_data[23] & 0x01) );
879     fgSetInt( "/radios/adf/bfo-btn", !(radio_switch_data[23] >> 1 & 0x01) );
880     fgSetInt( "/radios/adf/frq-btn", !(radio_switch_data[23] >> 2 & 0x01) );
881     fgSetInt( "/radios/adf/flt-et-btn", !(radio_switch_data[23] >> 3 & 0x01) );
882     fgSetInt( "/radios/adf/set-rst-btn", !(radio_switch_data[23] >> 4 & 0x01) );
883     /* cout << "adf = " << !(radio_switch_data[23] & 0x01)
884          << " bfo = " << !(radio_switch_data[23] >> 1 & 0x01)
885          << " stby = " << !(radio_switch_data[23] >> 2 & 0x01)
886          << " timer = " << !(radio_switch_data[23] >> 3 & 0x01)
887          << " set/rst = " << !(radio_switch_data[23] >> 4 & 0x01)
888          << endl; */
889
890     return true;
891 }
892
893
894 /////////////////////////////////////////////////////////////////////
895 // Update the radio display 
896 /////////////////////////////////////////////////////////////////////
897
898 bool FGATC610x::do_radio_display() {
899
900     char digits[10];
901     int i;
902
903     if ( dme_switch != 0 ) {
904         // DME minutes
905         float minutes = dme_min->getFloatValue();
906         if ( minutes > 999 ) {
907             minutes = 999.0;
908         }
909         sprintf(digits, "%03.0f", minutes);
910         for ( i = 0; i < 6; ++i ) {
911             digits[i] -= '0';
912         }
913         radio_display_data[0] = digits[1] << 4 | digits[2];
914         radio_display_data[1] = 0xf0 | digits[0];
915         
916         // DME knots
917         float knots = dme_kt->getFloatValue();
918         if ( knots > 999 ) {
919             knots = 999.0;
920         }
921         sprintf(digits, "%03.0f", knots);
922         for ( i = 0; i < 6; ++i ) {
923             digits[i] -= '0';
924         }
925         radio_display_data[2] = digits[1] << 4 | digits[2];
926         radio_display_data[3] = 0xf0 | digits[0];
927
928         // DME distance (nm)
929         float nm = dme_nm->getFloatValue();
930         if ( nm > 99 ) {
931             nm = 99.0;
932         }
933         sprintf(digits, "%04.1f", nm);
934         for ( i = 0; i < 6; ++i ) {
935             digits[i] -= '0';
936         }
937         radio_display_data[4] = digits[1] << 4 | digits[3];
938         radio_display_data[5] = 0x00 | digits[0];
939         // the 0x00 in the upper nibble of the 6th byte of each
940         // display turns on the decimal point
941     } else {
942         // blank dem display
943         for ( i = 0; i < 6; ++i ) {
944             radio_display_data[i] = 0xff;
945         }
946     }
947
948     // Com1 standby frequency
949     float com1_stby = com1_stby_freq->getFloatValue();
950     if ( fabs(com1_stby) > 999.99 ) {
951         com1_stby = 0.0;
952     }
953     sprintf(digits, "%06.3f", com1_stby);
954     for ( i = 0; i < 6; ++i ) {
955         digits[i] -= '0';
956     }
957     radio_display_data[6] = digits[4] << 4 | digits[5];
958     radio_display_data[7] = digits[1] << 4 | digits[2];
959     radio_display_data[8] = 0xf0 | digits[0];
960
961     // Com1 in use frequency
962     float com1 = com1_freq->getFloatValue();
963     if ( fabs(com1) > 999.99 ) {
964         com1 = 0.0;
965     }
966     sprintf(digits, "%06.3f", com1);
967     for ( i = 0; i < 6; ++i ) {
968         digits[i] -= '0';
969     }
970     radio_display_data[9] = digits[4] << 4 | digits[5];
971     radio_display_data[10] = digits[1] << 4 | digits[2];
972     radio_display_data[11] = 0x00 | digits[0];
973     // the 0x00 in the upper nibble of the 6th byte of each display
974     // turns on the decimal point
975
976     // Com2 standby frequency
977     float com2_stby = com2_stby_freq->getFloatValue();
978     if ( fabs(com2_stby) > 999.99 ) {
979         com2_stby = 0.0;
980     }
981     sprintf(digits, "%06.3f", com2_stby);
982     for ( i = 0; i < 6; ++i ) {
983         digits[i] -= '0';
984     }
985     radio_display_data[18] = digits[4] << 4 | digits[5];
986     radio_display_data[19] = digits[1] << 4 | digits[2];
987     radio_display_data[20] = 0xf0 | digits[0];
988
989     // Com2 in use frequency
990     float com2 = com2_freq->getFloatValue();
991     if ( fabs(com2) > 999.99 ) {
992         com2 = 0.0;
993     }
994     sprintf(digits, "%06.3f", com2);
995     for ( i = 0; i < 6; ++i ) {
996         digits[i] -= '0';
997     }
998     radio_display_data[21] = digits[4] << 4 | digits[5];
999     radio_display_data[22] = digits[1] << 4 | digits[2];
1000     radio_display_data[23] = 0x00 | digits[0];
1001     // the 0x00 in the upper nibble of the 6th byte of each display
1002     // turns on the decimal point
1003
1004     // Nav1 standby frequency
1005     float nav1_stby = nav1_stby_freq->getFloatValue();
1006     if ( fabs(nav1_stby) > 999.99 ) {
1007         nav1_stby = 0.0;
1008     }
1009     sprintf(digits, "%06.2f", nav1_stby);
1010     for ( i = 0; i < 6; ++i ) {
1011         digits[i] -= '0';
1012     }
1013     radio_display_data[12] = digits[4] << 4 | digits[5];
1014     radio_display_data[13] = digits[1] << 4 | digits[2];
1015     radio_display_data[14] = 0xf0 | digits[0];
1016
1017     // Nav1 in use frequency
1018     float nav1 = nav1_freq->getFloatValue();
1019     if ( fabs(nav1) > 999.99 ) {
1020         nav1 = 0.0;
1021     }
1022     sprintf(digits, "%06.2f", nav1);
1023     for ( i = 0; i < 6; ++i ) {
1024         digits[i] -= '0';
1025     }
1026     radio_display_data[15] = digits[4] << 4 | digits[5];
1027     radio_display_data[16] = digits[1] << 4 | digits[2];
1028     radio_display_data[17] = 0x00 | digits[0];
1029     // the 0x00 in the upper nibble of the 6th byte of each display
1030     // turns on the decimal point
1031
1032     // Nav2 standby frequency
1033     float nav2_stby = nav2_stby_freq->getFloatValue();
1034     if ( fabs(nav2_stby) > 999.99 ) {
1035         nav2_stby = 0.0;
1036     }
1037     sprintf(digits, "%06.2f", nav2_stby);
1038     for ( i = 0; i < 6; ++i ) {
1039         digits[i] -= '0';
1040     }
1041     radio_display_data[24] = digits[4] << 4 | digits[5];
1042     radio_display_data[25] = digits[1] << 4 | digits[2];
1043     radio_display_data[26] = 0xf0 | digits[0];
1044
1045     // Nav2 in use frequency
1046     float nav2 = nav2_freq->getFloatValue();
1047     if ( fabs(nav2) > 999.99 ) {
1048         nav2 = 0.0;
1049     }
1050     sprintf(digits, "%06.2f", nav2);
1051     for ( i = 0; i < 6; ++i ) {
1052         digits[i] -= '0';
1053     }
1054     radio_display_data[27] = digits[4] << 4 | digits[5];
1055     radio_display_data[28] = digits[1] << 4 | digits[2];
1056     radio_display_data[29] = 0x00 | digits[0];
1057     // the 0x00 in the upper nibble of the 6th byte of each display
1058     // turns on the decimal point
1059
1060     // ADF standby frequency / timer
1061     if ( adf_on_off_vol->getDoubleValue() >= 0.01 ) {
1062         if ( adf_stby_mode->getIntValue() == 0 ) {
1063             // frequency
1064             float adf_stby = adf_stby_freq->getFloatValue();
1065             if ( fabs(adf_stby) > 1799 ) {
1066                 adf_stby = 1799;
1067             }
1068             sprintf(digits, "%04.0f", adf_stby);
1069             for ( i = 0; i < 6; ++i ) {
1070                 digits[i] -= '0';
1071             }
1072             radio_display_data[30] = digits[3] << 4 | 0x0f;
1073             radio_display_data[31] = digits[1] << 4 | digits[2];
1074             if ( digits[0] == 0 ) {
1075                 radio_display_data[32] = 0xff;
1076             } else {
1077                 radio_display_data[32] = 0xf0 | digits[0];
1078             }
1079         } else {
1080             // timer
1081             double time;
1082             int hours, min, sec;
1083             if ( adf_timer_mode->getIntValue() == 0 ) {
1084                 time = adf_flight_timer->getDoubleValue();
1085             } else {
1086                 time = adf_elapsed_timer->getDoubleValue();
1087             }
1088             // cout << time << endl;
1089             hours = (int)(time / 3600.0);
1090             time -= hours * 3600.00;
1091             min = (int)(time / 60.0);
1092             time -= min * 60.0;
1093             sec = (int)time;
1094             int big, small;
1095             if ( hours > 0 ) {
1096                 big = hours;
1097                 if ( big > 99 ) {
1098                     big = 99;
1099                 }
1100                 small = min;
1101             } else {
1102                 big = min;
1103                 small = sec;
1104             }
1105             if ( big > 99 ) {
1106                 big = 99;
1107             }
1108             // cout << big << ":" << small << endl;
1109             sprintf(digits, "%02d%02d", big, small);
1110             for ( i = 0; i < 6; ++i ) {
1111                 digits[i] -= '0';
1112             }
1113             radio_display_data[30] = digits[3] << 4 | 0x0f;
1114             radio_display_data[31] = digits[1] << 4 | digits[2];
1115             radio_display_data[32] = 0xf0 | digits[0];
1116         }
1117
1118         // ADF in use frequency
1119         float adf = adf_freq->getFloatValue();
1120         if ( fabs(adf) > 1799 ) {
1121             adf = 1799;
1122         }
1123         sprintf(digits, "%04.0f", adf);
1124         for ( i = 0; i < 6; ++i ) {
1125             digits[i] -= '0';
1126         }
1127         radio_display_data[33] = digits[2] << 4 | digits[3];
1128         if ( digits[0] == 0 ) {
1129             radio_display_data[34] = 0xf0 | digits[1];
1130         } else {
1131             radio_display_data[34] = digits[0] << 4 | digits[1];
1132         }
1133     } else {
1134         radio_display_data[30] = 0xff;
1135         radio_display_data[31] = 0xff;
1136         radio_display_data[32] = 0xff;
1137         radio_display_data[33] = 0xff;
1138         radio_display_data[34] = 0xff;
1139     }
1140     
1141
1142     ATC610xSetRadios( radios_fd, radio_display_data );
1143
1144     return true;
1145 }
1146
1147
1148 /////////////////////////////////////////////////////////////////////
1149 // Drive the stepper motors
1150 /////////////////////////////////////////////////////////////////////
1151
1152 bool FGATC610x::do_steppers() {
1153     float diff = mag_compass->getFloatValue() - compass_position;
1154     while ( diff < -180.0 ) { diff += 360.0; }
1155     while ( diff >  180.0 ) { diff -= 360.0; }
1156
1157     int steps = (int)(diff * 4);
1158     // cout << "steps = " << steps << endl;
1159     if ( steps > 4 ) { steps = 4; }
1160     if ( steps < -4 ) { steps = -4; }
1161
1162     if ( abs(steps) > 0 ) {
1163         unsigned char cmd = 0x80;       // stepper command
1164         if ( steps > 0 ) {
1165             cmd |= 0x20;                // go up
1166         } else {
1167             cmd |= 0x00;                // go down
1168         }
1169         cmd |= abs(steps);
1170
1171         // sync compass_position with hardware position
1172         compass_position += (float)steps / 4.0;
1173
1174         ATC610xSetStepper( stepper_fd, ATC_COMPASS_CH, cmd );
1175     }
1176
1177     return true;
1178 }
1179
1180
1181 /////////////////////////////////////////////////////////////////////
1182 // Read the switch positions
1183 /////////////////////////////////////////////////////////////////////
1184
1185 // decode the packed switch data
1186 static void update_switch_matrix(
1187         int board,
1188         unsigned char switch_data[ATC_SWITCH_BYTES],
1189         int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES] )
1190 {
1191     for ( int row = 0; row < ATC_SWITCH_BYTES; ++row ) {
1192         unsigned char switches = switch_data[row];
1193
1194         for( int column = 0; column < ATC_NUM_COLS; ++column ) {
1195             switch_matrix[board][column][row] = switches & 1;
1196             switches = switches >> 1;
1197         }                       
1198     }
1199 }                     
1200
1201 bool FGATC610x::do_switches() {
1202     ATC610xReadSwitches( switches_fd, switch_data );
1203
1204     // unpack the switch data
1205     int switch_matrix[2][ATC_NUM_COLS][ATC_SWITCH_BYTES];
1206     update_switch_matrix( board, switch_data, switch_matrix );
1207
1208     // magnetos and starter switch
1209     int magnetos = 0;
1210     bool starter = false;
1211     if ( switch_matrix[board][3][1] == 1 ) {
1212         magnetos = 3;
1213         starter = true;
1214     } else if ( switch_matrix[board][2][1] == 1 ) {
1215         magnetos = 3;
1216         starter = false;
1217     } else if ( switch_matrix[board][1][1] == 1 ) {
1218         magnetos = 2;
1219         starter = false;
1220     } else if ( switch_matrix[board][0][1] == 1 ) {
1221         magnetos = 1;
1222         starter = false;
1223     } else {
1224         magnetos = 0;
1225         starter = false;
1226     }
1227
1228     // flaps
1229     float flaps = 0.0;
1230     if ( switch_matrix[board][6][3] == 1 ) {
1231         flaps = 1.0;
1232     } else if ( switch_matrix[board][5][3] == 1 ) {
1233         flaps = 2.0 / 3.0;
1234     } else if ( switch_matrix[board][4][3] == 1 ) {
1235         flaps = 1.0 / 3.0;
1236     } else if ( switch_matrix[board][4][3] == 0 ) {
1237         flaps = 0.0;
1238     }
1239
1240     // do a bit of filtering on the magneto/starter switch and the
1241     // flap lever because these are not well debounced in hardware
1242     static int mag1, mag2, mag3;
1243     mag3 = mag2;
1244     mag2 = mag1;
1245     mag1 = magnetos;
1246     if ( mag1 == mag2 && mag2 == mag3 ) {
1247         fgSetInt( "/controls/magnetos[0]", magnetos );
1248     }
1249
1250     static bool start1, start2, start3;
1251     start3 = start2;
1252     start2 = start1;
1253     start1 = starter;
1254     if ( start1 == start2 && start2 == start3 ) {
1255         fgSetBool( "/controls/starter[0]", starter );
1256     }
1257
1258     static float flap1, flap2, flap3;
1259     flap3 = flap2;
1260     flap2 = flap1;
1261     flap1 = flaps;
1262     if ( flap1 == flap2 && flap2 == flap3 ) {
1263         fgSetFloat( "/controls/flaps", flaps );
1264     }
1265
1266     return true;
1267 }
1268
1269
1270 bool FGATC610x::process() {
1271     SGTimeStamp current;
1272     current.stamp();
1273
1274     double dt = (double)(current - last_time_stamp) / 1000000;
1275     last_time_stamp.stamp();
1276
1277     // Lock the hardware, skip if it's not ready yet
1278     if ( ATC610xLock( lock_fd ) > 0 ) {
1279
1280         do_analog_in();
1281         do_lights( dt );
1282         do_radio_switches();
1283         do_radio_display();
1284         do_steppers();
1285         do_switches();
1286         
1287         ATC610xRelease( lock_fd );
1288
1289         return true;
1290     } else {
1291         return false;
1292     }
1293 }
1294
1295
1296 bool FGATC610x::close() {
1297
1298     return true;
1299 }