mirror of
https://github.com/VK3FNG/soundmodem.git
synced 2025-12-06 03:01:59 +01:00
317 lines
9.2 KiB
C++
317 lines
9.2 KiB
C++
/*****************************************************************************/
|
|
|
|
/*
|
|
* gendf9icfilt.cc -- Compute DF9IC Hardware Modem Filter Curves.
|
|
*
|
|
* Copyright (C) 2003
|
|
* Thomas Sailer (t.sailer@alumni.ethz.ch)
|
|
*
|
|
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
/*****************************************************************************/
|
|
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <cstdarg>
|
|
#include <cstdio>
|
|
#include <complex>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
using namespace std;
|
|
|
|
#include "fft.hh"
|
|
#include "mat.hh"
|
|
#include "raisedcosine.h"
|
|
|
|
#include "getopt.h"
|
|
|
|
/* --------------------------------------------------------------------- */
|
|
|
|
template<typename T> complex<T> rxfilter(T freq)
|
|
{
|
|
complex<T> g1(T(1)/100e3,0);
|
|
complex<T> g2(T(1)/82e3,0);
|
|
complex<T> g3(T(1)/39e3,0);
|
|
complex<T> g4(T(1)/27e3,0);
|
|
freq *= 2 * M_PI * 9600;
|
|
complex<T> y1(0,freq*470e-9);
|
|
complex<T> y2(0,freq*1e-9);
|
|
complex<T> y3(0,freq*220e-12);
|
|
complex<T> y4(0,freq*1e-9);
|
|
|
|
return y1 * g2 * g3 * g4 /
|
|
(((g3 + y3) * g2 * (y1 + g1) + y3 * (y2 + g3) * (y1 + g1 + g2)) * (g4 + y4));
|
|
}
|
|
|
|
template<typename T> complex<T> txfilter(T freq)
|
|
{
|
|
complex<T> g1(T(1)/100,0);
|
|
complex<T> g2(T(1)/10e3,0);
|
|
complex<T> g3(T(1)/100e3,0);
|
|
complex<T> g4(T(1)/100e3,0);
|
|
complex<T> g5(T(1)/56e3,0);
|
|
complex<T> g6(T(1)/8.2e3,0);
|
|
complex<T> g7(T(1)/12e3,0);
|
|
freq *= 2 * M_PI * 9600;
|
|
complex<T> y1(0,freq*3.3e-9);
|
|
complex<T> y2(0,freq*470e-12);
|
|
complex<T> y3(0,freq*100e-12);
|
|
complex<T> y4(0,freq*1e-9);
|
|
|
|
return g1 * g4 * g5 * g6 /
|
|
(((g1 + g2 + g3 + y1) * (g5 + y3) * g4 +
|
|
(g1 + g2 + g3 + y1 + g4) * y3 * (y2 + g5)) * (y4 + g7));
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static void printtransferfunc(ostream& os, unsigned int nr, double over)
|
|
{
|
|
over /= nr;
|
|
os << "# name: rxf\n"
|
|
"# type: complex matrix\n"
|
|
"# rows: " << nr << "\n"
|
|
"# columns: 1\n";
|
|
for (unsigned int i = 0; i < nr; i++) {
|
|
complex<double> tf = rxfilter(i * over);
|
|
os << "(" << tf.real() << "," << tf.imag() << ")\n";
|
|
}
|
|
os << "# name: txf\n"
|
|
"# type: complex matrix\n"
|
|
"# rows: " << nr << "\n"
|
|
"# columns: 1\n";
|
|
for (unsigned int i = 0; i < nr; i++) {
|
|
complex<double> tf = txfilter(i * over);
|
|
os << "(" << tf.real() << "," << tf.imag() << ")\n";
|
|
}
|
|
os.flush();
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
template<typename T> static void matprintf(ostream& os, const char *name,
|
|
unsigned int size1, unsigned int stride1,
|
|
unsigned int size2, unsigned int stride2, const T *m)
|
|
{
|
|
os << "# name: " << name << "\n# type: matrix\n# rows: " << size1 << "\n# columns: " << size2 << "\n";
|
|
for (unsigned int i = 0; i < size1; i++) {
|
|
for (unsigned int j = 0; j < size2; j++)
|
|
os << " " << m[i*stride1 + j*stride2];
|
|
os << "\n";
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static double comptx(ostream& os, double *filt, const double *pulse, unsigned int filtlen, unsigned int pulselen,
|
|
unsigned int over, unsigned int odiv, double shift, unsigned int oct)
|
|
{
|
|
unsigned int rlen = 2 * filtlen + pulselen;
|
|
double C[rlen * filtlen];
|
|
double CT[rlen * filtlen];
|
|
double CTC[filtlen * filtlen];
|
|
double CTr[filtlen];
|
|
double Cf[rlen];
|
|
double r[rlen];
|
|
double e, e1;
|
|
double rcosalpha = 3.0 / 8;
|
|
int pidx;
|
|
unsigned int i, j;
|
|
|
|
for (i = 0; i < rlen; i++) {
|
|
pidx = i - 2 * filtlen + 1;
|
|
if (pidx >= (signed int)pulselen) {
|
|
rlen = i;
|
|
break;
|
|
}
|
|
for (j = 0; j < filtlen; j++, pidx += odiv) {
|
|
if (pidx < 0 || pidx >= (int)pulselen)
|
|
C[i*filtlen+j] = 0;
|
|
else
|
|
C[i*filtlen+j] = pulse[pidx];
|
|
}
|
|
}
|
|
for (i = 0; i < rlen; i++)
|
|
r[i] = raised_cosine_time((i - 0.5 * (rlen-1)) / (double)over + shift, rcosalpha);
|
|
if (oct) {
|
|
matprintf(os, "C", rlen, filtlen, filtlen, 1, C);
|
|
matprintf(os, "r", rlen, 1, 1, 1, r);
|
|
}
|
|
mtranspose(CT, C, rlen, filtlen);
|
|
mmul(CTC, CT, C, filtlen, rlen, filtlen);
|
|
mmul(CTr, CT, r, filtlen, rlen, 1);
|
|
mchol(CTC, CTr, filt, filtlen);
|
|
if (oct)
|
|
matprintf(os, "filt", filtlen, 1, 1, 1, filt);
|
|
mmul(Cf, C, filt, rlen, filtlen, 1);
|
|
for (i = 0, e = 0; i < rlen; i++) {
|
|
e1 = Cf[i] - r[i];
|
|
e += e1 * e1;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
static void printfcoeff(ostream& os, unsigned int fftsz, unsigned int over, unsigned int oct)
|
|
{
|
|
if (oct)
|
|
printtransferfunc(os, fftsz, over);
|
|
else
|
|
os << "/* this file is automatically generated, do not edit!! */\n\n";
|
|
complex<double> fftb[fftsz];
|
|
double rpulse[fftsz], tpulse[fftsz];
|
|
double dover = double(over) / fftsz;
|
|
double sum;
|
|
|
|
/* compute rx pulse */
|
|
for (unsigned int i = 1; i < fftsz/2; i++)
|
|
fftb[i] = rxfilter(i * dover);
|
|
fftb[0] = complex<double>(fftb[1].real(), 0);
|
|
fftb[fftsz/2] = 0;
|
|
for (unsigned int i = 1; i < fftsz/2; i++)
|
|
fftb[fftsz-i] = conj(fftb[i]);
|
|
if (oct) {
|
|
os << "# name: rxs\n"
|
|
"# type: complex matrix\n"
|
|
"# rows: " << fftsz << "\n"
|
|
"# columns: 1\n";
|
|
for (unsigned int i = 0; i < fftsz; i++)
|
|
os << "(" << fftb[i].real() << "," << fftb[i].imag() << ")\n";
|
|
}
|
|
fft_rif(fftb, fftsz, -1);
|
|
sum = 0;
|
|
for (unsigned int i = fftsz/2; i < fftsz; i++)
|
|
sum -= fftb[i].real();
|
|
sum *= double(2) / fftsz;
|
|
for (unsigned int i = 0; i < fftsz; i++)
|
|
rpulse[i] = dover * (fftb[i].real() + sum);
|
|
if (oct) {
|
|
os << "# name: rx\n"
|
|
"# type: matrix\n"
|
|
"# rows: " << fftsz << "\n"
|
|
"# columns: 1\n";
|
|
for (unsigned int i = 0; i < fftsz; i++)
|
|
os << rpulse[i] << "\n";
|
|
} else {
|
|
os << "#define FSKIC_RXOVER " << over << "\n";
|
|
os << "\nstatic float fskic_rxpulse[" << 4 * over << "] = {";
|
|
for (unsigned int i = 0;;) {
|
|
if (!(i & 3))
|
|
os << "\n\t";
|
|
os << " " << rpulse[i];
|
|
i++;
|
|
if (i >= 4 * over)
|
|
break;
|
|
os << ",";
|
|
}
|
|
os << "\n};\n\n";
|
|
}
|
|
/* compute tx pulse */
|
|
for (unsigned int i = 1; i < fftsz/2; i++)
|
|
fftb[i] = txfilter(i * dover);
|
|
fftb[0] = complex<double>(fftb[1].real(), 0);
|
|
fftb[fftsz/2] = 0;
|
|
for (unsigned int i = 1; i < fftsz/2; i++)
|
|
fftb[fftsz-i] = conj(fftb[i]);
|
|
if (oct) {
|
|
os << "# name: txs\n"
|
|
"# type: complex matrix\n"
|
|
"# rows: " << fftsz << "\n"
|
|
"# columns: 1\n";
|
|
for (unsigned int i = 0; i < fftsz; i++)
|
|
os << "(" << fftb[i].real() << "," << fftb[i].imag() << ")\n";
|
|
}
|
|
fft_rif(fftb, fftsz, -1);
|
|
sum = 0;
|
|
for (unsigned int i = fftsz/2; i < fftsz; i++)
|
|
sum -= fftb[i].real();
|
|
sum *= double(2) / fftsz;
|
|
for (unsigned int i = 0; i < fftsz; i++)
|
|
tpulse[i] = dover * (fftb[i].real() + sum);
|
|
if (oct) {
|
|
os << "# name: tx\n"
|
|
"# type: matrix\n"
|
|
"# rows: " << fftsz << "\n"
|
|
"# columns: 1\n";
|
|
for (unsigned int i = 0; i < fftsz; i++)
|
|
os << tpulse[i] << "\n";
|
|
}
|
|
/* compute tx compensation filter */
|
|
double filt[8 * over];
|
|
double shift = 0;
|
|
ostringstream oss;
|
|
double err = comptx(oss, filt, rpulse, sizeof(filt) / sizeof(filt[0]), 4 * over, over, 2, shift, oct);
|
|
double maxf = 0;
|
|
for (unsigned int i = 0; i < sizeof(filt) / sizeof(filt[0]); i++)
|
|
if (filt[i] > maxf) {
|
|
maxf = filt[i];
|
|
shift = i;
|
|
}
|
|
shift -= sizeof(filt) / sizeof(filt[0]) / double(2);
|
|
shift *= double(2) / over;
|
|
shift = -shift;
|
|
err = comptx(os, filt, rpulse, sizeof(filt) / sizeof(filt[0]), 4 * over, over, 2, shift, oct);
|
|
if (oct) {
|
|
os << "# name: err\n"
|
|
<< "# type: scalar\n"
|
|
<< err << "\n";
|
|
} else {
|
|
os << "#define FSKIC_TXOVER " << over / 2 << "\n";
|
|
os << "\nstatic float fskic_txpulse[" << sizeof(filt) / sizeof(filt[0]) - 2 * over << "] = {";
|
|
for (unsigned int i = over;;) {
|
|
if (!(i & 3))
|
|
os << "\n\t";
|
|
os << " " << filt[i];
|
|
i++;
|
|
if (i >= sizeof(filt) / sizeof(filt[0]) - 2 * over)
|
|
break;
|
|
os << ",";
|
|
}
|
|
os << "\n};\n\n";
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
static const struct option long_options[] = {
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
int c, err = 0;
|
|
unsigned int oct = 0;
|
|
|
|
while ((c = getopt_long(argc, argv, "o", long_options, NULL)) != EOF) {
|
|
switch (c) {
|
|
case 'o':
|
|
oct = 1;
|
|
break;
|
|
|
|
default:
|
|
err++;
|
|
break;
|
|
}
|
|
}
|
|
if (err) {
|
|
cerr << "usage: [-o]\n";
|
|
exit(1);
|
|
}
|
|
printfcoeff(cout, 2048, 16, oct);
|
|
return 0;
|
|
}
|