460 lines
15 KiB
C
460 lines
15 KiB
C
/* utilities.c
|
|
|
|
This file is part of a program that implements a Software-Defined Radio.
|
|
|
|
Copyright (C) 2013, 2019 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
|
|
|
|
*/
|
|
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#include "comm.h"
|
|
|
|
/********************************************************************************************************
|
|
* *
|
|
* Required Utilities *
|
|
* *
|
|
********************************************************************************************************/
|
|
|
|
PORT
|
|
void *malloc0 (int size)
|
|
{
|
|
int alignment = 16;
|
|
void* p = _aligned_malloc (size, alignment);
|
|
if (p != 0) memset (p, 0, size);
|
|
return p;
|
|
}
|
|
|
|
#if !defined(linux) && !defined(__APPLE__)
|
|
// Exported calls
|
|
|
|
PORT void
|
|
*NewCriticalSection()
|
|
{ // used by VAC
|
|
LPCRITICAL_SECTION cs_ptr;
|
|
cs_ptr = (LPCRITICAL_SECTION)calloc (1,sizeof (CRITICAL_SECTION));
|
|
return (void *)cs_ptr;
|
|
}
|
|
|
|
PORT void
|
|
DestroyCriticalSection (LPCRITICAL_SECTION cs_ptr)
|
|
{ // used by VAC
|
|
free ((char *)cs_ptr);
|
|
}
|
|
|
|
#endif
|
|
|
|
/********************************************************************************************************
|
|
* *
|
|
* Test & Debug Utilities *
|
|
* *
|
|
********************************************************************************************************/
|
|
|
|
void print_impulse (const char* filename, int N, double* impulse, int rtype, int pr_mode)
|
|
{
|
|
int i;
|
|
FILE* file;
|
|
if (pr_mode == 0)
|
|
file = fopen (filename, "w");
|
|
else
|
|
file = fopen (filename, "a");
|
|
if (rtype == 0)
|
|
for (i = 0; i < N; i++)
|
|
fprintf (file, "%.17e\n", impulse[i]);
|
|
else
|
|
for (i = 0; i < N; i++)
|
|
fprintf (file, "%.17e\t%.17e\n", impulse[2 * i + 0], impulse[2 * i + 1]);
|
|
fprintf (file, "\n\n\n\n");
|
|
fflush (file);
|
|
fclose (file);
|
|
}
|
|
|
|
PORT
|
|
void analyze_bandpass_filter (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale)
|
|
{
|
|
double* linphase_imp = (double *) malloc0 (N * sizeof (complex));
|
|
double* minphase_imp = (double *) malloc0 (N * sizeof (complex));
|
|
linphase_imp = fir_bandpass (N, f_low, f_high, samplerate, wintype, rtype, scale);
|
|
mp_imp (N, linphase_imp, minphase_imp, 16, 0);
|
|
print_impulse ("linear_phase_impulse.txt", N, linphase_imp, 1, 0);
|
|
print_impulse ("minimum_phase_impulse.txt", N, minphase_imp, 1, 0);
|
|
_aligned_free (minphase_imp);
|
|
_aligned_free (linphase_imp);
|
|
}
|
|
|
|
void print_peak_val (const char* filename, int N, double* buff, double thresh)
|
|
{
|
|
int i;
|
|
static unsigned int seqnum;
|
|
double peak = 0.0;
|
|
FILE* file;
|
|
for (i = 0; i < N; i++)
|
|
if (buff[i] > peak) peak = buff[i];
|
|
if (peak >= thresh)
|
|
{
|
|
file = fopen(filename, "a");
|
|
fprintf(file, "%d\t\t%.17e\n", seqnum, peak);
|
|
fflush(file);
|
|
fclose(file);
|
|
}
|
|
seqnum++;
|
|
}
|
|
|
|
void print_peak_env (const char* filename, int N, double* buff, double thresh)
|
|
{
|
|
int i;
|
|
static unsigned int seqnum;
|
|
double peak = 0.0;
|
|
double new_peak;
|
|
FILE* file;
|
|
for (i = 0; i < N; i++)
|
|
{
|
|
new_peak = sqrt (buff[2 * i + 0] * buff[2 * i + 0] + buff[2 * i + 1] * buff[2 * i + 1]);
|
|
if (new_peak > peak) peak = new_peak;
|
|
}
|
|
if (peak >= thresh)
|
|
{
|
|
file = fopen (filename, "a");
|
|
fprintf (file, "%d\t\t%.17e\n", seqnum, peak);
|
|
fflush (file);
|
|
fclose (file);
|
|
}
|
|
seqnum++;
|
|
}
|
|
|
|
void print_peak_env_f2 (const char* filename, int N, float* Ibuff, float* Qbuff)
|
|
{
|
|
int i;
|
|
double peak = 0.0;
|
|
double new_peak;
|
|
FILE* file = fopen (filename, "a");
|
|
for (i = 0; i < N; i++)
|
|
{
|
|
new_peak = sqrt (Ibuff[i] * Ibuff[i] + Qbuff[i] * Qbuff[i]);
|
|
if (new_peak > peak) peak = new_peak;
|
|
}
|
|
fprintf (file, "%.17e\n", peak);
|
|
fflush (file);
|
|
fclose (file);
|
|
}
|
|
|
|
void print_iqc_values (const char* filename, int state, double env_in, double I, double Q, double ym, double yc, double ys, double thresh)
|
|
{
|
|
static unsigned int seqnum;
|
|
double env_out;
|
|
FILE* file;
|
|
env_out = sqrt (I * I + Q * Q);
|
|
if (env_out > thresh)
|
|
{
|
|
file = fopen(filename, "a");
|
|
if (seqnum == 0)
|
|
fprintf(file, "seqnum\tstate\tenv_in\t\tenv_out\t\tym\t\tyc\t\tys\n");
|
|
fprintf(file, "%d\t%d\t%f\t%f\t%f\t%f\t%f\n", seqnum, state, env_in, env_out, ym, yc, ys);
|
|
fflush(file);
|
|
fclose(file);
|
|
seqnum++;
|
|
}
|
|
}
|
|
|
|
PORT
|
|
void print_buffer_parameters (const char* filename, int channel)
|
|
{
|
|
IOB a = ch[channel].iob.pc;
|
|
FILE* file = fopen (filename, "a");
|
|
fprintf (file, "channel = %d\n", channel);
|
|
fprintf (file, "in_size = %d\n", a->in_size);
|
|
fprintf (file, "r1_outsize = %d\n", a->r1_outsize);
|
|
fprintf (file, "r1_size = %d\n", a->r1_size);
|
|
fprintf (file, "r2_size = %d\n", a->r2_size);
|
|
fprintf (file, "out_size = %d\n", a->out_size);
|
|
fprintf (file, "r2_insize = %d\n", a->r2_insize);
|
|
fprintf (file, "r1_active_buffsize = %d\n", a->r1_active_buffsize);
|
|
fprintf (file, "f2_active_buffsize = %d\n", a->r2_active_buffsize);
|
|
fprintf (file, "r1_inidx = %d\n", a->r1_inidx);
|
|
fprintf (file, "r1_outidx = %d\n", a->r1_outidx);
|
|
fprintf (file, "r1_unqueuedsamps = %d\n", a->r1_unqueuedsamps);
|
|
fprintf (file, "r2_inidx = %d\n", a->r2_inidx);
|
|
fprintf (file, "r2_outidx = %d\n", a->r2_outidx);
|
|
fprintf (file, "r2_havesamps = %d\n", a->r2_havesamps);
|
|
fprintf (file, "in_rate = %d\n", ch[channel].in_rate);
|
|
fprintf (file, "dsp_rate = %d\n", ch[channel].dsp_rate);
|
|
fprintf (file, "out_rate = %d\n", ch[channel].out_rate);
|
|
fprintf (file, "\n");
|
|
fflush (file);
|
|
fclose (file);
|
|
}
|
|
|
|
void print_meter (const char* filename, double* meter, int enum_av, int enum_pk, int enum_gain)
|
|
{
|
|
FILE* file = fopen (filename, "a");
|
|
if (enum_gain >= 0)
|
|
fprintf (file, "%.4e\t%.4e\t%.4e\n", meter[enum_av], meter[enum_pk], meter[enum_gain]);
|
|
else
|
|
fprintf (file, "%.4e\t%.4e\n", meter[enum_av], meter[enum_pk]);
|
|
fflush (file);
|
|
fclose (file);
|
|
}
|
|
|
|
void print_message (const char* filename, const char* message, int p0, int p1, int p2)
|
|
{
|
|
FILE* file = fopen (filename, "a");
|
|
const char* msg = message;
|
|
fprintf (file, "%s %d %d %d\n", msg, p0, p1, p2);
|
|
fflush (file);
|
|
fclose (file);
|
|
}
|
|
|
|
void print_window_gain (const char* filename, int wintype, double inv_coherent_gain, double inherent_power_gain)
|
|
{
|
|
FILE* file = fopen (filename, "a");
|
|
double enb = inherent_power_gain * inv_coherent_gain * inv_coherent_gain;
|
|
switch (wintype)
|
|
{
|
|
case 0:
|
|
fprintf (file, "Rectangular %.4f\t%.4f\t%.4f\n", inv_coherent_gain, inherent_power_gain, enb);
|
|
break;
|
|
case 1:
|
|
fprintf (file, "Blackman-Harris 4-term %.4f\t%.4f\t%.4f\n", inv_coherent_gain, inherent_power_gain, enb);
|
|
break;
|
|
case 2:
|
|
fprintf (file, "Hann %.4f\t%.4f\t%.4f\n", inv_coherent_gain, inherent_power_gain, enb);
|
|
break;
|
|
case 3:
|
|
fprintf (file, "Flat Top %.4f\t%.4f\t%.4f\n", inv_coherent_gain, inherent_power_gain, enb);
|
|
break;
|
|
case 4:
|
|
fprintf (file, "Hamming %.4f\t%.4f\t%.4f\n", inv_coherent_gain, inherent_power_gain, enb);
|
|
break;
|
|
case 5:
|
|
fprintf (file, "Kaiser %.4f\t%.4f\t%.4f\n", inv_coherent_gain, inherent_power_gain, enb);
|
|
break;
|
|
case 6:
|
|
fprintf (file, "Blackman-Harris 7-term %.4f\t%.4f\t%.4f\n", inv_coherent_gain, inherent_power_gain, enb);
|
|
break;
|
|
}
|
|
fflush (file);
|
|
fclose (file);
|
|
}
|
|
|
|
void print_deviation (const char* filename, double dpmax, double rate)
|
|
{
|
|
FILE* file = fopen (filename, "a");
|
|
double peak = dpmax * rate / TWOPI;
|
|
fprintf (file, "Peak Dev = %.4f\n", peak);
|
|
fflush (file);
|
|
fclose (file);
|
|
}
|
|
|
|
void __cdecl CalccPrintSamples (void *pargs)
|
|
{
|
|
int i;
|
|
double env_tx, env_rx;
|
|
int channel = (int)(uintptr_t)pargs;
|
|
CALCC a = txa[channel].calcc.p;
|
|
FILE* file = fopen("samples.txt", "w");
|
|
fprintf (file, "\n");
|
|
for (i = 0; i < a->nsamps; i++)
|
|
{
|
|
env_tx = sqrt(a->txs[2 * i + 0] * a->txs[2 * i + 0] + a->txs[2 * i + 1] * a->txs[2 * i + 1]);
|
|
env_rx = sqrt(a->rxs[2 * i + 0] * a->rxs[2 * i + 0] + a->rxs[2 * i + 1] * a->rxs[2 * i + 1]);
|
|
fprintf(file, "%.12f %.12f %.12f %.12f %.12f %.12f\n",
|
|
a->txs[2 * i + 0], a->txs[2 * i + 1], env_tx,
|
|
a->rxs[2 * i + 0], a->rxs[2 * i + 1], env_rx);
|
|
}
|
|
fflush(file);
|
|
fclose(file);
|
|
_endthread();
|
|
}
|
|
|
|
void doCalccPrintSamples(int channel)
|
|
{ // no sample buffering - use in single cal mode
|
|
_beginthread(CalccPrintSamples, 0, (void *)(uintptr_t)channel);
|
|
}
|
|
|
|
void print_anb_parms (const char* filename, ANB a)
|
|
{
|
|
FILE* file = fopen (filename, "a");
|
|
fprintf (file, "Run = %d\n", a->run);
|
|
fprintf (file, "Buffer Size = %d\n", a->buffsize);
|
|
fprintf (file, "Sample Rate = %d\n", (int)a->samplerate);
|
|
fprintf (file, "Threshold = %.6f\n", a->threshold);
|
|
fprintf (file, "BackTau = %.6f\n", a->backtau);
|
|
fprintf (file, "BackMult = %.6f\n", a->backmult);
|
|
fprintf (file, "Tau = %.6f\n", a->tau);
|
|
fflush (file);
|
|
fclose (file);
|
|
}
|
|
|
|
#if !defined(linux) && !defined(__APPLE__)
|
|
// Audacity: Import Raw Data, Signed 32-bit PCM, Little-endian, Mono/Stereo per mode selection, 48K rate
|
|
|
|
int audiocount = 0;
|
|
int* data;
|
|
int ready = 0;
|
|
int done = 0;
|
|
|
|
void WriteAudioFile(void* arg)
|
|
{
|
|
byte* dat = (byte *)arg;
|
|
FILE* file = fopen("AudioFile", "wb");
|
|
|
|
// reverse bits of each byte (possibly needed on some platforms)
|
|
// int i;
|
|
// byte b;
|
|
// for (i = 0; i < 4 * audiocount; i++)
|
|
// {
|
|
// b = dat[i];
|
|
// b = ((b >> 1) & 0x55) | ((b << 1) & 0xaa);
|
|
// b = ((b >> 2) & 0x33) | ((b << 2) & 0xcc);
|
|
// b = ((b >> 4) & 0x0f) | ((b << 4) & 0xf0);
|
|
// dat[i] = b;
|
|
// }
|
|
fwrite((int *)dat, sizeof(int), audiocount, file);
|
|
fflush(file);
|
|
fclose(file);
|
|
free(data);
|
|
_endthread();
|
|
}
|
|
|
|
void WriteAudioWDSP (double seconds, int rate, int size, double* indata, int mode, double gain)
|
|
{
|
|
// seconds - number of seconds of audio to record
|
|
// rate - sample rate
|
|
// size - number of complex samples
|
|
// indata - pointer to data
|
|
static int n;
|
|
int i;
|
|
const double conv = 2147483647.0 * gain;
|
|
if (!ready)
|
|
{
|
|
if (mode != 3)
|
|
n = (int)(seconds * rate);
|
|
else
|
|
n = 2 * (int)(seconds * rate);
|
|
data = (int *)calloc(n, sizeof(int));
|
|
ready = 1;
|
|
}
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
if (audiocount < n)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case 0: // I only (mono)
|
|
data[audiocount++] = (int)(conv * indata[2 * i + 0]);
|
|
break;
|
|
case 1: // Q only (mono)
|
|
data[audiocount++] = (int)(conv * indata[2 * i + 1]);
|
|
break;
|
|
case 2: // envelope (mono)
|
|
data[audiocount++] = (int)(conv * sqrt(indata[2 * i + 0] * indata[2 * i + 0] + indata[2 * i + 1] * indata[2 * i + 1]));
|
|
break;
|
|
case 3: // complex samples (stereo)
|
|
data[audiocount++] = (int)(conv * indata[2 * i + 0]);
|
|
data[audiocount++] = (int)(conv * indata[2 * i + 1]);
|
|
break;
|
|
case 4: // double samples (mono)
|
|
data[audiocount++] = (int)(conv * indata[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ((audiocount == n) && !done)
|
|
{
|
|
done = 1;
|
|
_beginthread(WriteAudioFile, 0, (void *)data);
|
|
}
|
|
}
|
|
|
|
void WriteScaledAudioFile (void* arg)
|
|
{
|
|
typedef struct
|
|
{
|
|
int n;
|
|
double* ddata;
|
|
} *dstr;
|
|
dstr dstruct = (dstr)arg;
|
|
|
|
FILE* file = fopen("AudioFile", "wb");
|
|
int i;
|
|
double max = 0.0;
|
|
double abs_val;
|
|
const double conv = 2147483647.0;
|
|
int *idata = (int *) malloc0 (dstruct->n * sizeof (int));
|
|
|
|
for (i = 0; i < dstruct->n; i++)
|
|
{
|
|
abs_val = fabs (dstruct->ddata[i]);
|
|
if (abs_val > max)
|
|
max = abs_val;
|
|
}
|
|
for (i = 0; i < dstruct->n; i++)
|
|
idata[i] = dstruct->ddata[i] >= 0.0 ? (int)floor(conv * dstruct->ddata[i] / max + 0.5) : (int)ceil(conv * dstruct->ddata[i] / max - 0.5);
|
|
|
|
fwrite(idata, sizeof(int), dstruct->n, file);
|
|
fflush(file);
|
|
fclose(file);
|
|
|
|
_aligned_free (dstruct->ddata);
|
|
_aligned_free (dstruct);
|
|
_aligned_free (idata);
|
|
_endthread();
|
|
}
|
|
|
|
void WriteScaledAudio (
|
|
double seconds, // number of seconds of audio to record
|
|
int rate, // sample rate
|
|
int size, // incoming buffer size
|
|
double* indata ) // pointer to incoming data buffer
|
|
{
|
|
static int ready;
|
|
typedef struct
|
|
{
|
|
int n;
|
|
double* ddata;
|
|
} dstr, *DSTR;
|
|
static DSTR dstruct;
|
|
|
|
static int count, complete;
|
|
int i;
|
|
|
|
if (!ready)
|
|
{
|
|
dstruct = (DSTR) malloc0 (sizeof (dstr));
|
|
dstruct->n = 2 * (int)(seconds * rate);
|
|
dstruct->ddata = (double *) malloc0 (dstruct->n * sizeof (double));
|
|
ready = 1;
|
|
}
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
if (count < dstruct->n)
|
|
{
|
|
dstruct->ddata[count++] = indata[2 * i + 0];
|
|
dstruct->ddata[count++] = indata[2 * i + 1];
|
|
}
|
|
}
|
|
if ((count >= dstruct->n) && !complete)
|
|
{
|
|
complete = 1;
|
|
_beginthread (WriteScaledAudioFile, 0, (void *)dstruct);
|
|
}
|
|
}
|
|
#endif
|