[CONTACT]

[ABOUT]

[POLICY]

Log gph Files gph Refs gph

Found at: gopher.r-36.net:70/scm/acarsdec/commit/45bafeee14982a9d10e1e3dd33b08beeb93eee6f.gph

Initial commit of acarsdec. - acarsdec - an ACARS decoder

Log

Files

Refs

README

---

commit 45bafeee14982a9d10e1e3dd33b08beeb93eee6f

Author: Christoph Lohmann <20h@r-36.net>

Date:   Wed, 13 Jun 2012 15:05:11 +0200
Initial commit of acarsdec.
Diffstat:
  Makefile                            |      21 +++++++++++++++++++++
  PROTOCOL                            |      30 ++++++++++++++++++++++++++++++
  README                              |      69 ++++++++++++++++++++++++++++++
  README.mod                          |      62 +++++++++++++++++++++++++++++++
  acarsdec.h                          |      36 +++++++++++++++++++++++++++++++
  example/acars.mp3                   |       0
  example/acars.wav                   |       0
  getbits.c                           |     192 +++++++++++++++++++++++++++++++
  getmesg.c                           |     218 +++++++++++++++++++++++++++++++
  input.c                             |     206 +++++++++++++++++++++++++++++++
  main.c                              |     235 +++++++++++++++++++++++++++++++
  rc.d/acarsdec.archlinux             |      44 +++++++++++++++++++++++++++++++
  rc.d/acarsdec.conf.d.archlinux      |       3 +++
  serv.c                              |     311 +++++++++++++++++++++++++++++++
  stdinsrv.py                         |     113 +++++++++++++++++++++++++++++++
  version.h                           |       3 +++
16 files changed, 1543 insertions(+), 0 deletions(-)
---

diff --git a/Makefile b/Makefile

@@ -0,0 +1,21 @@
+
+INCLUDES=-I.
+
+CFLAGS= -g -O2 -Wall $(INCLUDES)
+
+OBJS=  getbits.o input.o getmesg.o main.o serv.o
+
+acarsdec:        $(OBJS)
+        $(CC) -o $@ $(OBJS) -lm -lsndfile -lasound
+
+main.o:        main.c version.h
+
+clean:
+        rm -f *.o acarsdec
+
+getbits.o: acarsdec.h getbits.c
+getmesg.o: acarsdec.h getmesg.c
+main.o: acarsdec.h main.c
+input.o: acarsdec.h input.c
+serv.o: acarsdec.h serv.c
+

diff --git a/PROTOCOL b/PROTOCOL

@@ -0,0 +1,30 @@
+PROTOCOL FOR -p FLAG
+
+Format:
+
+msg ::= "MESG\n" + descs + content + "END MESG\n";
+descs ::= n * desc;
+desc ::= name + ": " + value + "\n";
+name ::= UTF-8-String;
+value ::= UTF-8-String;
+content ::= "CONTENT: " + UTF-8-String + "\n.\n";
+
+The content can be multi-line, so parse for "\n.\n".
+
+Example:
+
+---[START]---
+MESG
+Mode: G
+REG: .ZK-OKB
+LABEL: BA
+BLKID: 55
+MSGNO: L34A
+FLIGHTID: NZ0039
+CONTENT: /MSTEC7X.DR1.ZK-OKB6A40B74740B55EBD70E7E2
+.
+TIMESTAMP: 2010-06-26T11:29:35+00:00
+END MESG
+
+---[END]---
+

diff --git a/README b/README

@@ -0,0 +1,69 @@
+
+ACARSDEC
+
+Acarsdec is an open source, realtime  ACARS demodulator and position decoder for Linux.
+
+Aircraft Communication Addressing and Reporting System (or ACARS) is a digital datalink system for transmission of small messages between aircraft and ground stations via VHF radio.
+
+HOW DOES IT WORK ?
+
+To receive ACARS you need at least an AM VHF air band receiver tuned to one of these frequencies :
+
+131.725        Europe primary
+131.525        European secondary
+131.550        USA primary
+130.025        USA secondary
+131.450 Japan primary
+(these are the most common, google is your friend for other frequencies)
+
+Audio output from this receiver is send to the soundcard input of your PC under Linux.
+Then, acarsdec will demodulate the signals sent by aircrafts and print the received messages on its standart output in airnav log text format.
+
+BUILDING IT
+On a Linux system, you will need libsnd librairy, alsa audio system and gcc/make installed.
+Then just type :
+make
+
+USING IT
+acarsdec could be called with the following options :
+acarsdec [-LR][-s noport] -d alsapcmdevice | -f sndfile
+ -f sndfile :           decode from file sndfile (ie: a .wav file)
+ -d alsapcmdevice :     decode from soundcard input alsapcmdevice (ie: hw:0,0)
+ [-LR] :                diseable left or right channel decoding of stereo signal (save cpu)
+ [-s noport ] :         "xastir" mode : act as an APRS local server, on port : noport (see below)
+
+Input could be mono or stereo but with 48Khz sampling frequency.
+If stereo, acarsdec will demod the 2 channels independantly (if no L ou R options specified)
+
+Typical usage for realtime decoding is :
+acarsdec -d hw:0
+
+Be sure that correct record level is set for the used soundcard input.
+For testing, you could try to record your receiver output at 48khz sampling frequency with any audio recording tool.
+Save as wav file, then decode it by :
+acarsdec -f audiofile.wav.
+
+
+USING IT WITH XASTIR
+acarsdec have a special output mode to use it with APRS position reporting plotting program : xastir (www.xastir.org).
+In this mode, acarsdec acts as a very basic local aprsd server.
+ACARS messages, and in particular, position report messages are converted to APRS format, so you can plot aircraft positions on a map.
+
+PS: position decoding is in experimental stage. Mail me if you find errors or lack of position reporting.
+
+start acarsdec with the following option :
+acarsdec -d hw:0 -s 14000
+
+Then in xastir, choose : Interface->Interface Control->Add
+Select : Internet Server, then Add
+Set Host at 127.0.0.1, Port 14000, Don't allow transmitting, then Ok.
+This will add an interface in the Interface Control dialog.
+
+Then select this interface and press start.
+To check that acarsdec send messages to xastir, select View->Incoming traffic
+ACARS messages look like that in xastir :
+F-XXYZ>ACARS:>Fid:AFXXXX Lbl:Q0
+
+Lots of ACARS messages are messages without position report, so be patient before seeing aircraft plotted on the map.
+
+

