Filter improvement: LP and HP filters, added test function
The -3 dB level at cut-off frequency is now maintained for multiple iterations.
This commit is contained in:
parent
799b96c801
commit
ac0da3f76e
|
@ -9,7 +9,6 @@ config.sub
|
||||||
configure
|
configure
|
||||||
depcomp
|
depcomp
|
||||||
install-sh
|
install-sh
|
||||||
libcolorize.pc
|
|
||||||
libtool
|
libtool
|
||||||
ltmain.sh
|
ltmain.sh
|
||||||
missing
|
missing
|
||||||
|
@ -28,6 +27,7 @@ src/cnetz/cnetz
|
||||||
src/nmt/nmt
|
src/nmt/nmt
|
||||||
src/amps/amps
|
src/amps/amps
|
||||||
sim/cnetz_sim
|
sim/cnetz_sim
|
||||||
|
src/test/test_filter
|
||||||
src/test/test_compandor
|
src/test/test_compandor
|
||||||
src/test/test_emphasis
|
src/test/test_emphasis
|
||||||
src/test/test_dms
|
src/test/test_dms
|
||||||
|
|
|
@ -48,7 +48,7 @@ void scrambler_init(void)
|
||||||
|
|
||||||
void scrambler_setup(scrambler_t *scrambler, int samplerate)
|
void scrambler_setup(scrambler_t *scrambler, int samplerate)
|
||||||
{
|
{
|
||||||
filter_lowpass_init(&scrambler->lp, CARRIER_HZ - FILTER_BELOW, samplerate);
|
filter_lowpass_init(&scrambler->lp, CARRIER_HZ - FILTER_BELOW, samplerate, FILTER_TURNS);
|
||||||
scrambler->carrier_phaseshift256 = 256.0 / ((double)samplerate / CARRIER_HZ);
|
scrambler->carrier_phaseshift256 = 256.0 / ((double)samplerate / CARRIER_HZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ void scrambler(scrambler_t *scrambler, int16_t *samples, int length)
|
||||||
scrambler->carrier_phase256 = phase;
|
scrambler->carrier_phase256 = phase;
|
||||||
|
|
||||||
/* cut off carrier frequency and modulation above carrier frequency */
|
/* cut off carrier frequency and modulation above carrier frequency */
|
||||||
filter_lowpass_process(&scrambler->lp, spl, length, FILTER_TURNS);
|
filter_process(&scrambler->lp, spl, length);
|
||||||
|
|
||||||
for (i = 0; i < length; i++) {
|
for (i = 0; i < length; i++) {
|
||||||
/* store result */
|
/* store result */
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#include "../common/filter.h"
|
#include "../common/filter.h"
|
||||||
|
|
||||||
typedef struct scrambler {
|
typedef struct scrambler {
|
||||||
double carrier_phaseshift256; /* carrier phase shift per sample */
|
double carrier_phaseshift256; /* carrier phase shift per sample */
|
||||||
double carrier_phase256; /* current phase of carrier frequency */
|
double carrier_phase256; /* current phase of carrier frequency */
|
||||||
filter_lowpass_t lp; /* filter to remove carrier frequency */
|
filter_t lp; /* filter to remove carrier frequency */
|
||||||
} scrambler_t;
|
} scrambler_t;
|
||||||
|
|
||||||
void scrambler_init(void);
|
void scrambler_init(void);
|
||||||
|
|
|
@ -26,14 +26,18 @@
|
||||||
|
|
||||||
#define PI M_PI
|
#define PI M_PI
|
||||||
|
|
||||||
//#define CASCADE
|
void filter_lowpass_init(filter_t *bq, double frequency, int samplerate, int iterations)
|
||||||
|
|
||||||
void filter_lowpass_init(filter_lowpass_t *bq, double frequency, int samplerate)
|
|
||||||
{
|
{
|
||||||
double Fc, Q, K, norm;
|
double Fc, Q, K, norm;
|
||||||
|
|
||||||
|
if (iterations > 64) {
|
||||||
|
fprintf(stderr, "%s failed: too many iterations, please fix!\n", __func__);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
memset(bq, 0, sizeof(*bq));
|
memset(bq, 0, sizeof(*bq));
|
||||||
Q = sqrt(0.5); /* 0.7071... */
|
bq->iter = iterations;
|
||||||
|
Q = pow(sqrt(0.5), 1.0 / (double)iterations); /* 0.7071 @ 1 iteration */
|
||||||
Fc = frequency / (double)samplerate;
|
Fc = frequency / (double)samplerate;
|
||||||
K = tan(PI * Fc);
|
K = tan(PI * Fc);
|
||||||
norm = 1 / (1 + K / Q + K * K);
|
norm = 1 / (1 + K / Q + K * K);
|
||||||
|
@ -44,18 +48,31 @@ void filter_lowpass_init(filter_lowpass_t *bq, double frequency, int samplerate)
|
||||||
bq->b2 = (1 - K / Q + K * K) * norm;
|
bq->b2 = (1 - K / Q + K * K) * norm;
|
||||||
}
|
}
|
||||||
|
|
||||||
void filter_lowpass_process(filter_lowpass_t *bq, double *samples, int length, int iterations)
|
void filter_highpass_init(filter_t *bq, double frequency, int samplerate, int iterations)
|
||||||
|
{
|
||||||
|
double Fc, Q, K, norm;
|
||||||
|
|
||||||
|
memset(bq, 0, sizeof(*bq));
|
||||||
|
bq->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);
|
||||||
|
bq->a0 = 1 * norm;
|
||||||
|
bq->a1 = -2 * bq->a0;
|
||||||
|
bq->a2 = bq->a0;
|
||||||
|
bq->b1 = 2 * (K * K - 1) * norm;
|
||||||
|
bq->b2 = (1 - K / Q + K * K) * norm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void filter_process(filter_t *bq, double *samples, int length)
|
||||||
{
|
{
|
||||||
double a0, a1, a2, b1, b2;
|
double a0, a1, a2, b1, b2;
|
||||||
double *z1, *z2;
|
double *z1, *z2;
|
||||||
double in, out;
|
double in, out;
|
||||||
|
int iterations = bq->iter;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
if (iterations > 10) {
|
|
||||||
fprintf(stderr, "%s failed: too many iterations, please fix!\n", __func__);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get states */
|
/* get states */
|
||||||
a0 = bq->a0;
|
a0 = bq->a0;
|
||||||
a1 = bq->a1;
|
a1 = bq->a1;
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
#ifndef _FILTER_H
|
#ifndef _FILTER_H
|
||||||
#define _FILTER_H
|
#define _FILTER_H
|
||||||
|
|
||||||
typedef struct filter_lowpass {
|
typedef struct filter {
|
||||||
|
int iter;
|
||||||
double a0, a1, a2, b1, b2;
|
double a0, a1, a2, b1, b2;
|
||||||
double z1[10], z2[10];
|
double z1[64], z2[64];
|
||||||
} filter_lowpass_t;
|
} filter_t;
|
||||||
|
|
||||||
void filter_lowpass_init(filter_lowpass_t *bq, double frequency, int samplerate);
|
void filter_lowpass_init(filter_t *bq, double frequency, int samplerate, int iterations);
|
||||||
void filter_lowpass_process(filter_lowpass_t *bq, double *samples, int length, int iterations);
|
void filter_highpass_init(filter_t *bq, double frequency, int samplerate, int iterations);
|
||||||
|
void filter_process(filter_t *bq, double *samples, int length);
|
||||||
|
|
||||||
#endif /* _FILTER_H */
|
#endif /* _FILTER_H */
|
||||||
|
|
|
@ -37,8 +37,8 @@ int init_samplerate(samplerate_t *state, double samplerate)
|
||||||
memset(state, 0, sizeof(*state));
|
memset(state, 0, sizeof(*state));
|
||||||
state->factor = samplerate / 8000.0;
|
state->factor = samplerate / 8000.0;
|
||||||
|
|
||||||
filter_lowpass_init(&state->up.lp, 4000.0, samplerate);
|
filter_lowpass_init(&state->up.lp, 4000.0, samplerate, 1);
|
||||||
filter_lowpass_init(&state->down.lp, 4000.0, samplerate);
|
filter_lowpass_init(&state->down.lp, 4000.0, samplerate, 1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ int samplerate_downsample(samplerate_t *state, int16_t *input, int input_num, in
|
||||||
spl[i] = *input++ / 32768.0;
|
spl[i] = *input++ / 32768.0;
|
||||||
|
|
||||||
/* filter down */
|
/* filter down */
|
||||||
filter_lowpass_process(&state->down.lp, spl, input_num, 1);
|
filter_process(&state->down.lp, spl, input_num);
|
||||||
|
|
||||||
/* resample filtered result */
|
/* resample filtered result */
|
||||||
in_index = state->down.in_index;
|
in_index = state->down.in_index;
|
||||||
|
@ -125,7 +125,7 @@ int samplerate_upsample(samplerate_t *state, int16_t *input, int input_num, int1
|
||||||
state->up.in_index = in_index;
|
state->up.in_index = in_index;
|
||||||
|
|
||||||
/* filter up */
|
/* filter up */
|
||||||
filter_lowpass_process(&state->up.lp, spl, output_num, 1);
|
filter_process(&state->up.lp, spl, output_num);
|
||||||
|
|
||||||
/* convert double to samples */
|
/* convert double to samples */
|
||||||
for (i = 0; i < output_num; i++) {
|
for (i = 0; i < output_num; i++) {
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
typedef struct samplerate {
|
typedef struct samplerate {
|
||||||
double factor;
|
double factor;
|
||||||
struct {
|
struct {
|
||||||
filter_lowpass_t lp;
|
filter_t lp;
|
||||||
double in_index;
|
double in_index;
|
||||||
} down;
|
} down;
|
||||||
struct {
|
struct {
|
||||||
filter_lowpass_t lp;
|
filter_t lp;
|
||||||
double in_index;
|
double in_index;
|
||||||
} up;
|
} up;
|
||||||
} samplerate_t;
|
} samplerate_t;
|
||||||
|
|
|
@ -39,7 +39,7 @@ typedef struct sdr_chan {
|
||||||
double rx_rot; /* rotation step per sample to shift rx frequency (used to shift) */
|
double rx_rot; /* rotation step per sample to shift rx frequency (used to shift) */
|
||||||
double rx_phase; /* current rotation phase (used to shift) */
|
double rx_phase; /* current rotation phase (used to shift) */
|
||||||
double rx_last_phase; /* last phase of FM (used to demodulate) */
|
double rx_last_phase; /* last phase of FM (used to demodulate) */
|
||||||
filter_lowpass_t rx_lp[2]; /* filters received IQ signal */
|
filter_t rx_lp[2]; /* filters received IQ signal */
|
||||||
} sdr_chan_t;
|
} sdr_chan_t;
|
||||||
|
|
||||||
typedef struct sdr {
|
typedef struct sdr {
|
||||||
|
@ -123,8 +123,8 @@ void *sdr_open(const char __attribute__((__unused__)) *audiodev, double *tx_freq
|
||||||
PDEBUG(DSDR, DEBUG_INFO, "Frequency #%d: TX = %.6f MHz, RX = %.6f MHz\n", c, tx_frequency[c] / 1e6, rx_frequency[c] / 1e6);
|
PDEBUG(DSDR, DEBUG_INFO, "Frequency #%d: TX = %.6f MHz, RX = %.6f MHz\n", c, tx_frequency[c] / 1e6, rx_frequency[c] / 1e6);
|
||||||
sdr->chan[c].tx_frequency = tx_frequency[c];
|
sdr->chan[c].tx_frequency = tx_frequency[c];
|
||||||
sdr->chan[c].rx_frequency = rx_frequency[c];
|
sdr->chan[c].rx_frequency = rx_frequency[c];
|
||||||
filter_lowpass_init(&sdr->chan[c].rx_lp[0], bandwidth, samplerate);
|
filter_lowpass_init(&sdr->chan[c].rx_lp[0], bandwidth, samplerate, 1);
|
||||||
filter_lowpass_init(&sdr->chan[c].rx_lp[1], bandwidth, samplerate);
|
filter_lowpass_init(&sdr->chan[c].rx_lp[1], bandwidth, samplerate, 1);
|
||||||
}
|
}
|
||||||
if (sdr->paging_channel) {
|
if (sdr->paging_channel) {
|
||||||
PDEBUG(DSDR, DEBUG_INFO, "Paging Frequency: TX = %.6f MHz\n", paging_frequency / 1e6);
|
PDEBUG(DSDR, DEBUG_INFO, "Paging Frequency: TX = %.6f MHz\n", paging_frequency / 1e6);
|
||||||
|
@ -378,8 +378,8 @@ int sdr_read(void *inst, int16_t **samples, int num, int channels)
|
||||||
Q[s] = i * sin(phase) + q * cos(phase);
|
Q[s] = i * sin(phase) + q * cos(phase);
|
||||||
}
|
}
|
||||||
sdr->chan[c].rx_phase = phase;
|
sdr->chan[c].rx_phase = phase;
|
||||||
filter_lowpass_process(&sdr->chan[c].rx_lp[0], I, count, 1);
|
filter_process(&sdr->chan[c].rx_lp[0], I, count);
|
||||||
filter_lowpass_process(&sdr->chan[c].rx_lp[1], Q, count, 1);
|
filter_process(&sdr->chan[c].rx_lp[1], Q, count);
|
||||||
last_phase = sdr->chan[c].rx_last_phase;
|
last_phase = sdr->chan[c].rx_last_phase;
|
||||||
for (s = 0; s < count; s++) {
|
for (s = 0; s < count; s++) {
|
||||||
phase = atan2(Q[s], I[s]);
|
phase = atan2(Q[s], I[s]);
|
||||||
|
|
|
@ -2,11 +2,21 @@ AUTOMAKE_OPTIONS = subdir-objects
|
||||||
AM_CPPFLAGS = -Wall -g $(all_includes)
|
AM_CPPFLAGS = -Wall -g $(all_includes)
|
||||||
|
|
||||||
noinst_PROGRAMS = \
|
noinst_PROGRAMS = \
|
||||||
|
test_filter \
|
||||||
test_compandor \
|
test_compandor \
|
||||||
test_emphasis \
|
test_emphasis \
|
||||||
test_dms \
|
test_dms \
|
||||||
test_sms
|
test_sms
|
||||||
|
|
||||||
|
test_filter_SOURCES = test_filter.c dummy.c
|
||||||
|
|
||||||
|
test_filter_LDADD = \
|
||||||
|
$(COMMON_LA) \
|
||||||
|
$(top_builddir)/src/common/libcommon.a \
|
||||||
|
$(ALSA_LIBS) \
|
||||||
|
$(UHD_LIBS) \
|
||||||
|
-lm
|
||||||
|
|
||||||
test_compandor_SOURCES = test_compandor.c
|
test_compandor_SOURCES = test_compandor.c
|
||||||
|
|
||||||
test_compandor_LDADD = \
|
test_compandor_LDADD = \
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "../common/filter.h"
|
||||||
|
#include "../common/debug.h"
|
||||||
|
|
||||||
|
#define level2db(level) (20 * log10(level))
|
||||||
|
#define db2level(db) pow(10, (double)db / 20.0)
|
||||||
|
|
||||||
|
#define SAMPLERATE 48000
|
||||||
|
|
||||||
|
static double get_level(double *samples)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
int i;
|
||||||
|
double last = 0, envelope = 0;
|
||||||
|
int up = 0;
|
||||||
|
|
||||||
|
for (i = SAMPLERATE/2; i < SAMPLERATE; i++) {
|
||||||
|
if (last < samples[i]) {
|
||||||
|
up = 1;
|
||||||
|
} else if (last > samples[i]) {
|
||||||
|
if (up) {
|
||||||
|
if (last > envelope)
|
||||||
|
envelope = last;
|
||||||
|
}
|
||||||
|
up = 0;
|
||||||
|
}
|
||||||
|
last = samples[i];
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int i;
|
||||||
|
double envelope = 0;
|
||||||
|
for (i = SAMPLERATE/2; i < SAMPLERATE; i++) {
|
||||||
|
if (samples[i] > envelope)
|
||||||
|
envelope = samples[i];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return envelope;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_samples(double *samples, double freq)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
double value;
|
||||||
|
|
||||||
|
for (i = 0; i < SAMPLERATE; i++) {
|
||||||
|
value = cos(2.0 * M_PI * freq / (double)SAMPLERATE * (double)i);
|
||||||
|
samples[i] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
filter_t filter_low;
|
||||||
|
filter_t filter_high;
|
||||||
|
double samples[SAMPLERATE];
|
||||||
|
double level;
|
||||||
|
int iter = 2;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
debuglevel = DEBUG_DEBUG;
|
||||||
|
|
||||||
|
printf("testing low-pass filter with %d iterations\n", iter);
|
||||||
|
|
||||||
|
filter_lowpass_init(&filter_low, 1000.0, SAMPLERATE, iter);
|
||||||
|
|
||||||
|
for (i = 0; i < 4001; i += 100) {
|
||||||
|
gen_samples(samples, (double)i);
|
||||||
|
filter_process(&filter_low, samples, SAMPLERATE);
|
||||||
|
level = get_level(samples);
|
||||||
|
printf("%4d Hz: %.1f dB", i, level2db(level));
|
||||||
|
if (i == 1000)
|
||||||
|
printf(" cutoff\n");
|
||||||
|
else if (i == 2000)
|
||||||
|
printf(" double frequency\n");
|
||||||
|
else if (i == 4000)
|
||||||
|
printf(" quad frequency\n");
|
||||||
|
else
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("testing high-pass filter with %d iterations\n", iter);
|
||||||
|
|
||||||
|
filter_highpass_init(&filter_high, 2000.0, SAMPLERATE, iter);
|
||||||
|
|
||||||
|
for (i = 0; i < 4001; i += 100) {
|
||||||
|
gen_samples(samples, (double)i);
|
||||||
|
filter_process(&filter_high, samples, SAMPLERATE);
|
||||||
|
level = get_level(samples);
|
||||||
|
printf("%4d Hz: %.1f dB", i, level2db(level));
|
||||||
|
if (i == 2000)
|
||||||
|
printf(" cutoff\n");
|
||||||
|
else if (i == 1000)
|
||||||
|
printf(" half frequency\n");
|
||||||
|
else if (i == 500)
|
||||||
|
printf(" quarter frequency\n");
|
||||||
|
else
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("testing band-pass filter with %d iterations\n", iter);
|
||||||
|
|
||||||
|
filter_lowpass_init(&filter_low, 2000.0, SAMPLERATE, iter);
|
||||||
|
filter_highpass_init(&filter_high, 1000.0, SAMPLERATE, iter);
|
||||||
|
|
||||||
|
for (i = 0; i < 4001; i += 100) {
|
||||||
|
gen_samples(samples, (double)i);
|
||||||
|
filter_process(&filter_low, samples, SAMPLERATE);
|
||||||
|
filter_process(&filter_high, samples, SAMPLERATE);
|
||||||
|
level = get_level(samples);
|
||||||
|
printf("%4d Hz: %.1f dB", i, level2db(level));
|
||||||
|
if (i == 1000)
|
||||||
|
printf(" cutoff high\n");
|
||||||
|
else if (i == 2000)
|
||||||
|
printf(" cutoff low\n");
|
||||||
|
else
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue