tg/algo.c
2015-10-04 11:56:54 +02:00

467 lines
13 KiB
C

/*
tg
Copyright (C) 2015 Marcello Mamino
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 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 General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "tg.h"
int fl_cmp(const void *a, const void *b)
{
float x = *(float*)a;
float y = *(float*)b;
return x<y ? -1 : x>y ? 1 : 0;
}
int int_cmp(const void *a, const void *b)
{
int x = *(int*)a;
int y = *(int*)b;
return x<y ? -1 : x>y ? 1 : 0;
}
void make_hp(struct filter *f, double freq)
{
double K = tan(M_PI * freq);
double norm = 1 / (1 + K * sqrt(2) + K * K);
f->a0 = 1 * norm;
f->a1 = -2 * f->a0;
f->a2 = f->a0;
f->b1 = 2 * (K * K - 1) * norm;
f->b2 = (1 - K * sqrt(2) + K * K) * norm;
}
void make_lp(struct filter *f, double freq)
{
double K = tan(M_PI * freq);
double norm = 1 / (1 + K * sqrt(2) + K * K);
f->a0 = K * K * norm;
f->a1 = 2 * f->a0;
f->a2 = f->a0;
f->b1 = 2 * (K * K - 1) * norm;
f->b2 = (1 - K * sqrt(2) + K * K) * norm;
}
void run_filter(struct filter *f, float *buff, int size)
{
int i;
double z1 = 0, z2 = 0;
for(i=0; i<size; i++) {
double in = buff[i];
double out = in * f->a0 + z1;
z1 = in * f->a1 + z2 - f->b1 * out;
z2 = in * f->a2 - f->b2 * out;
buff[i] = out;
}
}
void setup_buffers(struct processing_buffers *b)
{
b->samples = fftwf_malloc(2 * b->sample_count * sizeof(float));
b->samples_sc = malloc(2 * b->sample_count * sizeof(float));
b->waveform = malloc(2 * b->sample_rate * sizeof(float));
b->waveform_sc = malloc(2 * b->sample_rate * sizeof(float));
b->fft = fftwf_malloc((b->sample_count + 1) * sizeof(fftwf_complex));
b->sc_fft = fftwf_malloc((b->sample_count + 1) * sizeof(fftwf_complex));
b->tic_wf = fftwf_malloc(2 * b->sample_count * sizeof(float));
b->tic_c = fftwf_malloc(2 * b->sample_count * sizeof(float));
b->tic_fft = fftwf_malloc((b->sample_count + 1) * sizeof(fftwf_complex));
b->plan_a = fftwf_plan_dft_r2c_1d(2 * b->sample_count, b->samples, b->fft, FFTW_ESTIMATE);
b->plan_b = fftwf_plan_dft_c2r_1d(2 * b->sample_count, b->sc_fft, b->samples_sc, FFTW_ESTIMATE);
b->plan_c = fftwf_plan_dft_r2c_1d(2 * b->sample_rate, b->waveform, b->sc_fft, FFTW_ESTIMATE);
b->plan_d = fftwf_plan_dft_c2r_1d(2 * b->sample_rate, b->sc_fft, b->waveform_sc, FFTW_ESTIMATE);
b->plan_e = fftwf_plan_dft_r2c_1d(2 * b->sample_count, b->tic_wf, b->tic_fft, FFTW_ESTIMATE);
b->plan_f = fftwf_plan_dft_c2r_1d(2 * b->sample_count, b->sc_fft, b->tic_c, FFTW_ESTIMATE);
b->hpf = malloc(sizeof(struct filter));
make_hp(b->hpf,(double)FILTER_CUTOFF/b->sample_rate);
b->lpf = malloc(sizeof(struct filter));
make_lp(b->lpf,(double)FILTER_CUTOFF/b->sample_rate);
b->events = malloc(EVENTS_MAX * sizeof(uint64_t));
b->ready = 0;
#ifdef DEBUG
b->debug = fftwf_malloc(b->sample_count * sizeof(float));
#endif
}
struct processing_buffers *pb_clone(struct processing_buffers *p)
{
struct processing_buffers *new = malloc(sizeof(struct processing_buffers));
new->sample_rate = p->sample_rate;
new->waveform = malloc(new->sample_rate * sizeof(float));
memcpy(new->waveform, p->waveform, new->sample_rate * sizeof(float));
#ifdef DEBUG
new->debug = malloc(p->sample_count * sizeof(float));
memcpy(new->debug, p->debug, p->sample_count * sizeof(float));
#endif
new->sample_count = p->sample_count;
new->period = p->period;
new->sigma = p->sigma;
new->be = p->be;
new->waveform_max = p->waveform_max;
new->tic = p->tic;
new->toc = p->toc;
new->ready = p->ready;
return new;
}
void pb_destroy_clone(struct processing_buffers *p)
{
free(p->waveform);
#ifdef DEBUG
free(p->debug);
#endif
free(p);
}
void noise_suppressor(struct processing_buffers *p)
{
float *a = p->samples_sc;
float *b = p->samples_sc + p->sample_count;
int window = p->sample_rate / 50;
int i;
for(i = 0; i < p->sample_count; i++)
a[i] = p->samples[i] * p->samples[i];
double r_av = 0;
for(i = 0; i < window; i++)
r_av += a[i];
for(i = 0;; i++) {
b[i] = r_av;
if(i + window == p->sample_count) break;
r_av += a[i + window] - a[i];
}
int m = p->sample_count - window + 1;
float max = 0;
int j = 0;
for(i = 0; i < m; i++) {
if(b[i] > max) max = b[i];
if((i+1) % (p->sample_rate/2) == 0) {
a[j++] = max;
max = 0;
}
}
qsort(a, j, sizeof(float), fl_cmp);
float k = a[j/2];
for(i = 0; i < p->sample_count; i++) {
int j = i - window / 2;
j = j < 0 ? 0 : j > p->sample_count - window ? p->sample_count - window : j;
if(b[j] > 2*k) p->samples[i] = 0;
}
}
void prepare_data(struct processing_buffers *b)
{
int i;
memset(b->samples + b->sample_count, 0, b->sample_count * sizeof(float));
run_filter(b->hpf, b->samples, b->sample_count);
noise_suppressor(b);
for(i=0; i < b->sample_count; i++)
b->samples[i] = fabs(b->samples[i]);
run_filter(b->lpf, b->samples, b->sample_count);
double average = 0;
for(i=0; i < b->sample_count; i++)
average += b->samples[i];
average /= b->sample_count;
for(i=0; i < b->sample_count; i++)
b->samples[i] -= average;
for(i=0; i < b->sample_rate/10; i++) {
double k = ( 1 - cos(i*M_PI/(b->sample_rate/10)) ) / 2;
b->samples[i] *= k;
b->samples[b->sample_count - i - 1] *= k;
}
fftwf_execute(b->plan_a);
for(i=0; i < b->sample_count+1; i++)
b->sc_fft[i] = b->fft[i] * conj(b->fft[i]);
fftwf_execute(b->plan_b);
#ifdef DEBUG
for(i=0; i < b->sample_count+1; i++)
b->debug[i] = b->samples_sc[i];
#endif
}
int peak_detector(float *buff, int a, int b)
{
int i;
double max = buff[a];
int i_max = a;
for(i=a+1; i<=b; i++) {
if(buff[i] > max) {
max = buff[i];
i_max = i;
}
}
if(max <= 0) return -1;
float v[b-a+1];
for(i=a; i<=b; i++)
v[i-a] = buff[i];
qsort(v, b-a+1, sizeof(float), fl_cmp);
float x = v[(b-a+1)/10];
float y = v[(b-a+1)/2];
debug("max = %f x = %f y = %f\n",max,x,y);
if(max < 4*y - 3*x)
return -1;
return i_max;
}
double estimate_period(struct processing_buffers *p)
{
int estimate = peak_detector(p->samples_sc, p->sample_rate / 12, p->sample_rate );
if(estimate == -1) {
debug("no candidate period\n");
return -1;
}
int a = estimate*3/2 - p->sample_rate / 50;
int b = estimate*3/2 + p->sample_rate / 50;
double max = p->samples_sc[a];
int i;
for(i=a+1; i<=b; i++)
if(p->samples_sc[i] > max)
max = p->samples_sc[i];
if(max < 0.2 * p->samples_sc[estimate]) {
if(estimate * 2 < p->sample_rate ) {
debug("double triggered\n");
return peak_detector(p->samples_sc,
estimate*2 - p->sample_rate / 50,
estimate*2 + p->sample_rate / 50);
} else {
debug("period rejected (immense beat error?)");
return -1;
}
} else return estimate;
}
int compute_period(struct processing_buffers *b, int bph)
{
double estimate;
if(bph)
estimate = peak_detector(b->samples_sc,
7200 * b->sample_rate / bph - b->sample_rate / 50,
7200 * b->sample_rate / bph + b->sample_rate / 50);
else
estimate = estimate_period(b);
if(estimate == -1) {
debug("failed to estimate period\n");
return 1;
}
double delta = b->sample_rate * 0.02;
double new_estimate = estimate;
double sum = 0;
double sq_sum = 0;
int count = 0;
int cycle = 1;
for(;;) {
int inf = floor(new_estimate * cycle - delta);
int sup = ceil(new_estimate * cycle + delta);
if(sup > b->sample_count * 2 / 3)
break;
new_estimate = peak_detector(b->samples_sc,inf,sup);
if(new_estimate == -1) {
debug("cycle = %d peak not found\n",cycle);
return 1;
}
new_estimate /= cycle;
if(new_estimate < estimate - delta || new_estimate > estimate + delta) {
debug("cycle = %d new_estimate = %f invalid peak\n",cycle,new_estimate/b->sample_rate);
return 1;
} else
debug("cycle = %d new_estimate = %f\n",cycle,new_estimate/b->sample_rate);
if(inf > b->sample_count / 3) {
sum += new_estimate;
sq_sum += new_estimate * new_estimate;
count++;
}
cycle++;
}
if(count > 0) estimate = sum / count;
if(estimate >= b->sample_rate / 2) return 1;
b->period = estimate;
if(count > 1)
b->sigma = sqrt((sq_sum - count * estimate * estimate)/ (count-1));
else
b->sigma = b->period;
return 0;
}
float tmean(float *x, int n)
{
qsort(x,n,sizeof(float),fl_cmp);
int i;
double sum = 0;
for(i=0; i < n*4/5; i++)
sum += x[i];
return sum/(n*4/5);
}
int compute_parameters(struct processing_buffers *p)
{
int i;
double x = 0, y = 0;
for(i=0; i<p->sample_count; i++) {
double a = i * 4 * M_PI / p->period;
x += p->samples[i] * cos(a);
y += p->samples[i] * sin(a);
}
double s = p->period * (M_PI + atan2(y,x)) / (4 * M_PI);
for(i=0; i<2*p->sample_rate; i++)
p->waveform[i] = 0;
float bin[(int)ceil(1 + p->sample_count / p->period)];
for(i=0; i < p->period; i++) {
int j;
double k = fmod(i+s,p->period);
for(j=0;;j++) {
int n = round(k+j*p->period);
if(n >= p->sample_count) break;
bin[j] = p->samples[n];
}
p->waveform[i] = tmean(bin,j);
}
for(i=0; i<p->period; i++)
p->waveform_sc[i] = p->waveform[i];
qsort(p->waveform_sc,floor(p->period),sizeof(float),fl_cmp);
double nl = p->waveform_sc[(int)floor(p->period/2)];
for(i=0; i<p->period; i++) {
p->waveform[i] -= nl;
//p->waveform[i] = cbrt(p->waveform[i]);
//if(p->waveform[i] <= 0) p->waveform[i] = 0;
}
fftwf_execute(p->plan_c);
for(i=0; i < p->sample_rate+1; i++)
p->sc_fft[i] = p->sc_fft[i] * conj(p->sc_fft[i]);
fftwf_execute(p->plan_d);
// for(i=0;i<p->period;i++)
// p->samples_sc[i] = p->waveform_sc[i];
int tic_to_toc = peak_detector(p->waveform_sc,
floor(p->period/2)-p->sample_rate/50,
floor(p->period/2)+p->sample_rate/50);
if(tic_to_toc < 0) {
debug("beat error = ---\n");
return 1;
} else {
p->be = p->period/2 - tic_to_toc;
debug("beat error = %.1f\n",fabs(p->be)*1000/p->sample_rate);
}
float max = 0;
int max_i = -1;
for(i=0; i + tic_to_toc < p->period; i++) {
float x = p->waveform[i] + p->waveform[i+tic_to_toc];
if(x > max) {
max = x;
max_i = i;
}
}
if(max_i < 0) return 1;
p->tic = max_i;
p->toc = max_i + tic_to_toc;
p->waveform_max = 0;
for(i=0; i < p->period; i++)
if(p->waveform[i] > p->waveform_max)
p->waveform_max = p->waveform[i];
double phase = p->timestamp - p->last_tic;
double apparent_phase = p->sample_count - (s + p->tic);
double shift = fmod(apparent_phase - phase, p->period);
if(shift < 0) shift += p->period;
debug("shift = %.3f\n",shift / p->period);
if(fabs(shift - p->period/2) < p->period/4) {
p->last_toc = p->timestamp - (uint64_t)round(fmod(apparent_phase, p->period));
int t = p->tic;
p->tic = p->toc;
p->toc = t;
apparent_phase = p->sample_count - (s + p->tic);
} else
p->last_toc = p->timestamp - (uint64_t)round(fmod(p->sample_count - (s + p->toc), p->period));
p->last_tic = p->timestamp - (uint64_t)round(fmod(apparent_phase, p->period));
return 0;
}
void do_locate_events(int *events, struct processing_buffers *p, float *waveform, int last, int offset, int count)
{
int i;
memset(p->tic_wf, 0, 2 * p->sample_count * sizeof(float));
for(i=0; i<floor(p->period)/2; i++)
p->tic_wf[i] = waveform[i];
fftwf_execute(p->plan_e);
for(i=0; i < p->sample_count+1; i++)
p->sc_fft[i] = p->fft[i] * conj(p->tic_fft[i]);
fftwf_execute(p->plan_f);
for(i=0; i<count; i++) {
int a = round(last - offset - i*p->period - 0.02*p->sample_rate);
int b = round(last - offset - i*p->period + 0.02*p->sample_rate);
if(a < 0 || b >= p->sample_count)
events[i] = -1;
else
events[i] = offset + peak_detector(p->tic_c,a,b);
}
}
void locate_events(struct processing_buffers *p)
{
int count = 1 + ceil((p->timestamp - p->events_from) / p->period);
if(count <= 0 || 2*count > EVENTS_MAX) {
p->events[0] = 0;
return;
}
int events[2*count];
int half = p->tic < p->period/2 ? 0 : round(p->period / 2);
int offset = p->tic - half;
do_locate_events(events, p, p->waveform + half, p->last_tic + p->sample_count - p->timestamp, offset, count);
half = p->toc < p->period/2 ? 0 : round(p->period / 2);
offset = p->toc - half;
do_locate_events(events+count, p, p->waveform + half, p->last_toc + p->sample_count - p->timestamp, offset, count);
qsort(events, 2*count, sizeof(int), int_cmp);
int i,j;
for(i=0, j=0; i < 2*count; i++) {
if(events[i] < 0 ||
events[i] + p->timestamp < p->sample_count ||
events[i] + p->timestamp - p->sample_count < p->events_from)
continue;
p->events[j++] = events[i] + p->timestamp - p->sample_count;
}
p->events[j] = 0;
}
void process(struct processing_buffers *p, int bph)
{
prepare_data(p);
p->ready = ! ( compute_period(p,bph) || compute_parameters(p) );
if(p->ready) locate_events(p);
}