diff --git a/README.mod b/README.mod

@@ -0,0 +1,62 @@
+INTRODUCTION
+
+This acarsdec was modified by
+        Christoph Lohmann <20h@r-36.net>
+
+In addition to this, stdinsrv.py was added, for additionaly functionality.
+See the examples for how to use these possibilities.
+
+
+INSTALLATION
+
+ % make
+ % cp acarsdec /usr/bin
+ % cp stdinsrv.py /usr/bin/stdinsrv
+
+
+STARTUP
+
+For a daemon mode, using stdinsrv, see the rc.d/* files. For now there
+are the example files for an Archlinux installation.
+
+
+EXAMPLES
+
+Reading from first alsa device, only processing the right stereo channel,
+outputting to stdout, in a parseable format and multiplexing it to the
+network on port 5102.
+
+ % acarsdec -d hw0,0 -p -R | stdinsrv -p 5102
+
+Decoding a recorded ACARS example wav file (48000!) on stdin, to the original
+acarsdec format:
+
+ % cat examples/acars.wav | acarsdec -t
+
+
+COMPLEX EXAMPLE
+
+Server: ACARS receiver -> sound in
+Client: We want to hear the ACARS signal and see the decoded message for
+        a comparison, whether the acarsdec decodes them right. (See the
+        examples folder for listening to an example ACARS message.)
+
+Client:
+ % socat - TCP-L:5467 | aplay &
+
+Server:
+ % mkfifo /tmp/acarsdec
+ % arecord -f dat | tee /tmp/acarsdec | socat - TCP:$ClientIP:5467 &
+ % acarsdec -f /tmp/acarsdec -p | stdinsrv -p 5102
+
+Client:
+ % socat - TCP:$ServerIP:5102
+
+
+OTHER FLAGS
+
+There is flag -v (verbose), which forces stdout output during the -s mode.
+The -e flag enables some debugging output, you might find interesting.
+
+Have fun!
+

diff --git a/acarsdec.h b/acarsdec.h

@@ -0,0 +1,36 @@
+ttypedef struct {
+        unsigned char mode;
+        unsigned char addr[8];
+        unsigned char ack;
+        unsigned char label[3];
+        unsigned char bid;
+        unsigned char no[5];
+        unsigned char fid[7];
+        char txt[256];
+} msg_t;
+
+extern int initsample(char *sourcename, int src);
+extern int getsample(short *sample, int nb);
+extern void endsample(void);
+
+extern void init_bits(void);
+extern void resetbits(int ch);
+extern int getbit(short in, unsigned char *outbits, int ch);
+
+extern void init_mesg(void);
+extern int getmesg(unsigned char r, msg_t * msg, int ch);
+
+extern int init_serv(short port);
+extern int send_mesg(msg_t *msg);
+extern void end_serv(void);
+
+enum {
+        IN_FILE = 0,
+        IN_ALSA = 1,
+        IN_STDIN = 2,
+
+        OUT_NET = 0x01,
+        OUT_PRINT = 0x02,
+        OUT_PROTO = 0x04,
+};
+

diff --git a/example/acars.mp3 b/example/acars.mp3

Binary files differ.

diff --git a/example/acars.wav b/example/acars.wav

Binary files differ.

diff --git a/getbits.c b/getbits.c

@@ -0,0 +1,192 @@
+/*
+ *  Copyright (c) 2007 by Thierry Leconte (F4DWV)
+ *
+ *      $Id: getbits.c,v 1.4 2007/04/15 15:06:54 f4dwv Exp $
+ *
+ *   This code is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library General Public License version 2
+ *   published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include 
+#include 
+#include 
+
+#include "acarsdec.h"
+
+
+#define Fe 48000.0
+#define Freqh 4800.0/Fe*2.0*M_PI
+#define Freql 2400.0/Fe*2.0*M_PI
+#define BITLEN ((int)Fe/1200)
+
+static float h[BITLEN];
+
+static struct bstat_s {
+        float hsample[BITLEN];
+        float lsample[BITLEN];
+        float isample[BITLEN];
+        float qsample[BITLEN];
+        float csample[BITLEN];
+        int is;
+        int clock;
+        float lin;
+        float phih,phil;
+        float dfh,dfl;
+        float pC,ppC;
+        int sgI, sgQ;
+        float ea;
+} bstat[2];
+
+
+void init_bits(void)
+{
+        int i;
+        for (i = 0; i < BITLEN; i++)
+                h[i] = sin(2.0 * M_PI * (float) i / (float) BITLEN);
+
+        for (i = 0; i < BITLEN; i++) {
+                bstat[0].hsample[i] = bstat[0].lsample[i] =
+                    bstat[0].isample[i] = bstat[0].qsample[i] =
+                    bstat[0].csample[i] = 0.0;
+                bstat[1].hsample[i] = bstat[1].lsample[i] =
+                    bstat[1].isample[i] = bstat[1].qsample[i] =
+                    bstat[1].csample[i] = 0.0;
+        }
+        bstat[0].is = bstat[0].clock = bstat[0].sgI = bstat[0].sgQ = 0;
+        bstat[1].is = bstat[1].clock = bstat[1].sgI = bstat[1].sgQ = 0;
+        bstat[0].phih = bstat[0].phil = bstat[0].dfh = bstat[0].dfl =
+            bstat[0].pC = bstat[0].ppC = bstat[0].ea = 0.0;
+        bstat[1].phih = bstat[1].phil = bstat[1].dfh = bstat[1].dfl =
+            bstat[1].pC = bstat[1].ppC = bstat[1].ea = 0.0;
+        bstat[0].lin=bstat[1].lin=1.0;
+
+}
+
+void resetbits(int ch)
+{
+        bstat[ch].sgI = bstat[ch].sgQ = 0;
+}
+
+#define VFOPLL 0.7e-3
+#define BITPLL 0.2
+
+int getbit(short sample, unsigned char *outbits, int ch)
+{
+        int i, bt;
+        float in, in2;
+        float C;
+        float I, Q;
+        float oscl, osch;
+        struct bstat_s *st;
+
+        bt = 0;
+        st = &bstat[ch];
+
+        in = (float) sample;
+        st->lin = 0.003 * fabs(in) + 0.997 * st->lin;
+        in /= st->lin;
+        in2 = in * in;
+
+        st->is--;
+        if (st->is < 0)
+                st->is = BITLEN - 1;
+
+        /* VFOs */
+        st->phih += Freqh - VFOPLL * st->dfh;
+        if (st->phih >= 4.0 * M_PI)
+                st->phih -= 4.0 * M_PI;
+        st->hsample[st->is] = in2 * sin(st->phih);
+        for (i = 0, st->dfh = 0.0; i < BITLEN / 2; i++) {
+                st->dfh += st->hsample[(st->is + i) % BITLEN];
+        }
+        osch = cos(st->phih / 2.0);
+
+        st->phil += Freql - VFOPLL * st->dfl;
+        if (st->phil >= 4.0 * M_PI)
+                st->phil -= 4.0 * M_PI;
+        st->lsample[st->is] = in2 * sin(st->phil);
+        for (i = 0, st->dfl = 0.0; i < BITLEN / 2; i++) {
+                st->dfl += st->lsample[(st->is + i) % BITLEN];
+        }
+        oscl = cos(st->phil / 2.0);
+
+        /* mix */
+        st->isample[st->is] = in * (oscl + osch);
+        st->qsample[st->is] = in * (oscl - osch);
+        st->csample[st->is] = oscl * osch;
+
+
+        /* bit clock */
+        st->clock++;
+        if (st->clock >= BITLEN/4 + st->ea) {
+                st->clock = 0;
+
+                /*  clock filter  */
+                for (i = 0, C = 0.0; i < BITLEN; i++) {
+                        C += h[i] * st->csample[(st->is + i) % BITLEN];
+                }
+
+                if (st->pC < C && st->pC < st->ppC) {
+
+                        /* integrator */
+                        for (i = 0, Q = 0.0; i < BITLEN; i++) {
+                                Q += st->qsample[(st->is + i) % BITLEN];
+                        }
+
+                        if (st->sgQ == 0) {
+                                if (Q < 0)
+                                        st->sgQ = -1;
+                                else
+                                        st->sgQ = 1;
+                        }
+
+                        *outbits =
+                            ((*outbits) >> 1) | (unsigned
+                                                 char) ((Q * st->sgQ >
+                                                         0) ? 0x80 : 0);
+                        bt = 1;
+
+                        st->ea = -BITPLL * (C - st->ppC);
+                        if(st->ea > 2.0) st->ea=2.0;
+                        if(st->ea < -2.0) st->ea=-2.0;
+                }
+                if (st->pC > C && st->pC > st->ppC) {
+
+                        /* integrator */
+                        for (i = 0, I = 0.0; i < BITLEN; i++) {
+                                I += st->isample[(st->is + i) % BITLEN];
+                        }
+
+                        if (st->sgI == 0) {
+                                if (I < 0)
+                                        st->sgI = -1;
+                                else
+                                        st->sgI = 1;
+                        }
+
+                        *outbits =
+                            ((*outbits) >> 1) | (unsigned
+                                                 char) ((I * st->sgI >
+                                                         0) ? 0x80 : 0);
+                        bt = 1;
+
+                        st->ea = BITPLL * (C - st->ppC);
+                        if(st->ea > 2.0) st->ea=2.0;
+                        if(st->ea < -2.0) st->ea=-2.0;
+                }
+                st->ppC = st->pC;
+                st->pC = C;
+        }
+        return bt;
+}

