349 lines
9.5 KiB
C
349 lines
9.5 KiB
C
/* channel.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"
|
|
|
|
struct _ch ch[MAX_CHANNELS];
|
|
|
|
void start_thread (int channel)
|
|
{
|
|
HANDLE handle = (HANDLE) _beginthread(wdspmain, 0, (void *)(uintptr_t)channel);
|
|
//SetThreadPriority(handle, THREAD_PRIORITY_HIGHEST);
|
|
}
|
|
|
|
void pre_main_build (int channel)
|
|
{
|
|
if (ch[channel].in_rate >= ch[channel].dsp_rate)
|
|
ch[channel].dsp_insize = ch[channel].dsp_size * (ch[channel].in_rate / ch[channel].dsp_rate);
|
|
else
|
|
ch[channel].dsp_insize = ch[channel].dsp_size / (ch[channel].dsp_rate / ch[channel].in_rate);
|
|
|
|
if (ch[channel].out_rate >= ch[channel].dsp_rate)
|
|
ch[channel].dsp_outsize = ch[channel].dsp_size * (ch[channel].out_rate / ch[channel].dsp_rate);
|
|
else
|
|
ch[channel].dsp_outsize = ch[channel].dsp_size / (ch[channel].dsp_rate / ch[channel].out_rate);
|
|
|
|
if (ch[channel].in_rate >= ch[channel].out_rate)
|
|
ch[channel].out_size = ch[channel].in_size / (ch[channel].in_rate / ch[channel].out_rate);
|
|
else
|
|
ch[channel].out_size = ch[channel].in_size * (ch[channel].out_rate / ch[channel].in_rate);
|
|
|
|
InitializeCriticalSectionAndSpinCount ( &ch[channel].csDSP, 2500 );
|
|
InitializeCriticalSectionAndSpinCount ( &ch[channel].csEXCH, 2500 );
|
|
InterlockedBitTestAndReset (&ch[channel].flushflag, 0);
|
|
create_iobuffs (channel);
|
|
}
|
|
|
|
void post_main_build (int channel)
|
|
{
|
|
InterlockedBitTestAndSet (&ch[channel].run, 0);
|
|
start_thread (channel);
|
|
if (ch[channel].state == 1)
|
|
InterlockedBitTestAndSet (&ch[channel].exchange, 0);
|
|
}
|
|
|
|
void build_channel (int channel)
|
|
{
|
|
pre_main_build (channel);
|
|
create_main (channel);
|
|
post_main_build (channel);
|
|
}
|
|
|
|
PORT
|
|
void OpenChannel (int channel, int in_size, int dsp_size, int input_samplerate, int dsp_rate, int output_samplerate,
|
|
int type, int state, double tdelayup, double tslewup, double tdelaydown, double tslewdown, int bfo)
|
|
{
|
|
WDSP_FPE_GUARD;
|
|
ch[channel].in_size = in_size;
|
|
ch[channel].dsp_size = dsp_size;
|
|
ch[channel].in_rate = input_samplerate;
|
|
ch[channel].dsp_rate = dsp_rate;
|
|
ch[channel].out_rate = output_samplerate;
|
|
ch[channel].type = type;
|
|
ch[channel].state = state;
|
|
ch[channel].tdelayup = tdelayup;
|
|
ch[channel].tslewup = tslewup;
|
|
ch[channel].tdelaydown = tdelaydown;
|
|
ch[channel].tslewdown = tslewdown;
|
|
ch[channel].bfo = bfo;
|
|
InterlockedBitTestAndReset (&ch[channel].exchange, 0);
|
|
build_channel (channel);
|
|
if (ch[channel].state)
|
|
{
|
|
InterlockedBitTestAndSet (&ch[channel].iob.pc->slew.upflag, 0);
|
|
InterlockedBitTestAndSet (&ch[channel].iob.ch_upslew, 0);
|
|
InterlockedBitTestAndReset (&ch[channel].iob.pc->exec_bypass, 0);
|
|
InterlockedBitTestAndSet (&ch[channel].exchange, 0);
|
|
}
|
|
#if !defined(linux) && !defined(__APPLE__)
|
|
_MM_SET_FLUSH_ZERO_MODE (_MM_FLUSH_ZERO_ON);
|
|
#endif
|
|
}
|
|
|
|
void pre_main_destroy (int channel)
|
|
{
|
|
IOB a = ch[channel].iob.pc;
|
|
InterlockedBitTestAndReset (&ch[channel].exchange, 0);
|
|
InterlockedBitTestAndReset (&ch[channel].run, 0);
|
|
InterlockedBitTestAndSet (&ch[channel].iob.pc->exec_bypass, 0);
|
|
ReleaseSemaphore (a->Sem_BuffReady, 1, 0);
|
|
Sleep (25);
|
|
}
|
|
|
|
void post_main_destroy (int channel)
|
|
{
|
|
destroy_iobuffs (channel);
|
|
DeleteCriticalSection ( &ch[channel].csEXCH );
|
|
DeleteCriticalSection ( &ch[channel].csDSP );
|
|
}
|
|
|
|
PORT
|
|
void CloseChannel (int channel)
|
|
{
|
|
pre_main_destroy (channel);
|
|
destroy_main (channel);
|
|
post_main_destroy (channel);
|
|
}
|
|
|
|
void flushChannel (void* p)
|
|
{
|
|
int channel = (int)(uintptr_t)p;
|
|
IOB a = ch[channel].iob.pc;
|
|
while (!InterlockedAnd(&a->flush_bypass, 0xffffffff))
|
|
{
|
|
WaitForSingleObject(a->Sem_Flush, INFINITE);
|
|
if (!InterlockedAnd(&a->flush_bypass, 0xffffffff))
|
|
{
|
|
EnterCriticalSection(&ch[channel].csDSP);
|
|
EnterCriticalSection(&ch[channel].csEXCH);
|
|
flush_iobuffs(channel);
|
|
InterlockedBitTestAndSet(&a->exec_bypass, 0);
|
|
flush_main(channel);
|
|
LeaveCriticalSection(&ch[channel].csEXCH);
|
|
LeaveCriticalSection(&ch[channel].csDSP);
|
|
InterlockedBitTestAndReset(&ch[channel].flushflag, 0);
|
|
}
|
|
}
|
|
InterlockedBitTestAndReset(&a->flush_bypass, 0);
|
|
}
|
|
|
|
/********************************************************************************************************
|
|
* *
|
|
* Channel Properties *
|
|
* *
|
|
********************************************************************************************************/
|
|
|
|
PORT
|
|
void SetType (int channel, int type)
|
|
{ // no need to rebuild buffers; but we did anyway
|
|
if (type != ch[channel].type)
|
|
{
|
|
CloseChannel (channel);
|
|
ch[channel].type = type;
|
|
build_channel (channel);
|
|
}
|
|
}
|
|
|
|
PORT
|
|
void SetInputBuffsize (int channel, int in_size)
|
|
{ // we do not rebuild main here since it didn't change
|
|
if (in_size != ch[channel].in_size)
|
|
{
|
|
pre_main_destroy (channel);
|
|
post_main_destroy (channel);
|
|
ch[channel].in_size = in_size;
|
|
pre_main_build (channel);
|
|
post_main_build (channel);
|
|
}
|
|
}
|
|
|
|
PORT
|
|
void SetDSPBuffsize (int channel, int dsp_size)
|
|
{
|
|
if (dsp_size != ch[channel].dsp_size)
|
|
{
|
|
int oldstate = SetChannelState (channel, 0, 1);
|
|
pre_main_destroy (channel);
|
|
post_main_destroy (channel);
|
|
ch[channel].dsp_size = dsp_size;
|
|
pre_main_build (channel);
|
|
setDSPBuffsize_main (channel);
|
|
post_main_build (channel);
|
|
SetChannelState (channel, oldstate, 0);
|
|
}
|
|
}
|
|
|
|
PORT
|
|
void SetInputSamplerate (int channel, int in_rate)
|
|
{ // no re-build of main required
|
|
if (in_rate != ch[channel].in_rate)
|
|
{
|
|
pre_main_destroy (channel);
|
|
post_main_destroy (channel);
|
|
ch[channel].in_rate = in_rate;
|
|
pre_main_build (channel);
|
|
setInputSamplerate_main (channel);
|
|
post_main_build (channel);
|
|
}
|
|
}
|
|
|
|
PORT
|
|
void SetDSPSamplerate (int channel, int dsp_rate)
|
|
{
|
|
if (dsp_rate != ch[channel].dsp_rate)
|
|
{
|
|
int oldstate = SetChannelState (channel, 0, 1);
|
|
pre_main_destroy (channel);
|
|
post_main_destroy (channel);
|
|
ch[channel].dsp_rate = dsp_rate;
|
|
pre_main_build (channel);
|
|
setDSPSamplerate_main (channel);
|
|
post_main_build (channel);
|
|
SetChannelState (channel, oldstate, 0);
|
|
}
|
|
}
|
|
|
|
PORT
|
|
void SetOutputSamplerate (int channel, int out_rate)
|
|
{ // no re-build of main required
|
|
if (out_rate != ch[channel].out_rate)
|
|
{
|
|
pre_main_destroy (channel);
|
|
post_main_destroy (channel);
|
|
ch[channel].out_rate = out_rate;
|
|
pre_main_build (channel);
|
|
setOutputSamplerate_main (channel);
|
|
post_main_build (channel);
|
|
}
|
|
}
|
|
|
|
PORT
|
|
void SetAllRates (int channel, int in_rate, int dsp_rate, int out_rate)
|
|
{
|
|
if ((in_rate != ch[channel].in_rate) || (dsp_rate != ch[channel].dsp_rate) || (out_rate != ch[channel].out_rate))
|
|
{
|
|
pre_main_destroy (channel);
|
|
post_main_destroy (channel);
|
|
ch[channel].in_rate = in_rate;
|
|
ch[channel].dsp_rate = dsp_rate;
|
|
ch[channel].out_rate = out_rate;
|
|
pre_main_build (channel);
|
|
setInputSamplerate_main (channel);
|
|
setDSPSamplerate_main (channel);
|
|
setOutputSamplerate_main (channel);
|
|
post_main_build (channel);
|
|
}
|
|
}
|
|
|
|
PORT
|
|
int SetChannelState (int channel, int state, int dmode)
|
|
{
|
|
IOB a = ch[channel].iob.pc;
|
|
int prior_state = ch[channel].state;
|
|
int count = 0;
|
|
const int timeout = 100;
|
|
if (ch[channel].state != state)
|
|
{
|
|
ch[channel].state = state;
|
|
switch (ch[channel].state)
|
|
{
|
|
case 0:
|
|
InterlockedBitTestAndSet (&a->slew.downflag, 0);
|
|
InterlockedBitTestAndSet (&ch[channel].flushflag, 0);
|
|
if (dmode)
|
|
{
|
|
while (_InterlockedAnd (&ch[channel].flushflag, 1) && count < timeout)
|
|
{
|
|
Sleep(1);
|
|
count++;
|
|
}
|
|
}
|
|
if (count >= timeout)
|
|
{
|
|
InterlockedBitTestAndReset (&ch[channel].exchange, 0);
|
|
InterlockedBitTestAndReset (&ch[channel].flushflag, 0);
|
|
InterlockedBitTestAndReset (&a->slew.downflag, 0);
|
|
}
|
|
break;
|
|
case 1:
|
|
InterlockedBitTestAndSet (&a->slew.upflag, 0);
|
|
InterlockedBitTestAndSet (&ch[channel].iob.ch_upslew, 0);
|
|
InterlockedBitTestAndReset (&ch[channel].iob.pc->exec_bypass, 0);
|
|
InterlockedBitTestAndSet (&ch[channel].exchange, 0);
|
|
break;
|
|
}
|
|
}
|
|
return prior_state;
|
|
}
|
|
|
|
PORT
|
|
void SetChannelTDelayUp (int channel, double time)
|
|
{
|
|
IOB a;
|
|
EnterCriticalSection (&ch[channel].csEXCH);
|
|
a = ch[channel].iob.pc;
|
|
ch[channel].tdelayup = time;
|
|
a->slew.ndelup = (int)(ch[a->channel].tdelayup * ch[a->channel].in_rate);
|
|
flush_slews (a);
|
|
LeaveCriticalSection (&ch[channel].csEXCH);
|
|
}
|
|
|
|
PORT
|
|
void SetChannelTSlewUp (int channel, double time)
|
|
{
|
|
IOB a;
|
|
EnterCriticalSection (&ch[channel].csEXCH);
|
|
a = ch[channel].iob.pc;
|
|
ch[channel].tslewup = time;
|
|
destroy_slews (a);
|
|
create_slews (a);
|
|
LeaveCriticalSection (&ch[channel].csEXCH);
|
|
}
|
|
|
|
PORT
|
|
void SetChannelTDelayDown (int channel, double time)
|
|
{
|
|
IOB a;
|
|
EnterCriticalSection (&ch[channel].csEXCH);
|
|
a = ch[channel].iob.pc;
|
|
ch[channel].tdelaydown = time;
|
|
a->slew.ndeldown = (int)(ch[a->channel].tdelaydown * ch[a->channel].out_rate);
|
|
flush_slews (a);
|
|
LeaveCriticalSection (&ch[channel].csEXCH);
|
|
}
|
|
|
|
PORT
|
|
void SetChannelTSlewDown (int channel, double time)
|
|
{
|
|
IOB a;
|
|
EnterCriticalSection (&ch[channel].csEXCH);
|
|
a = ch[channel].iob.pc;
|
|
ch[channel].tslewdown = time;
|
|
destroy_slews (a);
|
|
create_slews (a);
|
|
LeaveCriticalSection (&ch[channel].csEXCH);
|
|
}
|