remove src/lib from this repository, prepare for submodule

The contents of src/lib will be replaced with a submodule
in a following commmit
This commit is contained in:
Harald Welte 2022-05-01 13:23:22 +02:00
parent 9ed1f48b5d
commit 5c474118b1
144 changed files with 0 additions and 54263 deletions

View File

@ -1,7 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libaaimage.a
libaaimage_a_SOURCES = \
aaimage.c

View File

@ -1,74 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "aaimage.h"
extern const char *aaimage[];
void print_aaimage(void)
{
int i, j;
for (i = 0; aaimage[i]; i++) {
for (j = 0; j < (int)strlen(aaimage[i]); j++) {
if (aaimage[i][j] == '@') {
j++;
switch(aaimage[i][j]) {
case 'k': /* black */
printf("\033[0;30m");
break;
case 'r': /* red */
printf("\033[0;31m");
break;
case 'g': /* green */
printf("\033[0;32m");
break;
case 'y': /* yellow */
printf("\033[0;33m");
break;
case 'b': /* blue */
printf("\033[0;34m");
break;
case 'm': /* magenta */
printf("\033[0;35m");
break;
case 'c': /* cyan */
printf("\033[0;36m");
break;
case 'w': /* white */
printf("\033[0;37m");
break;
case 'K': /* bright black */
printf("This will not work on some terminals, please use 'w'\n");
abort();
case 'R': /* bright red */
printf("\033[1;31m");
break;
case 'G': /* bright green */
printf("\033[1;32m");
break;
case 'Y': /* bright yellow */
printf("\033[1;33m");
break;
case 'B': /* bright blue */
printf("\033[1;34m");
break;
case 'M': /* bright magenta */
printf("\033[1;35m");
break;
case 'C': /* bright cyan */
printf("\033[1;36m");
break;
case 'W': /* bright white */
printf("\033[1;37m");
break;
}
} else
printf("%c", aaimage[i][j]);
}
printf("\n");
}
printf("\033[0;39m");
}

View File

@ -1,3 +0,0 @@
void print_aaimage(void);

View File

@ -1,6 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libam.a
libam_a_SOURCES = \
am.c

View File

@ -1,204 +0,0 @@
/* AM modulation and de-modulation
*
* (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include "../libsample/sample.h"
#include "am.h"
static int has_init = 0;
static int fast_math = 0;
static float *sin_tab = NULL, *cos_tab = NULL;
/* global init */
int am_init(int _fast_math)
{
fast_math = _fast_math;
if (fast_math) {
int i;
sin_tab = calloc(65536+16384, sizeof(*sin_tab));
if (!sin_tab) {
fprintf(stderr, "No mem!\n");
return -ENOMEM;
}
cos_tab = sin_tab + 16384;
/* generate sine and cosine */
for (i = 0; i < 65536+16384; i++)
sin_tab[i] = sin(2.0 * M_PI * (double)i / 65536.0);
}
has_init = 1;
return 0;
}
/* global exit */
void am_exit(void)
{
if (sin_tab) {
free(sin_tab);
sin_tab = cos_tab = NULL;
}
has_init = 0;
}
#define CARRIER_FILTER 30.0
/* Amplitude modulation in SDR:
* Just use the base band (audio signal) as real value, and 0.0 as imaginary
* value. The you have two side bands. Be sure to have a DC level, so you
* have a carrier.
*/
int am_mod_init(am_mod_t *mod, double samplerate, double offset, double gain, double bias)
{
memset(mod, 0, sizeof(*mod));
mod->gain = gain;
mod->bias = bias;
if (fast_math)
mod->rot = 65536.0 * offset / samplerate;
else
mod->rot = 2.0 * M_PI * offset / samplerate;
return 0;
}
void am_mod_exit(am_mod_t __attribute__((unused)) *mod)
{
}
void am_modulate_complex(am_mod_t *mod, sample_t *amplitude, uint8_t *power, int num, float *baseband)
{
int s;
double vector;
double rot = mod->rot;
double phase = mod->phase;
double gain = mod->gain;
double bias = mod->bias;
for (s = 0; s < num; s++) {
if (*power++)
vector = *amplitude++ * gain + bias;
else
vector = 0.0;
if (fast_math) {
*baseband++ += cos_tab[(uint16_t)phase] * vector;
*baseband++ += sin_tab[(uint16_t)phase] * vector;
phase += rot;
if (phase < 0.0)
phase += 65536.0;
else if (phase >= 65536.0)
phase -= 65536.0;
} else {
*baseband++ += cos(phase) * vector;
*baseband++ += sin(phase) * vector;
phase += rot;
if (phase < 0.0)
phase += 2.0 * M_PI;
else if (phase >= 2.0 * M_PI)
phase -= 2.0 * M_PI;
}
}
mod->phase = phase;
}
/* init AM demodulator */
int am_demod_init(am_demod_t *demod, double samplerate, double offset, double bandwidth, double gain)
{
memset(demod, 0, sizeof(*demod));
demod->gain = gain;
if (fast_math)
demod->rot = 65536.0 * -offset / samplerate;
else
demod->rot = 2 * M_PI * -offset / samplerate;
/* use fourth order (2 iter) filter, since it is as fast as second order (1 iter) filter */
iir_lowpass_init(&demod->lp[0], bandwidth, samplerate, 2);
iir_lowpass_init(&demod->lp[1], bandwidth, samplerate, 2);
/* filter carrier */
iir_lowpass_init(&demod->lp[2], CARRIER_FILTER, samplerate, 1);
return 0;
}
void am_demod_exit(am_demod_t __attribute__((unused)) *demod)
{
}
/* do amplitude demodulation of baseband and write them to samples */
void am_demodulate_complex(am_demod_t *demod, sample_t *amplitude, int length, float *baseband, sample_t *I, sample_t *Q, sample_t *carrier)
{
int s, ss;
double rot = demod->rot;
double phase = demod->phase;
double gain = demod->gain;
double i, q;
double _sin, _cos;
/* rotate spectrum */
for (s = 0, ss = 0; s < length; s++) {
i = baseband[ss++];
q = baseband[ss++];
phase += rot;
if (fast_math) {
if (phase < 0.0)
phase += 65536.0;
else if (phase >= 65536.0)
phase -= 65536.0;
_sin = sin_tab[(uint16_t)phase];
_cos = cos_tab[(uint16_t)phase];
} else {
if (phase < 0.0)
phase += 2.0 * M_PI;
else if (phase >= 2.0 * M_PI)
phase -= 2.0 * M_PI;
_sin = sin(phase);
_cos = cos(phase);
}
I[s] = i * _cos - q * _sin;
Q[s] = i * _sin + q * _cos;
}
demod->phase = phase;
/* filter bandwidth */
iir_process(&demod->lp[0], I, length);
iir_process(&demod->lp[1], Q, length);
/* demod */
for (s = 0; s < length; s++)
amplitude[s] = carrier[s] = sqrt(I[s] * I[s] + Q[s] * Q[s]);
/* filter carrier */
iir_process(&demod->lp[2], carrier, length);
/* normalize */
for (s = 0; s < length; s++)
amplitude[s] = (amplitude[s] - carrier[s]) / carrier[s] * gain;
}

View File

@ -1,28 +0,0 @@
#include "../libfilter/iir_filter.h"
int am_init(int fast_math);
void am_exit(void);
typedef struct am_mod {
double rot; /* angle to rotate vector per sample */
double phase; /* current phase */
double gain; /* gain to be multiplied to amplitude */
double bias; /* DC offset to add (carrier amplitude) */
} am_mod_t;
int am_mod_init(am_mod_t *mod, double samplerate, double offset, double gain, double bias);
void am_mod_exit(am_mod_t *mod);
void am_modulate_complex(am_mod_t *mod, sample_t *amplitude, uint8_t *power, int num, float *baseband);
typedef struct am_demod {
double rot; /* angle to rotate vector per sample */
double phase; /* current rotation phase (used to shift) */
iir_filter_t lp[3]; /* filters received IQ signal/carrier */
double gain; /* gain to be expected from amplitude */
double bias; /* DC offset to be expected (carrier amplitude) */
} am_demod_t;
int am_demod_init(am_demod_t *demod, double samplerate, double offset, double gain, double bias);
void am_demod_exit(am_demod_t *demod);
void am_demodulate_complex(am_demod_t *demod, sample_t *amplitude, int length, float *baseband, sample_t *I, sample_t *Q, sample_t *carrier);

View File

@ -1,6 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libclipper.a
libclipper_a_SOURCES = \
clipper.c

View File

@ -1,81 +0,0 @@
/* Clipper implementation, based on code by Jonathan Olds
*
* (C) 2017 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include "../libsample/sample.h"
#include "clipper.h"
static double clipper_lut[6000];
static double clipper_point = NAN;
void clipper_init(double point)
{
double a;
int i;
if (point > 0.99)
point = 0.99;
if (point < 0.01)
point = 0.01;
clipper_point = point;
a = M_PI / (2.0 * (1.0 - clipper_point));
for (i = 0; i < 6000; i++)
clipper_lut[i] = clipper_point + atan(a * i / 1000.0) / a;
}
void clipper_process(sample_t *samples, int length)
{
int i;
double val, inv, shiftmultval;
int n, q;
if (isnan(clipper_point)) {
fprintf(stderr, "Clipper not initialized, aborting!\n");
abort();
}
for (i = 0; i < length; i++) {
val = samples[i];
if (val < 0) {
inv = -1.0;
val = -val;
} else
inv = 1.0;
shiftmultval = (val - clipper_point) * 1000.0;
/* no clipping up to clipping point */
if (shiftmultval <= 0.0)
continue;
n = (int)shiftmultval;
q = n + 1;
if (q >= 6000) {
samples[i] = inv;
continue;
}
/* get clipped value from lut, interpolate between table entries */
val = clipper_lut[n] + (shiftmultval - (double)n) * (clipper_lut[q] - clipper_lut[n]);
samples[i] = val * inv;
}
}

View File

@ -1,4 +0,0 @@
void clipper_init(double point);
void clipper_process(sample_t *samples, int length);

View File

@ -1,6 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libcompandor.a
libcompandor_a_SOURCES = \
compandor.c

View File

@ -1,141 +0,0 @@
/* Compandor to use various networks like C-Netz / NMT / AMPS / TACS
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include "../libsample/sample.h"
#include "compandor.h"
//#define db2level(db) pow(10, (double)db / 20.0)
/* factor is the gain (raise and fall) after given attack/recovery time */
#define COMPRESS_ATTACK_FACTOR 1.83 /* about 1.5 after 12 dB step up */
#define COMPRESS_RECOVERY_FACTOR 0.44 /* about 0.75 after 12 dB step down */
#define EXPAND_ATTACK_FACTOR 1.145 /* about 0.57 after 6 dB step up */
#define EXPAND_RECOVERY_FACTOR 0.753 /* about 1.51 after 6 dB step down */
/* Minimum level value to keep state (-60 dB) */
#define ENVELOPE_MIN 0.001
/* Maximum level, to prevent sqrt_tab to overflow */
#define ENVELOPE_MAX 9.990
static double sqrt_tab[10000];
/*
* Init compandor according to ITU-T G.162 specification
*
* Hopefully this is correct
*
*/
void init_compandor(compandor_t *state, double samplerate, double attack_ms, double recovery_ms)
{
int i;
memset(state, 0, sizeof(*state));
state->c.peak = 1.0;
state->c.envelope = 1.0;
state->e.peak = 1.0;
state->e.envelope = 1.0;
state->c.step_up = pow(COMPRESS_ATTACK_FACTOR, 1000.0 / attack_ms / samplerate);
state->c.step_down = pow(COMPRESS_RECOVERY_FACTOR, 1000.0 / recovery_ms / samplerate);
state->e.step_up = pow(EXPAND_ATTACK_FACTOR, 1000.0 / attack_ms / samplerate);
state->e.step_down = pow(EXPAND_RECOVERY_FACTOR, 1000.0 / recovery_ms / samplerate);
// FIXME: make global, not at instance
for (i = 0; i < 10000; i++)
sqrt_tab[i] = sqrt(i * 0.001);
}
void compress_audio(compandor_t *state, sample_t *samples, int num)
{
double value, peak, envelope, step_up, step_down;
int i;
step_up = state->c.step_up;
step_down = state->c.step_down;
peak = state->c.peak;
envelope = state->c.envelope;
// printf("envelope=%.4f\n", envelope);
for (i = 0; i < num; i++) {
value = *samples;
/* 'peak' is the level that raises directly with the signal
* level, but falls with specified recovery rate. */
if (fabs(value) > peak)
peak = fabs(value);
else
peak *= step_down;
/* 'evelope' is the level that raises with the specified attack
* rate to 'peak', but falls with specified recovery rate. */
if (peak > envelope)
envelope *= step_up;
else
envelope = peak;
if (envelope < ENVELOPE_MIN)
envelope = ENVELOPE_MIN;
if (envelope > ENVELOPE_MAX)
envelope = ENVELOPE_MAX;
*samples++ = value / sqrt_tab[(int)(envelope / 0.001)];
//if (i > 47000.0 && i < 48144)
//printf("time=%.4f envelope=%.4fdb, value=%.4f\n", (double)i/48000.0, 20*log10(envelope), value);
}
//exit(0);
state->c.envelope = envelope;
state->c.peak = peak;
}
void expand_audio(compandor_t *state, sample_t *samples, int num)
{
double value, peak, envelope, step_up, step_down;
int i;
step_up = state->e.step_up;
step_down = state->e.step_down;
peak = state->e.peak;
envelope = state->e.envelope;
for (i = 0; i < num; i++) {
value = *samples;
/* for comments: see compress_audio() */
if (fabs(value) > peak)
peak = fabs(value);
else
peak *= step_down;
if (peak > envelope)
envelope *= step_up;
else
envelope = peak;
if (envelope < ENVELOPE_MIN)
envelope = ENVELOPE_MIN;
*samples++ = value * envelope;
}
state->e.envelope = envelope;
state->e.peak = peak;
}

View File

@ -1,20 +0,0 @@
typedef struct compandor {
struct {
double step_up;
double step_down;
double peak;
double envelope;
} c;
struct {
double step_up;
double step_down;
double peak;
double envelope;
} e;
} compandor_t;
void init_compandor(compandor_t *state, double samplerate, double attack_ms, double recovery_ms);
void compress_audio(compandor_t *state, sample_t *samples, int num);
void expand_audio(compandor_t *state, sample_t *samples, int num);

View File

@ -1,7 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libdebug.a
libdebug_a_SOURCES = \
debug.c

View File

@ -1,325 +0,0 @@
/* Simple debug functions for level and category filtering
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <math.h>
#include <time.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include "debug.h"
const char *debug_level[] = {
"debug ",
"info ",
"notice ",
"error ",
NULL,
};
struct debug_cat {
const char *name;
const char *color;
} debug_cat[] = {
{ "options", "\033[0;33m" },
{ "sender", "\033[1;33m" },
{ "sound", "\033[0;35m" },
{ "dsp", "\033[0;31m" },
{ "anetz", "\033[1;34m" },
{ "bnetz", "\033[1;34m" },
{ "cnetz", "\033[1;34m" },
{ "nmt", "\033[1;34m" },
{ "amps", "\033[1;34m" },
{ "r2000", "\033[1;34m" },
{ "imts", "\033[1;34m" },
{ "mpt1327", "\033[1;34m" },
{ "jollycom", "\033[1;34m" },
{ "eurosignal", "\033[1;34m" },
{ "pocsag", "\033[1;34m" },
{ "5-ton-folge", "\033[1;34m" },
{ "frame", "\033[0;36m" },
{ "call", "\033[0;37m" },
{ "cc", "\033[1;32m" },
{ "database", "\033[0;33m" },
{ "transaction", "\033[0;32m" },
{ "dms", "\033[0;33m" },
{ "sms", "\033[1;37m" },
{ "sdr", "\033[1;31m" },
{ "uhd", "\033[1;35m" },
{ "soapy", "\033[1;35m" },
{ "wave", "\033[1;33m" },
{ "radio", "\033[1;34m" },
{ "am791x", "\033[0;31m" },
{ "uart", "\033[0;32m" },
{ "device", "\033[0;33m" },
{ "datenklo", "\033[1;34m" },
{ "zeit", "\033[1;34m" },
{ "sim layer 1", "\033[0;31m" },
{ "sim layer 2", "\033[0;33m" },
{ "sim ICL layer", "\033[0;36m" },
{ "sim layer 7", "\033[0;37m" },
{ "mtp layer 2", "\033[1;33m" },
{ "mtp layer 3", "\033[1;36m" },
{ "MuP", "\033[1;37m" },
{ "router", "\033[1;35m" },
{ "stderr", "\033[1;37m" },
{ "ss5", "\033[1;34m" },
{ "isdn", "\033[1;35m" },
{ "misdn", "\033[0;34m" },
{ "dss1", "\033[1;34m" },
{ "sip", "\033[1;35m" },
{ "telephone", "\033[1;34m" },
{ NULL, NULL }
};
int debuglevel = DEBUG_INFO;
int debug_date = 0;
uint64_t debug_mask = ~0;
extern int num_kanal;
void (*clear_console_text)(void) = NULL;
void (*print_console_text)(void) = NULL;
int debug_limit_scroll = 0;
static int lock_initialized = 0;
static pthread_mutex_t debug_mutex;
void lock_debug(void)
{
int rc;
if (!lock_initialized) {
rc = pthread_mutex_init(&debug_mutex, NULL);
if (rc == 0)
lock_initialized = 1;
}
if (lock_initialized)
pthread_mutex_lock(&debug_mutex);
}
void unlock_debug(void)
{
if (lock_initialized)
pthread_mutex_unlock(&debug_mutex);
}
void get_win_size(int *w, int *h)
{
struct winsize win;
int rc;
rc = ioctl(0, TIOCGWINSZ, &win);
if (rc) {
*w = 80;
*h = 25;
return;
}
*h = win.ws_row;
*w = win.ws_col;
}
void _printdebug(const char *file, const char __attribute__((unused)) *function, int line, int cat, int level, const char *kanal, const char *fmt, ...)
{
char buffer[4096], *b = buffer;
int s = sizeof(buffer) - 1;
const char *p;
va_list args;
int w, h;
if (debuglevel > level)
return;
if (!(debug_mask & ((uint64_t)1 << cat)))
return;
lock_debug();
buffer[sizeof(buffer) - 1] = '\0';
/* if kanal is used, prefix the channel number */
if (num_kanal > 1 && kanal) {
sprintf(buffer, "(chan %s) ", kanal);
b = strchr(buffer, '\0');
s -= strlen(buffer);
}
va_start(args, fmt);
vsnprintf(b, s, fmt, args);
va_end(args);
while ((p = strchr(file, '/')))
file = p + 1;
if (clear_console_text)
clear_console_text();
if (debug_limit_scroll) {
get_win_size(&w, &h);
printf("\0337\033[%d;%dr\0338", debug_limit_scroll + 1, h);
}
if (debug_date) {
struct timeval tv;
struct tm *tm;
gettimeofday(&tv, NULL);
tm = localtime(&tv.tv_sec);
printf("%04d-%02d-%02d %02d:%02d:%02d.%03d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 10000.0));
}
printf("%s%s:%4d %s: %s\033[0;39m", debug_cat[cat].color, file, line, debug_level[level], buffer);
if (debug_limit_scroll)
printf("\0337\033[%d;%dr\0338", 1, h);
if (print_console_text)
print_console_text();
fflush(stdout);
unlock_debug();
}
const char *debug_amplitude(double level)
{
static char text[42];
strcpy(text, " : ");
if (level > 1.0)
level = 1.0;
if (level < -1.0)
level = -1.0;
text[20 + (int)(level * 20)] = '*';
return text;
}
#define level2db(level) (20 * log10(level))
const char *debug_db(double level_db)
{
static char text[128];
int l;
strcpy(text, ": . : . : . : . : . : . : . : . | . : . : . : . : . : . : . : . :");
if (level_db <= 0.0)
return text;
l = (int)round(level2db(level_db));
if (l > 48)
return text;
if (l < -48)
return text;
text[l + 48] = '*';
return text;
}
void debug_print_help(void)
{
printf(" -v --verbose <level> | <level>,<category>[,<category>[,...]] | list\n");
printf(" Use 'list' to get a list of all levels and categories\n");
printf(" Verbose level: digit of debug level (default = '%d')\n", debuglevel);
printf(" Verbose level+category: level digit followed by one or more categories\n");
printf(" -> If no category is specified, all categories are selected\n");
printf(" -v --verbose date\n");
printf(" Show date with debug output\n");
}
void debug_list_cat(void)
{
int i;
printf("Give number of debug level:\n");
for (i = 0; debug_level[i]; i++)
printf(" %d = %s\n", i, debug_level[i]);
printf("\n");
printf("Give name(s) of debug category:\n");
for (i = 0; debug_cat[i].name; i++)
printf(" %s%s\033[0;39m\n", debug_cat[i].color, debug_cat[i].name);
printf("\n");
}
int parse_debug_opt(const char *optarg)
{
int i, max_level = 0;
char *dup, *dstring, *p;
if (!strcasecmp(optarg, "date")) {
debug_date = 1;
return 0;
}
for (i = 0; debug_level[i]; i++)
max_level = i;
dup = dstring = strdup(optarg);
p = strsep(&dstring, ",");
for (i = 0; i < p[i]; i++) {
if (p[i] < '0' || p[i] > '9') {
fprintf(stderr, "Only digits are allowed for debug level!\n");
free(dup);
return -EINVAL;
}
}
debuglevel = atoi(p);
if (debuglevel > max_level) {
fprintf(stderr, "Debug level too high, use 'list' to show available levels!\n");
free(dup);
return -EINVAL;
}
if (dstring)
debug_mask = 0;
while((p = strsep(&dstring, ","))) {
for (i = 0; debug_cat[i].name; i++) {
if (!strcasecmp(p, debug_cat[i].name))
break;
}
if (!debug_cat[i].name) {
fprintf(stderr, "Given debug category '%s' unknown, use 'list' to show available categories!\n", p);
free(dup);
return -EINVAL;
}
debug_mask |= ((uint64_t)1 << i);
}
free(dup);
return 0;
}
const char *debug_hex(const uint8_t *data, int len)
{
static char *text = NULL;
char *p;
int i;
if (text)
free(text);
p = text = calloc(1, len * 3 + 1);
for (i = 0; i < len; i++) {
sprintf(p, "%02x ", *data++);
p += 3;
}
if (text[0])
p[-1] = '\0';
return text;
}

View File

@ -1,80 +0,0 @@
#define DEBUG_DEBUG 0 /* debug info, not for normal use */
#define DEBUG_INFO 1 /* all info about process */
#define DEBUG_NOTICE 2 /* something unexpected happens */
#define DEBUG_ERROR 3 /* there is an error with this software */
#define DOPTIONS 0
#define DSENDER 1
#define DSOUND 2
#define DDSP 3
#define DANETZ 4
#define DBNETZ 5
#define DCNETZ 6
#define DNMT 7
#define DAMPS 8
#define DR2000 9
#define DIMTS 10
#define DMPT1327 11
#define DJOLLY 12
#define DEURO 13
#define DPOCSAG 14
#define DFUENF 15
#define DFRAME 16
#define DCALL 17
#define DCC 18
#define DDB 19
#define DTRANS 20
#define DDMS 21
#define DSMS 22
#define DSDR 23
#define DUHD 24
#define DSOAPY 25
#define DWAVE 26
#define DRADIO 27
#define DAM791X 28
#define DUART 29
#define DDEVICE 30
#define DDATENKLO 31
#define DZEIT 32
#define DSIM1 33
#define DSIM2 34
#define DSIMI 35
#define DSIM7 36
#define DMTP2 37
#define DMTP3 38
#define DMUP 39
#define DROUTER 40
#define DSTDERR 41
#define DSS5 42
#define DISDN 43
#define DMISDN 44
#define DDSS1 45
#define DSIP 46
#define DTEL 47
void lock_debug(void);
void unlock_debug(void);
void get_win_size(int *w, int *h);
#define PDEBUG(cat, level, fmt, arg...) _printdebug(__FILE__, __FUNCTION__, __LINE__, cat, level, NULL, fmt, ## arg)
#define PDEBUG_CHAN(cat, level, fmt, arg...) _printdebug(__FILE__, __FUNCTION__, __LINE__, cat, level, CHAN, fmt, ## arg)
void _printdebug(const char *file, const char *function, int line, int cat, int level, const char *chan_str, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 7, 8)));
const char *debug_amplitude(double level);
const char *debug_db(double level_db);
void debug_print_help(void);
void debug_list_cat(void);
int parse_debug_opt(const char *opt);
extern int debuglevel;
extern void (*clear_console_text)(void);
extern void (*print_console_text)(void);
extern int debug_limit_scroll;
const char *debug_hex(const uint8_t *data, int len);

View File

@ -1,19 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libdisplay.a
libdisplay_a_SOURCES = \
display_status.c \
display_wave.c \
display_measurements.c
if HAVE_SDR
libdisplay_a_SOURCES += \
display_iq.c \
display_spectrum.c
endif
if HAVE_SDR
AM_CPPFLAGS += -DHAVE_SDR
endif

View File

@ -1,103 +0,0 @@
#define DISPLAY_MEAS_INTERVAL 0.1 /* time (in seconds) for each measurement values interval */
#define DISPLAY_INTERVAL 0.04 /* time (in seconds) for each other interval */
#define DISPLAY_PARAM_HISTORIES 10 /* number of intervals (should result in one seconds) */
#define MAX_DISPLAY_WIDTH 1024
typedef struct display_wave {
const char *kanal;
int interval_pos;
int interval_max;
int offset;
sample_t buffer[MAX_DISPLAY_WIDTH];
} dispwav_t;
enum display_measurements_type {
DISPLAY_MEAS_LAST, /* display last value */
DISPLAY_MEAS_PEAK, /* display peak value */
DISPLAY_MEAS_PEAK2PEAK, /* display peak value of min..max range */
DISPLAY_MEAS_AVG, /* display average value */
};
enum display_measurements_bar {
DISPLAY_MEAS_LEFT, /* bar graph from left */
DISPLAY_MEAS_CENTER, /* bar graph from center */
};
typedef struct display_measurements_param {
struct display_measurements_param *next;
char name[32]; /* parameter name (e.g. 'Deviation') */
char format[32]; /* unit name (e.g. "%.2f KHz") */
enum display_measurements_type type;
enum display_measurements_bar bar;
double min; /* minimum value */
double max; /* maximum value */
double mark; /* mark (target) value */
double value; /* current value (peak, sum...) */
double value2; /* max value for min..max range */
double last; /* last valid value (used for DISPLAY_MEAS_LAST) */
int value_count; /* count number of values of one interval */
double value_history[DISPLAY_PARAM_HISTORIES]; /* history of values of last second */
double value2_history[DISPLAY_PARAM_HISTORIES]; /* stores max for min..max range */
int value_history_pos; /* next history value to write */
} dispmeasparam_t;
typedef struct display_measurements {
struct display_measurements *next;
const char *kanal;
dispmeasparam_t *param;
} dispmeas_t;
#define MAX_DISPLAY_IQ 1024
typedef struct display_iq {
int interval_pos;
int interval_max;
float buffer[MAX_DISPLAY_IQ * 2];
} dispiq_t;
#define MAX_DISPLAY_SPECTRUM 1024
typedef struct display_spectrum_mark {
struct display_spectrum_mark *next;
const char *kanal;
double frequency;
} dispspectrum_mark_t;
typedef struct display_spectrum {
int interval_pos;
int interval_max;
double buffer_I[MAX_DISPLAY_SPECTRUM];
double buffer_Q[MAX_DISPLAY_SPECTRUM];
dispspectrum_mark_t *mark;
} dispspectrum_t;
#define MAX_HEIGHT_STATUS 32
void display_wave_init(dispwav_t *disp, int samplerate, const char *kanal);
void display_wave_on(int on);
void display_wave(dispwav_t *disp, sample_t *samples, int length, double range);
void display_status_on(int on);
void display_status_start(void);
void display_status_channel(const char *kanal, const char *type, const char *state);
void display_status_subscriber(const char *number, const char *state);
void display_status_end(void);
void display_measurements_init(dispmeas_t *disp, int samplerate, const char *kanal);
void display_measurements_exit(dispmeas_t *disp);
void display_measurements_on(int on);
dispmeasparam_t *display_measurements_add(dispmeas_t *disp, char *name, char *format, enum display_measurements_type type, enum display_measurements_bar bar, double min, double max, double mark);
void display_measurements_update(dispmeasparam_t *param, double value, double value2);
void display_measurements(double elapsed);
void display_iq_init(int samplerate);
void display_iq_on(int on);
void display_iq(float *samples, int length);
void display_spectrum_init(int samplerate, double center_frequency);
void display_spectrum_add_mark(const char *kanal, double frequency);
void display_spectrum_exit(void);
void display_spectrum_on(int on);
void display_spectrum(float *samples, int length);

View File

@ -1,282 +0,0 @@
/* display IQ data form functions
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <pthread.h>
#include <stdlib.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "../libdisplay/display.h"
/* must be odd value! */
#define SIZE 23
static char screen[SIZE][MAX_DISPLAY_WIDTH];
static uint8_t screen_color[SIZE][MAX_DISPLAY_WIDTH];
static uint8_t screen_history[SIZE * 2][MAX_DISPLAY_WIDTH];
static int iq_on = 0;
static double db = 80;
static dispiq_t disp;
void display_iq_init(int samplerate)
{
memset(&disp, 0, sizeof(disp));
memset(&screen_history, 0, sizeof(screen_history));
disp.interval_max = (double)samplerate * DISPLAY_INTERVAL + 0.5;
/* should not happen due to low interval */
if (disp.interval_max < MAX_DISPLAY_IQ - 1)
disp.interval_max = MAX_DISPLAY_IQ - 1;
}
void display_iq_on(int on)
{
int j;
int w, h;
get_win_size(&w, &h);
if (w > MAX_DISPLAY_WIDTH - 1)
w = MAX_DISPLAY_WIDTH - 1;
if (iq_on) {
memset(&screen, ' ', sizeof(screen));
memset(&screen_history, 0, sizeof(screen_history));
lock_debug();
printf("\0337\033[H");
for (j = 0; j < SIZE; j++) {
screen[j][w] = '\0';
puts(screen[j]);
}
printf("\0338"); fflush(stdout);
unlock_debug();
}
if (on < 0) {
if (++iq_on == 3)
iq_on = 0;
} else
iq_on = on;
if (iq_on)
debug_limit_scroll = SIZE;
else
debug_limit_scroll = 0;
}
/*
* plot IQ data:
*
* theoretical example: SIZE = 3 allows 6 steps plotted as dots
*
* Line 0: :
* Line 1: :
* Line 2: :
*
* The level of -1.0 .. 1.0 is scaled to -3 and 3.
*
* The lowest of the upper 3 dots ranges from 0.0 .. <1.5.
* The upper most dot ranges from 2.5 .. <3.5.
* The highest of the lower 3 dots ranges from <0.0 .. >-1.5;
* The lower most dot ranges from -2.5 .. >-3.5.
*
* The center column ranges from -0.5 .. <0.5.
* The columns about the center from -1.5 .. <1.5.
*/
void display_iq(float *samples, int length)
{
int pos, max;
float *buffer;
int i, j, k;
int color = 9; /* default color */
int x_center, y_center;
double I, Q, L, l, s;
int x, y;
int v, r;
int width, h;
if (!iq_on)
return;
lock_debug();
get_win_size(&width, &h);
if (width > MAX_DISPLAY_WIDTH - 1)
width = MAX_DISPLAY_WIDTH - 1;
/* at what line we draw our zero-line and what character we use */
x_center = width >> 1;
y_center = (SIZE - 1) >> 1;
pos = disp.interval_pos;
max = disp.interval_max;
buffer = disp.buffer;
for (i = 0; i < length; i++) {
if (pos >= MAX_DISPLAY_IQ) {
if (++pos == max)
pos = 0;
continue;
}
buffer[pos * 2] = samples[i * 2];
buffer[pos * 2 + 1] = samples[i * 2 + 1];
pos++;
if (pos == MAX_DISPLAY_IQ) {
memset(&screen, ' ', sizeof(screen));
memset(&screen_color, 7, sizeof(screen_color));
/* render screen history to screen */
for (y = 0; y < SIZE * 2; y++) {
for (x = 0; x < width; x++) {
v = screen_history[y][x];
v -= 8;
if (v < 0)
v = 0;
screen_history[y][x] = v;
r = random() & 0x3f;
if (r >= v)
continue;
if (screen[y/2][x] == ':')
continue;
if (screen[y/2][x] == '.') {
if ((y & 1) == 0)
screen[y/2][x] = ':';
continue;
}
if (screen[y/2][x] == '\'') {
if ((y & 1))
screen[y/2][x] = ':';
continue;
}
if ((y & 1) == 0)
screen[y/2][x] = '\'';
else
screen[y/2][x] = '.';
screen_color[y/2][x] = 4;
}
}
/* plot current IQ date */
for (j = 0; j < MAX_DISPLAY_IQ; j++) {
I = buffer[j * 2];
Q = buffer[j * 2 + 1];
L = I*I + Q*Q;
if (iq_on > 1) {
/* logarithmic scale */
l = sqrt(L);
s = log10(l) * 20 + db;
if (s < 0)
s = 0;
I = (I / l) * (s / db);
Q = (Q / l) * (s / db);
}
x = x_center + (int)(I * (double)SIZE + (double)width + 0.5) - width;
if (x < 0)
continue;
if (x > width - 1)
continue;
if (Q >= 0)
y = SIZE - 1 - (int)(Q * (double)SIZE - 0.5);
else
y = SIZE - (int)(Q * (double)SIZE + 0.5);
if (y < 0)
continue;
if (y > SIZE * 2 - 1)
continue;
if (screen[y/2][x] == ':' && screen_color[y/2][x] >= 10)
goto cont;
if (screen[y/2][x] == '.' && screen_color[y/2][x] >= 10) {
if ((y & 1) == 0)
screen[y/2][x] = ':';
goto cont;
}
if (screen[y/2][x] == '\'' && screen_color[y/2][x] >= 10) {
if ((y & 1))
screen[y/2][x] = ':';
goto cont;
}
if ((y & 1) == 0)
screen[y/2][x] = '\'';
else
screen[y/2][x] = '.';
cont:
screen_history[y][x] = 255;
/* overdrive:
* red = close to -1..1 or above
* yellow = close to -0.5..0.5 or above
* Note: L is square of vector length,
* so we compare with square values.
*/
if (L > 0.9 * 0.9)
screen_color[y/2][x] = 11;
else if (L > 0.45 * 0.45 && screen_color[y/2][x] != 11)
screen_color[y/2][x] = 13;
else if (screen_color[y/2][x] < 10)
screen_color[y/2][x] = 12;
}
if (iq_on == 1)
sprintf(screen[0], "(IQ linear");
else
sprintf(screen[0], "(IQ log %.0f dB", db);
*strchr(screen[0], '\0') = ')';
printf("\0337\033[H");
for (j = 0; j < SIZE; j++) {
for (k = 0; k < width; k++) {
if ((j == y_center || k == x_center) && screen[j][k] == ' ') {
/* cross */
if (color != 4) {
color = 4;
printf("\033[0;34m");
}
if (j == y_center) {
if (k == x_center)
putchar('o');
else if (k == x_center - SIZE)
putchar('+');
else if (k == x_center + SIZE)
putchar('+');
else
putchar('-');
} else {
if (j == 0 || j == SIZE - 1)
putchar('+');
else
putchar('|');
}
} else {
if (screen_color[j][k] != color) {
color = screen_color[j][k];
printf("\033[%d;3%dm", color / 10, color % 10);
}
putchar(screen[j][k]);
}
}
printf("\n");
}
/* reset color and position */
printf("\033[0;39m\0338"); fflush(stdout);
}
}
disp.interval_pos = pos;
unlock_debug();
}

View File

@ -1,360 +0,0 @@
/* display measurements functions
*
* (C) 2017 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <math.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "../libdisplay/display.h"
#define MAX_NAME_LEN 16
#define MAX_UNIT_LEN 16
static int has_init = 0;
static int measurements_on = 0;
double time_elapsed = 0.0;
static int lines_total = 0;
static char line[MAX_DISPLAY_WIDTH];
static char line_color[MAX_DISPLAY_WIDTH];
dispmeas_t *meas_head = NULL;
void display_measurements_init(dispmeas_t *disp, int __attribute__((unused)) samplerate, const char *kanal)
{
dispmeas_t **disp_p;
memset(disp, 0, sizeof(*disp));
disp->kanal = kanal;
has_init = 1;
lines_total = 0;
time_elapsed = 0.0;
disp_p = &meas_head;
while (*disp_p)
disp_p = &((*disp_p)->next);
*disp_p = disp;
}
void display_measurements_exit(dispmeas_t *disp)
{
dispmeasparam_t *param = disp->param, *temp;
while (param) {
temp = param;
param = param->next;
free(temp);
}
disp->param = NULL;
has_init = 0;
}
static int color;
static void display_line(int on, int w)
{
int j;
if (on) {
for (j = 0; j < w; j++) {
if (line_color[j] != color && line[j] != ' ') {
color = line_color[j];
printf("\033[%d;3%dm", color / 10, color % 10);
}
putchar(line[j]);
}
} else {
for (j = 0; j < w; j++)
putchar(' ');
}
putchar('\n');
lines_total++;
}
static void print_measurements(int on)
{
dispmeas_t *disp;
dispmeasparam_t *param;
int i, j;
int width, h;
char text[128];
double value = 0.0, value2 = 0.0, hold, hold2;
int bar_width, bar_left, bar_right, bar_hold, bar_mark;
get_win_size(&width, &h);
if (width > MAX_DISPLAY_WIDTH - 1)
width = MAX_DISPLAY_WIDTH - 1;
/* no display, if bar graph is less than one character */
bar_width = width - MAX_NAME_LEN - MAX_UNIT_LEN;
if (bar_width < 1)
return;
lock_debug();
lines_total = 0;
color = -1;
printf("\0337\033[H");
for (disp = meas_head; disp; disp = disp->next) {
memset(line, ' ', width);
memset(line_color, 7, width);
sprintf(line, "(chan %s", disp->kanal);
*strchr(line, '\0') = ')';
display_line(on, width);
for (param = disp->param; param; param = param->next) {
memset(line, ' ', width);
memset(line_color, 7, width);
memset(line_color, 3, MAX_NAME_LEN); /* yellow */
switch (param->type) {
case DISPLAY_MEAS_LAST:
value = param->value;
param->value = -NAN;
break;
case DISPLAY_MEAS_PEAK:
/* peak value */
value = param->value;
param->value = -NAN;
param->value_count = 0;
break;
case DISPLAY_MEAS_PEAK2PEAK:
/* peak to peak value */
value = param->value;
value2 = param->value2;
param->value = -NAN;
param->value2 = -NAN;
param->value_count = 0;
break;
case DISPLAY_MEAS_AVG:
/* average value */
if (param->value_count)
value = param->value / (double)param->value_count;
else
value = -NAN;
param->value = 0.0;
param->value_count = 0;
break;
}
/* add current value to history */
param->value_history[param->value_history_pos] = value;
param->value2_history[param->value_history_pos] = value2;
param->value_history_pos = param->value_history_pos % DISPLAY_PARAM_HISTORIES;
/* calculate hold values */
hold = -NAN;
hold2 = -NAN;
switch (param->type) {
case DISPLAY_MEAS_LAST:
/* if we have valid value, we update 'last' */
if (!isnan(value)) {
param->last = value;
hold = value;
} else
hold = param->last;
break;
case DISPLAY_MEAS_PEAK:
for (i = 0; i < DISPLAY_PARAM_HISTORIES; i++) {
if (isnan(param->value_history[i]))
continue;
if (isnan(hold) || param->value_history[i] > hold)
hold = param->value_history[i];
}
break;
case DISPLAY_MEAS_PEAK2PEAK:
for (i = 0; i < DISPLAY_PARAM_HISTORIES; i++) {
if (isnan(param->value_history[i]))
continue;
if (isnan(hold) || param->value_history[i] < hold)
hold = param->value_history[i];
if (isnan(hold2) || param->value2_history[i] > hold2)
hold2 = param->value2_history[i];
}
if (!isnan(hold))
hold = hold2 - hold;
if (!isnan(value))
value = value2 - value;
break;
case DISPLAY_MEAS_AVG:
for (i = 0, j = 0; i < DISPLAY_PARAM_HISTORIES; i++) {
if (isnan(param->value_history[i]))
continue;
if (j == 0)
hold = 0.0;
hold += param->value_history[i];
j++;
}
if (j)
hold /= j;
break;
}
/* "Deviation ::::::::::............ 4.5 KHz" */
memcpy(line, param->name, (strlen(param->name) < MAX_NAME_LEN) ? strlen(param->name) : MAX_NAME_LEN);
if (isinf(value) || isnan(value)) {
bar_left = -1;
bar_right = -1;
} else if (param->bar == DISPLAY_MEAS_CENTER) {
if (value >= 0.0) {
bar_left = (-param->min) / (param->max - param->min) * ((double)bar_width - 1.0);
bar_right = (value - param->min) / (param->max - param->min) * ((double)bar_width - 1.0);
} else {
bar_left = (value - param->min) / (param->max - param->min) * ((double)bar_width - 1.0);
bar_right = (-param->min) / (param->max - param->min) * ((double)bar_width - 1.0);
}
} else {
bar_left = -1;
bar_right = (value - param->min) / (param->max - param->min) * ((double)bar_width - 1.0);
}
if (isinf(hold) || isnan(hold))
bar_hold = -1;
else
bar_hold = (hold - param->min) / (param->max - param->min) * ((double)bar_width - 1.0);
if (isinf(param->mark))
bar_mark = -1;
else
bar_mark = (param->mark - param->min) / (param->max - param->min) * ((double)bar_width - 1.0);
for (i = 0; i < bar_width; i++) {
line[i + MAX_NAME_LEN] = ':';
if (i == bar_hold)
line_color[i + MAX_NAME_LEN] = 13;
else if (i == bar_mark)
line_color[i + MAX_NAME_LEN] = 14;
else if (i >= bar_left && i <= bar_right)
line_color[i + MAX_NAME_LEN] = 2;
else
line_color[i + MAX_NAME_LEN] = 4;
}
sprintf(text, param->format, hold);
if (isnan(hold))
memset(line_color + width - MAX_UNIT_LEN, 4, MAX_UNIT_LEN); /* blue */
else
memset(line_color + width - MAX_UNIT_LEN, 3, MAX_UNIT_LEN); /* yellow */
strncpy(line + width - MAX_UNIT_LEN + 1, text, (strlen(text) < MAX_UNIT_LEN) ? strlen(text) : MAX_UNIT_LEN);
display_line(on, width);
}
}
/* reset color and position */
printf("\033[0;39m\0338"); fflush(stdout);
debug_limit_scroll = lines_total;
unlock_debug();
}
void display_measurements_on(int on)
{
if (measurements_on)
print_measurements(0);
if (on < 0)
measurements_on = 1 - measurements_on;
else
measurements_on = on;
debug_limit_scroll = 0;
}
/* add new parameter on startup to the list of measurements */
dispmeasparam_t *display_measurements_add(dispmeas_t *disp, char *name, char *format, enum display_measurements_type type, enum display_measurements_bar bar, double min, double max, double mark)
{
dispmeasparam_t *param, **param_p = &disp->param;
int i;
if (!has_init) {
fprintf(stderr, "Not initialized prior adding measurement, please fix!\n");
abort();
}
while (*param_p)
param_p = &((*param_p)->next);
*param_p = calloc(sizeof(dispmeasparam_t), 1);
if (!*param_p)
return NULL;
param = *param_p;
strncpy(param->name, name, sizeof(param->name) - 1);
strncpy(param->format, format, sizeof(param->format) - 1);
param->type = type;
param->bar = bar;
param->min = min;
param->max = max;
param->mark = mark;
param->value = -NAN;
param->value2 = -NAN;
param->last = -NAN;
for (i = 0; i < DISPLAY_PARAM_HISTORIES; i++)
param->value_history[i] = -NAN;
param->value_count = 0;
return param;
}
void display_measurements_update(dispmeasparam_t *param, double value, double value2)
{
/* special case where we do not have an instance of the parameter */
if (!param)
return;
if (!has_init) {
fprintf(stderr, "Not initialized prior updating measurement value, please fix!\n");
abort();
}
switch (param->type) {
case DISPLAY_MEAS_LAST:
param->value = value;
break;
case DISPLAY_MEAS_PEAK:
if (isnan(param->value) || value > param->value)
param->value = value;
break;
case DISPLAY_MEAS_PEAK2PEAK:
if (param->value_count == 0 || value < param->value)
param->value = value;
if (param->value_count == 0 || value2 > param->value2)
param->value2 = value2;
param->value_count++;
break;
case DISPLAY_MEAS_AVG:
param->value += value;
param->value_count++;
break;
default:
fprintf(stderr, "Parameter '%s' has unknown type %d, please fix!\n", param->name, param->type);
abort();
}
}
void display_measurements(double elapsed)
{
if (!measurements_on)
return;
if (!has_init)
return;
/* count and check if we need to display this time */
time_elapsed += elapsed;
if (time_elapsed < DISPLAY_MEAS_INTERVAL)
return;
time_elapsed = fmod(time_elapsed, DISPLAY_MEAS_INTERVAL);
print_measurements(1);
}

View File

@ -1,414 +0,0 @@
/* display spectrum of IQ data
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "../libsample/sample.h"
#include "../libfft/fft.h"
#include "../libdebug/debug.h"
#include "../libdisplay/display.h"
#define HEIGHT 20
static int has_init = 0;
static double buffer_delay[MAX_DISPLAY_SPECTRUM];
static double buffer_hold[MAX_DISPLAY_SPECTRUM];
static char screen[HEIGHT][MAX_DISPLAY_WIDTH];
static uint8_t screen_color[HEIGHT][MAX_DISPLAY_WIDTH];
static int spectrum_on = 0;
static double db = 120;
static double center_frequency, frequency_range;
static dispspectrum_t disp;
void display_spectrum_init(int samplerate, double _center_frequency)
{
memset(&disp, 0, sizeof(disp));
disp.interval_max = (double)samplerate * DISPLAY_INTERVAL + 0.5;
/* should not happen due to low interval */
if (disp.interval_max < MAX_DISPLAY_SPECTRUM - 1)
disp.interval_max = MAX_DISPLAY_SPECTRUM - 1;
memset(buffer_delay, 0, sizeof(buffer_delay));
center_frequency = _center_frequency;
frequency_range = (double)samplerate;
has_init = 1;
}
void display_spectrum_add_mark(const char *kanal, double frequency)
{
dispspectrum_mark_t *mark, **mark_p;
if (!has_init)
return;
mark = calloc(1, sizeof(*mark));
if (!mark) {
fprintf(stderr, "no mem!");
abort();
}
mark->kanal = kanal;
mark->frequency = frequency;
mark_p = &disp.mark;
while (*mark_p)
mark_p = &((*mark_p)->next);
*mark_p = mark;
}
void display_spectrum_exit(void)
{
dispspectrum_mark_t *mark = disp.mark, *temp;
while (mark) {
temp = mark;
mark = mark->next;
free(temp);
}
disp.mark = NULL;
has_init = 0;
}
void display_spectrum_on(int on)
{
int j;
int w, h;
get_win_size(&w, &h);
if (w > MAX_DISPLAY_WIDTH - 1)
w = MAX_DISPLAY_WIDTH - 1;
if (spectrum_on) {
memset(&screen, ' ', sizeof(screen));
memset(&buffer_hold, 0, sizeof(buffer_hold));
lock_debug();
printf("\0337\033[H");
for (j = 0; j < HEIGHT; j++) {
screen[j][w] = '\0';
puts(screen[j]);
}
printf("\0338"); fflush(stdout);
unlock_debug();
}
if (on < 0) {
if (++spectrum_on == 3)
spectrum_on = 0;
} else
spectrum_on = on;
if (spectrum_on)
debug_limit_scroll = HEIGHT;
else
debug_limit_scroll = 0;
}
/*
* plot spectrum data:
*
*/
void display_spectrum(float *samples, int length)
{
dispspectrum_mark_t *mark;
char print_channel[32], print_frequency[32];
int width, h;
int pos, max;
double *buffer_I, *buffer_Q;
int color = 9; /* default color */
int i, j, k, o;
double I, Q, v;
int s, e, l, n;
if (!spectrum_on)
return;
lock_debug();
get_win_size(&width, &h);
if (width > MAX_DISPLAY_WIDTH - 1)
width = MAX_DISPLAY_WIDTH - 1;
/* calculate size of FFT */
int m, fft_size = 0, fft_taps = 0;
for (m = 0; m < 16; m++) {
if ((1 << m) > MAX_DISPLAY_SPECTRUM)
break;
if ((1 << m) <= width) {
fft_taps = m;
fft_size = 1 << m;
}
}
if (m == 16) {
fprintf(stderr, "Size of spectrum is not a power of 2, please fix!\n");
abort();
}
int hold[fft_size], delay[fft_size], current[fft_size];
pos = disp.interval_pos;
max = disp.interval_max;
buffer_I = disp.buffer_I;
buffer_Q = disp.buffer_Q;
for (i = 0; i < length; i++) {
if (pos >= fft_size) {
if (++pos == max)
pos = 0;
continue;
}
buffer_I[pos] = samples[i * 2];
buffer_Q[pos] = samples[i * 2 + 1];
pos++;
if (pos == fft_size) {
fft_process(1, fft_taps, buffer_I, buffer_Q);
k = 0;
for (j = 0; j < fft_size; j++) {
/* scale result vertically */
I = buffer_I[(j + fft_size / 2) % fft_size];
Q = buffer_Q[(j + fft_size / 2) % fft_size];
v = sqrt(I*I + Q*Q);
v = log10(v) * 20 + db;
if (v < 0)
v = 0;
v /= db;
/* delayed */
buffer_delay[j] -= DISPLAY_INTERVAL / 10.0;
if (v > buffer_delay[j])
buffer_delay[j] = v;
delay[j] = (double)(HEIGHT * 2 - 1) * (1.0 - buffer_delay[j]);
if (delay[j] < 0)
delay[j] = 0;
if (delay[j] >= (HEIGHT * 2))
delay[j] = (HEIGHT * 2) - 1;
/* hold */
if (spectrum_on == 2) {
if (v > buffer_hold[j])
buffer_hold[j] = v;
hold[j] = (double)(HEIGHT * 2 - 1) * (1.0 - buffer_hold[j]);
if (hold[j] < 0)
hold[j] = 0;
if (hold[j] >= (HEIGHT * 2))
hold[j] = (HEIGHT * 2) - 1;
}
/* current */
current[j] = (double)(HEIGHT * 2 - 1) * (1.0 - v);
if (current[j] < 0)
current[j] = 0;
if (current[j] >= (HEIGHT * 2))
current[j] = (HEIGHT * 2) - 1;
}
/* plot scaled buffer */
memset(&screen, ' ', sizeof(screen));
memset(&screen_color, 7, sizeof(screen_color)); /* all white */
sprintf(screen[0], "(spectrum log %.0f dB%s", db, (spectrum_on == 2) ? " HOLD" : "");
*strchr(screen[0], '\0') = ')';
for (j = 2; j < HEIGHT; j += 2) {
memset(screen_color[j], 4, 7); /* blue */
sprintf(screen[j], "%4.0f dB", -(double)(j+1) * db / (double)(HEIGHT - 1));
screen[j][7] = ' ';
}
o = (width - fft_size) / 2; /* offset from left border */
for (j = 0; j < fft_size; j++) {
/* show current spectrum in yellow */
s = l = n = current[j];
/* get last and next value */
if (j > 0)
l = (current[j - 1] + s) / 2;
if (j < fft_size - 1)
n = (current[j + 1] + s) / 2;
if (s > l && s > n) {
/* current value is a minimum */
e = s;
s = (l < n) ? (l + 1) : (n + 1);
} else if (s < l && s < n) {
/* current value is a maximum */
e = (l > n) ? l : n;
} else if (l < n) {
/* last value is higher, next value is lower */
s = l + 1;
e = n;
} else if (l > n) {
/* last value is lower, next value is higher */
s = n + 1;
e = l;
} else {
/* current, last and next values are equal */
e = s;
}
if (s == e) {
if ((s & 1) == 0)
screen[s >> 1][j + o] = '\'';
else
screen[s >> 1][j + o] = '.';
screen_color[s >> 1][j + o] = 13;
} else {
if ((s & 1) == 0)
screen[s >> 1][j + o] = '|';
else
screen[s >> 1][j + o] = '.';
screen_color[s >> 1][j + o] = 13;
if ((e & 1) == 0)
screen[e >> 1][j + o] = '\'';
else
screen[e >> 1][j + o] = '|';
screen_color[e >> 1][j + o] = 13;
for (k = (s >> 1) + 1; k < (e >> 1); k++) {
screen[k][j + o] = '|';
screen_color[k][j + o] = 13;
}
}
/* show delayed spectrum in blue */
e = s;
s = delay[j];
if ((s >> 1) < (e >> 1)) {
if ((s & 1) == 0)
screen[s >> 1][j + o] = '|';
else
screen[s >> 1][j + o] = '.';
screen_color[s >> 1][j + o] = 4;
for (k = (s >> 1) + 1; k < (e >> 1); k++) {
screen[k][j + o] = '|';
screen_color[k][j + o] = 4;
}
}
if (spectrum_on == 2) {
/* show hold spectrum in white */
s = l = n = hold[j];
/* get last and next value */
if (j > 0)
l = (hold[j - 1] + s) / 2;
if (j < fft_size - 1)
n = (hold[j + 1] + s) / 2;
if (s > l && s > n) {
/* hold value is a minimum */
e = s;
s = (l < n) ? (l + 1) : (n + 1);
} else if (s < l && s < n) {
/* hold value is a maximum */
e = (l > n) ? l : n;
} else if (l < n) {
/* last value is higher, next value is lower */
s = l + 1;
e = n;
} else if (l > n) {
/* last value is lower, next value is higher */
s = n + 1;
e = l;
} else {
/* hold, last and next values are equal */
e = s;
}
if (s == e) {
if ((s & 1) == 0)
screen[s >> 1][j + o] = '\'';
else
screen[s >> 1][j + o] = '.';
screen_color[s >> 1][j + o] = 17;
} else {
if ((s & 1) == 0)
screen[s >> 1][j + o] = '|';
else
screen[s >> 1][j + o] = '.';
screen_color[s >> 1][j + o] = 17;
if ((e & 1) == 0)
screen[e >> 1][j + o] = '\'';
else
screen[e >> 1][j + o] = '|';
screen_color[e >> 1][j + o] = 17;
for (k = (s >> 1) + 1; k < (e >> 1); k++) {
screen[k][j + o] = '|';
screen_color[k][j + o] = 17;
}
}
}
}
/* add channel positions in spectrum */
for (mark = disp.mark; mark; mark = mark->next) {
j = (int)((mark->frequency - center_frequency) / frequency_range * (double) fft_size + width / 2 + 0.5);
if (j < 0 || j >= width) /* check out-of-range, should not happen */
continue;
for (k = 0; k < HEIGHT; k++) {
/* skip yellow/white graph */
if (screen_color[k][j] == 13 || screen_color[k][j] == 17)
continue;
screen[k][j] = ':';
screen_color[k][j] = 12;
}
sprintf(print_channel, "Ch%s", mark->kanal);
for (o = 0; o < (int)strlen(print_channel); o++) {
s = j - strlen(print_channel) + o;
if (s >= 0 && s < width) {
screen[HEIGHT - 1][s] = print_channel[o];
screen_color[HEIGHT - 1][s] = 7;
}
}
if (fmod(mark->frequency, 1000.0))
sprintf(print_frequency, "%.4f", mark->frequency / 1e6);
else
sprintf(print_frequency, "%.3f", mark->frequency / 1e6);
for (o = 0; o < (int)strlen(print_frequency); o++) {
s = j + o + 1;
if (s >= 0 && s < width) {
screen[HEIGHT - 1][s] = print_frequency[o];
screen_color[HEIGHT - 1][s] = 7;
}
}
}
/* add center (DC line) to spectrum */
j = width / 2 + 0.5;
if (j < 1 || j >= width-1) /* check out-of-range, should not happen */
continue;
for (k = 0; k < HEIGHT; k++) {
/* skip green/yellow/white graph */
if (screen_color[k][j] == 13 || screen_color[k][j] == 17 || screen_color[k][j] == 12)
continue;
screen[k][j] = '.';
screen_color[k][j] = 7;
}
screen[0][j-1] = 'D';
screen[0][j+1] = 'C';
screen_color[0][j-1] = 7;
screen_color[0][j+1] = 7;
/* display buffer */
printf("\0337\033[H");
for (j = 0; j < HEIGHT; j++) {
for (k = 0; k < width; k++) {
if (screen_color[j][k] != color) {
color = screen_color[j][k];
printf("\033[%d;3%dm", color / 10, color % 10);
}
putchar(screen[j][k]);
}
printf("\n");
}
/* reset color and position */
printf("\033[0;39m\0338"); fflush(stdout);
}
}
disp.interval_pos = pos;
unlock_debug();
}

View File

@ -1,145 +0,0 @@
/* display status functions
*
* (C) 2017 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "../libdisplay/display.h"
static int status_on = 0;
static int line_count = 0;
static int lines_total = 0;
static char screen[MAX_HEIGHT_STATUS][MAX_DISPLAY_WIDTH];
static void print_status(int on)
{
int i, j;
int w, h;
get_win_size(&w, &h);
if (w > MAX_DISPLAY_WIDTH - 1)
w = MAX_DISPLAY_WIDTH - 1;
if (w > MAX_DISPLAY_WIDTH)
w = MAX_DISPLAY_WIDTH;
h--;
if (h > lines_total)
h = lines_total;
lock_debug();
printf("\0337\033[H\033[1;37m");
for (i = 0; i < h; i++) {
j = 0;
if (on) {
for (j = 0; j < w; j++)
putchar(screen[i][j]);
} else {
for (j = 0; j < w; j++)
putchar(' ');
}
putchar('\n');
}
printf("\0338"); fflush(stdout);
unlock_debug();
}
void display_status_on(int on)
{
if (status_on)
print_status(0);
if (on < 0)
status_on = 1 - status_on;
else
status_on = on;
if (status_on)
print_status(1);
if (status_on)
debug_limit_scroll = lines_total;
else
debug_limit_scroll = 0;
}
/* start status display */
void display_status_start(void)
{
memset(screen, ' ', sizeof(screen));
memset(screen[0], '-', sizeof(screen[0]));
memcpy(screen[0] + 4, "Channel Status", 14);
line_count = 1;
}
void display_status_channel(const char *kanal, const char *type, const char *state)
{
char line[MAX_DISPLAY_WIDTH];
/* add empty line after previous channel+subscriber */
if (line_count > 1 && line_count < MAX_HEIGHT_STATUS)
line_count++;
if (line_count == MAX_HEIGHT_STATUS)
return;
if (type)
snprintf(line, sizeof(line), "Channel: %s Type: %s State: %s", kanal, type, state);
else
snprintf(line, sizeof(line), "Channel: %s State: %s", kanal, state);
line[sizeof(line) - 1] = '\0';
memcpy(screen[line_count++], line, strlen(line));
}
void display_status_subscriber(const char *number, const char *state)
{
char line[MAX_DISPLAY_WIDTH];
if (line_count == MAX_HEIGHT_STATUS)
return;
if (state)
snprintf(line, sizeof(line), " Subscriber: %s State: %s", number, state);
else
snprintf(line, sizeof(line), " Subscriber: %s", number);
line[sizeof(line) - 1] = '\0';
memcpy(screen[line_count++], line, strlen(line));
}
void display_status_end(void)
{
if (line_count < MAX_HEIGHT_STATUS) {
memset(screen[line_count], '-', sizeof(screen[line_count]));
line_count++;
}
/* if last total lines exceed current line count, keep it, so removed lines are overwritten with spaces */
if (line_count > lines_total)
lines_total = line_count;
if (status_on)
print_status(1);
/* set new total lines */
lines_total = line_count;
if (status_on)
debug_limit_scroll = lines_total;
}

View File

@ -1,252 +0,0 @@
/* display wave form functions
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <pthread.h>
#include <math.h>
#include <sys/ioctl.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "../libdisplay/display.h"
#define HEIGHT 11
static int num_sender = 0;
static char screen[HEIGHT][MAX_DISPLAY_WIDTH];
static int wave_on = 0;
void display_wave_init(dispwav_t *disp, int samplerate, const char *kanal)
{
memset(disp, 0, sizeof(*disp));
disp->offset = (num_sender++) * HEIGHT;
disp->interval_max = (double)samplerate * DISPLAY_INTERVAL + 0.5;
disp->kanal = kanal;
}
void display_wave_on(int on)
{
int i, j;
int w, h;
get_win_size(&w, &h);
if (w > MAX_DISPLAY_WIDTH - 1)
w = MAX_DISPLAY_WIDTH - 1;
if (wave_on) {
memset(&screen, ' ', sizeof(screen));
lock_debug();
printf("\0337\033[H");
for (i = 0; i < num_sender; i++) {
for (j = 0; j < HEIGHT; j++) {
screen[j][w] = '\0';
puts(screen[j]);
}
}
printf("\0338"); fflush(stdout);
unlock_debug();
}
if (on < 0)
wave_on = 1 - wave_on;
else
wave_on = on;
if (wave_on)
debug_limit_scroll = HEIGHT * num_sender;
else
debug_limit_scroll = 0;
}
/*
* draw wave form:
*
* theoretical example: HEIGHT = 3 allows 5 steps
*
* Line 0: '.
* Line 1: '.
* Line 2: '
*
* HEIGHT is odd, so the center line's char is ''' (otherwise '.')
* (HEIGHT - 1) / 2 = 1, so the center line is drawn in line 1
*
* y is in range of 0..4, so these are 5 steps, where 2 is the
* center line. this is calculated by (HEIGHT * 2 - 1)
*/
void display_wave(dispwav_t *disp, sample_t *samples, int length, double range)
{
int pos, max;
sample_t *buffer;
int i, j, k, s, e;
double last, current, next;
int color = 9; /* default color */
int center_line;
char center_char;
int width, h;
if (!wave_on)
return;
lock_debug();
get_win_size(&width, &h);
if (width > MAX_DISPLAY_WIDTH - 1)
width = MAX_DISPLAY_WIDTH - 1;
/* at what line we draw our zero-line and what character we use */
center_line = (HEIGHT - 1) >> 1;
center_char = (HEIGHT & 1) ? '\'' : '.';
pos = disp->interval_pos;
max = disp->interval_max;
buffer = disp->buffer;
for (i = 0; i < length; i++) {
if (pos >= width + 2) {
if (++pos == max)
pos = 0;
continue;
}
buffer[pos++] = samples[i];
if (pos == width + 2) {
memset(&screen, ' ', sizeof(screen));
for (j = 0; j < width; j++) {
/* Input value is scaled to range -1 .. 1 and then subtracted from 1,
* so the result ranges from 0 .. 2.
* HEIGHT-1 is multiplied with the range, so a HEIGHT of 3 would allow
* 0..4 (5 steps) and a HEIGHT of 11 would allow 0..20 (21 steps).
* We always use odd number of steps, so there will be a center between
* values.
*/
last = (1.0 - buffer[j] / range) * (double)(HEIGHT - 1);
current = (1.0 - buffer[j + 1] / range) * (double)(HEIGHT - 1);
next = (1.0 - buffer[j + 2] / range) * (double)(HEIGHT - 1);
/* calculate start and end for vertical line
* if the current value is a peak (above or below last AND next point),
* round this peak point to become one end of the vertical line.
* the other end is rounded up or down, so the end of the line will
* not overlap with the ends of the surrounding lines.
*/
if (last > current) {
if (next > current) {
/* current point is a peak up */
s = round(current);
/* use lowest neighbor point and end is half way */
if (last > next)
e = floor((last + current) / 2.0);
else
e = floor((next + current) / 2.0);
/* end point must not be above start point */
if (e < s)
e = s;
} else {
/* current point is a transition upwards */
s = ceil((next + current) / 2.0);
e = floor((last + current) / 2.0);
/* end point must not be above start point */
if (e < s)
s = e = round(current);
}
} else {
if (next <= current) {
/* current point is a peak down */
e = round(current);
/* use heighes neighbor point and start is half way */
if (last <= next)
s = ceil((last + current) / 2.0);
else
s = ceil((next + current) / 2.0);
/* start point must not be below end point */
if (s > e)
s = e;
} else {
/* current point is a transition downwards */
s = ceil((last + current) / 2.0);
e = floor((next + current) / 2.0);
/* start point must not be below end point */
if (s > e)
s = e = round(current);
}
}
/* only draw line, if it is in range */
if (e >= 0 && s < HEIGHT * 2 - 1) {
/* clip */
if (s < 0)
s = 0;
if (e >= HEIGHT * 2 - 1)
e = HEIGHT * 2 - 1;
/* plot start and end point */
if ((s & 1))
screen[s >> 1][j] = '.';
else if (e != s)
screen[s >> 1][j] = '|';
if (!(e & 1))
screen[e >> 1][j] = '\'';
else if (e != s)
screen[e >> 1][j] = '|';
/* plot line between start and end point */
for (k = (s >> 1) + 1; k < (e >> 1); k++)
screen[k][j] = '|';
}
}
sprintf(screen[0], "(chan %s", disp->kanal);
*strchr(screen[0], '\0') = ')';
printf("\0337\033[H");
for (j = 0; j < disp->offset; j++)
puts("");
for (j = 0; j < HEIGHT; j++) {
for (k = 0; k < width; k++) {
if (j == center_line && screen[j][k] == ' ') {
/* blue 0-line */
if (color != 4) {
color = 4;
printf("\033[0;34m");
}
putchar(center_char);
} else if (screen[j][k] == '\'' || screen[j][k] == '.' || screen[j][k] == '|') {
/* green scope curve */
if (color != 2) {
color = 2;
printf("\033[1;32m");
}
putchar(screen[j][k]);
} else if (screen[j][k] != ' ') {
/* white other characters */
if (color != 7) {
color = 7;
printf("\033[1;37m");
}
putchar(screen[j][k]);
} else
putchar(screen[j][k]);
}
printf("\n");
}
/* reset color and position */
printf("\033[0;39m\0338"); fflush(stdout);
}
}
disp->interval_pos = pos;
unlock_debug();
}

View File

@ -1,7 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libdtmf.a
libdtmf_a_SOURCES = \
dtmf_encode.c \
dtmf_decode.c

View File

@ -1,259 +0,0 @@
/* DTMF coder
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include "../libsample/sample.h"
#include "dtmf_decode.h"
//#define DEBUG
#define level2db(level) (20 * log10(level))
#define db2level(db) pow(10, (double)db / 20.0)
#define DTMF_LOW_1 697.0
#define DTMF_LOW_2 770.0
#define DTMF_LOW_3 852.0
#define DTMF_LOW_4 941.0
#define DTMF_HIGH_1 1209.0
#define DTMF_HIGH_2 1336.0
#define DTMF_HIGH_3 1477.0
#define DTMF_HIGH_4 1633.0
static const char dtmf_digit[] = " 123A456B789C*0#D";
#ifdef DEBUG
const char *_debug_amplitude(double level)
{
static char text[42];
strcpy(text, " : ");
if (level > 1.0)
level = 1.0;
if (level < -1.0)
level = -1.0;
text[20 + (int)(level * 20)] = '*';
return text;
}
#endif
int dtmf_decode_init(dtmf_dec_t *dtmf, void *priv, void (*recv_digit)(void *priv, char digit, dtmf_meas_t *meas), int samplerate, double max_amplitude, double min_amplitude)
{
int rc;
memset(dtmf, 0, sizeof(*dtmf));
dtmf->priv = priv;
dtmf->recv_digit = recv_digit;
dtmf->samplerate = samplerate;
dtmf->freq_margin = 1.03; /* 1.8 .. 3.5 % */
dtmf->max_amplitude = max_amplitude;
dtmf->min_amplitude = min_amplitude;
dtmf->forward_twist = db2level(4.0);
dtmf->reverse_twist = db2level(8.0);
dtmf->time_detect = (int)(0.025 * (double)samplerate);
dtmf->time_meas = (int)(0.015 * (double)samplerate);
dtmf->time_pause = (int)(0.010 * (double)samplerate);
/* init fm demodulator */
rc = fm_demod_init(&dtmf->demod_low, (double)samplerate, (DTMF_LOW_1 + DTMF_LOW_4) / 2.0, DTMF_LOW_4 - DTMF_LOW_1);
if (rc < 0)
goto error;
rc = fm_demod_init(&dtmf->demod_high, (double)samplerate, (DTMF_HIGH_1 + DTMF_HIGH_4) / 2.0, DTMF_HIGH_4 - DTMF_HIGH_1);
if (rc < 0)
goto error;
/* use fourth order (2 iter) filter, since it is as fast as second order (1 iter) filter */
iir_lowpass_init(&dtmf->freq_lp[0], 100.0, samplerate, 2);
iir_lowpass_init(&dtmf->freq_lp[1], 100.0, samplerate, 2);
return 0;
error:
dtmf_decode_exit(dtmf);
return rc;
}
void dtmf_decode_exit(dtmf_dec_t *dtmf)
{
fm_demod_exit(&dtmf->demod_low);
fm_demod_exit(&dtmf->demod_high);
}
void dtmf_decode_filter(dtmf_dec_t *dtmf, sample_t *samples, int length, sample_t *frequency_low, sample_t *frequency_high, sample_t *amplitude_low, sample_t *amplitude_high)
{
sample_t I_low[length], Q_low[length];
sample_t I_high[length], Q_high[length];
int i;
fm_demodulate_real(&dtmf->demod_low, frequency_low, length, samples, I_low, Q_low);
fm_demodulate_real(&dtmf->demod_high, frequency_high, length, samples, I_high, Q_high);
/* peak amplitude is the length of I/Q vector
* since we filter out the unwanted modulation product, the vector is only half of length */
for (i = 0; i < length; i++) {
amplitude_low[i] = sqrt(I_low[i] * I_low[i] + Q_low[i] * Q_low[i]) * 2.0;
amplitude_high[i] = sqrt(I_high[i] * I_high[i] + Q_high[i] * Q_high[i]) * 2.0;
}
iir_process(&dtmf->freq_lp[0], frequency_low, length);
iir_process(&dtmf->freq_lp[1], frequency_high, length);
}
void dtmf_decode(dtmf_dec_t *dtmf, sample_t *samples, int length)
{
sample_t frequency_low[length], amplitude_low[length];
sample_t frequency_high[length], amplitude_high[length];
double margin, min_amplitude, max_amplitude, forward_twist, reverse_twist, f1, f2;
int time_detect, time_meas, time_pause;
int low = 0, high = 0;
char detected, digit;
int count;
int amplitude_ok, twist_ok;
int i;
margin = dtmf->freq_margin;
min_amplitude = dtmf->min_amplitude;
max_amplitude = dtmf->max_amplitude;
forward_twist = dtmf->forward_twist;
reverse_twist = dtmf->reverse_twist;
time_detect = dtmf->time_detect;
time_meas = dtmf->time_meas;
time_pause = dtmf->time_pause;
detected = dtmf->detected;
count = dtmf->count;
/* FM/AM demod */
dtmf_decode_filter(dtmf, samples, length, frequency_low, frequency_high, amplitude_low, amplitude_high);
for (i = 0; i < length; i++) {
#ifdef DEBUG
// printf("%s %.5f\n", _debug_amplitude(samples[i]/2.0), samples[i]/2.0);
#endif
/* get frequency of low frequencies, correct amplitude drop at cutoff point */
f1 = frequency_low[i] + (DTMF_LOW_1 + DTMF_LOW_4) / 2.0;
if (f1 >= DTMF_LOW_1 / margin && f1 <= DTMF_LOW_1 * margin) {
/* cutoff point */
amplitude_low[i] /= 0.7071;
low = 1;
f1 -= DTMF_LOW_1;
} else
if (f1 >= DTMF_LOW_2 / margin && f1 <= DTMF_LOW_2 * margin) {
amplitude_low[i] /= 1.0734;
low = 2;
f1 -= DTMF_LOW_2;
} else
if (f1 >= DTMF_LOW_3 / margin && f1 <= DTMF_LOW_3 * margin) {
amplitude_low[i] /= 1.0389;
low = 3;
f1 -= DTMF_LOW_3;
} else
if (f1 >= DTMF_LOW_4 / margin && f1 <= DTMF_LOW_4 * margin) {
/* cutoff point */
amplitude_low[i] /= 0.7071;
low = 4;
f1 -= DTMF_LOW_4;
} else
low = 0;
/* get frequency of high frequencies, correct amplitude drop at cutoff point */
f2 = frequency_high[i] + (DTMF_HIGH_1 + DTMF_HIGH_4) / 2.0;
if (f2 >= DTMF_HIGH_1 / margin && f2 <= DTMF_HIGH_1 * margin) {
/* cutoff point */
amplitude_high[i] /= 0.7071;
high = 1;
f2 -= DTMF_HIGH_1;
} else
if (f2 >= DTMF_HIGH_2 / margin && f2 <= DTMF_HIGH_2 * margin) {
amplitude_high[i] /= 1.0731;
high = 2;
f2 -= DTMF_HIGH_2;
} else
if (f2 >= DTMF_HIGH_3 / margin && f2 <= DTMF_HIGH_3 * margin) {
amplitude_high[i] /= 1.0372;
high = 3;
f2 -= DTMF_HIGH_3;
} else
if (f2 >= DTMF_HIGH_4 / margin && f2 <= DTMF_HIGH_4 * margin) {
/* cutoff point */
amplitude_high[i] /= 0.7071;
high = 4;
f2 -= DTMF_HIGH_4;
} else
high = 0;
digit = 0;
amplitude_ok = 0;
twist_ok = 0;
if (low && high) {
digit = dtmf_digit[low*4+high];
/* check for limits */
if (amplitude_low[i] <= max_amplitude && amplitude_low[i] >= min_amplitude && amplitude_high[i] <= max_amplitude && amplitude_high[i] >= min_amplitude) {
amplitude_ok = 1;
#ifdef DEBUG
printf("%.1f %.1f (limits %.1f .. %.1f) %.1f\n", level2db(amplitude_low[i]), level2db(amplitude_high[i]), level2db(min_amplitude), level2db(max_amplitude), level2db(amplitude_high[i] / amplitude_low[i]));
#endif
if (amplitude_high[i] / amplitude_low[i] <= forward_twist && amplitude_low[i] / amplitude_high[i] <= reverse_twist)
twist_ok = 1;
}
}
if (!detected) {
if (digit && amplitude_ok && twist_ok) {
if (count == 0) {
memset(&dtmf->meas, 0, sizeof(dtmf->meas));
}
if (count >= time_meas) {
dtmf->meas.frequency_low += f1;
dtmf->meas.frequency_high += f2;
dtmf->meas.amplitude_low += amplitude_low[i];
dtmf->meas.amplitude_high += amplitude_high[i];
dtmf->meas.count++;
}
count++;
if (count >= time_detect) {
detected = digit;
dtmf->meas.frequency_low /= dtmf->meas.count;
dtmf->meas.frequency_high /= dtmf->meas.count;
dtmf->meas.amplitude_low /= dtmf->meas.count;
dtmf->meas.amplitude_high /= dtmf->meas.count;
dtmf->meas.count = 1;
dtmf->recv_digit(dtmf->priv, digit, &dtmf->meas);
}
} else
count = 0;
} else {
if (!digit || digit != detected || !amplitude_ok || !twist_ok) {
count++;
if (count >= time_pause) {
detected = 0;
#ifdef DEBUG
printf("lost!\n");
#endif
}
} else
count = 0;
}
#ifdef DEBUG
if (digit)
printf("DTMF tone='%c' diff frequency=%.1f %.1f amplitude=%.1f %.1f dB (%s) twist=%.1f dB (%s)\n", digit, f1, f2, level2db(amplitude_low[i]), level2db(amplitude_high[i]), (amplitude_ok) ? "OK" : "nok", level2db(amplitude_high[i] / amplitude_low[i]), (twist_ok) ? "OK" : "nok");
#endif
dtmf->detected = detected;
dtmf->count = count;
}
}

View File

@ -1,35 +0,0 @@
#include "../libfm/fm.h"
typedef struct ftmf_meas {
double frequency_low;
double frequency_high;
double amplitude_low;
double amplitude_high;
int count;
} dtmf_meas_t;
typedef struct dtmf_dec {
void *priv;
void (*recv_digit)(void *priv, char digit, dtmf_meas_t *meas);
int samplerate; /* samplerate */
double freq_margin; /* +- limit of frequency deviation (percent) valid tone*/
double min_amplitude; /* minimum amplitude relative to 0 dBm */
double max_amplitude; /* maximum amplitude relative to 0 dBm */
double forward_twist; /* how much do higher frequencies are louder than lower frequencies */
double reverse_twist; /* how much do lower frequencies are louder than higher frequencies */
int time_detect;
int time_meas;
int time_pause;
fm_demod_t demod_low; /* demodulator for low frequencies */
fm_demod_t demod_high; /* demodulator for high frequencies */
iir_filter_t freq_lp[2]; /* low pass to filter the frequency result */
char detected; /* currently detected DTMF digit or 0 for no detection */
int count; /* counter to count detection or loss (pause) of signal */
dtmf_meas_t meas; /* measurements */
} dtmf_dec_t;
int dtmf_decode_init(dtmf_dec_t *dtmf, void *priv, void (*recv_digit)(void *priv, char digit, dtmf_meas_t *meas), int samplerate, double max_amplitude, double min_amplitude);
void dtmf_decode_exit(dtmf_dec_t *dtmf);
void dtmf_decode(dtmf_dec_t *dtmf, sample_t *samples, int length);
void dtmf_decode_filter(dtmf_dec_t *dtmf, sample_t *samples, int length, sample_t *frequency_low, sample_t *frequency_high, sample_t *amplitude_low, sample_t *amplitude_high);

View File

@ -1,123 +0,0 @@
/* DTMF coder
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdint.h>
#include <string.h>
#include <math.h>
#include "../libsample/sample.h"
#include "dtmf_encode.h"
#define PI M_PI
#define PEAK_DTMF_LOW 0.2818 /* -11 dBm, relative to 0 dBm level */
#define PEAK_DTMF_HIGH 0.3548 /* -9 dBm, relative to 0 dBm level */
#define DTMF_DURATION 0.100 /* duration in seconds */
static sample_t dsp_sine_dtmf_low[65536];
static sample_t dsp_sine_dtmf_high[65536];
void dtmf_encode_init(dtmf_enc_t *dtmf, int samplerate, double dBm_level)
{
int i;
memset(dtmf, 0, sizeof(*dtmf));
dtmf->samplerate = samplerate;
dtmf->max = (int)((double)samplerate * DTMF_DURATION + 0.5);
// FIXME: do this globally and not per instance */
for (i = 0; i < 65536; i++) {
dsp_sine_dtmf_low[i] = sin((double)i / 65536.0 * 2.0 * PI) * PEAK_DTMF_LOW * dBm_level;
dsp_sine_dtmf_high[i] = sin((double)i / 65536.0 * 2.0 * PI) * PEAK_DTMF_HIGH * dBm_level;
}
}
/* set dtmf tone */
void dtmf_encode_set_tone(dtmf_enc_t *dtmf, char tone)
{
double f1, f2;
switch(tone) {
case '1': f1 = 697.0; f2 = 1209.0; break;
case '2': f1 = 697.0; f2 = 1336.0; break;
case '3': f1 = 697.0; f2 = 1477.0; break;
case'a':case 'A': f1 = 697.0; f2 = 1633.0; break;
case '4': f1 = 770.0; f2 = 1209.0; break;
case '5': f1 = 770.0; f2 = 1336.0; break;
case '6': f1 = 770.0; f2 = 1477.0; break;
case'b':case 'B': f1 = 770.0; f2 = 1633.0; break;
case '7': f1 = 852.0; f2 = 1209.0; break;
case '8': f1 = 852.0; f2 = 1336.0; break;
case '9': f1 = 852.0; f2 = 1477.0; break;
case'c':case 'C': f1 = 852.0; f2 = 1633.0; break;
case '*': f1 = 941.0; f2 = 1209.0; break;
case '0': f1 = 941.0; f2 = 1336.0; break;
case '#': f1 = 941.0; f2 = 1477.0; break;
case'd':case 'D': f1 = 941.0; f2 = 1633.0; break;
default:
dtmf->tone = 0;
return;
}
dtmf->tone = tone;
dtmf->pos = 0;
dtmf->phaseshift65536[0] = 65536.0 / ((double)dtmf->samplerate / f1);
dtmf->phaseshift65536[1] = 65536.0 / ((double)dtmf->samplerate / f2);
}
/* Generate audio stream from DTMF tone. Keep phase for next call of function. */
void dtmf_encode(dtmf_enc_t *dtmf, sample_t *samples, int length)
{
double *phaseshift, *phase;
int i, pos, max;
/* use silence, if no tone */
if (!dtmf->tone) {
memset(samples, 0, length * sizeof(*samples));
return;
}
phaseshift = dtmf->phaseshift65536;
phase = dtmf->phase65536;
pos = dtmf->pos;
max = dtmf->max;
for (i = 0; i < length; i++) {
*samples++ = dsp_sine_dtmf_low[(uint16_t)phase[0]]
+ dsp_sine_dtmf_high[(uint16_t)phase[1]];
phase[0] += phaseshift[0];
if (phase[0] >= 65536)
phase[0] -= 65536;
phase[1] += phaseshift[1];
if (phase[1] >= 65536)
phase[1] -= 65536;
/* tone ends */
if (++pos == max) {
dtmf->tone = 0;
break;
}
}
length -= i;
dtmf->pos = pos;
/* if tone ends, fill rest with silence */
if (length)
memset(samples, 0, length * sizeof(*samples));
}

View File

@ -1,14 +0,0 @@
typedef struct dtmf_enc {
int samplerate; /* samplerate */
char tone; /* current tone to be played */
int pos; /* sample counter for tone */
int max; /* max number of samples for tone duration */
double phaseshift65536[2]; /* how much the phase of sine wave changes per sample */
double phase65536[2]; /* current phase */
} dtmf_enc_t;
void dtmf_encode_init(dtmf_enc_t *dtmf, int samplerate, double dBm_level);
void dtmf_encode_set_tone(dtmf_enc_t *dtmf, char tone);
void dtmf_encode(dtmf_enc_t *dtmf, sample_t *samples, int length);

View File

@ -1,6 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libemphasis.a
libemphasis_a_SOURCES = \
emphasis.c

View File

@ -1,144 +0,0 @@
/* Pre-Emphasis and De-Emphasis implementation
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include "../libsample/sample.h"
#include "../libfilter/iir_filter.h"
#include "emphasis.h"
#define PI M_PI
static void gen_sine(sample_t *samples, int num, int samplerate, double freq)
{
int i;
for (i = 0; i < num; i++)
samples[i] = cos(2.0 * M_PI * freq / (double)samplerate * (double)i);
}
static double get_level(sample_t *samples, int num)
{
int i;
double envelope = 0;
for (i = num/2; i < num; i++) {
if (samples[i] > envelope)
envelope = samples[i];
}
return envelope;
}
/* calculate cut off from time constant in uS */
double timeconstant2cutoff(double time_constant_us)
{
return 1.0 / (2.0 * PI * time_constant_us / 1e6);
}
int init_emphasis(emphasis_t *state, int samplerate, double cut_off, double cut_off_h, double cut_off_l)
{
double factor;
sample_t test_samples[samplerate / 10];
memset(state, 0, sizeof(*state));
/* exp (-2 * PI * CUT_OFF * delta_t) */
factor = exp(-2.0 * PI * cut_off / (double)samplerate); /* 1/samplerate == delta_t */
// printf("Emphasis factor = %.3f\n", factor);
state->p.factor = factor;
state->p.amp = 1.0;
state->d.factor = factor;
state->d.amp = 1.0;
/* do not de-emphasis below CUT_OFF_H */
iir_highpass_init(&state->d.hp, cut_off_h, samplerate, 1);
/* do not pre-emphasis above CUT_OFF_L
* Mobile network specifications want -18 dB per octave.
* With two iterations we have 24 dB, - 6 dB (from emphasis). */
iir_lowpass_init(&state->p.lp, cut_off_l, samplerate, 2);
/* calibrate amplification to be neutral at 1000 Hz */
gen_sine(test_samples, sizeof(test_samples) / sizeof(test_samples[0]), samplerate, 1000.0);
pre_emphasis(state, test_samples, sizeof(test_samples) / sizeof(test_samples[0]));
state->p.amp = 1.0 / get_level(test_samples, sizeof(test_samples) / sizeof(test_samples[0]));
gen_sine(test_samples, sizeof(test_samples) / sizeof(test_samples[0]), samplerate, 1000.0);
de_emphasis(state, test_samples, sizeof(test_samples) / sizeof(test_samples[0]));
state->d.amp = 1.0 / get_level(test_samples, sizeof(test_samples) / sizeof(test_samples[0]));
return 0;
}
void pre_emphasis(emphasis_t *state, sample_t *samples, int num)
{
double x, y, x_last, factor, amp;
int i;
iir_process(&state->p.lp, samples, num);
x_last = state->p.x_last;
factor = state->p.factor;
amp = state->p.amp;
for (i = 0; i < num; i++) {
x = *samples;
/* pre-emphasis */
y = x - factor * x_last;
x_last = x;
*samples++ = amp * y;
}
state->p.x_last = x_last;
}
void de_emphasis(emphasis_t *state, sample_t *samples, int num)
{
double x, y, y_last, factor, amp;
int i;
y_last = state->d.y_last;
factor = state->d.factor;
amp = state->d.amp;
for (i = 0; i < num; i++) {
x = *samples;
/* de-emphasis */
y = x + factor * y_last;
y_last = y;
*samples++ = amp * y;
}
state->d.y_last = y_last;
}
/* high pass filter to remove DC and low frequencies */
void dc_filter(emphasis_t *state, sample_t *samples, int num)
{
iir_process(&state->d.hp, samples, num);
}

View File

@ -1,28 +0,0 @@
#include "../libfilter/iir_filter.h"
typedef struct emphasis {
struct {
iir_filter_t lp;
double x_last;
double factor;
double amp;
} p;
struct {
iir_filter_t hp;
double y_last;
double factor;
double amp;
} d;
} emphasis_t;
/* refers to NMT specs, cnetz uses different emphasis cutoff */
#define CUT_OFF_EMPHASIS_DEFAULT 300.0
#define CUT_OFF_HIGHPASS_DEFAULT 300.0
#define CUT_OFF_LOWPASS_DEFAULT 3400.0
double timeconstant2cutoff(double time_constant_us);
int init_emphasis(emphasis_t *state, int samplerate, double cut_off, double cut_off_h, double cut_off_l);
void pre_emphasis(emphasis_t *state, sample_t *samples, int num);
void de_emphasis(emphasis_t *state, sample_t *samples, int num);
void dc_filter(emphasis_t *state, sample_t *samples, int num);

View File

@ -1,6 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libfft.a
libfft_a_SOURCES = \
fft.c

View File

@ -1,96 +0,0 @@
/* Fast Fourier Transformation (FFT)
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <math.h>
#include <string.h>
#include "fft.h"
/*
* Code based closely to work by Paul Bourke
*
* This computes an in-place complex-to-complex FFT
* x and y are the real and imaginary arrays of 2^m points.
* dir = 1 gives forward transform
* dir = -1 gives reverse transform
*/
void fft_process(int dir, int m, double *x, double *y)
{
int n, i, i1, j, k, i2, l, l1, l2;
double c1, c2, tx, ty, t1, t2, u1, u2, z;
/* Calculate the number of points */
n = 1 << m;
/* Do the bit reversal */
i2 = n >> 1;
j = 0;
for (i = 0; i < n - 1; i++) {
if (i < j) {
tx = x[i];
ty = y[i];
x[i] = x[j];
y[i] = y[j];
x[j] = tx;
y[j] = ty;
}
k = i2;
while (k <= j) {
j -= k;
k >>= 1;
}
j += k;
}
/* Compute the FFT */
c1 = -1.0;
c2 = 0.0;
l2 = 1;
for (l = 0; l < m; l++) {
l1 = l2;
l2 <<= 1;
u1 = 1.0;
u2 = 0.0;
for (j = 0; j < l1; j++) {
for (i = j; i < n; i += l2) {
i1 = i + l1;
t1 = u1 * x[i1] - u2 * y[i1];
t2 = u1 * y[i1] + u2 * x[i1];
x[i1] = x[i] - t1;
y[i1] = y[i] - t2;
x[i] += t1;
y[i] += t2;
}
z = u1 * c1 - u2 * c2;
u2 = u1 * c2 + u2 * c1;
u1 = z;
}
c2 = sqrt((1.0 - c1) / 2.0);
if (dir == 1)
c2 = -c2;
c1 = sqrt((1.0 + c1) / 2.0);
}
/* Scaling for forward transform */
if (dir == 1) {
for (i = 0; i < n; i++) {
x[i] /= n;
y[i] /= n;
}
}
}

View File

@ -1,3 +0,0 @@
void fft_process(int dir, int m, double *x, double *y);

View File

@ -1,8 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libfilter.a
libfilter_a_SOURCES = \
iir_filter.c \
fir_filter.c

View File

@ -1,197 +0,0 @@
/* FIR filter
*
* (C) 2017 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "../libsample/sample.h"
#include "fir_filter.h"
//#define DEBUG_TAPS
static void kernel(double *taps, int M, double cutoff, int invert)
{
int i;
double sum;
for (i = 0; i <= M; i++) {
/* gen sinc */
if (i == M / 2)
taps[i] = 2.0 * M_PI * cutoff;
else
taps[i] = sin(2.0 * M_PI * cutoff * (double)(i - M / 2))
/ (double)(i - M / 2);
/* blackman window */
taps[i] *= 0.42 - 0.50 * cos(2 * M_PI * (double)(i / M))
+ 0.08 * cos(4 * M_PI * (double)(i / M));
}
/* normalize */
sum = 0;
for (i = 0; i <= M; i++)
sum += taps[i];
for (i = 0; i <= M; i++)
taps[i] /= sum;
/* invert */
if (invert) {
for (i = 0; i <= M; i++)
taps[i] = -taps[i];
taps[M / 2] += 1.0;
}
#ifdef DEBUG_TAPS
puts("start");
for (i = 0; i <= M; i++)
puts(debug_amplitude(taps[i]));
#endif
}
static fir_filter_t *fir_init(double samplerate, double transition_bandwidth)
{
fir_filter_t *fir;
int M;
/* alloc struct */
fir = calloc(1, sizeof(*fir));
if (!fir) {
fprintf(stderr, "No memory creating FIR filter!\n");
return NULL;
}
/* transition bandwidth */
M = ceil(1.0 / (transition_bandwidth / samplerate));
if ((M & 1))
M++;
// printf("cutoff=%.4f\n", cutoff / samplerate);
// printf("tb=%.4f\n", transition_bandwidth / samplerate);
fir->ntaps = M + 1;
fir->delay = M / 2;
/* alloc taps */
fir->taps = calloc(fir->ntaps, sizeof(*fir->taps));
if (!fir->taps) {
fprintf(stderr, "No memory creating FIR filter!\n");
fir_exit(fir);
return NULL;
}
/* alloc ring buffer */
fir->buffer = calloc(fir->ntaps, sizeof(*fir->buffer));
if (!fir->buffer) {
fprintf(stderr, "No memory creating FIR filter!\n");
fir_exit(fir);
return NULL;
}
return fir;
}
fir_filter_t *fir_lowpass_init(double samplerate, double cutoff, double transition_bandwidth)
{
/* calculate kernel */
fir_filter_t *fir = fir_init(samplerate, transition_bandwidth);
if (!fir)
return NULL;
kernel(fir->taps, fir->ntaps - 1, cutoff / samplerate, 0);
return fir;
}
fir_filter_t *fir_highpass_init(double samplerate, double cutoff, double transition_bandwidth)
{
fir_filter_t *fir = fir_init(samplerate, transition_bandwidth);
if (!fir)
return NULL;
kernel(fir->taps, fir->ntaps - 1, cutoff / samplerate, 1);
return fir;
}
fir_filter_t *fir_allpass_init(double samplerate, double transition_bandwidth)
{
fir_filter_t *fir = fir_init(samplerate, transition_bandwidth);
if (!fir)
return NULL;
fir->taps[(fir->ntaps - 1) / 2] = 1.0;
return fir;
}
fir_filter_t *fir_twopass_init(double samplerate, double cutoff_low, double cutoff_high, double transition_bandwidth)
{
int i;
double sum;
fir_filter_t *fir = fir_init(samplerate, transition_bandwidth);
if (!fir)
return NULL;
double lp_taps[fir->ntaps], hp_taps[fir->ntaps];
kernel(lp_taps, fir->ntaps - 1, cutoff_low / samplerate, 0);
kernel(hp_taps, fir->ntaps - 1, cutoff_high / samplerate, 1);
sum = 0;
printf("#warning does not work as expected\n");
abort();
for (i = 0; i < fir->ntaps; i++) {
fir->taps[i] = lp_taps[i] + hp_taps[i];
sum += fir->taps[i];
}
/* hp will die */
// for (i = 0; i < fir->ntaps; i++)
// fir->taps[i] /= sum;
return fir;
}
void fir_exit(fir_filter_t *fir)
{
if (!fir)
return;
free(fir->taps);
free(fir->buffer);
free(fir);
}
void fir_process(fir_filter_t *fir, sample_t *samples, int num)
{
int i, j;
double y;
for (i = 0; i < num; i++) {
/* put sample in ring buffer */
fir->buffer[fir->buffer_pos] = samples[i];
if (++fir->buffer_pos == fir->ntaps)
fir->buffer_pos = 0;
/* convolve samples */
y = 0;
for (j = 0; j < fir->ntaps; j++) {
/* convolve sample from ring buffer, starting with oldest */
y += fir->buffer[fir->buffer_pos] * fir->taps[j];
if (++fir->buffer_pos == fir->ntaps)
fir->buffer_pos = 0;
}
samples[i] = y;
}
}
int fir_get_delay(fir_filter_t *fir)
{
return fir->delay;
}

View File

@ -1,21 +0,0 @@
#ifndef _FIR_FILTER_H
#define _FIR_FILTER_H
typedef struct fir_filter {
int ntaps;
int delay;
double *taps;
double *buffer;
int buffer_pos;
} fir_filter_t;
fir_filter_t *fir_lowpass_init(double samplerate, double cutoff, double transition_bandwidth);
fir_filter_t *fir_highpass_init(double samplerate, double cutoff, double transition_bandwidth);
fir_filter_t *fir_allpass_init(double samplerate, double transition_bandwidth);
fir_filter_t *fir_twopass_init(double samplerate, double cutoff_low, double cutoff_high, double transition_bandwidth);
void fir_exit(fir_filter_t *fir);
void fir_process(fir_filter_t *fir, sample_t *samples, int num);
int fir_get_delay(fir_filter_t *fir);
#endif /* _FIR_FILTER_H */

View File

@ -1,204 +0,0 @@
/* cut-off filter (biquad) based on Nigel Redmon (www.earlevel.com)
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "../libsample/sample.h"
#include "iir_filter.h"
//#define DEBUG_NAN
#define PI M_PI
void iir_lowpass_init(iir_filter_t *filter, double frequency, int samplerate, int iterations)
{
double Fc, Q, K, norm;
if (iterations > 64) {
fprintf(stderr, "%s failed: too many iterations, please fix!\n", __func__);
abort();
}
memset(filter, 0, sizeof(*filter));
filter->iter = iterations;
Q = pow(sqrt(0.5), 1.0 / (double)iterations); /* 0.7071 @ 1 iteration */
Fc = frequency / (double)samplerate;
K = tan(PI * Fc);
norm = 1 / (1 + K / Q + K * K);
filter->a0 = K * K * norm;
filter->a1 = 2 * filter->a0;
filter->a2 = filter->a0;
filter->b1 = 2 * (K * K - 1) * norm;
filter->b2 = (1 - K / Q + K * K) * norm;
#ifdef DEBUG_NAN
printf("%p\n", filter);
#endif
}
void iir_highpass_init(iir_filter_t *filter, double frequency, int samplerate, int iterations)
{
double Fc, Q, K, norm;
memset(filter, 0, sizeof(*filter));
filter->iter = iterations;
Q = pow(sqrt(0.5), 1.0 / (double)iterations); /* 0.7071 @ 1 iteration */
Fc = frequency / (double)samplerate;
K = tan(PI * Fc);
norm = 1 / (1 + K / Q + K * K);
filter->a0 = 1 * norm;
filter->a1 = -2 * filter->a0;
filter->a2 = filter->a0;
filter->b1 = 2 * (K * K - 1) * norm;
filter->b2 = (1 - K / Q + K * K) * norm;
}
void iir_bandpass_init(iir_filter_t *filter, double frequency, int samplerate, int iterations)
{
double Fc, Q, K, norm;
memset(filter, 0, sizeof(*filter));
filter->iter = iterations;
Q = pow(sqrt(0.5), 1.0 / (double)iterations); /* 0.7071 @ 1 iteration */
Fc = frequency / (double)samplerate;
K = tan(PI * Fc);
norm = 1 / (1 + K / Q + K * K);
filter->a0 = K / Q * norm;
filter->a1 = 0;
filter->a2 = -filter->a0;
filter->b1 = 2 * (K * K - 1) * norm;
filter->b2 = (1 - K / Q + K * K) * norm;
}
void iir_notch_init(iir_filter_t *filter, double frequency, int samplerate, int iterations)
{
double Fc, Q, K, norm;
memset(filter, 0, sizeof(*filter));
filter->iter = iterations;
Q = pow(sqrt(0.5), 1.0 / (double)iterations); /* 0.7071 @ 1 iteration */
Fc = frequency / (double)samplerate;
K = tan(PI * Fc);
norm = 1 / (1 + K / Q + K * K);
filter->a0 = (1 + K * K) * norm;
filter->a1 = 2 * (K * K - 1) * norm;
filter->a2 = filter->a0;
filter->b1 = filter->a1;
filter->b2 = (1 - K / Q + K * K) * norm;
}
void iir_process(iir_filter_t *filter, sample_t *samples, int length)
{
double a0, a1, a2, b1, b2;
double *z1, *z2;
double in, out;
int iterations = filter->iter;
int i, j;
/* get states */
a0 = filter->a0;
a1 = filter->a1;
a2 = filter->a2;
b1 = filter->b1;
b2 = filter->b2;
/* these are state pointers, so no need to write back */
z1 = filter->z1;
z2 = filter->z2;
/* process filter */
for (i = 0; i < length; i++) {
/* add a small value, otherwise this loop will perform really bad on my 'nuedel' machine!!! */
in = *samples + 0.000000001;
for (j = 0; j < iterations; j++) {
out = in * a0 + z1[j];
z1[j] = in * a1 + z2[j] - b1 * out;
z2[j] = in * a2 - b2 * out;
in = out;
}
*samples++ = in;
}
}
#ifdef DEBUG_NAN
#pragma GCC push_options
//#pragma GCC optimize ("O0")
#endif
void iir_process_baseband(iir_filter_t *filter, float *baseband, int length)
{
double a0, a1, a2, b1, b2;
double *z1, *z2;
double in, out;
int iterations = filter->iter;
int i, j;
/* get states */
a0 = filter->a0;
a1 = filter->a1;
a2 = filter->a2;
b1 = filter->b1;
b2 = filter->b2;
/* these are state pointers, so no need to write back */
z1 = filter->z1;
z2 = filter->z2;
/* process filter */
for (i = 0; i < length; i++) {
/* add a small value, otherwise this loop will perform really bad on my 'nuedel' machine!!! */
in = *baseband + 0.000000001;
for (j = 0; j < iterations; j++) {
out = in * a0 + z1[j];
#ifdef DEBUG_NAN
if (!(out > -100 && out < 100)) {
printf("%p\n", filter);
printf("1. i=%d j=%d z=%.5f in=%.5f a0=%.5f out=%.5f\n", i, j, z1[j], in, a0, out);
abort();
}
#endif
z1[j] = in * a1 + z2[j] - b1 * out;
#ifdef DEBUG_NAN
if (!(z1[j] > -100 && z1[j] < 100)) {
printf("%p\n", filter);
printf("2. i=%d j=%d z1=%.5f z2=%.5f in=%.5f a1=%.5f out=%.5f b1=%.5f\n", i, j, z1[j], z2[j], in, a1, out, b1);
abort();
}
#endif
z2[j] = in * a2 - b2 * out;
#ifdef DEBUG_NAN
if (!(z2[j] > -100 && z2[j] < 100)) {
printf("%p\n", filter);
printf("%.5f\n", (in * a2) - (b2 * out));
printf("3. i=%d j=%d z2=%.5f in=%.5f a2=%.5f b2=%.5f out=%.5f\n", i, j, z2[j], in, a2, b2, out);
abort();
}
#endif
in = out;
}
*baseband = in;
baseband += 2;
}
}
#ifdef DEBUG_NAN
#pragma GCC pop_options
#endif

View File

@ -1,17 +0,0 @@
#ifndef _FILTER_H
#define _FILTER_H
typedef struct iir_filter {
int iter;
double a0, a1, a2, b1, b2;
double z1[64], z2[64];
} iir_filter_t;
void iir_lowpass_init(iir_filter_t *filter, double frequency, int samplerate, int iterations);
void iir_highpass_init(iir_filter_t *filter, double frequency, int samplerate, int iterations);
void iir_bandpass_init(iir_filter_t *filter, double frequency, int samplerate, int iterations);
void iir_notch_init(iir_filter_t *filter, double frequency, int samplerate, int iterations);
void iir_process(iir_filter_t *filter, sample_t *samples, int length);
void iir_process_baseband(iir_filter_t *filter, float *baseband, int length);
#endif /* _FILTER_H */

View File

@ -1,6 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libfm.a
libfm_a_SOURCES = \
fm.c

View File

@ -1,413 +0,0 @@
/* FM modulation processing
*
* (C) 2017 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include "../libsample/sample.h"
#include "fm.h"
static int has_init = 0;
static int fast_math = 0;
static float *sin_tab = NULL, *cos_tab = NULL;
/* global init */
int fm_init(int _fast_math)
{
fast_math = _fast_math;
if (fast_math) {
int i;
sin_tab = calloc(65536+16384, sizeof(*sin_tab));
if (!sin_tab) {
fprintf(stderr, "No mem!\n");
return -ENOMEM;
}
cos_tab = sin_tab + 16384;
/* generate sine and cosine */
for (i = 0; i < 65536+16384; i++)
sin_tab[i] = sin(2.0 * M_PI * (double)i / 65536.0);
}
has_init = 1;
return 0;
}
/* global exit */
void fm_exit(void)
{
if (sin_tab) {
free(sin_tab);
sin_tab = cos_tab = NULL;
}
has_init = 0;
}
/* init FM modulator */
int fm_mod_init(fm_mod_t *mod, double samplerate, double offset, double amplitude)
{
int i;
if (!has_init) {
fprintf(stderr, "libfm was not initialized, please fix!\n");
abort();
}
memset(mod, 0, sizeof(*mod));
mod->samplerate = samplerate;
mod->offset = offset;
mod->amplitude = amplitude;
mod->ramp_length = samplerate * 0.001;
mod->ramp_tab = calloc(mod->ramp_length, sizeof(*mod->ramp_tab));
if (!mod->ramp_tab) {
fprintf(stderr, "No mem!\n");
return -ENOMEM;
}
mod->state = MOD_STATE_OFF;
/* generate ramp up with ramp_length */
for (i = 0; i < mod->ramp_length; i++)
mod->ramp_tab[i] = 0.5 - cos(M_PI * i / mod->ramp_length) / 2.0;
return 0;
}
void fm_mod_exit(fm_mod_t *mod)
{
if (mod->ramp_tab) {
free(mod->ramp_tab);
mod->ramp_tab = NULL;
}
}
/* do frequency modulation of samples and add them to existing baseband */
void fm_modulate_complex(fm_mod_t *mod, sample_t *frequency, uint8_t *power, int length, float *baseband)
{
double dev, rate, phase, offset;
int ramp, ramp_length;
double *ramp_tab;
double amplitude;
rate = mod->samplerate;
phase = mod->phase;
offset = mod->offset;
ramp = mod->ramp;
ramp_length = mod->ramp_length;
ramp_tab = mod->ramp_tab;
amplitude = mod->amplitude;
again:
switch (mod->state) {
case MOD_STATE_ON:
/* modulate */
while (length) {
/* is power is not set, ramp down */
if (!(*power)) {
mod->state = MOD_STATE_RAMP_DOWN;
break;
}
/* deviation is defined by the frequency value and the offset */
dev = offset + *frequency++;
power++;
length--;
if (fast_math) {
phase += 65536.0 * dev / rate;
if (phase < 0.0)
phase += 65536.0;
else if (phase >= 65536.0)
phase -= 65536.0;
*baseband++ += cos_tab[(uint16_t)phase] * amplitude;
*baseband++ += sin_tab[(uint16_t)phase] * amplitude;
} else {
phase += 2.0 * M_PI * dev / rate;
if (phase < 0.0)
phase += 2.0 * M_PI;
else if (phase >= 2.0 * M_PI)
phase -= 2.0 * M_PI;
*baseband++ += cos(phase) * amplitude;
*baseband++ += sin(phase) * amplitude;
}
}
break;
case MOD_STATE_RAMP_DOWN:
while (length) {
/* if power is set, ramp up */
if (*power) {
mod->state = MOD_STATE_RAMP_UP;
break;
}
if (ramp == 0) {
mod->state = MOD_STATE_OFF;
break;
}
dev = offset + *frequency++;
power++;
length--;
if (fast_math) {
phase += 65536.0 * dev / rate;
if (phase < 0.0)
phase += 65536.0;
else if (phase >= 65536.0)
phase -= 65536.0;
*baseband++ += cos_tab[(uint16_t)phase] * amplitude * ramp_tab[ramp];
*baseband++ += sin_tab[(uint16_t)phase] * amplitude * ramp_tab[ramp];
} else {
phase += 2.0 * M_PI * dev / rate;
if (phase < 0.0)
phase += 2.0 * M_PI;
else if (phase >= 2.0 * M_PI)
phase -= 2.0 * M_PI;
*baseband++ += cos(phase) * amplitude * ramp_tab[ramp];
*baseband++ += sin(phase) * amplitude * ramp_tab[ramp];
}
ramp--;
}
break;
case MOD_STATE_OFF:
while (length) {
/* if power is set, ramp up */
if (*power) {
mod->state = MOD_STATE_RAMP_UP;
break;
}
/* just count, and add nothing */
frequency++;
power++;
length--;
baseband += 2;
}
break;
case MOD_STATE_RAMP_UP:
while (length) {
/* is power is not set, ramp down */
if (!(*power)) {
mod->state = MOD_STATE_RAMP_DOWN;
break;
}
if (ramp == ramp_length - 1) {
mod->state = MOD_STATE_ON;
break;
}
/* deviation is defined by the frequency value and the offset */
dev = offset + *frequency++;
power++;
length--;
if (fast_math) {
phase += 65536.0 * dev / rate;
if (phase < 0.0)
phase += 65536.0;
else if (phase >= 65536.0)
phase -= 65536.0;
*baseband++ += cos_tab[(uint16_t)phase] * amplitude * ramp_tab[ramp];
*baseband++ += sin_tab[(uint16_t)phase] * amplitude * ramp_tab[ramp];
} else {
phase += 2.0 * M_PI * dev / rate;
if (phase < 0.0)
phase += 2.0 * M_PI;
else if (phase >= 2.0 * M_PI)
phase -= 2.0 * M_PI;
*baseband++ += cos(phase) * amplitude * ramp_tab[ramp];
*baseband++ += sin(phase) * amplitude * ramp_tab[ramp];
}
ramp++;
}
break;
}
if (length)
goto again;
mod->phase = phase;
mod->ramp = ramp;
}
/* init FM demodulator */
int fm_demod_init(fm_demod_t *demod, double samplerate, double offset, double bandwidth)
{
if (!has_init) {
fprintf(stderr, "libfm was not initialized, please fix!\n");
abort();
}
memset(demod, 0, sizeof(*demod));
demod->samplerate = samplerate;
if (fast_math)
demod->rot = 65536.0 * -offset / samplerate;
else
demod->rot = 2 * M_PI * -offset / samplerate;
/* use fourth order (2 iter) filter, since it is as fast as second order (1 iter) filter */
iir_lowpass_init(&demod->lp[0], bandwidth / 2.0, samplerate, 2);
iir_lowpass_init(&demod->lp[1], bandwidth / 2.0, samplerate, 2);
return 0;
}
void fm_demod_exit(fm_demod_t __attribute__ ((unused)) *demod)
{
}
static inline float fast_tan(float z)
{
const float n1 = 0.97239411f;
const float n2 = -0.19194795f;
return (n1 + n2 * z * z) * z;
}
static inline float fast_atan2(float y, float x)
{
if (x != 0.0) {
if (fabsf(x) > fabsf(y)) {
const float z = y / x;
if (x > 0.0) /* atan2(y,x) = atan(y/x) if x > 0 */
return fast_tan(z);
else if (y >= 0.0) /* atan2(y,x) = atan(y/x) + PI if x < 0, y >= 0 */
return fast_tan(z) + M_PI;
else /* atan2(y,x) = atan(y/x) - PI if x < 0, y < 0 */
return fast_tan(z) - M_PI;
} else { /* Use property atan(y/x) = PI/2 - atan(x/y) if |y/x| > 1 */
const float z = x / y;
if (y > 0.0) /* atan2(y,x) = PI/2 - atan(x/y) if |y/x| > 1, y > 0 */
return -fast_tan(z) + M_PI_2;
else /* atan2(y,x) = -PI/2 - atan(x/y) if |y/x| > 1, y < 0 */
return -fast_tan(z) - M_PI_2;
}
} else {
if (y > 0.0) /* x = 0, y > 0 */
return M_PI_2;
else if (y < 0.0) /* x = 0, y < 0 */
return -M_PI_2;
}
return 0.0; /* x,y = 0. return 0, because NaN would harm further processing */
}
/* do frequency demodulation of baseband and write them to samples */
void fm_demodulate_complex(fm_demod_t *demod, sample_t *frequency, int length, float *baseband, sample_t *I, sample_t *Q)
{
double phase, rot, last_phase, dev, rate;
double _sin, _cos;
sample_t i, q;
int s, ss;
rate = demod->samplerate;
phase = demod->phase;
rot = demod->rot;
for (s = 0, ss = 0; s < length; s++) {
phase += rot;
i = baseband[ss++];
q = baseband[ss++];
if (fast_math) {
if (phase < 0.0)
phase += 65536.0;
else if (phase >= 65536.0)
phase -= 65536.0;
_sin = sin_tab[(uint16_t)phase];
_cos = cos_tab[(uint16_t)phase];
} else {
if (phase < 0.0)
phase += 2.0 * M_PI;
else if (phase >= 2.0 * M_PI)
phase -= 2.0 * M_PI;
_sin = sin(phase);
_cos = cos(phase);
}
I[s] = i * _cos - q * _sin;
Q[s] = i * _sin + q * _cos;
}
demod->phase = phase;
iir_process(&demod->lp[0], I, length);
iir_process(&demod->lp[1], Q, length);
last_phase = demod->last_phase;
for (s = 0; s < length; s++) {
if (fast_math)
phase = fast_atan2(Q[s], I[s]);
else
phase = atan2(Q[s], I[s]);
dev = (phase - last_phase) / 2 / M_PI;
last_phase = phase;
if (dev < -0.49)
dev += 1.0;
else if (dev > 0.49)
dev -= 1.0;
dev *= rate;
frequency[s] = dev;
}
demod->last_phase = last_phase;
}
void fm_demodulate_real(fm_demod_t *demod, sample_t *frequency, int length, sample_t *baseband, sample_t *I, sample_t *Q)
{
double phase, rot, last_phase, dev, rate;
double _sin, _cos;
sample_t i;
int s, ss;
rate = demod->samplerate;
phase = demod->phase;
rot = demod->rot;
for (s = 0, ss = 0; s < length; s++) {
phase += rot;
i = baseband[ss++];
if (fast_math) {
if (phase < 0.0)
phase += 65536.0;
else if (phase >= 65536.0)
phase -= 65536.0;
_sin = sin_tab[(uint16_t)phase];
_cos = cos_tab[(uint16_t)phase];
} else {
if (phase < 0.0)
phase += 2.0 * M_PI;
else if (phase >= 2.0 * M_PI)
phase -= 2.0 * M_PI;
_sin = sin(phase);
_cos = cos(phase);
}
I[s] = i * _cos;
Q[s] = i * _sin;
}
demod->phase = phase;
iir_process(&demod->lp[0], I, length);
iir_process(&demod->lp[1], Q, length);
last_phase = demod->last_phase;
for (s = 0; s < length; s++) {
if (fast_math)
phase = fast_atan2(Q[s], I[s]);
else
phase = atan2(Q[s], I[s]);
dev = (phase - last_phase) / 2 / M_PI;
last_phase = phase;
if (dev < -0.49)
dev += 1.0;
else if (dev > 0.49)
dev -= 1.0;
dev *= rate;
frequency[s] = dev;
}
demod->last_phase = last_phase;
}

View File

@ -1,40 +0,0 @@
#include "../libfilter/iir_filter.h"
int fm_init(int fast_math);
void fm_exit(void);
enum fm_mod_state {
MOD_STATE_OFF, /* transmitter off, no IQ vector */
MOD_STATE_ON, /* transmitter on, FM modulated IQ vector */
MOD_STATE_RAMP_UP, /* use half cos to ramp up IQ vector */
MOD_STATE_RAMP_DOWN, /* use half cos to ramp down IQ vector */
};
typedef struct fm_mod {
double samplerate; /* sample rate of in and out */
double offset; /* offset to calculated center frequency */
double amplitude; /* how much amplitude to add to the buff */
double phase; /* current phase of FM (used to shift and modulate ) */
enum fm_mod_state state;/* state of transmit power */
double *ramp_tab; /* half cosine ramp up */
int ramp; /* current ramp position */
int ramp_length; /* number of values in ramp */
} fm_mod_t;
int fm_mod_init(fm_mod_t *mod, double samplerate, double offset, double amplitude);
void fm_mod_exit(fm_mod_t *mod);
void fm_modulate_complex(fm_mod_t *mod, sample_t *frequency, uint8_t *power, int num, float *baseband);
typedef struct fm_demod {
double samplerate; /* sample rate of in and out */
double phase; /* current rotation phase (used to shift) */
double rot; /* rotation step per sample to shift rx frequency (used to shift) */
double last_phase; /* last phase of FM (used to demodulate) */
iir_filter_t lp[2]; /* filters received IQ signal */
} fm_demod_t;
int fm_demod_init(fm_demod_t *demod, double samplerate, double offset, double bandwidth);
void fm_demod_exit(fm_demod_t *demod);
void fm_demodulate_complex(fm_demod_t *demod, sample_t *frequency, int length, float *baseband, sample_t *I, sample_t *Q);
void fm_demodulate_real(fm_demod_t *demod, sample_t *frequency, int length, sample_t *baseband, sample_t *I, sample_t *Q);

View File

@ -1,6 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libfsk.a
libfsk_a_SOURCES = \
fsk.c

View File

@ -1,363 +0,0 @@
/* FSK audio processing (FSK/FFSK modem)
*
* (C) 2017 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "fsk.h"
#define PI M_PI
/*
* fsk = instance of fsk modem
* inst = instance of user
* send_bit() = function to be called whenever a new bit has to be sent
* samplerate = samplerate
* bitrate = bits per second
* f0, f1 = two frequencies for bit 0 and bit 1
* level = level to modulate the frequencies
* ffsk = use FFSK modulation (each symbol ends at zero crossing)
*/
int fsk_mod_init(fsk_mod_t *fsk, void *inst, int (*send_bit)(void *inst), int samplerate, double bitrate, double f0, double f1, double level, int ffsk, int filter)
{
int i;
int rc;
PDEBUG(DDSP, DEBUG_DEBUG, "Setup FSK for Transmitter. (F0 = %.1f, F1 = %.1f, peak = %.1f)\n", f0, f1, level);
memset(fsk, 0, sizeof(*fsk));
/* gen sine table with deviation */
fsk->sin_tab = calloc(65536+16384, sizeof(*fsk->sin_tab));
if (!fsk->sin_tab) {
fprintf(stderr, "No mem!\n");
rc = -ENOMEM;
goto error;
}
for (i = 0; i < 65536; i++)
fsk->sin_tab[i] = sin((double)i / 65536.0 * 2.0 * PI) * level;
fsk->inst = inst;
fsk->tx_bit = -1;
fsk->level = level;
fsk->send_bit = send_bit;
fsk->f0_deviation = (f0 - f1) / 2.0;
fsk->f1_deviation = (f1 - f0) / 2.0;
if (f0 < f1) {
fsk->low_bit = 0;
fsk->high_bit = 1;
} else {
fsk->low_bit = 1;
fsk->high_bit = 0;
}
fsk->bits_per_sample = (double)bitrate / (double)samplerate;
PDEBUG(DDSP, DEBUG_DEBUG, "Bitduration of %.4f bits per sample @ %d.\n", fsk->bits_per_sample, samplerate);
fsk->phaseshift65536[0] = f0 / (double)samplerate * 65536.0;
PDEBUG(DDSP, DEBUG_DEBUG, "F0 = %.0f Hz (phaseshift65536[0] = %.4f)\n", f0, fsk->phaseshift65536[0]);
fsk->phaseshift65536[1] = f1 / (double)samplerate * 65536.0;
PDEBUG(DDSP, DEBUG_DEBUG, "F1 = %.0f Hz (phaseshift65536[1] = %.4f)\n", f1, fsk->phaseshift65536[1]);
/* use ffsk modulation, i.e. each bit has an integer number of
* half waves and starts/ends at zero crossing
*/
if (ffsk) {
double waves;
PDEBUG(DDSP, DEBUG_DEBUG, "enable FFSK modulation mode\n");
fsk->ffsk = 1;
waves = (f0 / bitrate);
if (fabs(round(waves * 2) - (waves * 2)) > 0.001) {
fprintf(stderr, "Failed to set FFSK mode, half waves of F0 does not fit exactly into one bit, please fix!\n");
abort();
}
fsk->cycles_per_bit65536[0] = waves * 65536.0;
waves = (f1 / bitrate);
if (fabs(round(waves * 2) - (waves * 2)) > 0.001) {
fprintf(stderr, "Failed to set FFSK mode, half waves of F1 does not fit exactly into one bit, please fix!\n");
abort();
}
fsk->cycles_per_bit65536[1] = waves * 65536.0;
}
/* if filter is enabled, add a band pass filter to smooth the spectrum of the tones
* the bandwidth is twice the difference between f0 and f1
*/
if (filter) {
double low = (f0 + f1) / 2.0 - fabs(f0 - f1);
double high = (f0 + f1) / 2.0 + fabs(f0 - f1);
PDEBUG(DDSP, DEBUG_DEBUG, "enable filter to smooth FSK transmission. (frequency rage %.0f .. %.0f)\n", low, high);
fsk->filter = 1;
/* use fourth order (2 iter) filter, since it is as fast as second order (1 iter) filter */
iir_highpass_init(&fsk->lp[0], low, samplerate, 2);
iir_lowpass_init(&fsk->lp[1], high, samplerate, 2);
}
return 0;
error:
fsk_mod_cleanup(fsk);
return rc;
}
/* Cleanup transceiver instance. */
void fsk_mod_cleanup(fsk_mod_t *fsk)
{
PDEBUG(DDSP, DEBUG_DEBUG, "Cleanup FSK for Transmitter.\n");
if (fsk->sin_tab) {
free(fsk->sin_tab);
fsk->sin_tab = NULL;
}
}
/* modulate bits
*
* If first/next bit is required, callback function send_bit() is called.
* If there is no (more) data to be transmitted, the callback functions shall
* return -1. In this case, this function stops and returns the number of
* samples that have been rendered so far, if any.
*
* For FFSK mode, we round the phase on every bit change to the
* next zero crossing. This prevents phase shifts due to rounding errors.
*/
int fsk_mod_send(fsk_mod_t *fsk, sample_t *sample, int length, int add)
{
int count = 0;
double phase, phaseshift;
phase = fsk->tx_phase65536;
/* get next bit */
if (fsk->tx_bit < 0) {
next_bit:
fsk->tx_bit = fsk->send_bit(fsk->inst);
#ifdef DEBUG_MODULATOR
printf("bit change to %d\n", fsk->tx_bit);
#endif
if (fsk->tx_bit < 0)
goto done;
/* correct phase when changing bit */
if (fsk->ffsk) {
/* round phase to nearest zero crossing */
if (phase > 16384.0 && phase < 49152.0)
phase = 32768.0;
else
phase = 0;
/* set phase according to current position in bit */
phase += fsk->tx_bitpos * fsk->cycles_per_bit65536[fsk->tx_bit & 1];
#ifdef DEBUG_MODULATOR
printf("phase %.3f bitpos=%.6f\n", phase, fsk->tx_bitpos);
#endif
}
}
/* modulate bit */
phaseshift = fsk->phaseshift65536[fsk->tx_bit & 1];
while (count < length && fsk->tx_bitpos < 1.0) {
if (add)
sample[count++] += fsk->sin_tab[(uint16_t)phase];
else
sample[count++] = fsk->sin_tab[(uint16_t)phase];
#ifdef DEBUG_MODULATOR
printf("|%s|\n", debug_amplitude(fsk->sin_tab[(uint16_t)phase] / fsk->level));
#endif
phase += phaseshift;
if (phase >= 65536.0)
phase -= 65536.0;
fsk->tx_bitpos += fsk->bits_per_sample;
}
if (fsk->tx_bitpos >= 1.0) {
fsk->tx_bitpos -= 1.0;
goto next_bit;
}
/* post filter */
if (fsk->filter) {
iir_process(&fsk->lp[0], sample, length);
iir_process(&fsk->lp[1], sample, length);
}
done:
fsk->tx_phase65536 = phase;
return count;
}
/* reset transmitter state, so we get a clean start */
void fsk_mod_tx_reset(fsk_mod_t *fsk)
{
fsk->tx_phase65536 = 0;
fsk->tx_bitpos = 0;
fsk->tx_bit = -1;
}
/*
* fsk = instance of fsk modem
* inst = instance of user
* receive_bit() = function to be called whenever a new bit was received
* samplerate = samplerate
* bitrate = bits per second
* f0, f1 = two frequencies for bit 0 and bit 1
* bitadjust = how much to adjust the sample clock when a bitchange was detected. (0 = nothing, don't use this, 0.5 full adjustment)
*/
int fsk_demod_init(fsk_demod_t *fsk, void *inst, void (*receive_bit)(void *inst, int bit, double quality, double level), int samplerate, double bitrate, double f0, double f1, double bitadjust)
{
double bandwidth;
int rc;
PDEBUG(DDSP, DEBUG_DEBUG, "Setup FSK for Receiver. (F0 = %.1f, F1 = %.1f)\n", f0, f1);
memset(fsk, 0, sizeof(*fsk));
fsk->inst = inst;
fsk->rx_bit = -1;
fsk->rx_bitadjust = bitadjust;
fsk->receive_bit = receive_bit;
fsk->f0_deviation = (f0 - f1) / 2.0;
fsk->f1_deviation = (f1 - f0) / 2.0;
if (f0 < f1) {
fsk->low_bit = 0;
fsk->high_bit = 1;
} else {
fsk->low_bit = 1;
fsk->high_bit = 0;
}
/* calculate bandwidth */
bandwidth = fabs(f0 - f1) * 2.0;
/* init fm demodulator */
rc = fm_demod_init(&fsk->demod, (double)samplerate, (f0 + f1) / 2.0, bandwidth);
if (rc < 0)
goto error;
fsk->bits_per_sample = (double)bitrate / (double)samplerate;
PDEBUG(DDSP, DEBUG_DEBUG, "Bitduration of %.4f bits per sample @ %d.\n", fsk->bits_per_sample, samplerate);
return 0;
error:
fsk_demod_cleanup(fsk);
return rc;
}
/* Cleanup transceiver instance. */
void fsk_demod_cleanup(fsk_demod_t *fsk)
{
PDEBUG(DDSP, DEBUG_DEBUG, "Cleanup FSK for Receiver.\n");
fm_demod_exit(&fsk->demod);
}
//#define DEBUG_MODULATOR
//#define DEBUG_FILTER
/* Demodulates bits
*
* If bit is received, callback function send_bit() is called.
*
* We sample each bit 0.5 bits after polarity change.
*
* If we have a bit change, adjust sample counter towards one half bit duration.
* We may have noise, so the bit change may be wrong or not at the correct place.
* This can cause bit slips.
* Therefore we change the sample counter only slightly, so bit slips may not
* happen so quickly.
*/
void fsk_demod_receive(fsk_demod_t *fsk, sample_t *sample, int length)
{
sample_t I[length], Q[length], frequency[length], f;
int i;
int bit;
double level, quality;
/* demod samples to offset around center frequency */
fm_demodulate_real(&fsk->demod, frequency, length, sample, I, Q);
for (i = 0; i < length; i++) {
f = frequency[i];
if (f < 0)
bit = fsk->low_bit;
else
bit = fsk->high_bit;
#ifdef DEBUG_FILTER
printf("|%s| %.3f\n", debug_amplitude(f / fabs(fsk->f0_deviation) / 2), f / fabs(fsk->f0_deviation));
#endif
if (fsk->rx_bit != bit) {
#ifdef DEBUG_FILTER
puts("bit change");
#endif
fsk->rx_bit = bit;
if (fsk->rx_bitpos < 0.5) {
fsk->rx_bitpos += fsk->rx_bitadjust;
if (fsk->rx_bitpos > 0.5)
fsk->rx_bitpos = 0.5;
} else
if (fsk->rx_bitpos > 0.5) {
fsk->rx_bitpos -= fsk->rx_bitadjust;
if (fsk->rx_bitpos < 0.5)
fsk->rx_bitpos = 0.5;
}
/* if we have a pulse before we sampled a bit after last pulse */
if (fsk->rx_change) {
/* peak level is the length of I/Q vector
* since we filter out the unwanted modulation product, the vector is only half of length */
level = sqrt(I[i] * I[i] + Q[i] * Q[i]) * 2.0;
#ifdef DEBUG_FILTER
printf("prematurely bit change (level=%.3f)\n", level);
#endif
/* quality is 0.0, because a prematurely level change is caused by noise and has nothing to measure. */
fsk->receive_bit(fsk->inst, fsk->rx_bit, 0.0, level);
}
fsk->rx_change = 1;
}
/* if bit counter reaches 1, we subtract 1 and sample the bit */
if (fsk->rx_bitpos >= 1.0) {
/* peak level is the length of I/Q vector
* since we filter out the unwanted modulation product, the vector is only half of length */
level = sqrt(I[i] * I[i] + Q[i] * Q[i]) * 2.0;
/* quality is defined on how accurat the target frequency it hit
* if it is hit close to the center or close to double deviation from center, quality is close to 0 */
if (bit == 0)
quality = 1.0 - fabs((f - fsk->f0_deviation) / fsk->f0_deviation);
else
quality = 1.0 - fabs((f - fsk->f1_deviation) / fsk->f1_deviation);
if (quality < 0)
quality = 0;
#ifdef DEBUG_FILTER
printf("sample (level=%.3f, quality=%.3f)\n", level, quality);
#endif
fsk->receive_bit(fsk->inst, bit, quality, level);
fsk->rx_bitpos -= 1.0;
fsk->rx_change = 0;
}
fsk->rx_bitpos += fsk->bits_per_sample;
}
}

View File

@ -1,43 +0,0 @@
#include "../libfm/fm.h"
typedef struct fsk_mod {
void *inst;
int (*send_bit)(void *inst);
double bits_per_sample; /* fraction of a bit per sample */
double *sin_tab; /* sine table with correct peak level */
double phaseshift65536[2]; /* how much the phase of fsk synbol changes per sample */
double cycles_per_bit65536[2]; /* cycles of one bit */
double tx_phase65536; /* current transmit phase */
double level; /* level (amplitude) of signal */
int ffsk; /* set, if FFSK TX mode */
double f0_deviation; /* deviation of frequencies, relative to center */
double f1_deviation;
int low_bit, high_bit; /* a low or high deviation means which bit? */
int tx_bit; /* current transmitting bit (-1 if not set) */
double tx_bitpos; /* current transmit position in bit */
int filter; /* set, if filters are used */
iir_filter_t lp[2]; /* filter to smooth transmission spectrum */
} fsk_mod_t;
typedef struct fsk_demod {
void *inst;
void (*receive_bit)(void *inst, int bit, double quality, double level);
fm_demod_t demod;
double bits_per_sample; /* fraction of a bit per sample */
double f0_deviation; /* deviation of frequencies, relative to center */
double f1_deviation;
int low_bit, high_bit; /* a low or high deviation means which bit? */
int rx_bit; /* current receiving bit (-1 if not yet measured) */
double rx_bitpos; /* current receive position in bit (sampleclock) */
double rx_bitadjust; /* how much does a bit change cause the sample clock to be adjusted in phase */
int rx_change; /* set, if we have a level change before sampling the bit */
} fsk_demod_t;
int fsk_mod_init(fsk_mod_t *fsk, void *inst, int (*send_bit)(void *inst), int samplerate, double bitrate, double f0, double f1, double level, int coherent, int filter);
void fsk_mod_cleanup(fsk_mod_t *fsk);
int fsk_mod_send(fsk_mod_t *fsk, sample_t *sample, int length, int add);
void fsk_mod_tx_reset(fsk_mod_t *fsk);
int fsk_demod_init(fsk_demod_t *fsk, void *inst, void (*receive_bit)(void *inst, int bit, double quality, double level), int samplerate, double bitrate, double f0, double f1, double bitadjust);
void fsk_demod_cleanup(fsk_demod_t *fsk);
void fsk_demod_receive(fsk_demod_t *fsk, sample_t *sample, int length);

View File

@ -1,7 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libg711.a
libg711_a_SOURCES = \
g711.c

View File

@ -1,537 +0,0 @@
/*****************************************************************************\
** **
** PBX4Linux **
** **
**---------------------------------------------------------------------------**
** Copyright: Andreas Eversberg (GPL) **
** **
** audio conversions for alaw and ulaw **
** **
\*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
/* ulaw -> signed 16-bit */
static int16_t g711_ulaw_flipped_to_linear[256] =
{
0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84,
0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84,
0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84,
0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84,
0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804,
0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004,
0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444,
0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844,
0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64,
0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64,
0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74,
0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74,
0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc,
0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c,
0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0,
0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0xffff,
0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c,
0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c,
0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c,
0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c,
0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc,
0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc,
0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc,
0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc,
0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c,
0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c,
0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c,
0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c,
0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084,
0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
};
/* alaw -> signed 16-bit */
static int16_t g711_alaw_flipped_to_linear[256] =
{
0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
};
/* Xlaw -> signed 16-bit */
static int16_t g711_alaw_to_linear[256];
static int16_t g711_ulaw_to_linear[256];
/* signed 16-bit -> Xlaw */
static uint8_t g711_linear_to_alaw_flipped[65536];
static uint8_t g711_linear_to_ulaw_flipped[65536];
static uint8_t g711_linear_to_alaw[65536];
static uint8_t g711_linear_to_ulaw[65536];
/* transcode */
static uint8_t g711_alaw_to_ulaw[256];
static uint8_t g711_ulaw_to_alaw[256];
static uint8_t g711_alaw_flipped_to_ulaw[256];
static uint8_t g711_ulaw_flipped_to_alaw[256];
static uint8_t g711_alaw_to_ulaw_flipped[256];
static uint8_t g711_ulaw_to_alaw_flipped[256];
/* table is used to generate linear_to_alaw */
static int16_t g711_alaw_relations[] =
{
0x8684, 0x55, 0x8a84, 0xd5, 0x8e84, 0x15, 0x9284, 0x95,
0x9684, 0x75, 0x9a84, 0xf5, 0x9e84, 0x35, 0xa284, 0xb5,
0xa684, 0x45, 0xaa84, 0xc5, 0xae84, 0x05, 0xb284, 0x85,
0xb684, 0x65, 0xba84, 0xe5, 0xbe84, 0x25, 0xc184, 0xa5,
0xc384, 0x5d, 0xc584, 0xdd, 0xc784, 0x1d, 0xc984, 0x9d,
0xcb84, 0x7d, 0xcd84, 0xfd, 0xcf84, 0x3d, 0xd184, 0xbd,
0xd384, 0x4d, 0xd584, 0xcd, 0xd784, 0x0d, 0xd984, 0x8d,
0xdb84, 0x6d, 0xdd84, 0xed, 0xdf84, 0x2d, 0xe104, 0xad,
0xe204, 0x51, 0xe304, 0xd1, 0xe404, 0x11, 0xe504, 0x91,
0xe604, 0x71, 0xe704, 0xf1, 0xe804, 0x31, 0xe904, 0xb1,
0xea04, 0x41, 0xeb04, 0xc1, 0xec04, 0x01, 0xed04, 0x81,
0xee04, 0x61, 0xef04, 0xe1, 0xf004, 0x21, 0xf0c4, 0x59,
0xf0c4, 0xa1, 0xf144, 0xd9, 0xf1c4, 0x19, 0xf244, 0x99,
0xf2c4, 0x79, 0xf344, 0xf9, 0xf3c4, 0x39, 0xf444, 0xb9,
0xf4c4, 0x49, 0xf544, 0xc9, 0xf5c4, 0x09, 0xf644, 0x89,
0xf6c4, 0x69, 0xf744, 0xe9, 0xf7c4, 0x29, 0xf844, 0x57,
0xf844, 0xa9, 0xf8a4, 0xd7, 0xf8e4, 0x17, 0xf924, 0x97,
0xf964, 0x77, 0xf9a4, 0xf7, 0xf9e4, 0x37, 0xfa24, 0xb7,
0xfa64, 0x47, 0xfaa4, 0xc7, 0xfae4, 0x07, 0xfb24, 0x87,
0xfb64, 0x67, 0xfba4, 0xe7, 0xfbe4, 0x27, 0xfc24, 0x5f,
0xfc24, 0xa7, 0xfc64, 0x1f, 0xfc64, 0xdf, 0xfc94, 0x9f,
0xfcb4, 0x7f, 0xfcd4, 0xff, 0xfcf4, 0x3f, 0xfd14, 0xbf,
0xfd34, 0x4f, 0xfd54, 0xcf, 0xfd74, 0x0f, 0xfd94, 0x8f,
0xfdb4, 0x6f, 0xfdd4, 0xef, 0xfdf4, 0x2f, 0xfe14, 0x53,
0xfe14, 0xaf, 0xfe34, 0x13, 0xfe34, 0xd3, 0xfe54, 0x73,
0xfe54, 0x93, 0xfe74, 0x33, 0xfe74, 0xf3, 0xfe8c, 0xb3,
0xfe9c, 0x43, 0xfeac, 0xc3, 0xfebc, 0x03, 0xfecc, 0x83,
0xfedc, 0x63, 0xfeec, 0xe3, 0xfefc, 0x23, 0xff0c, 0xa3,
0xff1c, 0x5b, 0xff2c, 0xdb, 0xff3c, 0x1b, 0xff4c, 0x9b,
0xff5c, 0x7b, 0xff6c, 0xfb, 0xff7c, 0x3b, 0xff88, 0xbb,
0xff98, 0x4b, 0xffa8, 0xcb, 0xffb8, 0x0b, 0xffc8, 0x8b,
0xffd8, 0x6b, 0xffe8, 0xeb, 0xfff8, 0x2b, 0xfff8, 0xab,
0x0008, 0x2a, 0x0008, 0xaa, 0x0018, 0xea, 0x0028, 0x6a,
0x0038, 0x8a, 0x0048, 0x0a, 0x0058, 0xca, 0x0068, 0x4a,
0x0078, 0xba, 0x0084, 0x3a, 0x0094, 0xfa, 0x00a4, 0x7a,
0x00b4, 0x9a, 0x00c4, 0x1a, 0x00d4, 0xda, 0x00e4, 0x5a,
0x00f4, 0xa2, 0x0104, 0x22, 0x0114, 0xe2, 0x0124, 0x62,
0x0134, 0x82, 0x0144, 0x02, 0x0154, 0xc2, 0x0164, 0x42,
0x0174, 0xb2, 0x018c, 0x32, 0x018c, 0xf2, 0x01ac, 0x72,
0x01ac, 0x92, 0x01cc, 0x12, 0x01cc, 0xd2, 0x01ec, 0x52,
0x01ec, 0xae, 0x020c, 0x2e, 0x022c, 0xee, 0x024c, 0x6e,
0x026c, 0x8e, 0x028c, 0x0e, 0x02ac, 0xce, 0x02cc, 0x4e,
0x02ec, 0xbe, 0x030c, 0x3e, 0x032c, 0xfe, 0x034c, 0x7e,
0x036c, 0x9e, 0x039c, 0x1e, 0x039c, 0xde, 0x03dc, 0x5e,
0x03dc, 0xa6, 0x041c, 0x26, 0x045c, 0xe6, 0x049c, 0x66,
0x04dc, 0x86, 0x051c, 0x06, 0x055c, 0xc6, 0x059c, 0x46,
0x05dc, 0xb6, 0x061c, 0x36, 0x065c, 0xf6, 0x069c, 0x76,
0x06dc, 0x96, 0x071c, 0x16, 0x075c, 0xd6, 0x07bc, 0x56,
0x07bc, 0xa8, 0x083c, 0x28, 0x08bc, 0xe8, 0x093c, 0x68,
0x09bc, 0x88, 0x0a3c, 0x08, 0x0abc, 0xc8, 0x0b3c, 0x48,
0x0bbc, 0xb8, 0x0c3c, 0x38, 0x0cbc, 0xf8, 0x0d3c, 0x78,
0x0dbc, 0x98, 0x0e3c, 0x18, 0x0ebc, 0xd8, 0x0f3c, 0x58,
0x0f3c, 0xa0, 0x0ffc, 0x20, 0x10fc, 0xe0, 0x11fc, 0x60,
0x12fc, 0x80, 0x13fc, 0x00, 0x14fc, 0xc0, 0x15fc, 0x40,
0x16fc, 0xb0, 0x17fc, 0x30, 0x18fc, 0xf0, 0x19fc, 0x70,
0x1afc, 0x90, 0x1bfc, 0x10, 0x1cfc, 0xd0, 0x1dfc, 0x50,
0x1efc, 0xac, 0x207c, 0x2c, 0x227c, 0xec, 0x247c, 0x6c,
0x267c, 0x8c, 0x287c, 0x0c, 0x2a7c, 0xcc, 0x2c7c, 0x4c,
0x2e7c, 0xbc, 0x307c, 0x3c, 0x327c, 0xfc, 0x347c, 0x7c,
0x367c, 0x9c, 0x387c, 0x1c, 0x3a7c, 0xdc, 0x3c7c, 0x5c,
0x3e7c, 0xa4, 0x417c, 0x24, 0x457c, 0xe4, 0x497c, 0x64,
0x4d7c, 0x84, 0x517c, 0x04, 0x557c, 0xc4, 0x597c, 0x44,
0x5d7c, 0xb4, 0x617c, 0x34, 0x657c, 0xf4, 0x697c, 0x74,
0x6d7c, 0x94, 0x717c, 0x14, 0x757c, 0xd4, 0x797c, 0x54
};
uint8_t g711_flip[256];
static int g711_initialized = 0;
/* generate tables
*/
void g711_init(void)
{
int i, j;
/* flip tables */
for (i = 0; i < 256; i++) {
g711_flip[i]
= ((i & 1) << 7)
+ ((i & 2) << 5)
+ ((i & 4) << 3)
+ ((i & 8) << 1)
+ ((i & 16) >> 1)
+ ((i & 32) >> 3)
+ ((i & 64) >> 5)
+ ((i & 128) >> 7);
g711_alaw_to_linear[i] = g711_alaw_flipped_to_linear[g711_flip[i]];
g711_ulaw_to_linear[i] = g711_ulaw_flipped_to_linear[g711_flip[i]];
}
/* linear to alaw tables */
i = j = 0;
while(i < 65536) {
if (i - 32768 > g711_alaw_relations[j << 1])
j++;
if (j > 255)
j = 255;
g711_linear_to_alaw_flipped[(i - 32768) & 0xffff] = g711_alaw_relations[(j << 1) | 1];
g711_linear_to_alaw[(i - 32768) & 0xffff] = g711_flip[g711_alaw_relations[(j << 1) | 1]];
i++;
}
/* linear to ulaw tables */
i = j = 0;
while(i < 32768) {
if (i - 32768 > g711_ulaw_flipped_to_linear[j])
j++;
g711_linear_to_ulaw_flipped[(i - 32768) & 0xffff] = j;
g711_linear_to_ulaw[(i - 32768) & 0xffff] = g711_flip[j];
i++;
}
j = 255;
while(i < 65536) {
if (i - 32768 > g711_alaw_flipped_to_linear[j])
j--;
g711_linear_to_ulaw_flipped[(i - 32768) & 0xffff] = j;
g711_linear_to_ulaw[(i - 32768) & 0xffff] = g711_flip[j];
i++;
}
/* transcode */
for (i = 0; i < 256; i++) {
g711_alaw_to_ulaw[i] = g711_linear_to_ulaw[(uint16_t)g711_alaw_to_linear[i]];
g711_ulaw_to_alaw[i] = g711_linear_to_alaw[(uint16_t)g711_ulaw_to_linear[i]];
g711_alaw_flipped_to_ulaw[i] = g711_linear_to_ulaw[(uint16_t)g711_alaw_to_linear[g711_flip[i]]];
g711_ulaw_flipped_to_alaw[i] = g711_linear_to_alaw[(uint16_t)g711_ulaw_to_linear[g711_flip[i]]];
g711_alaw_to_ulaw_flipped[i] = g711_flip[g711_linear_to_ulaw[(uint16_t)g711_alaw_to_linear[i]]];
g711_ulaw_to_alaw_flipped[i] = g711_flip[g711_linear_to_alaw[(uint16_t)g711_ulaw_to_linear[i]]];
}
g711_initialized = 1;
}
void g711_encode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
int16_t *src = (int16_t *)src_data;
uint8_t *dst;
int len = src_len / 2, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_linear_to_alaw_flipped[(uint16_t)src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_encode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
int16_t *src = (int16_t *)src_data;
uint8_t *dst;
int len = src_len / 2, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_linear_to_ulaw_flipped[(uint16_t)src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_decode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data;
int16_t *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len * 2);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_alaw_flipped_to_linear[src[i]];
*dst_data = (uint8_t *)dst;
*dst_len = len * 2;
}
void g711_decode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data;
int16_t *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len * 2);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_ulaw_flipped_to_linear[src[i]];
*dst_data = (uint8_t *)dst;
*dst_len = len * 2;
}
void g711_encode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
int16_t *src = (int16_t *)src_data;
uint8_t *dst;
int len = src_len / 2, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_linear_to_alaw[(uint16_t)src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_encode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
int16_t *src = (int16_t *)src_data;
uint8_t *dst;
int len = src_len / 2, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_linear_to_ulaw[(uint16_t)src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_decode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data;
int16_t *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len * 2);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_alaw_to_linear[src[i]];
*dst_data = (uint8_t *)dst;
*dst_len = len * 2;
}
void g711_decode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data;
int16_t *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len * 2);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_ulaw_to_linear[src[i]];
*dst_data = (uint8_t *)dst;
*dst_len = len * 2;
}
void g711_transcode_alaw_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_alaw_to_ulaw[src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_transcode_alaw_flipped_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_alaw_flipped_to_ulaw[src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_transcode_alaw_to_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_alaw_to_ulaw_flipped[src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_transcode_ulaw_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_ulaw_to_alaw[src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_transcode_ulaw_flipped_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_ulaw_flipped_to_alaw[src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_transcode_ulaw_to_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_ulaw_to_alaw_flipped[src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_transcode_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_flip[src[i]];
*dst_data = dst;
*dst_len = len;
}

View File

@ -1,17 +0,0 @@
void g711_init(void);
void g711_encode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_encode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_decode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_decode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_encode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_encode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_decode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_decode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_alaw_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_alaw_flipped_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_alaw_to_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_ulaw_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_ulaw_flipped_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_ulaw_to_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);

View File

@ -1,6 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libgoertzel.a
libgoertzel_a_SOURCES = \
goertzel.c

View File

@ -1,120 +0,0 @@
/* Goertzel functions
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "goertzel.h"
/*
* audio level calculation
*/
/* Return average value (rectified value)
* The input must not have any dc offset!
* For a perfect rectangualr wave, the result would equal the peak level.
* For a sine wave the result would be factor (2 / PI) below peak level.
*/
double audio_mean_level(sample_t *samples, int length)
{
double level, sk;
int n;
/* level calculation */
level = 0;
for (n = 0; n < length; n++) {
sk = samples[n];
if (sk < 0)
level -= (double)sk;
if (sk > 0)
level += (double)sk;
}
level = level / (double)length;
return level;
}
/* use hamming window */
double window[256];
int window_generated = 0;
static void gen_window(void)
{
int i;
for (i = 0; i < 256; i++)
window[i] = 0.54 - 0.46 * cos(2.0 * M_PI * (double)i / 256.0);
window_generated = 1;
}
void audio_goertzel_init(goertzel_t *goertzel, double freq, int samplerate)
{
if (!window_generated)
gen_window();
memset(goertzel, 0, sizeof(*goertzel));
goertzel->coeff = 2.0 * cos(2.0 * M_PI * freq / (double)samplerate);
}
/*
* goertzel filter
*/
/* filter frequencies and return their levels
*
* samples: pointer to sample buffer
* length: length of buffer: -7.4 dB @ +-(1 / duration) Hz and -INF @ +-(1 / duration * 2) Hz
* -> if duration is 10 ms, we got -7.4 dB @ +-100 Hz and -INF at +-200 Hz
* offset: for ring buffer, start here and wrap around to 0 when length has been hit
* coeff: array of coefficients (coeff << 15)
* result: array of result levels (peak value of the target frequency)
* k: number of frequencies to check
*/
void audio_goertzel(goertzel_t *goertzel, sample_t *samples, int length, int offset, double *result, int k)
{
double sk, sk1, sk2;
double cos2pik;
int i, n;
/* we do goertzel */
for (i = 0; i < k; i++) {
sk = 0;
sk1 = 0;
sk2 = 0;
cos2pik = goertzel[i].coeff;
/* note: after 'length' cycles, offset is restored to its initial value */
for (n = 0; n < length; n++) {
sk = (cos2pik * sk1) - sk2 + samples[offset++] * window[n * 256 / length];
sk2 = sk1;
sk1 = sk;
if (offset == length)
offset = 0;
}
/* compute level of signal */
result[i] = sqrt(
(sk * sk) -
(cos2pik * sk * sk2) +
(sk2 * sk2)
) / (double)length * 4 / 1.08;
}
}

View File

@ -1,10 +0,0 @@
double audio_mean_level(sample_t *samples, int length);
typedef struct goertzel {
double coeff;
} goertzel_t;
void audio_goertzel_init(goertzel_t *goertzel, double freq, int samplerate);
void audio_goertzel(goertzel_t *goertzel, sample_t *samples, int length, int offset, double *result, int k);

View File

@ -1,6 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libhagelbarger.a
libhagelbarger_a_SOURCES = \
hagelbarger.c

View File

@ -1,101 +0,0 @@
/* Hagelbarger (6,19) code
*
* A burst up to 6 encoded bits may be corrupt, to correct them.
* After corrupt bits, a minimum of 19 bits must be correct to correct
* another burst of corrupted bits.
*
* There is no parity check, so it is required to check all information
* elements of each message. With NMT System: Messages that contain signals
* or digits are protected by repeating the digits in the information element.
*
* (C) 2017 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 "stdint.h"
/* enable to debug the process of parity check */
//#define DEBUG_HAGEL
/* To encode NMT message: (MSB first)
* Use input with 9 bytes, the last byte must be 0x00.
* Use output with 18 bytes, ignore the last four (lower) bits of last byte.
* Use length of 70.
*/
void hagelbarger_encode(const uint8_t *input, uint8_t *output, int length)
{
uint8_t reg = 0x00, data, check;
int i;
for (i = 0; i < length; i++) {
/* get data from input (MSB first) */
data = (input[i / 8] >> (7 - (i & 7))) & 1;
/* push data into shift register (LSB first) */
reg = (reg << 1) | data;
/* get data bit from register */
data = (reg >> 6) & 1;
/* calc check bit from register */
check = (reg + (reg >> 3) + 1) & 1;
/* put check bit and data bit to output (MSB first) */
output[i / 4] = (output[i / 4] << 2) | (check << 1) | data;
}
/* shift last output byte all the way to MSB */
while ((i % 4))
output[i++ / 4] <<= 2;
}
/* To decode NMT message: (MSB first)
* Use input with 19 bytes, the unused last 12 (lower) bits must be zero.
* Use output with 8 bytes.
* Use length of 64.
*/
void hagelbarger_decode(const uint8_t *input, uint8_t *output, int length)
{
uint16_t reg_data = 0x00, reg_check = 0xff, data, check, r_parity, s_parity;
int i, o;
length += 10;
for (i = 0, o = 0; i < length; i++) {
/* get check bit from input (MSB first) */
check = (input[i / 4] >> (7 - (i & 3) * 2)) & 1;
/* get data bit from input (MSB first) */
data = (input[i / 4] >> (6 - (i & 3) * 2)) & 1;
/* push check bit into shift register (LSB first) */
reg_check = (reg_check << 1) | check;
/* push data bit into shift register (LSB first) */
reg_data = (reg_data << 1) | data;
/* calculate parity */
r_parity = (reg_data + (reg_data >> 3) + (reg_check >> 6) + 1) & 1;
s_parity = ((reg_data >> 3) + (reg_data >> 6) + (reg_check >> 9) + 1) & 1;
#ifdef DEBUG_HAGEL
printf("#%d: r=%d s=%d\n", i - 10, r_parity, s_parity);
#endif
/* flip message bit, if both parity checks fail */
/* use 4th bit that will be shifted to 5th bit next loop */
if (r_parity && s_parity)
reg_data ^= 0x0008;
/* put message bit to output (MSB first) */
if (i >= 10) {
output[o / 8] = (output[o / 8] << 1) | ((reg_data >> 4) & 1);
o++;
}
}
/* shift last output byte all the way to MSB */
while ((o % 8))
output[o++ / 8] <<= 1;
}

View File

@ -1,4 +0,0 @@
void hagelbarger_encode(const uint8_t *input, uint8_t *output, int length);
void hagelbarger_decode(const uint8_t *input, uint8_t *output, int length);

View File

@ -1,11 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) $(IMAGEMAGICK_CFLAGS)
noinst_LIBRARIES = libimage.a
libimage_a_SOURCES = \
img.c
if HAVE_MAGICK
AM_CPPFLAGS += -DHAVE_MAGICK
endif

View File

@ -1,394 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "img.h"
int save_depth = 16;
#ifdef HAVE_MAGICK
#include <MagickCore/MagickCore.h>
/* load given image to memory. return short RGB values */
unsigned short *load_img(int *width, int *height, const char *filename, int index)
{
Image *image = NULL;
ImageInfo *imageinfo = NULL;
ExceptionInfo *exception;
unsigned short *img = NULL;
MagickCoreGenesis(NULL, MagickFalse);
// InitializeMagick(NULL);
imageinfo = CloneImageInfo(0);
exception = AcquireExceptionInfo();
sprintf(imageinfo->filename, filename, index);
image = ReadImage(imageinfo, exception);
if (!image) {
// printf("failed to read image '%s' via *magick\n", filename);
goto exit;
}
*width = image->columns;
*height = image->rows;
img = (unsigned short *)malloc((*width) * (*height) * 3 * 2);
if (!img) {
printf("%s:failed to allocate image data\n", __func__);
goto exit;
}
ExportImagePixels(image, 0, 0, *width, *height, "RGB", ShortPixel, img, NULL);
// DispatchImage(image, 0, 0, *width, *height, "RGB", ShortPixel, img, NULL);
exit:
if (image)
DestroyImage(image);
if (imageinfo)
DestroyImageInfo(imageinfo);
if (exception)
DestroyExceptionInfo(exception);
MagickCoreTerminus();
// DestroyMagick();
return img;
}
/* save given image */
int save_img(unsigned short *img, int width, int height, int alpha, const char *filename, int index)
{
int rc = -1;
Image *image = NULL;
ImageInfo *imageinfo = NULL;
ExceptionInfo *exception;
MagickCoreGenesis(NULL, MagickFalse);
// InitializeMagick(NULL);
imageinfo = CloneImageInfo(0);
exception = AcquireExceptionInfo();
imageinfo->quality = 100;
if (strlen(filename) >= 4 && !strcmp(filename + strlen(filename) - 4, ".png"))
imageinfo->quality = 1;
image=ConstituteImage(width, height, (alpha)?"RGBA":"RGB", ShortPixel, img, exception);
if (!image) {
printf("%s:failed to prepare to write image\n", __func__);
goto exit;
}
/* store as 16 bit, if lib and format supports it */
image->depth = save_depth;
sprintf(image->filename, filename, index); /* ACHTUNG: nicht imageinfo!!! */
if (!WriteImage(imageinfo, image, exception)) {
printf("%s:failed to write image\n", __func__);
goto exit;
}
rc = 0;
exit:
if (image)
DestroyImage(image);
if (imageinfo)
DestroyImageInfo(imageinfo);
if (exception)
DestroyExceptionInfo(exception);
MagickCoreTerminus();
// DestroyMagick();
return rc;
}
#else
/* load given image to memory. return short RGB values */
unsigned short *load_img(int *width, int *height, const char *filename, int index)
{
FILE *fp = NULL;
unsigned short *img = NULL;
char line[256];
int words, i;
sprintf(line, filename, index);
// printf("reading image: %s\n", line);
fp = fopen(line, "r");
if (!fp) {
// printf("failed to read ppm image '%s'\n", filename);
goto exit;
}
again1:
if (!fgets(line, sizeof(line), fp)) {
printf("%s:failed to read image depth\n", __func__);
goto exit;
}
line[sizeof(line)-1] = '\0';
if (line[0]) line[strlen(line)-1] = '\0';
if (line[0] == '#')
goto again1;
if (!!strcmp(line, "P6")) {
printf("%s:expecting image depth 'P6'\n", __func__);
goto exit;
}
again2:
if (!fgets(line, sizeof(line), fp)) {
printf("%s:failed to read image size\n", __func__);
goto exit;
}
line[sizeof(line)-1] = '\0';
if (line[0]) line[strlen(line)-1] = '\0';
if (line[0] == '#')
goto again2;
sscanf(line, "%d %d", width, height);
// printf("Image size: w=%d h=%d\n", *width, *height);
again3:
if (!fgets(line, sizeof(line), fp)) {
printf("%s:failed to read line '255' or '65535'\n", __func__);
goto exit;
}
line[sizeof(line)-1] = '\0';
if (line[0]) line[strlen(line)-1] = '\0';
if (line[0] == '#')
goto again3;
if (!strcmp(line, "255")) {
words = 1;
} else
if (!strcmp(line, "65535")) {
words = 2;
} else {
printf("%s:expecting line '255' or '65535'\n", __func__);
goto exit;
}
img = (unsigned short *)malloc((*width) * (*height) * 3 * 2);
if (!img) {
printf("%s:failed to allocate image data\n", __func__);
goto exit;
}
if (fread(img, (*width) * (*height) * 3 * words, 1, fp) != 1) {
printf("%s:failed to read image data\n", __func__);
goto exit;
}
/* char to short (255 -> 65535) */
if (words == 1) {
unsigned char *from = (unsigned char *)img, c;
for (i = (*width) * (*height) * 3 - 1; i >= 0; i--) {
c = from[i];
img[i] = (c << 8) | c;
}
} else {
/* correct byte order */
unsigned short v;
unsigned char *from = (unsigned char *)img;
for (i = 0; i < (*width) * (*height) * 3; i++) {
v = ((*from++) << 8);
v |= (*from++);
img[i] = v;
}
}
exit:
if (fp)
fclose(fp);
return img;
}
/* save given image */
int save_img(unsigned short *img, int width, int height, int alpha, const char *filename, int index)
{
FILE *fp = NULL;
int rc = -1;
char line[256];
int i;
unsigned short v;
unsigned char *to;
if (alpha) {
printf("%s:cannot save alpha component with PPM support only\n", __func__);
alpha = 0;
goto exit;
}
sprintf(line, filename, index);
// printf("writing image: %s\n", line);
fp = fopen(line, "w");
if (!fp) {
printf("%s:failed to write image\n", __func__);
goto exit;
}
fprintf(fp, "P6\n%d %d\n65535\n", width, height);
/* correct byte order, write and restore byte order */
to = (unsigned char *)img;
for (i = 0; i < width * height * 3; i++) {
v = img[i];
if (i/100*i == i) { printf("%04x ", v); }
(*to++) = v >> 8;
(*to++) = v;
}
rc = fwrite(img, width * height * 3 * 2, 1, fp);
to = (unsigned char *)img;
for (i = 0; i < width * height * 3; i++) {
v = (*to++) << 8;
v |= (*to++);
img[i] = v;
}
if (rc != 1) {
printf("%s:failed to write image data\n", __func__);
goto exit;
}
rc = 0;
exit:
if (fp)
fclose(fp);
return rc;
}
#endif
int save_img_array(double *array, int width, int height, int alpha, const char *filename, int index)
{
int rc = -1;
unsigned short *img = NULL;
int components;
#ifndef HAVE_MAGICK
if (alpha) {
printf("%s:warning, cannot save alpha component with PPM support only\n", __func__);
alpha = 0;
}
#endif
components = (alpha) ? 4 : 3;
img = (unsigned short *)malloc(width * height * components * 2);
if (!img) {
printf("%s:failed to allocate image data\n", __func__);
goto exit;
}
array2img_short(array, width, height, img, width, height, alpha);
save_img(img, width, height, alpha, filename, index);
rc = 0;
exit:
if (img)
free(img);
return rc;
}
/* convert an image to a three dimensional array of double
* the size is: width, height, 3
*/
void img2array_short(unsigned short *img, int iw, int ih, double *array, int aw, int ah)
{
int x, y;
int channel;
double r, g, b;
channel = aw * ah;
for (y = 0; y < ih; y++) {
for (x = 0; x < iw; x++) {
r = img[(x+iw*y)*3] / 65535.0F;
g = img[(x+iw*y)*3+1] / 65535.0F;
b = img[(x+iw*y)*3+2] / 65535.0F;
array[x+aw*y] = r;
array[x+aw*y+channel] = g;
array[x+aw*y+channel+channel] = b;
}
}
}
/* convert a three dimensional array of double to an image
* the size is: width, height, 3
*/
void array2img_short(double *array, int aw, int ah, unsigned short *img, int iw, int ih, int alpha)
{
int x, y, c;
int channel, components;
double r, g, b, a;
channel = aw * ah;
components = (alpha) ? 4 : 3;
for (y = 0; y < ih; y++) {
for (x = 0; x < iw; x++) {
r = array[x+aw*y];
c = (r * 65535.0F + 0.5F);
if (c < 0)
c = 0;
else if (c > 65535)
c = 65535;
img[(x+iw*y)*components] = c;
g = array[x+aw*y+channel];
c = (g * 65535.0F + 0.5F);
if (c < 0)
c = 0;
else if (c > 65535)
c = 65535;
img[(x+iw*y)*components+1] = c;
b = array[x+aw*y+channel+channel];
c = (b * 65535.0F + 0.5F);
if (c < 0)
c = 0;
else if (c > 65535)
c = 65535;
img[(x+iw*y)*components+2] = c;
if (alpha) {
a = array[x+aw*y+channel+channel+channel];
c = (a * 65535.0F + 0.5F);
if (c < 0)
c = 0;
else if (c > 65535)
c = 65535;
img[(x+iw*y)*components+3] = c;
}
}
}
}
/*
* scale down image in img_buffer by calculating average
*/
void scale_img(unsigned short *img, int width, int height, int scale)
{
int w, h, i, j, x, y;
int r, g, b;
if (scale == 1)
return;
w = width / scale;
h = height / scale;
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
r = g = b = 0;
for (y = 0; y < scale; y++) {
for (x = 0; x < scale; x++) {
r += img[((i*scale+y) * width + j*scale+x) * 3 + 0];
g += img[((i*scale+y) * width + j*scale+x) * 3 + 1];
b += img[((i*scale+y) * width + j*scale+x) * 3 + 2];
}
}
img[(i * w + j)*3 + 0] = r / scale / scale;
img[(i * w + j)*3 + 1] = g / scale / scale;
img[(i * w + j)*3 + 2] = b / scale / scale;
}
}
}

View File

@ -1,7 +0,0 @@
extern int save_depth;
unsigned short *load_img(int *width, int *height, const char *filename, int index);
int save_img(unsigned short *img, int width, int height, int alpha, const char *filename, int index);
int save_img_array(double *array, int width, int height, int alpha, const char *filename, int index);
void img2array_short(unsigned short *img, int iw, int ih, double *array, int aw, int ah);
void array2img_short(double *array, int aw, int ah, unsigned short *img, int iw, int ih, int alpha);
void scale_img(unsigned short *img, int width, int height, int scale);

View File

@ -1,6 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libjitter.a
libjitter_a_SOURCES = \
jitter.c

View File

@ -1,125 +0,0 @@
/* Jitter buffering functions
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "jitter.h"
/* create jitter buffer */
int jitter_create(jitter_t *jitter, int length)
{
memset(jitter, 0, sizeof(*jitter));
jitter->spl = malloc(length * sizeof(sample_t));
if (!jitter->spl) {
PDEBUG(DDSP, DEBUG_ERROR, "No memory for jitter buffer.\n");
return -ENOMEM;
}
jitter->len = length;
jitter_reset(jitter);
return 0;
}
void jitter_reset(jitter_t *jitter)
{
memset(jitter->spl, 0, jitter->len * sizeof(sample_t));
/* put write pointer ahead by half of the buffer length */
jitter->inptr = jitter->len / 2;
}
void jitter_destroy(jitter_t *jitter)
{
if (jitter->spl) {
free(jitter->spl);
jitter->spl = NULL;
}
}
/* store audio in jitterbuffer
*
* stop if buffer is completely filled
*/
void jitter_save(jitter_t *jb, sample_t *samples, int length)
{
sample_t *spl;
int inptr, outptr, len, space;
int i;
spl = jb->spl;
inptr = jb->inptr;
outptr = jb->outptr;
len = jb->len;
space = (outptr - inptr + len - 1) % len;
if (space < length)
length = space;
for (i = 0; i < length; i++) {
spl[inptr++] = *samples++;
if (inptr == len)
inptr = 0;
}
jb->inptr = inptr;
}
/* get audio from jitterbuffer
*/
void jitter_load(jitter_t *jb, sample_t *samples, int length)
{
sample_t *spl;
int inptr, outptr, len, fill;
int i, ii;
spl = jb->spl;
inptr = jb->inptr;
outptr = jb->outptr;
len = jb->len;
fill = (inptr - outptr + len) % len;
if (fill < length)
ii = fill;
else
ii = length;
/* fill what we got */
for (i = 0; i < ii; i++) {
*samples++ = spl[outptr++];
if (outptr == len)
outptr = 0;
}
/* on underrun, fill with silence */
for (; i < length; i++) {
*samples++ = 0;
}
jb->outptr = outptr;
}
void jitter_clear(jitter_t *jb)
{
jb->inptr = jb->outptr = 0;
}

View File

@ -1,14 +0,0 @@
typedef struct jitter {
sample_t *spl; /* pointer to sample buffer */
int len; /* buffer size: number of samples */
int inptr, outptr; /* write pointer and read pointer */
} jitter_t;
int jitter_create(jitter_t *jitter, int length);
void jitter_reset(jitter_t *jitter);
void jitter_destroy(jitter_t *jitter);
void jitter_save(jitter_t *jb, sample_t *samples, int length);
void jitter_load(jitter_t *jb, sample_t *samples, int length);
void jitter_clear(jitter_t *jb);

View File

@ -1,20 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libmobile.a
libmobile_a_SOURCES = \
sender.c \
call.c \
console.c \
testton.c \
cause.c \
main_mobile.c
if HAVE_ALSA
AM_CPPFLAGS += -DHAVE_ALSA
endif
if HAVE_SDR
AM_CPPFLAGS += -DHAVE_SDR
endif

View File

@ -1,942 +0,0 @@
/* interface between mobile network/phone implementation and OsmoCC
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "../libtimer/timer.h"
#include "../libosmocc/endpoint.h"
#include "../libosmocc/helper.h"
#include "../libg711/g711.h"
#include "cause.h"
#include "sender.h"
#include "call.h"
#include "main_mobile.h"
#include "console.h"
#define DISC_TIMEOUT 30
//#define DEBUG_LEVEL
#ifdef DEBUG_LEVEL
static double level_of(double *samples, int count)
{
double level = 0;
int i;
for (i = 0; i < count; i++) {
if (samples[i] > level)
level = samples[i];
}
return level;
}
#endif
static int connect_on_setup; /* send patterns towards fixed network */
static int release_on_disconnect; /* release towards mobile phone, if OSMO-CC call disconnects, don't send disconnect tone */
osmo_cc_endpoint_t endpoint, *ep;
void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint16_t *src = (uint16_t *)src_data, *dst;
int len = src_len / 2, i;
dst = malloc(len * 2);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = htons(src[i]);
*dst_data = (uint8_t *)dst;
*dst_len = len * 2;
}
void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint16_t *src = (uint16_t *)src_data, *dst;
int len = src_len / 2, i;
dst = malloc(len * 2);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = ntohs(src[i]);
*dst_data = (uint8_t *)dst;
*dst_len = len * 2;
}
static struct osmo_cc_helper_audio_codecs codecs[] = {
{ "L16", 8000, 1, encode_l16, decode_l16 },
{ "PCMA", 8000, 1, g711_encode_alaw, g711_decode_alaw },
{ "PCMU", 8000, 1, g711_encode_ulaw, g711_decode_ulaw },
{ NULL, 0, 0, NULL, NULL},
};
/* stream patterns/announcements */
int16_t *ringback_spl = NULL;
int ringback_size = 0;
int ringback_max = 0;
int16_t *hangup_spl = NULL;
int hangup_size = 0;
int hangup_max = 0;
int16_t *busy_spl = NULL;
int busy_size = 0;
int busy_max = 0;
int16_t *noanswer_spl = NULL;
int noanswer_size = 0;
int noanswer_max = 0;
int16_t *outoforder_spl = NULL;
int outoforder_size = 0;
int outoforder_max = 0;
int16_t *invalidnumber_spl = NULL;
int invalidnumber_size = 0;
int invalidnumber_max = 0;
int16_t *congestion_spl = NULL;
int congestion_size = 0;
int congestion_max = 0;
int16_t *recall_spl = NULL;
int recall_size = 0;
int recall_max = 0;
enum audio_pattern {
PATTERN_NONE = 0,
PATTERN_TEST,
PATTERN_RINGBACK,
PATTERN_HANGUP,
PATTERN_BUSY,
PATTERN_NOANSWER,
PATTERN_OUTOFORDER,
PATTERN_INVALIDNUMBER,
PATTERN_CONGESTION,
PATTERN_RECALL,
};
static void get_pattern(const int16_t **spl, int *size, int *max, enum audio_pattern pattern)
{
*spl = NULL;
*size = 0;
*max = 0;
switch (pattern) {
case PATTERN_RINGBACK:
no_recall:
*spl = ringback_spl;
*size = ringback_size;
*max = ringback_max;
break;
case PATTERN_HANGUP:
if (!hangup_spl)
goto no_hangup;
*spl = hangup_spl;
*size = hangup_size;
*max = hangup_max;
break;
case PATTERN_BUSY:
no_hangup:
no_noanswer:
*spl = busy_spl;
*size = busy_size;
*max = busy_max;
break;
case PATTERN_NOANSWER:
if (!noanswer_spl)
goto no_noanswer;
*spl = noanswer_spl;
*size = noanswer_size;
*max = noanswer_max;
break;
case PATTERN_OUTOFORDER:
if (!outoforder_spl)
goto no_outoforder;
*spl = outoforder_spl;
*size = outoforder_size;
*max = outoforder_max;
break;
case PATTERN_INVALIDNUMBER:
if (!invalidnumber_spl)
goto no_invalidnumber;
*spl = invalidnumber_spl;
*size = invalidnumber_size;
*max = invalidnumber_max;
break;
case PATTERN_CONGESTION:
no_outoforder:
no_invalidnumber:
*spl = congestion_spl;
*size = congestion_size;
*max = congestion_max;
break;
case PATTERN_RECALL:
if (!recall_spl)
goto no_recall;
*spl = recall_spl;
*size = recall_size;
*max = recall_max;
break;
default:
;
}
}
static enum audio_pattern cause2pattern(int cause)
{
int pattern;
switch (cause) {
case CAUSE_NORMAL:
pattern = PATTERN_HANGUP;
break;
case CAUSE_BUSY:
pattern = PATTERN_BUSY;
break;
case CAUSE_NOANSWER:
pattern = PATTERN_NOANSWER;
break;
case CAUSE_OUTOFORDER:
pattern = PATTERN_OUTOFORDER;
break;
case CAUSE_INVALNUMBER:
pattern = PATTERN_INVALIDNUMBER;
break;
case CAUSE_NOCHANNEL:
pattern = PATTERN_CONGESTION;
break;
default:
pattern = PATTERN_HANGUP;
}
return pattern;
}
enum process_state {
PROCESS_IDLE = 0, /* IDLE */
PROCESS_SETUP_RO, /* call from radio to OSMO-CC */
PROCESS_SETUP_RT, /* call from OSMO-CC to radio */
PROCESS_ALERTING_RO, /* call from radio to OSMO-CC */
PROCESS_ALERTING_RT, /* call from OSMO-CC to radio */
PROCESS_CONNECT,
PROCESS_DISCONNECT,
};
/* call process */
typedef struct process {
struct process *next;
int callref;
enum process_state state;
int audio_disconnected; /* if not associated with transceiver anymore */
enum audio_pattern pattern;
int audio_pos;
uint8_t cause;
struct timer timer;
osmo_cc_session_t *session;
osmo_cc_session_codec_t *codec; /* codec to send */
} process_t;
static process_t *process_head = NULL;
static void process_timeout(struct timer *timer);
static void indicate_disconnect_release(int callref, int cause, uint8_t msg_type);
static process_t *create_process(int callref, enum process_state state)
{
process_t *process;
process = calloc(sizeof(*process), 1);
if (!process) {
PDEBUG(DCALL, DEBUG_ERROR, "No memory!\n");
abort();
}
timer_init(&process->timer, process_timeout, process);
process->next = process_head;
process_head = process;
process->callref = callref;
process->state = state;
return process;
}
static void destroy_process(int callref)
{
process_t *process = process_head;
process_t **process_p = &process_head;
while (process) {
if (process->callref == callref) {
*process_p = process->next;
timer_exit(&process->timer);
if (process->session)
osmo_cc_free_session(process->session);
free(process);
return;
}
process_p = &process->next;
process = process->next;
}
PDEBUG(DCALL, DEBUG_ERROR, "Process with callref %d not found!\n", callref);
}
static process_t *get_process(int callref)
{
process_t *process = process_head;
while (process) {
if (process->callref == callref)
return process;
process = process->next;
}
return NULL;
}
static void new_state_process(int callref, enum process_state state)
{
process_t *process = get_process(callref);
if (!process) {
PDEBUG(DCALL, DEBUG_ERROR, "Process with callref %d not found!\n", callref);
return;
}
PDEBUG(DCALL, DEBUG_DEBUG, "Changing state for callref %d %d->%d\n", callref, process->state, state);
process->state = state;
}
static void set_pattern_process(int callref, enum audio_pattern pattern)
{
process_t *process = get_process(callref);
if (!process) {
PDEBUG(DCALL, DEBUG_ERROR, "Process with callref %d not found!\n", callref);
return;
}
process->pattern = pattern;
process->audio_pos = 0;
}
/* disconnect audio, now send audio directly from pattern/announcement, not from transceiver */
static void disconnect_process(int callref, int cause)
{
process_t *process = get_process(callref);
if (!process) {
PDEBUG(DCALL, DEBUG_ERROR, "Process with callref %d not found!\n", callref);
return;
}
process->pattern = cause2pattern(cause);
process->audio_disconnected = 1;
process->audio_pos = 0;
process->cause = cause;
timer_start(&process->timer, DISC_TIMEOUT);
}
static void get_process_patterns(process_t *process, int16_t *samples, int length)
{
const int16_t *spl;
int size, max, pos;
get_pattern(&spl, &size, &max, process->pattern);
/* stream sample */
pos = process->audio_pos;
while(length--) {
if (pos >= size)
*samples++ = 0;
else
*samples++ = spl[pos] >> 2;
if (++pos == max)
pos = 0;
}
process->audio_pos = pos;
}
static void process_timeout(struct timer *timer)
{
process_t *process = (process_t *)timer->priv;
{
/* announcement timeout */
if (process->state == PROCESS_DISCONNECT) {
PDEBUG(DCALL, DEBUG_INFO, "Call released toward mobile network (after timeout)\n");
call_down_release(process->callref, process->cause);
}
indicate_disconnect_release(process->callref, process->cause, OSMO_CC_MSG_REL_IND);
destroy_process(process->callref);
}
}
void down_audio(struct osmo_cc_session_codec *codec, uint16_t __attribute__((unused)) sequence_number, uint32_t __attribute__((unused)) timestamp, uint8_t *data, int len)
{
process_t *process = codec->media->session->priv;
sample_t samples[len / 2];
/* if we are disconnected, ignore audio */
if (!process || process->pattern != PATTERN_NONE)
return;
int16_to_samples(samples, (int16_t *)data, len / 2);
#ifdef DEBUG_LEVEL
double lev = level_of(samples, len / 2);
printf("festnetz-level: %s %.4f\n", debug_db(lev), (20 * log10(lev)));
#endif
call_down_audio(process->callref, samples, len / 2);
}
static void indicate_setup(process_t *process, const char *callerid, const char *dialing, uint8_t network_type, const char *network_id)
{
osmo_cc_msg_t *msg;
msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_IND);
/* network type */
if (network_type)
osmo_cc_add_ie_calling_network(msg, network_type, network_id);
/* calling number */
if (callerid && callerid[0])
osmo_cc_add_ie_calling(msg, OSMO_CC_TYPE_SUBSCRIBER, OSMO_CC_PLAN_TELEPHONY, OSMO_CC_PRESENT_ALLOWED, OSMO_CC_SCREEN_NETWORK, callerid);
/* called number */
if (dialing && dialing[0])
osmo_cc_add_ie_called(msg, OSMO_CC_TYPE_UNKNOWN, OSMO_CC_PLAN_TELEPHONY, dialing);
/* bearer capability */
osmo_cc_add_ie_bearer(msg, OSMO_CC_CODING_ITU_T, OSMO_CC_CAPABILITY_AUDIO, OSMO_CC_MODE_CIRCUIT);
/* sdp offer */
process->session = osmo_cc_helper_audio_offer(&ep->session_config, process, codecs, down_audio, msg, 1);
PDEBUG(DCALL, DEBUG_INFO, "Indicate OSMO-CC setup towards fixed network\n");
osmo_cc_ll_msg(ep, process->callref, msg);
}
static void indicate_proceeding(int callref, const char *sdp)
{
osmo_cc_msg_t *msg;
msg = osmo_cc_new_msg(OSMO_CC_MSG_PROC_IND);
/* sdp */
osmo_cc_add_ie_sdp(msg, sdp);
/* progress information */
osmo_cc_add_ie_progress(msg, OSMO_CC_CODING_ITU_T, OSMO_CC_LOCATION_BEYOND_INTERWORKING, OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE);
PDEBUG(DCALL, DEBUG_INFO, "Indicate OSMO-CC call confirm towards fixed network\n");
osmo_cc_ll_msg(ep, callref, msg);
}
static void indicate_alerting(int callref)
{
osmo_cc_msg_t *msg;
msg = osmo_cc_new_msg(OSMO_CC_MSG_ALERT_IND);
PDEBUG(DCALL, DEBUG_INFO, "Indicate OSMO-CC alerting towards fixed network\n");
osmo_cc_ll_msg(ep, callref, msg);
}
static void indicate_answer(int callref, const char *sdp, const char *connectid)
{
osmo_cc_msg_t *msg;
msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_CNF);
/* calling number */
if (connectid && connectid[0])
osmo_cc_add_ie_calling(msg, OSMO_CC_TYPE_SUBSCRIBER, OSMO_CC_PLAN_TELEPHONY, OSMO_CC_PRESENT_ALLOWED, OSMO_CC_SCREEN_NETWORK, connectid);
/* sdp */
if (sdp)
osmo_cc_add_ie_sdp(msg, sdp);
PDEBUG(DCALL, DEBUG_INFO, "Indicate OSMO-CC answer towards fixed network\n");
osmo_cc_ll_msg(ep, callref, msg);
}
static void indicate_answer_ack(int callref)
{
osmo_cc_msg_t *msg;
msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_COMP_IND);
PDEBUG(DCALL, DEBUG_INFO, "Indicate OSMO-CC setup complete towards fixed network\n");
osmo_cc_ll_msg(ep, callref, msg);
}
static void indicate_disconnect_release(int callref, int cause, uint8_t msg_type)
{
osmo_cc_msg_t *msg;
msg = osmo_cc_new_msg(msg_type);
/* cause */
osmo_cc_add_ie_cause(msg, OSMO_CC_LOCATION_PRIV_SERV_LOC_USER, cause, 0, 0);
/* progress information */
if (msg_type == OSMO_CC_MSG_DISC_IND)
osmo_cc_add_ie_progress(msg, OSMO_CC_CODING_ITU_T, OSMO_CC_LOCATION_BEYOND_INTERWORKING, OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE);
PDEBUG(DCALL, DEBUG_INFO, "%s OSMO-CC %s towards fixed network\n", (msg_type == OSMO_CC_MSG_REL_CNF) ? "Confirm" : "Indicated", (msg_type == OSMO_CC_MSG_DISC_IND) ? "disconnect" : "release");
osmo_cc_ll_msg(ep, callref, msg);
}
/* Setup is received from transceiver. */
int call_up_setup(const char *callerid, const char *dialing, uint8_t network, const char *network_id)
{
osmo_cc_call_t *call;
process_t *process;
PDEBUG(DCALL, DEBUG_INFO, "Incoming call from '%s' to '%s'\n", callerid ? : "unknown", dialing);
if (!strcmp(dialing, "010"))
PDEBUG(DCALL, DEBUG_INFO, " -> Call to Operator '%s'\n", dialing);
call = osmo_cc_call_new(ep);
process = create_process(call->callref, PROCESS_SETUP_RO);
indicate_setup(process, callerid, dialing, network, network_id);
return call->callref;
}
/* Transceiver indicates alerting. */
void call_up_alerting(int callref)
{
if (!callref) {
PDEBUG(DCALL, DEBUG_DEBUG, "Ignoring alerting, because callref not set. (not for us)\n");
return;
}
PDEBUG(DCALL, DEBUG_INFO, "Call is alerting\n");
if (!connect_on_setup)
indicate_alerting(callref);
set_pattern_process(callref, PATTERN_RINGBACK);
new_state_process(callref, PROCESS_ALERTING_RT);
}
/* Transceiver indicates early audio */
void call_up_early(int callref)
{
set_pattern_process(callref, PATTERN_NONE);
}
/* Transceiver indicates answer. */
void call_up_answer(int callref, const char *connect_id)
{
if (!callref) {
PDEBUG(DCALL, DEBUG_DEBUG, "Ignoring answer, because callref not set. (not for us)\n");
return;
}
PDEBUG(DCALL, DEBUG_INFO, "Call has been answered by '%s'\n", connect_id);
if (!connect_on_setup)
indicate_answer(callref, NULL, connect_id);
set_pattern_process(callref, PATTERN_NONE);
new_state_process(callref, PROCESS_CONNECT);
}
/* Transceiver indicates release. */
void call_up_release(int callref, int cause)
{
process_t *process;
if (!callref) {
PDEBUG(DCALL, DEBUG_DEBUG, "Ignoring release, because callref not set. (not for us)\n");
return;
}
PDEBUG(DCALL, DEBUG_INFO, "Call has been released with cause=%d\n", cause);
process = get_process(callref);
if (process) {
/* just keep OSMO-CC connection if tones shall be sent.
* no tones while setting up / alerting the call. */
if (connect_on_setup
&& process->state != PROCESS_SETUP_RO
&& process->state != PROCESS_ALERTING_RO)
disconnect_process(callref, cause);
else
/* if no tones shall be sent, release on disconnect
* or RO setup states */
if (process->state == PROCESS_DISCONNECT
|| process->state == PROCESS_SETUP_RO
|| process->state == PROCESS_ALERTING_RO) {
destroy_process(callref);
indicate_disconnect_release(callref, cause, OSMO_CC_MSG_REL_IND);
/* if no tones shall be sent, disconnect on all other states */
} else {
disconnect_process(callref, cause);
indicate_disconnect_release(callref, cause, OSMO_CC_MSG_DISC_IND);
}
} else {
/* we don't know about the process, just send release to upper layer anyway */
indicate_disconnect_release(callref, cause, OSMO_CC_MSG_REL_IND);
}
}
/* turn recall tone on or off */
void call_tone_recall(int callref, int on)
{
set_pattern_process(callref, (on) ? PATTERN_RECALL : PATTERN_NONE);
}
/* forward audio to OSMO-CC or call instance */
void call_up_audio(int callref, sample_t *samples, int count)
{
process_t *process;
int16_t data[count];
if (count != 160) {
fprintf(stderr, "Samples must be 160, please fix!\n");
abort();
}
if (!callref)
return;
/* if we are disconnected, ignore audio */
process = get_process(callref);
if (!process || process->pattern != PATTERN_NONE)
return;
/* forward audio */
#ifdef DEBUG_LEVEL
double lev = level_of(samples, count);
printf(" mobil-level: %s%.4f\n", debug_db(lev), (20 * log10(lev)));
#endif
samples_to_int16(data, samples, count);
osmo_cc_rtp_send(process->codec, (uint8_t *)data, count * 2, 1, count);
/* don't destroy process here in case of an error */
}
/* clock that is used to transmit patterns */
void call_clock(void)
{
process_t *process = process_head;
call_down_clock();
while(process) {
if (process->pattern != PATTERN_NONE) {
int16_t data[160];
/* try to get patterns, else copy the samples we got */
get_process_patterns(process, data, 160);
#ifdef DEBUG_LEVEL
sample_t samples[160];
int16_to_samples(samples, (int16_t *)data->data, 160);
double lev = level_of(samples, 160);
printf(" mobil-level: %s%.4f\n", debug_db(lev), (20 * log10(lev)));
samples_to_int16(data, samples, 160);
#endif
osmo_cc_rtp_send(process->codec, (uint8_t *)data, 160 * 2, 1, 160);
/* don't destroy process here in case of an error */
}
process = process->next;
}
}
/* messages received from fixed network */
void ll_msg_cb(osmo_cc_endpoint_t __attribute__((unused)) *ep, uint32_t callref, osmo_cc_msg_t *msg)
{
process_t *process;
uint8_t coding, location, progress, isdn_cause, socket_cause;
uint16_t sip_cause;
uint8_t type, plan, present, screen, caller_type;
char caller_id[33], number[33];
const char *suffix, *invalid;
int rc;
process = get_process(callref);
if (!process) {
if (msg->type == OSMO_CC_MSG_SETUP_REQ)
process = create_process(callref, PROCESS_SETUP_RT);
else {
/* release collisions is not forbidden */
if (msg->type != OSMO_CC_MSG_REL_REQ)
PDEBUG(DCALL, DEBUG_ERROR, "No process!\n");
osmo_cc_free_msg(msg);
return;
}
}
if (process->audio_disconnected) {
switch(msg->type) {
case OSMO_CC_MSG_DISC_REQ:
rc = osmo_cc_get_ie_cause(msg, 0, &location, &isdn_cause, &sip_cause, &socket_cause);
if (rc < 0)
isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR;
PDEBUG(DCALL, DEBUG_INFO, "Received OSMO-CC disconnect from fixed network with cause %d\n", isdn_cause);
PDEBUG(DCALL, DEBUG_INFO, "Call disconnected, releasing!\n");
destroy_process(callref);
indicate_disconnect_release(callref, isdn_cause, OSMO_CC_MSG_REL_IND);
break;
case OSMO_CC_MSG_REL_REQ:
rc = osmo_cc_get_ie_cause(msg, 0, &location, &isdn_cause, &sip_cause, &socket_cause);
if (rc < 0)
isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR;
PDEBUG(DCALL, DEBUG_INFO, "Received OSMO-CC release from fixed network with cause %d\n", isdn_cause);
PDEBUG(DCALL, DEBUG_INFO, "Call released\n");
destroy_process(callref);
indicate_disconnect_release(callref, isdn_cause, OSMO_CC_MSG_REL_CNF);
break;
}
osmo_cc_free_msg(msg);
return;
}
switch(msg->type) {
case OSMO_CC_MSG_SETUP_REQ:
{
const char *sdp;
/* sdp accept */
sdp = osmo_cc_helper_audio_accept(&ep->session_config, process, codecs, down_audio, msg, &process->session, &process->codec, 0);
if (!sdp) {
disconnect_process(callref, 47);
indicate_disconnect_release(callref, 47, OSMO_CC_MSG_REJ_IND);
break;
}
/* caller id */
rc = osmo_cc_get_ie_calling(msg, 0, &type, &plan, &present, &screen, caller_id, sizeof(caller_id));
if (rc < 0) {
caller_type = TYPE_NOTAVAIL;
caller_id[0] = '\0';
} else {
switch (type) {
case OSMO_CC_TYPE_INTERNATIONAL:
caller_type = TYPE_INTERNATIONAL;
break;
case OSMO_CC_TYPE_NATIONAL:
caller_type = TYPE_NATIONAL;
break;
case OSMO_CC_TYPE_SUBSCRIBER:
caller_type = TYPE_SUBSCRIBER;
break;
default:
caller_type = TYPE_UNKNOWN;
}
if (present == OSMO_CC_PRESENT_RESTRICTED)
caller_type = TYPE_ANONYMOUS;
}
/* dialing */
rc = osmo_cc_get_ie_called(msg, 0, &type, &plan, number, sizeof(number));
if (rc < 0)
number[0] = '\0';
PDEBUG(DCALL, DEBUG_INFO, "Received OSMO-CC call from fixed network '%s' to mobile '%s'\n", caller_id, number);
if (!connect_on_setup)
indicate_proceeding(callref, sdp);
else {
PDEBUG(DCALL, DEBUG_DEBUG, "Early connecting after setup\n");
indicate_answer(callref, sdp, number);
}
PDEBUG(DCALL, DEBUG_INFO, "Outgoing call from '%s' to '%s'\n", caller_id, number);
/* insert '+' for international dialing */
if (type == OSMO_CC_TYPE_INTERNATIONAL && number[0] != '+') {
memmove(number + 1, number, sizeof(number) - 2);
number[0] = '+';
}
/* remove prefix, if any */
suffix = mobile_number_remove_prefix(number);
/* check suffix length */
invalid = mobile_number_check_length(suffix);
if (invalid) {
PDEBUG(DCALL, DEBUG_NOTICE, "Mobile number '%s' has invalid length: %s\n", suffix, invalid);
disconnect_process(callref, OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT);
if (!connect_on_setup) {
PDEBUG(DCALL, DEBUG_INFO, "Disconnecting OSMO-CC call towards fixed network (cause=%d)\n", OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT);
indicate_disconnect_release(callref, OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT, OSMO_CC_MSG_DISC_IND);
}
break;
}
/* check suffix digits */
invalid = mobile_number_check_digits(suffix);
if (invalid) {
PDEBUG(DCALL, DEBUG_NOTICE, "Mobile number '%s' has invalid digit: %s.\n", suffix, invalid);
disconnect_process(callref, OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT);
if (!connect_on_setup) {
PDEBUG(DCALL, DEBUG_INFO, "Disconnecting OSMO-CC call towards fixed network (cause=%d)\n", OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT);
indicate_disconnect_release(callref, OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT, OSMO_CC_MSG_DISC_IND);
}
break;
}
/* check if suffix is valid */
if (mobile_number_check_valid) {
invalid = mobile_number_check_valid(suffix);
if (invalid) {
PDEBUG(DCALL, DEBUG_NOTICE, "Mobile number '%s' is invalid for this network: %s\n", suffix, invalid);
disconnect_process(callref, OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT);
if (!connect_on_setup) {
PDEBUG(DCALL, DEBUG_INFO, "Disconnecting OSMO-CC call towards fixed network (cause=%d)\n", OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT);
indicate_disconnect_release(callref, OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT, OSMO_CC_MSG_DISC_IND);
}
break;
}
}
/* setup call */
rc = call_down_setup(callref, caller_id, caller_type, suffix);
if (rc < 0) {
PDEBUG(DCALL, DEBUG_NOTICE, "Call rejected, cause %d\n", -rc);
if (!connect_on_setup) {
PDEBUG(DCALL, DEBUG_INFO, "Disconnecting OSMO-CC call towards fixed network (cause=%d)\n", -rc);
indicate_disconnect_release(callref, -rc, OSMO_CC_MSG_DISC_IND);
}
disconnect_process(callref, -rc);
break;
}
break;
}
case OSMO_CC_MSG_SETUP_ACK_REQ:
PDEBUG(DCALL, DEBUG_INFO, "Received OSMO-CC overlap from fixed network\n");
rc = osmo_cc_helper_audio_negotiate(msg, &process->session, &process->codec);
if (rc < 0) {
nego_failed:
PDEBUG(DCALL, DEBUG_INFO, "Releasing, because codec negotiation failed.\n");
destroy_process(callref);
indicate_disconnect_release(callref, 47, OSMO_CC_MSG_REL_IND);
PDEBUG(DCALL, DEBUG_INFO, "Call released toward mobile network\n");
call_down_release(callref, 47);
break;
}
break;
case OSMO_CC_MSG_PROC_REQ:
PDEBUG(DCALL, DEBUG_INFO, "Received OSMO-CC proceeding from fixed network\n");
rc = osmo_cc_helper_audio_negotiate(msg, &process->session, &process->codec);
if (rc < 0)
goto nego_failed;
break;
case OSMO_CC_MSG_PROGRESS_REQ:
PDEBUG(DCALL, DEBUG_INFO, "Received OSMO-CC progress from fixed network\n");
rc = osmo_cc_helper_audio_negotiate(msg, &process->session, &process->codec);
if (rc < 0)
goto nego_failed;
break;
case OSMO_CC_MSG_ALERT_REQ:
PDEBUG(DCALL, DEBUG_INFO, "Received OSMO-CC alerting from fixed network\n");
rc = osmo_cc_helper_audio_negotiate(msg, &process->session, &process->codec);
if (rc < 0)
goto nego_failed;
new_state_process(callref, PROCESS_ALERTING_RO);
break;
case OSMO_CC_MSG_SETUP_RSP:
PDEBUG(DCALL, DEBUG_INFO, "Received OSMO-CC answer from fixed network\n");
rc = osmo_cc_helper_audio_negotiate(msg, &process->session, &process->codec);
if (rc < 0)
goto nego_failed;
new_state_process(callref, PROCESS_CONNECT);
PDEBUG(DCALL, DEBUG_INFO, "Call answered\n");
call_down_answer(callref);
indicate_answer_ack(callref);
break;
case OSMO_CC_MSG_DISC_REQ:
rc = osmo_cc_helper_audio_negotiate(msg, &process->session, &process->codec);
if (rc < 0)
goto nego_failed;
rc = osmo_cc_get_ie_cause(msg, 0, &location, &isdn_cause, &sip_cause, &socket_cause);
if (rc < 0)
isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR;
rc = osmo_cc_get_ie_progress(msg, 0, &coding, &location, &progress);
if (rc < 0)
progress = 0;
PDEBUG(DCALL, DEBUG_INFO, "Received OSMO-CC disconnect from fixed network with cause %d\n", isdn_cause);
if (release_on_disconnect || (progress != 1 && progress != 8)) {
PDEBUG(DCALL, DEBUG_INFO, "Releasing, because we don't send disconnect tones to mobile phone\n");
destroy_process(callref);
indicate_disconnect_release(callref, isdn_cause, OSMO_CC_MSG_REL_IND);
PDEBUG(DCALL, DEBUG_INFO, "Call released toward mobile network\n");
call_down_release(callref, isdn_cause);
break;
}
new_state_process(callref, PROCESS_DISCONNECT);
PDEBUG(DCALL, DEBUG_INFO, "Call disconnected\n");
call_down_disconnect(callref, isdn_cause);
/* we might get released during disconnect handling!!! */
process = get_process(callref);
if (process && process->state == PROCESS_DISCONNECT)
timer_start(&process->timer, DISC_TIMEOUT);
break;
case OSMO_CC_MSG_REJ_REQ:
case OSMO_CC_MSG_REL_REQ:
rc = osmo_cc_get_ie_cause(msg, 0, &location, &isdn_cause, &sip_cause, &socket_cause);
if (rc < 0)
isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR;
destroy_process(callref);
if (msg->type == OSMO_CC_MSG_REL_REQ) {
PDEBUG(DCALL, DEBUG_INFO, "Received OSMO-CC release from fixed network with cause %d\n", isdn_cause);
indicate_disconnect_release(callref, isdn_cause, OSMO_CC_MSG_REL_CNF);
} else
PDEBUG(DCALL, DEBUG_INFO, "Received OSMO-CC reject from fixed network with cause %d\n", isdn_cause);
PDEBUG(DCALL, DEBUG_INFO, "Call released toward mobile network\n");
call_down_release(callref, isdn_cause);
break;
}
osmo_cc_free_msg(msg);
}
int call_init(const char *name, int _send_patterns, int _release_on_disconnect, int use_socket, int argc, const char *argv[])
{
int rc;
connect_on_setup = _send_patterns;
release_on_disconnect = _release_on_disconnect;
g711_init();
ep = &endpoint;
rc = osmo_cc_new(ep, OSMO_CC_VERSION, name, OSMO_CC_LOCATION_PRIV_SERV_LOC_USER, ll_msg_cb, (use_socket) ? NULL : console_msg, NULL, argc, argv);
if (rc > 0)
return -EINVAL;
if (rc < 0)
return rc;
return 0;
}
void call_exit(void)
{
if (ep) {
osmo_cc_delete(ep);
ep = NULL;
}
}
int call_handle(void)
{
return osmo_cc_handle();
}
void call_media_handle(void)
{
process_t *process = process_head;
while(process) {
if (process->session)
osmo_cc_session_handle(process->session);
process = process->next;
}
}

View File

@ -1,45 +0,0 @@
/* number type, includes presentation info */
enum number_type {
TYPE_NOTAVAIL,
TYPE_ANONYMOUS,
TYPE_UNKNOWN,
TYPE_SUBSCRIBER,
TYPE_NATIONAL,
TYPE_INTERNATIONAL,
};
int call_init(const char *name, int _send_patterns, int _release_on_disconnect, int use_socket, int argc, const char *argv[]);
void call_exit(void);
int call_handle(void);
void call_media_handle(void);
/* function pointer to delive MNCC messages to upper layer */
extern int (*mncc_up)(uint8_t *buf, int length);
/* MNCC messages from upper layer */
void mncc_down(uint8_t *buf, int length);
/* flush all calls in case of MNCC socket failure */
void mncc_flush(void);
/* received messages */
int call_up_setup(const char *callerid, const char *dialing, uint8_t network, const char *network_id);
void call_up_alerting(int callref);
void call_up_early(int callref);
void call_up_answer(int callref, const char *connect_id);
void call_up_release(int callref, int cause);
void call_tone_recall(int callref, int on);
/* send messages */
int call_down_setup(int callref, const char *caller_id, enum number_type caller_type, const char *dialing);
void call_down_answer(int callref);
void call_down_disconnect(int callref, int cause);
void call_down_release(int callref, int cause);
/* send and receive audio */
void call_up_audio(int callref, sample_t *samples, int count);
void call_down_audio(int callref, sample_t *samples, int count);
/* clock to transmit to */
void call_clock(void); /* from main loop */
void call_down_clock(void); /* towards mobile implementation */

View File

@ -1,52 +0,0 @@
/* Clear cause names
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include "cause.h"
const char *cause_name(int cause)
{
static char cause_str[16];
switch (cause) {
case CAUSE_NORMAL:
return "hangup";
case CAUSE_BUSY:
return "busy";
case CAUSE_NOANSWER:
return "no-answer";
case CAUSE_OUTOFORDER:
return "out-of-order";
case CAUSE_INVALNUMBER:
return "invalid-number";
case CAUSE_NOCHANNEL:
return "no-channel";
case CAUSE_TEMPFAIL:
return "link-failure";
case CAUSE_RESOURCE_UNAVAIL:
return "resource-unavail";
case CAUSE_INVALCALLREF:
return "invalid-callref";
default:
sprintf(cause_str, "cause=%d", cause);
return cause_str;
}
}

View File

@ -1,16 +0,0 @@
#define CAUSE_NORMAL 16
#define CAUSE_BUSY 17
#define CAUSE_NOANSWER 19
#define CAUSE_OUTOFORDER 27
#define CAUSE_INVALNUMBER 28
#define CAUSE_NOCHANNEL 34
#define CAUSE_TEMPFAIL 41
#define CAUSE_RESOURCE_UNAVAIL 47
#define CAUSE_INVALCALLREF 81
#define LOCATION_USER 0
#define LOCATION_PRIVATE_LOCAL 1
const char *cause_name(int cause);

View File

@ -1,636 +0,0 @@
/* built-in console to talk to a phone
*
* (C) 2017 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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
G* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include "../libsample/sample.h"
#include "../libsamplerate/samplerate.h"
#include "../libjitter/jitter.h"
#include "../libdebug/debug.h"
#include "../libtimer/timer.h"
#include "../libosmocc/endpoint.h"
#include "../libosmocc/helper.h"
#include "testton.h"
#include "../libmobile/main_mobile.h"
#include "console.h"
#include "cause.h"
#include "../libmobile/call.h"
#ifdef HAVE_ALSA
#include "../libsound/sound.h"
#endif
enum console_state {
CONSOLE_IDLE = 0, /* IDLE */
CONSOLE_SETUP_RO, /* call from radio to console */
CONSOLE_SETUP_RT, /* call from console to radio */
CONSOLE_ALERTING_RO, /* call from radio to console */
CONSOLE_ALERTING_RT, /* call from console to radio */
CONSOLE_CONNECT,
CONSOLE_DISCONNECT_RO,
};
static const char *console_state_name[] = {
"IDLE",
"SETUP_RO",
"SETUP_RT",
"ALERTING_RO",
"ALERTING_RT",
"CONNECT",
"DISCONNECT_RO",
};
/* console call instance */
typedef struct console {
osmo_cc_session_t *session;
osmo_cc_session_codec_t *codec;
uint32_t callref;
enum console_state state;
int disc_cause; /* cause that has been sent by transceiver instance for release */
char station_id[33];
char dialing[33];
char audiodev[64]; /* headphone interface, if used */
int samplerate; /* sample rate of headphone interface */
void *sound; /* headphone interface */
int buffer_size; /* sample buffer size at headphone interface */
samplerate_t srstate; /* patterns/announcement upsampling */
jitter_t dejitter; /* headphone audio dejittering */
int test_audio_pos; /* position for test tone toward mobile */
sample_t tx_buffer[160];/* transmit audio buffer */
int tx_buffer_pos; /* current position in transmit audio buffer */
const struct number_lengths *number_lengths;/* number of digits to be dialed */
int number_max_length; /* number of digits of the longest number to be dialed */
int loopback; /* loopback test for echo */
int echo_test; /* send echo back to mobile phone */
const char *digits; /* list of dialable digits */
} console_t;
static console_t console;
extern osmo_cc_endpoint_t *ep;
void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
static struct osmo_cc_helper_audio_codecs codecs[] = {
{ "L16", 8000, 1, encode_l16, decode_l16 },
{ NULL, 0, 0, NULL, NULL},
};
/* stream test music */
int16_t *test_spl = NULL;
int test_size = 0;
int test_max = 0;
static void get_test_patterns(int16_t *samples, int length)
{
const int16_t *spl;
int size, max, pos;
spl = test_spl;
size = test_size;
max = test_max;
/* stream sample */
pos = console.test_audio_pos;
while(length--) {
if (pos >= size)
*samples++ = 0;
else
*samples++ = spl[pos] >> 2;
if (++pos == max)
pos = 0;
}
console.test_audio_pos = pos;
}
static void console_new_state(enum console_state state)
{
PDEBUG(DCC, DEBUG_DEBUG, "Call state '%s' -> '%s'\n", console_state_name[console.state], console_state_name[state]);
console.state = state;
console.test_audio_pos = 0;
}
static void free_console(void)
{
if (console.session) {
osmo_cc_free_session(console.session);
console.session = NULL;
}
console.codec = NULL;
console.callref = 0;
}
void up_audio(struct osmo_cc_session_codec *codec, uint16_t __attribute__((unused)) sequence_number, uint32_t __attribute__((unused)) timestamp, uint8_t *data, int len)
{
int count = len / 2;
sample_t samples[count];
/* save audio from transceiver to jitter buffer */
if (console.sound) {
sample_t up[(int)((double)count * console.srstate.factor + 0.5) + 10];
int16_to_samples(samples, (int16_t *)data, count);
count = samplerate_upsample(&console.srstate, samples, count, up);
jitter_save(&console.dejitter, up, count);
return;
}
/* if echo test is used, send echo back to mobile */
if (console.echo_test) {
osmo_cc_rtp_send(codec, (uint8_t *)data, count * 2, 1, count);
return;
}
/* if no sound is used, send test tone to mobile */
if (console.state == CONSOLE_CONNECT) {
get_test_patterns((int16_t *)data, count);
osmo_cc_rtp_send(codec, (uint8_t *)data, count * 2, 1, count);
return;
}
}
static void request_setup(int callref, const char *dialing)
{
osmo_cc_msg_t *msg;
msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_REQ);
/* called number */
if (dialing)
osmo_cc_add_ie_called(msg, OSMO_CC_TYPE_UNKNOWN, OSMO_CC_PLAN_TELEPHONY, dialing);
/* bearer capability */
osmo_cc_add_ie_bearer(msg, OSMO_CC_CODING_ITU_T, OSMO_CC_CAPABILITY_AUDIO, OSMO_CC_MODE_CIRCUIT);
/* sdp offer */
console.session = osmo_cc_helper_audio_offer(&ep->session_config, NULL, codecs, up_audio, msg, 1);
osmo_cc_ul_msg(ep, callref, msg);
}
static void request_answer(int callref, const char *connectid, const char *sdp)
{
osmo_cc_msg_t *msg;
msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_RSP);
/* calling number */
if (connectid)
osmo_cc_add_ie_calling(msg, OSMO_CC_TYPE_SUBSCRIBER, OSMO_CC_PLAN_TELEPHONY, OSMO_CC_PRESENT_ALLOWED, OSMO_CC_SCREEN_NETWORK, connectid);
/* SDP */
if (sdp)
osmo_cc_add_ie_sdp(msg, sdp);
osmo_cc_ul_msg(ep, callref, msg);
}
static void request_answer_ack(int callref)
{
osmo_cc_msg_t *msg;
msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_COMP_REQ);
osmo_cc_ul_msg(ep, callref, msg);
}
static void request_disconnect_release_reject(int callref, int cause, uint8_t msg_type)
{
osmo_cc_msg_t *msg;
msg = osmo_cc_new_msg(msg_type);
osmo_cc_add_ie_cause(msg, OSMO_CC_LOCATION_USER, cause, 0, 0);
osmo_cc_ul_msg(ep, callref, msg);
}
void console_msg(osmo_cc_call_t *call, osmo_cc_msg_t *msg)
{
uint8_t location, isdn_cause, socket_cause;
uint16_t sip_cause;
uint8_t type, plan, present, screen;
uint8_t progress, coding;
char caller_id[33], number[33];
const char *sdp;
int rc;
if (msg->type != OSMO_CC_MSG_SETUP_IND && console.callref != call->callref) {
PDEBUG(DCC, DEBUG_ERROR, "invalid call ref %u (msg=0x%02x).\n", call->callref, msg->type);
request_disconnect_release_reject(call->callref, CAUSE_INVALCALLREF, OSMO_CC_MSG_REL_REQ);
osmo_cc_free_msg(msg);
return;
}
switch(msg->type) {
case OSMO_CC_MSG_SETUP_IND:
{
/* caller id */
rc = osmo_cc_get_ie_calling(msg, 0, &type, &plan, &present, &screen, caller_id, sizeof(caller_id));
if (rc < 0)
caller_id[0] = '\0';
/* dialing */
rc = osmo_cc_get_ie_called(msg, 0, &type, &plan, number, sizeof(number));
if (rc < 0)
number[0] = '\0';
PDEBUG(DCC, DEBUG_INFO, "Incoming call from '%s'\n", caller_id);
/* setup is also allowed on disconnected call */
if (console.state == CONSOLE_DISCONNECT_RO) {
PDEBUG(DCC, DEBUG_INFO, "Releasing pending disconnected call\n");
if (console.callref) {
request_disconnect_release_reject(console.callref, CAUSE_NORMAL, OSMO_CC_MSG_REL_REQ);
free_console();
}
console_new_state(CONSOLE_IDLE);
}
if (console.state != CONSOLE_IDLE) {
PDEBUG(DCC, DEBUG_NOTICE, "We are busy, rejecting.\n");
request_disconnect_release_reject(console.callref, CAUSE_NORMAL, OSMO_CC_MSG_REJ_REQ);
osmo_cc_free_msg(msg);
return;
}
console.callref = call->callref;
/* sdp accept */
sdp = osmo_cc_helper_audio_accept(&ep->session_config, NULL, codecs, up_audio, msg, &console.session, &console.codec, 0);
if (!sdp) {
PDEBUG(DCC, DEBUG_NOTICE, "Cannot accept codec, rejecting.\n");
request_disconnect_release_reject(console.callref, CAUSE_RESOURCE_UNAVAIL, OSMO_CC_MSG_REJ_REQ);
osmo_cc_free_msg(msg);
return;
}
if (caller_id[0]) {
strncpy(console.station_id, caller_id, sizeof(console.station_id));
console.station_id[sizeof(console.station_id) - 1] = '\0';
}
strncpy(console.dialing, number, sizeof(console.dialing) - 1);
console.dialing[sizeof(console.dialing) - 1] = '\0';
console_new_state(CONSOLE_CONNECT);
PDEBUG(DCC, DEBUG_INFO, "Call automatically answered\n");
request_answer(console.callref, number, sdp);
break;
}
case OSMO_CC_MSG_SETUP_ACK_IND:
case OSMO_CC_MSG_PROC_IND:
osmo_cc_helper_audio_negotiate(msg, &console.session, &console.codec);
break;
case OSMO_CC_MSG_ALERT_IND:
PDEBUG(DCC, DEBUG_INFO, "Call alerting\n");
osmo_cc_helper_audio_negotiate(msg, &console.session, &console.codec);
console_new_state(CONSOLE_ALERTING_RT);
break;
case OSMO_CC_MSG_SETUP_CNF:
{
/* connected id */
rc = osmo_cc_get_ie_calling(msg, 0, &type, &plan, &present, &screen, caller_id, sizeof(caller_id));
if (rc < 0)
caller_id[0] = '\0';
PDEBUG(DCC, DEBUG_INFO, "Call connected to '%s'\n", caller_id);
osmo_cc_helper_audio_negotiate(msg, &console.session, &console.codec);
console_new_state(CONSOLE_CONNECT);
if (caller_id[0]) {
strncpy(console.station_id, caller_id, sizeof(console.station_id));
console.station_id[sizeof(console.station_id) - 1] = '\0';
}
request_answer_ack(console.callref);
break;
}
case OSMO_CC_MSG_SETUP_COMP_IND:
break;
case OSMO_CC_MSG_DISC_IND:
rc = osmo_cc_get_ie_cause(msg, 0, &location, &isdn_cause, &sip_cause, &socket_cause);
if (rc < 0)
isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR;
rc = osmo_cc_get_ie_progress(msg, 0, &coding, &location, &progress);
osmo_cc_helper_audio_negotiate(msg, &console.session, &console.codec);
if (rc >= 0 && (progress == 1 || progress == 8)) {
PDEBUG(DCC, DEBUG_INFO, "Call disconnected with audio (%s)\n", cause_name(isdn_cause));
console_new_state(CONSOLE_DISCONNECT_RO);
console.disc_cause = isdn_cause;
} else {
PDEBUG(DCC, DEBUG_INFO, "Call disconnected without audio (%s)\n", cause_name(isdn_cause));
request_disconnect_release_reject(console.callref, isdn_cause, OSMO_CC_MSG_REL_REQ);
console_new_state(CONSOLE_IDLE);
free_console();
}
break;
case OSMO_CC_MSG_REL_IND:
case OSMO_CC_MSG_REJ_IND:
rc = osmo_cc_get_ie_cause(msg, 0, &location, &isdn_cause, &sip_cause, &socket_cause);
if (rc < 0)
isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR;
PDEBUG(DCC, DEBUG_INFO, "Call released (%s)\n", cause_name(isdn_cause));
console_new_state(CONSOLE_IDLE);
free_console();
break;
}
osmo_cc_free_msg(msg);
}
static char console_text[256];
static char console_clear[256];
static int console_len = 0;
static void _clear_console_text(void)
{
if (!console_len)
return;
fwrite(console_clear, console_len, 1, stdout);
// note: fflused by user of this function
console_len = 0;
}
static void _print_console_text(void)
{
if (!console_len)
return;
printf("\033[1;37m");
fwrite(console_text, console_len, 1, stdout);
printf("\033[0;39m");
}
int console_init(const char *audiodev, int samplerate, int buffer, int loopback, int echo_test, const char *digits, const struct number_lengths *lengths, const char *station_id)
{
int rc = 0;
int i;
init_testton();
clear_console_text = _clear_console_text;
print_console_text = _print_console_text;
memset(&console, 0, sizeof(console));
strncpy(console.audiodev, audiodev, sizeof(console.audiodev) - 1);
console.samplerate = samplerate;
console.buffer_size = buffer * samplerate / 1000;
console.loopback = loopback;
console.echo_test = echo_test;
console.digits = digits;
console.number_lengths = lengths;
if (lengths) {
for (i = 0; lengths[i].usage; i++) {
if (lengths[i].digits > console.number_max_length)
console.number_max_length = lengths[i].digits;
}
}
if (station_id)
strncpy(console.station_id, station_id, sizeof(console.station_id) - 1);
if (!audiodev[0])
return 0;
rc = init_samplerate(&console.srstate, 8000.0, (double)samplerate, 3300.0);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to init sample rate conversion!\n");
goto error;
}
rc = jitter_create(&console.dejitter, samplerate / 5);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create and init dejitter buffer!\n");
goto error;
}
return 0;
error:
console_cleanup();
return rc;
}
int console_open_audio(int __attribute__((unused)) buffer_size, double __attribute__((unused)) interval)
{
if (!console.audiodev[0])
return 0;
#ifdef HAVE_ALSA
/* open sound device for call control */
/* use factor 1.4 of speech level for complete range of sound card */
console.sound = sound_open(console.audiodev, NULL, NULL, NULL, 1, 0.0, console.samplerate, buffer_size, interval, 1.4, 4000.0, 2.0);
if (!console.sound) {
PDEBUG(DSENDER, DEBUG_ERROR, "No sound device!\n");
return -EIO;
}
#else
PDEBUG(DSENDER, DEBUG_ERROR, "No sound card support compiled in!\n");
return -ENOTSUP;
#endif
return 0;
}
int console_start_audio(void)
{
if (!console.audiodev[0])
return 0;
#ifdef HAVE_ALSA
return sound_start(console.sound);
#else
return -EINVAL;
#endif
}
void console_cleanup(void)
{
#ifdef HAVE_ALSA
/* close sound devoice */
if (console.sound) {
sound_close(console.sound);
console.sound = NULL;
}
#endif
jitter_destroy(&console.dejitter);
if (console.session) {
osmo_cc_free_session(console.session);
console.session = NULL;
}
}
/* process input from console
* it is not called at loopback mode
* calling this implies that the console.number_lengths is set
*/
static void process_ui(int c)
{
char text[256] = "";
int len;
int i;
switch (console.state) {
case CONSOLE_IDLE:
if (c > 0) {
if ((int)strlen(console.station_id) < console.number_max_length) {
for (i = 0; i < (int)strlen(console.digits); i++) {
if (c == console.digits[i]) {
console.station_id[strlen(console.station_id) + 1] = '\0';
console.station_id[strlen(console.station_id)] = c;
}
}
}
if ((c == 8 || c == 127) && strlen(console.station_id))
console.station_id[strlen(console.station_id) - 1] = '\0';
dial_after_hangup:
len = strlen(console.station_id);
for (i = 0; console.number_lengths[i].usage; i++) {
if (len == console.number_lengths[i].digits)
break;
}
if (c == 'd' && console.number_lengths[i].usage) {
PDEBUG(DCC, DEBUG_INFO, "Outgoing call to '%s'\n", console.station_id);
console.dialing[0] = '\0';
console_new_state(CONSOLE_SETUP_RT);
console.callref = osmo_cc_new_callref();
request_setup(console.callref, console.station_id);
}
}
sprintf(text, "on-hook: %s%s ", console.station_id, "................................" + 32 - console.number_max_length + strlen(console.station_id));
len = strlen(console.station_id);
for (i = 0; console.number_lengths[i].usage; i++) {
if (len == console.number_lengths[i].digits)
break;
}
if (console.number_lengths[i].usage) {
if (console.number_lengths[i + 1].usage)
sprintf(strchr(text, '\0'), "(enter digits %s or press d=dial)\r", console.digits);
else
sprintf(strchr(text, '\0'), "(press d=dial)\r");
} else
sprintf(strchr(text, '\0'), "(enter digits %s)\r", console.digits);
break;
case CONSOLE_SETUP_RO:
case CONSOLE_SETUP_RT:
case CONSOLE_ALERTING_RO:
case CONSOLE_ALERTING_RT:
case CONSOLE_CONNECT:
case CONSOLE_DISCONNECT_RO:
if (c > 0) {
if (c == 'h' || (c == 'd' && console.state == CONSOLE_DISCONNECT_RO)) {
PDEBUG(DCC, DEBUG_INFO, "Call hangup\n");
if (console.callref) {
if (console.state == CONSOLE_SETUP_RO)
request_disconnect_release_reject(console.callref, CAUSE_NORMAL, OSMO_CC_MSG_REJ_REQ);
else
request_disconnect_release_reject(console.callref, CAUSE_NORMAL, OSMO_CC_MSG_REL_REQ);
free_console();
}
console_new_state(CONSOLE_IDLE);
if (c == 'd')
goto dial_after_hangup;
}
}
if (console.state == CONSOLE_SETUP_RT)
sprintf(text, "call setup: %s (press h=hangup)\r", console.station_id);
if (console.state == CONSOLE_ALERTING_RT)
sprintf(text, "call ringing: %s (press h=hangup)\r", console.station_id);
if (console.state == CONSOLE_CONNECT) {
if (console.dialing[0])
sprintf(text, "call active: %s->%s (press h=hangup)\r", console.station_id, console.dialing);
else
sprintf(text, "call active: %s (press h=hangup)\r", console.station_id);
}
if (console.state == CONSOLE_DISCONNECT_RO)
sprintf(text, "call disconnected: %s (press h=hangup d=redial)\r", cause_name(console.disc_cause));
break;
}
/* skip if nothing has changed */
len = strlen(text);
if (console_len == len && !memcmp(console_text, text, len))
return;
clear_console_text();
console_len = len;
memcpy(console_text, text, len);
if (len) {
memset(console_clear, ' ', len - 1);
console_clear[len - 1] = '\r';
}
print_console_text();
fflush(stdout);
}
/* get keys from keyboard to control call via console
* returns 1 on exit (ctrl+c) */
void process_console(int c)
{
if (!console.loopback && console.number_max_length)
process_ui(c);
if (console.session)
osmo_cc_session_handle(console.session);
if (!console.sound)
return;
#ifdef HAVE_ALSA
/* handle audio, if sound device is used */
sample_t samples[console.buffer_size + 10], *samples_list[1];
uint8_t *power_list[1];
int count;
int rc;
count = sound_get_tosend(console.sound, console.buffer_size);
if (count < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to get samples in buffer (rc = %d)!\n", count);
if (count == -EPIPE)
PDEBUG(DSENDER, DEBUG_ERROR, "Trying to recover.\n");
return;
}
if (count > 0) {
jitter_load(&console.dejitter, samples, count);
samples_list[0] = samples;
power_list[0] = NULL;
rc = sound_write(console.sound, samples_list, power_list, count, NULL, NULL, 1);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to write TX data to sound device (rc = %d)\n", rc);
if (rc == -EPIPE)
PDEBUG(DSENDER, DEBUG_ERROR, "Trying to recover.\n");
return;
}
}
samples_list[0] = samples;
count = sound_read(console.sound, samples_list, console.buffer_size, 1, NULL);
if (count < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to read from sound device (rc = %d)!\n", count);
if (count == -EPIPE)
PDEBUG(DSENDER, DEBUG_ERROR, "Trying to recover.\n");
return;
}
if (count) {
int i;
if (console.loopback == 3)
jitter_save(&console.dejitter, samples, count);
count = samplerate_downsample(&console.srstate, samples, count);
/* put samples into ring buffer */
for (i = 0; i < count; i++) {
console.tx_buffer[console.tx_buffer_pos] = samples[i];
/* if ring buffer wraps, deliver data down to call process */
if (++console.tx_buffer_pos == 160) {
console.tx_buffer_pos = 0;
/* only if we have a call */
if (console.callref && console.codec) {
int16_t data[160];
samples_to_int16(data, console.tx_buffer, 160);
osmo_cc_rtp_send(console.codec, (uint8_t *)data, 160 * 2, 1, 160);
}
}
}
}
#endif
}

View File

@ -1,9 +0,0 @@
void console_msg(osmo_cc_call_t *call, osmo_cc_msg_t *msg);
int console_init(const char *audiodev, int samplerate, int buffer, int loopback, int echo_test, const char *digits, const struct number_lengths *lengths, const char *station_id);
void console_cleanup(void);
int console_open_audio(int buffer_size, double interval);
int console_start_audio(void);
void console_process(int c);
void process_console(int c);

View File

@ -1,857 +0,0 @@
/* Common part for main.c of each base station type
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sched.h>
#include <unistd.h>
#include <math.h>
#include <termios.h>
#include <errno.h>
#include "../libsample/sample.h"
#include "main_mobile.h"
#include "../libdebug/debug.h"
#include "sender.h"
#include "../libtimer/timer.h"
#include "call.h"
#include "../libosmocc/endpoint.h"
#include "console.h"
#ifdef HAVE_SDR
#include "../libsdr/sdr.h"
#include "../libsdr/sdr_config.h"
#endif
#include "../liboptions/options.h"
#include "../libfm/fm.h"
#include "../libaaimage/aaimage.h"
#define DEFAULT_LO_OFFSET -1000000.0
static int got_init = 0;
/* common mobile settings */
int num_kanal = 0;
const char *kanal[MAX_SENDER];
int num_device = 0;
const char *dsp_device[MAX_SENDER] = { "hw:0,0" };
int allow_sdr = 1;
int use_sdr = 0;
int dsp_samplerate = 48000;
double dsp_interval = 1.0;
int dsp_buffer = 50;
static const char *call_device = "";
static int call_samplerate = 48000;
static int call_buffer = 50;
int uses_emphasis = 1;
int do_pre_emphasis = 0;
int do_de_emphasis = 0;
double rx_gain = 1.0;
double tx_gain = 1.0;
static int echo_test = 0;
static int use_osmocc_cross = 0;
static int use_osmocc_sock = 0;
#define MAX_CC_ARGS 1024
static int cc_argc = 0;
static const char *cc_argv[MAX_CC_ARGS];
int send_patterns = 1;
static int release_on_disconnect = 1;
int loopback = 0;
int rt_prio = 1;
int fast_math = 0;
const char *write_tx_wave = NULL;
const char *write_rx_wave = NULL;
const char *read_tx_wave = NULL;
const char *read_rx_wave = NULL;
static const char *number_digits;
static const struct number_lengths *number_lengths;
static const char **number_prefixes;
const char *mobile_number_remove_prefix(const char *number)
{
size_t len;
int i, j;
if (!number_prefixes)
return number;
len = strlen(number);
for (i = 0; number_prefixes[i]; i++) {
/* skip different lengths */
if (len != strlen(number_prefixes[i]))
continue;
/* match prefix, stop at 'x' */
for (j = 0; number_prefixes[i][j]; j++) {
if (number_prefixes[i][j] == 'x')
break;
if (number_prefixes[i][j] != number[j])
break;
}
/* if prefix matches, return suffix */
if (number_prefixes[i][j] == 'x')
return number + j;
}
/* return number, if there is no prefix matching */
return number;
}
const char *mobile_number_check_length(const char *number)
{
size_t len;
int i;
static char invalid[256];
if (!number_lengths)
return NULL;
len = strlen(number);
for (i = 0; number_lengths[i].usage; i++) {
if ((int)len == number_lengths[i].digits)
break;
}
if (!number_lengths[i].usage) {
sprintf(invalid, "Number does not have");
for (i = 0; number_lengths[i].usage; i++) {
sprintf(strchr(invalid, '\0'), " %d", number_lengths[i].digits);
if (number_lengths[i + 1].usage) {
if (number_lengths[i + 2].usage)
strcat(invalid, ",");
else
strcat(invalid, " or");
}
}
sprintf(strchr(invalid, '\0'), " digits.");
return invalid;
}
return NULL;
}
const char *mobile_number_check_digits(const char *number)
{
int i;
static char invalid[256];
for (i = 0; number[i]; i++) {
if (!strchr(number_digits, number[i])) {
sprintf(invalid, "Digit #%d of number has digit '%c' which is not in the set of allowed digits. ('%s')\n", i + 1, number[i], number_digits);
return invalid;
}
}
return NULL;
}
const char *(*mobile_number_check_valid)(const char *);
void main_mobile_init(const char *digits, const struct number_lengths lengths[], const char *prefixes[], const char *(*check_valid)(const char *))
{
cc_argv[cc_argc++] = options_strdup("remote auto");
number_digits = digits;
number_lengths = lengths;
number_prefixes = prefixes;
mobile_number_check_valid = check_valid;
got_init = 1;
#ifdef HAVE_SDR
sdr_config_init(DEFAULT_LO_OFFSET);
#endif
}
void main_mobile_set_number_check_valid(const char *(*check_valid)(const char *))
{
mobile_number_check_valid = check_valid;
}
/* ask if number is connect */
int main_mobile_number_ask(const char *number, const char *what)
{
const char *invalid;
if (!got_init) {
fprintf(stderr, "main_mobile_init was not called, please fix!\n");
abort();
}
number = mobile_number_remove_prefix(number);
invalid = mobile_number_check_length(number);
if (invalid) {
printf("Given %s '%s' has invalid length: %s\n", what, number, invalid);
return -EINVAL;
}
invalid = mobile_number_check_digits(number);
if (invalid) {
printf("Given %s '%s' has invalid digit: %s\n", what, number, invalid);
return -EINVAL;
}
if (mobile_number_check_valid) {
invalid = mobile_number_check_valid(number);
if (invalid) {
printf("Given %s '%s' is invalid for this network: %s\n", what, number, invalid);
return -EINVAL;
}
}
return 0;
}
void main_mobile_print_help(const char *arg0, const char *ext_usage)
{
printf("Usage: %s -k <kanal/channel> %s[options] [station_id]\n", arg0, ext_usage);
printf("\nGlobal options:\n");
/* - - */
printf(" -h --help\n");
printf(" This help\n");
printf(" --config [~/]<path to config file>\n");
printf(" Give a config file to use. If it starts with '~/', path is at home dir.\n");
printf(" Each line in config file is one option, '-' or '--' must not be given!\n");
debug_print_help();
printf(" -k --kanal <channel>\n");
printf(" -k --channel <channel>\n");
printf(" Channel (German = Kanal) number of \"Sender\" (German = Transceiver)\n");
printf(" -a --audio-device hw:<card>,<device>\n");
printf(" Sound card and device number (default = '%s')\n", dsp_device[0]);
printf(" Don't set it for SDR!\n");
printf(" -s --samplerate <rate>\n");
printf(" Sample rate of sound device (default = '%d')\n", dsp_samplerate);
printf(" -i --interval 0.1..25\n");
printf(" Interval of processing loop in ms (default = '%.1f' ms)\n", dsp_interval);
printf(" Use 10 to drastically reduce CPU usage. In case of buffer underrun,\n");
printf(" increase buffer accordingly.\n");
printf(" -b --buffer <ms>\n");
printf(" How many milliseconds are processed in advance (default = '%d')\n", dsp_buffer);
printf(" A buffer below 10 ms requires low interval like 0.1 ms.\n");
if (uses_emphasis) {
printf(" -p --pre-emphasis\n");
printf(" Enable pre-emphasis, if you directly connect to the oscillator of the\n");
printf(" transmitter. (No pre-emphasis done by the transmitter.)\n");
printf(" -d --de-emphasis\n");
printf(" Enable de-emphasis, if you directly connect to the discriminator of\n");
printf(" the receiver. (No de-emphasis done by the receiver.)\n");
}
printf(" --rx-gain <dB>\n");
printf(" Raise/lower receiver's RX level by given gain in dB.\n");
printf(" (Works with sound card only.)\n");
printf(" --tx-gain <dB>\n");
printf(" Raise/lower transmitters's RX level by given gain in dB.\n");
printf(" (Works with sound card only.)\n");
printf(" -e --echo-test\n");
printf(" Use echo test, to send back audio from mobile phone's microphone to\n");
printf(" the speaker. (German: 'Blasprobe').\n");
printf(" -c --call-device hw:<card>,<device>\n");
printf(" Sound card and device number for headset (default = '%s')\n", call_device);
printf(" --call-samplerate <rate>\n");
printf(" Sample rate of sound device for headset (default = '%d')\n", call_samplerate);
printf(" --call-buffer <ms>\n");
printf(" How many milliseconds are processed in advance (default = '%d')\n", call_buffer);
printf(" -x --osmocc-cross\n");
printf(" Enable built-in call forwarding between mobiles. Be sure to have\n");
printf(" at least one control channel and two voice channels. Alternatively\n");
printf(" use one combined control+voice channel and one voice channels.\n");
printf(" -o --osmocc-sock\n");
printf(" Disable built-in call control and offer socket\n");
printf(" --cc \"<osmo-cc arg>\" [--cc ...]\n");
printf(" Pass arguments to Osmo-CC endpoint. Use '-cc help' for description.\n");
printf(" -t --tones 0 | 1\n");
printf(" Connect call on setup/release to provide classic tones towards fixed\n");
printf(" network (default = '%d')\n", send_patterns);
printf(" -l --loopback <type>\n");
printf(" Loopback test: 1 = internal | 2 = external | 3 = echo\n");
printf(" -r --realtime <prio>\n");
printf(" Set prio: 0 to disable, 99 for maximum (default = %d)\n", rt_prio);
printf(" --fast-math\n");
printf(" Use fast math approximation for slow CPU / ARM based systems.\n");
printf(" --write-rx-wave <file>\n");
printf(" Write received audio to given wave file.\n");
printf(" --write-tx-wave <file>\n");
printf(" Write transmitted audio to given wave file.\n");
printf(" --read-rx-wave <file>\n");
printf(" Replace received audio by given wave file.\n");
printf(" --read-tx-wave <file>\n");
printf(" Replace transmitted audio by given wave file.\n");
#ifdef HAVE_SDR
if (allow_sdr) {
printf(" --limesdr\n");
printf(" Auto-select several required options for LimeSDR\n");
printf(" --limesdr-mini\n");
printf(" Auto-select several required options for LimeSDR Mini\n");
sdr_config_print_help();
}
#endif
printf("\nNetwork specific options:\n");
}
void main_mobile_print_station_id(void)
{
int i;
if (!number_lengths)
return;
printf("\nstation_id: Give");
for (i = 0; number_lengths[i].usage; i++) {
printf(" %d", number_lengths[i].digits);
if (number_lengths[i + 1].usage) {
if (number_lengths[i + 2].usage)
printf(",");
else
printf(" or");
}
}
printf(" digits of station ID,\n");
printf(" so you don't need to enter it for every start of this application.\n");
for (i = 0; number_lengths[i].usage; i++)
printf(" Give %d digits for %s.\n", number_lengths[i].digits, number_lengths[i].usage);
if (number_prefixes) {
for (i = 0; number_prefixes[i]; i++)
printf(" You may use '%s' as prefix.\n", number_prefixes[i]);
}
}
void main_mobile_print_hotkeys(void)
{
printf("\n");
printf("Press digits '0'..'9' and then 'd' key to dial towards mobile station.\n");
printf("Press 'h' key to hangup.\n");
printf("Press 'w' key to toggle display of RX wave form.\n");
printf("Press 'c' key to toggle display of channel status.\n");
printf("Press 'm' key to toggle display of measurement value.\n");
#ifdef HAVE_SDR
if (allow_sdr) {
sdr_config_print_hotkeys();
}
#endif
}
#define OPT_CHANNEL 1000
#define OPT_RX_GAIN 1001
#define OPT_TX_GAIN 1002
#define OPT_OSMO_CC 1003
#define OPT_WRITE_RX_WAVE 1004
#define OPT_WRITE_TX_WAVE 1005
#define OPT_READ_RX_WAVE 1006
#define OPT_READ_TX_WAVE 1007
#define OPT_CALL_SAMPLERATE 1008
#define OPT_CALL_BUFFER 1009
#define OPT_FAST_MATH 1010
#define OPT_LIMESDR 1100
#define OPT_LIMESDR_MINI 1101
void main_mobile_add_options(void)
{
option_add('h', "help", 0);
option_add('v', "verbose", 1);
option_add('k', "kanal", 1);
option_add(OPT_CHANNEL, "channel", 1);
option_add('a', "audio-device", 1);
option_add('s', "samplerate", 1);
option_add('i', "interval", 1);
option_add('b', "buffer", 1);
option_add('p', "pre-emphasis", 0);
option_add('d', "de-emphasis", 0);
option_add(OPT_RX_GAIN, "rx-gain", 1);
option_add(OPT_TX_GAIN, "tx-gain", 1);
option_add('e', "echo-test", 0);
option_add('x', "osmocc-cross", 0);
option_add('o', "osmocc-sock", 0);
option_add(OPT_OSMO_CC, "cc", 1);
option_add('c', "call-device", 1);
option_add(OPT_CALL_SAMPLERATE, "call-samplerate", 1);
option_add(OPT_CALL_BUFFER, "call-buffer", 1);
option_add('t', "tones", 1);
option_add('l', "loopback", 1);
option_add('r', "realtime", 1);
option_add(OPT_FAST_MATH, "fast-math", 0);
option_add(OPT_WRITE_RX_WAVE, "write-rx-wave", 1);
option_add(OPT_WRITE_TX_WAVE, "write-tx-wave", 1);
option_add(OPT_READ_RX_WAVE, "read-rx-wave", 1);
option_add(OPT_READ_TX_WAVE, "read-tx-wave", 1);
#ifdef HAVE_SDR
option_add(OPT_LIMESDR, "limesdr", 0);
option_add(OPT_LIMESDR_MINI, "limesdr-mini", 0);
sdr_config_add_options();
#endif
};
void print_help(const char *arg0);
int main_mobile_handle_options(int short_option, int argi, char **argv)
{
double gain_db;
int rc;
switch (short_option) {
case 'h':
print_help(argv[0]);
return 0;
case 'v':
if (!strcasecmp(argv[argi], "list")) {
debug_list_cat();
return 0;
}
rc = parse_debug_opt(argv[argi]);
if (rc < 0) {
fprintf(stderr, "Failed to parse debug option, please use -h for help.\n");
return rc;
}
break;
case 'k':
case OPT_CHANNEL:
OPT_ARRAY(num_kanal, kanal, argv[argi])
break;
case 'a':
OPT_ARRAY(num_device, dsp_device, options_strdup(argv[argi]))
break;
case 's':
dsp_samplerate = atoi(argv[argi]);
break;
case 'i':
dsp_interval = atof(argv[argi]);
if (dsp_interval < 0.1)
dsp_interval = 0.1;
if (dsp_interval > 10)
dsp_interval = 10;
break;
case 'b':
dsp_buffer = atoi(argv[argi]);
break;
case 'p':
if (!uses_emphasis) {
no_emph:
fprintf(stderr, "This network does not use emphasis, please do not enable pre- or de-emphasis! Disable emphasis on transceiver, if possible.\n");
return -EINVAL;
}
do_pre_emphasis = 1;
break;
case 'd':
if (!uses_emphasis)
goto no_emph;
do_de_emphasis = 1;
break;
case OPT_RX_GAIN:
gain_db = atof(argv[argi]);
rx_gain = pow(10, gain_db / 20.0);
break;
case OPT_TX_GAIN:
gain_db = atof(argv[argi]);
tx_gain = pow(10, gain_db / 20.0);
break;
case 'e':
echo_test = 1;
break;
case 'x':
use_osmocc_cross = 1;
break;
case 'o':
use_osmocc_sock = 1;
break;
case OPT_OSMO_CC:
if (!strcasecmp(argv[argi], "help")) {
osmo_cc_help();
return 0;
}
if (cc_argc == MAX_CC_ARGS) {
fprintf(stderr, "Too many osmo-cc args!\n");
break;
}
cc_argv[cc_argc++] = options_strdup(argv[argi]);
break;
case 'c':
call_device = options_strdup(argv[argi]);
break;
case OPT_CALL_SAMPLERATE:
call_samplerate = atoi(argv[argi]);
break;
case OPT_CALL_BUFFER:
call_buffer = atoi(argv[argi]);
break;
case 't':
send_patterns = atoi(argv[argi]);
break;
case 'l':
loopback = atoi(argv[argi]);
break;
case 'r':
rt_prio = atoi(argv[argi]);
break;
case OPT_FAST_MATH:
fast_math = 1;
break;
case OPT_WRITE_RX_WAVE:
write_rx_wave = options_strdup(argv[argi]);
break;
case OPT_WRITE_TX_WAVE:
write_tx_wave = options_strdup(argv[argi]);
break;
case OPT_READ_RX_WAVE:
read_rx_wave = options_strdup(argv[argi]);
break;
case OPT_READ_TX_WAVE:
read_tx_wave = options_strdup(argv[argi]);
break;
#ifdef HAVE_SDR
case OPT_LIMESDR:
if (allow_sdr) {
char *argv_lime[] = { argv[0],
"--sdr-soapy",
"--sdr-device-args", "driver=lime",
"--sdr-rx-antenna", "LNAL",
"--sdr-rx-gain", "30",
"--sdr-tx-gain", "30",
"--sdr-samplerate", "5000000",
"--sdr-bandwidth", "15000000",
"-s", "200000",
};
int argc_lime = sizeof(argv_lime) / sizeof (*argv_lime);
return options_command_line(argc_lime, argv_lime, main_mobile_handle_options);
}
break;
case OPT_LIMESDR_MINI:
if (allow_sdr) {
char *argv_lime[] = { argv[0],
"--sdr-soapy",
"--sdr-device-args", "driver=lime",
"--sdr-rx-antenna", "LNAW",
"--sdr-tx-antenna", "BAND2",
"--sdr-rx-gain", "25",
"--sdr-tx-gain", "30",
"--sdr-samplerate", "5000000",
"--sdr-bandwidth", "15000000",
"-s", "200000",
};
int argc_lime = sizeof(argv_lime) / sizeof (*argv_lime);
return options_command_line(argc_lime, argv_lime, main_mobile_handle_options);
}
break;
#endif
default:
#ifdef HAVE_SDR
if (allow_sdr)
return sdr_config_handle_options(short_option, argi, argv);
#endif
return -EINVAL;
}
return 1;
}
/* global variable to quit main loop */
int quit = 0;
void sighandler(int sigset)
{
if (sigset == SIGHUP)
return;
if (sigset == SIGPIPE)
return;
if (clear_console_text)
clear_console_text();
printf("Signal received: %d\n", sigset);
quit = 1;
}
static int get_char()
{
struct timeval tv = {0, 0};
fd_set fds;
char c = 0;
int __attribute__((__unused__)) rc;
FD_ZERO(&fds);
FD_SET(0, &fds);
select(0+1, &fds, NULL, NULL, &tv);
if (FD_ISSET(0, &fds)) {
rc = read(0, &c, 1);
return c;
} else
return -1;
}
/* Loop through all transceiver instances of one network. */
void main_mobile_loop(const char *name, int *quit, void (*myhandler)(void), const char *station_id)
{
int buffer_size;
sender_t *sender;
double last_time_call = 0, begin_time, now, sleep;
struct termios term, term_orig;
int c;
int rc;
if (!got_init) {
fprintf(stderr, "main_mobile_init was not called, please fix!\n");
abort();
}
/* station id preset */
if (station_id)
station_id = mobile_number_remove_prefix(station_id);
/* size of dsp buffer in samples */
buffer_size = dsp_samplerate * dsp_buffer / 1000;
/* check OSMO-CC support */
if (use_osmocc_cross && num_kanal == 1) {
fprintf(stderr, "You selected built-in call forwarding, but only channel is used. Does this makes sense?\n");
return;
}
if (use_osmocc_sock && use_osmocc_cross) {
fprintf(stderr, "You selected OSMO-CC socket interface and built-in call forwarding, but only one can be selected.\n");
return;
}
if (echo_test && call_device[0]) {
fprintf(stderr, "You selected call device (headset) and echo test, but only one can be selected.\n");
return;
}
if (use_osmocc_sock && call_device[0]) {
fprintf(stderr, "You selected OSMO-CC socket interface, but it cannot be used with call device (headset).\n");
return;
}
if (use_osmocc_cross && call_device[0]) {
fprintf(stderr, "You selected built-in call forwarding, but it cannot be used with call device (headset).\n");
return;
}
if (use_osmocc_sock && echo_test) {
fprintf(stderr, "You selected OSMO-CC socket interface, but it cannot be used with echo test.\n");
return;
}
if (use_osmocc_cross && echo_test) {
fprintf(stderr, "You selected built-in call forwarding, but it cannot be used with echo test.\n");
return;
}
/* OSMO-CC crossover */
if (use_osmocc_cross) {
use_osmocc_sock = 1;
cc_argc = 0;
cc_argv[cc_argc++] = options_strdup("local 127.0.0.1:4200");
cc_argv[cc_argc++] = options_strdup("remote 127.0.0.1:4200");
}
/* init OSMO-CC */
if (!use_osmocc_sock)
console_init(call_device, call_samplerate, call_buffer, loopback, echo_test, number_digits, number_lengths, station_id);
/* init call control instance */
rc = call_init(name, (use_osmocc_sock) ? send_patterns : 0, release_on_disconnect, use_osmocc_sock, cc_argc, cc_argv);
if (rc < 0) {
fprintf(stderr, "Failed to create call control instance. Quitting!\n");
return;
}
#ifdef HAVE_SDR
rc = sdr_configure(dsp_samplerate);
if (rc < 0)
return;
#endif
/* open audio */
if (sender_open_audio(buffer_size, dsp_interval))
return;
if (console_open_audio(buffer_size, dsp_interval))
return;
if (!loopback)
print_aaimage();
/* real time priority */
if (rt_prio > 0) {
struct sched_param schedp;
int rc;
memset(&schedp, 0, sizeof(schedp));
schedp.sched_priority = rt_prio;
rc = sched_setscheduler(0, SCHED_RR, &schedp);
if (rc)
fprintf(stderr, "Error setting SCHED_RR with prio %d\n", rt_prio);
}
/* prepare terminal */
tcgetattr(0, &term_orig);
term = term_orig;
term.c_lflag &= ~(ISIG|ICANON|ECHO);
term.c_cc[VMIN]=1;
term.c_cc[VTIME]=2;
tcsetattr(0, TCSANOW, &term);
/* catch signals */
signal(SIGINT, sighandler);
signal(SIGHUP, sighandler);
signal(SIGTERM, sighandler);
signal(SIGPIPE, sighandler);
/* start streaming */
if (sender_start_audio())
*quit = 1;
if (console_start_audio())
*quit = 1;
while(!(*quit)) {
begin_time = get_time();
/* process sound of all transceivers */
for (sender = sender_head; sender; sender = sender->next) {
/* do not process audio for an audio slave, since it is done by audio master */
if (sender->master) /* if master is set, we are an audio slave */
continue;
process_sender_audio(sender, quit, buffer_size);
}
/* process timers */
process_timer();
/* process audio for call instances */
now = get_time();
if (now - last_time_call >= 0.1)
last_time_call = now;
if (now - last_time_call >= 0.020) {
last_time_call += 0.020;
/* call clock every 20ms */
call_clock();
}
next_char:
c = get_char();
switch (c) {
case 3:
/* quit */
if (clear_console_text)
clear_console_text();
printf("CTRL+c received, quitting!\n");
*quit = 1;
goto next_char;
case 'w':
/* toggle wave display */
display_status_on(0);
display_measurements_on(0);
#ifdef HAVE_SDR
display_iq_on(0);
display_spectrum_on(0);
#endif
display_wave_on(-1);
goto next_char;
case 'c':
/* toggle call state display */
display_wave_on(0);
display_measurements_on(0);
#ifdef HAVE_SDR
display_iq_on(0);
display_spectrum_on(0);
#endif
display_status_on(-1);
goto next_char;
case 'm':
/* toggle measurements display */
display_wave_on(0);
display_status_on(0);
#ifdef HAVE_SDR
display_iq_on(0);
display_spectrum_on(0);
#endif
display_measurements_on(-1);
goto next_char;
#ifdef HAVE_SDR
case 'q':
/* toggle IQ display */
display_wave_on(0);
display_status_on(0);
display_measurements_on(0);
display_spectrum_on(0);
display_iq_on(-1);
goto next_char;
case 's':
/* toggle spectrum display */
display_wave_on(0);
display_status_on(0);
display_measurements_on(0);
display_iq_on(0);
display_spectrum_on(-1);
goto next_char;
#endif
case 'i':
/* dump info */
dump_info();
goto next_char;
#ifdef HAVE_SDR
case 'b':
calibrate_bias();
goto next_char;
#endif
}
/* process call control */
call_media_handle();
while (call_handle());
if (!use_osmocc_sock)
process_console(c);
if (myhandler)
myhandler();
display_measurements(dsp_interval / 1000.0);
now = get_time();
/* sleep interval */
sleep = (dsp_interval / 1000.0) - (now - begin_time);
if (sleep > 0)
usleep(sleep * 1000000.0);
// now = get_time();
// printf("duration =%.6f\n", now - begin_time);
}
/* reset signals */
signal(SIGINT, SIG_DFL);
signal(SIGHUP, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
/* get rid of last entry */
if (clear_console_text)
clear_console_text();
/* reset terminal */
tcsetattr(0, TCSANOW, &term_orig);
/* reset real time prio */
if (rt_prio > 0) {
struct sched_param schedp;
memset(&schedp, 0, sizeof(schedp));
schedp.sched_priority = 0;
sched_setscheduler(0, SCHED_OTHER, &schedp);
}
//* cleanup call control */
call_exit();
/* cleanup console */
if (!use_osmocc_sock)
console_cleanup();
}

View File

@ -1,60 +0,0 @@
extern int num_kanal;
extern const char *kanal[];
extern int swap_links;
extern int num_device;
extern int allow_sdr;
extern int use_sdr;
extern const char *dsp_device[];
extern int dsp_samplerate;
extern double dsp_interval;
extern int dsp_buffer;
extern int uses_emphasis;
extern int do_pre_emphasis;
extern int do_de_emphasis;
extern double rx_gain;
extern double tx_gain;
extern int send_patterns;
extern int loopback;
extern int rt_prio;
extern int fast_math;
extern const char *write_rx_wave;
extern const char *write_tx_wave;
extern const char *read_rx_wave;
extern const char *read_tx_wave;
struct number_lengths {
int digits;
const char *usage;
};
const char *mobile_number_remove_prefix(const char *number);
const char *mobile_number_check_length(const char *number);
const char *mobile_number_check_digits(const char *number);
extern const char *(*mobile_number_check_valid)(const char *);
int main_mobile_number_ask(const char *number, const char *what);
void main_mobile_init(const char *digits, const struct number_lengths lengths[], const char *prefixes[], const char *(*check_valid)(const char *));
void main_mobile_set_number_check_valid(const char *(*check_valid)(const char *));
void main_mobile_print_help(const char *arg0, const char *ext_usage);
void main_mobile_print_hotkeys(void);
void main_mobile_print_station_id(void);
void main_mobile_add_options(void);
int main_mobile_handle_options(int short_option, int argi, char **argv);
#define OPT_ARRAY(num_name, name, value) \
{ \
if (num_name == MAX_SENDER) { \
fprintf(stderr, "Too many channels defined!\n"); \
exit(0); \
} \
name[num_name++] = value; \
}
extern int quit;
void sighandler(int sigset);
void main_mobile_loop(const char *name, int *quit, void (*myhandler)(void), const char *station_id);
void dump_info(void);

View File

@ -1,494 +0,0 @@
/* Common transceiver functions
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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/>.
*/
#define CHAN sender->kanal
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "sender.h"
#include "../libtimer/timer.h"
#ifdef HAVE_SDR
#include "../libsdr/sdr_config.h"
#endif
/* debug time consumption of audio processing */
//#define DEBUG_TIME_CONSUMPTION
sender_t *sender_head = NULL;
static sender_t **sender_tailp = &sender_head;
int cant_recover = 0;
int check_channel = 1;
/* Init transceiver instance and link to list of transceivers. */
int sender_create(sender_t *sender, const char *kanal, double sendefrequenz, double empfangsfrequenz, const char *device, int use_sdr, int samplerate, double rx_gain, double tx_gain, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback, enum paging_signal paging_signal)
{
sender_t *master, *slave;
int rc = 0;
sender->kanal = kanal;
sender->sendefrequenz = sendefrequenz;
sender->empfangsfrequenz = (loopback) ? sendefrequenz : empfangsfrequenz;
strncpy(sender->device, device, sizeof(sender->device) - 1);
sender->samplerate = samplerate;
sender->rx_gain = rx_gain;
sender->tx_gain = tx_gain;
sender->pre_emphasis = pre_emphasis;
sender->de_emphasis = de_emphasis;
sender->loopback = loopback;
sender->paging_signal = paging_signal;
sender->write_rx_wave = write_rx_wave;
sender->write_tx_wave = write_tx_wave;
sender->read_rx_wave = read_rx_wave;
sender->read_tx_wave = read_tx_wave;
/* no gain with SDR */
if (use_sdr) {
sender->rx_gain = 1.0;
sender->tx_gain = 1.0;
}
if (samplerate < 8000) {
PDEBUG(DSENDER, DEBUG_NOTICE, "Given sample rate is below 8 KHz. Please use higher sample rate!\n");
rc = -EINVAL;
goto error;
}
PDEBUG_CHAN(DSENDER, DEBUG_DEBUG, "Creating 'Sender' instance\n");
/* if we find a channel that uses the same device as we do,
* we will link us as slave to this master channel. then we
* receive and send audio via second channel of the device
* of the master channel.
*/
for (master = sender_head; master; master = master->next) {
if (!strcmp(master->kanal, kanal)) {
PDEBUG(DSENDER, DEBUG_ERROR, "Channel %s may not be defined for multiple transceivers!\n", kanal);
rc = -EIO;
goto error;
}
if (check_channel && abs(atoi(master->kanal) - atoi(kanal)) == 1) {
PDEBUG(DSENDER, DEBUG_NOTICE, "------------------------------------------------------------------------\n");
PDEBUG(DSENDER, DEBUG_NOTICE, "NOTE: Channel %s is next to channel %s. This will cause interferences.\n", kanal, master->kanal);
PDEBUG(DSENDER, DEBUG_NOTICE, "Please use at least one channel distance to avoid that.\n");
PDEBUG(DSENDER, DEBUG_NOTICE, "------------------------------------------------------------------------\n");
}
if (!strcmp(master->device, device))
break;
}
if (master) {
if (master->paging_signal != PAGING_SIGNAL_NONE && !use_sdr) {
PDEBUG(DSENDER, DEBUG_ERROR, "Cannot share audio device with channel %s, because its second audio channel is used for paging signal! Use different audio device.\n", master->kanal);
rc = -EBUSY;
goto error;
}
if (paging_signal != PAGING_SIGNAL_NONE && !use_sdr) {
PDEBUG(DSENDER, DEBUG_ERROR, "Cannot share audio device with channel %s, because we need a second audio channel for paging signal! Use different audio device.\n", master->kanal);
rc = -EBUSY;
goto error;
}
/* link us to a master */
sender->master = master;
/* link master (or last slave) to us */
for (slave = master; ; slave = slave->slave) {
if (!slave->slave)
break;
}
slave->slave = sender;
} else {
/* link audio device */
#ifdef HAVE_SDR
if (use_sdr) {
sender->audio_open = sdr_open;
sender->audio_start = sdr_start;
sender->audio_close = sdr_close;
sender->audio_read = sdr_read;
sender->audio_write = sdr_write;
sender->audio_get_tosend = sdr_get_tosend;
} else
#endif
{
#ifdef HAVE_ALSA
sender->audio_open = sound_open;
sender->audio_start = sound_start;
sender->audio_close = sound_close;
sender->audio_read = sound_read;
sender->audio_write = sound_write;
sender->audio_get_tosend = sound_get_tosend;
#else
PDEBUG(DSENDER, DEBUG_ERROR, "No sound card support compiled in!\n");
rc = -ENOTSUP;
goto error;
#endif
}
}
rc = init_samplerate(&sender->srstate, 8000.0, (double)samplerate, 3300.0);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to init sample rate conversion!\n");
goto error;
}
rc = jitter_create(&sender->dejitter, samplerate / 5);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create and init audio buffer!\n");
goto error;
}
rc = init_emphasis(&sender->estate, samplerate, CUT_OFF_EMPHASIS_DEFAULT, CUT_OFF_HIGHPASS_DEFAULT, CUT_OFF_LOWPASS_DEFAULT);
if (rc < 0)
goto error;
*sender_tailp = sender;
sender_tailp = &sender->next;
display_wave_init(&sender->dispwav, samplerate, sender->kanal);
display_measurements_init(&sender->dispmeas, samplerate, sender->kanal);
return 0;
error:
sender_destroy(sender);
return rc;
}
int sender_open_audio(int buffer_size, double interval)
{
sender_t *master, *inst;
int channels;
int i;
int rc;
for (master = sender_head; master; master = master->next) {
/* skip audio slaves */
if (master->master)
continue;
/* get list of frequencies */
channels = 0;
for (inst = master; inst; inst = inst->slave) {
channels++;
}
double tx_f[channels], rx_f[channels], paging_frequency = 0.0;
int am[channels];
for (i = 0, inst = master; inst; i++, inst = inst->slave) {
tx_f[i] = inst->sendefrequenz;
rx_f[i] = inst->empfangsfrequenz;
am[i] = inst->am;
if (inst->ruffrequenz)
paging_frequency = inst->ruffrequenz;
}
if (master->write_rx_wave) {
rc = wave_create_record(&master->wave_rx_rec, master->write_rx_wave, master->samplerate, channels, (master->max_deviation) ?: 1.0);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create WAVE recoding instance!\n");
return rc;
}
}
if (master->write_tx_wave) {
rc = wave_create_record(&master->wave_tx_rec, master->write_tx_wave, master->samplerate, channels, (master->max_deviation) ?: 1.0);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create WAVE recoding instance!\n");
return rc;
}
}
if (master->read_rx_wave) {
rc = wave_create_playback(&master->wave_rx_play, master->read_rx_wave, &master->samplerate, &channels, (master->max_deviation) ?: 1.0);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create WAVE playback instance!\n");
return rc;
}
}
if (master->read_tx_wave) {
rc = wave_create_playback(&master->wave_tx_play, master->read_tx_wave, &master->samplerate, &channels, (master->max_deviation) ?: 1.0);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create WAVE playback instance!\n");
return rc;
}
}
/* open device */
master->audio = master->audio_open(master->device, tx_f, rx_f, am, channels, paging_frequency, master->samplerate, buffer_size, interval, (master->max_deviation) ?: 1.0, master->max_modulation, master->modulation_index);
if (!master->audio) {
PDEBUG(DSENDER, DEBUG_ERROR, "No device for transceiver!\n");
return -EIO;
}
}
#ifdef HAVE_SDR
/* in case of initialized spectrum display (SDR), we add all channels.
* if spectrum display was not initialized (sound card), function call is ignored */
for (inst = sender_head; inst; inst = inst->next)
display_spectrum_add_mark(inst->kanal, (sdr_config->swap_links) ? inst->sendefrequenz : inst->empfangsfrequenz);
#endif
return 0;
}
int sender_start_audio(void)
{
sender_t *master;
int rc = 0;
for (master = sender_head; master; master = master->next) {
/* skip audio slaves */
if (master->master)
continue;
rc = master->audio_start(master->audio);
if (rc)
break;
}
return rc;
}
/* Destroy transceiver instance and unlink from list. */
void sender_destroy(sender_t *sender)
{
PDEBUG_CHAN(DSENDER, DEBUG_DEBUG, "Destroying 'Sender' instance\n");
sender_tailp = &sender_head;
while (*sender_tailp) {
if (sender == *sender_tailp)
*sender_tailp = (*sender_tailp)->next;
else
sender_tailp = &((*sender_tailp)->next);
}
if (sender->audio) {
sender->audio_close(sender->audio);
sender->audio = NULL;
}
wave_destroy_record(&sender->wave_rx_rec);
wave_destroy_record(&sender->wave_tx_rec);
wave_destroy_playback(&sender->wave_rx_play);
wave_destroy_playback(&sender->wave_tx_play);
jitter_destroy(&sender->dejitter);
}
/* set frequency modulation and parameters */
void sender_set_fm(sender_t *sender, double max_deviation, double max_modulation, double speech_deviation, double max_display)
{
sender->max_deviation = max_deviation;
sender->max_modulation = max_modulation;
sender->speech_deviation = speech_deviation;
sender->max_display = max_display;
PDEBUG_CHAN(DSENDER, DEBUG_DEBUG, "Maximum deviation: %.1f kHz, Maximum modulation: %.1f kHz\n", max_deviation / 1000.0, max_modulation / 1000.0);
PDEBUG_CHAN(DSENDER, DEBUG_DEBUG, "Deviation at speech level: %.1f kHz\n", speech_deviation / 1000.0);
}
/* set amplitude modulation and parameters */
void sender_set_am(sender_t *sender, double max_modulation, double speech_level, double max_display, double modulation_index)
{
sender->am = 1;
sender->max_deviation = 0;
sender->max_modulation = max_modulation;
sender->speech_deviation = speech_level;
sender->max_display = max_display;
sender->modulation_index = modulation_index;
PDEBUG_CHAN(DSENDER, DEBUG_DEBUG, "Modulation degree: %.0f %%, Maximum modulation: %.1f kHz\n", modulation_index / 100.0, max_modulation / 1000.0);
}
static void gain_samples(sample_t *samples, int length, double gain)
{
int i;
for (i = 0; i < length; i++)
*samples++ *= gain;
}
/* Handle audio streaming of one transceiver. */
void process_sender_audio(sender_t *sender, int *quit, int buffer_size)
{
sender_t *inst;
int rc, count;
int num_chan, i;
#ifdef DEBUG_TIME_CONSUMPTION
static double t1, t2, t3, t4, t5, d1 = 0, d2 = 0, d3 = 0, d4 = 0, s = 0;
#endif
/* count instances for audio channel */
for (num_chan = 0, inst = sender; inst; num_chan++, inst = inst->slave);
sample_t buff[num_chan][buffer_size], *samples[num_chan];
uint8_t pbuff[num_chan][buffer_size], *power[num_chan];
enum paging_signal paging_signal[num_chan];
int on[num_chan];
double rf_level_db[num_chan];
for (i = 0; i < num_chan; i++) {
samples[i] = buff[i];
power[i] = pbuff[i];
}
#ifdef DEBUG_TIME_CONSUMPTION
t1 = get_time();
#endif
count = sender->audio_get_tosend(sender->audio, buffer_size);
if (count < 0) {
PDEBUG_CHAN(DSENDER, DEBUG_ERROR, "Failed to get number of samples in buffer (rc = %d)!\n", count);
if (count == -EPIPE) {
if (cant_recover) {
cant_recover:
PDEBUG(DSENDER, DEBUG_ERROR, "Cannot recover due to measurements, quitting!\n");
*quit = 1;
return;
}
PDEBUG(DSENDER, DEBUG_ERROR, "Trying to recover!\n");
}
return;
}
#ifdef DEBUG_TIME_CONSUMPTION
t2 = get_time();
#endif
if (count > 0) {
/* limit to our buffer */
if (count > buffer_size)
count = buffer_size;
/* loop through all channels */
for (i = 0, inst = sender; inst; i++, inst = inst->slave) {
/* load TX data from audio loop or from sender instance */
if (inst->loopback == 3)
jitter_load(&inst->dejitter, samples[i], count);
else
sender_send(inst, samples[i], power[i], count);
/* internal loopback: loop back TX audio to RX */
if (inst->loopback == 1) {
display_wave(&inst->dispwav, samples[i], count, inst->max_display);
sender_receive(inst, samples[i], count, 0.0);
}
/* do pre emphasis towards radio */
if (inst->pre_emphasis)
pre_emphasis(&inst->estate, samples[i], count);
/* tx gain */
if (inst->tx_gain != 1.0)
gain_samples(samples[i], count, inst->tx_gain);
/* normal level to frequency deviation of speech level */
gain_samples(samples[i], count, inst->speech_deviation);
/* set paging signal */
paging_signal[i] = inst->paging_signal;
on[i] = inst->paging_on;
}
#ifdef DEBUG_TIME_CONSUMPTION
t2 = get_time();
#endif
if (sender->wave_tx_rec.fp)
wave_write(&sender->wave_tx_rec, samples, count);
if (sender->wave_tx_play.fp)
wave_read(&sender->wave_tx_play, samples, count);
rc = sender->audio_write(sender->audio, samples, power, count, paging_signal, on, num_chan);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to write TX data to audio device (rc = %d)\n", rc);
if (rc == -EPIPE) {
if (cant_recover)
goto cant_recover;
PDEBUG(DSENDER, DEBUG_ERROR, "Trying to recover!\n");
}
return;
}
}
#ifdef DEBUG_TIME_CONSUMPTION
t3 = get_time();
#endif
count = sender->audio_read(sender->audio, samples, buffer_size, num_chan, rf_level_db);
if (count < 0) {
/* special case when audio_read wants us to quit */
if (count == -EPERM) {
*quit = 1;
return;
}
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to read from audio device (rc = %d)!\n", count);
if (count == -EPIPE) {
if (cant_recover)
goto cant_recover;
PDEBUG(DSENDER, DEBUG_ERROR, "Trying to recover!\n");
}
return;
}
#ifdef DEBUG_TIME_CONSUMPTION
t4 = get_time();
#endif
if (count) {
if (sender->wave_rx_rec.fp)
wave_write(&sender->wave_rx_rec, samples, count);
if (sender->wave_rx_play.fp)
wave_read(&sender->wave_rx_play, samples, count);
/* loop through all channels */
for (i = 0, inst = sender; inst; i++, inst = inst->slave) {
/* frequency deviation of speech level to normal level */
gain_samples(samples[i], count, 1.0 / inst->speech_deviation);
/* rx gain */
if (inst->rx_gain != 1.0)
gain_samples(samples[i], count, inst->rx_gain);
/* do filter and de-emphasis from radio receive audio, process echo test */
if (inst->de_emphasis) {
dc_filter(&inst->estate, samples[i], count);
de_emphasis(&inst->estate, samples[i], count);
}
if (inst->loopback != 1) {
display_wave(&inst->dispwav, samples[i], count, inst->max_display);
sender_receive(inst, samples[i], count, rf_level_db[i]);
}
if (inst->loopback == 3)
jitter_save(&inst->dejitter, samples[i], count);
}
}
#ifdef DEBUG_TIME_CONSUMPTION
t5 = get_time();
d1 += (t2 - t1);
d2 += (t3 - t2);
d3 += (t4 - t3);
d4 += (t5 - t4);
if (get_time() - s >= 1.0) {
printf("duration: %.3f (process TX), %.3f (send TX), %.3f (receive RX), %.3f (process RX)\n", d1, d2, d3, d4);
s = get_time();
d1 = d2 = d3 = d4 = 0;
}
#endif
}
void sender_paging(sender_t *sender, int on)
{
sender->paging_on = on;
}
sender_t *get_sender_by_empfangsfrequenz(double freq)
{
sender_t *sender;
for (sender = sender_head; sender; sender = sender->next) {
if (sender->empfangsfrequenz == freq)
return sender;
}
return NULL;
}

View File

@ -1,107 +0,0 @@
#ifdef HAVE_ALSA
#include "../libsound/sound.h"
#endif
#ifdef HAVE_SDR
#include "../libsdr/sdr.h"
#endif
#include "../libwave/wave.h"
#include "../libsamplerate/samplerate.h"
#include "../libjitter/jitter.h"
#include "../libemphasis/emphasis.h"
#include "../libdisplay/display.h"
#define MAX_SENDER 16
/* how to send a 'paging' signal (trigger transmitter) */
enum paging_signal {
PAGING_SIGNAL_NONE = 0,
PAGING_SIGNAL_TONE,
PAGING_SIGNAL_NOTONE,
PAGING_SIGNAL_POSITIVE,
PAGING_SIGNAL_NEGATIVE,
};
/* common structure of each transmitter */
typedef struct sender {
struct sender *next;
struct sender *slave; /* points to 'slave' that uses next channel of audio device */
struct sender *master; /* if set, the audio device is owned by 'master' */
/* system info */
const char *kanal; /* channel number */
double sendefrequenz; /* transmitter frequency */
double empfangsfrequenz; /* receiver frequency */
double ruffrequenz; /* special paging frequency used for B-Netz */
/* FM/AM levels */
int am; /* use AM instead of FM */
double max_deviation; /* max frequency deviation / level */
double max_modulation; /* max frequency modulated */
double speech_deviation; /* deviation / level of 1000 Hz reference tone at speech level */
double modulation_index; /* AM modulation index */
double max_display; /* level of displaying wave form */
/* audio */
void *audio;
char device[64]; /* audio device name (alsa or sdr) */
void *(*audio_open)(const char *, double *, double *, int *, int, double, int, int, double, double, double, double);
int (*audio_start)(void *);
void (*audio_close)(void *);
int (*audio_write)(void *, sample_t **, uint8_t **, int, enum paging_signal *, int *, int);
int (*audio_read)(void *, sample_t **, int, int, double *);
int (*audio_get_tosend)(void *, int);
int samplerate;
samplerate_t srstate; /* sample rate conversion state */
double rx_gain; /* factor of level to apply on RX samples */
double tx_gain; /* factor of level to apply on TX samples */
int pre_emphasis; /* use pre_emhasis, done by sender */
int de_emphasis; /* use de_emhasis, done by sender */
emphasis_t estate; /* pre and de emphasis */
/* loopback test */
int loopback; /* 0 = off, 1 = internal, 2 = external */
/* record and playback */
const char *write_rx_wave; /* file name pointers */
const char *write_tx_wave;
const char *read_rx_wave;
const char *read_tx_wave;
wave_rec_t wave_rx_rec; /* wave recording (from rx) */
wave_rec_t wave_tx_rec; /* wave recording (from tx) */
wave_play_t wave_rx_play; /* wave playback (as rx) */
wave_play_t wave_tx_play; /* wave playback (as tx) */
/* audio buffer for audio to send to transmitter (also used as loopback buffer) */
jitter_t dejitter;
/* audio buffer for audio to send to caller (20ms = 160 samples @ 8000Hz) */
sample_t rxbuf[160];
int rxbuf_pos; /* current fill of buffer */
/* paging tone */
enum paging_signal paging_signal; /* if paging signal is used and how it is performed */
int paging_on; /* 1 or 0 for on or off */
/* display wave */
dispwav_t dispwav; /* display wave form */
/* display measurements */
dispmeas_t dispmeas; /* display measurements */
} sender_t;
extern sender_t *sender_head;
extern int cant_recover;
extern int check_channel;
int sender_create(sender_t *sender, const char *kanal, double sendefrequenz, double empfangsfrequenz, const char *device, int use_sdr, int samplerate, double rx_gain, double tx_gain, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback, enum paging_signal paging_signal);
void sender_destroy(sender_t *sender);
void sender_set_fm(sender_t *sender, double max_deviation, double max_modulation, double speech_deviation, double max_display);
void sender_set_am(sender_t *sender, double max_modulation, double speech_deviation, double max_display, double modulation_index);
int sender_open_audio(int buffer_size, double interval);
int sender_start_audio(void);
void process_sender_audio(sender_t *sender, int *quit, int buffer_size);
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int count);
void sender_receive(sender_t *sender, sample_t *samples, int count, double rf_level_db);
void sender_paging(sender_t *sender, int on);
sender_t *get_sender_by_empfangsfrequenz(double freq);

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +0,0 @@
void init_testton(void);

View File

@ -1,10 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libmtp.a
libmtp_a_SOURCES = \
mtp.c \
layer2.c \
layer3.c \
crc16.c

View File

@ -1,38 +0,0 @@
#include <stdint.h>
#include "crc16.h"
/*
* 16 12 5
* this is the CCITT CRC 16 polynomial X + X + X + 1.
* This works out to be 0x1021, but the way the algorithm works
* lets us use 0x8408 (the reverse of the bit pattern). The high
* bit is always assumed to be set, thus we only use 16 bits to
* represent the 17 bit value.
* The low bit contains the first bit of the data on the line.
* The low byte(bit) contains the first bit of the CRC on the line.
*/
#define POLY 0x8408
uint16_t calc_crc16(uint8_t *data_p, int length)
{
int i;
uint16_t data;
uint16_t crc = 0xffff;
while (length--) {
data = *data_p++;
for (i = 0; i < 8; i++) {
if ((crc & 1) ^ (data & 1))
crc = (crc >> 1) ^ POLY;
else
crc >>= 1;
data >>= 1;
}
}
crc = ~crc;
return (crc);
}

View File

@ -1,3 +0,0 @@
uint16_t calc_crc16(uint8_t *data_p, int length);

File diff suppressed because it is too large Load Diff

View File

@ -1,103 +0,0 @@
/* Jolly's implementation of MTP layer 3
*
* (C) 2020 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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/>.
*/
/*
* This is only a minimal implementation to make a C-Netz-BTS working.
*/
#define CHAN mtp->name
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include "../libtimer/timer.h"
#include "../libdebug/debug.h"
#include "mtp.h"
/* message from layer 4 */
int mtp_send(mtp_t *mtp, enum mtp_prim prim, uint8_t slc, uint8_t *data, int len)
{
uint8_t buffer[len + 4];
if (prim == MTP_PRIM_DATA) {
PDEBUG_CHAN(DMTP3, DEBUG_DEBUG, "Send frame to remote: SIO=0x%02x DPC=%d OPC=%d SLC=%d %s\n", mtp->sio, mtp->remote_pc, mtp->local_pc, slc, debug_hex(data, len));
/* add header */
buffer[0] = mtp->remote_pc;
buffer[1] = (mtp->remote_pc >> 8) & 0x3f;
buffer[1] |= (mtp->local_pc << 6) & 0xc0;
buffer[2] = mtp->local_pc >> 2;
buffer[3] = (mtp->local_pc >> 10) & 0x0f;
buffer[3] |= slc << 4;
/* add payload */
if (len)
memcpy(buffer + 4, data, len);
data = buffer;
len += 4;
}
/* transmit */
return mtp_l3l2(mtp, prim, mtp->sio, data, len);
}
/* message from layer 2 */
void mtp_l2l3(mtp_t *mtp, enum mtp_prim prim, uint8_t sio, uint8_t *data, int len)
{
uint16_t dpc, opc;
uint8_t slc = 0;
if (prim == MTP_PRIM_DATA) {
if (len < 4) {
PDEBUG_CHAN(DMTP3, DEBUG_NOTICE, "Short frame from layer 2 (len=%d)\n", len);
return;
}
/* parse header */
dpc = data[0];
dpc |= (data[1] << 8) & 0x3f00;
opc = data[1] >> 6;
opc |= data[2] << 2;
opc |= (data[3] << 10) & 0x3c00;
slc = data[3] >> 4;
data += 4;
len -= 4;
PDEBUG_CHAN(DMTP3, DEBUG_DEBUG, "Received frame from remote: SIO=0x%02x DPC=%d OPC=%d SLC=%d %s\n", sio, dpc, opc, slc, debug_hex(data, len));
if (dpc != mtp->local_pc || opc != mtp->remote_pc) {
PDEBUG_CHAN(DMTP3, DEBUG_NOTICE, "Received message with wrong point codes: %d->%d but expecting %d->%d\n", opc, dpc, mtp->remote_pc, mtp->local_pc);
return;
}
if ((sio & 0x0f) == 0x0 && len >= 1) {
PDEBUG_CHAN(DMTP3, DEBUG_NOTICE, "MGMT message received: SLC=%d H0=%d H1=%d %s\n", slc, data[0] & 0xf, data[0] >> 4, debug_hex(data + 1, len - 1));
return;
}
if (sio != mtp->sio) {
PDEBUG_CHAN(DMTP3, DEBUG_NOTICE, "Received message with wrong SIO: 0x%02x but expecting 0x%02x\n", sio, mtp->sio);
return;
}
}
/* receive */
mtp->mtp_receive(mtp->inst, prim, slc, data, len);
}

View File

@ -1,107 +0,0 @@
/* MTP common functions
*
* (C) 2020 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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/>.
*/
#define CHAN mtp->name
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "../libtimer/timer.h"
#include "../libdebug/debug.h"
#include "mtp.h"
static void mtp_t1(struct timer *timer)
{
mtp_t *mtp = (mtp_t *)timer->priv;
mtp_send(mtp, MTP_PRIM_T1_TIMEOUT, 0, NULL, 0);
}
static void mtp_t2(struct timer *timer)
{
mtp_t *mtp = (mtp_t *)timer->priv;
mtp_send(mtp, MTP_PRIM_T2_TIMEOUT, 0, NULL, 0);
}
static void mtp_t3(struct timer *timer)
{
mtp_t *mtp = (mtp_t *)timer->priv;
mtp_send(mtp, MTP_PRIM_T3_TIMEOUT, 0, NULL, 0);
}
static void mtp_t4(struct timer *timer)
{
mtp_t *mtp = (mtp_t *)timer->priv;
mtp_send(mtp, MTP_PRIM_T4_TIMEOUT, 0, NULL, 0);
}
int mtp_init(mtp_t *mtp, const char *name, void *inst, void (*mtp_receive)(void *inst, enum mtp_prim prim, uint8_t slc, uint8_t *data, int len), int bitrate, int ignore_monitor, uint8_t sio, uint16_t local_pc, uint16_t remote_pc)
{
memset(mtp, 0, sizeof(*mtp));
mtp->name = name;
mtp->inst = inst;
mtp->mtp_receive = mtp_receive;
if (bitrate != 64000 && bitrate != 4800) {
fprintf(stderr, "Wrong bit rate %d, please fix!\n", bitrate);
abort();
}
mtp->bitrate = bitrate;
mtp->ignore_monitor = ignore_monitor;
mtp->sio = sio;
mtp->local_pc = local_pc;
mtp->remote_pc = remote_pc;
timer_init(&mtp->t1, mtp_t1, mtp);
timer_init(&mtp->t2, mtp_t2, mtp);
timer_init(&mtp->t3, mtp_t3, mtp);
timer_init(&mtp->t4, mtp_t4, mtp);
return 0;
}
void mtp_exit(mtp_t *mtp)
{
if (!mtp)
return;
timer_exit(&mtp->t1);
timer_exit(&mtp->t2);
timer_exit(&mtp->t3);
timer_exit(&mtp->t4);
mtp_flush(mtp);
}
void mtp_flush(mtp_t *mtp)
{
struct mtp_msg *temp;
while (mtp->tx_queue) {
temp = mtp->tx_queue;
mtp->tx_queue = mtp->tx_queue->next;
free(temp);
}
}

View File

@ -1,142 +0,0 @@
enum mtp_prim {
MTP_PRIM_POWER_ON,
MTP_PRIM_EMERGENCY,
MTP_PRIM_EMERGENCY_CEASES,
MTP_PRIM_LOCAL_PROCESSOR_OUTAGE,
MTP_PRIM_LOCAL_PROCESSOR_RECOVERED,
MTP_PRIM_REMOTE_PROCESSOR_OUTAGE,
MTP_PRIM_REMOTE_PROCESSOR_RECOVERED,
MTP_PRIM_START,
MTP_PRIM_STOP,
MTP_PRIM_DATA,
MTP_PRIM_IN_SERVICE,
MTP_PRIM_OUT_OF_SERVICE,
MTP_PRIM_SIOS,
MTP_PRIM_SIO,
MTP_PRIM_SIN,
MTP_PRIM_SIE,
MTP_PRIM_SIPO,
MTP_PRIM_SIB,
MTP_PRIM_MSU,
MTP_PRIM_FISU,
MTP_PRIM_T1_TIMEOUT,
MTP_PRIM_T2_TIMEOUT,
MTP_PRIM_T3_TIMEOUT,
MTP_PRIM_T4_TIMEOUT,
MTP_PRIM_CORRECT_SU,
MTP_PRIM_ABORT_PROVING,
MTP_PRIM_LINK_FAILURE,
};
#define MTP_CAUSE_ALIGNMENT_TIMEOUT 1
#define MTP_CAUSE_LINK_FAILURE_LOCAL 2
#define MTP_CAUSE_LINK_FAILURE_REMOTE 3
#define MTP_CAUSE_PROVING_FAILURE_LOCAL 4
#define MTP_CAUSE_PROVING_FAILURE_REMOTE 5
#define MTP_CAUSE_PROVING_TIMEOUT 6
enum mtp_l2state {
MTP_L2STATE_POWER_OFF = 0,
MTP_L2STATE_OUT_OF_SERVICE,
MTP_L2STATE_NOT_ALIGNED,
MTP_L2STATE_ALIGNED,
MTP_L2STATE_PROVING,
MTP_L2STATE_ALIGNED_READY,
MTP_L2STATE_ALIGNED_NOT_READY,
MTP_L2STATE_IN_SERVICE,
MTP_L2STATE_PROCESSOR_OUTAGE,
};
struct mtp_msg {
struct mtp_msg *next;
uint8_t sequence;
int transmitted;
int len;
uint8_t sio;
uint8_t data[0];
};
typedef struct mtp {
/* config */
const char *name; /* instance name (channel) */
int bitrate; /* link bit rate (4.8k or 64k) */
int ignore_monitor; /* ignore link monitoring errors */
/* layer 2 states */
enum mtp_l2state l2_state; /* layer 2 state (link & alignment state) */
int local_emergency; /* we request emergency alignment */
int remote_emergency; /* remote requests emergency alignment */
int local_outage; /* current local processor outage */
int remote_outage; /* current remote processor outage */
int tx_lssu; /* what LSSU status to transmit (-1 for nothing) */
struct timer t1; /* timer "alignment ready" */
struct timer t2; /* timer "not aligned" */
struct timer t3; /* timer "aligned" */
struct timer t4; /* proving period timer */
int proving_try; /* counts number of proving attempts */
int further_proving;/* flag that indicates another proving attempt */
/* frame transmission */
uint8_t tx_frame[272]; /* frame memory */
int tx_frame_len; /* number of bytes in frame */
int tx_byte_count; /* count bytes within frame */
int tx_bit_count; /* count bits within byte */
int tx_transmitting;/* transmit frame, if 0: transmit flag */
uint8_t tx_byte; /* current byte transmitting */
uint8_t tx_stream; /* output stream to track bit stuffing */
/* frame reception */
uint8_t rx_frame[272]; /* frame memory */
int rx_byte_count; /* count bytes within frame */
int rx_bit_count; /* count bits within byte */
int rx_receiving; /* receive frame, if 0: no flag yet */
uint8_t rx_byte; /* current byte receiving */
uint8_t rx_stream; /* input stream to track bit stuffing/flag/abort */
int rx_flag_count; /* counter to detect exessively received flags */
int rx_octet_counting; /* we are in octet counting mode */
int rx_octet_count; /* counter when performing octet counting */
/* frame sequencing */
struct mtp_msg *tx_queue; /* head of all messages in queue */
uint8_t tx_queue_seq; /* last sequence assigned to a frame in the queue */
uint8_t tx_seq; /* current sequence number transmitting */
uint8_t fib; /* current FIB */
uint8_t rx_seq; /* last accepted seqeuence number */
uint8_t bib; /* current BIB */
int tx_nack; /* next frame shall send a NAK by inverting BIB */
/* monitor */
int proving_errors;/* counts errors while proving */
int monitor_errors;/* counts link errors */
int monitor_good; /* counts good frames */
/* layer 3 */
void (*mtp_receive)(void *inst, enum mtp_prim prim, uint8_t slc, uint8_t *data, int len);
void *inst;
uint8_t sio;
uint16_t local_pc, remote_pc;
} mtp_t;
int mtp_init(mtp_t *mtp, const char *name, void *inst, void (*mtp_receive)(void *inst, enum mtp_prim prim, uint8_t slc, uint8_t *data, int len), int bitrate, int ignore_monitor, uint8_t sio, uint16_t local_pc, uint16_t remote_pc);
void mtp_exit(mtp_t *mtp);
void mtp_flush(mtp_t *mtp);
void mtp_l2_new_state(mtp_t *mtp, enum mtp_l2state state);
int mtp_send(mtp_t *mtp, enum mtp_prim prim, uint8_t slc, uint8_t *data, int len);
int mtp_l3l2(mtp_t *mtp, enum mtp_prim prim, uint8_t sio, uint8_t *data, int len);
void mtp_l2l3(mtp_t *mtp, enum mtp_prim prim, uint8_t sio, uint8_t *data, int len);
uint8_t mtp_send_bit(mtp_t *mtp);
void mtp_receive_bit(mtp_t *mtp, uint8_t bit);
void mtp_send_block(mtp_t *mtp, uint8_t *data, int len);
void mtp_receive_block(mtp_t *mtp, uint8_t *data, int len);
/* overload receive functions to redirect for sniffing */
extern void (*func_mtp_receive_lssu)(mtp_t *mtp, uint8_t fsn, uint8_t bib, uint8_t status);
extern void (*func_mtp_receive_msu)(mtp_t *mtp, uint8_t bsn, uint8_t bib, uint8_t fsn, uint8_t fib, uint8_t sio, uint8_t *data, int len);
extern void (*func_mtp_receive_fisu)(mtp_t *mtp, uint8_t bsn, uint8_t bib, uint8_t fsn, uint8_t fib);

View File

@ -1,7 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = liboptions.a
liboptions_a_SOURCES = \
options.c

View File

@ -1,339 +0,0 @@
/* command line options and config file parsing
*
* (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "options.h"
#include "../libdebug/debug.h"
typedef struct option {
struct option *next;
int short_option;
const char *long_option;
int parameter_count;
} option_t;
static option_t *option_head = NULL;
static option_t **option_tailp = &option_head;
static int first_option = 1;
static struct options_strdup_entry {
struct options_strdup_entry *next;
char s[1];
} *options_strdup_list = NULL;
char *options_strdup(const char *s)
{
struct options_strdup_entry *o;
o = malloc(sizeof(struct options_strdup_entry) + strlen(s));
if (!o) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "No mem!\n");
abort();
}
o->next = options_strdup_list;
options_strdup_list = o;
strcpy(o->s, s);
return o->s;
}
void option_add(int short_option, const char *long_option, int parameter_count)
{
option_t *option;
/* check if option already exists or is not allowed */
for (option = option_head; option; option = option->next) {
if (!strcmp(option->long_option, "config")) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Option '%s' is not allowed to add, please fix!\n", option->long_option);
abort();
}
if (option->short_option == short_option
|| !strcmp(option->long_option, long_option)) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Option '%s' added twice, please fix!\n", option->long_option);
abort();
}
}
option = calloc(1, sizeof(*option));
if (!option) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "No mem!\n");
abort();
}
option->short_option = short_option;
option->long_option = long_option;
option->parameter_count = parameter_count;
*option_tailp = option;
option_tailp = &(option->next);
}
int options_config_file(int argc, char *argv[], const char *config_file, int (*handle_options)(int short_option, int argi, char *argv[]))
{
static const char *home;
char config[256];
FILE *fp;
char buffer[256], opt[256], param[256], *p, *args[16];
char params[1024];
int line;
int rc = 1;
int i, j, quote;
option_t *option;
/* select for alternative config file */
if (argc > 2 && !strcmp(argv[1], "--config"))
config_file = argv[2];
/* add home directory */
if (config_file[0] == '~' && config_file[1] == '/') {
home = getenv("HOME");
if (home == NULL)
return 1;
sprintf(config, "%s/%s", home, config_file + 2);
} else
strcpy(config, config_file);
/* open config file */
fp = fopen(config, "r");
if (!fp) {
PDEBUG(DOPTIONS, DEBUG_INFO, "Config file '%s' seems not to exist, using command line options only.\n", config);
return 1;
}
/* parse config file */
line = 0;
while((fgets(buffer, sizeof(buffer), fp))) {
line++;
/* prevent buffer overflow */
buffer[sizeof(buffer) - 1] = '\0';
/* cut away new-line and white spaces */
while (buffer[0] && buffer[strlen(buffer) - 1] <= ' ')
buffer[strlen(buffer) - 1] = '\0';
p = buffer;
/* remove white spaces in front of first keyword */
while (*p > '\0' && *p <= ' ')
p++;
/* ignore '#' lines */
if (*p == '#')
continue;
/* get option form line */
i = 0;
while (*p > ' ')
opt[i++] = *p++;
opt[i] = '\0';
if (opt[0] == '\0')
continue;
/* skip white spaces behind option */
while (*p > '\0' && *p <= ' ')
p++;
/* get param from line */
params[0] = '\0';
i = 0;
while (*p) {
/* copy parameter */
j = 0;
quote = 0;
while (*p) {
/* escape allows all following characters */
if (*p == '\\') {
p++;
if (*p)
param[j++] = *p++;
continue;
}
/* no quote, check for them or break on white space */
if (quote == 0) {
if (*p == '\'') {
quote = 1;
p++;
continue;
}
if (*p == '\"') {
quote = 2;
p++;
continue;
}
if (*p <= ' ')
break;
}
/* single quote, check for unquote */
if (quote == 1 && *p == '\'') {
quote = 0;
p++;
continue;
}
/* double quote, check for unquote */
if (quote == 2 && *p == '\"') {
quote = 0;
p++;
continue;
}
/* copy character */
param[j++] = *p++;
}
param[j] = '\0';
args[i] = options_strdup(param);
sprintf(strchr(params, '\0'), " '%s'", param);
/* skip white spaces behind option */
while (*p > '\0' && *p <= ' ')
p++;
i++;
}
/* search option */
for (option = option_head; option; option = option->next) {
if (opt[0] == option->short_option && opt[1] == '\0') {
PDEBUG(DOPTIONS, DEBUG_INFO, "Config file option '%s' ('%s'), parameter%s\n", opt, option->long_option, params);
break;
}
if (!strcmp(opt, option->long_option)) {
PDEBUG(DOPTIONS, DEBUG_INFO, "Config file option '%s', parameter%s\n", opt, params);
break;
}
}
if (!option) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given option '%s' in config file '%s' at line %d is not a valid option, use '-h' for help!\n", opt, config_file, line);
rc = -EINVAL;
goto done;
}
if (option->parameter_count != i) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given option '%s' in config file '%s' at line %d requires %d parameter(s), use '-h' for help!\n", opt, config_file, line, option->parameter_count);
return -EINVAL;
}
rc = handle_options(option->short_option, 0, args);
if (rc <= 0)
goto done;
first_option = 0;
}
done:
/* close config file */
fclose(fp);
return rc;
}
int options_command_line(int argc, char *argv[], int (*handle_options)(int short_option, int argi, char *argv[]))
{
option_t *option;
char params[1024];
int argi, i;
int rc;
for (argi = 1; argi < argc; argi++) {
/* --config */
if (!strcmp(argv[argi], "--config")) {
if (argi > 1) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' must be the first option specified, use '-h' for help!\n", argv[argi]);
return -EINVAL;
}
if (argc <= 2) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' requires 1 parameter, use '-h' for help!\n", argv[argi]);
return -EINVAL;
}
argi += 1;
continue;
}
if (argv[argi][0] == '-') {
if (argv[argi][1] != '-') {
if (strlen(argv[argi]) != 2) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' exceeds one character, use '-h' for help!\n", argv[argi]);
return -EINVAL;
}
/* -x */
for (option = option_head; option; option = option->next) {
if (argv[argi][1] == option->short_option) {
if (option->parameter_count && argi + option->parameter_count < argc) {
params[0] = '\0';
for (i = 0; i < option->parameter_count; i++)
sprintf(strchr(params, '\0'), " '%s'", argv[argi + 1 + i]);
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s' ('--%s'), parameter%s\n", argv[argi], option->long_option, params);
} else
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s' ('--%s')\n", argv[argi], option->long_option);
break;
}
}
} else {
/* --xxxxxx */
for (option = option_head; option; option = option->next) {
if (!strcmp(argv[argi] + 2, option->long_option)) {
if (option->parameter_count && argi + option->parameter_count < argc) {
params[0] = '\0';
for (i = 0; i < option->parameter_count; i++)
sprintf(strchr(params, '\0'), " '%s'", argv[argi + 1 + i]);
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s', parameter%s\n", argv[argi], params);
} else
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s'\n", argv[argi]);
break;
}
}
}
if (!option) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' is not a valid option, use '-h' for help!\n", argv[argi]);
return -EINVAL;
}
if (argi + option->parameter_count >= argc) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' requires %d parameter(s), use '-h' for help!\n", argv[argi], option->parameter_count);
return -EINVAL;
}
rc = handle_options(option->short_option, argi + 1, argv);
if (rc <= 0)
return rc;
first_option = 0;
argi += option->parameter_count;
} else
break;
}
/* no more options, so we check if there is an option after a non-option parameter */
for (i = argi; i < argc; i++) {
if (argv[i][0] == '-') {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' behind command line parameter '%s' not allowed! Please put all command line options before command line parameter(s).\n", argv[i], argv[argi]);
return -EINVAL;
}
}
return argi;
}
int option_is_first(void)
{
return first_option;
}
void options_free(void)
{
while (options_strdup_list) {
struct options_strdup_entry *o;
o = options_strdup_list;
options_strdup_list = o->next;
free(o);
}
while (option_head) {
option_t *o;
o = option_head;
option_head = o->next;
free(o);
}
option_tailp = &option_head;
}

View File

@ -1,8 +0,0 @@
char *options_strdup(const char *s);
void option_add(int short_option, const char *long_option, int parameter_count);
int options_config_file(int argc, char *argv[], const char *config_file, int (*handle_options)(int short_option, int argi, char *argv[]));
int options_command_line(int argc, char *argv[], int (*handle_options)(int short_option, int argi, char *argv[]));
int option_is_first(void);
void options_free(void);

View File

@ -1,15 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libosmocc.a
libosmocc_a_SOURCES = \
message.c \
socket.c \
cause.c \
screen.c \
endpoint.c \
session.c \
sdp.c \
rtp.c \
helper.c

View File

@ -1,252 +0,0 @@
/* OSMO-CC Processing: convert causes
*
* (C) 2019 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdint.h>
#include <arpa/inet.h>
#include "message.h"
#include "cause.h"
/* stolen from freeswitch */
/* map sip responses to QSIG cause codes ala RFC4497 section 8.4.4 */
static uint8_t status2isdn_cause(uint16_t status)
{
switch (status) {
case 200:
return 16; //SWITCH_CAUSE_NORMAL_CLEARING;
case 401:
case 402:
case 403:
case 407:
case 603:
return 21; //SWITCH_CAUSE_CALL_REJECTED;
case 404:
return 1; //SWITCH_CAUSE_UNALLOCATED_NUMBER;
case 485:
case 604:
return 3; //SWITCH_CAUSE_NO_ROUTE_DESTINATION;
case 408:
case 504:
return 102; //SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE;
case 410:
return 22; //SWITCH_CAUSE_NUMBER_CHANGED;
case 413:
case 414:
case 416:
case 420:
case 421:
case 423:
case 505:
case 513:
return 127; //SWITCH_CAUSE_INTERWORKING;
case 480:
return 180; //SWITCH_CAUSE_NO_USER_RESPONSE;
case 400:
case 481:
case 500:
case 503:
return 41; //SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE;
case 486:
case 600:
return 17; //SWITCH_CAUSE_USER_BUSY;
case 484:
return 28; //SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
case 488:
case 606:
return 88; //SWITCH_CAUSE_INCOMPATIBLE_DESTINATION;
case 502:
return 38; //SWITCH_CAUSE_NETWORK_OUT_OF_ORDER;
case 405:
return 63; //SWITCH_CAUSE_SERVICE_UNAVAILABLE;
case 406:
case 415:
case 501:
return 79; //SWITCH_CAUSE_SERVICE_NOT_IMPLEMENTED;
case 482:
case 483:
return 25; //SWITCH_CAUSE_EXCHANGE_ROUTING_ERROR;
case 487:
return 31; //??? SWITCH_CAUSE_ORIGINATOR_CANCEL;
default:
return 31; //SWITCH_CAUSE_NORMAL_UNSPECIFIED;
}
}
static uint16_t isdn2status_cause(uint8_t cause, uint8_t location)
{
switch (cause) {
case 1:
return 404;
case 2:
return 404;
case 3:
return 404;
case 17:
return 486;
case 18:
return 408;
case 19:
return 480;
case 20:
return 480;
case 21:
if (location == OSMO_CC_LOCATION_USER)
return 603;
return 403;
case 22:
//return 301;
return 410;
case 23:
return 410;
case 26:
return 404;
case 27:
return 502;
case 28:
return 484;
case 29:
return 501;
case 31:
return 480;
case 34:
return 503;
case 38:
return 503;
case 41:
return 503;
case 42:
return 503;
case 47:
return 503;
case 55:
return 403;
case 57:
return 403;
case 58:
return 503;
case 65:
return 488;
case 69:
return 501;
case 70:
return 488;
case 79:
return 501;
case 87:
return 403;
case 88:
return 503;
case 102:
return 504;
case 111:
return 500;
case 127:
return 500;
default:
return 468;
}
}
static uint8_t socket2isdn_cause(uint8_t sock)
{
switch (sock) {
case OSMO_CC_SOCKET_CAUSE_FAILED:
return 47;
case OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE:
return 41;
case OSMO_CC_SOCKET_CAUSE_VERSION_MISMATCH:
return 38;
case OSMO_CC_SOCKET_CAUSE_TIMEOUT:
return 41;
default:
return 31;
}
}
void osmo_cc_convert_cause(struct osmo_cc_ie_cause *cause)
{
/* complete cause, from socket cause */
if (cause->socket_cause && cause->isdn_cause == 0 && ntohs(cause->sip_cause_networkorder) == 0)
cause->isdn_cause = socket2isdn_cause(cause->socket_cause);
/* convert ISDN cause to SIP cause */
if (cause->isdn_cause && ntohs(cause->sip_cause_networkorder) == 0) {
cause->sip_cause_networkorder = htons(isdn2status_cause(cause->isdn_cause, cause->location));
}
/* convert SIP cause to ISDN cause */
if (ntohs(cause->sip_cause_networkorder) && cause->isdn_cause == 0) {
cause->isdn_cause = status2isdn_cause(ntohs(cause->sip_cause_networkorder));
}
/* no cause at all: use Normal Call Clearing */
if (cause->isdn_cause == 0 && ntohs(cause->sip_cause_networkorder) == 0) {
cause->isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR;
cause->sip_cause_networkorder = htons(486);
}
}
void osmo_cc_convert_cause_msg(osmo_cc_msg_t *msg)
{
void *ie;
uint8_t type;
uint16_t length;
void *value;
/* search for (all) cause IE and convert the values, if needed */
ie = msg->data;
while ((value = osmo_cc_msg_sep_ie(msg, &ie, &type, &length))) {
if (type == OSMO_CC_IE_CAUSE && length >= sizeof(struct osmo_cc_ie_cause)) {
osmo_cc_convert_cause(value);
}
}
}
uint8_t osmo_cc_collect_cause(uint8_t old_cause, uint8_t new_cause)
{
/* first cause */
if (old_cause == 0)
return new_cause;
/* first prio: return 17 */
if (old_cause == OSMO_CC_ISDN_CAUSE_USER_BUSY
|| new_cause == OSMO_CC_ISDN_CAUSE_USER_BUSY)
return OSMO_CC_ISDN_CAUSE_USER_BUSY;
/* second prio: return 21 */
if (old_cause == OSMO_CC_ISDN_CAUSE_CALL_REJECTED
|| new_cause == OSMO_CC_ISDN_CAUSE_CALL_REJECTED)
return OSMO_CC_ISDN_CAUSE_CALL_REJECTED;
/* third prio: return other than 88 and 18 (what ever was first) */
if (old_cause != OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST
&& old_cause != OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND)
return old_cause;
if (new_cause != OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST
&& new_cause != OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND)
return new_cause;
/* fourth prio: return 88 */
if (old_cause == OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST
|| new_cause == OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST)
return OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST;
/* fith prio: return 18 */
return OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND;
}

View File

@ -1,5 +0,0 @@
void osmo_cc_convert_cause(struct osmo_cc_ie_cause *cause);
void osmo_cc_convert_cause_msg(osmo_cc_msg_t *msg);
uint8_t osmo_cc_collect_cause(uint8_t old_cause, uint8_t new_cause);

File diff suppressed because it is too large Load Diff

View File

@ -1,131 +0,0 @@
#ifndef OSMO_CC_ENDPOINT_H
#define OSMO_CC_ENDPOINT_H
#include "message.h"
#include "socket.h"
#include "cause.h"
/* special osmo-cc error codes */
#define OSMO_CC_RC_SEE_ERRNO -1
#define OSMO_CC_RC_VERSION_MISMATCH 1
#define OSMO_CC_ATTACH_TIMER 2
/* call control state */
enum osmo_cc_state {
OSMO_CC_STATE_IDLE = 0,
/* call states */
OSMO_CC_STATE_INIT_OUT, /* outgoing CC-SETUP-REQ sent */
OSMO_CC_STATE_INIT_IN, /* incoming CC-SETUP-IND received */
OSMO_CC_STATE_OVERLAP_OUT, /* received CC-SETUP-ACK-IND on outgoing call */
OSMO_CC_STATE_OVERLAP_IN, /* sent CC-SETUP-ACK-REQ on incoming call */
OSMO_CC_STATE_PROCEEDING_OUT, /* received CC-PROC-IND on outgoing call */
OSMO_CC_STATE_PROCEEDING_IN, /* sent CC-PROC-REQ on incoming call */
OSMO_CC_STATE_ALERTING_OUT, /* received CC-ALERT-IND on outgoing call */
OSMO_CC_STATE_ALERTING_IN, /* sent CC-ALERT-REQ on incoming call */
OSMO_CC_STATE_CONNECTING_OUT, /* received CC-SETUP-CNF on outgoing call */
OSMO_CC_STATE_CONNECTING_IN, /* sent CC-SETUP-RSP on incoming call */
OSMO_CC_STATE_ACTIVE, /* received or sent CC-SETUP-COMPL-* */
OSMO_CC_STATE_DISCONNECTING_OUT, /* sent CC-DISC-REQ */
OSMO_CC_STATE_DISCONNECTING_IN, /* received CC-DISC-IND */
OSMO_CC_STATE_DISC_COLLISION, /* received CC-DISC-IND after sending CC-DISC_REQ */
OSMO_CC_STATE_RELEASING_OUT, /* sent CC-REL-REQ */
/* attachment states */
OSMO_CC_STATE_ATTACH_SENT, /* outgoing CC-ATT-REQ sent to socket */
OSMO_CC_STATE_ATTACH_OUT, /* received CC-ATT-RSP on outgoing socket */
OSMO_CC_STATE_ATTACH_WAIT, /* wait for outgoing attachment after failure */
OSMO_CC_STATE_ATTACH_IN, /* incoming CC-ATT-REQ received from socket*/
};
/* sample type */
typedef int16_t osmo_cc_sample_t;
#define OSMO_CC_SAMPLE_MILLIWATT 23170 /* peak sine at -3 dB of full sample range */
#define OSMO_CC_SAMPLE_SPEECH 3672 /* peak speech at -16 dB of milliwatt */
#define OSMO_CC_SAMPLE_MIN -32768 /* lowest level */
#define OSMO_CC_SAMPLE_MAX 32767 /* highest level */
#include "session.h"
struct osmo_cc_call;
typedef struct osmo_cc_screen_list {
struct osmo_cc_screen_list *next;
int has_from_type;
uint8_t from_type;
int has_from_present;
uint8_t from_present;
char from[128];
int has_to_type;
uint8_t to_type;
int has_to_present;
uint8_t to_present;
char to[128];
} osmo_cc_screen_list_t;
/* endpoint instance */
typedef struct osmo_cc_endpoint {
struct osmo_cc_endpoint *next;
void *priv;
void (*ll_msg_cb)(struct osmo_cc_endpoint *ep, uint32_t callref, osmo_cc_msg_t *msg);
void (*ul_msg_cb)(struct osmo_cc_call *call, osmo_cc_msg_t *msg);
osmo_cc_msg_list_t *ll_queue; /* messages towards lower layer */
struct osmo_cc_call *call_list;
const char *local_name; /* name of interface */
const char *local_address; /* host+port */
const char *local_host;
uint16_t local_port;
const char *remote_address; /* host+port */
const char *remote_host;
uint16_t remote_port;
uint8_t serving_location;
osmo_cc_socket_t os;
osmo_cc_screen_list_t *screen_calling_in;
osmo_cc_screen_list_t *screen_called_in;
osmo_cc_screen_list_t *screen_calling_out;
osmo_cc_screen_list_t *screen_called_out;
int remote_auto; /* automatic remote address */
struct timer attach_timer; /* timer to retry attachment */
osmo_cc_session_config_t session_config; /* SDP/RTP default configuration */
} osmo_cc_endpoint_t;
extern osmo_cc_endpoint_t *osmo_cc_endpoint_list;
/* call process */
typedef struct osmo_cc_call {
struct osmo_cc_call *next;
osmo_cc_endpoint_t *ep;
enum osmo_cc_state state;
int lower_layer_released; /* when lower layer sent release, while upper layer gets a disconnect */
int upper_layer_released; /* when upper layer sent release, while lower layer gets a disconnect */
uint32_t callref;
osmo_cc_msg_list_t *sock_queue; /* messages from socket */
const char *attached_host; /* host and port from remote peer that attached to us */
uint16_t attached_port;
const char *attached_name; /* interface name from remote peer that attached to us */
} osmo_cc_call_t;
/* returns 0 if ok
* returns <0 for error as indicated
* returns >=1 to indicate osmo-cc error code
*/
void osmo_cc_help(void);
int osmo_cc_new(osmo_cc_endpoint_t *ep, const char *version, const char *name, uint8_t serving_location, void (*ll_msg_cb)(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg), void (*ul_msg_cb)(osmo_cc_call_t *call, osmo_cc_msg_t *msg), void *priv, int argc, const char *argv[]);
void osmo_cc_delete(struct osmo_cc_endpoint *ep);
int osmo_cc_handle(void);
osmo_cc_call_t *osmo_cc_call_by_callref(osmo_cc_endpoint_t *ep, uint32_t callref);
osmo_cc_call_t *osmo_cc_get_attached_interface(osmo_cc_endpoint_t *ep, const char *interface);
void osmo_cc_ll_msg(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg);
void osmo_cc_ul_msg(void *priv, uint32_t callref, osmo_cc_msg_t *msg);
osmo_cc_call_t *osmo_cc_call_new(osmo_cc_endpoint_t *ep);
void osmo_cc_call_delete(struct osmo_cc_call *call);
enum osmo_cc_session_addrtype osmo_cc_address_type(const char *address);
const char *osmo_cc_host_of_address(const char *address);
const char *osmo_cc_port_of_address(const char *address);
#include "rtp.h"
#include "sdp.h"
#include "screen.h"
#endif /* OSMO_CC_ENDPOINT_H */

View File

@ -1,191 +0,0 @@
/* Osmo-CC: helpers to simplify Osmo-CC usage
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include <inttypes.h>
#include "../libtimer/timer.h"
#include "../libdebug/debug.h"
#include "endpoint.h"
#include "helper.h"
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug)
{
osmo_cc_session_t *session;
osmo_cc_session_media_t *media;
const char *sdp;
int i;
session = osmo_cc_new_session(conf, priv, NULL, NULL, NULL, 0, 0, NULL, NULL, debug);
if (!session)
return NULL;
media = osmo_cc_add_media(session, 0, 0, NULL, osmo_cc_session_media_type_audio, 0, osmo_cc_session_media_proto_rtp, 1, 1, receiver, debug);
osmo_cc_rtp_open(media);
for (i = 0; codecs[i].payload_name; i++)
osmo_cc_add_codec(media, codecs[i].payload_name, codecs[i].payload_rate, codecs[i].payload_channels, codecs[i].encoder, codecs[i].decoder, debug);
sdp = osmo_cc_session_send_offer(session);
osmo_cc_add_ie_sdp(msg, sdp);
return session;
}
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec)
{
char offer_sdp[65536];
const char *accept_sdp;
osmo_cc_session_media_t *media, *selected_media;
osmo_cc_session_codec_t *codec, *selected_codec, *telephone_event;
int rc;
int i, selected_codec_i, telephone_event_i;
if (*session_p) {
PDEBUG(DCC, DEBUG_ERROR, "Session already set, please fix!\n");
abort();
}
if (*codec_p) {
PDEBUG(DCC, DEBUG_ERROR, "Codec already set, please fix!\n");
abort();
}
/* SDP IE */
rc = osmo_cc_get_ie_sdp(msg, 0, offer_sdp, sizeof(offer_sdp));
if (rc < 0) {
PDEBUG(DCC, DEBUG_ERROR, "There is no SDP included in setup request.\n");
return NULL;
}
*session_p = osmo_cc_session_receive_offer(conf, priv, offer_sdp);
if (!*session_p) {
PDEBUG(DCC, DEBUG_ERROR, "Failed to parse SDP.\n");
return NULL;
}
selected_media = NULL;
osmo_cc_session_for_each_media((*session_p)->media_list, media) {
/* only audio */
if (media->description.type != osmo_cc_session_media_type_audio)
continue;
selected_codec_i = -1;
selected_codec = NULL;
telephone_event_i = -1;
telephone_event = NULL;
osmo_cc_session_for_each_codec(media->codec_list, codec) {
if (!!strcasecmp(codec->payload_name, "telephone-event")) {
for (i = 0; codecs[i].payload_name; i++) {
if (osmo_cc_session_if_codec(codec, codecs[i].payload_name, codecs[i].payload_rate, codecs[i].payload_channels)) {
/* select the first matchting codec or the one we prefer */
if (selected_codec_i < 0 || i < selected_codec_i) {
selected_codec = codec;
selected_codec_i = i;
selected_media = media;
}
/* if we don't force our preferred codec, use the preferred one from the remote */
if (!force_our_codec)
break;
}
}
} else {
/* special case: add telephone-event, if supported */
for (i = 0; codecs[i].payload_name; i++) {
if (!!strcasecmp(codecs[i].payload_name, "telephone-event"))
continue;
telephone_event = codec;
telephone_event_i = i;
break;
}
}
}
/* codec is selected within this media, we are done */
if (selected_codec)
break;
}
if (!selected_codec) {
PDEBUG(DCC, DEBUG_ERROR, "No codec found in setup message that we support.\n");
osmo_cc_free_session(*session_p);
return NULL;
}
osmo_cc_session_accept_codec(selected_codec, codecs[selected_codec_i].encoder, codecs[selected_codec_i].decoder);
if (telephone_event)
osmo_cc_session_accept_codec(telephone_event, codecs[telephone_event_i].encoder, codecs[telephone_event_i].decoder);
osmo_cc_session_accept_media(selected_media, 0, 0, NULL, 1, 1, receiver);
osmo_cc_rtp_open(selected_media);
osmo_cc_rtp_connect(selected_media);
*codec_p = selected_codec;
accept_sdp = osmo_cc_session_send_answer(*session_p);
if (!accept_sdp) {
osmo_cc_free_session(*session_p);
return NULL;
}
return accept_sdp;
}
int osmo_cc_helper_audio_negotiate(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p)
{
char sdp[65536];
osmo_cc_session_media_t *media;
int rc;
if (!(*session_p)) {
PDEBUG(DCC, DEBUG_ERROR, "Session not set, please fix!\n");
abort();
}
/* once done, just ignore further messages that reply to setup */
if (*codec_p)
return 0;
/* SDP IE */
rc = osmo_cc_get_ie_sdp(msg, 0, sdp, sizeof(sdp));
if (rc < 0)
return 0; // no reply in this message
rc = osmo_cc_session_receive_answer(*session_p, sdp);
if (rc < 0)
return rc;
osmo_cc_session_for_each_media((*session_p)->media_list, media) {
/* only audio */
if (media->description.type != osmo_cc_session_media_type_audio)
continue;
/* select first codec, if one was accpeted */
if (media->codec_list)
*codec_p = media->codec_list;
if (*codec_p) {
osmo_cc_rtp_connect(media);
/* no more media streams */
break;
}
}
if (!(*codec_p)) {
PDEBUG(DCC, DEBUG_ERROR, "No codec found in setup reply message that we support.\n");
return -EIO;
}
return 0;
}

View File

@ -1,13 +0,0 @@
struct osmo_cc_helper_audio_codecs {
const char *payload_name;
uint32_t payload_rate;
int payload_channels;
void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
};
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug);
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec);
int osmo_cc_helper_audio_negotiate(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p);

File diff suppressed because it is too large Load Diff

View File

@ -1,508 +0,0 @@
#ifndef OSMO_CC_MSG_H
#define OSMO_CC_MSG_H
#define OSMO_CC_VERSION "OSMOCCv1"
/* call control messages types */
enum osmo_cc_msg_type {
OSMO_CC_MSG_SETUP_REQ = 0x00,
OSMO_CC_MSG_SETUP_IND = 0x01,
OSMO_CC_MSG_REJ_REQ = 0x10,
OSMO_CC_MSG_REJ_IND = 0x11,
OSMO_CC_MSG_SETUP_ACK_REQ = 0x20,
OSMO_CC_MSG_SETUP_ACK_IND = 0x21,
OSMO_CC_MSG_PROC_REQ = 0x30,
OSMO_CC_MSG_PROC_IND = 0x31,
OSMO_CC_MSG_ALERT_REQ = 0x40,
OSMO_CC_MSG_ALERT_IND = 0x41,
OSMO_CC_MSG_SETUP_RSP = 0x02,
OSMO_CC_MSG_SETUP_CNF = 0x03,
OSMO_CC_MSG_SETUP_COMP_REQ = 0x50,
OSMO_CC_MSG_SETUP_COMP_IND = 0x51,
OSMO_CC_MSG_DISC_REQ = 0x60,
OSMO_CC_MSG_DISC_IND = 0x61,
OSMO_CC_MSG_REL_REQ = 0x70,
OSMO_CC_MSG_REL_CNF = 0x73,
OSMO_CC_MSG_REL_IND = 0x71,
OSMO_CC_MSG_PROGRESS_REQ = 0x80,
OSMO_CC_MSG_PROGRESS_IND = 0x81,
OSMO_CC_MSG_NOTIFY_REQ = 0x84,
OSMO_CC_MSG_NOTIFY_IND = 0x85,
OSMO_CC_MSG_INFO_REQ = 0x88,
OSMO_CC_MSG_INFO_IND = 0x89,
OSMO_CC_MSG_ATTACH_REQ = 0xf8,
OSMO_CC_MSG_ATTACH_IND = 0xf9,
OSMO_CC_MSG_ATTACH_RSP = 0xfa,
OSMO_CC_MSG_ATTACH_CNF = 0xfb,
OSMO_CC_MSG_DUMMY_REQ = 0xfc,
};
#define OSMO_CC_MSG_NUM 0x100
#define OSMO_CC_MSG_MASK 0x03,
#define OSMO_CC_MSG_REQ 0x00,
#define OSMO_CC_MSG_IND 0x01,
#define OSMO_CC_MSG_RSP 0x02,
#define OSMO_CC_MSG_CNF 0x03,
const char *osmo_cc_msg_value2name(int value);
int osmo_cc_msg_name2value(const char *name);
/* information elements */
enum osmo_cc_ie_type {
OSMO_CC_IE_CALLED = 0x11,
OSMO_CC_IE_CALLED_SUB = 0x12,
OSMO_CC_IE_CALLED_NAME = 0x13,
OSMO_CC_IE_CALLED_INTERFACE = 0x14,
OSMO_CC_IE_DTMF = 0x1d,
OSMO_CC_IE_KEYPAD = 0x1e,
OSMO_CC_IE_COMPLETE = 0x1f,
OSMO_CC_IE_CALLING = 0x21,
OSMO_CC_IE_CALLING_SUB = 0x22,
OSMO_CC_IE_CALLING_NAME = 0x23,
OSMO_CC_IE_CALLING_INTERFACE = 0x24,
OSMO_CC_IE_CALLING_NETWORK = 0x2f,
OSMO_CC_IE_REDIR = 0x31,
OSMO_CC_IE_PROGRESS = 0x32,
OSMO_CC_IE_NOTIFY = 0x33,
OSMO_CC_IE_DISPLAY = 0x34,
OSMO_CC_IE_CAUSE = 0x41,
OSMO_CC_IE_BEARER = 0x51,
OSMO_CC_IE_SDP = 0x52,
OSMO_CC_IE_SOCKET_ADDRESS = 0x5e,
OSMO_CC_IE_PRIVATE = 0x5f,
};
#define OSMO_CC_IE_NUM 0x100
const char *osmo_cc_ie_value2name(int value);
int osmo_cc_ie_name2value(const char *name);
/* type of number, see ITU-T Rec. Q.931 */
#define OSMO_CC_TYPE_UNKNOWN 0
#define OSMO_CC_TYPE_INTERNATIONAL 1
#define OSMO_CC_TYPE_NATIONAL 2
#define OSMO_CC_TYPE_NETWORK 3
#define OSMO_CC_TYPE_SUBSCRIBER 4
#define OSMO_CC_TYPE_ABBREVIATED 5
#define OSMO_CC_TYPE_RESERVED 7
#define OSMO_CC_TYPE_NUM 8
const char *osmo_cc_type_value2name(int value);
int osmo_cc_type_name2value(const char *name);
/* numbering plan, see ITU-T Rec. Q.931 */
#define OSMO_CC_PLAN_UNKNOWN 0
#define OSMO_CC_PLAN_TELEPHONY 1
#define OSMO_CC_PLAN_DATA 3
#define OSMO_CC_PLAN_TTY 4
#define OSMO_CC_PLAN_NATIONAL_STANDARD 8
#define OSMO_CC_PLAN_PRIVATE 9
#define OSMO_CC_PLAN_RESERVED 15
#define OSMO_CC_PLAN_NUM 16
const char *osmo_cc_plan_value2name(int value);
int osmo_cc_plan_name2value(const char *name);
/* presentation indicator, see ITU-T Rec. Q.931 */
#define OSMO_CC_PRESENT_ALLOWED 0
#define OSMO_CC_PRESENT_RESTRICTED 1
#define OSMO_CC_PRESENT_NOT_AVAIL 2
#define OSMO_CC_PRESENT_RESERVED 3
#define OSMO_CC_PRESENT_NUM 4
const char *osmo_cc_present_value2name(int value);
int osmo_cc_present_name2value(const char *name);
/* screening indicator, see ITU-T Rec. Q.931 */
#define OSMO_CC_SCREEN_USER_UNSCREENED 0
#define OSMO_CC_SCREEN_USER_VERIFIED_PASSED 1
#define OSMO_CC_SCREEN_USER_VERIFIED_FAILED 2
#define OSMO_CC_SCREEN_NETWORK 3
#define OSMO_CC_SCREEN_NUM 4
const char *osmo_cc_screen_value2name(int value);
int osmo_cc_screen_name2value(const char *name);
/* screening indicator, see ITU-T Rec. Q.931 */
#define OSMO_CC_REDIR_REASON_UNKNOWN 0
#define OSMO_CC_REDIR_REASON_CFB 1
#define OSMO_CC_REDIR_REASON_CFNR 2
#define OSMO_CC_REDIR_REASON_CD 4
#define OSMO_CC_REDIR_REASON_CF_OUTOFORDER 9
#define OSMO_CC_REDIR_REASON_CF_BY_DTE 10
#define OSMO_CC_REDIR_REASON_CFU 15
#define OSMO_CC_REDIR_REASON_NUM 16
const char *osmo_cc_redir_reason_value2name(int value);
int osmo_cc_redir_reason_name2value(const char *name);
/* notification indicator, see ITU-T Rec. Q.931 ff. */
#define OSMO_CC_NOTIFY_USER_SUSPENDED 0x00
#define OSMO_CC_NOTIFY_USER_RESUMED 0x01
#define OSMO_CC_NOTIFY_BEARER_SERVICE_CHANGE 0x02
#define OSMO_CC_NOTIFY_CALL_COMPLETION_DELAY 0x03
#define OSMO_CC_NOTIFY_CONFERENCE_ESTABLISHED 0x42
#define OSMO_CC_NOTIFY_CONFERENCE_DISCONNECTED 0x43
#define OSMO_CC_NOTIFY_OTHER_PARTY_ADDED 0x44
#define OSMO_CC_NOTIFY_ISOLATED 0x45
#define OSMO_CC_NOTIFY_REATTACHED 0x46
#define OSMO_CC_NOTIFY_OTHER_PARTY_ISOLATED 0x47
#define OSMO_CC_NOTIFY_OTHER_PARTY_REATTACHED 0x48
#define OSMO_CC_NOTIFY_OTHER_PARTY_SPLIT 0x49
#define OSMO_CC_NOTIFY_OTHER_PARTY_DISCONNECTED 0x4a
#define OSMO_CC_NOTIFY_CONFERENCE_FLOATING 0x4b
#define OSMO_CC_NOTIFY_CONFERENCE_DISC_PREEMPT 0x4c /* disconnect preemted */
#define OSMO_CC_NOTIFY_CONFERENCE_FLOATING_SUP 0x4f /* served user preemted */
#define OSMO_CC_NOTIFY_CALL_IS_A_WAITING_CALL 0x60
#define OSMO_CC_NOTIFY_DIVERSION_ACTIVATED 0x68
#define OSMO_CC_NOTIFY_RESERVED_CT_1 0x69
#define OSMO_CC_NOTIFY_RESERVED_CT_2 0x6a
#define OSMO_CC_NOTIFY_REVERSE_CHARGING 0x6e
#define OSMO_CC_NOTIFY_REMOTE_HOLD 0x79
#define OSMO_CC_NOTIFY_REMOTE_RETRIEVAL 0x7a
#define OSMO_CC_NOTIFY_CALL_IS_DIVERTING 0x7b
#define OSMO_CC_NOTIFY_NUM 0x100
const char *osmo_cc_notify_value2name(int value);
int osmo_cc_notify_name2value(const char *name);
/* coding standard, see ITU-T Rec. Q.931 */
#define OSMO_CC_CODING_ITU_T 0
#define OSMO_CC_CODING_ISO_IEC 1
#define OSMO_CC_CODING_NATIONAL 2
#define OSMO_CC_CODING_STANDARD_SPECIFIC 3
#define OSMO_CC_CODING_NUM 4
const char *osmo_cc_coding_value2name(int value);
int osmo_cc_coding_name2value(const char *name);
/* cause, see ITU-T Rec. Q.850 */
#define OSMO_CC_ISDN_CAUSE_UNASSIGNED_NR 1
#define OSMO_CC_ISDN_CAUSE_NO_ROUTE_TRANSIT 2
#define OSMO_CC_ISDN_CAUSE_NO_ROUTE 3
#define OSMO_CC_ISDN_CAUSE_CHAN_UNACCEPT 6
#define OSMO_CC_ISDN_CAUSE_OP_DET_BARRING 8
#define OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR 16
#define OSMO_CC_ISDN_CAUSE_USER_BUSY 17
#define OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND 18
#define OSMO_CC_ISDN_CAUSE_USER_ALERTING_NA 19
#define OSMO_CC_ISDN_CAUSE_CALL_REJECTED 21
#define OSMO_CC_ISDN_CAUSE_NUMBER_CHANGED 22
#define OSMO_CC_ISDN_CAUSE_PRE_EMPTION 25
#define OSMO_CC_ISDN_CAUSE_NONSE_USER_CLR 26
#define OSMO_CC_ISDN_CAUSE_DEST_OOO 27
#define OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT 28
#define OSMO_CC_ISDN_CAUSE_FACILITY_REJ 29
#define OSMO_CC_ISDN_CAUSE_RESP_STATUS_INQ 30
#define OSMO_CC_ISDN_CAUSE_NORMAL_UNSPEC 31
#define OSMO_CC_ISDN_CAUSE_NO_CIRCUIT_CHAN 34
#define OSMO_CC_ISDN_CAUSE_NETWORK_OOO 38
#define OSMO_CC_ISDN_CAUSE_TEMP_FAILURE 41
#define OSMO_CC_ISDN_CAUSE_SWITCH_CONG 42
#define OSMO_CC_ISDN_CAUSE_ACC_INF_DISCARD 43
#define OSMO_CC_ISDN_CAUSE_REQ_CHAN_UNAVAIL 44
#define OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL 47
#define OSMO_CC_ISDN_CAUSE_QOS_UNAVAIL 49
#define OSMO_CC_ISDN_CAUSE_REQ_FAC_NOT_SUBSC 50
#define OSMO_CC_ISDN_CAUSE_INC_BARRED_CUG 55
#define OSMO_CC_ISDN_CAUSE_BEARER_CAP_UNAUTH 57
#define OSMO_CC_ISDN_CAUSE_BEARER_CA_UNAVAIL 58
#define OSMO_CC_ISDN_CAUSE_SERV_OPT_UNAVAIL 63
#define OSMO_CC_ISDN_CAUSE_BEARERSERV_UNIMPL 65
#define OSMO_CC_ISDN_CAUSE_ACM_GE_ACM_MAX 68
#define OSMO_CC_ISDN_CAUSE_REQ_FAC_NOTIMPL 69
#define OSMO_CC_ISDN_CAUSE_RESTR_BCAP_AVAIL 70
#define OSMO_CC_ISDN_CAUSE_SERV_OPT_UNIMPL 79
#define OSMO_CC_ISDN_CAUSE_INVAL_CALLREF 81
#define OSMO_CC_ISDN_CAUSE_USER_NOT_IN_CUG 87
#define OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST 88
#define OSMO_CC_ISDN_CAUSE_INVAL_TRANS_NET 91
#define OSMO_CC_ISDN_CAUSE_SEMANTIC_INCORR 95
#define OSMO_CC_ISDN_CAUSE_INVAL_MAND_INF 96
#define OSMO_CC_ISDN_CAUSE_MSGTYPE_NOTEXIST 97
#define OSMO_CC_ISDN_CAUSE_MSGTYPE_INCOMPAT 98
#define OSMO_CC_ISDN_CAUSE_IE_NOTEXIST 99
#define OSMO_CC_ISDN_CAUSE_COND_IE_ERR 100
#define OSMO_CC_ISDN_CAUSE_MSG_INCOMP_STATE 101
#define OSMO_CC_ISDN_CAUSE_RECOVERY_TIMER 102
#define OSMO_CC_ISDN_CAUSE_PROTO_ERR 111
#define OSMO_CC_ISDN_CAUSE_INTERWORKING 127
#define OSMO_CC_ISDN_CAUSE_NUM 128
const char *osmo_cc_isdn_cause_value2name(int value);
int osmo_cc_isdn_cause_name2value(const char *name);
/* location, see ITU-T Rec. Q.931 */
#define OSMO_CC_LOCATION_USER 0
#define OSMO_CC_LOCATION_PRIV_SERV_LOC_USER 1
#define OSMO_CC_LOCATION_PUB_SERV_LOC_USER 2
#define OSMO_CC_LOCATION_TRANSIT 3
#define OSMO_CC_LOCATION_PUB_SERV_REM_USER 4
#define OSMO_CC_LOCATION_PRIV_SERV_REM_USER 5
#define OSMO_CC_LOCATION_BEYOND_INTERWORKING 10
#define OSMO_CC_LOCATION_NUM 16
const char *osmo_cc_location_value2name(int value);
int osmo_cc_location_name2value(const char *name);
/* progress description, see ITU-T Rec. Q.931 */
#define OSMO_CC_PROGRESS_NOT_END_TO_END_ISDN 1
#define OSMO_CC_PROGRESS_DEST_NOT_ISDN 2
#define OSMO_CC_PROGRESS_ORIG_NOT_ISDN 3
#define OSMO_CC_PROGRESS_RETURN_TO_ISDN 4
#define OSMO_CC_PROGRESS_INTERWORKING 5
#define OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE 8
#define OSMO_CC_PROGRESS_NUM 16
const char *osmo_cc_progress_value2name(int value);
int osmo_cc_progress_name2value(const char *name);
/* information transfer capability, see ITU-T Rec. Q.931 */
#define OSMO_CC_CAPABILITY_SPEECH 0
#define OSMO_CC_CAPABILITY_DATA 8
#define OSMO_CC_CAPABILITY_DATA_RESTRICTED 9
#define OSMO_CC_CAPABILITY_AUDIO 16
#define OSMO_CC_CAPABILITY_DATA_WITH_TONES 17
#define OSMO_CC_CAPABILITY_VIDEO 24
#define OSMO_CC_CAPABILITY_NUM 32
const char *osmo_cc_capability_value2name(int value);
int osmo_cc_capability_name2value(const char *name);
/* transfer mode, see ITU-T Rec. Q.931 */
#define OSMO_CC_MODE_CIRCUIT 0
#define OSMO_CC_MODE_PACKET 2
#define OSMO_CC_MODE_NUM 4
const char *osmo_cc_mode_value2name(int value);
int osmo_cc_mode_name2value(const char *name);
#define OSMO_CC_DTMF_MODE_OFF 0 /* stop tone */
#define OSMO_CC_DTMF_MODE_ON 1 /* start tone */
#define OSMO_CC_DTMF_MODE_DIGITS 2 /* play tone(s) with duration and pauses */
#define OSMO_CC_DTMF_MODE_NUM 3
const char *osmo_cc_dtmf_mode_value2name(int value);
int osmo_cc_dtmf_mode_name2value(const char *name);
#define OSMO_CC_SOCKET_CAUSE_VERSION_MISMATCH 1 /* version mismatch */
#define OSMO_CC_SOCKET_CAUSE_FAILED 2 /* connection failed */
#define OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE 3 /* connected socket failed */
#define OSMO_CC_SOCKET_CAUSE_TIMEOUT 4 /* keepalive packets timeout */
// if you add causes here, add them in process_cause.c also!
#define OSMO_CC_SOCKET_CAUSE_NUM 5
const char *osmo_cc_socket_cause_value2name(int value);
int osmo_cc_socket_cause_name2value(const char *name);
/* network type (network IE) and meaning of 'id' */
#define OSMO_CC_NETWORK_UNDEFINED 0x00
#define OSMO_CC_NETWORK_ALSA_NONE 0x01
#define OSMO_CC_NETWORK_POTS_NONE 0x02
#define OSMO_CC_NETWORK_ISDN_NONE 0x03
#define OSMO_CC_NETWORK_SIP_NONE 0x04
#define OSMO_CC_NETWORK_GSM_IMSI 0x05 /* id has decimal IMSI */
#define OSMO_CC_NETWORK_GSM_IMEI 0x06 /* id has decimal IMEI */
#define OSMO_CC_NETWORK_WEB_NONE 0x07
#define OSMO_CC_NETWORK_DECT_NONE 0x08
#define OSMO_CC_NETWORK_BLUETOOTH_NONE 0x09
#define OSMO_CC_NETWORK_SS5_NONE 0x0a
#define OSMO_CC_NETWORK_ANETZ_NONE 0x80
#define OSMO_CC_NETWORK_BNETZ_MUENZ 0x81 /* id starts with 'M' */
#define OSMO_CC_NETWORK_CNETZ_NONE 0x82
#define OSMO_CC_NETWORK_NMT_NONE 0x83 /* id has decimal password */
#define OSMO_CC_NETWORK_R2000_NONE 0x84
#define OSMO_CC_NETWORK_AMPS_ESN 0x85 /* if has decimal ESN (TACS also) */
#define OSMO_CC_NETWORK_MTS_NONE 0x86
#define OSMO_CC_NETWORK_IMTS_NONE 0x87
#define OSMO_CC_NETWORK_EUROSIGNAL_NONE 0x88
#define OSMO_CC_NETWORK_JOLLYCOM_NONE 0x89 /* call from JollyCom... */
#define OSMO_CC_NETWORK_MPT1327_PSTN 0x8a /* call from MPT1327 */
#define OSMO_CC_NETWORK_MPT1327_PBX 0x8b /* id is selected PBX number */
#define OSMO_CC_NETWORK_NUM 0x100
const char *osmo_cc_network_value2name(int value);
int osmo_cc_network_name2value(const char *name);
typedef struct osmo_cc_msg {
uint8_t type;
uint16_t length_networkorder;
uint8_t data[0];
} __attribute__((packed)) osmo_cc_msg_t;
typedef struct osmo_cc_msg_list {
struct osmo_cc_msg_list *next;
struct osmo_cc_msg *msg;
uint32_t callref;
char host[128];
uint16_t port;
} osmo_cc_msg_list_t;
typedef struct osmo_cc_ie {
uint8_t type;
uint16_t length_networkorder;
uint8_t data[0];
} __attribute__((packed)) osmo_cc_ie_t;
struct osmo_cc_ie_called {
uint8_t type;
uint8_t plan;
char digits[0];
} __attribute__((packed));
struct osmo_cc_ie_called_sub {
uint8_t type;
char digits[0];
} __attribute__((packed));
struct osmo_cc_ie_called_name {
char name[0];
} __attribute__((packed));
struct osmo_cc_ie_called_interface {
char name[0];
} __attribute__((packed));
struct osmo_cc_ie_calling {
uint8_t type;
uint8_t plan;
uint8_t present;
uint8_t screen;
char digits[0];
} __attribute__((packed));
struct osmo_cc_ie_calling_sub {
uint8_t type;
char digits[0];
} __attribute__((packed));
struct osmo_cc_ie_calling_name {
char name[0];
} __attribute__((packed));
struct osmo_cc_ie_calling_interface {
char name[0];
} __attribute__((packed));
struct osmo_cc_ie_network {
uint8_t type;
char id[0];
} __attribute__((packed));
struct osmo_cc_ie_bearer {
uint8_t coding;
uint8_t capability;
uint8_t mode;
} __attribute__((packed));
struct osmo_cc_ie_redir {
uint8_t type;
uint8_t plan;
uint8_t present;
uint8_t screen;
uint8_t redir_reason;
char digits[0];
} __attribute__((packed));
struct osmo_cc_ie_dtmf {
uint8_t duration_ms;
uint8_t pause_ms;
uint8_t dtmf_mode;
char digits[0];
} __attribute__((packed));
struct osmo_cc_ie_keypad {
char digits[0];
} __attribute__((packed));
struct osmo_cc_ie_progress {
uint8_t coding;
uint8_t location;
uint8_t progress;
} __attribute__((packed));
struct osmo_cc_ie_notify {
uint8_t notify;
} __attribute__((packed));
struct osmo_cc_ie_cause {
uint8_t location;
uint8_t isdn_cause;
uint16_t sip_cause_networkorder;
uint8_t socket_cause;
} __attribute__((packed));
struct osmo_cc_ie_display {
char text[0];
} __attribute__((packed));
struct osmo_cc_ie_sdp {
char sdp[0];
} __attribute__((packed));
struct osmo_cc_ie_socket_address {
char address[0];
} __attribute__((packed));
struct osmo_cc_ie_private {
uint32_t unique_networkorder;
uint8_t data[0];
} __attribute__((packed));
uint32_t osmo_cc_new_callref(void);
osmo_cc_msg_t *osmo_cc_new_msg(uint8_t msg_type);
osmo_cc_msg_t *osmo_cc_clone_msg(osmo_cc_msg_t *msg);
osmo_cc_msg_t *osmo_cc_msg_list_dequeue(osmo_cc_msg_list_t **mlp, uint32_t *callref_p);
osmo_cc_msg_list_t *osmo_cc_msg_list_enqueue(osmo_cc_msg_list_t **mlp, osmo_cc_msg_t *msg, uint32_t callref);
void osmo_cc_free_msg(osmo_cc_msg_t *msg);
void osmo_cc_debug_ie(osmo_cc_msg_t *msg, int level);
int osmo_cc_get_ie_struct(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat, int ie_len, const osmo_cc_ie_t **ie_struct);
int osmo_cc_get_ie_data(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat, int ie_len, const void **ie_data);
int osmo_cc_has_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat);
int osmo_cc_remove_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat);
void *osmo_cc_add_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_len);
void *osmo_cc_msg_sep_ie(osmo_cc_msg_t *msg, void **iep, uint8_t *ie_type, uint16_t *ie_length);
void osmo_cc_add_ie_called(osmo_cc_msg_t *msg, uint8_t type, uint8_t plan, const char *dialing);
int osmo_cc_get_ie_called(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, uint8_t *plan, char *dialing, size_t dialing_size);
void osmo_cc_add_ie_called_sub(osmo_cc_msg_t *msg, uint8_t type, const char *dialing);
int osmo_cc_get_ie_called_sub(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, char *dialing, size_t dialing_size);
void osmo_cc_add_ie_called_name(osmo_cc_msg_t *msg, const char *name);
int osmo_cc_get_ie_called_name(osmo_cc_msg_t *msg, int ie_repeat, char *name, size_t name_size);
void osmo_cc_add_ie_called_interface(osmo_cc_msg_t *msg, const char *interface);
int osmo_cc_get_ie_called_interface(osmo_cc_msg_t *msg, int ie_repeat, char *interface, size_t interface_size);
void osmo_cc_add_ie_complete(osmo_cc_msg_t *msg);
int osmo_cc_get_ie_complete(osmo_cc_msg_t *msg, int ie_repeat);
void osmo_cc_add_ie_calling(osmo_cc_msg_t *msg, uint8_t type, uint8_t plan, uint8_t present, uint8_t screen, const char *callerid);
int osmo_cc_get_ie_calling(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, uint8_t *plan, uint8_t *present, uint8_t *screen, char *callerid, size_t callerid_size);
void osmo_cc_add_ie_calling_sub(osmo_cc_msg_t *msg, uint8_t type, const char *callerid);
int osmo_cc_get_ie_calling_sub(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, char *callerid, size_t callerid_size);
void osmo_cc_add_ie_calling_name(osmo_cc_msg_t *msg, const char *name);
int osmo_cc_get_ie_calling_name(osmo_cc_msg_t *msg, int ie_repeat, char *name, size_t name_size);
void osmo_cc_add_ie_calling_interface(osmo_cc_msg_t *msg, const char *interface);
int osmo_cc_get_ie_calling_interface(osmo_cc_msg_t *msg, int ie_repeat, char *interface, size_t interface_size);
void osmo_cc_add_ie_calling_network(osmo_cc_msg_t *msg, uint8_t type, const char *networkid);
int osmo_cc_get_ie_calling_network(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, char *networkid, size_t networkid_size);
void osmo_cc_add_ie_bearer(osmo_cc_msg_t *msg, uint8_t coding, uint8_t capability, uint8_t mode);
int osmo_cc_get_ie_bearer(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *coding, uint8_t *capability, uint8_t *mode);
void osmo_cc_add_ie_redir(osmo_cc_msg_t *msg, uint8_t type, uint8_t plan, uint8_t present, uint8_t screen, uint8_t redir_reason, const char *callerid);
int osmo_cc_get_ie_redir(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, uint8_t *plan, uint8_t *present, uint8_t *screen, uint8_t *reason, char *callerid, size_t callerid_size);
void osmo_cc_add_ie_dtmf(osmo_cc_msg_t *msg, uint8_t duration_ms, uint8_t pause_ms, uint8_t dtmf_mode, const char *digits);
int osmo_cc_get_ie_dtmf(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *duration_ms, uint8_t *pause_ms, uint8_t *dtmf_mode, char *digits, size_t digits_size);
void osmo_cc_add_ie_keypad(osmo_cc_msg_t *msg, const char *digits);
int osmo_cc_get_ie_keypad(osmo_cc_msg_t *msg, int ie_repeat, char *digits, size_t digits_size);
void osmo_cc_add_ie_progress(osmo_cc_msg_t *msg, uint8_t coding, uint8_t location, uint8_t progress);
int osmo_cc_get_ie_progress(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *coding, uint8_t *location, uint8_t *progress);
void osmo_cc_add_ie_notify(osmo_cc_msg_t *msg, uint8_t notify);
int osmo_cc_get_ie_notify(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *notify);
void osmo_cc_add_ie_cause(osmo_cc_msg_t *msg, uint8_t location, uint8_t isdn_cause, uint16_t sip_cause, uint8_t socket_cause);
int osmo_cc_get_ie_cause(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *location, uint8_t *isdn_cause, uint16_t *sip_cause, uint8_t *socket_cause);
void osmo_cc_add_ie_display(osmo_cc_msg_t *msg, const char *text);
int osmo_cc_get_ie_display(osmo_cc_msg_t *msg, int ie_repeat, char *text, size_t text_size);
void osmo_cc_add_ie_sdp(osmo_cc_msg_t *msg, const char *sdp);
int osmo_cc_get_ie_sdp(osmo_cc_msg_t *msg, int ie_repeat, char *sdp, size_t sdp_size);
void osmo_cc_add_ie_socket_address(osmo_cc_msg_t *msg, const char *address);
int osmo_cc_get_ie_socket_address(osmo_cc_msg_t *msg, int ie_repeat, char *address, size_t address_size);
void osmo_cc_add_ie_private(osmo_cc_msg_t *msg, uint32_t unique, const uint8_t *data, size_t data_size);
int osmo_cc_get_ie_private(osmo_cc_msg_t *msg, int ie_repeat, uint32_t *unique, uint8_t *data, size_t data_size);
#endif /* OSMO_CC_MSG_H */

View File

@ -1,403 +0,0 @@
/* Osmo-CC: RTP handling
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "../libdebug/debug.h"
#include "../libtimer/timer.h"
#include "endpoint.h"
#define RTP_VERSION 2
void osmo_cc_set_rtp_ports(osmo_cc_session_config_t *conf, uint16_t from, uint16_t to)
{
conf->rtp_port_next = from;
conf->rtp_port_from = from;
conf->rtp_port_to = to;
}
struct rtp_hdr {
uint8_t byte0;
uint8_t byte1;
uint16_t sequence;
uint32_t timestamp;
uint32_t ssrc;
} __attribute__((packed));
struct rtp_x_hdr {
uint16_t by_profile;
uint16_t length;
} __attribute__((packed));
static int rtp_receive(int sock, uint8_t **payload_p, int *payload_len_p, uint8_t *marker_p, uint8_t *pt_p, uint16_t *sequence_p, uint32_t *timestamp_p)
{
static uint8_t data[2048];
int len;
struct rtp_hdr *rtph = (struct rtp_hdr *)data;
uint8_t version, padding, extension, csrc_count, marker, payload_type;
struct rtp_x_hdr *rtpxh;
uint8_t *payload;
int payload_len;
int x_len;
len = read(sock, data, sizeof(data));
if (len < 0) {
if (errno == EAGAIN)
return -EAGAIN;
PDEBUG(DCC, DEBUG_DEBUG, "Read errno = %d (%s)\n", errno, strerror(errno));
return -EIO;
}
if (len < 12) {
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP frame too short (len = %d).\n", len);
return -EINVAL;
}
version = rtph->byte0 >> 6;
padding = (rtph->byte0 >> 5) & 1;
extension = (rtph->byte0 >> 4) & 1;
csrc_count = rtph->byte0 & 0x0f;
marker = rtph->byte1 >> 7;
payload_type = rtph->byte1 & 0x7f;
*sequence_p = ntohs(rtph->sequence);
*timestamp_p = ntohl(rtph->timestamp);
if (version != RTP_VERSION) {
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP version %d not supported.\n", version);
return -EINVAL;
}
payload = data + sizeof(*rtph) + (csrc_count << 2);
payload_len = len - sizeof(*rtph) - (csrc_count << 2);
if (payload_len < 0) {
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP frame too short (len = %d, csrc count = %d).\n", len, csrc_count);
return -EINVAL;
}
if (extension) {
if (payload_len < (int)sizeof(*rtpxh)) {
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP frame too short for extension header.\n");
return -EINVAL;
}
rtpxh = (struct rtp_x_hdr *)payload;
x_len = ntohs(rtpxh->length) * 4 + sizeof(*rtpxh);
payload += x_len;
payload_len -= x_len;
if (payload_len < (int)sizeof(*rtpxh)) {
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP frame too short, extension header exceeds frame length.\n");
return -EINVAL;
}
}
if (padding) {
if (payload_len < 1) {
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP frame too short for padding length.\n");
return -EINVAL;
}
payload_len -= payload[payload_len - 1];
if (payload_len < 0) {
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP frame padding is greater than payload.\n");
return -EINVAL;
}
}
*payload_p = payload;
*payload_len_p = payload_len;
*marker_p = marker;
*pt_p = payload_type;
return 0;
}
static void rtp_send(int sock, uint8_t *payload, int payload_len, uint8_t pt, uint16_t sequence, uint32_t timestamp, uint32_t ssrc)
{
struct rtp_hdr *rtph;
char data[sizeof(*rtph) + payload_len];
int len, rc;
rtph = (struct rtp_hdr *)data;
len = sizeof(*rtph);
rtph->byte0 = RTP_VERSION << 6;
rtph->byte1 = pt;
rtph->sequence = htons(sequence);
rtph->timestamp = htonl(timestamp);
rtph->ssrc = htonl(ssrc);
len += payload_len;
if (len > (int)sizeof(data)) {
PDEBUG(DCC, DEBUG_NOTICE, "Buffer overflow, please fix!.\n");
abort();
}
memcpy(data + sizeof(*rtph), payload, payload_len);
rc = write(sock, data, len);
if (rc < 0)
PDEBUG(DCC, DEBUG_DEBUG, "Write errno = %d (%s)\n", errno, strerror(errno));
}
/* open and bind RTP
* set local port to what we bound
*/
int osmo_cc_rtp_open(osmo_cc_session_media_t *media)
{
osmo_cc_session_config_t *conf = media->session->config;
int domain = 0; // make GCC happy
uint16_t start_port;
struct sockaddr_storage sa;
int slen = 0; // make GCC happy
struct sockaddr_in6 *sa6;
struct sockaddr_in *sa4;
uint16_t *sport;
int flags;
int rc;
media->rtp_ssrc = rand();
osmo_cc_rtp_close(media);
switch (media->connection_data_local.addrtype) {
case osmo_cc_session_addrtype_ipv4:
domain = AF_INET;
memset(&sa, 0, sizeof(sa));
sa4 = (struct sockaddr_in *)&sa;
sa4->sin_family = domain;
rc = inet_pton(AF_INET, media->connection_data_local.address, &sa4->sin_addr);
if (rc < 1) {
pton_error:
PDEBUG(DCC, DEBUG_NOTICE, "Cannot bind to address '%s'.\n", media->connection_data_local.address);
return -EINVAL;
}
sport = &sa4->sin_port;
slen = sizeof(*sa4);
break;
case osmo_cc_session_addrtype_ipv6:
domain = AF_INET6;
memset(&sa, 0, sizeof(sa));
sa6 = (struct sockaddr_in6 *)&sa;
sa6->sin6_family = domain;
rc = inet_pton(AF_INET6, media->connection_data_local.address, &sa6->sin6_addr);
if (rc < 1)
goto pton_error;
sport = &sa6->sin6_port;
slen = sizeof(*sa6);
break;
case osmo_cc_session_addrtype_unknown:
PDEBUG(DCC, DEBUG_NOTICE, "Unsupported address type '%s'.\n", media->connection_data_local.addrtype_name);
return -EINVAL;
}
/* rtp_port_from/rtp_port_to may be changed at run time, so rtp_port_next can become out of range. */
if (conf->rtp_port_next < conf->rtp_port_from || conf->rtp_port_next > conf->rtp_port_to)
conf->rtp_port_next = conf->rtp_port_from;
start_port = conf->rtp_port_next;
while (1) {
/* open sockets */
rc = socket(domain, SOCK_DGRAM, IPPROTO_UDP);
if (rc < 0) {
socket_error:
PDEBUG(DCC, DEBUG_ERROR, "Cannot create socket (domain=%d, errno=%d(%s))\n", domain, errno, strerror(errno));
osmo_cc_rtp_close(media);
return -EIO;
}
media->rtp_socket = rc;
rc = socket(domain, SOCK_DGRAM, IPPROTO_UDP);
if (rc < 0)
goto socket_error;
media->rtcp_socket = rc;
/* bind sockets */
*sport = htons(conf->rtp_port_next);
rc = bind(media->rtp_socket, (struct sockaddr *)&sa, slen);
if (rc < 0) {
bind_error:
osmo_cc_rtp_close(media);
conf->rtp_port_next = (conf->rtp_port_next + 2 > conf->rtp_port_to) ? conf->rtp_port_from : conf->rtp_port_next + 2;
if (conf->rtp_port_next == start_port) {
PDEBUG(DCC, DEBUG_ERROR, "Cannot bind socket (errno=%d(%s))\n", errno, strerror(errno));
return -EIO;
}
continue;
}
*sport = htons(conf->rtp_port_next + 1);
rc = bind(media->rtcp_socket, (struct sockaddr *)&sa, slen);
if (rc < 0)
goto bind_error;
media->description.port_local = conf->rtp_port_next;
conf->rtp_port_next = (conf->rtp_port_next + 2 > conf->rtp_port_to) ? conf->rtp_port_from : conf->rtp_port_next + 2;
/* set nonblocking io */
flags = fcntl(media->rtp_socket, F_GETFL);
flags |= O_NONBLOCK;
fcntl(media->rtp_socket, F_SETFL, flags);
flags = fcntl(media->rtcp_socket, F_GETFL);
flags |= O_NONBLOCK;
fcntl(media->rtcp_socket, F_SETFL, flags);
break;
}
PDEBUG(DCC, DEBUG_DEBUG, "Opening media port %d\n", media->description.port_local);
return 0;
}
/* connect RTP
* use remote port to connect to
*/
int osmo_cc_rtp_connect(osmo_cc_session_media_t *media)
{
struct sockaddr_storage sa;
int slen = 0; // make GCC happy
struct sockaddr_in6 *sa6;
struct sockaddr_in *sa4;
uint16_t *sport;
int rc;
PDEBUG(DCC, DEBUG_DEBUG, "Connecting media port %d->%d\n", media->description.port_local, media->description.port_remote);
switch (media->connection_data_remote.addrtype) {
case osmo_cc_session_addrtype_ipv4:
memset(&sa, 0, sizeof(sa));
sa4 = (struct sockaddr_in *)&sa;
sa4->sin_family = AF_INET;
rc = inet_pton(AF_INET, media->connection_data_remote.address, &sa4->sin_addr);
if (rc < 1) {
pton_error:
PDEBUG(DCC, DEBUG_NOTICE, "Cannot connect to address '%s'.\n", media->connection_data_remote.address);
return -EINVAL;
}
sport = &sa4->sin_port;
slen = sizeof(*sa4);
break;
case osmo_cc_session_addrtype_ipv6:
memset(&sa, 0, sizeof(sa));
sa6 = (struct sockaddr_in6 *)&sa;
sa6->sin6_family = AF_INET6;
rc = inet_pton(AF_INET6, media->connection_data_remote.address, &sa6->sin6_addr);
if (rc < 1)
goto pton_error;
sport = &sa6->sin6_port;
slen = sizeof(*sa6);
break;
case osmo_cc_session_addrtype_unknown:
PDEBUG(DCC, DEBUG_NOTICE, "Unsupported address type '%s'.\n", media->connection_data_local.addrtype_name);
return -EINVAL;
}
*sport = htons(media->description.port_remote);
rc = connect(media->rtp_socket, (struct sockaddr *)&sa, slen);
if (rc < 0) {
connect_error:
PDEBUG(DCC, DEBUG_NOTICE, "Cannot connect to address '%s'.\n", media->connection_data_remote.address);
osmo_cc_rtp_close(media);
return -EIO;
}
*sport = htons(media->description.port_remote + 1);
rc = connect(media->rtcp_socket, (struct sockaddr *)&sa, slen);
if (rc < 0)
goto connect_error;
return 0;
}
/* send rtp data with given codec */
void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, int inc_sequence, int inc_timestamp)
{
uint8_t *payload = NULL;
int payload_len = 0;
if (!codec || !codec->media->rtp_socket)
return;
if (codec->encoder)
codec->encoder(data, len, &payload, &payload_len);
else {
payload = data;
payload_len = len;
}
rtp_send(codec->media->rtp_socket, payload, payload_len, codec->payload_type_remote, codec->media->tx_sequence, codec->media->tx_timestamp, codec->media->rtp_ssrc);
codec->media->tx_sequence += inc_sequence;
codec->media->tx_timestamp += inc_timestamp;
if (codec->encoder)
free(payload);
}
/* receive rtp data for given media, return < 0, if there is nothing this time */
int osmo_cc_rtp_receive(osmo_cc_session_media_t *media)
{
int rc;
uint8_t *payload = NULL;
int payload_len = 0;
uint8_t marker;
uint8_t payload_type;
osmo_cc_session_codec_t *codec;
uint8_t *data;
int len;
if (!media || media->rtp_socket <= 0)
return -EIO;
rc = rtp_receive(media->rtp_socket, &payload, &payload_len, &marker, &payload_type, &media->rx_sequence, &media->rx_timestamp);
if (rc < 0)
return rc;
/* search for codec */
for (codec = media->codec_list; codec; codec = codec->next) {
if (codec->payload_type_local == payload_type)
break;
}
if (!codec) {
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP frame for unknown codec (payload_type = %d).\n", payload_type);
return 0;
}
if (codec->decoder)
codec->decoder(payload, payload_len, &data, &len);
else {
data = payload;
len = payload_len;
}
if (codec->media->receive)
codec->media->receiver(codec, media->rx_sequence, media->rx_timestamp, data, len);
if (codec->decoder)
free(data);
return 0;
}
void osmo_cc_rtp_close(osmo_cc_session_media_t *media)
{
if (media->rtp_socket) {
close(media->rtp_socket);
media->rtp_socket = 0;
}
if (media->rtcp_socket) {
close(media->rtcp_socket);
media->rtcp_socket = 0;
}
}

View File

@ -1,8 +0,0 @@
void osmo_cc_set_rtp_ports(osmo_cc_session_config_t *conf, uint16_t from, uint16_t to);
int osmo_cc_rtp_open(osmo_cc_session_media_t *media);
int osmo_cc_rtp_connect(osmo_cc_session_media_t *media);
void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, int inc_sequence, int inc_timestamp);
int osmo_cc_rtp_receive(osmo_cc_session_media_t *media);
void osmo_cc_rtp_close(osmo_cc_session_media_t *media);

View File

@ -1,684 +0,0 @@
/* Endpoint and call process handling
*
* (C) 2019 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "../libtimer/timer.h"
#include "../libdebug/debug.h"
#include "endpoint.h"
#include "message.h"
#define SCREEN_QUESTIONMARK 1
#define SCREEN_STAR 2
#define SCREEN_AT 3
void osmo_cc_help_screen(void)
{
printf("Screening options:\n\n");
printf("screen-calling-in [attrs] <current caller ID> [attrs] <new caller ID>\n");
printf("screen-called-in [attrs] <current dialed number> [attrs] <new dialed number>\n");
printf("screen-calling-out [attrs] <current caller ID> [attrs] <new caller ID>\n");
printf("screen-called-out [attrs] <current dialed number> [attrs] <new dialed number>\n\n");
printf("These options allow to screen an incoming or outgoing caller ID or dialed\n");
printf("number. If 'the current caller ID' or 'current dialed number' matches, it will\n");
printf("be replaced by 'new caller ID' or 'new dialed number'. 'incoming' means from\n");
printf(" the interface and 'outgoing' means towards the interface.\n\n");
printf("Attributes prior 'current caller ID' or 'new dialed number' may be used to\n");
printf("perform screening only if the attribute match. Attributes prior\n");
printf("'new caller ID' or 'new dialed number' may be used to alter them. Attribute to\n");
printf("define the type of number can be: 'unknown', 'international', 'national',\n");
printf("'network', 'subscriber', 'abbreviated' Attribute to define the restriction of a\n");
printf("caller ID: 'allowed', 'restricted'\n\n");
printf("The current caller ID or dialed number may contain one or more '?', to allow\n");
printf("any digit to match. The current caller ID or dialed number may contain a '*',\n");
printf("to allow any suffix to match from now on. The new caller ID or dialed number\n");
printf("may contain a '*', to append the suffix from the current caller ID or dialed\n");
printf("number.\n\n");
}
char *osmo_cc_strtok_quotes(const char **text_p)
{
static char token[1024];
const char *text = *text_p;
int i, quote;
/* skip spaces */
while (*text) {
if (*text > 32)
break;
text++;
}
/* if eol, return NULL */
if (!(*text))
return NULL;
i = 0;
quote = 0;
while (*text) {
/* escape allows all following characters */
if (*text == '\\') {
text++;
if (*text)
token[i++] = *text++;
continue;
}
/* no quote, check for them or break on white space */
if (quote == 0) {
if (*text == '\'') {
quote = 1;
text++;
continue;
}
if (*text == '\"') {
quote = 2;
text++;
continue;
}
if (*text <= ' ')
break;
}
/* single quote, check for unquote */
if (quote == 1 && *text == '\'') {
quote = 0;
text++;
continue;
}
/* double quote, check for unquote */
if (quote == 2 && *text == '\"') {
quote = 0;
text++;
continue;
}
/* copy character */
token[i++] = *text++;
}
token[i] = '\0';
*text_p = text;
return token;
}
int osmo_cc_add_screen(osmo_cc_endpoint_t *ep, const char *text)
{
osmo_cc_screen_list_t **list_p = NULL, *list;
const char *token;
int no_present = 0, calling_in = 0, star_used, at_used;
int i, j;
star_used = 0;
if (!strncasecmp(text, "screen-calling-in", 17)) {
text += 17;
list_p = &ep->screen_calling_in;
no_present = 1;
calling_in = 1;
} else if (!strncasecmp(text, "screen-called-in", 16)) {
text += 16;
list_p = &ep->screen_called_in;
} else if (!strncasecmp(text, "screen-calling-out", 18)) {
text += 18;
list_p = &ep->screen_calling_out;
no_present = 1;
} else if (!strncasecmp(text, "screen-called-out", 17)) {
text += 17;
list_p = &ep->screen_called_out;
} else {
PDEBUG(DCC, DEBUG_ERROR, "Invalid screening definition \"%s\". It must start with 'screen-calling-in' or 'screen-called-in' or 'screen-calling-out' or 'screen-called-out'\n", text);
return -EINVAL;
}
/* skip space behind screen list string */
while (*text) {
if (*text > 32)
break;
text++;
}
list = calloc(1, sizeof(*list));
if (!list)
return -ENOMEM;
next_from:
token = osmo_cc_strtok_quotes(&text);
if (!token) {
free(list);
PDEBUG(DCC, DEBUG_ERROR, "Missing 'from' string in screening definition \"%s\". If the string shall be empty, use double quotes. (\'\' or \"\")\n", text);
return -EINVAL;
}
if (!strcasecmp(token, "unknown")) {
list->has_from_type = 1;
list->from_type = OSMO_CC_TYPE_UNKNOWN;
goto next_from;
} else
if (!strcasecmp(token, "international")) {
list->has_from_type = 1;
list->from_type = OSMO_CC_TYPE_INTERNATIONAL;
goto next_from;
} else
if (!strcasecmp(token, "national")) {
list->has_from_type = 1;
list->from_type = OSMO_CC_TYPE_NATIONAL;
goto next_from;
} else
if (!strcasecmp(token, "network")) {
list->has_from_type = 1;
list->from_type = OSMO_CC_TYPE_NETWORK;
goto next_from;
} else
if (!strcasecmp(token, "subscriber")) {
list->has_from_type = 1;
list->from_type = OSMO_CC_TYPE_SUBSCRIBER;
goto next_from;
} else
if (!strcasecmp(token, "abbreviated")) {
list->has_from_type = 1;
list->from_type = OSMO_CC_TYPE_ABBREVIATED;
goto next_from;
} else
if (!strcasecmp(token, "allowed")) {
if (no_present) {
no_present_error:
free(list);
PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
PDEBUG(DCC, DEBUG_ERROR, "Keyword '%s' not allowed in screen entry for called number\n", token);
return -EINVAL;
}
list->has_from_present = 1;
list->from_present = OSMO_CC_PRESENT_ALLOWED;
goto next_from;
} else
if (!strcasecmp(token, "restricted")) {
if (no_present)
goto no_present_error;
list->has_from_present = 1;
list->from_present = OSMO_CC_PRESENT_RESTRICTED;
goto next_from;
} else {
for (i = j = 0; token[i] && j < (int)sizeof(list->from) - 1; i++, j++) {
if (token[i] == '?')
list->from[j] = SCREEN_QUESTIONMARK;
else
if (token[i] == '*') {
if (star_used) {
free(list);
PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
PDEBUG(DCC, DEBUG_ERROR, "The '*' may be used only once.\n");
return -EINVAL;
}
list->from[j] = SCREEN_STAR;
star_used = 1;
} else
if (token[i] == '\\' && token[i + 1] != '\0')
list->from[j] = token[++i];
else
list->from[j] = token[i];
}
list->from[j] = '\0';
}
star_used = 0;
next_to:
token = osmo_cc_strtok_quotes(&text);
if (!token) {
free(list);
PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
PDEBUG(DCC, DEBUG_ERROR, "Missing screening result. If the string shall be empty, use double quotes. (\'\' or \"\")\n");
return -EINVAL;
}
if (!strcasecmp(token, "unknown")) {
list->has_to_type = 1;
list->to_type = OSMO_CC_TYPE_UNKNOWN;
goto next_to;
} else
if (!strcasecmp(token, "international")) {
list->has_to_type = 1;
list->to_type = OSMO_CC_TYPE_INTERNATIONAL;
goto next_to;
} else
if (!strcasecmp(token, "national")) {
list->has_to_type = 1;
list->to_type = OSMO_CC_TYPE_NATIONAL;
goto next_to;
} else
if (!strcasecmp(token, "network")) {
list->has_to_type = 1;
list->to_type = OSMO_CC_TYPE_NETWORK;
goto next_to;
} else
if (!strcasecmp(token, "subscriber")) {
list->has_to_type = 1;
list->to_type = OSMO_CC_TYPE_SUBSCRIBER;
goto next_to;
} else
if (!strcasecmp(token, "abbreviated")) {
list->has_to_type = 1;
list->to_type = OSMO_CC_TYPE_ABBREVIATED;
goto next_to;
} else
if (!strcasecmp(token, "allowed")) {
if (no_present)
goto no_present_error;
list->has_to_present = 1;
list->to_present = OSMO_CC_PRESENT_ALLOWED;
goto next_to;
} else
if (!strcasecmp(token, "restricted")) {
if (no_present)
goto no_present_error;
list->has_to_present = 1;
list->to_present = OSMO_CC_PRESENT_RESTRICTED;
goto next_to;
} else {
for (i = j = 0; token[i] && j < (int)sizeof(list->to) - 1; i++, j++) {
if (token[i] == '*') {
if (star_used) {
free(list);
PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
PDEBUG(DCC, DEBUG_ERROR, "The '*' may be used only once.\n");
return -EINVAL;
}
list->to[j] = SCREEN_STAR;
star_used = 1;
} else
if (token[i] == '@') {
if (!calling_in) {
free(list);
PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
PDEBUG(DCC, DEBUG_ERROR, "The '@' may be used only for incoming calls from interface.\n");
return -EINVAL;
}
if (at_used) {
free(list);
PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
PDEBUG(DCC, DEBUG_ERROR, "The '@' may be used only once.\n");
return -EINVAL;
}
list->to[j] = SCREEN_AT;
at_used = 1;
} else
if (token[i] == '\\' && token[i + 1] != '\0')
list->to[j] = token[++i];
else
list->to[j] = token[i];
}
list->to[j] = '\0';
}
token = osmo_cc_strtok_quotes(&text);
if (token) {
free(list);
PDEBUG(DCC, DEBUG_ERROR, "Error in screening definition '%s'.\n", text);
PDEBUG(DCC, DEBUG_ERROR, "Got garbage behind screening result.\n");
return -EINVAL;
}
/* attach screen entry to list */
while (*list_p)
list_p = &((*list_p)->next);
*list_p = list;
return 0;
}
void osmo_cc_flush_screen(osmo_cc_screen_list_t *list)
{
osmo_cc_screen_list_t *temp;
while (list) {
temp = list;
list = list->next;
free(temp);
}
}
const char *print_rule_string(const char *input)
{
static char output[256];
int i;
for (i = 0; *input && i < (int)sizeof(output) - 1; i++, input++) {
switch (*input) {
case SCREEN_QUESTIONMARK:
output[i] = '?';
break;
case SCREEN_STAR:
output[i] = '*';
break;
case SCREEN_AT:
output[i] = '@';
break;
default:
output[i] = *input;
}
}
output[i] = '\0';
return output;
}
static int osmo_cc_screen(const char *what, osmo_cc_screen_list_t *list, uint8_t *type, uint8_t *present, char *id_to, int id_to_size, const char *id_from, const char **routing_p)
{
const char *suffix;
int i, j, rule;
PDEBUG(DCC, DEBUG_INFO, "Screening %s '%s':\n", what, id_from);
switch (*type) {
case OSMO_CC_TYPE_UNKNOWN:
PDEBUG(DCC, DEBUG_INFO, " -> type = unknown\n");
break;
case OSMO_CC_TYPE_INTERNATIONAL:
PDEBUG(DCC, DEBUG_INFO, " -> type = international\n");
break;
case OSMO_CC_TYPE_NATIONAL:
PDEBUG(DCC, DEBUG_INFO, " -> type = national\n");
break;
case OSMO_CC_TYPE_NETWORK:
PDEBUG(DCC, DEBUG_INFO, " -> type = network\n");
break;
case OSMO_CC_TYPE_SUBSCRIBER:
PDEBUG(DCC, DEBUG_INFO, " -> type = subscriber\n");
break;
case OSMO_CC_TYPE_ABBREVIATED:
PDEBUG(DCC, DEBUG_INFO, " -> type = abbreviated\n");
break;
}
if (present) switch (*present) {
case OSMO_CC_PRESENT_ALLOWED:
PDEBUG(DCC, DEBUG_INFO, " -> present = allowed\n");
break;
case OSMO_CC_PRESENT_RESTRICTED:
PDEBUG(DCC, DEBUG_INFO, " -> present = restricted\n");
break;
}
rule = 0;
while (list) {
rule++;
PDEBUG(DCC, DEBUG_INFO, "Comparing with rule #%d: '%s':\n", rule, print_rule_string(list->from));
if (list->has_from_type) switch (list->from_type) {
case OSMO_CC_TYPE_UNKNOWN:
PDEBUG(DCC, DEBUG_INFO, " -> type = unknown\n");
break;
case OSMO_CC_TYPE_INTERNATIONAL:
PDEBUG(DCC, DEBUG_INFO, " -> type = international\n");
break;
case OSMO_CC_TYPE_NATIONAL:
PDEBUG(DCC, DEBUG_INFO, " -> type = national\n");
break;
case OSMO_CC_TYPE_NETWORK:
PDEBUG(DCC, DEBUG_INFO, " -> type = network\n");
break;
case OSMO_CC_TYPE_SUBSCRIBER:
PDEBUG(DCC, DEBUG_INFO, " -> type = subscriber\n");
break;
case OSMO_CC_TYPE_ABBREVIATED:
PDEBUG(DCC, DEBUG_INFO, " -> type = abbreviated\n");
break;
}
if (list->has_from_present) switch (list->from_present) {
case OSMO_CC_PRESENT_ALLOWED:
PDEBUG(DCC, DEBUG_INFO, " -> present = allowed\n");
break;
case OSMO_CC_PRESENT_RESTRICTED:
PDEBUG(DCC, DEBUG_INFO, " -> present = restricted\n");
break;
}
suffix = NULL;
/* attributes do not match */
if (list->has_from_type && list->from_type != *type) {
PDEBUG(DCC, DEBUG_INFO, "Rule does not match, because 'type' is different.\n");
continue;
}
if (present && list->has_from_present && list->from_present != *present) {
PDEBUG(DCC, DEBUG_INFO, "Rule does not match, because 'present' is different.\n");
continue;
}
for (i = 0; list->from[i] && id_from[i]; i++) {
/* '?' means: any digit, so it machtes */
if (list->from[i] == SCREEN_QUESTIONMARK) {
continue;
}
/* '*' means: anything may follow, so it machtes */
if (list->from[i] == SCREEN_STAR) {
suffix = id_from + i;
break;
}
/* check if digit doesn't matches */
if (list->from[i] != id_from[i])
break;
}
/* if last checked digit is '*', we have a match */
/* also if we hit EOL at id_from and next check digit is '*' */
if (list->from[i] == SCREEN_STAR)
break;
/* if all digits have matched */
if (list->from[i] == '\0' && id_from[i] == '\0')
break;
PDEBUG(DCC, DEBUG_INFO, "Rule does not match, because %s is different.\n", what);
list = list->next;
}
/* if no list entry matches */
if (!list)
return -1;
/* replace ID */
if (list->has_to_type) {
*type = list->to_type;
}
if (present && list->has_to_present) {
*present = list->to_present;
}
for (i = j = 0; list->to[i]; i++) {
if (j == id_to_size - 1)
break;
/* '*' means to use suffix of input string */
if (list->to[i] == SCREEN_STAR && suffix) {
while (*suffix) {
id_to[j++] = *suffix++;
if (j == id_to_size - 1)
break;
}
continue;
/* '@' means to stop and return routing also */
} else if (list->to[i] == SCREEN_AT) {
*routing_p = &list->to[i];
break;
}
/* copy output digit */
id_to[j++] = list->to[i];
}
id_to[j] = '\0';
PDEBUG(DCC, DEBUG_INFO, "Rule matches, changing %s to '%s'.\n", what, print_rule_string(id_to));
if (list->has_to_type) switch (list->to_type) {
case OSMO_CC_TYPE_UNKNOWN:
PDEBUG(DCC, DEBUG_INFO, " -> type = unknown\n");
break;
case OSMO_CC_TYPE_INTERNATIONAL:
PDEBUG(DCC, DEBUG_INFO, " -> type = international\n");
break;
case OSMO_CC_TYPE_NATIONAL:
PDEBUG(DCC, DEBUG_INFO, " -> type = national\n");
break;
case OSMO_CC_TYPE_NETWORK:
PDEBUG(DCC, DEBUG_INFO, " -> type = network\n");
break;
case OSMO_CC_TYPE_SUBSCRIBER:
PDEBUG(DCC, DEBUG_INFO, " -> type = subscriber\n");
break;
case OSMO_CC_TYPE_ABBREVIATED:
PDEBUG(DCC, DEBUG_INFO, " -> type = abbreviated\n");
break;
}
if (list->has_to_present) switch (list->to_present) {
case OSMO_CC_PRESENT_ALLOWED:
PDEBUG(DCC, DEBUG_INFO, " -> present = allowed\n");
break;
case OSMO_CC_PRESENT_RESTRICTED:
PDEBUG(DCC, DEBUG_INFO, " -> present = restricted\n");
break;
}
return 0;
}
osmo_cc_msg_t *osmo_cc_screen_msg(osmo_cc_endpoint_t *ep, osmo_cc_msg_t *old_msg, int in, const char **routing_p)
{
osmo_cc_msg_t *new_msg;
char id[256], calling[256], called[256], redir[256];
uint8_t calling_type, calling_plan, calling_present, calling_screen;
uint8_t called_type, called_plan;
uint8_t redir_type, redir_plan, redir_present, redir_screen, redir_reason;
int calling_status = 0, called_status = 0, redir_status = 0;
int rc;
void *ie, *to_ie;
uint8_t ie_type;
uint16_t ie_length;
void *ie_value;
if (in && ep->screen_calling_in) {
rc = osmo_cc_get_ie_calling(old_msg, 0, &calling_type, &calling_plan, &calling_present, &calling_screen, id, sizeof(id));
if (rc >= 0) {
rc = osmo_cc_screen("incoming caller ID", ep->screen_calling_in, &calling_type, &calling_present, calling, sizeof(calling), id, routing_p);
if (rc >= 0)
calling_status = 1;
} else {
calling_type = OSMO_CC_TYPE_UNKNOWN;
calling_plan = OSMO_CC_PLAN_TELEPHONY;
calling_present = OSMO_CC_PRESENT_ALLOWED;
calling_screen = OSMO_CC_SCREEN_NETWORK;
rc = osmo_cc_screen("incoming caller ID", ep->screen_calling_in, &calling_type, &calling_present, calling, sizeof(calling), "", routing_p);
if (rc >= 0)
calling_status = 1;
}
rc = osmo_cc_get_ie_redir(old_msg, 0, &redir_type, &redir_plan, &redir_present, &redir_screen, &redir_reason, id, sizeof(id));
if (rc >= 0) {
rc = osmo_cc_screen("incoming redirecting number", ep->screen_calling_in, &redir_type, &redir_present, redir, sizeof(redir), id, NULL);
if (rc >= 0)
redir_status = 1;
}
}
if (in && ep->screen_called_in) {
rc = osmo_cc_get_ie_called(old_msg, 0, &called_type, &called_plan, id, sizeof(id));
if (rc >= 0) {
rc = osmo_cc_screen("incoming dialed number", ep->screen_called_in, &called_type, NULL, called, sizeof(called), id, NULL);
if (rc >= 0)
called_status = 1;
} else {
called_type = OSMO_CC_TYPE_UNKNOWN;
called_plan = OSMO_CC_PLAN_TELEPHONY;
rc = osmo_cc_screen("incoming dialed number", ep->screen_called_in, &called_type, NULL, called, sizeof(called), "", NULL);
if (rc >= 0)
called_status = 1;
}
}
if (!in && ep->screen_calling_out) {
rc = osmo_cc_get_ie_calling(old_msg, 0, &calling_type, &calling_plan, &calling_present, &calling_screen, id, sizeof(id));
if (rc >= 0) {
rc = osmo_cc_screen("outgoing caller ID", ep->screen_calling_out, &calling_type, &calling_present, calling, sizeof(calling), id, NULL);
if (rc >= 0)
calling_status = 1;
} else {
calling_type = OSMO_CC_TYPE_UNKNOWN;
calling_plan = OSMO_CC_PLAN_TELEPHONY;
calling_present = OSMO_CC_PRESENT_ALLOWED;
calling_screen = OSMO_CC_SCREEN_NETWORK;
rc = osmo_cc_screen("outgoing caller ID", ep->screen_calling_out, &calling_type, &calling_present, calling, sizeof(calling), "", NULL);
if (rc >= 0)
calling_status = 1;
}
rc = osmo_cc_get_ie_redir(old_msg, 0, &redir_type, &redir_plan, &redir_present, &redir_screen, &redir_reason, id, sizeof(id));
if (rc >= 0) {
rc = osmo_cc_screen("outgoing redirecting number", ep->screen_calling_out, &redir_type, &redir_present, redir, sizeof(redir), id, NULL);
if (rc >= 0)
redir_status = 1;
}
}
if (!in && ep->screen_called_out) {
rc = osmo_cc_get_ie_called(old_msg, 0, &called_type, &called_plan, id, sizeof(id));
if (rc >= 0) {
rc = osmo_cc_screen("outgoing dialed number", ep->screen_called_out, &called_type, NULL, called, sizeof(called), id, NULL);
if (rc >= 0)
called_status = 1;
} else {
called_type = OSMO_CC_TYPE_UNKNOWN;
called_plan = OSMO_CC_PLAN_TELEPHONY;
rc = osmo_cc_screen("outgoing dialed number", ep->screen_called_out, &called_type, NULL, called, sizeof(called), "", NULL);
if (rc >= 0)
called_status = 1;
}
}
/* nothing screened */
if (!calling_status && !called_status && !redir_status)
return old_msg;
new_msg = osmo_cc_new_msg(old_msg->type);
/* copy and replace */
ie = old_msg->data;
while ((ie_value = osmo_cc_msg_sep_ie(old_msg, &ie, &ie_type, &ie_length))) {
switch (ie_type) {
case OSMO_CC_IE_CALLING:
if (calling_status) {
osmo_cc_add_ie_calling(new_msg, calling_type, calling_plan, calling_present, calling_screen, calling);
calling_status = 0;
break;
}
goto copy;
case OSMO_CC_IE_CALLED:
if (called_status) {
osmo_cc_add_ie_called(new_msg, called_type, called_plan, called);
called_status = 0;
break;
}
goto copy;
case OSMO_CC_IE_REDIR:
if (redir_status) {
osmo_cc_add_ie_redir(new_msg, redir_type, redir_plan, redir_present, redir_screen, redir_reason, redir);
redir_status = 0;
break;
}
goto copy;
default:
copy:
to_ie = osmo_cc_add_ie(new_msg, ie_type, ie_length);
memcpy(to_ie, ie_value, ie_length);
}
}
/* applend, if not yet in message (except redir, since it must exist) */
if (calling_status)
osmo_cc_add_ie_calling(new_msg, calling_type, calling_plan, calling_present, calling_screen, calling);
if (called_status)
osmo_cc_add_ie_called(new_msg, called_type, called_plan, called);
free(old_msg);
return new_msg;
}

View File

@ -1,7 +0,0 @@
void osmo_cc_help_screen(void);
char *osmo_cc_strtok_quotes(const char **text_p);
int osmo_cc_add_screen(osmo_cc_endpoint_t *ep, const char *text);
void osmo_cc_flush_screen(osmo_cc_screen_list_t *list);
osmo_cc_msg_t *osmo_cc_screen_msg(osmo_cc_endpoint_t *ep, osmo_cc_msg_t *old_msg, int in, const char **routing_p);

View File

@ -1,544 +0,0 @@
/* Session Description Protocol parsing and generator
* This shall be simple and is incomplete.
*
* (C) 2019 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include "../libdebug/debug.h"
#include "../libtimer/timer.h"
#include "endpoint.h"
#include "sdp.h"
#define strncat_printf(sdp, fmt, arg...) \
{ \
snprintf(sdp + strlen(sdp), sizeof(sdp) - strlen(sdp), fmt, ## arg); \
sdp[sizeof(sdp) - 1] = '\0'; \
}
/* generate SDP from session structure */
char *osmo_cc_session_gensdp(osmo_cc_session_t *session)
{
/* calc max size of SDP: quick an dirty (close to max UDP payload size) */
static char sdp[65000];
const char *username, *sess_id, *sess_version, *nettype, *addrtype, *unicast_address;
const char *session_name;
int individual_connection_data = 1; /* in case there is no media, there is no connection data */
int individual_send_receive = 1; /* in case there is no media, there is no send/receive attribute */
struct osmo_cc_session_media *media;
struct osmo_cc_session_codec *codec;
sdp[0] = 0;
/* Version */
strncat_printf(sdp, "v=0\r\n");
/* Origin */
username = session->origin_local.username;
sess_id = session->origin_local.sess_id;
sess_version = session->origin_local.sess_version;
nettype = session->origin_local.nettype;
addrtype = session->origin_local.addrtype;
unicast_address = session->origin_local.unicast_address;
strncat_printf(sdp, "o=%s %s %s %s %s %s\r\n", username, sess_id, sess_version, nettype, addrtype, unicast_address);
/* Session */
session_name = session->name;
strncat_printf(sdp, "s=%s\r\n", session_name);
/* Connection Data (if all media have the same data) */
if (session->media_list) {
osmo_cc_session_for_each_media(session->media_list->next, media) {
if (session->media_list->connection_data_local.nettype != media->connection_data_local.nettype)
break;
if (session->media_list->connection_data_local.addrtype != media->connection_data_local.addrtype)
break;
if (!!strcmp(session->media_list->connection_data_local.address, media->connection_data_local.address))
break;
}
if (!media)
individual_connection_data = 0;
}
if (!individual_connection_data)
strncat_printf(sdp, "c=%s %s %s\r\n", osmo_cc_session_nettype2string(session->media_list->connection_data_local.nettype), osmo_cc_session_addrtype2string(session->media_list->connection_data_local.addrtype), session->media_list->connection_data_local.address);
/* timestamp */
strncat_printf(sdp, "t=0 0\r\n");
/* sendonly /recvonly (if all media have the same data) */
if (session->media_list) {
osmo_cc_session_for_each_media(session->media_list->next, media) {
if (session->media_list->send != media->send)
break;
if (session->media_list->receive != media->receive)
break;
}
if (!media)
individual_send_receive = 0;
}
if (!individual_send_receive) {
if (session->media_list->send && !session->media_list->receive)
strncat_printf(sdp, "a=sendonly\r\n");
if (!session->media_list->send && session->media_list->receive)
strncat_printf(sdp, "a=recvonly\r\n");
if (!session->media_list->send && !session->media_list->receive)
strncat_printf(sdp, "a=inactive\r\n");
}
/* media */
osmo_cc_session_for_each_media(session->media_list, media) {
strncat_printf(sdp, "m=%s %u %s",
osmo_cc_session_media_type2string(media->description.type) ? : media->description.type_name,
media->description.port_local,
osmo_cc_session_media_proto2string(media->description.proto) ? : media->description.proto_name);
osmo_cc_session_for_each_codec(media->codec_list, codec)
strncat_printf(sdp, " %u", codec->payload_type_local);
strncat_printf(sdp, "\r\n");
/* don't list rtpmap when session was canceled by setting port to 0 */
if (media->description.port_local == 0)
continue;
if (individual_connection_data)
strncat_printf(sdp, "c=%s %s %s\r\n", osmo_cc_session_nettype2string(media->connection_data_local.nettype), osmo_cc_session_addrtype2string(media->connection_data_local.addrtype), media->connection_data_local.address);
osmo_cc_session_for_each_codec(media->codec_list, codec) {
strncat_printf(sdp, "a=rtpmap:%u %s/%d", codec->payload_type_local, codec->payload_name, codec->payload_rate);
if (codec->payload_channels >= 2)
strncat_printf(sdp, "/%d", codec->payload_channels);
strncat_printf(sdp, "\r\n");
}
if (individual_send_receive) {
if (media->send && !media->receive)
strncat_printf(sdp, "a=sendonly\r\n");
if (!media->send && media->receive)
strncat_printf(sdp, "a=recvonly\r\n");
if (!media->send && !media->receive)
strncat_printf(sdp, "a=inactive\r\n");
}
}
/* check for overflow and return */
if (strlen(sdp) == sizeof(sdp) - 1) {
PDEBUG(DCC, DEBUG_ERROR, "Fatal error: Allocated SDP buffer with %d bytes is too small, please fix!\n", (int)sizeof(sdp));
return NULL;
}
return sdp;
}
/* separate a word from string that is delimited with one or more space characters */
static char *wordsep(char **text_p)
{
char *text = *text_p;
static char word[256];
int i;
/* no text */
if (text == NULL || *text == '\0')
return NULL;
/* skip spaces before text */
while (*text && *text <= ' ')
text++;
/* copy content */
i = 0;
while (*text > ' ' && i < (int)sizeof(word))
word[i++] = *text++;
word[i] = '\0';
/* set next */
*text_p = text;
return word;
}
/*
* codecs and their default values
*
* if format is -1, payload type is dynamic
* if rate is 0, rate may be any rate
*/
struct codec_defaults {
int fmt;
char *name;
uint32_t rate;
int channels;
} codec_defaults[] = {
{ 0, "PCMU", 8000, 1 },
{ 3, "GSM", 8000, 1 },
{ 4, "G723", 8000, 1 },
{ 5, "DVI4", 8000, 1 },
{ 6, "DVI4", 16000, 1 },
{ 7, "LPC", 8000, 1 },
{ 8, "PCMA", 8000, 1 },
{ 9, "G722", 8000, 1 },
{ 10, "L16", 44100, 2 },
{ 11, "L16", 44100, 1 },
{ 12, "QCELP", 8000, 1 },
{ 13, "CN", 8000, 1 },
{ 14, "MPA", 90000, 1 },
{ 15, "G728", 8000, 1 },
{ 16, "DVI4", 11025, 1 },
{ 17, "DVI4", 22050, 1 },
{ 18, "G729", 8000, 1 },
{ 25, "CELB", 90000, 0 },
{ 26, "JPEG", 90000, 0 },
{ 28, "nv", 90000, 0 },
{ 31, "H261", 90000, 0 },
{ 32, "MPV", 90000, 0 },
{ 33, "MP2T", 90000, 0 },
{ 34, "H263", 90000, 0 },
{ -1, NULL, 0, 0 },
};
static void complete_codec_by_fmt(uint8_t fmt, const char **name, uint32_t *rate, int *channels)
{
int i;
for (i = 0; codec_defaults[i].name; i++) {
if (codec_defaults[i].fmt == fmt)
break;
}
if (!codec_defaults[i].name)
return;
free((char *)*name);
*name = strdup(codec_defaults[i].name);
*rate = codec_defaults[i].rate;
*channels = codec_defaults[i].channels;
}
int osmo_cc_payload_type_by_attrs(uint8_t *fmt, const char *name, uint32_t *rate, int *channels)
{
int i;
for (i = 0; codec_defaults[i].name; i++) {
if (!strcmp(codec_defaults[i].name, name)
&& (*rate == 0 || codec_defaults[i].rate == *rate)
&& (*channels == 0 || codec_defaults[i].channels == *channels))
break;
}
if (!codec_defaults[i].name)
return -EINVAL;
*fmt = codec_defaults[i].fmt;
*rate = codec_defaults[i].rate;
*channels = codec_defaults[i].channels;
return 0;
}
/* parses data and codec list from SDP
*
* sdp = given SDP text
* return: SDP session description structure */
struct osmo_cc_session *osmo_cc_session_parsesdp(osmo_cc_session_config_t *conf, void *priv, const char *_sdp)
{
char buffer[strlen(_sdp) + 1], *sdp = buffer;
char *line, *p, *word, *next_word;
int line_no = 0;
struct osmo_cc_session_connection_data ccd, *cd;
int csend = 1, creceive = 1; /* common default */
struct osmo_cc_session *session = NULL;
struct osmo_cc_session_media *media = NULL;
struct osmo_cc_session_codec *codec = NULL;
/* prepare data */
strcpy(sdp, _sdp);
memset(&ccd, 0, sizeof(ccd));
/* create SDP session description */
session = osmo_cc_new_session(conf, priv, NULL, NULL, NULL, 0, 0, NULL, NULL, 0);
/* check every line of SDP and parse its data */
while(*sdp) {
if ((p = strchr(sdp, '\r'))) {
*p++ = '\0';
if (*p == '\n')
p++;
line = sdp;
sdp = p;
} else if ((p = strchr(sdp, '\n'))) {
*p++ = '\0';
line = sdp;
sdp = p;
} else {
line = sdp;
sdp = strchr(sdp, '\0');
}
next_word = line + 2;
line_no++;
if (line[0] == '\0')
continue;
if (line[1] != '=') {
PDEBUG(DCC, DEBUG_NOTICE, "SDP line %d = '%s' is garbage, expecting '=' as second character.\n", line_no, line);
continue;
}
switch(line[0]) {
case 'v':
PDEBUG(DCC, DEBUG_DEBUG, " -> Version: %s\n", next_word);
if (atoi(next_word) != 0) {
PDEBUG(DCC, DEBUG_NOTICE, "SDP line %d = '%s' describes unsupported version.\n", line_no, line);
osmo_cc_free_session(session);
return NULL;
}
break;
case 'o':
PDEBUG(DCC, DEBUG_DEBUG, " -> Originator: %s\n", next_word);
/* Originator */
word = wordsep(&next_word);
if (!word)
break;
free((char *)session->origin_remote.username); // if already set
session->origin_remote.username = strdup(word);
word = wordsep(&next_word);
if (!word)
break;
free((char *)session->origin_remote.sess_id); // if already set
session->origin_remote.sess_id = strdup(word);
word = wordsep(&next_word);
if (!word)
break;
free((char *)session->origin_remote.sess_version); // if already set
session->origin_remote.sess_version = strdup(word);
word = wordsep(&next_word);
if (!word)
break;
free((char *)session->origin_remote.nettype); // if already set
session->origin_remote.nettype = strdup(word);
word = wordsep(&next_word);
if (!word)
break;
free((char *)session->origin_remote.addrtype); // if already set
session->origin_remote.addrtype = strdup(word);
word = wordsep(&next_word);
if (!word)
break;
free((char *)session->origin_remote.unicast_address); // if already set
session->origin_remote.unicast_address = strdup(word);
break;
case 's':
/* Session Name */
PDEBUG(DCC, DEBUG_DEBUG, " -> Session Name: %s\n", next_word);
free((char *)session->name); // if already set
session->name = strdup(next_word);
break;
case 'c': /* Connection Data */
PDEBUG(DCC, DEBUG_DEBUG, " -> Connection Data: %s\n", next_word);
if (media)
cd = &media->connection_data_remote;
else
cd = &ccd;
/* network type */
if (!(word = wordsep(&next_word)))
break;
if (!strcmp(word, "IN"))
cd->nettype = osmo_cc_session_nettype_inet;
else {
PDEBUG(DCC, DEBUG_NOTICE, "Unsupported network type '%s' in SDP line %d = '%s'\n", word, line_no, line);
break;
}
/* address type */
if (!(word = wordsep(&next_word)))
break;
if (!strcmp(word, "IP4")) {
cd->addrtype = osmo_cc_session_addrtype_ipv4;
PDEBUG(DCC, DEBUG_DEBUG, " -> Address Type = IPv4\n");
} else
if (!strcmp(word, "IP6")) {
cd->addrtype = osmo_cc_session_addrtype_ipv6;
PDEBUG(DCC, DEBUG_DEBUG, " -> Address Type = IPv6\n");
} else {
PDEBUG(DCC, DEBUG_NOTICE, "Unsupported address type '%s' in SDP line %d = '%s'\n", word, line_no, line);
break;
}
/* connection address */
if (!(word = wordsep(&next_word)))
break;
if ((p = strchr(word, '/')))
*p++ = '\0';
free((char *)cd->address); // in case of multiple lines of 'c'
cd->address = strdup(word);
PDEBUG(DCC, DEBUG_DEBUG, " -> Address = %s\n", word);
break;
case 'm': /* Media Description */
PDEBUG(DCC, DEBUG_DEBUG, " -> Media Description: %s\n", next_word);
/* add media description */
media = osmo_cc_add_media(session, 0, 0, NULL, 0, 0, 0, csend, creceive, NULL, 0);
/* copy common connection data from common connection, if exists */
cd = &media->connection_data_remote;
memcpy(cd, &ccd, sizeof(*cd));
/* media type */
if (!(word = wordsep(&next_word)))
break;
if (!strcmp(word, "audio"))
media->description.type = osmo_cc_session_media_type_audio;
else
if (!strcmp(word, "video"))
media->description.type = osmo_cc_session_media_type_video;
else {
media->description.type = osmo_cc_session_media_type_unknown;
media->description.type_name = strdup(word);
PDEBUG(DCC, DEBUG_DEBUG, "Unsupported media type in SDP line %d = '%s'\n", line_no, line);
}
/* port */
if (!(word = wordsep(&next_word)))
break;
media->description.port_remote = atoi(word);
/* proto */
if (!(word = wordsep(&next_word)))
break;
if (!strcmp(word, "RTP/AVP"))
media->description.proto = osmo_cc_session_media_proto_rtp;
else {
media->description.proto = osmo_cc_session_media_proto_unknown;
media->description.proto_name = strdup(word);
PDEBUG(DCC, DEBUG_NOTICE, "Unsupported protocol type in SDP line %d = '%s'\n", line_no, line);
break;
}
/* create codec description for each codec and link */
while ((word = wordsep(&next_word))) {
/* create codec */
codec = osmo_cc_add_codec(media, NULL, 0, 1, NULL, NULL, 0);
/* fmt */
codec->payload_type_remote = atoi(word);
complete_codec_by_fmt(codec->payload_type_remote, &codec->payload_name, &codec->payload_rate, &codec->payload_channels);
PDEBUG(DCC, DEBUG_DEBUG, " -> payload type = %d\n", codec->payload_type_remote);
if (codec->payload_name)
PDEBUG(DCC, DEBUG_DEBUG, " -> payload name = %s\n", codec->payload_name);
if (codec->payload_rate)
PDEBUG(DCC, DEBUG_DEBUG, " -> payload rate = %d\n", codec->payload_rate);
if (codec->payload_channels)
PDEBUG(DCC, DEBUG_DEBUG, " -> payload channels = %d\n", codec->payload_channels);
}
break;
case 'a':
PDEBUG(DCC, DEBUG_DEBUG, " -> Attribute: %s\n", next_word);
word = wordsep(&next_word);
if (!strcmp(word, "sendrecv")) {
if (media) {
media->receive = 1;
media->send = 1;
} else {
creceive = 1;
csend = 1;
}
break;
} else
if (!strcmp(word, "recvonly")) {
if (media) {
media->receive = 1;
media->send = 0;
} else {
creceive = 1;
csend = 0;
}
break;
} else
if (!strcmp(word, "sendonly")) {
if (media) {
media->receive = 0;
media->send = 1;
} else {
creceive = 0;
csend = 1;
}
break;
} else
if (!strcmp(word, "inactive")) {
if (media) {
media->receive = 0;
media->send = 0;
} else {
creceive = 0;
csend = 0;
}
break;
} else
if (!media) {
PDEBUG(DCC, DEBUG_NOTICE, "Attribute without previously defined media in SDP line %d = '%s'\n", line_no, line);
break;
}
if (!strncmp(word, "rtpmap:", 7)) {
int fmt = atoi(word + 7);
osmo_cc_session_for_each_codec(media->codec_list, codec) {
if (codec->payload_type_remote == fmt)
break;
}
if (!codec) {
PDEBUG(DCC, DEBUG_NOTICE, "Attribute without previously defined codec in SDP line %d = '%s'\n", line_no, line);
break;
}
PDEBUG(DCC, DEBUG_DEBUG, " -> (rtpmap) payload type = %d\n", codec->payload_type_remote);
if (!(word = wordsep(&next_word)))
goto rtpmap_done;
if ((p = strchr(word, '/')))
*p++ = '\0';
free((char *)codec->payload_name); // in case it is already set above
codec->payload_name = strdup(word);
PDEBUG(DCC, DEBUG_DEBUG, " -> (rtpmap) payload name = %s\n", codec->payload_name);
if (!(word = p))
goto rtpmap_done;
if ((p = strchr(word, '/')))
*p++ = '\0';
codec->payload_rate = atoi(word);
PDEBUG(DCC, DEBUG_DEBUG, " -> (rtpmap) payload rate = %d\n", codec->payload_rate);
if (!(word = p)) {
/* if no channel is given and no default was specified, we must set 1 channel */
if (!codec->payload_channels)
codec->payload_channels = 1;
goto rtpmap_done;
}
codec->payload_channels = atoi(word);
PDEBUG(DCC, DEBUG_DEBUG, " -> (rtpmap) payload channels = %d\n", codec->payload_channels);
rtpmap_done:
if (!codec->payload_name || !codec->payload_rate || !codec->payload_channels) {
PDEBUG(DCC, DEBUG_NOTICE, "Broken 'rtpmap' definition in SDP line %d = '%s' Skipping codec!\n", line_no, line);
osmo_cc_free_codec(codec);
}
}
break;
}
}
/* if something is incomplete, abort here */
if (osmo_cc_session_check(session, 1)) {
PDEBUG(DCC, DEBUG_NOTICE, "Parsing SDP failed.\n");
osmo_cc_free_session(session);
return NULL;
}
return session;
}
void osmo_cc_debug_sdp(const char *_sdp)
{
const unsigned char *sdp = (const unsigned char *)_sdp;
char text[256];
int i;
while (*sdp) {
for (i = 0; *sdp > 0 && *sdp >= 32 && i < (int)sizeof(text) - 1; i++)
text[i] = *sdp++;
text[i] = '\0';
PDEBUG(DCC, DEBUG_DEBUG, " | %s\n", text);
while (*sdp > 0 && *sdp < 32)
sdp++;
}
}

View File

@ -1,6 +0,0 @@
char *osmo_cc_session_gensdp(struct osmo_cc_session *session);
struct osmo_cc_session *osmo_cc_session_parsesdp(osmo_cc_session_config_t *conf, void *priv, const char *_sdp);
int osmo_cc_payload_type_by_attrs(uint8_t *fmt, const char *name, uint32_t *rate, int *channels);
void osmo_cc_debug_sdp(const char *sdp);

View File

@ -1,640 +0,0 @@
/* Osmo-CC: Media Session handling
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include <inttypes.h>
#include "../libtimer/timer.h"
#include "../libdebug/debug.h"
#include "../liboptions/options.h"
#include "endpoint.h"
#define NTP_OFFSET 2208988800
void osmo_cc_set_local_peer(osmo_cc_session_config_t *conf, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address)
{
conf->default_nettype = nettype;
conf->default_addrtype = addrtype;
conf->default_unicast_address = options_strdup(address);
}
osmo_cc_session_t *osmo_cc_new_session(osmo_cc_session_config_t *conf, void *priv, const char *username, const char *sess_id, const char *sess_version, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *unicast_address, const char *session_name, int debug)
{
osmo_cc_session_t *session;
if (debug) PDEBUG(DCC, DEBUG_DEBUG, "Creating session structure.\n");
session = calloc(1, sizeof(*session));
if (!session) {
PDEBUG(DCC, DEBUG_ERROR, "No mem!\n");
abort();
}
session->config = conf;
session->priv = priv;
if (username) {
int i;
for (i = 0; username[i]; i++) {
if ((uint8_t)username[i] < 33) {
PDEBUG(DCC, DEBUG_ERROR, "Fatal error: SDP's originator (username) uses invalid characters, please fix!\n");
abort();
}
}
session->origin_local.username = strdup(username);
}
if (!username)
session->origin_local.username = strdup("-");
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> user name = %s\n", session->origin_local.username);
if (sess_id)
session->origin_local.sess_id = strdup(sess_id);
if (sess_version)
session->origin_local.sess_version = strdup(sess_version);
if (!sess_id || !sess_version) {
struct timeval tv;
char ntp_timestamp[32];
/* get time NTP format time stamp (time since 1900) */
gettimeofday(&tv, NULL);
sprintf(ntp_timestamp, "%" PRIu64, (uint64_t)tv.tv_sec + NTP_OFFSET);
if (!sess_id)
session->origin_local.sess_id = strdup(ntp_timestamp);
if (!sess_version)
session->origin_local.sess_version = strdup(ntp_timestamp);
}
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> session ID = %s\n", session->origin_local.sess_id);
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> session version = %s\n", session->origin_local.sess_version);
if (nettype)
session->origin_local.nettype = strdup(osmo_cc_session_nettype2string(nettype));
else
session->origin_local.nettype = strdup(osmo_cc_session_nettype2string(conf->default_nettype));
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> network type = %s\n", session->origin_local.nettype);
if (addrtype)
session->origin_local.addrtype = strdup(osmo_cc_session_addrtype2string(addrtype));
else
session->origin_local.addrtype = strdup(osmo_cc_session_addrtype2string(conf->default_addrtype));
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> address type = %s\n", session->origin_local.addrtype);
if (unicast_address)
session->origin_local.unicast_address = strdup(unicast_address);
else
session->origin_local.unicast_address = strdup(conf->default_unicast_address);
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> unicast address = %s\n", session->origin_local.unicast_address);
if (session_name)
session->name = strdup(session_name);
if (!session_name)
session->name = strdup("-");
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> session name = %s\n", session->name);
return session;
}
void osmo_cc_free_session(osmo_cc_session_t *session)
{
PDEBUG(DCC, DEBUG_DEBUG, "Free session structure.\n");
free((char *)session->origin_local.username);
free((char *)session->origin_local.sess_id);
free((char *)session->origin_local.sess_version);
free((char *)session->origin_local.nettype);
free((char *)session->origin_local.addrtype);
free((char *)session->origin_local.unicast_address);
free((char *)session->origin_remote.username);
free((char *)session->origin_remote.sess_id);
free((char *)session->origin_remote.sess_version);
free((char *)session->origin_remote.nettype);
free((char *)session->origin_remote.addrtype);
free((char *)session->origin_remote.unicast_address);
free((char *)session->name);
while (session->media_list)
osmo_cc_free_media(session->media_list);
free(session);
}
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), int debug)
{
osmo_cc_session_config_t *conf = session->config;
osmo_cc_session_media_t *media, **mediap;
media = calloc(1, sizeof(*media));
if (!media) {
PDEBUG(DCC, DEBUG_ERROR, "No mem!\n");
abort();
}
media->session = session;
if (nettype)
media->connection_data_local.nettype = nettype;
else
media->connection_data_local.nettype = conf->default_nettype;
if (addrtype)
media->connection_data_local.addrtype = addrtype;
else
media->connection_data_local.addrtype = conf->default_addrtype;
if (address)
media->connection_data_local.address = strdup(address);
else
media->connection_data_local.address = strdup(conf->default_unicast_address);
media->description.type = type;
media->description.port_local = port;
media->description.proto = proto;
media->send = send;
media->receive = receive;
media->receiver = receiver;
media->tx_sequence = random();
media->tx_timestamp = random();
mediap = &media->session->media_list;
while (*mediap)
mediap = &((*mediap)->next);
*mediap = media;
if (debug) PDEBUG(DCC, DEBUG_DEBUG, "Adding session media.\n");
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> network type = %s\n", osmo_cc_session_nettype2string(media->connection_data_local.nettype));
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> address type = %s\n", osmo_cc_session_addrtype2string(media->connection_data_local.addrtype));
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> address = %s\n", media->connection_data_local.address);
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> media type = %s\n", osmo_cc_session_media_type2string(media->description.type));
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> media port = %d\n", media->description.port_local);
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> media proto = %s\n", osmo_cc_session_media_proto2string(media->description.proto));
if (debug) PDEBUG(DCC, DEBUG_DEBUG, "Opening and binding media port %d\n", media->description.port_local);
return media;
}
void osmo_cc_free_media(osmo_cc_session_media_t *media)
{
osmo_cc_session_media_t **mediap;
PDEBUG(DCC, DEBUG_DEBUG, "Free session media.\n");
osmo_cc_rtp_close(media);
free((char *)media->connection_data_local.nettype_name);
free((char *)media->connection_data_local.addrtype_name);
free((char *)media->connection_data_local.address);
free((char *)media->connection_data_remote.nettype_name);
free((char *)media->connection_data_remote.addrtype_name);
free((char *)media->connection_data_remote.address);
while (media->codec_list)
osmo_cc_free_codec(media->codec_list);
mediap = &media->session->media_list;
while (*mediap != media)
mediap = &((*mediap)->next);
*mediap = media->next;
free(media);
}
osmo_cc_session_codec_t *osmo_cc_add_codec(osmo_cc_session_media_t *media, const char *payload_name, uint32_t payload_rate, int payload_channels, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), int debug)
{
osmo_cc_session_codec_t *codec, **codecp;
int rc;
codec = calloc(1, sizeof(*codec));
if (!codec) {
PDEBUG(DCC, DEBUG_ERROR, "No mem!\n");
abort();
}
codec->media = media;
if (payload_name) {
codec->payload_name = strdup(payload_name);
codec->payload_rate = payload_rate;
codec->payload_channels = payload_channels;
rc = osmo_cc_payload_type_by_attrs(&codec->payload_type_local, payload_name, &payload_rate, &payload_channels);
if (rc < 0) {
/* hunt for next free dynamic payload type */
uint8_t fmt = 96;
osmo_cc_session_codec_t *c;
osmo_cc_session_for_each_codec(media->codec_list, c) {
if (c->payload_type_local >= fmt)
fmt = c->payload_type_local + 1;
}
codec->payload_type_local = fmt;
}
}
codec->encoder = encoder;
codec->decoder = decoder;
codecp = &codec->media->codec_list;
while (*codecp)
codecp = &((*codecp)->next);
*codecp = codec;
if (debug) PDEBUG(DCC, DEBUG_DEBUG, "Adding session codec.\n");
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> payload type = %d\n", codec->payload_type_local);
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> payload name = %s\n", codec->payload_name);
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> payload rate = %d\n", codec->payload_rate);
if (debug) PDEBUG(DCC, DEBUG_DEBUG, " -> payload channels = %d\n", codec->payload_channels);
return codec;
}
void osmo_cc_free_codec(osmo_cc_session_codec_t *codec)
{
osmo_cc_session_codec_t **codecp;
PDEBUG(DCC, DEBUG_DEBUG, "Free session codec.\n");
free((char *)codec->payload_name);
codecp = &codec->media->codec_list;
while (*codecp != codec)
codecp = &((*codecp)->next);
*codecp = codec->next;
free(codec);
}
int osmo_cc_session_check(osmo_cc_session_t *session, int remote)
{
struct osmo_cc_session_origin *orig;
struct osmo_cc_session_media *media;
struct osmo_cc_session_connection_data *cd;
struct osmo_cc_session_media_description *md;
struct osmo_cc_session_codec *codec;
int i, j;
if (remote)
orig = &session->origin_remote;
else
orig = &session->origin_local;
if (!orig->username
|| !orig->sess_id
|| !orig->sess_version
|| !orig->nettype
|| !orig->addrtype
|| !orig->unicast_address) {
PDEBUG(DCC, DEBUG_NOTICE, "Missing data in session origin\n");
return -EINVAL;
}
if (!session->name) {
PDEBUG(DCC, DEBUG_NOTICE, "Missing data in session origin\n");
return -EINVAL;
}
if (!session->media_list) {
PDEBUG(DCC, DEBUG_NOTICE, "Missing media session\n");
return -EINVAL;
}
i = 0;
osmo_cc_session_for_each_media(session->media_list, media) {
i++;
if (remote)
cd = &media->connection_data_remote;
else
cd = &media->connection_data_local;
if (!cd->nettype && !cd->nettype_name) {
PDEBUG(DCC, DEBUG_NOTICE, "Session with media #%d is missing connection network type\n", i);
return -EINVAL;
}
if (!cd->addrtype && !cd->addrtype_name) {
PDEBUG(DCC, DEBUG_NOTICE, "Session with media #%d is missing connection address type\n", i);
return -EINVAL;
}
if (!cd->address) {
PDEBUG(DCC, DEBUG_NOTICE, "Session with media #%d is missing connection address\n", i);
return -EINVAL;
}
md = &media->description;
if (!md->type && !md->type_name) {
PDEBUG(DCC, DEBUG_NOTICE, "Session with media #%d is missing media type\n", i);
return -EINVAL;
}
if (!md->proto && !md->proto_name) {
PDEBUG(DCC, DEBUG_NOTICE, "Session with media #%d is missing protocol\n", i);
return -EINVAL;
}
j = 0;
osmo_cc_session_for_each_codec(media->codec_list, codec) {
j++;
if (!codec->payload_name) {
PDEBUG(DCC, DEBUG_NOTICE, "Session with media #%d, codec #%d is missing name\n", i, j);
return -EINVAL;
}
if (!codec->payload_rate) {
PDEBUG(DCC, DEBUG_NOTICE, "Session with media #%d, codec #%d is missing rate\n", i, j);
return -EINVAL;
}
if (!codec->payload_channels) {
PDEBUG(DCC, DEBUG_NOTICE, "Session with media #%d, codec #%d is missing channel count\n", i, j);
return -EINVAL;
}
}
}
return 0;
}
/* check session description and generate SDP */
const char *osmo_cc_session_send_offer(osmo_cc_session_t *session)
{
const char *sdp;
int rc;
PDEBUG(DCC, DEBUG_DEBUG, "Generating session offer and opening RTP stream.\n");
rc = osmo_cc_session_check(session, 0);
if (rc < 0) {
PDEBUG(DCC, DEBUG_ERROR, "Please fix!\n");
abort();
}
sdp = osmo_cc_session_gensdp(session);
osmo_cc_debug_sdp(sdp);
return sdp;
}
osmo_cc_session_t *osmo_cc_session_receive_offer(osmo_cc_session_config_t *conf, void *priv, const char *sdp)
{
osmo_cc_session_t *session;
int rc;
PDEBUG(DCC, DEBUG_DEBUG, "Parsing session offer.\n");
osmo_cc_debug_sdp(sdp);
session = osmo_cc_session_parsesdp(conf, priv, sdp);
if (!session)
return NULL;
rc = osmo_cc_session_check(session, 0);
if (rc < 0) {
osmo_cc_free_session(session);
return NULL;
}
return session;
}
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len))
{
osmo_cc_session_config_t *conf = media->session->config;
media->accepted = 1;
if (nettype)
media->connection_data_local.nettype = nettype;
else
media->connection_data_local.nettype = conf->default_nettype;
if (addrtype)
media->connection_data_local.addrtype = addrtype;
else
media->connection_data_local.addrtype = conf->default_addrtype;
free((char *)media->connection_data_local.address);
if (address)
media->connection_data_local.address = strdup(address);
else
media->connection_data_local.address = strdup(conf->default_unicast_address);
media->send = send;
media->receive = receive;
media->receiver = receiver;
PDEBUG(DCC, DEBUG_DEBUG, "Accepting session media.\n");
PDEBUG(DCC, DEBUG_DEBUG, " -> network type = %s\n", osmo_cc_session_nettype2string(media->connection_data_local.nettype));
PDEBUG(DCC, DEBUG_DEBUG, " -> address type = %s\n", osmo_cc_session_addrtype2string(media->connection_data_local.addrtype));
PDEBUG(DCC, DEBUG_DEBUG, " -> address = %s\n", media->connection_data_local.address);
}
void osmo_cc_session_accept_codec(osmo_cc_session_codec_t *codec, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len))
{
codec->accepted = 1;
codec->encoder = encoder;
codec->decoder = decoder;
/* when we accept a codec, we just use the same payload type as the remote */
codec->payload_type_local = codec->payload_type_remote;
PDEBUG(DCC, DEBUG_DEBUG, "Accepting session codec.\n");
PDEBUG(DCC, DEBUG_DEBUG, " -> payload type = %d\n", codec->payload_type_local);
PDEBUG(DCC, DEBUG_DEBUG, " -> payload name = %s\n", codec->payload_name);
PDEBUG(DCC, DEBUG_DEBUG, " -> payload rate = %d\n", codec->payload_rate);
PDEBUG(DCC, DEBUG_DEBUG, " -> payload channels = %d\n", codec->payload_channels);
}
/* remove codecs/media that have not been accepted and generate SDP */
const char *osmo_cc_session_send_answer(osmo_cc_session_t *session)
{
osmo_cc_session_media_t *media;
osmo_cc_session_codec_t *codec, **codec_p;
const char *sdp;
int rc;
PDEBUG(DCC, DEBUG_DEBUG, "Generating session answer.\n");
/* loop all media */
osmo_cc_session_for_each_media(session->media_list, media) {
/* remove unaccepted codecs */
codec_p = &media->codec_list;
codec = *codec_p;
while (codec) {
if (!codec->accepted) {
osmo_cc_free_codec(codec);
codec = *codec_p;
continue;
}
codec_p = &codec->next;
codec = *codec_p;
}
/* mark media as unused, if no codec or not accepted */
if (!media->accepted || !media->codec_list)
media->description.port_local = 0;
}
rc = osmo_cc_session_check(session, 0);
if (rc < 0) {
PDEBUG(DCC, DEBUG_ERROR, "Please fix!\n");
abort();
}
sdp = osmo_cc_session_gensdp(session);
osmo_cc_debug_sdp(sdp);
return sdp;
}
/* Apply remote session description to local session description.
* If remote media's port is 0, remove from local session description.
* If codecs in the remote session description are missing, remove from local session description.
*/
static int osmo_cc_session_negotiate(osmo_cc_session_t *session_local, struct osmo_cc_session *session_remote)
{
osmo_cc_session_media_t *media_local, *media_remote, **media_local_p;
osmo_cc_session_codec_t *codec_local, *codec_remote, **codec_local_p;
int rc;
PDEBUG(DCC, DEBUG_DEBUG, "Negotiating session.\n");
/* copy remote session information */
session_local->origin_remote.username = strdup(session_remote->origin_remote.username);
session_local->origin_remote.sess_id = strdup(session_remote->origin_remote.sess_id);
session_local->origin_remote.sess_version = strdup(session_remote->origin_remote.sess_version);
session_local->origin_remote.nettype = strdup(session_remote->origin_remote.nettype);
session_local->origin_remote.addrtype = strdup(session_remote->origin_remote.addrtype);
session_local->origin_remote.unicast_address = strdup(session_remote->origin_remote.unicast_address);
/* loop all media */
for (media_local = session_local->media_list, media_remote = session_remote->media_list; media_local && media_remote; media_local = media_local->next, media_remote = media_remote->next) {
/* copy remote media information */
media_local->connection_data_remote.nettype = media_remote->connection_data_remote.nettype;
if (media_remote->connection_data_remote.nettype_name)
media_local->connection_data_remote.nettype_name = strdup(media_remote->connection_data_remote.nettype_name);
media_local->connection_data_remote.addrtype = media_remote->connection_data_remote.addrtype;
if (media_remote->connection_data_remote.addrtype_name)
media_local->connection_data_remote.addrtype_name = strdup(media_remote->connection_data_remote.addrtype_name);
if (media_remote->connection_data_remote.address)
media_local->connection_data_remote.address = strdup(media_remote->connection_data_remote.address);
media_local->description.port_remote = media_remote->description.port_remote;
media_local->send = media_remote->send;
media_local->receive = media_remote->receive;
/* loop all codecs and remove if they are not found in local session description */
codec_local_p = &media_local->codec_list;
codec_local = *codec_local_p;
while (codec_local) {
/* search for equal codec, payload type may differe for each direction */
osmo_cc_session_for_each_codec(media_remote->codec_list, codec_remote) {
if (!strcmp(codec_local->payload_name, codec_remote->payload_name)
&& codec_local->payload_rate == codec_remote->payload_rate
&& codec_local->payload_channels == codec_remote->payload_channels)
break;
}
if (!codec_remote) {
osmo_cc_free_codec(codec_local);
codec_local = *codec_local_p;
continue;
}
/* copy remote codec information */
codec_local->payload_type_remote = codec_remote->payload_type_remote;
codec_local_p = &codec_local->next;
codec_local = *codec_local_p;
}
}
if (media_local) {
PDEBUG(DCC, DEBUG_NOTICE, "Negotiation failed, because remote endpoint returns less media streams than we offered.\n");
return -EINVAL;
}
if (media_remote) {
PDEBUG(DCC, DEBUG_NOTICE, "Negotiation failed, because remote endpoint returns more media streams than we offered.\n");
return -EINVAL;
}
/* remove media with port == 0 or no codec at all */
media_local_p = &session_local->media_list;
media_local = *media_local_p;
while (media_local) {
if (media_local->description.port_remote == 0 || !media_local->codec_list) {
osmo_cc_free_media(media_local);
media_local = *media_local_p;
continue;
}
media_local_p = &media_local->next;
media_local = *media_local_p;
}
rc = osmo_cc_session_check(session_local, 1);
if (rc < 0)
return rc;
return 0;
}
int osmo_cc_session_receive_answer(osmo_cc_session_t *session, const char *sdp)
{
osmo_cc_session_t *session_remote;
int rc;
PDEBUG(DCC, DEBUG_DEBUG, "Parsing session answer.\n");
osmo_cc_debug_sdp(sdp);
session_remote = osmo_cc_session_parsesdp(session->config, NULL, sdp);
if (!session_remote)
return -EINVAL;
rc = osmo_cc_session_check(session_remote, 1);
if (rc < 0) {
osmo_cc_free_session(session_remote);
return rc;
}
rc = osmo_cc_session_negotiate(session, session_remote);
if (rc < 0) {
osmo_cc_free_session(session_remote);
return rc;
}
osmo_cc_free_session(session_remote);
return 0;
}
const char *osmo_cc_session_nettype2string(enum osmo_cc_session_nettype nettype)
{
switch (nettype) {
case osmo_cc_session_nettype_inet:
return "IN";
default:
return NULL;
}
}
const char *osmo_cc_session_addrtype2string(enum osmo_cc_session_addrtype addrtype)
{
switch (addrtype) {
case osmo_cc_session_addrtype_ipv4:
return "IP4";
case osmo_cc_session_addrtype_ipv6:
return "IP6";
default:
return NULL;
}
}
const char *osmo_cc_session_media_type2string(enum osmo_cc_session_media_type media_type)
{
switch (media_type) {
case osmo_cc_session_media_type_audio:
return "audio";
case osmo_cc_session_media_type_video:
return "video";
default:
return NULL;
}
}
const char *osmo_cc_session_media_proto2string(enum osmo_cc_session_media_proto media_proto)
{
switch (media_proto) {
case osmo_cc_session_media_proto_rtp:
return "RTP/AVP";
default:
return NULL;
}
}
int osmo_cc_session_if_codec(osmo_cc_session_codec_t *codec, const char *name, uint32_t rate, int channels)
{
return (!strcmp(codec->payload_name, name)
&& codec->payload_rate == rate
&& codec->payload_channels == channels);
}
int osmo_cc_session_handle(osmo_cc_session_t *session)
{
osmo_cc_session_media_t *media;
int w = 0, rc;
osmo_cc_session_for_each_media(session->media_list, media) {
do {
rc = osmo_cc_rtp_receive(media);
if (rc >= 0)
w = 1;
} while (rc >= 0);
}
return w;
}

View File

@ -1,130 +0,0 @@
/* configuration */
enum osmo_cc_session_nettype {
osmo_cc_session_nettype_unknown = 0,
osmo_cc_session_nettype_inet,
};
enum osmo_cc_session_addrtype {
osmo_cc_session_addrtype_unknown = 0,
osmo_cc_session_addrtype_ipv4,
osmo_cc_session_addrtype_ipv6,
};
typedef struct osmo_cc_session_config {
enum osmo_cc_session_nettype default_nettype;
enum osmo_cc_session_addrtype default_addrtype;
const char *default_unicast_address;
uint16_t rtp_port_next;
uint16_t rtp_port_from;
uint16_t rtp_port_to;
} osmo_cc_session_config_t;
/* session description, global part: */
typedef struct osmo_cc_session_origin {
const char *username;
const char *sess_id;
const char *sess_version;
const char *nettype;
const char *addrtype;
const char *unicast_address;
} osmo_cc_session_origin_t;
/* session instance */
typedef struct osmo_cc_session {
osmo_cc_session_config_t *config;
void *priv;
osmo_cc_session_origin_t origin_local, origin_remote;
const char *name;
struct osmo_cc_session_media *media_list;
} osmo_cc_session_t;
/* connection description: */
typedef struct osmo_cc_session_connection_data {
enum osmo_cc_session_nettype nettype;
const char *nettype_name;
enum osmo_cc_session_addrtype addrtype;
const char *addrtype_name;
const char *address;
} osmo_cc_session_connection_data_t;
/* one media of session description: */
enum osmo_cc_session_media_type {
osmo_cc_session_media_type_unknown,
osmo_cc_session_media_type_audio,
osmo_cc_session_media_type_video,
};
enum osmo_cc_session_media_proto {
osmo_cc_session_media_proto_unknown,
osmo_cc_session_media_proto_rtp,
};
typedef struct osmo_cc_session_media_description {
enum osmo_cc_session_media_type type;
const char *type_name;
uint16_t port_local, port_remote;
enum osmo_cc_session_media_proto proto;
const char *proto_name;
} osmo_cc_session_media_description_t;
/* media entry */
typedef struct osmo_cc_session_media {
struct osmo_cc_session_media *next;
osmo_cc_session_t *session;
osmo_cc_session_media_description_t description;
osmo_cc_session_connection_data_t connection_data_local, connection_data_remote;
struct osmo_cc_session_codec *codec_list;
int send, receive;
void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len);
int rtp_socket;
int rtcp_socket;
uint32_t rtp_ssrc;
uint16_t tx_sequence, rx_sequence;
uint32_t tx_timestamp, rx_timestamp;
int accepted;
} osmo_cc_session_media_t;
/* codec entry */
typedef struct osmo_cc_session_codec {
struct osmo_cc_session_codec *next;
osmo_cc_session_media_t *media;
uint8_t payload_type_local, payload_type_remote; /* local = towards local, remote = toward remote */
const char *payload_name;
uint32_t payload_rate;
int payload_channels;
void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
int accepted;
} osmo_cc_session_codec_t;
#define osmo_cc_session_for_each_media(head, m) \
for (m = (head); m; m = m->next)
#define osmo_cc_session_for_each_codec(head, c) \
for (c = (head); c; c = c->next)
void osmo_cc_set_local_peer(osmo_cc_session_config_t *conf, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address);
osmo_cc_session_t *osmo_cc_new_session(osmo_cc_session_config_t *conf, void *priv, const char *username, const char *sess_id, const char *sess_version, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *unicast_address, const char *session_name, int debug);
void osmo_cc_free_session(osmo_cc_session_t *session);
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), int debug);
void osmo_cc_free_media(osmo_cc_session_media_t *media);
osmo_cc_session_codec_t *osmo_cc_add_codec(osmo_cc_session_media_t *media, const char *playload_name, uint32_t playload_rate, int playload_channels, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), int debug);
void osmo_cc_free_codec(osmo_cc_session_codec_t *codec);
int osmo_cc_session_check(struct osmo_cc_session *session, int remote);
const char *osmo_cc_session_send_offer(osmo_cc_session_t *session);
osmo_cc_session_t *osmo_cc_session_receive_offer(osmo_cc_session_config_t *conf, void *priv, const char *sdp);
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len));
void osmo_cc_session_accept_codec(osmo_cc_session_codec_t *codec, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len));
const char *osmo_cc_session_send_answer(osmo_cc_session_t *session);
int osmo_cc_session_receive_answer(osmo_cc_session_t *session, const char *sdp);
const char *osmo_cc_session_nettype2string(enum osmo_cc_session_nettype nettype);
const char *osmo_cc_session_addrtype2string(enum osmo_cc_session_addrtype addrtype);
const char *osmo_cc_session_media_type2string(enum osmo_cc_session_media_type media_type);
const char *osmo_cc_session_media_proto2string(enum osmo_cc_session_media_proto media_proto);
int osmo_cc_session_if_codec(osmo_cc_session_codec_t *codec, const char *name, uint32_t rate, int channels);
int osmo_cc_session_handle(osmo_cc_session_t *session);

View File

@ -1,583 +0,0 @@
/* Osmo-CC: Socket handling
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* 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 3 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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netdb.h>
#include "../libdebug/debug.h"
#include "../libtimer/timer.h"
#include "message.h"
#include "cause.h"
#include "socket.h"
static const char version_string[] = OSMO_CC_VERSION;
static int _getaddrinfo(const char *host, uint16_t port, struct addrinfo **result)
{
char portstr[8];
struct addrinfo hints;
int rc;
sprintf(portstr, "%d", port);
/* bind socket */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
rc = getaddrinfo(host, portstr, &hints, result);
if (rc < 0) {
PDEBUG(DCC, DEBUG_ERROR, "Failed to create socket for host '%s', port '%d': %s.\n", host, port, gai_strerror(rc));
return rc;
}
return rc;
}
/* send a reject message toward CC process.
* the CC process will change the reject message to a release message when not in INIT_IN state
*/
static void rej_msg(osmo_cc_socket_t *os, uint32_t callref, uint8_t socket_cause, uint8_t isdn_cause, uint16_t sip_cause)
{
osmo_cc_msg_t *msg;
/* create message */
msg = osmo_cc_new_msg(OSMO_CC_MSG_REJ_REQ);
if (!msg)
abort();
/* add cause */
osmo_cc_add_ie_cause(msg, os->location, isdn_cause, sip_cause, socket_cause);
osmo_cc_convert_cause_msg(msg);
/* message down */
os->recv_msg_cb(os->priv, callref, msg);
}
void tx_keepalive_timeout(struct timer *timer)
{
osmo_cc_conn_t *conn = (osmo_cc_conn_t *)timer->priv;
osmo_cc_msg_t *msg;
/* send keepalive message */
msg = osmo_cc_new_msg(OSMO_CC_MSG_DUMMY_REQ);
osmo_cc_msg_list_enqueue(&conn->os->write_list, msg, conn->callref);
timer_start(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
}
static void close_conn(osmo_cc_conn_t *conn, uint8_t socket_cause);
void rx_keepalive_timeout(struct timer *timer)
{
osmo_cc_conn_t *conn = (osmo_cc_conn_t *)timer->priv;
PDEBUG(DCC, DEBUG_ERROR, "OsmoCC-Socket failed due to timeout.\n");
close_conn(conn, OSMO_CC_SOCKET_CAUSE_TIMEOUT);
}
/* create socket process and bind socket */
int osmo_cc_open_socket(osmo_cc_socket_t *os, const char *host, uint16_t port, void *priv, void (*recv_msg_cb)(void *priv, uint32_t callref, osmo_cc_msg_t *msg), uint8_t location)
{
int try = 0, auto_port = 0;
struct addrinfo *result, *rp;
int rc, sock, flags;
memset(os, 0, sizeof(*os));
try_again:
/* check for given port, if NULL, autoselect port */
if (!port || auto_port) {
port = OSMO_CC_DEFAULT_PORT + try;
try++;
auto_port = 1;
}
PDEBUG(DCC, DEBUG_DEBUG, "Create socket for host %s port %d.\n", host, port);
rc = _getaddrinfo(host, port, &result);
if (rc < 0)
return rc;
for (rp = result; rp; rp = rp->ai_next) {
int on = 1;
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sock < 0)
continue;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (unsigned char *)&on, sizeof(on));
rc = bind(sock, rp->ai_addr, rp->ai_addrlen);
if (rc == 0)
break;
close(sock);
}
freeaddrinfo(result);
if (rp == NULL) {
if (auto_port && port < OSMO_CC_DEFAULT_PORT_MAX) {
PDEBUG(DCC, DEBUG_DEBUG, "Failed to bind host %s port %d, trying again.\n", host, port);
goto try_again;
}
PDEBUG(DCC, DEBUG_ERROR, "Failed to bind given host %s port %d.\n", host, port);
return -EIO;
}
/* listen to socket */
rc = listen(sock, 10);
if (rc < 0) {
PDEBUG(DCC, DEBUG_ERROR, "Failed to listen on socket.\n");
return rc;
}
/* set nonblocking io */
flags = fcntl(sock, F_GETFL);
flags |= O_NONBLOCK;
fcntl(sock, F_SETFL, flags);
os->socket = sock;
os->recv_msg_cb = recv_msg_cb;
os->priv = priv;
os->location = location;
return port;
}
/* create a connection */
static osmo_cc_conn_t *open_conn(osmo_cc_socket_t *os, int sock, uint32_t callref, int read_setup)
{
osmo_cc_conn_t *conn, **connp;
/* create connection */
conn = calloc(1, sizeof(*conn));
if (!conn) {
PDEBUG(DCC, DEBUG_ERROR, "No mem!\n");
abort();
}
conn->os = os;
conn->socket = sock;
conn->read_version = 1;
conn->write_version = 1;
conn->read_setup = read_setup;
if (callref)
conn->callref = callref;
else
conn->callref = osmo_cc_new_callref();
timer_init(&conn->tx_keepalive_timer, tx_keepalive_timeout, conn);
timer_init(&conn->rx_keepalive_timer, rx_keepalive_timeout, conn);
timer_start(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
timer_start(&conn->rx_keepalive_timer, OSMO_CC_SOCKET_RX_KEEPALIVE);
PDEBUG(DCC, DEBUG_DEBUG, "New socket connection (callref %d).\n", conn->callref);
/* attach to list */
connp = &os->conn_list;
while (*connp)
connp = &((*connp)->next);
*connp = conn;
return conn;
}
/* remove a connection */
static void close_conn(osmo_cc_conn_t *conn, uint8_t socket_cause)
{
osmo_cc_conn_t **connp;
osmo_cc_msg_list_t *ml;
/* detach connection first, to prevent a destruction during message handling (double free) */
connp = &conn->os->conn_list;
while (*connp != conn)
connp = &((*connp)->next);
*connp = conn->next;
/* send reject message, if socket_cause is set */
if (socket_cause && !conn->read_setup) {
/* receive a release or reject (depending on state), but only if we sent a setup */
rej_msg(conn->os, conn->callref, socket_cause, 0, 0);
}
PDEBUG(DCC, DEBUG_DEBUG, "Destroy socket connection (callref %d).\n", conn->callref);
/* close socket */
if (conn->socket)
close(conn->socket);
/* free partly received message */
if (conn->read_msg)
osmo_cc_free_msg(conn->read_msg);
/* free send queue */
while ((ml = conn->write_list)) {
osmo_cc_free_msg(ml->msg);
conn->write_list = ml->next;
free(ml);
}
/* free timers */
timer_exit(&conn->tx_keepalive_timer);
timer_exit(&conn->rx_keepalive_timer);
/* free connection (already detached above) */
free(conn);
}
/* close socket and remove */
void osmo_cc_close_socket(osmo_cc_socket_t *os)
{
osmo_cc_msg_list_t *ml;
PDEBUG(DCC, DEBUG_DEBUG, "Destroy socket.\n");
/* free all connections */
while (os->conn_list)
close_conn(os->conn_list, 0);
/* close socket */
if (os->socket > 0) {
close(os->socket);
os->socket = 0;
}
/* free send queue */
while ((ml = os->write_list)) {
osmo_cc_free_msg(ml->msg);
os->write_list = ml->next;
free(ml);
}
}
/* send message to send_queue of sock instance */
int osmo_cc_sock_send_msg(osmo_cc_socket_t *os, uint32_t callref, osmo_cc_msg_t *msg, const char *host, uint16_t port)
{
osmo_cc_msg_list_t *ml;
/* turn _IND into _REQ and _CNF into _RSP */
msg->type &= ~1;
/* create list entry */
ml = osmo_cc_msg_list_enqueue(&os->write_list, msg, callref);
if (host)
strncpy(ml->host, host, sizeof(ml->host) - 1);
ml->port = port;
return 0;
}
/* receive message
* return 1 if work was done.
*/
static int receive_conn(osmo_cc_conn_t *conn)
{
uint8_t socket_cause = OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE;
int rc;
osmo_cc_msg_t *msg;
uint8_t msg_type;
int len;
int work = 0;
/* get version from remote */
if (conn->read_version) {
rc = recv(conn->socket, conn->read_version_string + conn->read_version_pos, strlen(version_string) - conn->read_version_pos, 0);
if (rc < 0 && errno == EAGAIN)
return work;
work = 1;
if (rc <= 0) {
goto close;
}
conn->read_version_pos += rc;
if (conn->read_version_pos == strlen(version_string)) {
conn->read_version = 0;
if (!!memcmp(conn->read_version_string, version_string, strlen(version_string) - 1)) {
PDEBUG(DCC, DEBUG_NOTICE, "Remote does not seem to be an Osmo-CC socket, rejecting!\n");
socket_cause = OSMO_CC_SOCKET_CAUSE_FAILED;
goto close;
}
if (conn->read_version_string[strlen(version_string) - 1] != version_string[strlen(version_string) - 1]) {
PDEBUG(DCC, DEBUG_NOTICE, "Remote Osmo-CC socket has wrong version (local=%s, remote=%s), rejecting!\n", version_string, conn->read_version_string);
socket_cause = OSMO_CC_SOCKET_CAUSE_VERSION_MISMATCH;
goto close;
}
} else
return work;
}
try_next_message:
/* read message header from remote */
if (!conn->read_msg) {
rc = recv(conn->socket, ((uint8_t *)&conn->read_hdr) + conn->read_pos, sizeof(conn->read_hdr) - conn->read_pos, 0);
if (rc < 0 && errno == EAGAIN)
return work;
work = 1;
if (rc <= 0) {
goto close;
}
conn->read_pos += rc;
if (conn->read_pos == sizeof(conn->read_hdr)) {
conn->read_msg = osmo_cc_new_msg(conn->read_hdr.type);
if (!conn->read_msg)
abort();
conn->read_msg->length_networkorder = conn->read_hdr.length_networkorder;
/* prepare for reading message */
conn->read_pos = 0;
} else
return work;
}
/* read message data from remote */
msg = conn->read_msg;
len = ntohs(msg->length_networkorder);
if (len == 0)
goto empty_message;
rc = recv(conn->socket, msg->data + conn->read_pos, len - conn->read_pos, 0);
if (rc < 0 && errno == EAGAIN)
return work;
work = 1;
if (rc <= 0) {
goto close;
}
conn->read_pos += rc;
if (conn->read_pos == len) {
empty_message:
/* start RX keepalive timeer, if not already */
timer_start(&conn->rx_keepalive_timer, OSMO_CC_SOCKET_RX_KEEPALIVE);
/* we got our setup message, so we clear the flag */
conn->read_setup = 0;
/* prepare for reading header */
conn->read_pos = 0;
/* detach message first, because the connection might be destroyed during message handling */
msg_type = conn->read_msg->type;
conn->read_msg = NULL;
/* drop dummy or forward message */
if (msg_type == OSMO_CC_MSG_DUMMY_REQ)
osmo_cc_free_msg(msg);
else
conn->os->recv_msg_cb(conn->os->priv, conn->callref, msg);
if (msg_type == OSMO_CC_MSG_REL_REQ || msg_type == OSMO_CC_MSG_REJ_REQ) {
PDEBUG(DCC, DEBUG_DEBUG, "closing socket because we received a release or reject message.\n");
close_conn(conn, 0);
return 1; /* conn removed */
}
goto try_next_message;
}
return work;
close:
PDEBUG(DCC, DEBUG_ERROR, "OsmoCC-Socket failed, socket cause %d.\n", socket_cause);
close_conn(conn, socket_cause);
return work; /* conn removed */
}
/* transmit message
* return 1 if work was done.
*/
static int transmit_conn(osmo_cc_conn_t *conn)
{
uint8_t socket_cause = OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE;
int rc;
osmo_cc_msg_t *msg;
int len;
osmo_cc_msg_list_t *ml;
int work = 0;
/* send socket version to remote */
if (conn->write_version) {
rc = write(conn->socket, version_string, strlen(version_string));
if (rc < 0 && errno == EAGAIN)
return work;
work = 1;
if (rc <= 0) {
goto close;
}
if (rc != strlen(version_string)) {
PDEBUG(DCC, DEBUG_ERROR, "short write, please fix handling!\n");
abort();
}
conn->write_version = 0;
}
/* send message to remote */
while (conn->write_list) {
timer_stop(&conn->tx_keepalive_timer);
msg = conn->write_list->msg;
len = sizeof(*msg) + ntohs(msg->length_networkorder);
rc = write(conn->socket, msg, len);
if (rc < 0 && errno == EAGAIN)
return work;
work = 1;
if (rc <= 0) {
goto close;
}
if (rc != len) {
PDEBUG(DCC, DEBUG_ERROR, "short write, please fix handling!\n");
abort();
}
/* close socket after sending release/reject message */
if (msg->type == OSMO_CC_MSG_REL_REQ || msg->type == OSMO_CC_MSG_REJ_REQ) {
PDEBUG(DCC, DEBUG_DEBUG, "closing socket because we sent a release or reject message.\n");
close_conn(conn, 0);
return work; /* conn removed */
}
/* free message after sending */
ml = conn->write_list;
conn->write_list = ml->next;
osmo_cc_free_msg(msg);
free(ml);
}
/* start TX keepalive timeer, if not already
* because we stop at every message above, we actually restart the timer here.
* only if there is no message for the amount of time, the timer fires.
*/
if (!timer_running(&conn->tx_keepalive_timer))
timer_start(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
return work;
close:
PDEBUG(DCC, DEBUG_NOTICE, "OsmoCC-Socket failed.\n");
close_conn(conn, socket_cause);
return work; /* conn removed */
}
/* handle all sockets of a socket interface
* return 1 if work was done.
*/
int osmo_cc_handle_socket(osmo_cc_socket_t *os)
{
struct sockaddr_storage sa;
socklen_t slen = sizeof(sa);
int sock;
osmo_cc_conn_t *conn;
osmo_cc_msg_list_t *ml, **mlp;
int flags;
struct addrinfo *result, *rp;
int rc;
int work = 0;
/* handle messages in send queue */
while ((ml = os->write_list)) {
work = 1;
/* detach list entry */
os->write_list = ml->next;
ml->next = NULL;
/* search for socket connection */
for (conn = os->conn_list; conn; conn=conn->next) {
if (conn->callref == ml->callref)
break;
}
if (conn) {
/* attach to list */
mlp = &conn->write_list;
while (*mlp)
mlp = &((*mlp)->next);
*mlp = ml;
/* done with message */
continue;
}
/* reject and release are ignored */
if (ml->msg->type == OSMO_CC_MSG_REJ_REQ
|| ml->msg->type == OSMO_CC_MSG_REL_REQ) {
/* drop message */
osmo_cc_free_msg(ml->msg);
free(ml);
/* done with message */
continue;
}
/* reject, if this is not a setup message */
if (ml->msg->type != OSMO_CC_MSG_SETUP_REQ
&& ml->msg->type != OSMO_CC_MSG_ATTACH_REQ) {
PDEBUG(DCC, DEBUG_ERROR, "Message with unknown callref.\n");
rej_msg(os, ml->callref, 0, OSMO_CC_ISDN_CAUSE_INVAL_CALLREF, 0);
/* drop message */
osmo_cc_free_msg(ml->msg);
free(ml);
/* done with message */
continue;
}
/* connect to remote */
rc = _getaddrinfo(ml->host, ml->port, &result);
if (rc < 0) {
rej_msg(os, ml->callref, OSMO_CC_SOCKET_CAUSE_FAILED, 0, 0);
/* drop message */
osmo_cc_free_msg(ml->msg);
free(ml);
/* done with message */
continue;
}
for (rp = result; rp; rp = rp->ai_next) {
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sock < 0)
continue;
/* set nonblocking io */
flags = fcntl(sock, F_GETFL);
flags |= O_NONBLOCK;
fcntl(sock, F_SETFL, flags);
/* connect */
rc = connect(sock, rp->ai_addr, rp->ai_addrlen);
if (rc == 0 || errno == EINPROGRESS)
break;
close(sock);
}
freeaddrinfo(result);
if (rp == NULL) {
PDEBUG(DCC, DEBUG_ERROR, "Failed to connect to given host %s port %d.\n", ml->host, ml->port);
rej_msg(os, ml->callref, OSMO_CC_SOCKET_CAUSE_FAILED, 0, 0);
/* drop message */
osmo_cc_free_msg(ml->msg);
free(ml);
/* done with message */
continue;
}
/* create connection */
conn = open_conn(os, sock, ml->callref, 0);
/* attach to list */
conn->write_list = ml;
/* done with (setup) message */
}
/* handle new socket connection */
while ((sock = accept(os->socket, (struct sockaddr *)&sa, &slen)) > 0) {
work = 1;
/* set nonblocking io */
flags = fcntl(sock, F_GETFL);
flags |= O_NONBLOCK;
fcntl(sock, F_SETFL, flags);
/* create connection */
open_conn(os, sock, 0, 1);
}
/* start with list after each read/write, because while handling (the message), one or more connections may be destroyed */
for (conn = os->conn_list; conn; conn=conn->next) {
/* check for rx */
work = receive_conn(conn);
/* if "change" is set, connection list might have changed, so we restart processing the list */
if (work)
break;
/* check for tx */
work = transmit_conn(conn);
/* if "change" is set, connection list might have changed, so we restart processing the list */
if (work)
break;
}
return work;
}

Some files were not shown because too many files have changed in this diff Show More