diff --git a/getmesg.c b/getmesg.c

@@ -0,0 +1,218 @@
+/*
+ *  Copyright (c) 2007 by Thierry Leconte (F4DWV)
+ *
+ *      $Id: getmesg.c,v 1.3 2007/03/28 06:26:05 f4dwv Exp $
+ *
+ *   This code is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library General Public License version 2
+ *   published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include 
+#include 
+#include 
+
+#include "acarsdec.h"
+
+#define SYN 0x16
+#define SOH 0x01
+struct mstat_s {
+        enum { HEADL, HEADF, BSYNC1, BSYNC2, SYN1, SYN2, SOH1, TXT, CRC1,
+                    CRC2, END } state;
+        int ind;
+        unsigned short crc;
+        char txt[243];
+} mstat[2];
+
+
+/* CCITT 16 CRC */
+#define POLY 0x1021
+static void update_crc(unsigned short *crc, unsigned char ch)
+{
+        unsigned char v;
+        unsigned int i;
+        unsigned short flag;
+
+        v = 1;
+        for (i = 0; i < 8; i++) {
+                flag = (*crc & 0x8000);
+                *crc = *crc << 1;
+
+                if (ch & v)
+                        *crc = *crc + 1;
+
+                if (flag != 0)
+                        *crc = *crc ^ POLY;
+
+                v = v << 1;
+        }
+}
+
+static int build_mesg(char *txt, int len, msg_t * msg)
+{
+        int i, k;
+        char r;
+
+        /* remove special chars */
+        for (i = 0; i < len; i++) {
+                r = txt[i];
+                if (r < ' ' && r != 0x0d && r != 0x0a)
+                        r = 0xa4;
+                txt[i] = r;
+        }
+        txt[i] = '\0';
+
+        /* fill msg struct */
+        k = 0;
+        msg->mode = txt[k];
+        k++;
+
+        for (i = 0; i < 7; i++, k++) {
+                msg->addr[i] = txt[k];
+        }
+        msg->addr[7] = '\0';
+
+        /* ACK/NAK */
+        msg->ack = txt[k];
+        k++;
+
+        msg->label[0] = txt[k];
+        k++;
+        msg->label[1] = txt[k];
+        k++;
+        msg->label[2] = '\0';
+
+        msg->bid = txt[k];
+        k++;
+
+        k++;
+
+        for (i = 0; i < 4; i++, k++) {
+                msg->no[i] = txt[k];
+        }
+        msg->no[4] = '\0';
+
+        for (i = 0; i < 6; i++, k++) {
+                msg->fid[i] = txt[k];
+        }
+        msg->fid[6] = '\0';
+
+        strcpy(msg->txt, &(txt[k]));
+
+        return 1;
+}
+
+void init_mesg(void)
+{
+        mstat[0].state = mstat[1].state = HEADL;
+}
+
+int getmesg(unsigned char r, msg_t * msg, int ch)
+{
+        struct mstat_s *st;
+
+        st = &(mstat[ch]);
+
+        do {
+                switch (st->state) {
+                case HEADL:
+                        if (r == 0xff) {
+                                st->state = HEADF;
+                                return 8;
+                        }
+                        resetbits(ch);
+                        return 8;
+                        break;
+                case HEADF:
+                        if (r != 0xff) {
+                                int i;
+                                unsigned char m;
+
+                                for (i = 0, m = 1; i < 7; i++, m = m << 1) {
+                                        if (!(r & m))
+                                                break;
+                                }
+                                if (i < 2) {
+                                        st->state = HEADL;
+                                        break;
+                                }
+                                st->state = BSYNC1;
+                                st->ind = 0;
+                                if (i != 2)
+                                        return (i - 2);
+                                break;
+                        }
+                        return 6;
+                case BSYNC1:
+                        if (r != 0x80 + '+')
+                                st->ind++;
+                        st->state = BSYNC2;
+                        return 8;
+                case BSYNC2:
+                        if (r != '*')
+                                st->ind++;
+                        st->state = SYN1;
+                        return 8;
+                case SYN1:
+                        if (r != SYN)
+                                st->ind++;
+                        st->state = SYN2;
+                        return 8;
+                case SYN2:
+                        if (r != SYN)
+                                st->ind++;
+                        st->state = SOH1;
+                        return 8;
+                case SOH1:
+                        if (r != SOH)
+                                st->ind++;
+                        if (st->ind > 2) {
+                                st->state = HEADL;
+                                break;
+                        }
+                        st->state = TXT;
+                        st->ind = 0;
+                        st->crc = 0;
+                        return 8;
+                case TXT:
+                        update_crc(&st->crc, r);
+                        r = r & 0x7f;
+                        if (r == 0x03 || r == 0x17) {
+                                st->state = CRC1;
+                                return 8;
+                        }
+                        st->txt[st->ind] = r;
+                        st->ind++;
+                        if (st->ind > 243) {
+                                st->state = HEADL;
+                                break;
+                        }
+                        return 8;
+                case CRC1:
+                        update_crc(&st->crc, r);
+                        st->state = CRC2;
+                        return 8;
+                case CRC2:
+                        update_crc(&st->crc, r);
+                        st->state = END;
+                        return 8;
+                case END:
+                        st->state = HEADL;
+                        if (st->crc == 0) {
+                                build_mesg(st->txt, st->ind, msg);
+                                return 0;
+                        }
+                        return 8;
+                }
+        } while (1);
+}

