/*	nob.c

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

Copyright (C) 2013, 2014 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"

#define MAX_TAU			(0.002)		// maximum transition time, signal<->zero
#define MAX_ADVTIME		(0.002)		// maximum deadtime (zero output) in advance of detected noise
#define MAX_SAMPLERATE	(1536000)

void initBlanker(ANB a)
{
	int i;
	a->trans_count = (int)(a->tau * a->samplerate);
	if (a->trans_count < 2) a->trans_count = 2;
	a->hang_count = (int)(a->hangtime * a->samplerate);
	a->adv_count = (int)(a->advtime * a->samplerate);
	a->count = 0;
	a->in_idx = a->trans_count + a->adv_count;
	a->out_idx = 0;
	a->coef = PI / a->trans_count;
	a->state = 0;
	a->avg = 1.0;
	a->power = 1.0;
	a->backmult = exp(-1.0 / (a->samplerate * a->backtau));
	a->ombackmult = 1.0 - a->backmult;
	for (i = 0; i <= a->trans_count; i++)
		a->wave[i] = 0.5 * cos(i * a->coef);
	memset(a->dline, 0, a->dline_size * sizeof(complex));
}

PORT
ANB create_anb	(
	int run,
	int buffsize,
	double* in,
	double* out,
	double samplerate,
	double tau,
	double hangtime,
	double advtime,
	double backtau,
	double threshold
				)
{
	ANB a;
	a = (ANB) malloc0 (sizeof(anb));
	a->run = run;
	a->buffsize = buffsize;
	a->in = in;
	a->out = out;
	a->samplerate = samplerate;
	a->tau = tau;
	a->hangtime = hangtime;
	a->advtime = advtime;
	a->backtau = backtau;
	a->threshold = threshold;
	a->wave = (double *) malloc0 (((int)(MAX_SAMPLERATE * MAX_TAU) + 1) * sizeof(double));
	a->dline_size = (int)((MAX_TAU + MAX_ADVTIME) * MAX_SAMPLERATE) + 1;
	a->dline = (double *) malloc0 (a->dline_size * sizeof(complex));
	InitializeCriticalSectionAndSpinCount (&a->cs_update, 2500);
	initBlanker(a);
	a->legacy = (double *) malloc0 (2048 * sizeof (complex));														/////////////// legacy interface - remove
	return a;
}

PORT
void destroy_anb (ANB a)
{
	DeleteCriticalSection (&a->cs_update);
	_aligned_free (a->legacy);																						/////////////// legacy interface - remove
	_aligned_free (a->dline);
	_aligned_free (a->wave);
	_aligned_free (a);
}

PORT
void flush_anb (ANB a)
{
	EnterCriticalSection (&a->cs_update);
	initBlanker (a);
	LeaveCriticalSection (&a->cs_update);
}

PORT
void xanb (ANB a)
{
	double scale;
	double mag;
	int i;
	if (a->run)
	{
		EnterCriticalSection (&a->cs_update);
		for (i = 0; i < a->buffsize; i++)
		{
			mag = sqrt(a->in[2 * i + 0] * a->in[2 * i + 0] + a->in[2 * i + 1] * a->in[2 * i + 1]);
			a->avg = a->backmult * a->avg + a->ombackmult * mag;
			a->dline[2 * a->in_idx + 0] = a->in[2 * i + 0];
			a->dline[2 * a->in_idx + 1] = a->in[2 * i + 1];
			if (mag > (a->avg * a->threshold))
				a->count = a->trans_count + a->adv_count;

			switch (a->state)
			{
				case 0:
					a->out[2 * i + 0] = a->dline[2 * a->out_idx + 0];
					a->out[2 * i + 1] = a->dline[2 * a->out_idx + 1];
					if (a->count > 0)
					{
						a->state = 1;
						a->dtime = 0;
						a->power = 1.0;
					}
					break;
				case 1:
					scale = a->power * (0.5 + a->wave[a->dtime]);
					a->out[2 * i + 0] = a->dline[2 * a->out_idx + 0] * scale;
					a->out[2 * i + 1] = a->dline[2 * a->out_idx + 1] * scale;
					if (++a->dtime > a->trans_count)
					{
						a->state = 2;
						a->atime = 0;
					}
					break;
				case 2:
					a->out[2 * i + 0] = 0.0;
					a->out[2 * i + 1] = 0.0;
					if (++a->atime > a->adv_count)
						a->state = 3;
					break;
				case 3:
					if (a->count > 0)
						a->htime = -a->count;
								
					a->out[2 * i + 0] = 0.0;
					a->out[2 * i + 1] = 0.0;
					if (++a->htime > a->hang_count)
					{
						a->state = 4;
						a->itime = 0;
					}
					break;
				case 4:
					scale = 0.5 - a->wave[a->itime];
					a->out[2 * i + 0] = a->dline[2 * a->out_idx + 0] * scale;
					a->out[2 * i + 1] = a->dline[2 * a->out_idx + 1] * scale;
					if (a->count > 0)
					{
						a->state = 1;
						a->dtime = 0;
						a->power = scale;
					}
					else if (++a->itime > a->trans_count)
						a->state = 0;
					break;
			}
			if (a->count > 0) a->count--;
			if (++a->in_idx == a->dline_size) a->in_idx = 0;
			if (++a->out_idx == a->dline_size) a->out_idx = 0;
		}
		LeaveCriticalSection (&a->cs_update);
	}
	else if (a->in != a->out)
		memcpy (a->out, a->in, a->buffsize * sizeof (complex));
}

void setBuffers_anb (ANB a, double* in, double* out)
{
	a->in = in;
	a->out = out;
}

void setSamplerate_anb (ANB a, int rate)
{
	a->samplerate = rate;
	initBlanker (a);
}

void setSize_anb (ANB a, int size)
{
	a->buffsize = size;
	initBlanker (a);
}


/********************************************************************************************************
*																										*
*											  RXA PROPERTIES											*
*																										*
********************************************************************************************************/

