/* siphon.c This file is part of a program that implements a Software-Defined Radio. Copyright (C) 2013, 2024 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@pratt.one */ #include "comm.h" void build_window (SIPHON a) { int i; double arg0, cosphi; double sum, scale; arg0 = 2.0 * PI / ((double)a->fftsize - 1.0); sum = 0.0; for (i = 0; i < a->fftsize; i++) { cosphi = cos (arg0 * (double)i); a->window[i] = + 6.3964424114390378e-02 + cosphi * ( - 2.3993864599352804e-01 + cosphi * ( + 3.5015956323820469e-01 + cosphi * ( - 2.4774111897080783e-01 + cosphi * ( + 8.5438256055858031e-02 + cosphi * ( - 1.2320203369293225e-02 + cosphi * ( + 4.3778825791773474e-04 )))))); sum += a->window[i]; } scale = 1.0 / sum; for (i = 0; i < a->fftsize; i++) a->window[i] *= scale; } SIPHON create_siphon (int run, int position, int mode, int disp, int insize, double* in, int sipsize, int fftsize, int specmode) { SIPHON a = (SIPHON) malloc0 (sizeof (siphon)); a->run = run; a->position = position; a->mode = mode; a->disp = disp; a->insize = insize; a->in = in; a->sipsize = sipsize; // NOTE: sipsize MUST BE A POWER OF TWO!! a->fftsize = fftsize; a->specmode = specmode; a->sipbuff = (double *) malloc0 (a->sipsize * sizeof (complex)); a->idx = 0; a->sipout = (double *) malloc0 (a->sipsize * sizeof (complex)); a->specout = (double *) malloc0 (a->fftsize * sizeof (complex)); a->sipplan = fftw_plan_dft_1d (a->fftsize, (fftw_complex *)a->sipout, (fftw_complex *)a->specout, FFTW_FORWARD, FFTW_PATIENT); a->window = (double *) malloc0 (a->fftsize * sizeof (complex)); InitializeCriticalSectionAndSpinCount(&a->update, 2500); build_window (a); a->n_alloc_disps = 0; a->alloc_run = (int*) malloc0 (dMAX_DISPLAYS * sizeof(int)); a->alloc_disp = (int*) malloc0 (dMAX_DISPLAYS * sizeof(int)); return a; } void destroy_siphon (SIPHON a) { _aligned_free (a->alloc_disp); _aligned_free (a->alloc_run); DeleteCriticalSection (&a->update); fftw_destroy_plan (a->sipplan); _aligned_free (a->window); _aligned_free (a->specout); _aligned_free (a->sipout); _aligned_free (a->sipbuff); _aligned_free (a); } void flush_siphon (SIPHON a) { memset (a->sipbuff, 0, a->sipsize * sizeof (complex)); memset (a->sipout , 0, a->sipsize * sizeof (complex)); memset (a->specout, 0, a->fftsize * sizeof (complex)); a->idx = 0; } void xsiphon (SIPHON a, int pos) { int first, second, i; EnterCriticalSection(&a->update); if (a->run && a->position == pos) { switch (a->mode) { case 0: if (a->insize >= a->sipsize) memcpy (a->sipbuff, &(a->in[2 * (a->insize - a->sipsize)]), a->sipsize * sizeof (complex)); else { if (a->insize > (a->sipsize - a->idx)) { first = a->sipsize - a->idx; second = a->insize - first; } else { first = a->insize; second = 0; } memcpy (a->sipbuff + 2 * a->idx, a->in, first * sizeof (complex)); memcpy (a->sipbuff, a->in + 2 * first, second * sizeof (complex)); if ((a->idx += a->insize) >= a->sipsize) a->idx -= a->sipsize; } break; case 1: Spectrum0 (1, a->disp, 0, 0, a->in); for (i = 0; i < a->n_alloc_disps; i++) Spectrum0 (a->alloc_run[i], a->alloc_disp[i], 0, 0, a->in); break; } } LeaveCriticalSection(&a->update); } void setBuffers_siphon (SIPHON a, double* in) { a->in = in; } void setSamplerate_siphon (SIPHON a, int rate) { flush_siphon (a); } void setSize_siphon (SIPHON a, int size) { a->insize = size; flush_siphon (a); } void suck (SIPHON a) { if (a->outsize <= a->sipsize) { int mask = a->sipsize - 1; int j = (a->idx - a->outsize) & mask; int size = a->sipsize - j; if (size >= a->outsize) memcpy (a->sipout, &(a->sipbuff[2 * j]), a->outsize * sizeof (complex)); else { memcpy (a->sipout, &(a->sipbuff[2 * j]), size * sizeof (complex)); memcpy (&(a->sipout[2 * size]), a->sipbuff, (a->outsize - size) * sizeof (complex)); } } } void sip_spectrum (SIPHON a) { int i; for (i = 0; i < a->fftsize; i++) { a->sipout[2 * i + 0] *= a->window[i]; a->sipout[2 * i + 1] *= a->window[i]; } fftw_execute (a->sipplan); } /******************************************************************************************************** * * * RXA Properties * * * ********************************************************************************************************/ PORT void RXAGetaSipF (int channel, float* out, int size) { // return raw samples as floats SIPHON a= rxa[channel].sip1.p; int i; EnterCriticalSection (&a->update); a->outsize = size; suck (a); LeaveCriticalSection (&a->update); for (i = 0; i < size; i++) { out[i] = (float)a->sipout[2 * i + 0]; } } PORT void RXAGetaSipF1 (int channel, float* out, int size) { // return raw samples as floats SIPHON a= rxa[channel].sip1.p; int i; EnterCriticalSection (&a->update); a->outsize = size; suck (a); LeaveCriticalSection (&a->update); for (i = 0; i < size; i++) { out[2 * i + 0] = (float)a->sipout[2 * i + 0]; out[2 * i + 1] = (float)a->sipout[2 * i + 1]; } } /******************************************************************************************************** * * * TXA Properties * * * ********************************************************************************************************/ PORT void TXASetSipPosition (int channel, int pos) { SIPHON a = txa[channel].sip1.p; EnterCriticalSection (&a->update); a->position = pos; LeaveCriticalSection (&a->update); } PORT void TXASetSipMode (int channel, int mode) { SIPHON a = txa[channel].sip1.p; EnterCriticalSection (&a->update); a->mode = mode; LeaveCriticalSection (&a->update); } PORT void TXASetSipDisplay (int channel, int disp) { SIPHON a = txa[channel].sip1.p; EnterCriticalSection (&a->update); a->disp = disp; LeaveCriticalSection (&a->update); } PORT void TXAGetaSipF (int channel, float* out, int size) { // return raw samples as floats SIPHON a = txa[channel].sip1.p; int i; EnterCriticalSection (&a->update); a->outsize = size; suck (a); LeaveCriticalSection (&a->update); for (i = 0; i < size; i++) { out[i] = (float)a->sipout[2 * i + 0]; } } PORT void TXAGetaSipF1 (int channel, float* out, int size) { // return raw samples as floats SIPHON a = txa[channel].sip1.p; int i; EnterCriticalSection (&a->update); a->outsize = size; suck (a); LeaveCriticalSection (&a->update); for (i = 0; i < size; i++) { out[2 * i + 0] = (float)a->sipout[2 * i + 0]; out[2 * i + 1] = (float)a->sipout[2 * i + 1]; } } PORT void TXASetSipSpecmode (int channel, int mode) { SIPHON a = txa[channel].sip1.p; if (mode == 0) InterlockedBitTestAndReset (&a->specmode, 0); else InterlockedBitTestAndSet (&a->specmode, 0); } PORT void TXAGetSpecF1 (int channel, float* out) { // return spectrum magnitudes in dB SIPHON a = txa[channel].sip1.p; int i, j, mid, m, n; EnterCriticalSection (&a->update); a->outsize = a->fftsize; suck (a); LeaveCriticalSection (&a->update); sip_spectrum (a); mid = a->fftsize / 2; if (!InterlockedAnd (&a->specmode, 1)) // swap the halves of the spectrum for (i = 0, j = mid; i < mid; i++, j++) { out[i] = (float)(10.0 * mlog10 (a->specout[2 * j + 0] * a->specout[2 * j + 0] + a->specout[2 * j + 1] * a->specout[2 * j + 1] + 1.0e-60)); out[j] = (float)(10.0 * mlog10 (a->specout[2 * i + 0] * a->specout[2 * i + 0] + a->specout[2 * i + 1] * a->specout[2 * i + 1] + 1.0e-60)); } else // mirror each half of the spectrum in-place for (i = 0, j = mid - 1, m = mid, n = a->fftsize - 1; i < mid; i++, j--, m++, n--) { out[i] = (float)(10.0 * mlog10 (a->specout[2 * j + 0] * a->specout[2 * j + 0] + a->specout[2 * j + 1] * a->specout[2 * j + 1] + 1.0e-60)); out[m] = (float)(10.0 * mlog10 (a->specout[2 * n + 0] * a->specout[2 * n + 0] + a->specout[2 * n + 1] * a->specout[2 * n + 1] + 1.0e-60)); } } PORT void TXASetSipAllocDisps (int channel, int n_alloc_disps, int* alloc_run, int* alloc_disp) { SIPHON a = txa[channel].sip1.p; int i; EnterCriticalSection(&a->update); a->n_alloc_disps = n_alloc_disps; for (i = 0; i < a->n_alloc_disps; i++) { a->alloc_run[i] = alloc_run[i]; a->alloc_disp[i] = alloc_disp[i]; } LeaveCriticalSection(&a->update); } /******************************************************************************************************** * * * CALLS FOR EXTERNAL USE * * * ********************************************************************************************************/ #define MAX_EXT_SIPHONS (2) // maximum number of Siphons called from outside wdsp __declspec (align (16)) SIPHON psiphon[MAX_EXT_SIPHONS]; // array of pointers for Siphons used EXTERNAL to wdsp PORT void create_siphonEXT (int id, int run, int insize, int sipsize, int fftsize, int specmode) { psiphon[id] = create_siphon (run, 0, 0, 0, insize, 0, sipsize, fftsize, specmode); } PORT void destroy_siphonEXT (int id) { destroy_siphon (psiphon[id]); } PORT void flush_siphonEXT (int id) { flush_siphon (psiphon[id]); } PORT void xsiphonEXT (int id, double* buff) { SIPHON a = psiphon[id]; a->in = buff; xsiphon (a, 0); } PORT void GetaSipF1EXT (int id, float* out, int size) { // return raw samples as floats SIPHON a = psiphon[id]; int i; EnterCriticalSection (&a->update); a->outsize = size; suck (a); LeaveCriticalSection (&a->update); for (i = 0; i < size; i++) { out[2 * i + 0] = (float)a->sipout[2 * i + 0]; out[2 * i + 1] = (float)a->sipout[2 * i + 1]; } } PORT void SetSiphonInsize (int id, int size) { SIPHON a = psiphon[id]; EnterCriticalSection (&a->update); a->insize = size; LeaveCriticalSection (&a->update); }