diff --git a/input.c b/input.c

@@ -0,0 +1,206 @@
+/*
+ *  Copyright (c) 2007 by Thierry Leconte (F4DWV)
+ *              (c) 2010 by Christoph Lohmann <20h@r-36.net>
+ *
+ *      $Id: input.c,v 1.3 2007/03/29 16:21:49 f4dwv Exp $
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library General Public License version 2
+ *   published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include 
+#include 
+#include 
+
+#include "acarsdec.h"
+
+static int source = 0;
+static int nbch = 0;
+
+static SNDFILE *inwav;
+static int initstdin(void)
+{
+        SF_INFO infwav;
+
+/* open wav input file */
+        infwav.format = 0;
+        inwav = sf_open_fd(STDIN_FILENO, SFM_READ, &infwav, SF_TRUE);
+        if (inwav == NULL) {
+                fprintf(stderr, "could not open stdin\n");
+                return (0);
+        }
+        if (infwav.samplerate != 48000) {
+                fprintf(stderr,
+                        "Bad Input File sample rate: %d. Must be 48000\n",
+                        infwav.samplerate);
+                return (0);
+        }
+        nbch=infwav.channels;
+        return (infwav.channels);
+}
+
+static SNDFILE *inwav;
+static int initsnd(char *filename)
+{
+        SF_INFO infwav;
+
+/* open wav input file */
+        infwav.format = 0;
+        inwav = sf_open(filename, SFM_READ, &infwav);
+        if (inwav == NULL) {
+                fprintf(stderr, "could not open %s\n", filename);
+                return (0);
+        }
+        if (infwav.samplerate != 48000) {
+                fprintf(stderr,
+                        "Bad Input File sample rate: %d. Must be 48000\n",
+                        infwav.samplerate);
+                return (0);
+        }
+        nbch=infwav.channels;
+        return (infwav.channels);
+}
+
+static snd_pcm_t *capture_handle;
+static int initalsa(char *filename)
+{
+        snd_pcm_hw_params_t *hw_params;
+        int err;
+
+        if ((err =
+             snd_pcm_open(&capture_handle, filename,
+                          SND_PCM_STREAM_CAPTURE, 0)) < 0) {
+                fprintf(stderr, "cannot open audio device %s (%s)\n",
+                        filename, snd_strerror(err));
+                return 0;
+        }
+
+        if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
+                fprintf(stderr,
+                        "cannot allocate hardware parameter structure (%s)\n",
+                        snd_strerror(err));
+                return 0;
+        }
+
+        if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0) {
+                fprintf(stderr,
+                        "cannot initialize hardware parameter structure (%s)\n",
+                        snd_strerror(err));
+                return 0;
+        }
+
+        if ((err =
+             snd_pcm_hw_params_set_access(capture_handle, hw_params,
+                                          SND_PCM_ACCESS_RW_INTERLEAVED)) <
+            0) {
+                fprintf(stderr, "cannot set access type (%s)\n",
+                        snd_strerror(err));
+                return 0;
+        }
+
+        if ((err =
+             snd_pcm_hw_params_set_format(capture_handle, hw_params,
+                                          SND_PCM_FORMAT_S16)) < 0) {
+                fprintf(stderr, "cannot set sample format (%s)\n",
+                        snd_strerror(err));
+                return 0;
+        }
+
+        if ((err =
+             snd_pcm_hw_params_set_rate(capture_handle, hw_params, 48000,
+                                        0)) < 0) {
+                fprintf(stderr, "cannot set sample rate (%s)\n",
+                        snd_strerror(err));
+                return 0;
+        }
+
+        for(nbch=2;nbch>0;nbch--)  {
+                if (snd_pcm_hw_params_set_channels(capture_handle, hw_params, nbch)==0)
+                        break;
+        }
+
+        if (nbch ==0) {
+                fprintf(stderr, "cannot set number of channels\n");
+                return 0;
+        }
+
+        if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0) {
+                fprintf(stderr, "cannot set parameters (%s)\n",
+                        snd_strerror(err));
+                return 0;
+        }
+        snd_pcm_hw_params_free(hw_params);
+
+        if ((err = snd_pcm_prepare(capture_handle)) < 0) {
+                fprintf(stderr,
+                        "cannot prepare audio interface for use (%s)\n",
+                        snd_strerror(err));
+                return 0;
+        }
+        return nbch;
+}
+
+/* open input source*/
+int initsample(char *sourcename, int src)
+{
+        source = src;
+        switch(src) {
+        case IN_STDIN:
+                return initstdin();
+        case IN_FILE:
+                return initsnd(sourcename);
+        case IN_ALSA:
+                return initalsa(sourcename);
+        }
+
+        return 0;
+}
+
+int getsample(short *sample, int nb)
+{
+        int r = -1;
+
+        switch(source) {
+        case IN_STDIN:
+                r = sf_read_short(inwav, sample, nb);
+                break;
+        case IN_FILE:
+                r = sf_read_short(inwav, sample, nb);
+                if (r == 0)
+                        r = -1;        /* this is the end */
+                break;
+        case IN_ALSA:
+                r = snd_pcm_readi(capture_handle, sample, nb/nbch);
+                if (r <= 0)
+                        fprintf(stderr,
+                                "cannot read from interface (%s)\n",
+                                snd_strerror(r));
+                r=r*nbch;
+                break;
+        }
+        return r;
+}
+
+void endsample(void)
+{
+        switch(source) {
+        case IN_FILE:
+        case IN_STDIN:
+                sf_close(inwav);
+                break;
+        case IN_ALSA:
+                snd_pcm_close(capture_handle);
+                break;
+        }
+}

