Initial Commit

Downloaded from http://www.aishub.net/downloads/aisdecoder-1.0.0.tar.gz
This commit is contained in:
Sarah Jamie Lewis 2020-01-04 18:53:22 -08:00
commit a6f6209826
21 changed files with 2379 additions and 0 deletions

105
CMakeLists.txt Normal file
View File

@ -0,0 +1,105 @@
project(aisdecoder)
cmake_minimum_required(VERSION 2.8)
INCLUDE(CheckIncludeFiles)
set(STATIC_LIB "${PROJECT_NAME}_static_lib")
set(DYNAMIC_LIB "${PROJECT_NAME}_dynamic_lib")
macro(get_WIN32_WINNT version)
if (WIN32 AND CMAKE_SYSTEM_VERSION)
set(ver ${CMAKE_SYSTEM_VERSION})
string(REPLACE "." "" ver ${ver})
string(REGEX REPLACE "([0-9])" "0\\1" ver ${ver})
set(${version} "0x${ver}")
endif()
endmacro()
if(NOT WIN32)
CHECK_INCLUDE_FILES(alsa/asoundlib.h HAVE_ALSA)
CHECK_INCLUDE_FILES(pulse/pulseaudio.h HAVE_PULSEAUDIO)
else()
get_WIN32_WINNT(ver)
if(${CMAKE_SYSTEM_VERSION} LESS 5.1)
MESSAGE(FATAL_ERROR "Windows XP or higher is required")
endif()
add_definitions(-D_WIN32_WINNT=${ver})
endif()
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
set(CMAKE_BUILD_TYPE "Debug")
endif()
if(NOT $ENV{CMAKE_BUILD_TYPE} STREQUAL "")
set(CMAKE_BUILD_TYPE $ENV{CMAKE_BUILD_TYPE})
endif()
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE)
MESSAGE(STATUS "Build configuration: ${CMAKE_BUILD_TYPE}")
set(CMAKE_C_FLAGS_DEBUG "-g -Wall")
set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall")
if(${CMAKE_BUILD_TYPE} STREQUAL "RELEASE")
set(CMAKE_EXE_LINKER_FLAGS "-s")
endif()
if(NOT WIN32)
if(NOT HAVE_ALSA AND NOT HAVE_PULSEAUDIO)
MESSAGE(FATAL_ERROR "The development files for ALSA or PulseAudio are required - libasound-dev, libpulse-dev")
endif()
endif()
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/lib)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
set(SRC_LIST_LIB "${CMAKE_CURRENT_SOURCE_DIR}/src/sounddecoder.c")
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src/lib SRC_LIST_LIB)
if(NOT WIN32)
if(HAVE_PULSEAUDIO)
aux_source_directory(src/lib/pulseaudio SRC_LIST_LIB)
set(L_PULSE pulse-simple)
else()
message(WARNING "You do not have the development files for pulseaudio (DISABLE).")
endif()
if(HAVE_ALSA)
aux_source_directory(src/lib/alsaaudio SRC_LIST_LIB)
set(L_ALSA asound)
else()
message(WARNING "You do not have the development files for ASLA (DISABLE).")
endif()
else()
aux_source_directory(src/lib/winmmaudio SRC_LIST_LIB)
endif()
add_library(${DYNAMIC_LIB} SHARED ${SRC_LIST_LIB})
set_target_properties(${DYNAMIC_LIB} PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
add_library(${STATIC_LIB} STATIC ${SRC_LIST_LIB})
set_target_properties(${STATIC_LIB} PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
if(WIN32)
target_link_libraries(${DYNAMIC_LIB} winmm)
else()
set(MYLIB_VERSION_MAJOR 1)
set(MYLIB_VERSION_MINOR 0)
set(MYLIB_VERSION_PATCH 0)
set(MYLIB_VERSION_STRING ${MYLIB_VERSION_MAJOR}.${MYLIB_VERSION_MINOR}.${MYLIB_VERSION_PATCH})
set_target_properties(${DYNAMIC_LIB}
PROPERTIES
VERSION ${MYLIB_VERSION_STRING}
SOVERSION ${MYLIB_VERSION_MAJOR})
endif()
add_executable(${PROJECT_NAME} ${SRC_LIST})
target_link_libraries(${PROJECT_NAME} ${STATIC_LIB})
if(NOT WIN32)
target_link_libraries(${PROJECT_NAME} ${L_ALSA} ${L_PULSE})
else()
target_link_libraries(${PROJECT_NAME} winmm ws2_32)
endif()

3
config.h.in Normal file
View File

@ -0,0 +1,3 @@
#cmakedefine PROJECT_NAME "${PROJECT_NAME}"
#cmakedefine HAVE_ALSA
#cmakedefine HAVE_PULSEAUDIO

119
src/lib/alsaaudio/alsaaudio.c Executable file
View File

@ -0,0 +1,119 @@
/*
* input.c
*
* (c) Ruben Undheim 2008
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <strings.h>
#include "config.h"
#include "alsaaudio.h"
#include "hmalloc.h"
#ifdef DMALLOC
#include <dmalloc.h>
#endif
int input_initialize(snd_pcm_t * handle, short **buffer, int *buffer_l, int channels_num, char *error_msg)
{
int err;
int dir;
snd_pcm_hw_params_t *hwparams = NULL;
snd_pcm_hw_params_alloca(&hwparams);
if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0) {
sprintf(error_msg, "Error initializing hwparams");
return -1;
}
if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
sprintf(error_msg, "Error setting acecss mode (SND_PCM_ACCESS_RW_INTERLEAVED): %s", snd_strerror(err));
return -1;
}
if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE)) < 0) {
sprintf(error_msg, "Error setting format (SND_PCM_FORMAT_S16_LE): %s", snd_strerror(err));
return -1;
}
if ((err = snd_pcm_hw_params_set_channels(handle, hwparams, channels_num)) < 0) {
sprintf(error_msg, "Error setting channels %d: %s", channels_num, snd_strerror(err));
return -1;
}
unsigned int rate = 48000;
if ((err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &rate, 0)) < 0) {
sprintf(error_msg, "Error setting sample rate (%d): %s", rate, snd_strerror(err));
return -1;
}
snd_pcm_uframes_t size = 4096; /* number of frames */
dir = 0;
if ((err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &size, &dir)) < 0) {
sprintf(error_msg, "Error setting buffer size (%lu): %s", size, snd_strerror(err));
return -1;
}
if ((err = snd_pcm_hw_params(handle, hwparams)) < 0) {
sprintf(error_msg, "Error writing hwparams: %s", snd_strerror(err));
return -1;
}
snd_pcm_hw_params_get_period_size(hwparams, &size, &dir);
//int extra = (int) size % 5;
//*buffer_l = (int) size - extra;
*buffer_l = (int) size;
int buffer_len_in_bytes = *buffer_l * sizeof(short) * channels_num;
// hlog(LOG_DEBUG, LOGPREFIX "Using sound buffer size: %d frames of %d channels: %d bytes",
// *buffer_l, channels, buffer_len_in_bytes);
*buffer = (short *) hmalloc(buffer_len_in_bytes);
bzero(*buffer, buffer_len_in_bytes);
return 0;
}
int alsa_read(snd_pcm_t * handle, short *buffer, int count)
{
int err;
err = snd_pcm_readi(handle, buffer, count);
if (err == -EPIPE) {
// hlog(LOG_ERR, LOGPREFIX "Overrun");
snd_pcm_prepare(handle);
} else if (err < 0) {
// hlog(LOG_ERR, LOGPREFIX "Read error");
} else if (err != count) {
// hlog(LOG_INFO, LOGPREFIX "Short read, read %d frames", err);
} else {
/*hlog(LOG_DEBUG, LOGPREFIX "Read %d samples", err); */
}
return err;
}
void input_cleanup(snd_pcm_t *handle)
{
snd_pcm_close(handle);
}

