Skip to content

Commit

Permalink
initial support for using rtl_ais as a library
Browse files Browse the repository at this point in the history
This is implemented and used in the rtlsdr_pi opencpn plugin
The send tcp, and library queues should be combined and code shared
the aisdecoder should be encapsulated with a context like rtl_ais
to allow for multiple interfaces to work in the same process
  • Loading branch information
seandepagnier committed Jun 2, 2016
1 parent 95410e7 commit a54ab00
Show file tree
Hide file tree
Showing 8 changed files with 591 additions and 394 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ endif

CC?=gcc
SOURCES= \
rtl_ais.c convenience.c \
main.c rtl_ais.c convenience.c \
./aisdecoder/aisdecoder.c \
./aisdecoder/sounddecoder.c \
./aisdecoder/lib/receiver.c \
Expand Down
75 changes: 72 additions & 3 deletions aisdecoder/aisdecoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
#include <pthread.h>
//#include "config.h"
#include "sounddecoder.h"
#include "callbacks.h"
#include "lib/callbacks.h"
#include "../tcp_listener/tcp_listener.h"

#define MAX_BUFFER_LENGTH 2048
Expand All @@ -53,6 +53,58 @@ static int sock;
static int use_tcp = 0;

static struct addrinfo* addr=NULL;
// messages can be retrived from a different thread
static pthread_mutex_t message_mutex;

// queue of decoded ais messages
struct ais_message {
char *buffer;
struct ais_message *next;
} *ais_messages_head, *ais_messages_tail, *last_message;

static void append_message(const char *buffer)
{
struct ais_message *m = malloc(sizeof *m);

m->buffer = strdup(buffer);
m->next = NULL;
pthread_mutex_lock(&message_mutex);

// enqueue
if(!ais_messages_head)
ais_messages_head = m;
else
ais_messages_tail->next = m;
ais_messages_tail = m;
pthread_mutex_unlock(&message_mutex);
}

static void free_message(struct ais_message *m)
{
if(m) {
free(m->buffer);
free(m);
}
}

const char *aisdecoder_next_message()
{
free_message(last_message);
last_message = NULL;

pthread_mutex_lock(&message_mutex);
if(!ais_messages_head) {
pthread_mutex_unlock(&message_mutex);
return NULL;
}

// dequeue
last_message = ais_messages_head;
ais_messages_head = ais_messages_head->next;

pthread_mutex_unlock(&message_mutex);
return last_message->buffer;
}