diff --git a/main.c b/main.c

@@ -0,0 +1,235 @@
+/*
+ *  Copyright (c) 2007 by Thierry Leconte (F4DWV)
+ *            (c) 2010 by Christoph Lohmann <20h@r-36.net>
+ *
+ *      $Id: main.c,v 1.5 2007/04/22 16:14:41 f4dwv Exp $
+ *
+ *   This code is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library General Public License version 2
+ *   published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "version.h"
+#include "acarsdec.h"
+
+extern int optind, opterr;
+extern char *optarg;
+
+static void usage(void)
+{
+        fprintf(stderr, "%s\n", version);
+        fprintf(stderr, "Usage: acarsdec [-vep][-LR][-s noport] -d "
+                        "alsapcmdevice | -f sndfile | -t\n");
+        fprintf(stderr, " -f sndfile :\t\tdecode from file sndfile "
+                        "(ie: a .wav file)\n");
+        fprintf(stderr, " -d alsapcmdevice :\tdecode from soundcard "
+                        "input alsapcmdevice (ie: hw:0,0)\n");
+        fprintf(stderr, " -t :\t\t\tread from stdin.\n");
+        fprintf(stderr, " [-v] :\t\t\tbe verbose.\n");
+        fprintf(stderr, " [-p] :\t\t\toutput should be parseable protocol.\n");
+        fprintf(stderr, " [-e] :\t\t\tdo debug?\n");
+        fprintf(stderr, " [-LR] :\t\tdisable left or right channel "
+                        "decoding of stereo signal\n");
+        fprintf(stderr, " [-s noport ] :\t\tact as an APRS local server, "
+                        "on port : noport\n");
+        fprintf(stderr, "Input could be mono or stereo but with 48Khz "
+                        "sampling frequency.\nIf stereo, acarsdec will "
+                        "demod the 2 channels independantly (if no L ou "
+                        "R options specified)\n\n");
+        exit(1);
+}
+
+void print_mesg(msg_t * msg)
+{
+        time_t t;
+        struct tm *tmp;
+        char pos[128];
+
+        printf("ACARS mode: %c", msg->mode);
+        printf(" Aircraft reg: %s\n", msg->addr);
+        printf("Message label: %s", msg->label);
+        printf(" Block id: %d", (int) msg->bid);
+        printf(" Msg. no: %s\n", msg->no);
+        printf("Flight id: %s\n", msg->fid);
+        printf("Message content:-\n%s", msg->txt);
+
+        if (posconv(msg->txt, msg->label, pos)==0)
+                printf("\nAPRS : Addr:%s Fid:%s Lbl:%s pos:%s\n", msg->addr,
+                        msg->fid,msg->label,pos);
+
+        t = time(NULL);
+        tmp = gmtime(&t);
+        printf("\n--------------------------------------------"
+                        "--------------[%02d/%02d/%04d %02d:%02d]\n\n",
+             tmp->tm_mday, tmp->tm_mon + 1, tmp->tm_year + 1900,
+             tmp->tm_hour, tmp->tm_min);
+        fflush(stdout);
+
+}
+
+void print_proto(msg_t * msg)
+{
+        time_t t;
+        struct tm *tmp;
+        char pos[128];
+        char timestamp[128];
+
+        printf("MESG\n");
+        printf("Mode: %c\n", msg->mode);
+        printf("REG: %s\n", msg->addr);
+        printf("LABEL: %s\n", msg->label);
+        printf("BLKID: %d\n", (int) msg->bid);
+        printf("MSGNO: %s\n", msg->no);
+        printf("FLIGHTID: %s\n", msg->fid);
+        printf("CONTENT: %s\n.\n", msg->txt);
+            if (posconv(msg->txt, msg->label, pos)==0) {
+                printf("APRS-ADDR: %s\n", msg->addr);
+                printf("APRS-FID: %s\n", msg->fid);
+                printf("APRS-LABEL: %s\n", msg->label);
+                printf("APRS-POS: %s\n", pos);
+        }
+        t = time(NULL);
+        tmp = gmtime(&t);
+        strftime(timestamp, sizeof(timestamp) - 1,
+                "%FT%T+00:00", tmp);
+        printf("TIMESTAMP: %s\n", timestamp);
+        printf("END MESG\n\n");
+        fflush(stdout);
+}
+
+void do_output(int type, msg_t *msg)
+{
+
+        if(type & OUT_NET)
+                send_mesg(msg);
+        if(type & OUT_PRINT)
+                print_mesg(msg);
+        if(type & OUT_PROTO)
+                print_proto(msg);
+}
+
+int main(int argc, char **argv)
+{
+        int c;
+        unsigned char r[2];
+        msg_t msg[2];
+        int nbit[2] = {0, 0};
+        int nrbit[2] = {8, 8};
+        int nbch=0;
+        int esel[2] = {1, 1};
+        short port=0;
+        int debug=0;
+        int output = 0;
+        int i;
+
+        while ((c = getopt(argc, argv, "ptevd:f:RLs:")) != EOF) {
+                switch (c) {
+                case 'd':
+                        nbch = initsample(optarg, IN_ALSA);
+                        break;
+                case 'f':
+                        nbch = initsample(optarg, IN_FILE);
+                        break;
+                case 't':
+                        nbch = initsample("stdin", IN_STDIN);
+                        break;
+                case 'L':
+                        esel[0] = 0;
+                        break;
+                case 'R':
+                        esel[1] = 0;
+                        break;
+                case 's':
+                        port=atoi(optarg);
+                        output |= OUT_NET;
+                        break;
+                case 'v':
+                        output |= OUT_PRINT;
+                        break;
+                case 'p':
+                        output |= OUT_PROTO;
+                        break;
+                case 'e':
+                        debug++;
+                        break;
+                default:
+                        usage();
+                        exit(1);
+                }
+        }
+
+        if (output == 0)
+                output = OUT_PRINT;
+
+        if (nbch == 0) {
+                usage();
+                exit(1);
+        }
+
+        if (port) {
+                if (init_serv(port))
+                        exit(1);
+                 if (debug)
+                        fprintf(stderr, "Server initialized.\n");
+        }
+
+/* main loop */
+        init_bits();
+        init_mesg();
+
+        if(debug)
+                fprintf(stderr, "Starting receive loop.\n");
+        do {
+                short sample[4096];
+                int ind, len;
+
+                len = getsample(sample, 4096);
+                if (debug)
+                        fprintf(stderr, "Got sample: %d\n", len);
+                if (len < 0)
+                        break;
+
+                for (ind = 0; ind < len;) {
+                        for (i = 0; i < nbch; i++,ind++) {
+                                if (esel[i]) {
+                                        nbit[i] += getbit(sample[ind], &r[i],
+                                                        0);
+                                        if (nbit[i] >= nrbit[i]) {
+                                                nrbit[i] = getmesg(r[i],
+                                                               &msg[i], 0);
+                                                nbit[i] = 0;
+                                                if (nrbit[i] == 0) {
+                                                        do_output(output,
+                                                                &msg[i]);
+                                                        nrbit[i] = 8;
+                                                }
+                                        }
+                                }
+                        }
+                }
+        } while (1);
+
+
+        if(port)
+                end_serv();
+
+        endsample();
+
+        exit(0);
+}