View File

@ -0,0 +1,40 @@
/*
* input.h
*
* (c) Ruben Undheim 2008
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef INC_INPUT_H
#define INC_INPUT_H
#include <alsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
int input_initialize(snd_pcm_t * handle, short **, int *, int channels_num, char *error_msg);
int alsa_read(snd_pcm_t * handle, short *buffer, int count);
void input_cleanup(snd_pcm_t *handle);
#ifdef __cplusplus
}
#endif
#endif

20
src/lib/callbacks.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef CALLBACKS_H
#define CALLBACKS_H
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*receiver_on_level_changed)(float level, int channel, unsigned char high);
typedef void (*decoder_on_nmea_sentence_received)(const char *sentence,
unsigned int length,
unsigned char sentences,
unsigned char sentencenum);
extern receiver_on_level_changed on_sound_level_changed;
extern decoder_on_nmea_sentence_received on_nmea_sentence_received;
#ifdef __cplusplus
}
#endif
#endif // CALLBACKS_H

275
src/lib/filter-i386.h Normal file
View File

@ -0,0 +1,275 @@
/*
* filter-i386.h -- optimized filter routines
*
* Copyright (C) 1996
* Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu)
*
* 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* ---------------------------------------------------------------------- */
#ifndef _FILTER_I386_H
#define _FILTER_I386_H
/* ---------------------------------------------------------------------- */
#define __HAVE_ARCH_MAC
#define mac(a,b,size) \
(__builtin_constant_p(size) ? __mac_c((a),(b),(size)) : __mac_g((a),(b),(size)))
#include <stdio.h>
extern inline float __mac_g(const float *a, const float *b,
unsigned int size)
{
float sum = 0;
unsigned int i;
for (i = 0; i < size; i++)
sum += (*a++) * (*b++);
return sum;
}
extern inline float __mac_c(const float *a, const float *b,
unsigned int size)
{
float f;
/*
* inspired from Phil Karn, KA9Q's home page
*/
switch (size) {
case 53:
asm volatile ("flds (%1);\n\t"
"fmuls (%2);\n\t"
"flds 4(%1);\n\t"
"fmuls 4(%2);\n\t"
"flds 8(%1);\n\t"
"fmuls 8(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 12(%1);\n\t"
"fmuls 12(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 16(%1);\n\t"
"fmuls 16(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 20(%1);\n\t"
"fmuls 20(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 24(%1);\n\t"
"fmuls 24(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 28(%1);\n\t"
"fmuls 28(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 32(%1);\n\t"
"fmuls 32(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 36(%1);\n\t"
"fmuls 36(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 40(%1);\n\t"
"fmuls 40(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 44(%1);\n\t"
"fmuls 44(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 48(%1);\n\t"
"fmuls 48(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 52(%1);\n\t"
"fmuls 52(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 56(%1);\n\t"
"fmuls 56(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 60(%1);\n\t"
"fmuls 60(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 64(%1);\n\t"
"fmuls 64(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 68(%1);\n\t"
"fmuls 68(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 72(%1);\n\t"
"fmuls 72(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 76(%1);\n\t"
"fmuls 76(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 80(%1);\n\t"
"fmuls 80(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 84(%1);\n\t"
"fmuls 84(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 88(%1);\n\t"
"fmuls 88(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 92(%1);\n\t"
"fmuls 92(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 96(%1);\n\t"
"fmuls 96(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 100(%1);\n\t"
"fmuls 100(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 104(%1);\n\t"
"fmuls 104(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 108(%1);\n\t"
"fmuls 108(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 112(%1);\n\t"
"fmuls 112(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 116(%1);\n\t"
"fmuls 116(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 120(%1);\n\t"
"fmuls 120(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 124(%1);\n\t"
"fmuls 124(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 128(%1);\n\t"
"fmuls 128(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 132(%1);\n\t"
"fmuls 132(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 136(%1);\n\t"
"fmuls 136(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 140(%1);\n\t"
"fmuls 140(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 144(%1);\n\t"
"fmuls 144(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 148(%1);\n\t"
"fmuls 148(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 152(%1);\n\t"
"fmuls 152(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 156(%1);\n\t"
"fmuls 156(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 160(%1);\n\t"
"fmuls 160(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 164(%1);\n\t"
"fmuls 164(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 168(%1);\n\t"
"fmuls 168(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 172(%1);\n\t"
"fmuls 172(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 176(%1);\n\t"
"fmuls 176(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 180(%1);\n\t"
"fmuls 180(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 184(%1);\n\t"
"fmuls 184(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 188(%1);\n\t"
"fmuls 188(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 192(%1);\n\t"
"fmuls 192(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 196(%1);\n\t"
"fmuls 196(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 200(%1);\n\t"
"fmuls 200(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 204(%1);\n\t"
"fmuls 204(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"flds 208(%1);\n\t"
"fmuls 208(%2);\n\t"
"fxch %%st(2);\n\t"
"faddp;\n\t"
"faddp;\n\t":"=t" (f):"r"(a), "r"(b):"memory");
return f;
default:
fprintf(stderr,
"Warning: optimize __mac_c(..., ..., %d)\n", size);
return __mac_g(a, b, size);
}
}
/* ---------------------------------------------------------------------- */
#endif /* _FILTER_I386_H */

145
src/lib/filter.c Normal file
View File

@ -0,0 +1,145 @@
/*
* filter.c -- FIR filter
*
* Copyright (C) 2001, 2002, 2003
* Tomi Manninen (oh2bns@sral.fi)
*
* This file is part of gMFSK.
*
* gMFSK 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.
*
* gMFSK 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 gMFSK; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <stdlib.h>
#include <string.h>
#include "hmalloc.h"
#include "filter.h"
#undef DEBUG
#ifdef DEBUG
#include <stdio.h>
#endif
/* ---------------------------------------------------------------------- */
/*
* This gets used when not optimising
*/
#ifndef __OPTIMIZE__
float filter_mac(const float *a, const float *b, int size)
{
float sum = 0;
int i;
for (i = 0; i < size; i++)
sum += a[i] * b[i];
return sum;
}
#endif
/* ---------------------------------------------------------------------- */
struct filter *filter_init(int len, float *taps)
{
struct filter *f;
f = (struct filter *) hmalloc(sizeof(struct filter));
memset(f, 0, sizeof(struct filter));
f->taps = (float *) hmalloc(len * sizeof(float));
memcpy(f->taps, taps, len * sizeof(float));
f->length = len;
f->pointer = f->length;
return f;
}
void filter_free(struct filter *f)
{
if (f) {
hfree(f->taps);
hfree(f);
}
}
/* ---------------------------------------------------------------------- */
void filter_run(struct filter *f, float in, float *out)
{
float *ptr = f->buffer + f->pointer++;
*ptr = in;
// TODO: optimize: pass filter length as constant to enable
// using optimized __mac_c and fix the number of rounds there!
#ifndef __HAVE_ARCH_MAC
*out = filter_mac(ptr - f->length, f->taps, f->length);
#else
*out = mac(ptr - f->length, f->taps, f->length);
#endif
//*out = filter_mac(ptr - f->length, f->taps, 53);
if (f->pointer == BufferLen) {
memcpy(f->buffer,
f->buffer + BufferLen - f->length,
f->length * sizeof(float));
f->pointer = f->length;
}
}
short filter_run_buf(struct filter *f, short *in, float *out, int step, int len)
{
int id = 0;
int od = 0;
short maxval = 0;
int pointer = f->pointer;
float *buffer = f->buffer;
while (od < len) {
buffer[pointer] = in[id];
// look for peak volume
if (in[id] > maxval)
maxval = in[id];
#ifndef __HAVE_ARCH_MAC
out[od] = filter_mac(&buffer[pointer - f->length], f->taps, f->length);
#else
out[od] = mac(&buffer[pointer - f->length], f->taps, f->length);
#endif
pointer++;
/* the buffer is much smaller than the incoming chunks */
if (pointer == BufferLen) {
memcpy(buffer,
buffer + BufferLen - f->length,
f->length * sizeof(float));
pointer = f->length;
}
id += step;
od++;
}
f->pointer = pointer;
return maxval;
}
/* ---------------------------------------------------------------------- */

