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

1883 lines
52 KiB
C

/* analyzer.c
This file is part of a program that implements a Spectrum Analyzer
used in conjunction with software-defined-radio hardware.
Copyright (C) 2012, 2013, 2014, 2016, 2023, 2025 Warren Pratt, NR0V
Copyright (C) 2012 David McQuate, WA8YWQ - Kaiser window & Bessel function added.
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"
DP pdisp[dMAX_DISPLAYS];
double bessi0(double x)
{
double ax,ans;
double y;
if ((ax=fabs(x)) < 3.75) {
y = x / 3.75, y = y * y;
ans = 1.0 + y * (3.5156229 + y * (3.0899424 + y* (1.2067492
+ y * (0.2659732 + y * (0.360768e-1 + y * 0.45813e-2)))));
} else {
y = 3.75 / ax;
ans = (exp(ax) / sqrt(ax)) * (0.39894228 + y * (0.1328592e-1
+ y * (0.225319e-2 + y * (-0.157565e-2 + y * (0.916281e-2
+ y * (-0.2057706e-1 + y * (0.2635537e-1 + y * (-0.1647633e-1
+ y * 0.392377e-2))))))));
}
return ans;
}
void new_window(int disp, int type, int size, double PiAlpha)
{
DP a = pdisp[disp];
int i;
double arg0, arg1, cgsum, igsum;
switch (type)
{
case 0: // rectangular window
{
a->inv_coherent_gain = 1.0;
igsum = (double)size;
for (i = 0; i < size; i++)
a->window[i] = a->inv_coherent_gain * 1.0;
break;
}
case 1: // blackman-harris window (4 term)
{
arg0 = 2.0 * PI / ((double)size - 1.0);
cgsum = 0.0;
igsum = 0.0;
for (i = 0; i < size; i++)
{
arg1 = arg0 * (double)i;
a->window[i] = 0.35875 - 0.48829 * cos(arg1) + 0.14128 * cos(2.0 * arg1) - 0.01168 * cos(3.0 * arg1);
cgsum += a->window[i];
igsum += a->window[i] * a->window[i];
}
a->inv_coherent_gain = (double)size / cgsum;
for (i = 0; i < size; i++)
a->window[i] *= a->inv_coherent_gain;
break;
}
case 2: // hann window
{
arg0 = 2.0 * PI / ((double)size - 1.0);
cgsum = 0.0;
igsum = 0.0;
for (i = 0; i < size; i++)
{
a->window[i] = 0.5 * (1.0 - cos((double)i * arg0));
cgsum += a->window[i];
igsum += a->window[i] * a->window[i];
}
a->inv_coherent_gain = (double)size / cgsum;
for (i = 0; i < size; i++)
a->window[i] *= a->inv_coherent_gain;
break;
}
case 3: // flat-top window
{
arg0 = 2.0 * PI / ((double)size - 1.0);
cgsum = 0.0;
igsum = 0.0;
for (i = 0; i < size; i++)
{
arg1 = arg0 * (double)i;
a->window[i] = 0.21557895 - 0.41663158 * cos(arg1) + 0.277263158 * cos(2.0 * arg1) - 0.083578947 * cos(3.0 * arg1) + 0.006947368 * cos (4.0 * arg1);
cgsum += a->window[i];
igsum += a->window[i] * a->window[i];
}
a->inv_coherent_gain = (double)size / cgsum;
for (i = 0; i < size; i++)
a->window[i] *= a->inv_coherent_gain;
break;
}
case 4: // hamming window
{
arg0 = 2.0 * PI / ((double)size - 1.0);
cgsum = 0.0;
igsum = 0.0;
for (i = 0; i < size; i++)
{
a->window[i] = (0.54 - 0.46 * cos((double)i * arg0));
cgsum += a->window[i];
igsum += a->window[i] * a->window[i];
}
a->inv_coherent_gain = (double)size / cgsum;
for (i = 0; i < size; i++)
a->window[i] *= a->inv_coherent_gain;
break;
}
case 5: // Kaiser window
{ arg0 = bessi0(PiAlpha);
arg1 = (double)(size - 1);
cgsum = 0.0;
igsum = 0.0;
for (i = 0; i < size; ++i)
{
a->window[i] = bessi0(PiAlpha * sqrt(1.0 - pow(2.0 * (double)i / arg1 - 1.0, 2))) / arg0;
cgsum += a->window[i];
igsum += a->window[i] * a->window[i];
}
a->inv_coherent_gain = (double)size / cgsum;
for (i = 0; i < size; i++)
a->window[i] *= a->inv_coherent_gain;
break;
}
case 6: // Blackman-Harris window (7-term)
default:
{
arg0 = 2.0 * PI / ((double)size - 1.0);
cgsum = 0.0;
igsum = 0.0;
for (i = 0; i < size; ++i)
{
arg1 = cos (arg0 * (double)i);
a->window[i] = + 6.3964424114390378e-02
+ arg1 * ( - 2.3993864599352804e-01
+ arg1 * ( + 3.5015956323820469e-01
+ arg1 * ( - 2.4774111897080783e-01
+ arg1 * ( + 8.5438256055858031e-02
+ arg1 * ( - 1.2320203369293225e-02
+ arg1 * ( + 4.3778825791773474e-04 ))))));
cgsum += a->window[i];
igsum += a->window[i] * a->window[i];
}
a->inv_coherent_gain = (double)size / cgsum;
for (i = 0; i < size; i++)
a->window[i] *= a->inv_coherent_gain;
break;
}
}
a->inherent_power_gain = igsum / (double)size;
a->inv_enb = 1.0 / (a->inherent_power_gain * a->inv_coherent_gain * a->inv_coherent_gain);
// print_window_gain ("windows.txt", type, a->inv_coherent_gain, a->inherent_power_gain);
}
// spur elimination, REAL input data
void eliminate(int disp, int ss, int LO)
{
DP a = pdisp[disp];
int i, k, begin, end, ilim;
double mag;
if (ss == a->begin_ss)
begin = a->fscL + a->clip;
else
begin = a->clip;
if (ss == a->end_ss)
end = a->out_size - 1 -a->clip - a->fscH;
else
end = a->out_size - 1 - a->clip;
ilim = a->out_size - 1;
if (a->flip[LO])
for (i = ilim - begin, k = 0; i > ilim - end; i--, k++)
{
mag = (a->fft_out[ss][LO])[i][0] * (a->fft_out[ss][LO])[i][0] + (a->fft_out[ss][LO])[i][1] * (a->fft_out[ss][LO])[i][1];
if ((a->spec_flag[ss] == 0) || (mag < (a->result[ss])[k]))
(a->result[ss])[k] = mag;
}
else
for (i = begin, k = 0; i < end; i++, k++)
{
mag = (a->fft_out[ss][LO])[i][0] * (a->fft_out[ss][LO])[i][0] + (a->fft_out[ss][LO])[i][1] * (a->fft_out[ss][LO])[i][1];
if ((a->spec_flag[ss] == 0) || (mag < (a->result[ss])[k]))
(a->result[ss])[k] = mag;
}
a->ss_bins[ss] = k;
}
// spur elimination, COMPLEX input data
void Celiminate(int disp, int ss, int LO)
{
DP a = pdisp[disp];
int i, k, begin0, end0, begin1, end1, ilim;
double mag;
if (ss == a->begin_ss)
{
begin0 = a->out_size / 2 + 1 + a->clip + a->fscL;
if (begin0 > a->out_size)
begin1 = begin0 - a->out_size;
else
begin1 = 0;
}
else
{
begin0 = a->out_size / 2 + 1 + a->clip;
begin1 = 0;
}
if (ss == a->end_ss)
{
end1 = a->out_size / 2 - a->clip - a->fscH;
if (end1 < 0)
end0 = a->out_size + end1;
else
end0 = a->out_size;
}
else
{
end0 = a->out_size;
end1 = a->out_size / 2 - a->clip;
}
ilim = a->out_size - 1;
if (a->flip[LO])
{
for (i = ilim - begin0, k = 0; i > ilim - end0; i--, k++)
{
mag = (a->fft_out[ss][LO])[i][0] * (a->fft_out[ss][LO])[i][0] + (a->fft_out[ss][LO])[i][1] * (a->fft_out[ss][LO])[i][1];
if ((a->spec_flag[ss] == 0) || (mag < (a->result[ss])[k]))
(a->result[ss])[k] = mag;
}
for (i = ilim - begin1; i > ilim - end1; i--, k++)
{
mag = (a->fft_out[ss][LO])[i][0] * (a->fft_out[ss][LO])[i][0] + (a->fft_out[ss][LO])[i][1] * (a->fft_out[ss][LO])[i][1];
if ((a->spec_flag[ss] == 0) || (mag < (a->result[ss])[k]))
(a->result[ss])[k] = mag;
}
}
else
{
for (i = begin0, k = 0; i < end0; i++, k++)
{
mag = (a->fft_out[ss][LO])[i][0] * (a->fft_out[ss][LO])[i][0] + (a->fft_out[ss][LO])[i][1] * (a->fft_out[ss][LO])[i][1];
if ((a->spec_flag[ss] == 0) || (mag < (a->result[ss])[k]))
(a->result[ss])[k] = mag;
}
for (i = begin1; i < end1; i++, k++)
{
mag = (a->fft_out[ss][LO])[i][0] * (a->fft_out[ss][LO])[i][0] + (a->fft_out[ss][LO])[i][1] * (a->fft_out[ss][LO])[i][1];
if ((a->spec_flag[ss] == 0) || (mag < (a->result[ss])[k]))
(a->result[ss])[k] = mag;
}
}
a->ss_bins[ss] = k;
}
void detector ( int det_type, // detector type
int m, // number of bins
int num_pixels, // number of output pixels
double pix_per_bin, // pixels per bin
double bin_per_pix, // bins per pixel
double* bins, // input buffer
double* pixels, // output buffer
double inv_enb, // inverse equivalent noise bandwidth
double fsclipL,
double fsclipH,
double det_offset
)
{
int i, imin, ilim;
int pix_count = 0;
int rose, fell, next_pix_count, bcount, last_pix_count;
double prev_maxi, mini, maxi, psum;
if (pix_per_bin <= 1.0)
{
if (fsclipL == floor(fsclipL)) imin = 0;
else imin = 1;
if (fsclipH == floor(fsclipH)) ilim = m;
else ilim = m - 1;
switch (det_type)
{
case 0: // positive peak
for (i = 0; i < num_pixels; i++)
pixels[i] = - 1.0e300;
for (i = imin; i < ilim; i++)
{
pix_count = (int)(det_offset + (double)i * pix_per_bin);
if (pix_count >= num_pixels) pix_count = num_pixels - 1;
if (bins[i] > pixels[pix_count])
pixels[pix_count] = bins[i];
}
break;
case 1: // rosenfell
rose = 0;
fell = 0;
mini = + 1.0e300;
maxi = - 1.0e300;
prev_maxi = - 1.0e300;
for (i = imin; i < ilim; i++) // for each FFT bin
{
// determine the pixel number that this FFT bin goes into
pix_count = (int)(det_offset + (double)i * pix_per_bin);
if (pix_count >= num_pixels) pix_count = num_pixels - 1;
// determine the pixel number for the NEXT FFT bin
next_pix_count = (int)((double)(i + 1) * pix_per_bin);
// update the minimum and maximum of the set of bins within the pixel
if (bins[i] < mini) mini = bins[i];
if (bins[i] > maxi) maxi = bins[i];
// if the next bin is also within the pixel && there is a next bin,
// compare its value with the current bin and update rose and fell
if (next_pix_count == pix_count && i < ilim - 1)
{
// NOTE: when next_pix_count != pix_count, rose and fell do not get updated;
// that's OK because we do NOT need to know if there's a rise or fall across bins
if (bins[i + 1] > bins[i]) rose = 1;
if (bins[i + 1] < bins[i]) fell = 1;
}
// if the next bin is NOT within the pixel || there is no next bin, finalize the pixel
// value and reset parameters
else
{
if (rose && fell)
if (pix_count & 1) // odd pixel
pixels[pix_count] = max (prev_maxi, maxi);
else // even pixel
pixels[pix_count] = mini;
else
pixels[pix_count] = maxi;
rose = 0;
fell = 0;
prev_maxi = maxi;
mini = + 1.0e300;
maxi = - 1.0e300;
}
}
break;
case 2: // average - adjusted for window's equivalent noise bandwidth
psum = 0.0;
bcount = 0;
for (i = imin; i < ilim; i++)
{
last_pix_count = pix_count;
pix_count = (int)(det_offset + (double)i * pix_per_bin);
if (pix_count >= num_pixels) pix_count = num_pixels - 1;
if (pix_count == last_pix_count)
{
psum += bins[i];
bcount++;
}
else
{
pixels[last_pix_count] = psum / (double)bcount * inv_enb;
psum = bins[i];
bcount = 1;
}
if (i == ilim - 1)
{
pixels[pix_count] = psum / (double)bcount * inv_enb;
}
}
break;
case 3: // sample - adjusted for window's equivalent noise bandwidth
bcount = 0;
for (i = imin; i < ilim; i++)
{
last_pix_count = pix_count;
pix_count = (int)(det_offset + (double)i * pix_per_bin);
if (pix_count >= num_pixels) pix_count = num_pixels - 1;
if (pix_count == last_pix_count)
{
bcount++;
}
else
{
pixels[last_pix_count] = bins[i - bcount / 2 - 1] * inv_enb;
bcount = 1;
}
if (i == ilim - 1)
{
pixels[pix_count] = bins[i - bcount / 2] * inv_enb;
}
}
break;
case 4: // rms
psum = 0.0;
bcount = 0;
for (i = imin; i < ilim; i++)
{
last_pix_count = pix_count;
pix_count = (int)(det_offset + (double)i * pix_per_bin);
if (pix_count >= num_pixels) pix_count = num_pixels - 1;
if (pix_count == last_pix_count)
{
psum += bins[i] * bins[i];
bcount++;
}
else
{
pixels[last_pix_count] = sqrt (psum / (double)bcount) * inv_enb;
psum = bins[i] * bins[i];
bcount = 1;
}
if (i == ilim - 1)
{
pixels[pix_count] = sqrt (psum / (double)bcount) * inv_enb;
}
}
break;
}
}
else
{
double frac;
double pix_pos = fsclipL - floor(fsclipL);
int ampl_comp = (det_type == 2) || (det_type == 3) || (det_type == 4);
for (i = 1; i < m; i++)
{
while (pix_pos < ((double)i + 1.0e-06) && pix_count < num_pixels)
{
frac = pix_pos - (double)(i - 1);
pixels[pix_count] = bins[i - 1] * (1.0 - frac) + bins[i] * frac;
if (ampl_comp) pixels[pix_count] *= inv_enb;
pix_count++;
pix_pos += bin_per_pix;
}
}
}
}
void avenger ( int av_mode, // averaging mode
int num_pixels, // number of pixels
int* avail_frames, // number of available frames for window averaging
int num_average, // number of frames to average within a window
int* av_in_idx, // in index for av_buff
int* av_out_idx, // out index for av_buff
double av_backmult, // multiplier for recursive averaging
double scale, // scale factor
double* t_pixels, // input buffer
double* av_sum, // history buffer for averaging
double** av_buff, // frame buffer for window averaging
double* cd, // correction factor buffer
int norm, // if TRUE, normalize to one Hz bandwidth
double norm_oneHz, // normalization factor to add
dOUTREAL* pixels // output buffer
)
{
int i;
double factor;
switch (av_mode)
{
case -1: // peak-hold
{
for (i = 0; i < num_pixels; i++)
{
if (t_pixels[i] > av_sum[i])
av_sum[i] = t_pixels[i];
pixels[i] = (dOUTREAL)(10.0 * mlog10(scale * cd[i] * av_sum[i] + 1.0e-60));
}
break;
}
case 0: // no averaging
default:
{
for (i = 0; i < num_pixels; i++)
pixels[i] = (dOUTREAL)(10.0 * mlog10(scale * cd[i] * t_pixels[i] + 1.0e-60));
break;
}
case 1: // weighted averaging of linear data
{
double onem_avb = 1.0 - av_backmult;
for (i = 0; i < num_pixels; i++)
{
av_sum[i] = av_backmult * av_sum[i] + onem_avb * t_pixels[i];
pixels[i] = (dOUTREAL)(10.0 * mlog10(scale * cd[i] * av_sum[i] + 1.0e-60));
}
break;
}
case 2: // window averaging of linear data
{
if (*avail_frames < num_average)
{
factor = scale / (double)++(*avail_frames);
for (i = 0; i < num_pixels; i++)
{
av_sum[i] += t_pixels[i];
av_buff[*av_in_idx][i] = t_pixels[i];
pixels[i] = (dOUTREAL)(10.0 * mlog10(cd[i] * av_sum[i] * factor + 1.0e-60));
}
}
else
{
factor = scale / (double)(*avail_frames);
for (i = 0; i < num_pixels; i++)
{
av_sum[i] += t_pixels[i] - (av_buff[*av_out_idx])[i];
av_buff[*av_in_idx][i] = t_pixels[i];
pixels[i] = (dOUTREAL)(10.0 * mlog10(cd[i] * av_sum[i] * factor + 1.0e-60));
}
if (++(*av_out_idx) == dMAX_AVERAGE)
*av_out_idx = 0;
}
if (++(*av_in_idx) == dMAX_AVERAGE)
*av_in_idx = 0;
break;
}
case 3: // weighted averaging of log data - looks nice, not accurate for time-varying signals
{
double onem_avb = 1.0 - av_backmult;
for (i = 0; i < num_pixels; i++)
{
av_sum[i] = av_backmult * av_sum[i] + onem_avb * (10.0 * mlog10(scale * cd[i] * t_pixels[i] + 1e-60));
pixels[i] = (dOUTREAL)av_sum[i];
}
break;
}
}
if (norm)
for (i = 0; i < num_pixels; i++)
pixels[i] += (dOUTREAL)norm_oneHz;
}
void stitch(int disp)
{
DP a = pdisp[disp];
int i, j, k, n, m;
double* ptr;
// stitch
m = 0;
ptr = a->pre_av_out;
for (n = a->begin_ss; n <= a->end_ss; n++)
{
memcpy(ptr, a->result[n], a->ss_bins[n] * sizeof(double));
ptr += a->ss_bins[n];
m += a->ss_bins[n];
}
for (i = 0; i < a->num_pixout; i++) // for each output
{
EnterCriticalSection(&a->ResampleSection);
// if a detection of the same 'det_type' has already been done, use that result
j = i - 1;
k = i;
while (j >= 0)
{
if (a->det_type[i] == a->det_type[j])
k = j;
j--;
}
if (k == i)
// detect
detector (a->det_type[i], m, a->num_pixels, a->pix_per_bin, a->bin_per_pix, a->pre_av_out,
a->t_pixels[i], a->inv_enb, a->fsclipL, a->fsclipH, a->det_offset);
else
memcpy (a->t_pixels[i], a->t_pixels[k], a->num_pixels * sizeof (double));
// average & convert to dBm
avenger (a->av_mode[i], a->num_pixels, &a->avail_frames[i], a->num_average[i], &a->av_in_idx[i], &a->av_out_idx[i],
a->av_backmult[i], a->scale, a->t_pixels[i], a->av_sum[i], a->av_buff[i], a->cd, a->normalize[i], a->norm_oneHz,
a->pixels[i][a->w_pix_buff[i]]);
LeaveCriticalSection(&a->ResampleSection);
EnterCriticalSection(&a->PB_ControlsSection[i]);
a->last_pix_buff[i] = a->w_pix_buff[i];
while ((a->w_pix_buff[i] = (a->w_pix_buff[i] + 1) % dNUM_PIXEL_BUFFS) == a->r_pix_buff[i]);
LeaveCriticalSection(&a->PB_ControlsSection[i]);
InterlockedBitTestAndSet(&(a->pb_ready[i][a->last_pix_buff[i]]), 0);
}
}
DWORD WINAPI spectra (void *pargs)
{
int i, j;
int disp = ((int)(uintptr_t)pargs) >> 12;
int ss = (((int)(uintptr_t)pargs) >> 4) & 255;
int LO = ((int)(uintptr_t)pargs) & 15;
DP a = pdisp[disp];
if (a->stop)
{
InterlockedDecrement(a->pnum_threads);
return 0;
}
if ((ss >= a->begin_ss) && (ss <= a->end_ss))
{
for (i = 0; i < a->size; i++)
{
(a->fft_in[ss][LO])[i] = a->window[i] * (double)((a->I_samples[ss][LO])[a->IQO_idx[ss][LO]]);
if(++a->IQO_idx[ss][LO] >= a->bsize)
a->IQO_idx[ss][LO] -= a->bsize;
}
if (a->stop)
{
InterlockedDecrement(a->pnum_threads);
return 0;
}
fftw_execute (a->plan[ss][LO]);
}
if (a->stop)
{
InterlockedDecrement(a->pnum_threads);
return 0;
}
EnterCriticalSection(&(a->EliminateSection[ss]));
if ((ss >= a->begin_ss) && (ss <= a->end_ss))
eliminate(disp, ss, LO);
a->spec_flag[ss] |= 1 << LO;
if (a->spec_flag[ss] == ((1 << a->num_fft) - 1))
{
a->spec_flag[ss] = 0;
LeaveCriticalSection (&(a->EliminateSection[ss]));
EnterCriticalSection (&a->StitchSection);
a->stitch_flag |= ((uint64_t)1) << ss;
if (a->stitch_flag == ((((uint64_t)1) << a->num_stitch) - 1))
{
a->stitch_flag = 0;
LeaveCriticalSection(&a->StitchSection);
for (j = 0; j < dMAX_STITCH; j++)
for (i = 0; i < dMAX_NUM_FFT; i++)
InterlockedBitTestAndReset(&(a->input_busy[j][i]), 0);
stitch(disp);
}
else
LeaveCriticalSection(&a->StitchSection);
}
else
LeaveCriticalSection (&(a->EliminateSection[ss]));
InterlockedDecrement(a->pnum_threads);
return 1;
}
/********************************************************************************************************
* *
* BEGIN CODE TO GET MAX FFT_BIN WITHIN A FREQ RANGE *
* *
********************************************************************************************************/
// Call in XCreateAnalyzer(...)
// This gets initialized for each 'disp' that is set up.
void Init_DetectMaxBin(int disp)
{
DP a = pdisp[disp];
InitializeCriticalSectionAndSpinCount(&a->cs_dmb, 2500);
a->dmb_run = 0;
a->dmb_disp = disp;
a->dmb_ss = 0;
a->dmb_LO = 0;
a->dmb_rate = 48000.0;
a->dmb_fLow = 0.0;
a->dmb_fHigh = 0.0;
a->dmb_begin0 = 0;
a->dmb_end0 = -1;
a->dmb_begin1 = 0;
a->dmb_end1 = -1;
a->dmb_max_dB = -400.0;
}
// Call in DestroyAnalyzer(...)
void Destroy_DetectMaxBin(int disp)
{
DP a = pdisp[disp];
DeleteCriticalSection(&a->cs_dmb);
}
// Called from SetupDetectMaxBin(...) AND anytime 'size' changes, e.g., in SetAnalyzer(...)
void calc_dmb(int disp, int size)
{
DP a = pdisp[disp];
double bin_spacing, min_freq, max_freq;
bin_spacing = a->dmb_rate / size;
min_freq = -a->dmb_rate * (double)((size / 2 - 1)) / (double)size;
max_freq = -min_freq;
if (a->dmb_fLow < min_freq) a->dmb_fLow = min_freq;
if (a->dmb_fLow > max_freq) a->dmb_fLow = max_freq;
if (a->dmb_fHigh < min_freq) a->dmb_fHigh = min_freq;
if (a->dmb_fHigh > max_freq) a->dmb_fHigh = max_freq;
if (a->dmb_fLow < 0.0 && a->dmb_fHigh < 0.0)
{
a->dmb_begin0 = size - (int)ceil(-a->dmb_fLow / bin_spacing);
// Example: 1024, 48K, -2700 => 966
// Example: 1024, 48K, -23953.125==min_freq => 513
// NOTE: since dmb_fLow < 0.0, begin0 will never be greater than a->size - 1
a->dmb_end0 = size - (int)ceil(-a->dmb_fHigh / bin_spacing);
// Example: 1024, 48K, -300 => 1017
a->dmb_begin1 = 0;
a->dmb_end1 = -1;
}
else if (a->dmb_fLow >= 0.0 && a->dmb_fHigh >= 0.0)
{
a->dmb_begin0 = (int)round(a->dmb_fLow / bin_spacing);
// Example: 1024, 48K, +300 => 6
a->dmb_end0 = (int)round(a->dmb_fHigh / bin_spacing);
// Example: 1024, 48K, +2700 => 58
// Example: 1024, 48K, 23953.125==max_freq => 511
a->dmb_begin1 = 0;
a->dmb_end1 = -1;
}
else // fLow < 0.0 && fHigh >= 0.0
{
a->dmb_begin0 = size - (int)ceil(-a->dmb_fLow / bin_spacing);
a->dmb_end0 = size - 1;
a->dmb_begin1 = 0;
a->dmb_end1 = (int)round(a->dmb_fHigh / bin_spacing);
}
if (a->dmb_tau > 0.0 && a->dmb_frame_rate > 0)
a->dmb_decay = exp(-1.0 / (a->dmb_tau * (double)a->dmb_frame_rate));
else
a->dmb_decay = 0.0;
a->dmb_max_dB = -400.0;
}
// Call from console, for each 'disp' for which this function is desired.
// Call for initial setup and anytime one of the parameters changes.
// run: Set to '1' if this is being used; cycles can be saved by setting to
// '0' if this is not currently in use.
// disp: Display identifier number.
// ss: Set to '0' for Thetis use.
// LO: Set to '0' for Thetis use.
// rate: Sample_rate of display data, e.g., '192000.0'.
// fLow: Lowest frequency of frequency range to evaluate, referenced to center_frequency
// to which DDC is tuned. For example, for LSB, not using CTUN, this might be
// '-3000.0'.
// fHigh: Highest frequency of frequency range to evaluate, referenced to center_frequency
// to which DDC is tuned. Example, LSB: '-300.0'.
// tau: Metering time constant in seconds. E.g., '0.5'.
// frame_rate: Display frame_rate currently in use. E.g., '60'.
PORT
void SetupDetectMaxBin(int run, int disp, int ss, int LO, double rate,
double fLow, double fHigh, double tau, int frame_rate)
{
// We only allow setup for one (ss,LO) pair at a time for each 'disp'.
DP a = pdisp[disp];
a->dmb_run = run;
a->dmb_disp = disp;
a->dmb_ss = ss;
a->dmb_LO = LO;
a->dmb_rate = rate;
a->dmb_fLow = fLow;
a->dmb_fHigh = fHigh;
a->dmb_tau = tau;
a->dmb_frame_rate = frame_rate;
calc_dmb(a->dmb_disp, a->size);
}
// Call this function in 'Cspectra(...)', after the FFT.
void DetectMaxBin(int disp, int ss, int LO)
{
DP a = pdisp[disp];
int i;
double mag, dmb_max;
double dmb_max_dB;
// If 'run' is set and the FFT Output is from the correct disp, ss, LO ...
if (a->dmb_run && disp == a->dmb_disp && ss == a->dmb_ss && LO == a->dmb_LO)
{
fftw_complex* fft_out = a->fft_out[ss][LO];
dmb_max = 1.0e-60;
EnterCriticalSection(&a->cs_dmb);
for (i = a->dmb_begin0; i <= a->dmb_end0; i++)
{
mag = fft_out[i][0] * fft_out[i][0] + fft_out[i][1] * fft_out[i][1];
if (mag > dmb_max) dmb_max = mag;
}
for (i = a->dmb_begin1; i <= a->dmb_end1; i++)
{
mag = fft_out[i][0] * fft_out[i][0] + fft_out[i][1] * fft_out[i][1];
if (mag > dmb_max) dmb_max = mag;
}
a->dmb_max_dB -= fabs((1.0 - a->dmb_decay) * a->dmb_max_dB);
dmb_max_dB = 10.0 * mlog10(a->scale * dmb_max);
if (dmb_max_dB > a->dmb_max_dB) a->dmb_max_dB = dmb_max_dB;
LeaveCriticalSection(&a->cs_dmb);
// for test only.
// printf("Max Bin = %.5e\n", a->dmb_max_dB);
}
}
// Call from console, for each 'disp' for which this function is desired.
// Always returns the value from the most recent display frame.
PORT
double GetDetectMaxBin(int disp)
{
DP a = pdisp[disp];
double dmb_max_dB;
EnterCriticalSection(&a->cs_dmb);
dmb_max_dB = a->dmb_max_dB;
LeaveCriticalSection(&a->cs_dmb);
return dmb_max_dB;
}
/********************************************************************************************************
* *
* END CODE TO GET MAXIMUM FFT_BIN WITHIN A FREQ RANGE *
* *
********************************************************************************************************/
DWORD WINAPI Cspectra (void *pargs)
{
int i, j;
int disp = ((int)(uintptr_t)pargs) >> 12;
int ss = (((int)(uintptr_t)pargs) >> 4) & 255;
int LO = ((int)(uintptr_t)pargs) & 15;
DP a = pdisp[disp];
int trans_size = a->size * sizeof(double);
if (a->stop)
{
InterlockedDecrement(a->pnum_threads);
return 0;
}
if ((ss >= a->begin_ss) && (ss <= a->end_ss))
{
for (i = 0; i < a->size; i++)
{
(a->Cfft_in[ss][LO])[i][0] = a->window[i] * (double)((a->I_samples[ss][LO])[a->IQO_idx[ss][LO]]);
(a->Cfft_in[ss][LO])[i][1] = a->window[i] * (double)((a->Q_samples[ss][LO])[a->IQO_idx[ss][LO]]);
if(++a->IQO_idx[ss][LO] >= a->bsize)
a->IQO_idx[ss][LO] -= a->bsize;
}
if (a->stop)
{
InterlockedDecrement(a->pnum_threads);
return 0;
}
fftw_execute (a->Cplan[ss][LO]);
// Detect value of Max FFT Bin in a freq range
DetectMaxBin(disp, ss, LO);
//
}
if (a->stop)
{
InterlockedDecrement(a->pnum_threads);
return 0;
}
if (InterlockedBitTestAndReset(&(a->snap[ss][LO]), 0))
{
memcpy((char *)(a->snap_buff[ss][LO]), (char *)(a->fft_out[ss][LO]) + trans_size, trans_size);
memcpy((char *)(a->snap_buff[ss][LO]) + trans_size, (char *)(a->fft_out[ss][LO]), trans_size);
SetEvent(a->hSnapEvent[ss][LO]);
}
EnterCriticalSection(&(a->EliminateSection[ss]));
if ((ss >= a->begin_ss) && (ss <= a->end_ss))
Celiminate(disp, ss, LO);
a->spec_flag[ss] |= 1 << LO;
if (a->spec_flag[ss] == ((1 << a->num_fft) - 1))
{
a->spec_flag[ss] = 0;
LeaveCriticalSection (&(a->EliminateSection[ss]));
EnterCriticalSection (&a->StitchSection);
a->stitch_flag |= ((uint64_t)1) << ss;
if (a->stitch_flag == ((((uint64_t)1) << a->num_stitch) - 1))
{
a->stitch_flag = 0;
LeaveCriticalSection(&a->StitchSection);
for (j = 0; j < dMAX_STITCH; j++)
for (i = 0; i < dMAX_NUM_FFT; i++)
InterlockedBitTestAndReset(&(a->input_busy[j][i]), 0);
stitch(disp);
}
else
LeaveCriticalSection(&a->StitchSection);
}
else
LeaveCriticalSection (&(a->EliminateSection[ss]));
InterlockedDecrement(a->pnum_threads);
return 1;
}
void interpolate(int disp, int set, double fmin, double fmax, int num_pixels)
{
DP a = pdisp[disp];
int i;
double f;
int n = a->n_freqs[set];
int k;
int kmin = 0;
int kmax = n - 1;
int kdelta;
double dx;
double mag;
for (i = 0; i < num_pixels; i++)
{
f = fmin + (double)i * (fmax - fmin) / (double)(num_pixels - 1);
if (f < (a->freqs[set])[0])
k = 0;
else if (f > (a->freqs[set])[n - 1])
k = n - 2;
else
{
kdelta = 1;
while (f < (a->freqs[set])[kmin])
{
kmin = max(0, kmin - kdelta);
kdelta += kdelta;
}
while (f > (a->freqs[set])[kmax])
{
kmax = min(n - 1, kmax + kdelta);
kdelta += kdelta;
}
k = (kmin + kmax) / 2;
while ((kmax - kmin) > 1)
{
k = (kmin + kmax) / 2;
if (f > (a->freqs[set])[k])
kmin = k;
else
kmax = k--;
}
}
dx = f - (a->freqs[set])[k];
mag = (((a->ac3[set][0])[k] * dx + (a->ac2[set][0])[k]) * dx + (a->ac1[set][0])[k]) * dx + (a->ac0[set][0])[k];
a->cd[i] = mag * mag;
}
return;
}
int build_interpolants(int disp, int set, int n, int m, double *x, double (*y)[dMAX_M])
{
DP a = pdisp[disp];
double dx[dMAX_N];
double idx[dMAX_N];
double dmain[dMAX_N];
double dsub[dMAX_N];
double dsup[dMAX_N];
double d[dMAX_N][dMAX_M];
double S[dMAX_N][dMAX_M];
double b[dMAX_N];
double v[dMAX_N][dMAX_M];
double tmp;
int i, j;
if (n < 3) return -1;
for (i = 0; i < n - 1; i++)
{
dx[i] = x[i + 1] - x[i];
if (dx[i] < 1e-30)
return -1;
idx[i] = 1.0 / dx[i];
}
for (i = 1; i <= n - 2; i++)
{
if (i == 1)
{
dsub[i] = 0.0;
dmain[i] = 3.0 * dx[i - 1] + 2.0 * dx[i];
dsup[i] = dx[i];
}
else if (i == (n - 2))
{
dsub[i] = dx[i - 1];
dmain[i] = 2.0 * dx[i - 1] + 3.0 * dx[i];
dsup[i] = 0.0;
}
else
{
dsub[i] = dx[i - 1];
dmain[i] = 2.0 * (dx[i - 1] + dx[i]);
dsup[i] = dx[i];
}
for (j = 0; j < m; j++)
d[i][j] = 6.0 * ((y[i + 1][j] - y[i][j]) * idx[i] - (y[i][j] - y[i - 1][j]) * idx[i - 1]);
}
b[1] = dmain[1];
for (j = 0; j < m; j++)
v[1][j] = d[1][j];
for (i = 2; i <= n - 2; i++)
{
tmp = dsub[i] / b[i - 1];
b[i] = dmain[i] - tmp * dsup[i - 1];
for (j = 0; j < m; j++)
v[i][j] = d[i][j] - tmp * v[i - 1][j];
}
for (j = 0; j < m; j++)
S[n - 2][j] = v[n - 2][j] / b[n - 2];
for (i = n - 3; i >= 1; i--)
for (j = 0; j < m; j++)
S[i][j] = (v[i][j] - dsup[i] * S[i + 1][j]) / b[i];
for (j = 0; j < m; j++)
{
S[0][j] = S[1][j];
S[n - 1][j] = S[n - 2][j];
}
for (i = 0; i < n - 1; i++)
for (j = 0; j < m; j++)
{
(a->ac3[set][j])[i] = (S[i + 1][j] - S[i][j]) / (6.0 * dx[i]);
(a->ac2[set][j])[i] = 0.5 * S[i][j];
(a->ac1[set][j])[i] = (y[i + 1][j] - y[i][j]) * idx[i] - (2.0 * dx[i] * S[i][j] + dx[i] * S[i + 1][j]) / 6.0;
(a->ac0[set][j])[i] = y[i][j];
}
return 0;
}
void __cdecl sendbuf(void *arg)
{
DP a = pdisp[(int)(uintptr_t)arg];
while(!a->end_dispatcher)
{
for (a->ss = 0; a->ss < a->num_stitch; a->ss++)
for (a->LO = 0; a->LO < a->num_fft; a->LO++)
{
if (!_InterlockedAnd(&(a->input_busy[a->ss][a->LO]), 1) && _InterlockedAnd(&(a->buff_ready[a->ss][a->LO]), 1))
{
InterlockedBitTestAndSet(&(a->input_busy[a->ss][a->LO]), 0);
a->IQO_idx[a->ss][a->LO] = a->IQout_index[a->ss][a->LO];
InterlockedIncrement(a->pnum_threads);
if (a->type == 0)
QueueUserWorkItem(spectra, (void *)(((uintptr_t)arg << 12) + (a->ss << 4) + a->LO), 0);
else
QueueUserWorkItem(Cspectra, (void *)(((uintptr_t)arg << 12) + (a->ss << 4) + a->LO), 0);
if((a->IQout_index[a->ss][a->LO] += a->incr) >= a->bsize)
a->IQout_index[a->ss][a->LO] -= a->bsize;
EnterCriticalSection(&(a->BufferControlSection[a->ss][a->LO]));
if ((a->have_samples[a->ss][a->LO] -= a->incr) < a->size)
InterlockedBitTestAndReset(&(a->buff_ready[a->ss][a->LO]), 0);
LeaveCriticalSection(&(a->BufferControlSection[a->ss][a->LO]));
}
}
Sleep(1);
}
InterlockedBitTestAndReset(&a->dispatcher, 0);
_endthread();
}
void CalcBandwidthNormalization (DP a)
{
double bin_width;
if (a->sample_rate <= 0 || a->size <= 0) { a->norm_oneHz = 0.0; return; }
bin_width = (double)a->sample_rate / (double)a->size;
if (bin_width <= 0.0) { a->norm_oneHz = 0.0; return; }
a->norm_oneHz = 10.0 * mlog10 (1.0 / bin_width);
}
PORT
void ResetPixelBuffers(int disp)
{
//[2.10.2]MW0LGE reset all the pixel and average buffers
DP a = pdisp[disp];
int i, j, k;
EnterCriticalSection(&a->SetAnalyzerSection);
EnterCriticalSection(&a->ResampleSection);
for (i = 0; i < dMAX_PIXOUTS; i++)
{
for (j = 0; j < dMAX_PIXELS; j++)
a->t_pixels[i][j] = 0.0;
for (j = 0; j < dMAX_AVERAGE; j++)
for (k = 0; k < dMAX_PIXELS; k++)
a->av_buff[i][j][k] = 0.0f;
switch (a->av_mode[i])
{
case 1:
for (j = 0; j < dMAX_PIXELS; j++)
a->av_sum[i][j] = 1.0e-12;
break;
case 2:
//done below
//a->avail_frames[i] = 0;
//a->av_in_idx[i] = 0;
//a->av_out_idx[i] = 0;
break;
case 3:
for (j = 0; j < dMAX_PIXELS; j++)
a->av_sum[i][j] = -160.0;
break;
default:
memset((void*)a->av_sum[i], 0, sizeof(double) * dMAX_PIXELS);
break;
}
a->avail_frames[i] = 0;
a->av_in_idx[i] = 0;
a->av_out_idx[i] = 0;
EnterCriticalSection(&a->PB_ControlsSection[i]);
a->w_pix_buff[i] = 0;
a->r_pix_buff[i] = 0;
a->last_pix_buff[i] = 0;
for (j = 0; j < dNUM_PIXEL_BUFFS; j++)
a->pb_ready[i][j] = 0;
LeaveCriticalSection(&a->PB_ControlsSection[i]);
}
memset((void*)a->pre_av_out, 0, sizeof(double) * a->max_size * a->max_stitch);
LeaveCriticalSection(&a->ResampleSection);
EnterCriticalSection(&a->StitchSection);
for (i = 0; i < dMAX_STITCH; i++)
for (j = 0; j < dMAX_NUM_FFT; j++)
a->input_busy[i][j] = 0;
for (i = 0; i < dMAX_STITCH; i++)
a->spec_flag[i] = 0;
a->stitch_flag = 0;
a->ss = 0;
a->LO = 0;
for (i = 0; i < dMAX_STITCH; i++)
for (j = 0; j < dMAX_NUM_FFT; j++)
{
EnterCriticalSection(&(a->BufferControlSection[i][j]));
a->buff_ready[i][j] = 0;
a->have_samples[i][j] = 0;
a->IQin_index[i][j] = 0;
a->IQout_index[i][j] = 0;
LeaveCriticalSection(&(a->BufferControlSection[i][j]));
}
LeaveCriticalSection(&a->StitchSection);
LeaveCriticalSection(&a->SetAnalyzerSection);
}
PORT
void SetAnalyzer ( int disp, // display identifier
int n_pixout, // pixel output identifier
int n_fft, // number of LO frequencies = number of ffts used in elimination
int typ, // 0 for real input data (I only); 1 for complex input data (I & Q)
int *flp, // vector with one elt for each LO frequency, 1 if high-side LO, 0 otherwise
int sz, // size of the fft, i.e., number of input samples
int bf_sz, // number of samples transferred for each OpenBuffer()/CloseBuffer()
int win_type, // integer specifying which window function to use
double pi, // PiAlpha parameter for Kaiser window
int ovrlp, // number of samples each fft (other than the first) is to re-use from the previous
int clp, // number of fft output bins to be clipped from EACH side of each sub-span
double fscLin, // number of bins to clip from low end of entire span
double fscHin, // number of bins to clip from high end of entire span
int n_pix, // number of pixel values to return. may be either <= or > number of bins
int n_stch, // number of sub-spans to concatenate to form a complete span
int calset, // identifier of which set of calibration data to use
double fmin, // frequency at first pixel value
double fmax, // frequency at last pixel value
int max_w
)
{
WDSP_FPE_GUARD;
DP a = pdisp[disp];
int i, j;
EnterCriticalSection(&a->SetAnalyzerSection);
a->end_dispatcher = 1;
while (InterlockedAnd(&a->dispatcher, 1))
Sleep(1);
a->stop = 1;
while (_InterlockedAnd(a->pnum_threads, 1023))
Sleep(1);
a->num_pixout = n_pixout;
a->num_fft = n_fft;
a->type = typ;
a->buff_size = bf_sz;
for (i = 0; i < a->num_fft; i++)
a->flip[i] = *(flp + i);
a->overlap = ovrlp;
a->clip = clp;
a->fsclipL = fscLin;
a->fsclipH = fscHin;
a->num_stitch = n_stch;
if (sz != a->size)
{
for (i = 0; i < a->max_stitch; i++)
for (j = 0; j < a->max_num_fft; j++)
{
if (a->plan[i][j]) fftw_destroy_plan (a->plan[i][j]);
if (a->Cplan[i][j]) fftw_destroy_plan (a->Cplan[i][j]);
a->plan[i][j] = fftw_plan_dft_r2c_1d(sz, a->fft_in[i][j], a->fft_out[i][j], FFTW_PATIENT);
a->Cplan[i][j] = fftw_plan_dft_1d(sz, a->Cfft_in[i][j], a->fft_out[i][j], FFTW_FORWARD, FFTW_PATIENT);
}
// Setup DetectMaxBin for a 'size' change.
calc_dmb(disp, sz);
//
}
if ((sz != a->size) || (win_type != a->window_type) || (pi != a->PiAlpha))
new_window(disp, win_type, sz, pi);
a->size = sz;
a->window_type = win_type;
a->PiAlpha = pi;
a->max_writeahead = max_w;
CalcBandwidthNormalization (a);
if (((fmin != a->f_min) || (fmax != a->f_max)) && ((fmin == 0.0) && (fmax == 0.0)))
for (i = 0; i < dMAX_PIXELS; i++)
a->cd[i] = 1.0;
if (((fmax != 0.0) || (fmin != 0.0)) && ((n_pix != a->num_pixels) || (fmin != a->f_min) || (fmax != a->f_max) || (calset != a->cal_set) || a->cal_changed))
interpolate(disp, calset, fmin, fmax, n_pix);
a->incr = a->size - a->overlap;
a->num_pixels = n_pix;
a->f_min = fmin;
a->f_max = fmax;
a->cal_set = calset;
a->cal_changed = 0;
if (a->type == 0)
{
a->out_size = a->size / 2 + 1;
a->scale = 4.0 / ((double)a->size * (double)a->size);
}
else
{
a->out_size = a->size;
a->scale = 1.0 / ((double)a->size * (double)a->size);
}
a->begin_ss = 0;
a->end_ss = a->num_stitch - 1;
a->fscL = (int)a->fsclipL;
a->fscH = (int)a->fsclipH;
while (a->fscL >= (a->out_size - 1 - 2 * a->clip))
{
a->fscL -= a->out_size - 1 - 2 * a->clip;
a->ss_bins[a->begin_ss] = 0;
a->begin_ss++;
}
while (a->fscH >= (a->out_size - 1 - 2 * a->clip))
{
a->fscH -= a->out_size - 1 - 2 * a->clip;
a->ss_bins[a->end_ss] = 0;
a->end_ss--;
}
a->pix_per_bin = (double)a->num_pixels / ((double)(a->num_stitch * (a->out_size - 1 - 2 * a->clip)) - a->fsclipL - a->fsclipH - 1.0);
a->det_offset = -a->pix_per_bin * (a->fsclipL - floor(a->fsclipL));
a->bin_per_pix = ((double)(a->num_stitch * (a->out_size - 1 - 2 * a->clip)) - 1.0 - a->fsclipL - a->fsclipH) / ((double)a->num_pixels - 1.0);
for (i = 0; i < dMAX_STITCH; i++)
for (j = 0; j < dMAX_NUM_FFT; j++)
a->input_busy[i][j] = 0;
for (i = 0; i < dMAX_STITCH; i++)
a->spec_flag[i] = 0;
a->stitch_flag = 0;
for (i = 0; i < dMAX_PIXOUTS; i++)
{
a->w_pix_buff[i] = 0;
a->r_pix_buff[i] = 0;
a->last_pix_buff[i] = 0;
for (j = 0; j < dNUM_PIXEL_BUFFS; j++)
a->pb_ready[i][j] = 0;
}
a->ss = 0;
a->LO = 0;
for (i = 0; i < dMAX_STITCH; i++)
for (j = 0; j < dMAX_NUM_FFT; j++)
{
a->buff_ready[i][j] = 0;
a->have_samples[i][j] = 0;
a->IQin_index[i][j] = 0;
a->IQout_index[i][j] = 0;
}
a->stop = 0;
a->end_dispatcher = 0;
LeaveCriticalSection(&a->SetAnalyzerSection);
}
PORT
void XCreateAnalyzer( int disp,
int *success,
int m_size,
int m_num_fft,
int m_stitch,
char *app_data_path
)
{
WDSP_FPE_GUARD;
int i, j;
DP a = (DP) malloc0 (sizeof(dp));
pdisp[disp] = a;
a->max_size = m_size;
a->max_num_fft = m_num_fft;
a->max_stitch = m_stitch;
a->pnum_threads = (LONG*) malloc0 (sizeof (LONG));
for (i = 0; i < a->max_stitch; i++)
for (j = 0; j < a->max_num_fft; j++)
{
a->hSnapEvent[i][j] = CreateEvent(NULL, FALSE, FALSE, TEXT("snap"));
a->snap[i][j] = 0;
}
InitializeCriticalSectionAndSpinCount(&a->ResampleSection, 0);
InitializeCriticalSectionAndSpinCount(&a->SetAnalyzerSection, 0);
InitializeCriticalSectionAndSpinCount(&a->StitchSection, 0);
for (i = 0; i < dMAX_PIXOUTS; i++)
InitializeCriticalSectionAndSpinCount(&a->PB_ControlsSection[i], 0);
for (i = 0; i < dMAX_STITCH; i++)
{
InitializeCriticalSectionAndSpinCount(&(a->EliminateSection[i]), 0);
for (j = 0; j < dMAX_NUM_FFT; j++)
InitializeCriticalSectionAndSpinCount(&(a->BufferControlSection[i][j]), 0);
}
a->window = (double*) malloc0 (sizeof(double) * a->max_size);
for (i = 0; i < a->max_stitch; i++)
{
a->result[i] = (double*) malloc0 (sizeof(double) * a->max_size);
}
for (i = 0; i < a->max_stitch; i++)
for (j = 0; j < a->max_num_fft; j++)
{
a->plan[i][j] = 0;
a->Cplan[i][j] = 0;
a->fft_in[i][j] = (double*) malloc0 (sizeof(double) * a->max_size);
a->Cfft_in[i][j] = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * a->max_size);
a->fft_out[i][j] = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * a->max_size);
}
a->pre_av_out = (double*) malloc0 (sizeof(double) * a->max_size * a->max_stitch);
for (i = 0; i < dMAX_PIXOUTS; i++)
{
a->det_type[i] = 0;
a->av_mode[i] = 0;
a->av_sum[i] = (double*) malloc0 (sizeof(double) * dMAX_PIXELS);
for (j = 0; j < dMAX_AVERAGE; j++)
a->av_buff[i][j] = (double*) malloc0 (sizeof(double) * dMAX_PIXELS);
a->t_pixels[i] = (double*) malloc0 (sizeof(double) * dMAX_PIXELS);
for (j = 0; j < dNUM_PIXEL_BUFFS; j++)
a->pixels[i][j] = (dOUTREAL*) malloc0 (sizeof(dOUTREAL) * dMAX_PIXELS);
}
a->cd = (double*) malloc0 (sizeof(double) * dMAX_PIXELS);
for (j = 0; j < dMAX_PIXELS; j++)
a->cd[j] = 1.0;
for (i = 0; i < dMAX_CAL_SETS; i++)
{
a->freqs[i] = (double*) malloc0 (sizeof(double) * dMAX_N);
for (j = 0; j < dMAX_M; j++)
{
a->ac3[i][j] = (double*) malloc0 (sizeof(double) * dMAX_N);
a->ac2[i][j] = (double*) malloc0 (sizeof(double) * dMAX_N);
a->ac1[i][j] = (double*) malloc0 (sizeof(double) * dMAX_N);
a->ac0[i][j] = (double*) malloc0 (sizeof(double) * dMAX_N);
}
}
a->size = -1;
a->window_type = -1;
a->num_pixels = -1;
a->cal_set = -1;
a->f_min = -1.0;
a->f_max = -1.0;
a->bsize = a->max_size * dSAMP_BUFF_MULT;
for (i = 0; i < a->max_stitch; i++)
for (j = 0; j < a->max_num_fft; j++)
{
a->I_samples[i][j] = (dINREAL*) malloc0 (sizeof(dINREAL) * a->bsize);
a->Q_samples[i][j] = (dINREAL*) malloc0 (sizeof(dINREAL) * a->bsize);
}
// Initialize DetectMaxBin functionality
Init_DetectMaxBin(disp);
// for test only.
// SetupDetectMaxBin(1, 0, 0, 0, 192000.0, -3000.0, -300.0, 0.5, 60);
//
*success = 0;
}
PORT
void DestroyAnalyzer(int disp)
{
DP a = pdisp[disp];
int i, j;
a->end_dispatcher = 1;
while (InterlockedAnd(&a->dispatcher, 1))
Sleep(1);
for (i = 0; i < a->max_stitch; i++)
for (j = 0; j < a->max_num_fft; j++)
{
_aligned_free (a->I_samples[i][j]);
_aligned_free (a->Q_samples[i][j]);
}
for (i = 0; i < dMAX_CAL_SETS; i++)
{
_aligned_free (a->freqs[i]);
for (j = 0; j < dMAX_M; j++)
{
_aligned_free (a->ac3[i][j]);
_aligned_free (a->ac2[i][j]);
_aligned_free (a->ac1[i][j]);
_aligned_free (a->ac0[i][j]);
}
}
_aligned_free (a->cd);
for (i = 0; i < dMAX_PIXOUTS; i++)
{
for (j = 0; j < dNUM_PIXEL_BUFFS; j++)
_aligned_free (a->pixels[i][j]);
_aligned_free (a->t_pixels[i]);
for (j = 0; j < dMAX_AVERAGE; j++)
_aligned_free (a->av_buff[i][j]);
_aligned_free (a->av_sum[i]);
}
_aligned_free (a->pre_av_out);
for (i = 0; i < a->max_stitch; i++)
for (j = 0; j < a->max_num_fft; j++)
{
fftw_destroy_plan (a->plan[i][j]);
fftw_destroy_plan (a->Cplan[i][j]);
fftw_free (a->Cfft_in[i][j]);
_aligned_free (a->fft_in[i][j]);
fftw_free (a->fft_out[i][j]);
}
for (i = 0; i < a->max_stitch; i++)
_aligned_free (a->result[i]);
_aligned_free (a->window);
for (i = 0; i < dMAX_STITCH; i++)
{
DeleteCriticalSection(&(a->EliminateSection[i]));
for (j = 0; j < dMAX_NUM_FFT; j++)
DeleteCriticalSection(&(a->BufferControlSection[i][j]));
}
for (i = 0; i < dMAX_PIXOUTS; i++)
DeleteCriticalSection(&a->PB_ControlsSection[i]);
DeleteCriticalSection(&a->StitchSection);
DeleteCriticalSection(&a->SetAnalyzerSection);
DeleteCriticalSection(&a->ResampleSection);
for (i = 0; i < a->max_stitch; i++)
for (j = 0; j < a->max_num_fft; j++)
CloseHandle(a->hSnapEvent[i][j]);
_aligned_free ((void *) a->pnum_threads);
// Destroy DetectMaxBin functionality.
Destroy_DetectMaxBin(disp);
//
_aligned_free (a);
}
PORT
void GetPixels ( int disp,
int pixout,
dOUTREAL *pix, //if new pixel values avail, copies to pix and sets flag = 1
int *flag //else, returns 0 (try again later)
)
{
DP a = pdisp[disp];
EnterCriticalSection(&a->PB_ControlsSection[pixout]);
a->r_pix_buff[pixout] = a->last_pix_buff[pixout];
LeaveCriticalSection(&a->PB_ControlsSection[pixout]);
if (_InterlockedAnd(&(a->pb_ready[pixout][a->r_pix_buff[pixout]]), 1))
{
memcpy (pix, a->pixels[pixout][a->r_pix_buff[pixout]], a->num_pixels * sizeof(dOUTREAL));
*flag = 1;
InterlockedBitTestAndReset(&(a->pb_ready[pixout][a->r_pix_buff[pixout]]), 0);
}
else
*flag = 0;
}
PORT
void SnapSpectrum( int disp,
int ss,
int LO,
double *snap_buff)
{
DP a = pdisp[disp];
a->snap_buff[ss][LO] = snap_buff;
InterlockedBitTestAndSet(&(a->snap[ss][LO]), 0);
WaitForSingleObject(a->hSnapEvent[ss][LO], INFINITE);
}
PORT
void SnapSpectrumTimeout( int disp,
int ss,
int LO,
double* snap_buff,
DWORD timeout,
int* flag)
{
DP a = pdisp[disp];
a->snap_buff[ss][LO] = snap_buff;
ResetEvent(a->hSnapEvent[ss][LO]);
InterlockedBitTestAndSet(&(a->snap[ss][LO]), 0);
if (!WaitForSingleObject(a->hSnapEvent[ss][LO], timeout))
*flag = 1;
else
{
InterlockedBitTestAndReset(&(a->snap[ss][LO]), 0);
*flag = 0;
}
}
int calcompare (const void * a, const void * b)
{
if (*(double*)a < *(double*)b)
return -1;
else if (*(double*)a == *(double*)b)
return 0;
else
return 1;
}
PORT
void SetCalibration ( int disp,
int set_num, //identifier for this calibration data set
int n_points, //number of calibration points in the set
double (*cal)[dMAX_M+1] //pointer to the calibration table, first
) // column is frequency, add'l columns are
// data for variables being calibrated
{
DP a = pdisp[disp];
int i, j;
int k = 0;
double y [dMAX_N][dMAX_M];
qsort (cal, n_points, (dMAX_M+1) * sizeof(double), calcompare);
for (i = 0; i < n_points; i++)
{
if ((i == n_points - 1) || (cal[i][0] != cal[i + 1][0]))
{
(a->freqs[set_num])[k] = cal[i][0];
for (j = 0; j < dMAX_M; j++)
y[k][j] = cal[i][j + 1];
k++;
}
}
a->n_freqs[set_num] = k;
build_interpolants(disp, set_num, a->n_freqs[set_num], dMAX_M, a->freqs[set_num], y);
a->cal_changed = 1;
}
PORT
void OpenBuffer(int disp, int ss, int LO, void **Ipointer, void **Qpointer)
{
DP a = pdisp[disp];
EnterCriticalSection(&a->SetAnalyzerSection);
*Ipointer = &((a->I_samples[ss][LO])[a->IQin_index[ss][LO]]);
*Qpointer = &((a->Q_samples[ss][LO])[a->IQin_index[ss][LO]]);
LeaveCriticalSection(&a->SetAnalyzerSection);
}
PORT
void CloseBuffer(int disp, int ss, int LO)
{
DP a = pdisp[disp];
EnterCriticalSection(&a->SetAnalyzerSection);
EnterCriticalSection(&(a->BufferControlSection[ss][LO]));
if (a->have_samples[ss][LO] > a->max_writeahead)
{
//if we're receiving samples too much faster than we're consuming them, skip some
if ((a->IQout_index[ss][LO] += a->have_samples[ss][LO] - a->max_writeahead) >= a->bsize)
a->IQout_index[ss][LO] -= a->bsize;
a->have_samples[ss][LO] = a->max_writeahead;
}
if ((a->have_samples[ss][LO] += a->buff_size) >= a->size)
InterlockedBitTestAndSet(&(a->buff_ready[ss][LO]), 0);
LeaveCriticalSection(&(a->BufferControlSection[ss][LO]));
if((a->IQin_index[ss][LO] += a->buff_size) >= a->bsize) //REQUIRES buff_size IS A SUB-MULTIPLE OF SIZE OF INPUT SAMPLE BUFFS!
a->IQin_index[ss][LO] = 0;
if (!InterlockedAnd(&a->dispatcher, 1))
{
InterlockedBitTestAndSet (&a->dispatcher, 0);
LeaveCriticalSection(&a->SetAnalyzerSection);
_beginthread(sendbuf, 0, (void *)(uintptr_t)disp);
}
else
LeaveCriticalSection(&a->SetAnalyzerSection);
}
PORT
void Spectrum(int disp, int ss, int LO, dINREAL* pI, dINREAL* pQ)
{
dINREAL *Ipointer;
dINREAL *Qpointer;
DP a = pdisp[disp];
EnterCriticalSection(&a->SetAnalyzerSection);
Ipointer = &((a->I_samples[ss][LO])[a->IQin_index[ss][LO]]);
Qpointer = &((a->Q_samples[ss][LO])[a->IQin_index[ss][LO]]);
LeaveCriticalSection(&a->SetAnalyzerSection);
memcpy(Ipointer, pI, a->buff_size * sizeof(dINREAL));
memcpy(Qpointer, pQ, a->buff_size * sizeof(dINREAL));
EnterCriticalSection(&a->SetAnalyzerSection);
EnterCriticalSection(&(a->BufferControlSection[ss][LO]));
if (a->have_samples[ss][LO] > a->max_writeahead)
{
//if we're receiving samples too much faster than we're consuming them, skip some
if ((a->IQout_index[ss][LO] += a->have_samples[ss][LO] - a->max_writeahead) >= a->bsize)
a->IQout_index[ss][LO] -= a->bsize;
a->have_samples[ss][LO] = a->max_writeahead;
}
if ((a->have_samples[ss][LO] += a->buff_size) >= a->size)
InterlockedBitTestAndSet(&(a->buff_ready[ss][LO]), 0);
LeaveCriticalSection(&(a->BufferControlSection[ss][LO]));
if((a->IQin_index[ss][LO] += a->buff_size) >= a->bsize) //REQUIRES buff_size IS A SUB-MULTIPLE OF SIZE OF INPUT SAMPLE BUFFS!
a->IQin_index[ss][LO] = 0;
if (!InterlockedAnd(&a->dispatcher, 1))
{
InterlockedBitTestAndSet(&a->dispatcher, 0);
LeaveCriticalSection(&a->SetAnalyzerSection);
_beginthread(sendbuf, 0, (void *)(uintptr_t)disp);
}
else
LeaveCriticalSection(&a->SetAnalyzerSection);
}
PORT
void Spectrum2(int run, int disp, int ss, int LO, dINREAL* pbuff)
{
if (run)
{
int i;
dINREAL *Ipointer;
dINREAL *Qpointer;
DP a = pdisp[disp];
EnterCriticalSection(&a->SetAnalyzerSection);
Ipointer = &((a->I_samples[ss][LO])[a->IQin_index[ss][LO]]);
Qpointer = &((a->Q_samples[ss][LO])[a->IQin_index[ss][LO]]);
LeaveCriticalSection(&a->SetAnalyzerSection);
for (i = 0; i < a->buff_size; i++)
{
Ipointer[i] = pbuff[2 * i + 1];
Qpointer[i] = pbuff[2 * i + 0];
}
EnterCriticalSection(&a->SetAnalyzerSection);
EnterCriticalSection(&(a->BufferControlSection[ss][LO]));
if (a->have_samples[ss][LO] > a->max_writeahead)
{
//if we're receiving samples too much faster than we're consuming them, skip some
if ((a->IQout_index[ss][LO] += a->have_samples[ss][LO] - a->max_writeahead) >= a->bsize)
a->IQout_index[ss][LO] -= a->bsize;
a->have_samples[ss][LO] = a->max_writeahead;
}
if ((a->have_samples[ss][LO] += a->buff_size) >= a->size)
InterlockedBitTestAndSet(&(a->buff_ready[ss][LO]), 0);
LeaveCriticalSection(&(a->BufferControlSection[ss][LO]));
if((a->IQin_index[ss][LO] += a->buff_size) >= a->bsize) //REQUIRES buff_size IS A SUB-MULTIPLE OF SIZE OF INPUT SAMPLE BUFFS!
a->IQin_index[ss][LO] = 0;
if (!InterlockedAnd(&a->dispatcher, 1))
{
InterlockedBitTestAndSet(&a->dispatcher, 0);
LeaveCriticalSection(&a->SetAnalyzerSection);
_beginthread(sendbuf, 0, (void *)(uintptr_t)disp);
}
else
LeaveCriticalSection(&a->SetAnalyzerSection);
}
}
PORT
void Spectrum0(int run, int disp, int ss, int LO, double* pbuff)
{
if (run)
{
int i;
dINREAL *Ipointer;
dINREAL *Qpointer;
DP a = pdisp[disp];
EnterCriticalSection(&a->SetAnalyzerSection);
Ipointer = &((a->I_samples[ss][LO])[a->IQin_index[ss][LO]]);
Qpointer = &((a->Q_samples[ss][LO])[a->IQin_index[ss][LO]]);
LeaveCriticalSection(&a->SetAnalyzerSection);
for (i = 0; i < a->buff_size; i++)
{
Ipointer[i] = (dINREAL)pbuff[2 * i + 1];
Qpointer[i] = (dINREAL)pbuff[2 * i + 0];
}
EnterCriticalSection(&a->SetAnalyzerSection);
EnterCriticalSection(&(a->BufferControlSection[ss][LO]));
if (a->have_samples[ss][LO] > a->max_writeahead)
{
//if we're receiving samples too much faster than we're consuming them, skip some
if ((a->IQout_index[ss][LO] += a->have_samples[ss][LO] - a->max_writeahead) >= a->bsize)
a->IQout_index[ss][LO] -= a->bsize;
a->have_samples[ss][LO] = a->max_writeahead;
}
if ((a->have_samples[ss][LO] += a->buff_size) >= a->size)
InterlockedBitTestAndSet(&(a->buff_ready[ss][LO]), 0);
LeaveCriticalSection(&(a->BufferControlSection[ss][LO]));
if((a->IQin_index[ss][LO] += a->buff_size) >= a->bsize) //REQUIRES buff_size IS A SUB-MULTIPLE OF SIZE OF INPUT SAMPLE BUFFS!
a->IQin_index[ss][LO] = 0;
if (!InterlockedAnd(&a->dispatcher, 1))
{
InterlockedBitTestAndSet(&a->dispatcher, 0);
LeaveCriticalSection(&a->SetAnalyzerSection);
_beginthread(sendbuf, 0, (void *)(uintptr_t)disp);
}
else
LeaveCriticalSection(&a->SetAnalyzerSection);
}
}
PORT
void SetDisplayDetectorMode (int disp, int pixout, int mode)
{
DP a = pdisp[disp];
if (a->det_type[pixout] != mode)
{
EnterCriticalSection (&a->ResampleSection);
a->det_type[pixout] = mode;
LeaveCriticalSection (&a->ResampleSection);
}
}
PORT
void SetDisplayAverageMode (int disp, int pixout, int mode)
{
int i;
DP a = pdisp[disp];
if (a->av_mode[pixout] != mode)
{
EnterCriticalSection (&a->ResampleSection);
a->av_mode[pixout] = mode;
switch (mode)
{
case 1:
for (i = 0; i < dMAX_PIXELS; i++)
a->av_sum[pixout][i] = 1.0e-12;
break;
case 2:
a->avail_frames[pixout] = 0;
a->av_in_idx[pixout] = 0;
a->av_out_idx[pixout] = 0;
break;
case 3:
for (i = 0; i < dMAX_PIXELS; i++)
a->av_sum[pixout][i] = -160.0;
break;
default:
memset ((void *)a->av_sum[pixout], 0, sizeof(double) * dMAX_PIXELS);
break;
}
LeaveCriticalSection (&a->ResampleSection);
}
}
PORT
void SetDisplayNumAverage (int disp, int pixout, int num)
{
DP a = pdisp[disp];
if (a->num_average[pixout] != num)
{
EnterCriticalSection (&a->ResampleSection);
a->num_average[pixout] = num;
a->avail_frames[pixout] = 0;
a->av_in_idx[pixout] = 0;
a->av_out_idx[pixout] = 0;
LeaveCriticalSection (&a->ResampleSection);
}
}
PORT
void SetDisplayAvBackmult (int disp, int pixout, double mult)
{
DP a = pdisp[disp];
if (a->av_backmult[pixout] != mult)
{
EnterCriticalSection (&a->ResampleSection);
a->av_backmult[pixout] = mult;
LeaveCriticalSection (&a->ResampleSection);
}
}
PORT
void SetDisplaySampleRate (int disp, int rate)
{
DP a = pdisp[disp];
if (a->sample_rate != rate)
{
EnterCriticalSection (&a->ResampleSection);
a->sample_rate = rate;
CalcBandwidthNormalization (a);
LeaveCriticalSection (&a->ResampleSection);
}
}
PORT
void SetDisplayNormOneHz (int disp, int pixout, int norm)
{
DP a = pdisp[disp];
if (a->normalize[pixout] != norm)
{
EnterCriticalSection (&a->ResampleSection);
a->normalize[pixout] = norm;
LeaveCriticalSection (&a->ResampleSection);
}
}
PORT
double GetDisplayENB (int disp)
{
DP a = pdisp[disp];
double enb;
EnterCriticalSection(&a->SetAnalyzerSection);
enb = 1.0 / a->inv_enb;
LeaveCriticalSection(&a->SetAnalyzerSection);
return enb;
}