diff --git a/rc.d/acarsdec.archlinux b/rc.d/acarsdec.archlinux

@@ -0,0 +1,44 @@
+#!/bin/bash
+
+CONF=/etc/conf.d/acarsdec
+
+. /etc/rc.conf
+. /etc/rc.d/functions
+
+t[ -f $CONF ] && . $CONF
+
+ACARSDECBIN=/usr/bin/acarsdec
+STDINSRV=/usr/bin/stdinsrv
+APID=`pidof -x $ACARSDECBIN`
+case "$1" in
+  force)
+    stat_busy "Killing acarsdec by force."
+    kill $APID &> /dev/null
+    stat_done
+    ;;
+  start)
+    stat_busy "Starting acarsdec server"
+    [ -z "$APID" ] && $ACARSDECBIN $ACARSDECPARAMS \
+            | $STDINSRV $STDINSRVPARAMS 2>&1 >> /dev/null &
+    if [ $? -gt 0 ]; then
+      stat_fail
+    else
+      add_daemon acarsdec
+      stat_done
+    fi
+    ;;
+  stop)
+    stat_busy "Stopping acarsdec"
+    [ ! -z "$APID" ]  && kill $APID &>/dev/null
+    rm_daemon acarsdec
+    stat_done
+    ;;
+  restart)
+    $0 stop
+    $0 start
+    ;;
+  *)
+    echo "usage: $0 {start|stop|restart}"
+esac
+exit 0
+

diff --git a/rc.d/acarsdec.conf.d.archlinux b/rc.d/acarsdec.conf.d.archlinux

@@ -0,0 +1,3 @@
+ACARSDECPARAMS="-d hw:0,0 -p -R"
+STDINSRVPARAMS="-p 5102"
+

diff --git a/serv.c b/serv.c