72
src/lib/filter.h Normal file
View File

@ -0,0 +1,72 @@
/*
* filter.h -- FIR filter
*
* Copyright (C) 2001, 2002, 2003, 2004
* Tomi Manninen (oh2bns@sral.fi)
*
* This file is part of gMFSK.
*
* gMFSK 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.
*
* gMFSK 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 gMFSK; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef _FILTER_H
#define _FILTER_H
#define BufferLen 1024
/* ---------------------------------------------------------------------- */
#ifdef __OPTIMIZE__
#ifdef __i386__
#include "filter-i386.h"
#endif /* __i386__ */
#ifndef __HAVE_ARCH_MAC
static __inline__ float filter_mac(const float *a, const float *b, int size)
{
float sum = 0;
int i;
for (i = 0; i < size; i++)
sum += a[i] * b[i];
return sum;
}
#endif /* __HAVE_ARCH_MAC */
#endif /* __OPTIMIZE__ */
/* ---------------------------------------------------------------------- */
struct filter {
int length;
float *taps;
float buffer[BufferLen];
int pointer;
};
extern struct filter *filter_init(int len, float *taps);
extern void filter_free(struct filter *f);
extern void filter_run(struct filter *f, float in, float *out);
extern short filter_run_buf(struct filter *f, short *in, float *out, int step, int len);
/* ---------------------------------------------------------------------- */
#endif /* _FILTER_H */

74
src/lib/hmalloc.c Normal file
View File

@ -0,0 +1,74 @@
/*
* (c) Heikki Hannikainen, OH7LZB <hessu@hes.iki.fi>
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/*
* Replacements for malloc, realloc and free, which never fail,
* and might keep statistics on memory allocation...
*
* GPL'ed, by Heikki Hannikainen <hessu@hes.iki.fi>
*/
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include "hmalloc.h"
#ifdef DMALLOC
#include <dmalloc.h>
#endif
int mem_panic = 0;
void *hmalloc(size_t size) {
void *p;
if (!(p = malloc(size))) {
if (mem_panic) exit(1);
mem_panic = 1;
exit(1);
}
return p;
}
void *hrealloc(void *ptr, size_t size) {
void *p;
if (!(p = realloc(ptr, size))) {
if (mem_panic) exit(1);
mem_panic = 1;
exit(1);
}
return p;
}
void hfree(void *ptr) {
if (ptr) free(ptr);
}
char *hstrdup(const char *s) {
char *p;
p = (char*)hmalloc(strlen(s)+1);
strcpy(p, s);
return p;
}

42
src/lib/hmalloc.h Normal file
View File

@ -0,0 +1,42 @@
/*
* (c) Heikki Hannikainen, OH7LZB <hessu@hes.iki.fi>
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef HMALLOC_H
#define HMALLOC_H
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Replacements for malloc, realloc and free, which never fail,
* and might keep statistics on memory allocation...
*/
extern void *hmalloc(size_t size);
extern void *hrealloc(void *ptr, size_t size);
extern void hfree(void *ptr);
extern char *hstrdup(const char *s);
#ifdef __cplusplus
}
#endif
#endif

369
src/lib/protodec.c Normal file
View File

