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";
+