244 lines
6.2 KiB
C
244 lines
6.2 KiB
C
/*
|
|
* SpanDSP - a series of DSP components for telephony
|
|
*
|
|
* filter_tools.c - A collection of routines used for filter design.
|
|
*
|
|
* Written by Steve Underwood <steveu@coppice.org>
|
|
*
|
|
* Copyright (C) 2008 Steve Underwood
|
|
*
|
|
* This includes some elements based on the mkfilter package by
|
|
* A.J. Fisher, University of York <fisher@minster.york.ac.uk>, November 1996
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License version 2.1,
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <fcntl.h>
|
|
#if defined(HAVE_TGMATH_H)
|
|
#include <tgmath.h>
|
|
#endif
|
|
#if defined(HAVE_MATH_H)
|
|
#include <math.h>
|
|
#endif
|
|
#if defined(HAVE_STDBOOL_H)
|
|
#include <stdbool.h>
|
|
#else
|
|
#include "spandsp/stdbool.h"
|
|
#endif
|
|
#include "floating_fudge.h"
|
|
|
|
#include "spandsp/telephony.h"
|
|
#include "spandsp/complex.h"
|
|
#include "filter_tools.h"
|
|
|
|
#define MAXPZ 8192
|
|
#define SEQ_LEN 8192
|
|
#define MAX_FFT_LEN SEQ_LEN
|
|
|
|
static complex_t circle[MAX_FFT_LEN/2];
|
|
|
|
static __inline__ complex_t expj(double theta)
|
|
{
|
|
return complex_set(cos(theta), sin(theta));
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static __inline__ double fix(double x)
|
|
{
|
|
/* Nearest integer */
|
|
return (x >= 0.0) ? floor(0.5 + x) : -floor(0.5 - x);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static void fftx(complex_t data[], complex_t temp[], int n)
|
|
{
|
|
int i;
|
|
int h;
|
|
int p;
|
|
int t;
|
|
int i2;
|
|
complex_t wkt;
|
|
|
|
if (n > 1)
|
|
{
|
|
h = n/2;
|
|
for (i = 0; i < h; i++)
|
|
{
|
|
i2 = i*2;
|
|
temp[i] = data[i2]; /* Even */
|
|
temp[h + i] = data[i2 + 1]; /* Odd */
|
|
}
|
|
fftx(&temp[0], &data[0], h);
|
|
fftx(&temp[h], &data[h], h);
|
|
p = 0;
|
|
t = MAX_FFT_LEN/n;
|
|
for (i = 0; i < h; i++)
|
|
{
|
|
wkt = complex_mul(&circle[p], &temp[h + i]);
|
|
data[i] = complex_add(&temp[i], &wkt);
|
|
data[h + i] = complex_sub(&temp[i], &wkt);
|
|
p += t;
|
|
}
|
|
}
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
void ifft(complex_t data[], int len)
|
|
{
|
|
int i;
|
|
double x;
|
|
complex_t temp[MAX_FFT_LEN];
|
|
|
|
/* A very slow and clunky FFT, that's just fine for filter design. */
|
|
for (i = 0; i < MAX_FFT_LEN/2; i++)
|
|
{
|
|
x = (2.0*3.1415926535*i)/(double) MAX_FFT_LEN;
|
|
circle[i] = expj(x);
|
|
}
|
|
fftx(data, temp, len);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
void compute_raised_cosine_filter(double coeffs[],
|
|
int len,
|
|
int root,
|
|
int sinc_compensate,
|
|
double alpha,
|
|
double beta)
|
|
{
|
|
double f;
|
|
double x;
|
|
double f1;
|
|
double f2;
|
|
double tau;
|
|
complex_t vec[SEQ_LEN];
|
|
int i;
|
|
int j;
|
|
int h;
|
|
|
|
f1 = (1.0 - beta)*alpha;
|
|
f2 = (1.0 + beta)*alpha;
|
|
tau = 0.5/alpha;
|
|
/* (Root) raised cosine */
|
|
for (i = 0; i <= SEQ_LEN/2; i++)
|
|
{
|
|
f = (double) i/(double) SEQ_LEN;
|
|
if (f <= f1)
|
|
vec[i] = complex_set(1.0, 0.0);
|
|
else if (f <= f2)
|
|
vec[i] = complex_set(0.5*(1.0 + cos((3.1415926535*tau/beta)*(f - f1))), 0.0);
|
|
else
|
|
vec[i] = complex_set(0.0, 0.0);
|
|
}
|
|
if (root)
|
|
{
|
|
for (i = 0; i <= SEQ_LEN/2; i++)
|
|
vec[i].re = sqrt(vec[i].re);
|
|
}
|
|
if (sinc_compensate)
|
|
{
|
|
for (i = 1; i <= SEQ_LEN/2; i++)
|
|
{
|
|
x = 3.1415926535*(double) i/(double) SEQ_LEN;
|
|
vec[i].re *= (x/sin(x));
|
|
}
|
|
}
|
|
for (i = 0; i <= SEQ_LEN/2; i++)
|
|
vec[i].re *= tau;
|
|
for (i = 1; i < SEQ_LEN/2; i++)
|
|
vec[SEQ_LEN - i] = vec[i];
|
|
ifft(vec, SEQ_LEN);
|
|
h = (len - 1)/2;
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
j = (SEQ_LEN - h + i)%SEQ_LEN;
|
|
coeffs[i] = vec[j].re/(double) SEQ_LEN;
|
|
}
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
void compute_hilbert_transform(double coeffs[], int len)
|
|
{
|
|
double x;
|
|
int i;
|
|
int h;
|
|
|
|
h = (len - 1)/2;
|
|
coeffs[h] = 0.0;
|
|
for (i = 1; i <= h; i++)
|
|
{
|
|
if ((i & 1))
|
|
{
|
|
x = 1.0/(double) i;
|
|
coeffs[h + i] = -x;
|
|
coeffs[h - i] = x;
|
|
}
|
|
else
|
|
{
|
|
coeffs[h + i] =
|
|
coeffs[h - i] = 0.0;
|
|
}
|
|
}
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
void apply_hamming_window(double coeffs[], int len)
|
|
{
|
|
double w;
|
|
int i;
|
|
int h;
|
|
|
|
h = (len - 1)/2;
|
|
for (i = 1; i <= h; i++)
|
|
{
|
|
w = 0.53836 - 0.46164*cos(2.0*3.1415926535*(double) (h + i)/(double) (len - 1.0));
|
|
coeffs[h + i] *= w;
|
|
coeffs[h - i] *= w;
|
|
}
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
void truncate_coeffs(double coeffs[], int len, int bits, int hilbert)
|
|
{
|
|
double x;
|
|
double fac;
|
|
double max;
|
|
double scale;
|
|
int h;
|
|
int i;
|
|
|
|
fac = pow(2.0, (double) (bits - 1.0));
|
|
h = (len - 1)/2;
|
|
max = (hilbert) ? coeffs[h - 1] : coeffs[h]; /* Max coeff */
|
|
scale = (fac - 1.0)/(fac*max);
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
x = coeffs[i]*scale; /* Scale coeffs so max is (fac - 1.0)/fac */
|
|
coeffs[i] = fix(x*fac)/fac; /* Truncate */
|
|
}
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
/*- End of file ------------------------------------------------------------*/
|