@ -0,0 +1,369 @@
/*
* protodec.c
*
* (c) Ruben Undheim 2008
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <time.h>
#include <string.h> /* String function definitions */
#include "callbacks.h"
#include "config.h"
#include "protodec.h"
#include "hmalloc.h"
decoder_on_nmea_sentence_received on_nmea_sentence_received=NULL;
#ifdef DMALLOC
#include <dmalloc.h>
#endif
void protodec_initialize(struct demod_state_t *d, struct serial_state_t *serial, char chanid)
{
memset(d, 0, sizeof(struct demod_state_t));
d->chanid = chanid;
d->serial = serial;
d->receivedframes = 0;
d->lostframes = 0;
d->lostframes2 = 0;
protodec_reset(d);
d->seqnr = 0;
d->buffer = hmalloc(DEMOD_BUFFER_LEN);
d->rbuffer = hmalloc(DEMOD_BUFFER_LEN);
d->nmea = hmalloc(NMEABUFFER_LEN);
}
void protodec_deinit(struct demod_state_t *d)
{
hfree(d->buffer);
hfree(d->rbuffer);
hfree(d->nmea);
}
void protodec_reset(struct demod_state_t *d)
{
d->state = ST_SKURR;
d->nskurr = 0;
d->ndata = 0;
d->npreamble = 0;
d->nstartsign = 0;
d->nstopsign = 0;
d->antallpreamble = 0;
d->antallenner = 0;
d->last = 0;
d->bitstuff = 0;
d->bufferpos = 0;
}
/*
* Calculates CRC-checksum
*/
unsigned short protodec_sdlc_crc(const unsigned char *data, unsigned len)
{
unsigned short c, crc = 0xffff;
while (len--)
for (c = 0x100 + *data++; c > 1; c >>= 1)
if ((crc ^ c) & 1)
crc = (crc >> 1) ^ 0x8408;
else
crc >>= 1;
return ~crc;
}
int protodec_calculate_crc(int length_bits, struct demod_state_t *d)
{
int length_bytes;
unsigned char *buf;
int buflen;
int i, j, x;
unsigned char tmp;
if (length_bits <= 0) {
return 0;
}
length_bytes = length_bits / 8;
buflen = length_bytes + 2;
/* what is this? */
buf = (unsigned char *) hmalloc(sizeof(*buf) * buflen);
for (j = 0; j < buflen; j++) {
tmp = 0;
for (i = 0; i < 8; i++)
tmp |= (((d->buffer[i + 8 * j]) << (i)));
buf[j] = tmp;
}
/* ok, here's the actual CRC calculation */
unsigned short crc = protodec_sdlc_crc(buf, buflen);
//DBG(printf("CRC: %04x\n",crc));
/* what is this? */
memset(d->rbuffer, 0, DEMOD_BUFFER_LEN);
for (j = 0; j < length_bytes; j++) {
for (i = 0; i < 8; i++) {
x = j * 8 + i;
if (x >= DEMOD_BUFFER_LEN) {
hfree(buf);
return 0;
} else {
d->rbuffer[x] = (buf[j] >> (7 - i)) & 1;
}
}
}
hfree(buf);
return (crc == 0x0f47);
}
unsigned long protodec_henten(int from, int size, unsigned char *frame)
{
int i = 0;
unsigned long tmp = 0;
for (i = 0; i < size; i++)
tmp |= (frame[from + i]) << (size - 1 - i);
return tmp;
}
void protodec_generate_nmea(struct demod_state_t *d, int bufferlen, int fillbits, time_t received_t)
{
int senlen;
int pos;
int k, offset;
int m;
unsigned char sentences, sentencenum, nmeachk, letter;
//6bits to nmea-ascii. One sentence len max 82char
//inc. head + tail.This makes inside datamax 62char multipart, 62 single
senlen = 56; //this is normally not needed.For testing only. May be fixed number
if (bufferlen <= (senlen * 6)) {
sentences = 1;
} else {
sentences = bufferlen / (senlen * 6);
//sentences , if overflow put one more
if (bufferlen % (senlen * 6) != 0)
sentences++;
};
sentencenum = 0;
pos = 0;
offset = (sentences>1) ? 15 : 14;
do {
k = offset; //leave room for nmea header
while (k < senlen + offset && bufferlen > pos) {
letter = (unsigned char)protodec_henten(pos, 6, d->rbuffer);
// 6bit-to-ascii conversion by IEC
letter += (letter < 40) ? 48 : 56;
d->nmea[k] = letter;
pos += 6;
k++;
}
sentencenum++;
memcpy(&d->nmea[0], "!AIVDM,0,0,", 11);
d->nmea[7] += sentences;
d->nmea[9] += sentencenum;
memcpy(&d->nmea[k], ",0*00\0", 6);
if (sentences > 1) {
d->nmea[11] = '0' + d->seqnr;
d->nmea[12] = ',';
d->nmea[13] = d->chanid;
d->nmea[14] = ',';
if (sentencenum == sentences) d->nmea[k + 1] = '0' + fillbits;
} else {
d->nmea[11] = ',';
d->nmea[12] = d->chanid;
d->nmea[13] = ',';
}
m = 1;
nmeachk = d->nmea[m++];
while (d->nmea[m] != '*') nmeachk ^= d->nmea[m++];
sprintf(&d->nmea[k + 3], "%02X\r\n", nmeachk);
if (on_nmea_sentence_received != NULL)
on_nmea_sentence_received(d->nmea, k+7, sentences, sentencenum);
} while (sentencenum < sentences);
}
void protodec_getdata(int bufferlen, struct demod_state_t *d)
{
unsigned char type = protodec_henten(0, 6, d->rbuffer);
if (type < 1 || type > MAX_AIS_PACKET_TYPE /* 9 */)
return;
// unsigned long mmsi = protodec_henten(8, 30, d->rbuffer);
int fillbits = 0;
int k;
time_t received_t;
time(&received_t);
if (bufferlen % 6 > 0) {
fillbits = 6 - (bufferlen % 6);
for (k = bufferlen; k < bufferlen + fillbits; k++)
d->rbuffer[k] = 0;
bufferlen = bufferlen + fillbits;
}
protodec_generate_nmea(d, bufferlen, fillbits, received_t);
d->seqnr++;
if (d->seqnr > 9)
d->seqnr = 0;
if (type < 1 || type > MAX_AIS_PACKET_TYPE)
return; // unsupported packet type
}
void protodec_decode(char *in, int count, struct demod_state_t *d)
{
int i = 0;
int bufferlength, correct;
while (i < count) {
switch (d->state) {
case ST_DATA:
if (d->bitstuff) {
if (in[i] == 1) {
d->state = ST_STOPSIGN;
d->ndata = 0;
d->bitstuff = 0;
} else {
d->ndata++;
d->last = in[i];
d->bitstuff = 0;
}
} else {
if (in[i] == d->last && in[i] == 1) {
d->antallenner++;
if (d->antallenner == 4) {
d->bitstuff = 1;
d->antallenner = 0;
}
} else
d->antallenner = 0;
d->buffer[d->bufferpos] = in[i];
d->bufferpos++;
d->ndata++;
if (d->bufferpos >= 449) {
protodec_reset(d);
}
}
break;
case ST_SKURR:
if (in[i] != d->last)
d->antallpreamble++;
else
d->antallpreamble = 0;
d->last = in[i];
if (d->antallpreamble > 14 && in[i] == 0) {
d->state = ST_PREAMBLE;
d->nskurr = 0;
d->antallpreamble = 0;
}
d->nskurr++;
break;
case ST_PREAMBLE:
if (in[i] != d->last && d->nstartsign == 0) {
d->antallpreamble++;
} else {
if (in[i] == 1) {
if (d->nstartsign == 0) {
d->nstartsign = 3;
d->last = in[i];
} else if (d->nstartsign == 5) {
d->nstartsign++;
d->npreamble = 0;
d->antallpreamble = 0;
d->state = ST_STARTSIGN;
} else {
d->nstartsign++;
}
} else {
if (d->nstartsign == 0) {
d->nstartsign = 1;
} else {
protodec_reset(d);
}
}
}
d->npreamble++;
break;
case ST_STARTSIGN:
if (d->nstartsign >= 7) {
if (in[i] == 0) {
d->state = ST_DATA;
d->nstartsign = 0;
d->antallenner = 0;
memset(d->buffer, 0, DEMOD_BUFFER_LEN);
d->bufferpos = 0;
} else {
protodec_reset(d);
}
} else if (in[i] == 0) {
protodec_reset(d);
}
d->nstartsign++;
break;
case ST_STOPSIGN:
bufferlength = d->bufferpos - 6 - 16;
if (in[i] == 0 && bufferlength > 0) {
correct = protodec_calculate_crc(bufferlength, d);
if (correct) {
d->receivedframes++;
protodec_getdata(bufferlength, d);
} else {
d->lostframes++;
}
} else {
d->lostframes2++;
}
protodec_reset(d);
break;
}
d->last = in[i];
i++;
}
}

65
src/lib/protodec.h Normal file
View File