@@ -0,0 +1,311 @@
+/*
+ *  Copyright (c) 2007 by Thierry Leconte (F4DWV)
+ *            (c) 2010 by Christoph Lohmann <20h@r-36.net>
+ *
+ *      $Id: serv.c,v 1.2 2007/04/22 16:14:41 f4dwv Exp $
+ *
+ *   This code is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library General Public License version 2
+ *   published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "acarsdec.h"
+
+static int sa, sc;
+
+int init_serv(short port)
+{
+    struct sockaddr_in locaddr, remaddr;
+    socklen_t len;
+    char c;
+    int res;
+
+    sa = socket(PF_INET, SOCK_STREAM, 0);
+    if (sa < 0) {
+        fprintf(stderr, "socket : %s\n", strerror(errno));
+        return -1;
+    }
+
+    res = 1;
+    res = setsockopt(sa, SOL_SOCKET, SO_REUSEADDR, &res, sizeof(res));
+    if (res) {
+            fprintf(stderr, "reuseaddr : %s\n", strerror(errno));
+            return -1;
+    }
+
+    memset(&locaddr, 0, sizeof(locaddr));
+    locaddr.sin_family = AF_INET;
+    locaddr.sin_port = htons(port);
+    locaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+    len = sizeof(locaddr);
+    res = bind(sa, (struct sockaddr *) &locaddr, len);
+    if (res) {
+        fprintf(stderr, "bind : %s\n", strerror(errno));
+        return -1;
+    }
+
+    res = listen(sa, 1);
+    if (res) {
+        fprintf(stderr, "listen : %s\n", strerror(errno));
+        return -1;
+    }
+
+    memset(&remaddr, 0, sizeof(remaddr));
+    len = sizeof(remaddr);
+    sc = accept(sa, (struct sockaddr *) &remaddr, &len);
+    if (sc < 0) {
+        fprintf(stderr, "accept : %s\n", strerror(errno));
+        return -1;
+    }
+
+    do {
+        res = read(sc, &c, 1);
+    } while (res == 1 && c != '\n');
+
+
+    return 0;
+}
+
+
+/* convert ACARS position reports to APRS position */
+static void toaprs(int la, char lac, int ln, char lnc, int prec, char *out)
+{
+    int lad, lnd;
+    float lam, lnm;
+
+    lad = la / 10000;
+    lnd = ln / 10000;
+    lam = (float) (la - (lad * 10000)) * 60.0 / 10000.0;
+    lnm = (float) (ln - (lnd * 10000)) * 60.0 / 10000.0;
+
+    switch (prec) {
+        case 0:
+                    sprintf(out, "%02d%02.0f.  %c/%03d%02.0f.  %c^", lad, lam, lac, lnd, lnm, lnc);
+                break;
+        case 1:
+                    sprintf(out, "%02d%04.1f %c/%03d%04.1f %c^", lad, lam, lac, lnd, lnm, lnc);
+                break;
+        case 2:
+        default:
+                    sprintf(out, "%02d%05.2f%c/%03d%05.2f%c^", lad, lam, lac, lnd, lnm, lnc);
+                break;
+    }
+}
+
+int posconv(char *txt, unsigned char *label, char *pos)
+{
+    char lac, lnc;
+    int la, ln;
+    char las[7], lns[7];
+    int n;
+    char *p;
+
+/*try different heuristics */
+
+    n = sscanf(txt, "#M1BPOS%c%05d%c%063d,", &lac, &la, &lnc, &ln);
+    if (n == 4 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        la *= 10;
+        ln *= 10;
+        toaprs(la, lac, ln, lnc, 1, pos);
+        return 0;;
+    }
+    n = sscanf(txt, "#M1AAEP%c%06d%c%07d", &lac, &la, &lnc, &ln);
+    if (n == 4 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        toaprs(la, lac, ln, lnc, 2, pos);
+        return 0;;
+    }
+
+    if (strncmp(txt, "#M1B", 4) == 0) {
+        if ((p = strstr(txt, "/FPO")) != NULL) {
+            n = sscanf(p, "/FPO%c%05d%c%06d", &lac, &la, &lnc, &ln);
+            if (n == 4 && (lac == 'N' || lac == 'S')
+                && (lnc == 'E' || lnc == 'W')) {
+                la *= 10;
+                ln *= 10;
+                toaprs(la, lac, ln, lnc, 1, pos);
+                return 0;;
+            }
+        }
+        if ((p = strstr(txt, "/PS")) != NULL) {
+            n = sscanf(p, "/PS%c%05d%c%06d", &lac, &la, &lnc, &ln);
+            if (n == 4 && (lac == 'N' || lac == 'S')
+                && (lnc == 'E' || lnc == 'W')) {
+                la *= 10;
+                ln *= 10;
+                toaprs(la, lac, ln, lnc, 1, pos);
+                return 0;;
+            }
+        }
+    }
+
+    n = sscanf(txt, "FST01%*8s%c%06d%c%07d", &lac, &la, &lnc, &ln);
+    if (n == 4 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        toaprs(la, lac, ln, lnc, 2, pos);
+        return 0;;
+    }
+
+    n = sscanf(txt, "(2%c%5c%c%6c", &lac, las, &lnc, lns);
+    if (n == 4 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        las[5] = 0;
+        lns[6] = 0;
+        la = 10 * atoi(las);
+        ln = 10 * atoi(lns);
+        toaprs(la, lac, ln, lnc, 1, pos);
+        return 0;;
+    }
+
+    n = sscanf(txt, "(:2%c%5c%c%6c", &lac, las, &lnc, lns);
+    if (n == 4 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        las[5] = 0;
+        lns[6] = 0;
+        la = 10 * atoi(las);
+        ln = 10 * atoi(lns);
+        toaprs(la, lac, ln, lnc, 1, pos);
+        return 0;;
+    }
+
+
+    n = sscanf(txt, "(2%*4s%c%5c%c%6c", &lac, las, &lnc, lns);
+    if (n == 4 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        las[5] = 0;
+        lns[6] = 0;
+        la = 10 * atoi(las);
+        ln = 10 * atoi(lns);
+        toaprs(la, lac, ln, lnc, 1, pos);
+        return 0;;
+    }
+
+    n = sscanf(txt, "LAT %c%3c.%3c/LON %c%3c.%3c", &lac, las, &(las[3]),
+               &lnc, lns, &(lns[3]));
+    if (n == 6 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        las[6] = 0;
+        lns[6] = 0;
+        la = 10 * atoi(las);
+        ln = 10 * atoi(lns);
+        toaprs(la, lac, ln, lnc, 1, pos);
+        return 0;;
+    }
+
+
+    n = sscanf(txt, "#DFB(POS-%*6s-%04d%c%05d%c/", &la, &lac, &ln, &lnc);
+    if (n == 4 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        la *= 100;
+        ln *= 100;
+        toaprs(la, lac, ln, lnc, 0, pos);
+        return 0;;
+    }
+
+    n = sscanf(txt, "#DFB*POS\a%*8s%c%04d%c%05d/", &lac, &la, &lnc, &ln);
+    if (n == 4 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        la *= 100;
+        ln *= 100;
+        toaprs(la, lac, ln, lnc, 0, pos);
+        return 0;;
+    }
+
+    n = sscanf(txt, "POS%c%05d%c%06d,", &lac, &la, &lnc, &ln);
+    if (n == 4 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        la *= 10;
+        ln *= 10;
+        toaprs(la, lac, ln, lnc, 1, pos);
+        return 0;;
+    }
+
+    n = sscanf(txt, "POS%*2s,%c%05d%c%06d,", &lac, &la, &lnc, &ln);
+    if (n == 4 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        la *= 10;
+        ln *= 10;
+        toaprs(la, lac, ln, lnc, 1, pos);
+        return 0;;
+    }
+
+    n = sscanf(txt, "RCL%*2s,%c%05d%c%06d,", &lac, &la, &lnc, &ln);
+    if (n == 4 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        la *= 10;
+        ln *= 10;
+        toaprs(la, lac, ln, lnc, 1, pos);
+        return 0;;
+    }
+
+    n = sscanf(txt, "TWX%*2s,%c%05d%c%06d,", &lac, &la, &lnc, &ln);
+    if (n == 4 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        la *= 10;
+        ln *= 10;
+        toaprs(la, lac, ln, lnc, 1, pos);
+        return 0;;
+    }
+
+    n = sscanf(txt, "CLA%*2s,%c%05d%c%06d,", &lac, &la, &lnc, &ln);
+    if (n == 4 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        la *= 10;
+        ln *= 10;
+        toaprs(la, lac, ln, lnc, 1, pos);
+        return 0;;
+    }
+
+    n = sscanf(txt, "%c%05d/%c%06d,", &lac, &la, &lnc, &ln);
+    if (n == 4 && (lac == 'N' || lac == 'S') && (lnc == 'E' || lnc == 'W')) {
+        la *= 10;
+        ln *= 10;
+        toaprs(la, lac, ln, lnc, 1, pos);
+        return 0;;
+    }
+
+    return 1;
+}
+
+int send_mesg(msg_t * msg)
+{
+    char apstr[512];
+    char txt[512];
+    char pos[64];
+    unsigned char *ind;
+
+   if(msg->label[0]=='_' && msg->label[1]==0x7f)
+        return 0;
+
+    strcpy(txt,msg->txt);
+    for(ind = (unsigned char *)&txt; *ind != 0 ;ind++) {
+        if(*ind==0x0a || *ind == 0x0d) *ind=' ';
+     }
+
+    ind = msg->addr;
+    while (*ind == '.' && *ind != 0)
+        ind++;
+
+    if (posconv(msg->txt, msg->label, pos))
+        sprintf(apstr, "%s>ACARS:>Fid:%s Lbl:%s %s\n", ind, msg->fid,msg->label,txt);
+    else
+        sprintf(apstr, "%s>ACARS:!%sFid:%s Lbl:%s %s\n", ind, pos,msg->fid,msg->label,txt);
+
+    write(sc, apstr, strlen(apstr));
+
+    return 0;
+}
+
+
+void end_serv(void)
+{
+    close(sc);
+    close(sa);
+}

