wdsp/iobuffs.c
Uladzimir Karpenka 89c8a0e2b5 first commit
2026-06-01 15:58:45 +03:00

605 lines
15 KiB
C

/* iobuffs.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 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"
/********************************************************************************************************
* *
* Begin Slew Code *
* *
********************************************************************************************************/
enum _slew
{
BEGIN = 0,
DELAYUP,
UPSLEW,
ON,
DELAYDOWN,
DOWNSLEW,
ZERO,
OFF
};
void create_slews (IOB a)
{
int i;
double delta, theta;
a->slew.ustate = BEGIN;
a->slew.dstate = BEGIN;
a->slew.ucount = 0;
a->slew.dcount = 0;
a->slew.ndelup = (int)(ch[a->channel].tdelayup * ch[a->channel].in_rate);
a->slew.ndeldown = (int)(ch[a->channel].tdelaydown * ch[a->channel].out_rate);
a->slew.ntup = (int)(ch[a->channel].tslewup * ch[a->channel].in_rate);
a->slew.ntdown = (int)(ch[a->channel].tslewdown * ch[a->channel].out_rate);
a->slew.cup = (double *) malloc0 ((a->slew.ntup + 1) * sizeof (double));
a->slew.cdown = (double *) malloc0 ((a->slew.ntdown + 1) * sizeof (double));
delta = PI / (double)a->slew.ntup;
theta = 0.0;
for (i = 0; i <= a->slew.ntup; i++)
{
a->slew.cup[i] = 0.5 * (1.0 - cos (theta));
theta += delta;
}
delta = PI / (double)a->slew.ntdown;
theta = 0.0;
for (i = 0; i <= a->slew.ntdown; i++)
{
a->slew.cdown[i] = 0.5 * (1 + cos (theta));
theta += delta;
}
InterlockedBitTestAndReset (&a->slew.upflag, 0);
InterlockedBitTestAndReset (&a->slew.downflag, 0);
}
void destroy_slews(IOB a)
{
_aligned_free (a->slew.cdown);
_aligned_free (a->slew.cup);
}
void flush_slews (IOB a)
{
a->slew.ustate = BEGIN;
a->slew.dstate = BEGIN;
a->slew.ucount = 0;
a->slew.dcount = 0;
InterlockedBitTestAndReset (&a->slew.upflag, 0);
InterlockedBitTestAndReset (&a->slew.downflag, 0);
}
void upslew0 (IOB a, double* pin)
{
int i;
double *pout;
double I, Q;
pout = a->r1_baseptr + 2 * a->r1_inidx;
for (i = 0; i < a->in_size; i++)
{
I = pin[2 * i + 0];
Q = pin[2 * i + 1];
switch (a->slew.ustate)
{
case BEGIN:
pout[2 * i + 0] = 0.0;
pout[2 * i + 1] = 0.0;
if ((I != 0.0) || (Q != 0.0))
{
if (a->slew.ndelup > 0)
{
a->slew.ustate = DELAYUP;
a->slew.ucount = a->slew.ndelup;
}
else if (a->slew.ntup > 0)
{
a->slew.ustate = UPSLEW;
a->slew.ucount = a->slew.ntup;
}
else
a->slew.ustate = ON;
}
break;
case DELAYUP:
pout[2 * i + 0] = 0.0;
pout[2 * i + 1] = 0.0;
if (a->slew.ucount-- == 0)
{
if (a->slew.ntup > 0)
{
a->slew.ustate = UPSLEW;
a->slew.ucount = a->slew.ntup;
}
else
a->slew.ustate = ON;
}
break;
case UPSLEW:
pout[2 * i + 0] = I * a->slew.cup[a->slew.ntup - a->slew.ucount];
pout[2 * i + 1] = Q * a->slew.cup[a->slew.ntup - a->slew.ucount];
if (a->slew.ucount-- == 0)
a->slew.ustate = ON;
break;
case ON:
pout[2 * i + 0] = I;
pout[2 * i + 1] = Q;
if (i == a->in_size - 1)
{
a->slew.ustate = BEGIN;
InterlockedBitTestAndReset (&a->slew.upflag, 0);
}
break;
}
}
}
void upslew2 (IOB a, INREAL* pIin, INREAL* pQin)
{
int i;
double *pout;
double I, Q;
pout = a->r1_baseptr + 2 * a->r1_inidx;
for (i = 0; i < a->in_size; i++)
{
I = (double)pIin[i];
Q = (double)pQin[i];
switch (a->slew.ustate)
{
case BEGIN:
pout[2 * i + 0] = 0.0;
pout[2 * i + 1] = 0.0;
if ((I != 0.0) || (Q != 0.0))
{
if (a->slew.ndelup > 0)
{
a->slew.ustate = DELAYUP;
a->slew.ucount = a->slew.ndelup;
}
else if (a->slew.ntup > 0)
{
a->slew.ustate = UPSLEW;
a->slew.ucount = a->slew.ntup;
}
else
a->slew.ustate = ON;
}
break;
case DELAYUP:
pout[2 * i + 0] = 0.0;
pout[2 * i + 1] = 0.0;
if (a->slew.ucount-- == 0)
{
if (a->slew.ntup > 0)
{
a->slew.ustate = UPSLEW;
a->slew.ucount = a->slew.ntup;
}
else
a->slew.ustate = ON;
}
break;
case UPSLEW:
pout[2 * i + 0] = I * a->slew.cup[a->slew.ntup - a->slew.ucount];
pout[2 * i + 1] = Q * a->slew.cup[a->slew.ntup - a->slew.ucount];
if (a->slew.ucount-- == 0)
a->slew.ustate = ON;
break;
case ON:
pout[2 * i + 0] = I;
pout[2 * i + 1] = Q;
if (i == a->in_size - 1)
{
a->slew.ustate = BEGIN;
InterlockedBitTestAndReset (&a->slew.upflag, 0);
}
break;
}
}
}
void downslew0 (IOB a, double* pout)
{
int i;
double *pin;
double I, Q;
pin = a->r2_baseptr + 2 * a->r2_outidx;
for (i = 0; i < a->out_size; i++)
{
I = pin[2 * i + 0];
Q = pin[2 * i + 1];
switch (a->slew.dstate)
{
case BEGIN:
pout[2 * i + 0] = I;
pout[2 * i + 1] = Q;
if (a->slew.ndeldown > 0)
{
a->slew.dstate = DELAYDOWN;
a->slew.dcount = a->slew.ndeldown;
}
else if (a->slew.ntdown > 0)
{
a->slew.dstate = DOWNSLEW;
a->slew.dcount = a->slew.ntdown;
}
else
{
a->slew.dstate = ZERO;
a->slew.dcount = a->out_size;
}
break;
case DELAYDOWN:
pout[2 * i + 0] = I;
pout[2 * i + 1] = Q;
if (a->slew.dcount-- == 0)
{
if (a->slew.ntdown > 0)
{
a->slew.dstate = DOWNSLEW;
a->slew.dcount = a->slew.ntdown;
}
else
{
a->slew.dstate = ZERO;
a->slew.dcount = a->out_size;
}
}
break;
case DOWNSLEW:
pout[2 * i + 0] = I * a->slew.cdown[a->slew.ntdown - a->slew.dcount];
pout[2 * i + 1] = Q * a->slew.cdown[a->slew.ntdown - a->slew.dcount];
if (a->slew.dcount-- == 0)
{
a->slew.dstate = ZERO;
a->slew.dcount = a->out_size;
}
break;
case ZERO:
pout[2 * i + 0] = 0.0;
pout[2 * i + 1] = 0.0;
if (a->slew.dcount-- == 0)
a->slew.dstate = OFF;
break;
case OFF:
pout[2 * i + 0] = 0.0;
pout[2 * i + 1] = 0.0;
if (i == a->out_size - 1)
{
a->slew.dstate = BEGIN;
InterlockedBitTestAndReset (&a->slew.downflag, 0);
}
break;
}
}
}
void downslew2 (IOB a, OUTREAL* pIout, OUTREAL* pQout)
{
int i;
double *pin;
double I, Q;
pin = a->r2_baseptr + 2 * a->r2_outidx;
for (i = 0; i < a->out_size; i++)
{
I = pin[2 * i + 0];
Q = pin[2 * i + 1];
switch (a->slew.dstate)
{
case BEGIN:
pIout[i] = (OUTREAL)I;
pQout[i] = (OUTREAL)Q;
if (a->slew.ndeldown > 0)
{
a->slew.dstate = DELAYDOWN;
a->slew.dcount = a->slew.ndeldown;
}
else if (a->slew.ntdown > 0)
{
a->slew.dstate = DOWNSLEW;
a->slew.dcount = a->slew.ntdown;
}
else
{
a->slew.dstate = ZERO;
a->slew.dcount = a->out_size;
}
break;
case DELAYDOWN:
pIout[i] = (OUTREAL)I;
pQout[i] = (OUTREAL)Q;
if (a->slew.dcount-- == 0)
{
if (a->slew.ntdown > 0)
{
a->slew.dstate = DOWNSLEW;
a->slew.dcount = a->slew.ntdown;
}
else
{
a->slew.dstate = ZERO;
a->slew.dcount = a->out_size;
}
}
break;
case DOWNSLEW:
pIout[i] = (OUTREAL)(I * a->slew.cdown[a->slew.ntdown - a->slew.dcount]);
pQout[i] = (OUTREAL)(Q * a->slew.cdown[a->slew.ntdown - a->slew.dcount]);
if (a->slew.dcount-- == 0)
{
a->slew.dstate = ZERO;
a->slew.dcount = a->out_size;
}
break;
case ZERO:
pIout[i] = 0.0;
pQout[i] = 0.0;
if (a->slew.dcount-- == 0)
a->slew.dstate = OFF;
break;
case OFF:
pIout[i] = 0.0;
pQout[i] = 0.0;
if (i == a->out_size - 1)
{
a->slew.dstate = BEGIN;
InterlockedBitTestAndReset (&a->slew.downflag, 0);
}
break;
}
}
}
/********************************************************************************************************
* *
* Begin Buffer Code *
* *
********************************************************************************************************/
void create_iobuffs (int channel)
{
int n;
IOB a = (IOB) malloc0 (sizeof(iob));
ch[channel].iob.pc = ch[channel].iob.pd = ch[channel].iob.pe = ch[channel].iob.pf = a;
a->channel = channel;
a->in_size = ch[channel].in_size;
a->r1_outsize = ch[channel].dsp_insize;
if (a->r1_outsize > a->in_size)
a->r1_size = a->r1_outsize;
else
a->r1_size = a->in_size;
a->out_size = ch[channel].out_size;
a->r2_insize = ch[channel].dsp_outsize;
if (a->out_size > a->r2_insize)
a->r2_size = a->out_size;
else
a->r2_size = a->r2_insize;
a->r1_active_buffsize = DSP_MULT * a->r1_size;
a->r2_active_buffsize = DSP_MULT * a->r2_size;
a->r1_baseptr = (double*) malloc0 (a->r1_active_buffsize * sizeof (complex));
a->r2_baseptr = (double*) malloc0 (a->r2_active_buffsize * sizeof (complex));
a->r1_inidx = 0;
a->r1_outidx = 0;
a->r1_unqueuedsamps = 0;
a->r2_inidx = (DSP_MULT - 1) * a->r2_size;
a->r2_outidx = 0;
a->r2_havesamps = (DSP_MULT - 1) * a->r2_size;
n = a->r2_havesamps / a->out_size;
a->r2_unqueuedsamps = a->r2_havesamps - n * a->out_size;
InitializeCriticalSectionAndSpinCount(&a->r2_ControlSection, 2500);
a->Sem_BuffReady = CreateSemaphore(0, 0, 1000, 0);
a->Sem_OutReady = CreateSemaphore(0, n, 1000, 0);
a->bfo = ch[channel].bfo;
create_slews (a);
InterlockedBitTestAndReset(&a->flush_bypass, 0);
a->Sem_Flush = CreateSemaphore(0, 0, 1, 0);
_beginthread(flushChannel, 0, (void*)(uintptr_t)a->channel);
}
void destroy_iobuffs (int channel)
{
IOB a = ch[channel].iob.pc;
InterlockedBitTestAndSet(&a->flush_bypass, 0);
ReleaseSemaphore(a->Sem_Flush, 1, 0);
while (InterlockedAnd(&a->flush_bypass, 0xffffffff)) Sleep(1);
CloseHandle(a->Sem_Flush);
destroy_slews (a);
CloseHandle (a->Sem_OutReady);
CloseHandle (a->Sem_BuffReady);
DeleteCriticalSection(&a->r2_ControlSection);
_aligned_free (a->r2_baseptr);
_aligned_free (a->r1_baseptr);
_aligned_free (a);
}
void flush_iobuffs (int channel)
{
int n;
IOB a = ch[channel].iob.pf;
memset (a->r1_baseptr, 0, a->r1_active_buffsize * sizeof (complex));
memset (a->r2_baseptr, 0, a->r2_active_buffsize * sizeof (complex));
a->r1_inidx = 0;
a->r1_outidx = 0;
a->r1_unqueuedsamps = 0;
a->r2_inidx = (DSP_MULT - 1) * a->r2_size;
a->r2_outidx = 0;
a->r2_havesamps = (DSP_MULT - 1) * a->r2_size;
while (!WaitForSingleObject (a->Sem_BuffReady, 1));
n = a->r2_havesamps / a->out_size;
a->r2_unqueuedsamps = a->r2_havesamps - n * a->out_size;
CloseHandle (a->Sem_OutReady);
a->Sem_OutReady = CreateSemaphore(0, n, 1000, 0);
flush_slews (a);
}
PORT //double, interleaved I/Q
void fexchange0 (int channel, double* in, double* out, int* error)
{
int n;
int doit = 0;
IOB a;
*error = 0;
if (_InterlockedAnd (&ch[channel].exchange, 1))
{
EnterCriticalSection (&ch[channel].csEXCH);
a = ch[channel].iob.pe;
if (_InterlockedAnd (&a->slew.upflag, 1))
upslew0 (a, in);
else
memcpy (a->r1_baseptr + 2 * a->r1_inidx, in, a->in_size * sizeof (complex));
// add check with *error += -1; for case when r1 is full and an overwrite occurs
if ((a->r1_unqueuedsamps += a->in_size) >= a->r1_outsize)
{
n = a->r1_unqueuedsamps / a->r1_outsize;
ReleaseSemaphore(a->Sem_BuffReady, n, 0);
a->r1_unqueuedsamps -= n * a->r1_outsize;
}
if ((a->r1_inidx += a->in_size) == a->r1_active_buffsize)
a->r1_inidx = 0;
EnterCriticalSection (&a->r2_ControlSection);
if (a->r2_havesamps >= a->out_size)
doit = 1;
if ((a->r2_havesamps -= a->out_size) < 0) a->r2_havesamps = 0;
LeaveCriticalSection (&a->r2_ControlSection);
if (a->bfo) WaitForSingleObject (a->Sem_OutReady, INFINITE);
if (a->bfo || doit)
if (_InterlockedAnd (&a->slew.downflag, 1))
{
downslew0 (a, out);
if (!_InterlockedAnd (&a->slew.downflag, 1))
{
InterlockedBitTestAndReset (&ch[channel].exchange, 0);
ReleaseSemaphore(a->Sem_Flush, 1, 0);
}
}
else
memcpy (out, a->r2_baseptr + 2 * a->r2_outidx, a->out_size * sizeof (complex));
else
{
memset (out, 0, a->out_size * sizeof (complex));
*error += -2;
}
if ((a->r2_outidx += a->out_size) == a->r2_active_buffsize)
a->r2_outidx = 0;
LeaveCriticalSection (&ch[channel].csEXCH);
}
}
PORT //separate I/Q buffers
void fexchange2 (int channel, INREAL *Iin, INREAL *Qin, OUTREAL *Iout, OUTREAL *Qout, int* error)
{
int i, n;
int doit = 0;
IOB a;
*error = 0;
if (_InterlockedAnd (&ch[channel].exchange, 1))
{
EnterCriticalSection (&ch[channel].csEXCH);
a = ch[channel].iob.pe;
if (_InterlockedAnd (&a->slew.upflag, 1))
upslew2 (a, Iin, Qin);
else
for (i = 0; i < a->in_size; i++)
{
(a->r1_baseptr + 2 * a->r1_inidx)[2 * i + 0] = (double)(Iin[i]);
(a->r1_baseptr + 2 * a->r1_inidx)[2 * i + 1] = (double)(Qin[i]);
}
// add check with *error += -1; for case when r1 is full and an overwrite occurs
if ((a->r1_unqueuedsamps += a->in_size) >= a->r1_outsize)
{
n = a->r1_unqueuedsamps / a->r1_outsize;
ReleaseSemaphore(a->Sem_BuffReady, n, 0);
a->r1_unqueuedsamps -= n * a->r1_outsize;
}
if ((a->r1_inidx += a->in_size) == a->r1_active_buffsize)
a->r1_inidx = 0;
EnterCriticalSection (&a->r2_ControlSection);
if (a->r2_havesamps >= a->out_size)
doit = 1;
if ((a->r2_havesamps -= a->out_size) < 0) a->r2_havesamps = 0;
LeaveCriticalSection (&a->r2_ControlSection);
if (a->bfo) WaitForSingleObject (a->Sem_OutReady, INFINITE);
if (a->bfo || doit)
{
if (_InterlockedAnd (&a->slew.downflag, 1))
{
downslew2 (a, Iout, Qout);
if (!_InterlockedAnd (&a->slew.downflag, 1))
{
InterlockedBitTestAndReset (&ch[channel].exchange, 0);
ReleaseSemaphore(a->Sem_Flush, 1, 0);
}
}
else
for (i = 0; i < a->out_size; i++)
{
Iout[i] = (OUTREAL)((a->r2_baseptr + 2 * a->r2_outidx)[2 * i + 0]);
Qout[i] = (OUTREAL)((a->r2_baseptr + 2 * a->r2_outidx)[2 * i + 1]);
}
}
else
{
memset (Iout, 0, a->out_size * sizeof (OUTREAL));
memset (Qout, 0, a->out_size * sizeof (OUTREAL));
*error += -2;
}
if ((a->r2_outidx += a->out_size) == a->r2_active_buffsize)
a->r2_outidx = 0;
LeaveCriticalSection (&ch[channel].csEXCH);
}
}
void dexchange (int channel, double* in, double* out)
{
int n;
IOB a = ch[channel].iob.pd;
if (!_InterlockedAnd (&ch[channel].run, 1)) _endthread();
EnterCriticalSection (&a->r2_ControlSection);
a->r2_havesamps += a->r2_insize;
LeaveCriticalSection (&a->r2_ControlSection);
memcpy (a->r2_baseptr + 2 * a->r2_inidx, in, a->r2_insize * sizeof (complex));
if ((a->r2_inidx += a->r2_insize) == a->r2_active_buffsize)
a->r2_inidx = 0;
if (a->bfo && (a->r2_unqueuedsamps += a->r2_insize) >= a->out_size)
{
n = a->r2_unqueuedsamps / a->out_size;
ReleaseSemaphore(a->Sem_OutReady, n, 0);
a->r2_unqueuedsamps -= n * a->out_size;
}
memcpy (out, a->r1_baseptr + 2 * a->r1_outidx, a->r1_outsize * sizeof (complex));
if ((a->r1_outidx += a->r1_outsize) == a->r1_active_buffsize)
a->r1_outidx = 0;
}