@ -0,0 +1,65 @@
/*
* protodec.h
*
* (c) Ruben Undheim 2008
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef INC_PROTODEC_H
#define INC_PROTODEC_H
#define ST_SKURR 1
#define ST_PREAMBLE 2
#define ST_STARTSIGN 3
#define ST_DATA 4
#define ST_STOPSIGN 5
#define DEMOD_BUFFER_LEN 450
#define MAX_AIS_PACKET_TYPE 27
#define NMEABUFFER_LEN 100
struct demod_state_t {
char chanid;
int state;
unsigned int offset;
int nskurr, npreamble, nstartsign, ndata, nstopsign;
int antallenner;
unsigned char *buffer;
unsigned char *rbuffer;
char *tbuffer;
int bufferpos;
char last;
int antallpreamble;
int bitstuff;
int receivedframes;
int lostframes;
int lostframes2;
unsigned char seqnr;
struct serial_state_t *serial;
char *nmea;
};
void protodec_initialize(struct demod_state_t *d, struct serial_state_t *serial, char chanid);
void protodec_reset(struct demod_state_t *d);
void protodec_getdata(int bufferlengde, struct demod_state_t *d);
void protodec_decode(char *in, int count, struct demod_state_t *d);
#endif

View File

@ -0,0 +1,52 @@
/*
* pulseaudio.c
*
* (c) Ruben Undheim 2012
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include "pulseaudio.h"
pa_simple *pulseaudio_initialize(unsigned char channels_number, const char *appname) {
pa_simple *s;
pa_sample_spec ss;
ss.format = PA_SAMPLE_S16NE;
ss.channels = channels_number;
ss.rate = 48000;
s = pa_simple_new(NULL,
(appname != NULL) ? appname : "AIS Demodulator",
PA_STREAM_RECORD, NULL, "AIS data", &ss, NULL, NULL, NULL);
return s;
}
void pulseaudio_cleanup(pa_simple *s){
pa_simple_free(s);
}
int pulseaudio_read(pa_simple *s, short *buffer, int count, unsigned char channels_number){
int number_read = pa_simple_read(s, buffer, (size_t)(count * sizeof(short) * channels_number), NULL);
return (number_read < 0) ? -1 : count;
}

View File

@ -0,0 +1,39 @@
/*
* pulseaudio.h
*
* (c) Ruben Undheim 2012
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef INC_PULSEAUDIO_H
#define INC_PULSEAUDIO_H
#include <pulse/simple.h>
#ifdef __cplusplus
extern "C" {
#endif
pa_simple *pulseaudio_initialize(unsigned char channels_number, const char *appname);
void pulseaudio_cleanup(pa_simple *s);
int pulseaudio_read(pa_simple *s, short *buffer, int count, unsigned char channels_number);
#ifdef __cplusplus
}
#endif
#endif /* INC_PULSEAUDIO_H */

147
src/lib/receiver.c Executable file
View File

@ -0,0 +1,147 @@
/*
* receiver.c
*
* (c) Ruben Undheim 2008
* (c) Heikki Hannikainen 2008
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <string.h>
#include <time.h>
#include "receiver.h"
#include "hmalloc.h"
#include "filter.h"
static int sound_levellog=1;
receiver_on_level_changed on_sound_level_changed=NULL;
static float coeffs[]={
2.5959e-55, 2.9479e-49, 1.4741e-43, 3.2462e-38, 3.1480e-33,
1.3443e-28, 2.5280e-24, 2.0934e-20, 7.6339e-17, 1.2259e-13,
8.6690e-11, 2.6996e-08, 3.7020e-06, 2.2355e-04, 5.9448e-03,
6.9616e-02, 3.5899e-01, 8.1522e-01, 8.1522e-01, 3.5899e-01,
6.9616e-02, 5.9448e-03, 2.2355e-04, 3.7020e-06, 2.6996e-08,
8.6690e-11, 1.2259e-13, 7.6339e-17, 2.0934e-20, 2.5280e-24,
1.3443e-28, 3.1480e-33, 3.2462e-38, 1.4741e-43, 2.9479e-49,
2.5959e-55
};
#define COEFFS_L 36
struct receiver *init_receiver(char name, int num_ch, int ch_ofs)
{
struct receiver *rx;
rx = (struct receiver *) hmalloc(sizeof(struct receiver));
memset(rx, 0, sizeof(struct receiver));
rx->filter = filter_init(COEFFS_L, coeffs);
rx->decoder = hmalloc(sizeof(struct demod_state_t));
protodec_initialize(rx->decoder, NULL, name);
rx->name = name;
rx->lastbit = 0;
rx->num_ch = num_ch;
rx->ch_ofs = ch_ofs;
rx->pll = 0;
rx->pllinc = 0x10000 / 5;
rx->prev = 0;
rx->last_levellog = 0;
return rx;
}
void free_receiver(struct receiver *rx)
{
if (rx) {
filter_free(rx->filter);
hfree(rx);
}
}
#define INC 16
#define FILTERED_LEN 8192
void receiver_run(struct receiver *rx, short *buf, int len)
{
float out;
int curr, bit;
char b;
short maxval = 0;
int level_distance;
float level;
int rx_num_ch = rx->num_ch;
float filtered[FILTERED_LEN];
int i;
/* len is number of samples available in buffer for each
* channels - something like 1024, regardless of number of channels */
buf += rx->ch_ofs;
if (len > FILTERED_LEN)
abort();
maxval = filter_run_buf(rx->filter, buf, filtered, rx_num_ch, len);
for (i = 0; i < len; i++) {
out = filtered[i];
curr = (out > 0);
if ((curr ^ rx->prev) == 1) {
if (rx->pll < (0x10000 / 2)) {
rx->pll += rx->pllinc / INC;
} else {
rx->pll -= rx->pllinc / INC;
}
}
rx->prev = curr;
rx->pll += rx->pllinc;
if (rx->pll > 0xffff) {
/* slice */
bit = (out > 0);
/* nrzi decode */
b = !(bit ^ rx->lastbit);
/* feed to the decoder */
protodec_decode(&b, 1, rx->decoder);
rx->lastbit = bit;
rx->pll &= 0xffff;
}
}
/* calculate level, and log it */
level = (float)maxval / (float)32768 * (float)100;
level_distance = time(NULL) - rx->last_levellog;
if (level > 95.0 && (level_distance >= 30 || level_distance >= sound_levellog)) {
if (on_sound_level_changed != NULL) on_sound_level_changed(level, rx->ch_ofs, 1);
time(&rx->last_levellog);
} else if (sound_levellog != 0 && level_distance >= sound_levellog) {
if (on_sound_level_changed != NULL) on_sound_level_changed(level, rx->ch_ofs, 0);
time(&rx->last_levellog);
}
}

59
src/lib/receiver.h Normal file
View File

