linmodem/v34gen.c

322 lines
8.0 KiB
C
Raw Permalink Blame History

/*
* V34 constant data generator
*
* Copyright (c) 1999,2000 Fabrice Bellard.
*
* This code is released under the GNU General Public License version
* 2. Please read the file COPYING to know the exact terms of the
* license.
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include "dsp.h"
#define V34_SAMPLE_RATE_NUM 10
#define V34_SAMPLE_RATE_DEN 3
#define V34_SAMPLE_RATE ((2400*V34_SAMPLE_RATE_NUM)/V34_SAMPLE_RATE_DEN)
#define V22_TX_FILTER_SIZE (20 * 40)
#define RC_FILTER_SIZE 40
void find_data_rot(int *data_ptr, int *rot_ptr, int x0, int y0)
{
int xx,yy,data,rot,x,y;
/* find the data & rotation */
for(data=0;data<4;data++) {
x = -3 + (data & 1) * 4;
y = -3 + (data >> 1) * 4;
for(rot=0;rot<4;rot++) {
if (x == x0 && y == y0) {
*data_ptr = data;
*rot_ptr = rot;
return;
}
/* rotate by 90 */
xx = y;
yy = -x;
x = xx;
y = yy;
}
}
}
/* 0 : rotation by 180, 1 = rotation by 90 */
int classify(int x[2][2])
{
int x0, y0, x1, y1,d0,d1,r0,r1;
x0 = 2 * x[0][0] - 3;
y0 = 2 * x[0][1] - 3;
x1 = 2 * x[1][0] - 3;
y1 = 2 * x[1][1] - 3;
find_data_rot(&d0,&r0,x0,y0);
find_data_rot(&d1,&r1,x1,y1);
if (((r0 - r1) & 1) == 0)
return 0;
else
return 1;
}
void gen_table(int nb_trans)
{
int Y[5],Yt[5], trans, ss[2][3], xx[2][2];
int res, i, y0;
printf("u8 trellis_trans_%d[256][4] = {\n",
nb_trans);
for(y0=0;y0<2;y0++) {
for(trans=0;trans<nb_trans;trans++) {
printf(" /* trans=%d y0=%d */\n", trans, y0);
Yt[1] = trans & 1;
Yt[2] = (trans >> 1) & 1;
Yt[4] = (trans >> 2) & 1;
Yt[3] = (trans >> 3) & 1;
for(xx[0][0] = 0; xx[0][0] < 4; xx[0][0]++)
for(xx[0][1] = 0; xx[0][1] < 4; xx[0][1]++)
for(xx[1][0] = 0; xx[1][0] < 4; xx[1][0]++)
for(xx[1][1] = 0; xx[1][1] < 4; xx[1][1]++) {
/* (<28> 9.6.3.1) find Y vector. We traducted the table into binary expressions */
for(i=0;i<2;i++) {
int x,y,y0,x0,y1,x1;
/* XXX: is it right to suppose that we use figure 9 as a periodic mapping ? */
x = xx[i][0];
y = xx[i][1];
x0 = x & 1;
x1 = ((x & 2) >> 1);
y0 = y & 1;
y1 = ((y & 2) >> 1);
ss[i][2] = x1 ^ y1 ^ y0 ^ x0;
ss[i][1] = y0;
ss[i][0] = y0 ^ x0;
}
/* table 13 traducted into binary operations */
Y[4] = ss[0][2] ^ ss[1][2];
Y[3] = ss[0][1];
Y[2] = ss[0][0];
Y[1] = (ss[0][0] & ~ss[1][0] & 1) ^ ss[0][1] ^ ss[1][1];
res = 0;
switch(nb_trans) {
case 4:
res = (Yt[1] == Y[1] &&
Yt[2] == Y[2]);
break;
case 8:
res = (Yt[1] == Y[1] &&
Yt[2] == Y[2] &&
Yt[4] == Y[4]);
break;
case 16:
res = (Yt[1] == Y[1] &&
Yt[2] == Y[2] &&
Yt[3] == Y[3] &&
Yt[4] == Y[4]);
break;
}
if (res && classify(xx) == y0) {
printf(" { %d, %d, %d, %d },\n",
xx[0][0],
xx[0][1],
xx[1][0],
xx[1][1]);
}
}
}
}
printf("};\n");
}
static u8 S_tab[6][8] = {
/* a, c, d1, e1, d2, e2, J, P */
{ 1, 1, 2, 3, 3, 4, 7, 12, }, /* S=2400 */
{ 8, 7, 3, 5, 2, 3, 8, 12, }, /* S=2743 */
{ 7, 6, 3, 5, 2, 3, 7, 14, }, /* S=2800 */
{ 5, 4, 3, 5, 2, 3, 7, 15, }, /* S=3000 */
{ 4, 3, 4, 7, 3, 5, 7, 16, }, /* S=3200 */
{10, 7, 4, 7, 4, 7, 8, 15, }, /* S=3429 */
};
/* this table depends on the sample rate. We have S=a1/c1 * V34_SAMPLE_RATE */
static u8 baud_tab[6][2] = {
/* a1, c1 */
{ 3, 10 },
{ 12, 35 },
{ 7, 20 },
{ 3, 8 },
{ 2, 5 },
{ 3, 7 },
};
#define FFT_SIZE 2048
/* build a square raised cosine nyquist filter of n coefficients
centered on f0, with coefficients alpha and beta.
*/
void build_sqr_nyquist_filter(float *filter,
float f0, float alpha, float beta, int n)
{
float f, f1, f2, val, tau, norm;
int i,j;
complex tab[FFT_SIZE];
f1 = (1.0 - beta) * alpha;
f2 = (1.0 + beta) * alpha;
tau = 0.5 / alpha;
norm = tau / sqrt(FFT_SIZE);
if (f0 != 0)
norm *= 0.5;
for(i=0;i<=FFT_SIZE/2;i++) {
f = i / (float)FFT_SIZE;
/* center on f0 */
f = fabs(f - f0);
if (f <= f1)
val = 1;
else if (f <= f2) {
val = 0.5 * (1.0 + cos((M_PI * tau / beta) * (f - f1)));
} else {
val = 0;
}
val = sqrt(val);
tab[i].re = val * norm;
tab[i].im = 0;
}
for(i=1;i<FFT_SIZE;i++) tab[FFT_SIZE - i] = tab[i];
fft_calc(tab, FFT_SIZE, 0);
j = FFT_SIZE - ((n-1)/2);
for(i=0;i<n;i++) {
filter[i] = tab[j].re;
if (++j == FFT_SIZE)
j = 0;
}
}
void write_filter(char *name, float *filter, int n)
{
int i;
printf("s16 %s[%d]=\n{\n",
name, n);
for(i=0;i<n;i++) {
printf("%6d, ", (int)(filter[i] * 0x4000));
if ((i % 8) == 7)
printf("\n");
}
printf("\n};\n");
}
int main(int argc, char **argv)
{
int i, j, n;
float filter[FFT_SIZE];
char buf[512];
printf("/* THIS SOURCE CODE IS AUTOMATICALLY GENERATED - DO NOT MODIFY */\n");
printf("/*\n"
" * V34 tables\n"
" * \n"
" * Copyright (c) 1999,2000 Fabrice Bellard.\n"
" *\n"
" * This code is released under the GNU General Public License version\n"
" * 2. Please read the file COPYING to know the exact terms of the\n"
" * license.\n"
" */\n");
printf("#include \"lm.h\"\n"
"#include \"v34priv.h\"\n"
"\n");
/* tables for trellis coded modulation */
gen_table(4);
gen_table(8);
gen_table(16);
/* rx filters which convert the 8000 Hz flow to (symbol_rate * 3)
with a nyquist filter */
for(j=0;j<6;j++) {
float symbol_rate, carrier, alpha, beta, freq;
symbol_rate = 2400.0 *
(float)S_tab[j][0] / (float)S_tab[j][1];
for(i=0;i<2;i++) {
if (i == 1 &&
S_tab[j][2] == S_tab[j][4] &&
S_tab[j][3] == S_tab[j][5])
break;
carrier = symbol_rate *
(float)S_tab[j][2 + 2*i] / (float)S_tab[j][3 + 2*i];
alpha = symbol_rate / (2.0 * symbol_rate * 3.0 * baud_tab[j][1]);
beta = 0.1;
freq = carrier / (symbol_rate * 3.0 * baud_tab[j][1]);
n = RC_FILTER_SIZE * baud_tab[j][1] + 1;
printf("/* S=%d carrier=%d alpha=%f beta=%f f0=%f */\n",
(int)rint(symbol_rate),
(int)rint(carrier),
alpha, beta, freq);
build_sqr_nyquist_filter(filter, freq, alpha, beta, n);
sprintf(buf, "v34_rx_filter_%d_%d",
(int)rint(symbol_rate),
(int)rint(carrier));
write_filter(buf, filter, n);
}
}
/* tx filters */
for(j=0;j<6;j++) {
float alpha, beta;
alpha = 1.0 / (2.0 * baud_tab[j][1]);
beta = 0.1;
n = RC_FILTER_SIZE * baud_tab[j][1] + 1;
build_sqr_nyquist_filter(filter, 0.0, alpha, beta, n);
sprintf(buf, "v34_rc_%d_filter", baud_tab[j][1]);
write_filter(buf, filter, n);
}
/* V22 filters */
/* 600 sym/s for 8000 Hz, beta=0.75 */
build_sqr_nyquist_filter(filter, 0.0, 1.0 / (2.0 * 40.0), 0.75,
V22_TX_FILTER_SIZE);
write_filter("v22_tx_filter", filter, V22_TX_FILTER_SIZE);
return 0;
}