]> git.mxchange.org Git - flightgear.git/blob - src/Network/generic.cxx
FGCom[-sa]: add IAX denoiser and auto gain + set silence threshold
[flightgear.git] / src / Network / generic.cxx
1 // generic.cxx -- generic protocol class
2 //
3 // Written by Curtis Olson, started November 1999.
4 //
5 // Copyright (C) 1999  Curtis L. Olson - http://www.flightgear.org/~curt
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include <string.h>                // strstr()
28 #include <stdlib.h>                // strtod(), atoi()
29 #include <cstdio>
30
31 #include <simgear/debug/logstream.hxx>
32 #include <simgear/io/iochannel.hxx>
33 #include <simgear/structure/exception.hxx>
34 #include <simgear/misc/sg_path.hxx>
35 #include <simgear/misc/stdint.hxx>
36 #include <simgear/misc/strutils.hxx>
37 #include <simgear/props/props.hxx>
38 #include <simgear/props/props_io.hxx>
39 #include <simgear/math/SGMath.hxx>
40
41 #include <Main/globals.hxx>
42 #include <Main/fg_props.hxx>
43 #include <Main/fg_os.hxx>
44 #include <Main/util.hxx>
45 #include "generic.hxx"
46
47 using simgear::strutils::unescape;
48
49 class FGProtocolWrapper {
50 public:
51   virtual ~FGProtocolWrapper() {}
52   virtual int wrap( size_t n, uint8_t * buf ) = 0;
53   virtual int unwrap( size_t n, uint8_t * buf ) = 0;
54 };
55
56 /**
57  * http://www.ka9q.net/papers/kiss.html
58  */
59 class FGKissWrapper : public FGProtocolWrapper {
60 public:
61   virtual int wrap( size_t n, uint8_t * buf );
62   virtual int unwrap( size_t n, uint8_t * buf );
63 private:
64   static const uint8_t  FEND;
65   static const uint8_t  FESC;
66   static const uint8_t  TFEND;
67   static const uint8_t  TFESC;
68 };
69
70 const uint8_t  FGKissWrapper::FEND  = 0xC0;
71 const uint8_t  FGKissWrapper::FESC  = 0xDB;
72 const uint8_t  FGKissWrapper::TFEND = 0xDC;
73 const uint8_t  FGKissWrapper::TFESC = 0xDD;
74
75 int FGKissWrapper::wrap( size_t n, uint8_t * buf )
76 {
77   std::vector<uint8_t> dest;
78   uint8_t *sp = buf;
79
80   dest.push_back(FEND);
81   dest.push_back(0); // command/channel always zero
82   for( size_t i = 0; i < n; i++ ) {
83     uint8_t c = *sp++;
84     switch( c ) {
85       case FESC:
86         dest.push_back(FESC);
87         dest.push_back(TFESC);
88         break;
89
90       case FEND:
91         dest.push_back(FESC);
92         dest.push_back(TFEND);
93         break;
94
95       default:
96         dest.push_back(c);
97         break;
98     }
99   }
100   dest.push_back(FEND);
101
102   memcpy( buf, dest.data(), dest.size() );
103   return dest.size();
104 }
105
106 int FGKissWrapper::unwrap( size_t n, uint8_t * buf )
107 {
108   uint8_t * sp = buf;
109
110   // look for FEND
111   while( 0 < n && FEND != *sp ) {
112     sp++;
113     n--;
114   }
115
116   // ignore all leading FEND
117   while( 0 < n && FEND == *sp ) {
118     sp++;
119     n--;
120   }
121
122   if( 0 == n ) return 0;
123
124   std::vector<uint8_t> dest;
125   {
126     bool escaped = false;
127
128     while( 0 < n ) {
129
130       n--;
131       uint8_t c = *sp++;
132
133       if( escaped ) {
134         switch( c ) {
135
136           case TFESC:
137             dest.push_back( FESC );
138             break;
139
140           case TFEND:
141             dest.push_back( FEND );
142             break;
143
144           default: // this is an error - ignore and continue
145             break;
146         }
147
148         escaped = false;
149
150       } else {
151
152         switch( c ) {
153           case FESC:
154             escaped = true;
155             break;
156
157           case FEND:
158             if( 0 != n ) {
159               SG_LOG(SG_IO, SG_WARN,
160                "KISS frame detected FEND before end of frame. Trailing data dropped." );
161             }
162             n = 0; 
163             break;
164
165           default:
166             dest.push_back( c );
167             break;
168         }
169       }
170     }
171   }
172
173   memcpy( buf, dest.data(), dest.size() );
174   return dest.size();
175 }
176
177
178 class FGSTXETXWrapper : public FGProtocolWrapper {
179 public:
180   virtual int wrap( size_t n, uint8_t * buf );
181   virtual int unwrap( size_t n, uint8_t * buf );
182
183   static const uint8_t  STX;
184   static const uint8_t  ETX;
185   static const uint8_t  DLE;
186 };
187
188 const uint8_t  FGSTXETXWrapper::STX  = 0x02;
189 const uint8_t  FGSTXETXWrapper::ETX  = 0x03;
190 const uint8_t  FGSTXETXWrapper::DLE  = 0x00;
191
192 int FGSTXETXWrapper::wrap( size_t n, uint8_t * buf )
193 {
194   // stuff payload as
195   // <dle><stx>payload<dle><etx>
196   // if payload contains <dle>, stuff <dle> as <dle><dle>
197   std::vector<uint8_t> dest;
198   uint8_t *sp = buf;
199
200   dest.push_back(DLE);
201   dest.push_back(STX);
202
203   while( n > 0 ) {
204     n--;
205
206     if( DLE == *sp )
207       dest.push_back(DLE);
208
209     dest.push_back(*sp++);
210   }
211
212   dest.push_back(DLE);
213   dest.push_back(ETX);
214
215   memcpy( buf, dest.data(), dest.size() ); 
216   return dest.size();
217 }
218
219 int FGSTXETXWrapper::unwrap( size_t n, uint8_t * buf )
220 {
221   return n;
222 }
223
224 FGGeneric::FGGeneric(vector<string> tokens) : exitOnError(false), initOk(false), wrapper(NULL)
225 {
226     size_t configToken;
227     if (tokens[1] == "socket") {
228         configToken = 7;
229     } else if (tokens[1] == "file") {
230         configToken = 5;
231     } else {
232         configToken = 6; 
233     }
234
235     if ((configToken >= tokens.size())||(tokens[ configToken ] == "")) {
236        SG_LOG(SG_NETWORK, SG_ALERT,
237               "Not enough tokens passed for generic '" << tokens[1] << "' protocol. ");
238        return;
239     }
240
241     string config = tokens[ configToken ];
242     file_name = config+".xml";
243     direction = tokens[2];
244
245     if (direction != "in" && direction != "out" && direction != "bi") {
246         SG_LOG(SG_NETWORK, SG_ALERT, "Unsuported protocol direction: "
247                << direction);
248         return;
249     }
250
251     reinit();
252 }
253
254 FGGeneric::~FGGeneric() {
255   delete wrapper;
256 }
257
258 union u32 {
259     uint32_t intVal;
260     float floatVal;
261 };
262
263 union u64 {
264     uint64_t longVal;
265     double doubleVal;
266 };
267
268 // generate the message
269 bool FGGeneric::gen_message_binary() {
270     string generic_sentence;
271     length = 0;
272
273     double val;
274     for (unsigned int i = 0; i < _out_message.size(); i++) {
275
276         switch (_out_message[i].type) {
277         case FG_INT:
278         {
279             val = _out_message[i].offset +
280                   _out_message[i].prop->getIntValue() * _out_message[i].factor;
281             int32_t intVal = val;
282             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
283                 intVal = (int32_t) sg_bswap_32((uint32_t)intVal);
284             }
285             memcpy(&buf[length], &intVal, sizeof(int32_t));
286             length += sizeof(int32_t);
287             break;
288         }
289
290         case FG_BOOL:
291             buf[length] = (char) (_out_message[i].prop->getBoolValue() ? true : false);
292             length += 1;
293             break;
294
295         case FG_FIXED:
296         {
297             val = _out_message[i].offset +
298                  _out_message[i].prop->getFloatValue() * _out_message[i].factor;
299
300             int32_t fixed = (int)(val * 65536.0f);
301             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
302                 fixed = (int32_t) sg_bswap_32((uint32_t)fixed);
303             } 
304             memcpy(&buf[length], &fixed, sizeof(int32_t));
305             length += sizeof(int32_t);
306             break;
307         }
308
309         case FG_FLOAT:
310         {
311             val = _out_message[i].offset +
312                  _out_message[i].prop->getFloatValue() * _out_message[i].factor;
313             u32 tmpun32;
314             tmpun32.floatVal = static_cast<float>(val);
315
316             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
317                 tmpun32.intVal = sg_bswap_32(tmpun32.intVal);
318             }
319             memcpy(&buf[length], &tmpun32.intVal, sizeof(uint32_t));
320             length += sizeof(uint32_t);
321             break;
322         }
323
324         case FG_DOUBLE:
325         {
326             val = _out_message[i].offset +
327                  _out_message[i].prop->getDoubleValue() * _out_message[i].factor;
328             u64 tmpun64;
329             tmpun64.doubleVal = val;
330
331             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
332                 tmpun64.longVal = sg_bswap_64(tmpun64.longVal);
333             }
334             memcpy(&buf[length], &tmpun64.longVal, sizeof(uint64_t));
335             length += sizeof(uint64_t);
336             break;
337         }
338
339         case FG_BYTE:
340         {
341             val = _out_message[i].offset +
342                   _out_message[i].prop->getIntValue() * _out_message[i].factor;
343             int8_t byteVal = val;
344             memcpy(&buf[length], &byteVal, sizeof(int8_t));
345             length += sizeof(int8_t);
346             break;
347         }
348
349         default: // SG_STRING
350             const char *strdata = _out_message[i].prop->getStringValue();
351             int32_t strlength = strlen(strdata);
352
353             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
354                 SG_LOG( SG_IO, SG_ALERT, "Generic protocol: "
355                         "FG_STRING will be written in host byte order.");
356             }
357             /* Format for strings is 
358              * [length as int, 4 bytes][ASCII data, length bytes]
359              */
360             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
361                 strlength = sg_bswap_32(strlength);
362             }
363             memcpy(&buf[length], &strlength, sizeof(int32_t));
364             length += sizeof(int32_t);
365             strncpy(&buf[length], strdata, strlength);
366             length += strlength; 
367             /* FIXME padding for alignment? Something like: 
368              * length += (strlength % 4 > 0 ? sizeof(int32_t) - strlength % 4 : 0;
369              */
370             break;
371
372         }
373     }
374
375     // add the footer to the packet ("line")
376     switch (binary_footer_type) {
377         case FOOTER_LENGTH:
378             binary_footer_value = length;
379             break;
380
381         case FOOTER_MAGIC:
382         case FOOTER_NONE:
383             break;
384     }
385
386     if (binary_footer_type != FOOTER_NONE) {
387         int32_t intValue = binary_footer_value;
388         if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
389             intValue = sg_bswap_32(binary_footer_value);
390         }
391         memcpy(&buf[length], &intValue, sizeof(int32_t));
392         length += sizeof(int32_t);
393     }
394
395     if( wrapper ) length = wrapper->wrap( length, reinterpret_cast<uint8_t*>(buf) );
396
397     return true;
398 }
399
400 bool FGGeneric::gen_message_ascii() {
401     string generic_sentence;
402     char tmp[255];
403     length = 0;
404
405     double val;
406     for (unsigned int i = 0; i < _out_message.size(); i++) {
407
408         if (i > 0) {
409             generic_sentence += var_separator;
410         }
411         
412         string format = simgear::strutils::sanitizePrintfFormat(_out_message[i].format);
413
414         switch (_out_message[i].type) {
415         case FG_BYTE:
416         case FG_INT:
417             val = _out_message[i].offset +
418                   _out_message[i].prop->getIntValue() * _out_message[i].factor;
419             snprintf(tmp, 255, format.c_str(), (int)val);
420             break;
421
422         case FG_BOOL:
423             snprintf(tmp, 255, format.c_str(),
424                                _out_message[i].prop->getBoolValue());
425             break;
426
427         case FG_FIXED:
428             val = _out_message[i].offset +
429                 _out_message[i].prop->getFloatValue() * _out_message[i].factor;
430             snprintf(tmp, 255, format.c_str(), (float)val);
431             break;
432
433         case FG_FLOAT:
434             val = _out_message[i].offset +
435                 _out_message[i].prop->getFloatValue() * _out_message[i].factor;
436             snprintf(tmp, 255, format.c_str(), (float)val);
437             break;
438
439         case FG_DOUBLE:
440             val = _out_message[i].offset +
441                 _out_message[i].prop->getDoubleValue() * _out_message[i].factor;
442             snprintf(tmp, 255, format.c_str(), (double)val);
443             break;
444
445         default: // SG_STRING
446             snprintf(tmp, 255, format.c_str(),
447                                    _out_message[i].prop->getStringValue());
448         }
449
450         generic_sentence += tmp;
451     }
452
453     /* After each lot of variables has been added, put the line separator
454      * char/string
455      */
456     generic_sentence += line_separator;
457
458     length =  generic_sentence.length();
459     strncpy( buf, generic_sentence.c_str(), length );
460
461     return true;
462 }
463
464 bool FGGeneric::gen_message() {
465     if (binary_mode) {
466         return gen_message_binary();
467     } else {
468         return gen_message_ascii();
469     }
470 }
471
472 bool FGGeneric::parse_message_binary(int length) {
473     char *p2, *p1 = buf;
474     int32_t tmp32;
475     int i = -1;
476
477     p2 = p1 + length;
478     while ((++i < (int)_in_message.size()) && (p1  < p2)) {
479
480         switch (_in_message[i].type) {
481         case FG_INT:
482             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
483                 tmp32 = sg_bswap_32(*(int32_t *)p1);
484             } else {
485                 tmp32 = *(int32_t *)p1;
486             }
487             updateValue(_in_message[i], (int)tmp32);
488             p1 += sizeof(int32_t);
489             break;
490
491         case FG_BOOL:
492             updateValue(_in_message[i], p1[0] != 0);
493             p1 += 1;
494             break;
495
496         case FG_FIXED:
497             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
498                 tmp32 = sg_bswap_32(*(int32_t *)p1);
499             } else {
500                 tmp32 = *(int32_t *)p1;
501             }
502             updateValue(_in_message[i], (float)tmp32 / 65536.0f);
503             p1 += sizeof(int32_t);
504             break;
505
506         case FG_FLOAT:
507             u32 tmpun32;
508             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
509                 tmpun32.intVal = sg_bswap_32(*(uint32_t *)p1);
510             } else {
511                 tmpun32.floatVal = *(float *)p1;
512             }
513             updateValue(_in_message[i], tmpun32.floatVal);
514             p1 += sizeof(int32_t);
515             break;
516
517         case FG_DOUBLE:
518             u64 tmpun64;
519             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
520                 tmpun64.longVal = sg_bswap_64(*(uint64_t *)p1);
521             } else {
522                 tmpun64.doubleVal = *(double *)p1;
523             }
524             updateValue(_in_message[i], tmpun64.doubleVal);
525             p1 += sizeof(int64_t);
526             break;
527
528         case FG_BYTE:
529             tmp32 = *(int8_t *)p1;
530             updateValue(_in_message[i], (int)tmp32);
531             p1 += sizeof(int8_t);
532             break;
533
534         default: // SG_STRING
535             SG_LOG( SG_IO, SG_ALERT, "Generic protocol: "
536                     "Ignoring unsupported binary input chunk type.");
537             break;
538         }
539     }
540     
541     return true;
542 }
543
544 bool FGGeneric::parse_message_ascii(int length) {
545     char *p1 = buf;
546     int i = -1;
547     int chunks = _in_message.size();
548     int line_separator_size = line_separator.size();
549
550     if (length < line_separator_size ||
551         line_separator.compare(buf + length - line_separator_size) != 0) {
552
553         SG_LOG(SG_IO, SG_WARN,
554                "Input line does not end with expected line separator." );
555     } else {
556         buf[length - line_separator_size] = 0;
557     }
558
559     size_t varsep_len = var_separator.length();
560     while ((++i < chunks) && p1) {
561         char* p2 = NULL;
562
563         if (varsep_len > 0)
564         {
565             p2 = strstr(p1, var_separator.c_str());
566             if (p2) {
567                 *p2 = 0;
568                 p2 += varsep_len;
569             }
570         }
571
572         switch (_in_message[i].type) {
573         case FG_BYTE:
574         case FG_INT:
575             updateValue(_in_message[i], atoi(p1));
576             break;
577
578         case FG_BOOL:
579             updateValue(_in_message[i], atof(p1) != 0.0);
580             break;
581
582         case FG_FIXED:
583         case FG_FLOAT:
584             updateValue(_in_message[i], (float)strtod(p1, 0));
585             break;
586
587         case FG_DOUBLE:
588             updateValue(_in_message[i], (double)strtod(p1, 0));
589             break;
590
591         default: // SG_STRING
592             _in_message[i].prop->setStringValue(p1);
593             break;
594         }
595
596         p1 = p2;
597     }
598
599     return true;
600 }
601
602 bool FGGeneric::parse_message_len(int length) {
603     if (binary_mode) {
604         return parse_message_binary(length);
605     } else {
606         return parse_message_ascii(length);
607     }
608 }
609
610
611 // open hailing frequencies
612 bool FGGeneric::open() {
613     if ( is_enabled() ) {
614         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
615                 << "is already in use, ignoring" );
616         return false;
617     }
618
619     SGIOChannel *io = get_io_channel();
620
621     if ( ! io->open( get_direction() ) ) {
622         SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
623         return false;
624     }
625
626     set_enabled( true );
627
628     if ( ((get_direction() == SG_IO_OUT )||
629           (get_direction() == SG_IO_BI))
630           && ! preamble.empty() ) {
631         if ( ! io->write( preamble.c_str(), preamble.size() ) ) {
632             SG_LOG( SG_IO, SG_WARN, "Error writing preamble." );
633             return false;
634         }
635     }
636
637     return true;
638 }
639
640
641 // process work for this port
642 bool FGGeneric::process() {
643     SGIOChannel *io = get_io_channel();
644
645     if ( (get_direction() == SG_IO_OUT) ||
646          (get_direction() == SG_IO_BI) ) {
647         gen_message();
648         if ( ! io->write( buf, length ) ) {
649             SG_LOG( SG_IO, SG_WARN, "Error writing data." );
650             goto error_out;
651         }
652     }
653
654     if (( get_direction() == SG_IO_IN ) ||
655         (get_direction() == SG_IO_BI) ) {
656         if ( io->get_type() == sgFileType ) {
657             if (!binary_mode) {
658                 length = io->readline( buf, FG_MAX_MSG_SIZE );
659                 if ( length > 0 ) {
660                     parse_message_len( length );
661                 } else {
662                     SG_LOG( SG_IO, SG_ALERT, "Error reading data." );
663                     return false;
664                 }
665             } else {
666                 length = io->read( buf, binary_record_length );
667                 if ( length == binary_record_length ) {
668                     parse_message_len( length );
669                 } else {
670                     SG_LOG( SG_IO, SG_ALERT,
671                             "Generic protocol: Received binary "
672                             "record of unexpected size, expected: "
673                             << binary_record_length << " but received: "
674                             << length);
675                 }
676             }
677         } else {
678             if (!binary_mode) {
679                 while ((length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
680                     parse_message_len( length );
681                 }
682             } else {
683                 while ((length = io->read( buf, binary_record_length )) 
684                           == binary_record_length ) {
685                     parse_message_len( length );
686                 }
687
688                 if ( length > 0 ) {
689                     SG_LOG( SG_IO, SG_ALERT,
690                         "Generic protocol: Received binary "
691                         "record of unexpected size, expected: "
692                         << binary_record_length << " but received: "
693                         << length);
694                 }
695             }
696         }
697     }
698     return true;
699 error_out:
700     if (exitOnError) {
701         fgOSExit(1);
702         return true; // should not get there, but please the compiler
703     } else
704         return false;
705 }
706
707
708 // close the channel
709 bool FGGeneric::close() {
710     SGIOChannel *io = get_io_channel();
711
712     if ( ((get_direction() == SG_IO_OUT)||
713           (get_direction() == SG_IO_BI))
714           && ! postamble.empty() ) {
715         if ( ! io->write( postamble.c_str(), postamble.size() ) ) {
716             SG_LOG( SG_IO, SG_ALERT, "Error writing postamble." );
717             return false;
718         }
719     }
720
721     set_enabled( false );
722
723     if ( ! io->close() ) {
724         return false;
725     }
726
727     return true;
728 }
729
730
731 void
732 FGGeneric::reinit()
733 {
734     SGPath path( globals->get_fg_root() );
735     path.append("Protocol");
736     path.append(file_name.c_str());
737
738     SG_LOG(SG_NETWORK, SG_INFO, "Reading communication protocol from "
739                                 << path.str());
740
741     SGPropertyNode root;
742     try {
743         readProperties(path.str(), &root);
744     } catch (const sg_exception & ex) {
745         SG_LOG(SG_NETWORK, SG_ALERT,
746          "Unable to load the protocol configuration file: " << ex.getFormattedMessage() );
747          return;
748     }
749
750     if (direction == "out") {
751         SGPropertyNode *output = root.getNode("generic/output");
752         if (output) {
753             _out_message.clear();
754             if (!read_config(output, _out_message))
755             {
756                 // bad configuration
757                 return;
758             }
759         }
760     } else if (direction == "in") {
761         SGPropertyNode *input = root.getNode("generic/input");
762         if (input) {
763             _in_message.clear();
764             if (!read_config(input, _in_message))
765             {
766                 // bad configuration
767                 return;
768             }
769             if (!binary_mode && (line_separator.empty() ||
770                 *line_separator.rbegin() != '\n')) {
771
772                 SG_LOG(SG_IO, SG_WARN,
773                     "Warning: Appending newline to line separator in generic input.");
774                 line_separator.push_back('\n');
775             }
776         }
777     }
778
779     initOk = true;
780 }
781
782
783 bool
784 FGGeneric::read_config(SGPropertyNode *root, vector<_serial_prot> &msg)
785 {
786     binary_mode = root->getBoolValue("binary_mode");
787
788     if (!binary_mode) {
789         /* These variables specified in the $FG_ROOT/data/Protocol/xxx.xml
790          * file for each format
791          *
792          * var_sep_string  = the string/charachter to place between variables
793          * line_sep_string = the string/charachter to place at the end of each
794          *                   lot of variables
795          */
796         preamble = unescape(root->getStringValue("preamble"));
797         postamble = unescape(root->getStringValue("postamble"));
798         var_sep_string = unescape(root->getStringValue("var_separator"));
799         line_sep_string = unescape(root->getStringValue("line_separator"));
800
801         if ( var_sep_string == "newline" ) {
802             var_separator = '\n';
803         } else if ( var_sep_string == "tab" ) {
804             var_separator = '\t';
805         } else if ( var_sep_string == "space" ) {
806             var_separator = ' ';
807         } else if ( var_sep_string == "formfeed" ) {
808             var_separator = '\f';
809         } else if ( var_sep_string == "carriagereturn" ) {
810             var_sep_string = '\r';
811         } else if ( var_sep_string == "verticaltab" ) {
812             var_separator = '\v';
813         } else {
814             var_separator = var_sep_string;
815         }
816
817         if ( line_sep_string == "newline" ) {
818             line_separator = '\n';
819         } else if ( line_sep_string == "tab" ) {
820             line_separator = '\t';
821         } else if ( line_sep_string == "space" ) {
822             line_separator = ' ';
823         } else if ( line_sep_string == "formfeed" ) {
824             line_separator = '\f';
825         } else if ( line_sep_string == "carriagereturn" ) {
826             line_separator = '\r';
827         } else if ( line_sep_string == "verticaltab" ) {
828             line_separator = '\v';
829         } else {
830             line_separator = line_sep_string;
831         }
832     } else {
833         // default values: no footer and record_length = sizeof(representation)
834         binary_footer_type = FOOTER_NONE;
835         binary_record_length = -1;
836
837         // default choice is network byte order (big endian)
838         if (sgIsLittleEndian()) {
839            binary_byte_order = BYTE_ORDER_NEEDS_CONVERSION;
840         } else {
841            binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
842         }
843
844         if ( root->hasValue("binary_footer") ) {
845             string footer_type = root->getStringValue("binary_footer");
846             if ( footer_type == "length" ) {
847                 binary_footer_type = FOOTER_LENGTH;
848             } else if ( footer_type.substr(0, 5) == "magic" ) {
849                 binary_footer_type = FOOTER_MAGIC;
850                 binary_footer_value = strtol(footer_type.substr(6, 
851                             footer_type.length() - 6).c_str(), (char**)0, 0);
852             } else if ( footer_type != "none" ) {
853                 SG_LOG(SG_IO, SG_ALERT,
854                        "generic protocol: Unknown generic binary protocol "
855                        "footer '" << footer_type << "', using no footer.");
856             }
857         }
858
859         if ( root->hasValue("record_length") ) {
860             binary_record_length = root->getIntValue("record_length");
861         }
862
863         if ( root->hasValue("byte_order") ) {
864             string byte_order = root->getStringValue("byte_order");
865             if (byte_order == "network" ) {
866                 if ( sgIsLittleEndian() ) {
867                     binary_byte_order = BYTE_ORDER_NEEDS_CONVERSION;
868                 } else {
869                     binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
870                 }
871             } else if ( byte_order == "host" ) {
872                 binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
873             } else {
874                 SG_LOG(SG_IO, SG_ALERT,
875                        "generic protocol: Undefined generic binary protocol"
876                        "byte order, using HOST byte order.");
877             }
878         }
879
880         if( root->hasValue( "wrapper" ) ) {
881             string w = root->getStringValue( "wrapper" );
882             if( w == "kiss" )  wrapper = new FGKissWrapper();
883             else if( w == "stxetx" )  wrapper = new FGSTXETXWrapper();
884             else SG_LOG(SG_IO, SG_ALERT,
885                        "generic protocol: Undefined binary protocol wrapper '" + w + "' ignored" );
886         }
887     }
888
889     int record_length = 0; // Only used for binary protocols.
890     vector<SGPropertyNode_ptr> chunks = root->getChildren("chunk");
891
892     for (unsigned int i = 0; i < chunks.size(); i++) {
893
894         _serial_prot chunk;
895
896         // chunk.name = chunks[i]->getStringValue("name");
897         chunk.format = unescape(chunks[i]->getStringValue("format", "%d"));
898         chunk.offset = chunks[i]->getDoubleValue("offset");
899         chunk.factor = chunks[i]->getDoubleValue("factor", 1.0);
900         chunk.min = chunks[i]->getDoubleValue("min");
901         chunk.max = chunks[i]->getDoubleValue("max");
902         chunk.wrap = chunks[i]->getBoolValue("wrap");
903         chunk.rel = chunks[i]->getBoolValue("relative");
904
905         if( chunks[i]->hasChild("const") ) {
906             chunk.prop = new SGPropertyNode();
907             chunk.prop->setStringValue( chunks[i]->getStringValue("const", "" ) );
908         } else {
909             string node = chunks[i]->getStringValue("node", "/null");
910             chunk.prop = fgGetNode(node.c_str(), true);
911         }
912
913         string type = chunks[i]->getStringValue("type");
914
915         // Note: officially the type is called 'bool' but for backward
916         //       compatibility 'boolean' will also be supported.
917         if (type == "bool" || type == "boolean") {
918             chunk.type = FG_BOOL;
919             record_length += 1;
920         } else if (type == "float") {
921             chunk.type = FG_FLOAT;
922             record_length += sizeof(int32_t);
923         } else if (type == "double") {
924             chunk.type = FG_DOUBLE;
925             record_length += sizeof(int64_t);
926         } else if (type == "fixed") {
927             chunk.type = FG_FIXED;
928             record_length += sizeof(int32_t);
929         } else if (type == "string") {
930             chunk.type = FG_STRING;
931         } else if (type == "byte") {
932             chunk.type = FG_BYTE;
933             record_length += sizeof(int8_t);
934         } else {
935             chunk.type = FG_INT;
936             record_length += sizeof(int32_t);
937         }
938         msg.push_back(chunk);
939
940     }
941
942     if( !binary_mode )
943     {
944         if ((chunks.size() > 1)&&(var_sep_string.length() == 0))
945         {
946             // ASCII protocols really need a separator when there is more than one chunk per line
947             SG_LOG(SG_IO, SG_ALERT,
948                    "generic protocol: Invalid configuration. "
949                    "'var_separator' must not be empty for protocols which have more than one chunk per line.");
950             return false;
951         }
952     }
953     else
954     {
955         if (binary_record_length == -1) {
956             binary_record_length = record_length;
957         } else if (binary_record_length < record_length) {
958             SG_LOG(SG_IO, SG_ALERT,
959                    "generic protocol: Requested binary record length shorter than "
960                    " requested record representation.");
961             binary_record_length = record_length;
962         }
963     }
964
965     return true;
966 }
967
968 void FGGeneric::updateValue(FGGeneric::_serial_prot& prot, bool val)
969 {
970   if( prot.rel )
971   {
972     // value inverted if received true, otherwise leave unchanged
973     if( val )
974       setValue(prot.prop, !getValue<bool>(prot.prop));
975   }
976   else
977   {
978     setValue(prot.prop, val);
979   }
980 }