@ -0,0 +1,59 @@
/*
* receiver.h
*
* (c) Ruben Undheim 2008
* (c) Heikki Hannikainen 2008
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef INC_RECEIVER_H
#define INC_RECEIVER_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include "protodec.h"
#include "callbacks.h"
struct receiver {
struct filter *filter;
char name;
int lastbit;
int num_ch;
int ch_ofs;
unsigned int pll;
unsigned int pllinc;
struct demod_state_t *decoder;
int prev;
time_t last_levellog;
};
extern struct receiver *init_receiver(char name, int num_ch, int ch_ofs);
extern void free_receiver(struct receiver *rx);
extern void receiver_run(struct receiver *rx, short *buf, int len);
#ifdef __cplusplus
}
#endif
#endif

107
src/lib/winmmaudio/winmm_input.c Executable file
View File

@ -0,0 +1,107 @@
#include "winmm_input.h"
#include <ksmedia.h>
#include <stdio.h>
#ifdef EXTENSIBLE
#define _KSDATAFORMAT_SUBTYPE_PCM (GUID) {0x00000001,0x0000,0x0010,{0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}}
#endif
static WAVEHDR wave_chunk[WAVE_BUFFERS];
static LPWAVEHDR current;
static unsigned int buffer_size=0;
static void CALLBACK waveInProc( // wave in I/O completion procedure
HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2
) {
switch (uMsg) {
case WIM_DATA:
current = (LPWAVEHDR)dwParam1;
SetEvent((HANDLE)dwInstance);
break;
}
}
int winmm_init(unsigned int devId,
LPHWAVEIN device,
int channels,
int bytesCount,
HANDLE *eventHandler
) {
*eventHandler=CreateEvent(NULL, FALSE, FALSE, NULL);
if (*eventHandler == NULL) return 0;
#ifndef EXTENSIBLE
WAVEFORMATEX Format;
Format.cbSize = 0;
Format.wFormatTag = WAVE_FORMAT_PCM;
Format.nChannels = channels;
Format.wBitsPerSample = 16;
Format.nSamplesPerSec = 48000L;
Format.nBlockAlign = Format.nChannels * Format.wBitsPerSample / 8;
Format.nAvgBytesPerSec = Format.nSamplesPerSec * Format.nBlockAlign;
LPWAVEFORMATEX pFmt=&Format;
#else
WAVEFORMATEXTENSIBLE fmt;
fmt.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
fmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
fmt.Format.nChannels = channels;
fmt.Format.wBitsPerSample = 16;
fmt.Format.nSamplesPerSec = 48000L;
fmt.Format.nBlockAlign = fmt.Format.nChannels * fmt.Format.wBitsPerSample / 8;
fmt.Format.nAvgBytesPerSec = fmt.Format.nSamplesPerSec * fmt.Format.nBlockAlign;
fmt.SubFormat=_KSDATAFORMAT_SUBTYPE_PCM;
fmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
fmt.Samples.wReserved=0;
fmt.Samples.wValidBitsPerSample=16;
LPWAVEFORMATEX pFmt=&fmt.Format;
#endif
MMRESULT err = waveInOpen(device, devId, pFmt, (DWORD)waveInProc, (DWORD)*eventHandler, CALLBACK_FUNCTION);
if (err == MMSYSERR_NOERROR) {
buffer_size=sizeof(WAVEHDR);
size_t i;
for (i=0; i<WAVE_BUFFERS; i++) {
ZeroMemory(&wave_chunk[i], buffer_size);
wave_chunk[i].lpData = malloc(bytesCount);
wave_chunk[i].dwBufferLength = bytesCount;
wave_chunk[i].dwUser = pFmt->nBlockAlign;
ZeroMemory(wave_chunk[i].lpData, wave_chunk[i].dwBufferLength);
if (waveInPrepareHeader(*device, &wave_chunk[i], buffer_size) == MMSYSERR_NOERROR) {
waveInAddBuffer(*device, &wave_chunk[i], buffer_size);
} else return 0;
}
return 1;
}
return 0;
}
int winmm_getRecorded(LPHWAVEIN device, char *out) {
int number=current->dwBytesRecorded/current->dwUser;
if (number > 0) {
memcpy(out, current->lpData, current->dwBytesRecorded);
}
waveInUnprepareHeader(*device, current, buffer_size);
current->dwBytesRecorded=0;
current->dwFlags=0;
waveInPrepareHeader(*device, current, buffer_size);
waveInAddBuffer(*device, current, buffer_size);
current=NULL;
return number;
}
void winmm_cleanup(LPHWAVEIN device, HANDLE *eventHandler) {
if (device != NULL) {
waveInStop(*device);
size_t i;
for (i=0; i<WAVE_BUFFERS; i++) {
waveInUnprepareHeader(*device, &wave_chunk[i], wave_chunk[i].dwBufferLength);
}
waveInClose(*device);
*device=0;
if (*eventHandler != NULL) {
CloseHandle(*eventHandler);
eventHandler=NULL;
}
}
}

View File

@ -0,0 +1,23 @@
#include <windows.h>
#include <mmsystem.h>
#include <mmreg.h>
//#ifdef EXTENSIBLE
#define WAVE_BUFFERS 4
#ifdef __cplusplus
extern "C" {
#endif
int winmm_init(unsigned int devId,
LPHWAVEIN device,
int channels,
int bytesCount,
HANDLE *eventHandler);
int winmm_getRecorded(LPHWAVEIN device, char *out);
void winmm_cleanup(LPHWAVEIN device, HANDLE *eventHandler);
#ifdef __cplusplus
}
#endif

347
src/main.c Executable file
View File

@ -0,0 +1,347 @@
/*
* main.cpp -- AIS Decoder
*
* Copyright (C) 2013
* Astra Paging Ltd / AISHub (info@aishub.net)
*
* AISDecoder 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.
*
* AISDecoder uses parts of GNUAIS project (http://gnuais.sourceforge.net/)
*
*/
#ifndef WIN32
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
#include <getopt.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include "config.h"
#include "sounddecoder.h"
#include "callbacks.h"
#ifdef HAVE_ALSA
#include "alsaaudio/alsaaudio.h"
#endif
#ifndef WIN32
#ifdef HAVE_ALSA
#define HELP_AUDIO_DEVICE "-D\tALSA input device\n\tUse '-D list' to see all available system devices.\n"
#else
#define HELP_AUDIO_DEVICE ""
#endif
#ifdef HAVE_PULSEAUDIO
#ifdef HAVE_ALSA
#define HELP_AUDIO_DRIVERS "pulse,alsa,file"
#else
#define HELP_AUDIO_DRIVERS "pulse,file"
#endif
#else
#ifdef HAVE_ALSA
#define HELP_AUDIO_DRIVERS "alsa,file"
#endif
#endif
#else
#define HELP_AUDIO_DRIVERS "winmm,file"
#define HELP_AUDIO_DEVICE "-D\tNumeric value for input device (default WAVE_MAPPER)\n\tUse '-D list' to see all available system devices.\n"
#endif
#define HELP_MSG \
"Usage: " PROJECT_NAME " -h hostname -p port -a " HELP_AUDIO_DRIVERS " [-f /path/file.raw] [-l]\n\n"\
"-h\tDestination host or IP address\n"\
"-p\tDestination UDP port\n"\
"-a\tAudio driver [" HELP_AUDIO_DRIVERS "]\n" HELP_AUDIO_DEVICE\
"-c\tSound channels [stereo,mono,left,right] (default stereo)\n"\
"-f\tFull path to 48kHz raw audio file\n"\
"-l\tLog sound levels to console (stderr)\n"\
"-d\tLog NMEA sentences to console (stderr)\n"\
"-H\tDisplay this help\n"
#define MAX_BUFFER_LENGTH 2048
static char buffer[MAX_BUFFER_LENGTH];
static unsigned int buffer_count=0;
#ifdef WIN32
WSADATA wsaData;
void printInDevices() {
unsigned int count = waveInGetNumDevs();
WAVEINCAPS caps;
unsigned int i = 0;
for (i = 0; i < count; i++) {
if (waveInGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR) {
fprintf(stderr, "%u: %s\r\n", i, caps.szPname);
} else {
fprintf(stderr, "Can't read devices\r\n");
exit(EXIT_FAILURE);
}
}
}
#endif
#ifdef HAVE_ALSA
void printInDevices() {
char **hints;
/* Enumerate sound devices */
int err = snd_device_name_hint(0, "pcm", (void***)&hints);
if (err != 0) {
fprintf(stderr, "Can't read data\n");
exit(EXIT_FAILURE);
}
char** n = hints;
while (*n != NULL) {
char *name = snd_device_name_get_hint(*n, "NAME");
char *desc = snd_device_name_get_hint(*n, "DESC");
char *type = snd_device_name_get_hint(*n, "IOID");
if (name != NULL && desc != NULL) {
if (type == NULL || !strcmp(type, "Input")) {
printf("%s\n%s: <%s>\n\n", desc, type == NULL ? "Input/Output" : "Input",name);
}
}
if (name != NULL) free(name);
if (name != NULL) free(desc);
if (name != NULL) free(type);
n++;
}
//Free hint buffer too
snd_device_name_free_hint((void**)hints);
snd_config_update_free_global();
}
#endif
static int sock;
static struct addrinfo* addr=NULL;
static int initSocket(const char *host, const char *portname);
static int show_levels=0;
static int debug_nmea=0;
void sound_level_changed(float level, int channel, unsigned char high) {
if (high != 0)
fprintf(stderr, "Level on ch %d too high: %.0f %%\n", channel, level);
else
fprintf(stderr, "Level on ch %d: %.0f %%\n", channel, level);
}
void nmea_sentence_received(const char *sentence,
unsigned int length,
unsigned char sentences,
unsigned char sentencenum) {
if (sentences == 1) {
if (sendto(sock, sentence, length, 0, addr->ai_addr, addr->ai_addrlen) == -1) abort();
if (debug_nmea) fprintf(stderr, "%s", sentence);
} else {
if (buffer_count + length < MAX_BUFFER_LENGTH) {
memcpy(&buffer[buffer_count], sentence, length);
buffer_count += length;
} else {
buffer_count=0;
}
if (sentences == sentencenum && buffer_count > 0) {
if (sendto(sock, buffer, buffer_count, 0, addr->ai_addr, addr->ai_addrlen) == -1) abort();
if (debug_nmea) fprintf(stderr, "%s", buffer);
buffer_count=0;
};
}
}
#define CMD_PARAMS_COMMON "h:p:a:lHf:dc:"
#ifndef WIN32
#ifdef HAVE_ALSA
#define CMD_PARAMS CMD_PARAMS_COMMON "D:"
#else
#define CMD_PARAMS CMD_PARAMS_COMMON
#endif
#else
#define CMD_PARAMS CMD_PARAMS_COMMON "D:"
#endif
int main(int argc, char *argv[]) {
Sound_Channels channels = SOUND_CHANNELS_STEREO;
char *host, *port, *file_name=NULL;
const char *params=CMD_PARAMS;
int alsa=0, pulse=0, file=0, winmm=0;
int hfnd=0, pfnd=0, afnd=0;
#ifdef WIN32
unsigned int deviceId=WAVE_MAPPER;
#endif
#ifdef HAVE_ALSA
char *alsaDevice=NULL;
#endif
int opt;
while ((opt = getopt(argc, argv, params)) != -1) {
switch (opt) {
case 'h':
host = optarg;
hfnd = 1;
break;
case 'p':
port = optarg;
pfnd = 1;
break;
case 'a':
#ifdef HAVE_PULSEAUDIO
pulse = strcmp(optarg, "pulse") == 0;
#endif
#ifdef HAVE_ALSA
alsa = strcmp(optarg, "alsa") == 0;
#endif
#ifdef WIN32
winmm = strcmp(optarg, "winmm") == 0;
#endif
file = strcmp(optarg, "file") == 0;
afnd = 1;
break;
case 'c':
if (!strcmp(optarg, "mono")) channels = SOUND_CHANNELS_MONO;
else if (!strcmp(optarg, "left")) channels = SOUND_CHANNELS_LEFT;
else if (!strcmp(optarg, "right")) channels = SOUND_CHANNELS_RIGHT;
break;
case 'l':
show_levels = 1;
break;
case 'f':
file_name = optarg;
break;
case 'd':
debug_nmea = 1;
break;
#ifdef WIN32
case 'D':
if (!strcmp(optarg, "list")) {
printInDevices();
return EXIT_SUCCESS;
} else {
deviceId = atoi(optarg);
}
break;
#endif
#ifdef HAVE_ALSA
case 'D':
if (!strcmp(optarg, "list")) {
printInDevices();
return EXIT_SUCCESS;
} else {
alsaDevice = optarg;
}
break;
#endif
case 'H':
default:
fprintf(stderr, HELP_MSG);
return EXIT_SUCCESS;
break;
}
}
if (argc < 2) {
fprintf(stderr, HELP_MSG);
return EXIT_FAILURE;
}
if (!hfnd) {
fprintf(stderr, "Host is not set\n");
return EXIT_FAILURE;
}
if (!pfnd) {
fprintf(stderr, "Port is not set\n");
return EXIT_FAILURE;
}
if (!afnd) {
fprintf(stderr, "Audio driver is not set\n");
return EXIT_FAILURE;
}
if (!alsa && !pulse && !winmm && !file) {
fprintf(stderr, "Invalid audio driver\n");
return EXIT_FAILURE;
}
if (!initSocket(host, port)) {
return EXIT_FAILURE;
}
if (show_levels) on_sound_level_changed=sound_level_changed;
on_nmea_sentence_received=nmea_sentence_received;
Sound_Driver driver = DRIVER_FILE;
#ifdef HAVE_ALSA
if (alsa) driver = DRIVER_ALSA;
#endif
#ifdef HAVE_PULSEAUDIO
if (pulse) driver = DRIVER_PULSE;
#endif
int OK=0;
#ifdef WIN32
if (!file) driver = DRIVER_WINMM;
OK=initSoundDecoder(channels, driver, file_name, deviceId);
#else
#ifdef HAVE_ALSA
OK=initSoundDecoder(channels, driver, file_name, alsaDevice);
#else
OK=initSoundDecoder(channels, driver, file_name);
#endif
#endif
int stop=0;
if (OK) {
runSoundDecoder(&stop);
} else {
fprintf(stderr, "%s\n", errorSoundDecoder);
}
freeSoundDecoder();
freeaddrinfo(addr);
#ifdef WIN32
WSACleanup();
#endif
return 0;
}
int initSocket(const char *host, const char *portname) {
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family=AF_UNSPEC;
hints.ai_socktype=SOCK_DGRAM;
hints.ai_protocol=IPPROTO_UDP;
#ifndef WIN32
hints.ai_flags=AI_ADDRCONFIG;
#else
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 0;
}
#endif
int err=getaddrinfo(host, portname, &hints, &addr);
if (err!=0) {
fprintf(stderr, "Failed to resolve remote socket address!\n");
#ifdef WIN32
WSACleanup();
#endif
return 0;
}
sock=socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sock==-1) {
fprintf(stderr, "%s",strerror(errno));
#ifdef WIN32
WSACleanup();
#endif
return 0;
}
return 1;
}