static int initSocket(const char *host, const char *portname);
int send_nmea( const char *sentence, unsigned int length);
Expand All @@ -68,6 +120,8 @@ void nmea_sentence_received(const char *sentence,
unsigned int length,
unsigned char sentences,
unsigned char sentencenum) {
append_message(sentence);

if (sentences == 1) {
if (send_nmea( sentence, length) == -1) abort();
if (debug_nmea) fprintf(stderr, "%s", sentence);
Expand All @@ -91,20 +145,22 @@ int send_nmea( const char *sentence, unsigned int length) {
if( use_tcp) {
return add_nmea_ais_message(sentence, length);
}
else {
else if(sock) {
return sendto(sock, sentence, length, 0, addr->ai_addr, addr->ai_addrlen);
}
return 0;
}

int init_ais_decoder(char * host, char * port ,int show_levels,int _debug_nmea,int buf_len,int time_print_stats, int use_tcp_listener, int tcp_keep_ais_time){
debug_nmea=_debug_nmea;
use_tcp = use_tcp_listener;
pthread_mutex_init(&message_mutex, NULL);
if(debug_nmea)
fprintf(stderr,"Log NMEA sentences to console ON\n");
else
fprintf(stderr,"Log NMEA sentences to console OFF\n");
if( !use_tcp_listener) {
if (!initSocket(host, port)) {
if (host && port && !initSocket(host, port)) {
return EXIT_FAILURE;
}
}
Expand All @@ -125,6 +181,19 @@ void run_rtlais_decoder(short * buff, int len)
}
int free_ais_decoder(void)
{
pthread_mutex_destroy(&message_mutex);

// free all stored messa ages
free_message(last_message);
last_message = NULL;

while(ais_messages_head) {
struct ais_message *m = ais_messages_head;
ais_messages_head = ais_messages_head->next;

free_message(m);
}

freeSoundDecoder();
freeaddrinfo(addr);
#ifdef WIN32
Expand Down
1 change: 1 addition & 0 deletions aisdecoder/aisdecoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define __AIS_RL_AIS_INC_
int init_ais_decoder(char * host, char * port,int show_levels,int _debug_nmea,int buf_len,int time_print_stats, int use_tcp_listener, int tcp_keep_ais_time);
void run_rtlais_decoder(short * buff, int len);
const char *aisdecoder_next_message();
int free_ais_decoder(void);
#endif

4 changes: 2 additions & 2 deletions aisdecoder/sounddecoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
#include <fcntl.h>
#endif

#include "receiver.h"
#include "hmalloc.h"
#include "lib/receiver.h"
#include "lib/hmalloc.h"

#define MAX_FILENAME_SIZE 512
#define ERROR_MESSAGE_LENGTH 1024
Expand Down
4 changes: 4 additions & 0 deletions convenience.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@

/* a collection of user friendly tools */

#include <stdint.h>
//struct rtlsdr_dev_t;
//typedef struct rtlsdr_dev_t rtlsdr_dev_t;

/*!
* Convert standard suffixes (k, M, G) to double
*
Expand Down
217 changes: 217 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
/*
* Copyright (C) 2012 by Kyle Keen <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

typedef void* rtlsdr_dev_t;
#include "convenience.h"
#include "rtl_ais.h"

void usage(void)
{
fprintf(stderr,
"rtl_ais, a simple AIS tuner\n"
"\t and generic dual-frequency FM demodulator\n\n"
"(probably not a good idea to use with e4000 tuners)\n"
"Use: rtl_ais [options] [outputfile]\n"
"\t[-l left_frequency (default: 161.975M)]\n"
"\t[-r right_frequency (default: 162.025M)]\n"
"\t left freq < right freq\n"
"\t frequencies must be within 1.2MHz\n"
"\t[-s sample_rate (default: 24k)]\n"
"\t maximum value, might be down to 12k\n"
"\t[-o output_rate (default: 48k)]\n"
"\t must be equal or greater than twice -s value\n"
"\t[-E toggle edge tuning (default: off)]\n"
"\t[-D toggle DC filter (default: on)]\n"
//"\t[-O toggle oversampling (default: off)\n"
"\t[-d device_index (default: 0)]\n"
"\t[-g tuner_gain (default: automatic)]\n"
"\t[-p ppm_error (default: 0)]\n"
"\t[-R enable RTL chip AGC (default: off)]\n"
"\t[-A turn off built-in AIS decoder (default: on)]\n"
"\t use this option to output samples to file or stdout.\n"
"\tBuilt-in AIS decoder options:\n"
"\t[-h host (default: 127.0.0.1)]\n"
"\t[-P port (default: 10110)]\n"
"\t[-n log NMEA sentences to console (stderr) (default off)]\n"
"\t[-L log sound levels to console (stderr) (default off)]\n\n"
"\t[-S seconds_for_decoder_stats (default 0=off)]\n\n"
"\tWhen the built-in AIS decoder is disabled the samples are sent to\n"
"\tto [outputfile] (a '-' dumps samples to stdout)\n"
"\t omitting the filename also uses stdout\n\n"
"\tOutput is stereo 2x16 bit signed ints\n\n"
"\tExamples:\n"
"\tReceive AIS traffic,sent UDP NMEA sentences to 127.0.0.1 port 10110\n"
"\t and log the senteces to console:\n\n"
"\trtl_ais -n\n\n"
"\tTune two fm stations and play one on each channel:\n\n"
"\trtl_ais -l233.15M -r233.20M -A | play -r48k -traw -es -b16 -c2 -V1 - "
"\n");
exit(1);
}

static volatile int do_exit = 0;
static void sighandler(int signum)
{
signum = signum;
fprintf(stderr, "Signal caught, exiting!\n");
do_exit = 1;
}

int main(int argc, char **argv)
{
#ifndef WIN32
struct sigaction sigact;

sigact.sa_handler = sighandler;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigaction(SIGINT, &sigact, NULL);
sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGQUIT, &sigact, NULL);
sigaction(SIGPIPE, &sigact, NULL);
#else
signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
#endif
int opt;

struct rtl_ais_config config;
rtl_ais_default_config(&config);

config.host = strdup("127.0.0.1");
config.port = strdup("10110");

while ((opt = getopt(argc, argv, "l:r:s:o:EODd:g:p:RAP:h:nLS:?")) != -1)
{
switch (opt) {
case 'l':
config.left_freq = (int)atofs(optarg);
break;
case 'r':
config.right_freq = (int)atofs(optarg);
break;
case 's':
config.sample_rate = (int)atofs(optarg);
break;
case 'o':
config.output_rate = (int)atofs(optarg);
break;
case 'E':
config.edge = !config.edge;
break;
case 'D':
config.dc_filter = !config.dc_filter;
break;
case 'O':
config.oversample = !config.oversample;
break;
case 'd':
config.dev_index = verbose_device_search(optarg);
config.dev_given = 1;
break;
case 'g':
config.gain = (int)(atof(optarg) * 10);
break;
case 'p':
config.ppm_error = atoi(optarg);
config.custom_ppm = 1;
break;
case 'R':
config.rtl_agc=1;
break;
case 'A':
config.use_internal_aisdecoder=0;
break;
case 'P':
config.port=strdup(optarg);
break;
case 'T':
config.use_tcp_listener=1;
break;
case 't':
config.tcp_keep_ais_time = atoi(optarg);
break;
case 'h':
config.host=strdup(optarg);
break;
case 'L':
config.show_levels=1;
break;
case 'S':
config.seconds_for_decoder_stats=atoi(optarg);
break;
case 'n':
config.debug_nmea = 1;
break;
case '?':
default:
usage();
return 2;
}
}

if (argc <= optind) {
config.filename = "-";
} else {
config.filename = argv[optind];
}

if (config.edge) {
fprintf(stderr, "Edge tuning enabled.\n");
} else {
fprintf(stderr, "Edge tuning disabled.\n");
}
if (config.dc_filter) {
fprintf(stderr, "DC filter enabled.\n");
} else {
fprintf(stderr, "DC filter disabled.\n");
}
if (config.rtl_agc) {
fprintf(stderr, "RTL AGC enabled.\n");
} else {
fprintf(stderr, "RTL AGC disabled.\n");
}
if (config.use_internal_aisdecoder) {
fprintf(stderr, "Internal AIS decoder enabled.\n");
} else {
fprintf(stderr, "Internal AIS decoder disabled.\n");
}

struct rtl_ais_context *ctx = rtl_ais_start(&config);
if(!ctx) {
fprintf(stderr, "\nrtl_ais_start failed, exiting...\n");
exit(1);
}

// loop printing received ais messages to console
while(!do_exit && rtl_ais_isactive(ctx)) {
const char *str;
while((str = rtl_ais_next_message(ctx)))
puts(str);

usleep(50000);
}

rtl_ais_cleanup(ctx);
return 0;
}
Loading

0 comments on commit a54ab00

Please sign in to comment.