// The following is an example to follow for properties in the event that this function is used inside
// a channel.

// PORT
// void SetRXAANBRun (int channel, int run)
// {
//	 ANB a = rxa[channel].anb.p;
//	 EnterCriticalSection (&a->cs_update);
//	 a->run = run;
//	 LeaveCriticalSection (&a->cs_update);
// }

/********************************************************************************************************
*																										*
*										POINTER-BASED PROPERTIES										*
*																										*
********************************************************************************************************/

PORT
void pSetRCVRANBRun (ANB a, int run)
{
	EnterCriticalSection (&a->cs_update);
	a->run = run;
	LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetRCVRANBBuffsize (ANB a, int size)
{
	EnterCriticalSection (&a->cs_update);
	a->buffsize = size;
	LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetRCVRANBSamplerate (ANB a, int rate)
{
	EnterCriticalSection (&a->cs_update);
	a->samplerate = (double) rate;
	initBlanker (a);
	LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetRCVRANBTau (ANB a, double tau)
{
	EnterCriticalSection (&a->cs_update);
	a->tau = tau;
	initBlanker (a);
	LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetRCVRANBHangtime (ANB a, double time)
{
	EnterCriticalSection (&a->cs_update);
	a->hangtime = time;
	initBlanker (a);
	LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetRCVRANBAdvtime (ANB a, double time)
{
	EnterCriticalSection (&a->cs_update);
	a->advtime = time;
	initBlanker (a);
	LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetRCVRANBBacktau (ANB a, double tau)
{
	EnterCriticalSection (&a->cs_update);
	a->backtau = tau;
	initBlanker (a);
	LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetRCVRANBThreshold (ANB a, double thresh)
{
	EnterCriticalSection (&a->cs_update);
	a->threshold = thresh;
	LeaveCriticalSection (&a->cs_update);
}

/********************************************************************************************************
*																										*
*										CALLS FOR EXTERNAL USE											*
*																										*
********************************************************************************************************/

#define MAX_EXT_ANBS	(32)						// maximum number of NOBs called from outside wdsp
__declspec (align (16)) ANB panb[MAX_EXT_ANBS];		// array of pointers for NOBs used EXTERNAL to wdsp

PORT
void create_anbEXT	(
	int id,
	int run,
	int buffsize,
	double samplerate,
	double tau,
	double hangtime,
	double advtime,
	double backtau,
	double threshold
					)
{
	panb[id] = create_anb (run, buffsize, 0, 0,samplerate, tau, hangtime, advtime, backtau, threshold);
}

PORT
void destroy_anbEXT (int id)
{
	destroy_anb (panb[id]);
}

PORT
void flush_anbEXT (int id)
{
	flush_anb (panb[id]);
}

PORT
void xanbEXT (int id, double* in, double* out)
{
	ANB a = panb[id];
	a->in = in;
	a->out = out;
	xanb (a);
}

PORT
void SetEXTANBRun (int id, int run)
{
	ANB a = panb[id];
	EnterCriticalSection (&a->cs_update);
	a->run = run;
	LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEXTANBBuffsize (int id, int size)
{
	ANB a = panb[id];
	EnterCriticalSection (&a->cs_update);
	a->buffsize = size;
	LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEXTANBSamplerate (int id, int rate)
{
	ANB a = panb[id];
	EnterCriticalSection (&a->cs_update);
	a->samplerate = (double) rate;
	initBlanker (a);
	LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEXTANBTau (int id, double tau)
{
	ANB a = panb[id];
	EnterCriticalSection (&a->cs_update);
	a->tau = tau;
	initBlanker (a);
	LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEXTANBHangtime (int id, double time)
{
	ANB a = panb[id];
	EnterCriticalSection (&a->cs_update);
	a->hangtime = time;
	initBlanker (a);
	LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEXTANBAdvtime (int id, double time)
{
	ANB a = panb[id];
	EnterCriticalSection (&a->cs_update);
	a->advtime = time;
	initBlanker (a);
	LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEXTANBBacktau (int id, double tau)
{
	ANB a = panb[id];
	EnterCriticalSection (&a->cs_update);
	a->backtau = tau;
	initBlanker (a);
	LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEXTANBThreshold (int id, double thresh)
{
	ANB a = panb[id];
	EnterCriticalSection (&a->cs_update);
	a->threshold = thresh;
	LeaveCriticalSection (&a->cs_update);
}

/********************************************************************************************************
*																										*
*											LEGACY INTERFACE											*
*																										*
********************************************************************************************************/

PORT
void xanbEXTF (int id, float *I, float *Q)
{
	int i;
	ANB a = panb[id];
	a->in = a->legacy;
	a->out = a->legacy;
	for (i = 0; i < a->buffsize; i++)
	{
		a->legacy[2 * i + 0] = (double)I[i];
		a->legacy[2 * i + 1] = (double)Q[i];
	}
	xanb (a);
	for (i = 0; i < a->buffsize; i++)
	{
		I[i] = (float)a->legacy[2 * i + 0];
		Q[i] = (float)a->legacy[2 * i + 1];
	}
}