231
src/sounddecoder.c Executable file
View File

@ -0,0 +1,231 @@
/*
* sounddecoder.cpp
*
* This file is part of AISDecoder.
*
* Copyright (C) 2013
* Astra Paging Ltd / AISHub (info@aishub.net)
*
* AISDecoder 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.
*
* AISDecoder uses parts of GNUAIS project (http://gnuais.sourceforge.net/)
*
*/
#include <string.h>
#include <stdio.h>
#include "config.h"
#include "receiver.h"
#include "hmalloc.h"
#ifdef HAVE_ALSA
#include "alsaaudio/alsaaudio.h"
#endif
#ifdef HAVE_PULSEAUDIO
#include "pulseaudio/pulseaudio.h"
#endif
#ifdef WIN32
#include "winmmaudio/winmm_input.h"
#endif
#define MAX_FILENAME_SIZE 512
#define ERROR_MESSAGE_LENGTH 1024
#include "sounddecoder.h"
char errorSoundDecoder[ERROR_MESSAGE_LENGTH];
static struct receiver *rx_a=NULL;
static struct receiver *rx_b=NULL;
static short *buffer=NULL;
static int buffer_l=0;
static int buffer_read=0;
static int channels=0;
static Sound_Channels sound_channels;
static Sound_Driver driver;
static FILE *fp=NULL;
static void readBuffers();
#ifdef HAVE_ALSA
static snd_pcm_t *pcm=NULL;
#endif
#ifdef HAVE_PULSEAUDIO
static pa_simple *pa_dev=NULL;
#endif
#ifdef WIN32
static HWAVEIN winmm_device=0;
static HANDLE winmm_event=0;
#endif
#ifndef WIN32
#ifdef HAVE_ALSA
int initSoundDecoder(const Sound_Channels _channels, const Sound_Driver _driver,const char *file, const char *_alsaDevice) {
#else
int initSoundDecoder(const Sound_Channels _channels, const Sound_Driver _driver, const char *file) {
#endif
#else
int initSoundDecoder(const Sound_Channels _channels, const Sound_Driver _driver, const char *file, unsigned int deviceId) {
#endif
sound_channels = _channels;
driver = _driver;
channels = sound_channels == SOUND_CHANNELS_MONO ? 1 : 2;
errorSoundDecoder[0]=0;
char soundFile[MAX_FILENAME_SIZE+1];
switch (driver) {
case DRIVER_FILE:
strncpy(soundFile, file, MAX_FILENAME_SIZE);
soundFile[MAX_FILENAME_SIZE]=0;
fp = fopen(soundFile, "rb");
if (fp) {
buffer_l = 1024;
int extra = buffer_l % 5;
buffer_l -= extra;
buffer = (short *) hmalloc(buffer_l * sizeof(short) * channels);
} else {
strcpy(errorSoundDecoder, "Can't open raw file for read");
return 0;
}
break;
#ifdef HAVE_PULSEAUDIO
case DRIVER_PULSE:
if((pa_dev = pulseaudio_initialize(channels, NULL)) == NULL){
pa_dev=NULL;
strcpy(errorSoundDecoder, "Can't initialize pulse audio");
return 0;
} else {
buffer_l = 1024;
int extra = buffer_l % 5;
buffer_l -= extra;
buffer = (short *) hmalloc(buffer_l * sizeof(short) * channels);
}
break;
#endif
#ifdef HAVE_ALSA
case DRIVER_ALSA:
if (snd_pcm_open(&pcm, (_alsaDevice != NULL ? _alsaDevice : "default"), SND_PCM_STREAM_CAPTURE, 0) < 0 ) {
strcpy(errorSoundDecoder, "Can't open default capture device");
return 0;
} else {
if (input_initialize(pcm, &buffer, &buffer_l, channels, errorSoundDecoder) < 0) return 0;
}
break;
#endif
#ifdef WIN32
case DRIVER_WINMM:
buffer_l = 4096;
int buffer_len_in_bytes = buffer_l * sizeof(short) * channels;
buffer = (short *) hmalloc(buffer_len_in_bytes);
if (!winmm_init(deviceId, &winmm_device, channels, buffer_len_in_bytes, &winmm_event)) {
strcpy(errorSoundDecoder, "Can't initialize windows audio");
return 0;
}
break;
#endif
}
if (sound_channels == SOUND_CHANNELS_MONO) {
rx_a = init_receiver('A', 1, 0);
} else {
rx_a = init_receiver('A', 2, 0);
rx_b = init_receiver('B', 2, 1);
}
return 1;
}
void runSoundDecoder(int *stop) {
#ifdef WIN32
waveInStart(winmm_device);
#endif
while (!*stop) {
switch (driver) {
case DRIVER_FILE:
buffer_read = fread(buffer, channels * sizeof(short), buffer_l, fp);
if (buffer_read <= 0) *stop = 1;
break;
#ifdef HAVE_PULSEAUDIO
case DRIVER_PULSE:
buffer_read = pulseaudio_read(pa_dev, buffer, buffer_l, channels);
break;
#endif
#ifdef HAVE_ALSA
case DRIVER_ALSA:
buffer_read = alsa_read(pcm, buffer, buffer_l);
break;
#endif
#ifdef WIN32
case DRIVER_WINMM:
buffer_read=0;
switch(WaitForSingleObject(winmm_event, 2000)) {
case WAIT_TIMEOUT:
buffer_read=0;
break;
case WAIT_OBJECT_0:
buffer_read = winmm_getRecorded(&winmm_device, (char*)buffer);
break;
}
break;
ResetEvent(winmm_event);
#endif
}
readBuffers();
}
#ifdef WIN32
waveInStop(winmm_device);
#endif
}
static void readBuffers() {
if (buffer_read <= 0) return;
if (rx_a != NULL && sound_channels != SOUND_CHANNELS_RIGHT)
receiver_run(rx_a, buffer, buffer_read);
if (rx_b != NULL &&
(sound_channels == SOUND_CHANNELS_STEREO || sound_channels == SOUND_CHANNELS_RIGHT)
) receiver_run(rx_b, buffer, buffer_read);
}
void freeSoundDecoder() {
#ifdef HAVE_PULSEAUDIO
if (pa_dev) {
pulseaudio_cleanup(pa_dev);
pa_dev=NULL;
}
#endif
#ifdef HAVE_ALSA
if (pcm != NULL) {
input_cleanup(pcm);
pcm = NULL;
}
#endif
#ifdef WIN32
if (winmm_device) {
winmm_cleanup(&winmm_device, &winmm_event);
}
#endif
if (fp != NULL) {
fclose(fp);
fp=NULL;
}
if (rx_a != NULL) {
free_receiver(rx_a);
rx_a=NULL;
}
if (rx_b != NULL) {
free_receiver(rx_b);
rx_b=NULL;
}
if (buffer != NULL) {
hfree(buffer);
buffer = NULL;
}
}

45
src/sounddecoder.h Executable file
View File

@ -0,0 +1,45 @@
#ifndef SOUNDDECODER_H
#define SOUNDDECODER_H
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
SOUND_CHANNELS_MONO,
SOUND_CHANNELS_STEREO,
SOUND_CHANNELS_LEFT,
SOUND_CHANNELS_RIGHT
} Sound_Channels;
typedef enum {
#ifdef HAVE_ALSA
DRIVER_ALSA,
#endif
#ifdef HAVE_PULSEAUDIO
DRIVER_PULSE,
#endif
#ifdef WIN32
DRIVER_WINMM,
#endif
DRIVER_FILE
} Sound_Driver;
extern char errorSoundDecoder[];
#ifndef WIN32
#ifdef HAVE_ALSA
int initSoundDecoder(const Sound_Channels _channels, const Sound_Driver _driver,const char *file, const char *_alsaDevice);
#else
int initSoundDecoder(const Sound_Channels _channels, const Sound_Driver _driver, const char *file);
#endif
#else
int initSoundDecoder(const Sound_Channels _channels, const Sound_Driver _driver, const char *file, unsigned int deviceId);
#endif
void runSoundDecoder(int *stop);
void freeSoundDecoder();
#ifdef __cplusplus
}
#endif
#endif // SOUNDDECODER_H