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 @@
+typedef 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 <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#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 <stdlib.h>
+#include <sndfile.h>
+#include <alsa/asoundlib.h>
+
+#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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+
+#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
+
+[ -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 <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+
+#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";
+