/*	anf.c

This file is part of a program that implements a Software-Defined Radio.

Copyright (C) 2012, 2013 Warren Pratt, NR0V

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

The author can be reached by email at

warren@wpratt.com

*/

#include "comm.h"

ANF create_anf	(
				int run,
				int position,
				int buff_size,
				double *in_buff,
				double *out_buff,
				int dline_size,
				int n_taps,
				int delay,
				double two_mu,
				double gamma,

				double lidx,
				double lidx_min,
				double lidx_max,
				double ngamma,
				double den_mult,
				double lincr,
				double ldecr
			)
{
	ANF a = (ANF) malloc0 (sizeof(anf));
	a->run = run;
	a->position = position;
	a->buff_size = buff_size;
	a->in_buff = in_buff;
	a->out_buff = out_buff;
	a->dline_size = dline_size;
	a->mask = dline_size - 1;
	a->n_taps = n_taps;
	a->delay = delay;
	a->two_mu = two_mu;
	a->gamma = gamma;
	a->in_idx = 0;
	a->lidx = lidx;
	a->lidx_min = lidx_min;
	a->lidx_max = lidx_max;
	a->ngamma = ngamma;
	a->den_mult = den_mult;
	a->lincr = lincr;
	a->ldecr = ldecr;
	
	memset (a->d, 0, sizeof(double) * ANF_DLINE_SIZE);
	memset (a->w, 0, sizeof(double) * ANF_DLINE_SIZE);
	
	return a;
}

void destroy_anf (ANF a)
{
	_aligned_free (a);
}

void xanf(ANF a, int position)
{
	int i, j, idx;
	double c0, c1;
	double y, error, sigma, inv_sigp;
	double nel, nev;
	if (a->run && (a->position == position))
	{
		for (i = 0; i < a->buff_size; i++)
		{
			a->d[a->in_idx] = a->in_buff[2 * i + 0];

			y = 0;
			sigma = 0;

			for (j = 0; j < a->n_taps; j++)
			{
				idx = (a->in_idx + j + a->delay) & a->mask;
				y += a->w[j] * a->d[idx];
				sigma += a->d[idx] * a->d[idx];
			}
			inv_sigp = 1.0 / (sigma + 1e-10);
			error = a->d[a->in_idx] - y;

			a->out_buff[2 * i + 0] = error;
			a->out_buff[2 * i + 1] = 0.0;

			if((nel = error * (1.0 - a->two_mu * sigma * inv_sigp)) < 0.0) nel = -nel;
			if((nev = a->d[a->in_idx] - (1.0 - a->two_mu * a->ngamma) * y - a->two_mu * error * sigma * inv_sigp) < 0.0) nev = -nev;
			if (nev < nel)
			{
				if ((a->lidx += a->lincr) > a->lidx_max) a->lidx = a->lidx_max;
			}
			else
			{
				if ((a->lidx -= a->ldecr) < a->lidx_min) a->lidx = a->lidx_min;
			}
			a->ngamma = a->gamma * (a->lidx * a->lidx) * (a->lidx * a->lidx) * a->den_mult;

			c0 = 1.0 - a->two_mu * a->ngamma;
			c1 = a->two_mu * error * inv_sigp;

			for (j = 0; j < a->n_taps; j++)
			{
				idx = (a->in_idx + j + a->delay) & a->mask;
				a->w[j] = c0 * a->w[j] + c1 * a->d[idx];
			}
			a->in_idx = (a->in_idx + a->mask) & a->mask;
		}
	}
	else if (a->in_buff != a->out_buff)
		memcpy (a->out_buff, a->in_buff, a->buff_size * sizeof (complex));
}

void flush_anf (ANF a)
{
	memset (a->d, 0, sizeof(double) * ANF_DLINE_SIZE);
	memset (a->w, 0, sizeof(double) * ANF_DLINE_SIZE);
	a->in_idx = 0;
}

void setBuffers_anf (ANF a, double* in, double* out)
{
	a->in_buff = in;
	a->out_buff = out;
}

void setSamplerate_anf (ANF a, int rate)
{
	flush_anf (a);
}

void setSize_anf (ANF a, int size)
{
	a->buff_size = size;
	flush_anf (a);
}

/********************************************************************************************************
*																										*
*											RXA Properties												*
*																										*
********************************************************************************************************/

PORT void
SetRXAANFRun (int channel, int run)
{
	ANF a = rxa[channel].anf.p;
	if (a->run != run)
	{
		RXAbp1Check (channel, rxa[channel].amd.p->run, rxa[channel].snba.p->run,
			rxa[channel].emnr.p->run, run, rxa[channel].anr.p->run,
			rxa[channel].rnnr.p->run, rxa[channel].sbnr.p->run);  // NR3 + NR4 support
		EnterCriticalSection (&ch[channel].csDSP);
		a->run = run;
		RXAbp1Set (channel);
		flush_anf (a);
		LeaveCriticalSection (&ch[channel].csDSP);
	}
}


PORT void
SetRXAANFVals (int channel, int taps, int delay, double gain, double leakage)
{
	EnterCriticalSection (&ch[channel].csDSP);
	rxa[channel].anf.p->n_taps = taps;
	rxa[channel].anf.p->delay = delay;
	rxa[channel].anf.p->two_mu = gain;			//try two_mu = 1e-4
	rxa[channel].anf.p->gamma = leakage;		//try gamma = 0.10
	flush_anf (rxa[channel].anf.p);
	LeaveCriticalSection (&ch[channel].csDSP);
}

PORT void
SetRXAANFTaps (int channel, int taps)
{
	EnterCriticalSection (&ch[channel].csDSP);
	rxa[channel].anf.p->n_taps = taps;
	flush_anf (rxa[channel].anf.p);
	LeaveCriticalSection (&ch[channel].csDSP);
}

PORT void
SetRXAANFDelay (int channel, int delay)
{
	EnterCriticalSection (&ch[channel].csDSP);
	rxa[channel].anf.p->delay = delay;
	flush_anf (rxa[channel].anf.p);
	LeaveCriticalSection (&ch[channel].csDSP);
}

PORT void
SetRXAANFGain (int channel, double gain)
{
	EnterCriticalSection (&ch[channel].csDSP);
	rxa[channel].anf.p->two_mu = gain;
	flush_anf (rxa[channel].anf.p);
	LeaveCriticalSection (&ch[channel].csDSP);
}

PORT void
SetRXAANFLeakage (int channel, double leakage)
{
	EnterCriticalSection (&ch[channel].csDSP);
	rxa[channel].anf.p->gamma = leakage;
	flush_anf (rxa[channel].anf.p);
	LeaveCriticalSection (&ch[channel].csDSP);
}

PORT void
SetRXAANFPosition (int channel, int position)
{
	EnterCriticalSection (&ch[channel].csDSP);
	rxa[channel].anf.p->position = position;
	rxa[channel].bp1.p->position = position;
	flush_anf (rxa[channel].anf.p);
	LeaveCriticalSection (&ch[channel].csDSP);
}