diff --git a/stdinsrv.py b/stdinsrv.py

@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+# coding=utf-8
+#
+# Copy me if you can.
+# by Christoph Lohmann <20h@r-36.net>
+#
+
+import sys
+from socket import socket, AF_INET, SOCK_STREAM, SO_REUSEADDR, SOL_SOCKET
+from select import poll, POLLIN, POLLPRI, POLLERR, POLLHUP
+from fcntl import fcntl, F_GETFL, F_SETFL
+from os import O_NONBLOCK
+import errno
+import time
+from getopt import getopt, GetoptError
+
+def dbg(msg):
+        sys.stderr.write("%s\n" % (msg))
+
+def usage(app):
+        sys.stderr.write("usage: %s [-d] [-p port] [-h bind host]\n" % (app))
+        sys.exit(1)
+
+def main(args):
+        try:
+                opts, args = getopt(args[1:], "dp:h:")
+        except GetoptError, err:
+                sys.stderr.write("%s\n" % (str(err)))
+                usage(args[0])
+
+        debug = False
+        host = ""
+        port = 6789
+        for o, a in opts:
+                if o == "-p":
+                        port = int(a)
+                elif o == "-d":
+                        debug = True
+                elif o == "-h":
+                        host = a
+                else:
+                        assert False, "unhandled option"
+
+        sock = socket(AF_INET, SOCK_STREAM)
+        sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
+        sock.bind((host, port))
+        sock.listen(5)
+
+        fl = fcntl(sys.stdin, F_GETFL)
+        fcntl(sys.stdin, F_SETFL, fl | O_NONBLOCK)
+
+        clients = []
+        def getcli(fd):
+                for i in clients:
+                        if i.fileno() == fd:
+                                return i
+
+        def broadcastcli(data):
+                if debug == True:
+                        dbg("Broadcast")
+                for i in clients:
+                        try:
+                                i.send(data)
+                                i.flush(data)
+                        except:
+                                next
+
+        def closecli(fd):
+                if debug == True:
+                        dbg("Close: %d" % (fd))
+                cli = getcli(i[0])
+                clients.remove(cli)
+                po.unregister(cli)
+                cli.close()
+
+        po = poll()
+        po.register(sys.stdin, POLLIN|POLLPRI|POLLERR|POLLHUP)
+        po.register(sock, POLLIN|POLLPRI)
+        while 1:
+                events = po.poll(30)
+                for i in events:
+                        if i[0] == sock.fileno():
+                                cli = sock.accept()[0]
+                                cli.setblocking(0)
+                                clients.append(cli)
+                                po.register(cli, POLLIN|POLLPRI)
+                                if debug == True:
+                                        dbg("Accept: %d" % (cli.fileno()))
+                        elif i[1] in (POLLERR, POLLHUP):
+                                if debug == True:
+                                        dbg("err or hup: %d" % (i[0]))
+                                if i[0] == sys.stdin.fileno():
+                                        return 1
+                                cli = getcli(i[0])
+                                clients.remove(cli)
+                                po.unregister(cli)
+                                cli.close()
+                        elif i[1] in (POLLIN, POLLPRI):
+                                if i[0] == sys.stdin.fileno():
+                                        if debug == True:
+                                                dbg("Input on stdin.")
+                                        broadcastcli(sys.stdin.read(4096))
+                                else:
+                                        if debug == True:
+                                                dbg("Input from client: %d" % (i[0]))
+                                        cli = getcli(i[0])
+                                        a = cli.recv(4096)
+                                        if len(a) == 0:
+                                                closecli(i[0])
+
+if __name__ == "__main__":
+        sys.exit(main(sys.argv))
+

diff --git a/version.h b/version.h

@@ -0,0 +1,3 @@
+const char version[] = "Acarsdec 1.2  (c) 2007 Thierry Leconte F4DWV\n"
+                "              (c) 2010 Christoph Lohmann <20h@r-36.net>\n";
+
.


AD: