linmodem/v90.c

799 lines
20 KiB
C

/*
* Implementation of the V90 modulation/demodulation
*
* 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.
*
* This implementation is totally clean room. It was written by
* reading the V90 specification and by using basic signal processing
* knowledge.
*/
#include "lm.h"
#include "v90priv.h"
#include "v34priv.h"
#define DEBUG
/* Compute the average power of a given constellation :
nothing complicated here, except that each ucode must be counted
correctly. We took the algorithm of the spec. It could be very
optimized and simplified.
*/
int compute_power(V90EncodeState *s)
{
int m,v,i,j;
u64 p,n,r[6],k[6],a;
n = ((u64)1 << s->K) - 1;
for(i=0;i<6;i++) {
r[i] = n;
k[i] = n % s->M[i];
n = n / s->M[i];
}
p = 0;
a = 1;
for(i=0;i<6;i++) {
m = s->M[i];
for(j=0;j<m;j++) {
if (j < k[i]) {
n = a * (r[i+1] + 1);
} else if (j == k[i]) {
n = ((u64)1 << s->K) - a * (r[i] - r[i+1]);
} else {
n = a * r[i+1];
}
v = s->ucode_to_linear[s->m_to_ucode[i][j]];
p += v * v * n;
}
a = a * m;
}
return p / ((u64) 6 << s->K);
}
static const int sign_op[4] = { 0, 0x55, 0xff, 0xaa };
/* Select the best method for the current shaping frame
We find the the shortest path in a treillis of depth ld
*/
static void select_best_signs(V90EncodeState *s, int sp_frame_size, int pp)
{
int ucode_ptr, depth, i;
int state, lstate, lstate_min;
int t_min, x_min, y_min, v_min, w_min, sg;
int x1, y1, v1, w1, x, y, v, w;
u8 mem_l[2][TREILLIS_MAX_DEPTH+2],
mem_t[2][TREILLIS_MAX_DEPTH+2];
s16 mem_x[2][TREILLIS_MAX_DEPTH+2],
mem_y[2][TREILLIS_MAX_DEPTH+2],
mem_v[2][TREILLIS_MAX_DEPTH+2],
mem_w[2][TREILLIS_MAX_DEPTH+2];
/* save new pp signs */
s->pp[s->ucode_ptr] = pp;
/* Find best path starting at state Q */
ucode_ptr = (s->ucode_ptr - (sp_frame_size * s->ld)) & (UCODE_BUF_SIZE-1);
/* fill the treillis memory */
mem_t[s->Q][0] = s->t;
mem_x[s->Q][0] = s->x;
mem_y[s->Q][0] = s->y;
mem_v[s->Q][0] = s->v;
mem_w[s->Q][0] = 0;
for(depth = 0; depth <= s->ld; depth++) {
for(state = 0; state < 2; state++) {
w_min = 0x7fffffff;
lstate_min = t_min = x_min = y_min = v_min = 0;
/* we select the current state at the beginning of the treillis */
if (depth == 0)
lstate = s->Q;
else
lstate = 0;
do {
/* compute the signs */
sg = mem_t[lstate][depth] ^ s->pp[ucode_ptr];
/* apply the bit invertion method */
sg ^= sign_op[(state << 1) | lstate];
x1 = mem_x[lstate][depth];
y1 = mem_y[lstate][depth];
v1 = mem_v[lstate][depth];
w1 = mem_w[lstate][depth];
/* apply the spectral shaping filter to the frame */
for(i=0;i<sp_frame_size;i++) {
x = s->ucode_to_linear[s->ucode[(ucode_ptr + i) & (UCODE_BUF_SIZE-1)]];
if (((sg >> i) & 1) == 0)
x = -x;
y = x - ((s->b1 * x1 + s->a1 * y1) >> 6);
v = y - ((s->b2 * y1 + s->a2 * v1) >> 6);
w = ((v * v) >> 4) + w1;
x1 = x;
y1 = y;
v1 = v;
w1 = w;
}
/* best transition ? */
if (w1 < w_min) {
t_min = sg;
x_min = x1;
y_min = y1;
v_min = v1;
w_min = w1;
lstate_min = lstate;
}
lstate++;
} while (lstate < 2 && depth != 0);
/* selects the best transition */
mem_t[state][depth+1] = t_min;
mem_x[state][depth+1] = x_min;
mem_y[state][depth+1] = y_min;
mem_v[state][depth+1] = v_min;
mem_w[state][depth+1] = w_min;
mem_l[state][depth+1] = lstate_min;
}
ucode_ptr = (ucode_ptr + sp_frame_size) & (UCODE_BUF_SIZE-1);
}
/* now select the best next state by recursing thru the treillis */
if (mem_w[1][s->ld + 1] < mem_w[0][s->ld + 1])
state = 1;
else
state = 0;
for(depth=s->ld;depth>0;depth--)
state = mem_l[state][depth];
s->Q = state;
/* update current values */
s->t = mem_t[state][1];
s->x = mem_x[state][1];
s->y = mem_y[state][1];
s->v = mem_v[state][1];
s->ucode_ptr = (s->ucode_ptr + sp_frame_size) & (UCODE_BUF_SIZE - 1);
}
/*
* converts (S+K) data bits to 6 a/u law values (note: we output
* linear values which may be converted back to u/a law). A delay of
* (ld * frame_size) is introduced due to the shaping treillis.
*/
static void v90_encode_mapping_frame(V90EncodeState *s, s16 *samples, u8 *data)
{
int k, l, i, j, frame_size, nb_frames, p, signs;
u64 v;
int pv[3];
/* modulo mapping to ucodes */
v = 0;
for(i=0;i<s->K;i++) v |= (data[i+s->S] << i);
for(i=0;i<6;i++) {
k = v % s->M[i];
v /= s->M[i];
/* compute the corresponding ucode */
s->ucode[(s->ucode_ptr + i) & (UCODE_BUF_SIZE - 1)] = s->m_to_ucode[i][k];
}
/* computation of the sign of the ucodes */
switch(s->S) {
case 6:
default:
/* no redundancy & spectral shaping */
l = s->last_sign;
signs = 0;
for(i=0;i<6;i++) {
l = data[i] ^ l;
signs |= (l << i);
}
s->last_sign = l;
s->ucode_ptr = (s->ucode_ptr + 6) & (UCODE_BUF_SIZE - 1);
frame_size = 6;
goto skip_spectral_shaping;
case 5:
/* 1 bit of redundancy */
{
int pp1, pp3, pp5;
pp1 = data[0] ^ s->last_sign;
pp3 = data[2] ^ pp1;
pp5 = data[4] ^ pp3;
s->last_sign = pp5;
pv[0] = (pp1 << 1) | (data[1] << 2) | (pp3 << 3) |
(data[3] << 4) | (pp5 << 5);
frame_size = 6;
}
break;
case 4:
/* 2 bits of redundancy */
{
int pp1, pp4;
pp1 = data[0] ^ s->last_sign;
pp4 = data[2] ^ pp1;
s->last_sign = pp4;
pv[0] = (pp1 << 1) | (data[1] << 2);
pv[1] = (pp4 << 1) | (data[3] << 2);
frame_size = 3;
}
break;
case 3:
/* 3 bits of redundancy */
{
int pp1, pp3, pp5;
pp1 = data[0] ^ s->last_sign;
pp3 = data[1] ^ pp1;
pp5 = data[2] ^ pp3;
s->last_sign = pp5;
pv[0] = (pp1 << 1);
pv[1] = (pp3 << 1);
pv[2] = (pp5 << 1);
frame_size = 2;
}
break;
}
/* select the best signs for each frame (with a delay of ld) */
nb_frames = 6-s->S;
signs = 0;
for(i=0,j=0; i<nb_frames; i++, j+=frame_size) {
select_best_signs(s, frame_size, pv[i]);
l = s->t & ((1 << frame_size) - 1);
signs |= (l << j);
}
skip_spectral_shaping:
/* now the signs are computed, we can compute the pcm values from
the ucodes. It may be faster to use the convertion already done
for spectral shaping */
p = (s->ucode_ptr - 6 - (frame_size * s->ld)) & (UCODE_BUF_SIZE - 1);
for(i=0;i<6;i++) {
int x;
x = s->ucode_to_linear[s->ucode[p]];
if (((signs >> i) & 1) == 0)
x = -x;
samples[i] = x;
p = (p + 1) & (UCODE_BUF_SIZE - 1);
}
}
/*
* converts 6 a/u law samples to (S+K) data bits
*/
static void v90_decode_mapping_frame(V90DecodeState *s, u8 *data, s16 *samples)
{
s16 *tab;
int d1, d2, i, j, l, val, v, m_min, m_max, m;
u8 signs[6];
u64 bitbuf;
/* find signs & ucode */
bitbuf = 0;
for(j=5;j>=0;j--) {
val = samples[j];
if (val < 0) {
val = - val;
signs[j] = 0;
} else {
signs[j] = 1;
}
/* We look for the value closest to val with a binary search:
see Knuth section 6.2 exercice 1. */
tab = &s->m_to_linear[j][0];
m_min = 0;
m_max = s->M[j] - 1;
while (m_min <= m_max) {
m = (m_min + m_max) >> 1;
v = tab[m];
if (v == val)
goto found;
else if (val > v) {
m_max = m - 1;
} else {
m_min = m + 1;
}
}
d1 = tab[m_max] - val;
d2 = val - tab[m_min];
if (d1 < d2)
m = m_max;
else
m = m_min;
found:
/* now we update the modulo value */
bitbuf = bitbuf * s->M[j] + m;
}
/* output the K bits */
for(i=0;i<s->K;i++)
data[i + s->S] = (bitbuf >> i) & 1;
switch(s->S) {
default:
case 6:
/* no redundant bit */
l = s->last_sign;
for(i=0;i<6;i++) {
data[i] = l ^ signs[i];
l = signs[i];
}
s->last_sign = l;
break;
case 5:
/* 1 redundant bit */
{
int pp1, pp3, pp5, t0, pv0, Q1;
t0 = 0;
for(i=0;i<6;i++)
t0 |= (signs[i] << i);
pv0 = s->t ^ t0;
Q1 = (pv0 & 1) ^ s->Q;
pv0 ^= sign_op[s->Q | (Q1 << 1)] & 0x3f;
s->t = t0;
s->Q = Q1;
/* extract the data bits */
data[1] = (pv0 >> 2) & 1;
data[3] = (pv0 >> 4) & 1;
pp1 = ((pv0 >> 1) & 1);
pp3 = ((pv0 >> 3) & 1);
pp5 = ((pv0 >> 5) & 1);
data[0] = pp1 ^ s->last_sign;
data[2] = pp1 ^ pp3;
data[4] = pp3 ^ pp5;
s->last_sign = pp5;
}
break;
case 4:
/* 2 redundant bits */
{
int pp1, pp4, t0, t1, pv0, pv1, Q1, Q2;
t0 = signs[0] | (signs[1] << 1) | (signs[2] << 2);
t1 = signs[3] | (signs[4] << 1) | (signs[5] << 2);
pv0 = s->t ^ t0;
Q1 = (pv0 & 1) ^ s->Q;
pv0 ^= sign_op[s->Q | (Q1 << 1)] & 7;
pv1 = t0 ^ t1;
Q2 = (pv1 & 1) ^ Q1;
pv1 ^= sign_op[Q1 | (Q2 << 1)] & 7;
s->t = t1;
s->Q = Q2;
data[1] = (pv0 >> 2) & 1;
data[3] = (pv1 >> 2) & 1;
pp1 = (pv0 >> 1) & 1;
pp4 = (pv1 >> 1) & 1;
data[0] = pp1 ^ s->last_sign;
data[2] = pp4 ^ pp1;
s->last_sign = pp4;
}
break;
case 3:
/* 3 redundant bits */
{
int pp1, pp3, pp5, t0, t1, t2, pv0, pv1, pv2, Q1, Q2, Q3;
t0 = signs[0] | (signs[1] << 1);
t1 = signs[2] | (signs[3] << 1);
t2 = signs[4] | (signs[5] << 1);
pv0 = s->t ^ t0;
Q1 = (pv0 & 1) ^ s->Q;
pv0 ^= sign_op[s->Q | (Q1 << 1)] & 3;
pv1 = t0 ^ t1;
Q2 = (pv1 & 1) ^ Q1;
pv1 ^= sign_op[Q1 | (Q2 << 1)] & 3;
pv2 = t1 ^ t2;
Q3 = (pv2 & 1) ^ Q2;
pv2 ^= sign_op[Q2 | (Q3 << 1)] & 3;
s->t = t2;
s->Q = Q3;
pp1 = (pv0 >> 1) & 1;
pp3 = (pv1 >> 1) & 1;
pp5 = (pv2 >> 1) & 1;
data[0] = pp1 ^ s->last_sign;
data[1] = pp3 ^ pp1;
data[2] = pp5 ^ pp3;
s->last_sign = pp5;
}
break;
}
}
static void compute_constellation(V90EncodeState *s,
u8 m_index[6], u8 ucode_used[6][128])
{
int i,j,k,m;
/* the ucode are taken from the bigger value down to the smaller value */
for(j=0;j<6;j++) {
k = m_index[j];
m = 0;
for(i=127;i>=0;i--) {
if (ucode_used[k][i]) {
s->m_to_ucode[j][m] = i;
m++;
}
}
s->M[j] = m;
}
#ifdef DEBUG
for(j=0;j<6;j++) {
printf("M[%d]: ", j);
for(i=0;i<s->M[j];i++) {
printf("%3d ", s->m_to_ucode[j][i]);
}
printf("\n");
}
#endif
}
void v90_encode_init(V90EncodeState *s)
{
}
void v90_decode_init(V90DecodeState *s)
{
int i,j,m;
/* some test values (You can modify them to test the
modulator/demodulator) */
for(i=0;i<6;i++) {
for(j=0;j<16;j++)
s->ucode_used[i][10 + j * 6] = 1;
}
s->K = 4 * 6;
s->S = 5;
s->alaw = 1;
s->ld = 2;
s->a1 = (int) (0.1 * 64);
s->a2 = (int) (0.2 * 64);
s->b1 = (int) (-0.1 * 64);
s->b2 = (int) (0.1 * 64);
/* we suppose that all other values are set to zero */
/* compute the decode tables */
if (s->alaw)
s->ucode_to_linear = v90_alaw_ucode_to_linear;
else
s->ucode_to_linear = v90_ulaw_ucode_to_linear;
for(j=0;j<6;j++) {
m = 0;
for(i=127;i>=0;i--) {
if (s->ucode_used[j][i]) {
s->m_to_linear[j][m] = s->ucode_to_linear[i];
m++;
}
}
s->M[j] = m;
}
}
static u8 test_buf[1000];
/* send a CP frame (analog modem) */
static void v90_send_CP(V90DecodeState *s, int is_CP, int ack)
{
u8 *buf, *p;
int i, crc, drn;
buf = test_buf;
p = buf;
put_bits(&p, 17, 0x1ffff); /* frame sync */
put_bits(&p, 1, 0); /* start bit */
put_bits(&p, 1, 0); /* reserved */
put_bits(&p, 1, is_CP); /* 0=CPt 1=CP frame */
drn = s->K + s->S;
if (is_CP)
drn -= 20;
else
drn -= 8;
put_bits(&p, 5, drn); /* drn: speed */
put_bits(&p, 5, 0); /* reserved */
put_bits(&p, 1, 0); /* 1 if silence asked */
put_bits(&p, 2, 6 - s->S); /* Sr */
put_bits(&p, 1, ack); /* ack */
put_bits(&p, 1, 0); /* start bit */
put_bits(&p, 1, s->alaw); /* u/a law selection */
for(i=0;i<13;i++) {
put_bits(&p, 1, 1); /* speed (i+2) * 2400 supported (V34 part) */
}
put_bits(&p, 2, s->ld);
put_bits(&p, 1, 0); /* start bit */
put_bits(&p, 16, 0); /* RMS value for TRN1d */
put_bits(&p, 1, 0); /* start bit */
put_bits(&p, 8, s->a1 & 0xff); /* spectral shaping parameters */
put_bits(&p, 8, s->a2 & 0xff); /* spectral shaping parameters */
put_bits(&p, 1, 0); /* start bit */
put_bits(&p, 8, s->b1 & 0xff); /* spectral shaping parameters */
put_bits(&p, 8, s->b2 & 0xff); /* spectral shaping parameters */
put_bits(&p, 1, 0); /* start bit */
for(i=0;i<6;i++) {
if (i == 4)
put_bits(&p, 1, 0); /* start bit */
put_bits(&p, 4, 0); /* modulation index */
}
put_bits(&p, 1, 0); /* different different tx - D/A constellations ? */
put_bits(&p, 7, 0); /* reserved */
/* transmit the constellation */
for(i=0;i<128;i++) {
if ((i & 15) == 0)
put_bits(&p, 1, 0); /* start bit */
put_bits(&p, 1, s->ucode_used[0][i]);
}
/* CRC */
put_bits(&p, 1, 0); /* start bit */
crc = calc_crc(buf + 17, p - (buf+17));
put_bits(&p, 16, crc);
put_bits(&p, 3, 0); /* fill */
printf("CP size= %d\n", p - buf);
}
static int get_bit(u8 **pp)
{
u8 *p;
int v;
p = *pp;
v = *p++;
*pp = p;
return v;
}
static int get_bits(u8 *p, int n)
{
int i, v;
v = 0;
for(i=n-1;i>=0;i--) {
v |= *p++ << i;
}
return v;
}
/* parse the CP packet & compute the V90 parameters */
static void v90_parse_CP(V90EncodeState *s, u8 *buf)
{
u8 m_index[6];
u8 ucode_used[7][128];
int drn, i, j, k, nb_constellations;
/* now the whole packet is can be read */
s->S = 6 - get_bits(buf + 31, 2);
s->alaw = get_bits(buf + 35, 1);
s->ld = get_bits(buf + 49, 2);
s->a1 = (s8) get_bits(buf + 69, 8);
s->a2 = (s8) get_bits(buf + 77, 8);
s->b1 = (s8) get_bits(buf + 86, 8);
s->b2 = (s8) get_bits(buf + 94, 8);
for(i=0;i<4;i++)
m_index[i] = get_bits(buf + 103 + i * 4, 4);
for(i=0;i<2;i++)
m_index[4 + i] = get_bits(buf + 120 + i * 4, 4);
nb_constellations = 0;
for(i=0;i<6;i++) {
if (m_index[i] > nb_constellations) nb_constellations = m_index[i];
}
nb_constellations++;
if (buf[128])
nb_constellations++;
for(i=0;i<nb_constellations;i++) {
for(j=0;j<128;j++) {
k = i * 128 + j;
ucode_used[i][j] = buf[137 + (17 * (k >> 4)) + (k & 15)];
}
}
/* compute K */
drn = get_bits(buf + 20, 5);
if (buf[19])
drn += 20;
else
drn += 8;
s->K = drn - s->S;
if (s->alaw)
s->ucode_to_linear = v90_alaw_ucode_to_linear;
else
s->ucode_to_linear = v90_ulaw_ucode_to_linear;
printf("V90_received_CP:\n");
compute_constellation(s, m_index, ucode_used);
printf("S=%d K=%d R=%d alaw=%d ld=%d a1=%d a2=%d b1=%d b2=%d\n",
s->S, s->K, ((s->S + s->K) * 8000) / 6,
s->alaw, s->ld, s->a1, s->a2, s->b1, s->b2);
}
/* received & parse the CP packet */
static void v90_receive_CP(V90EncodeState *s)
{
u8 buf[1024], *p, *q;
int b, i, frame_index, one_count, frame_count, nb_constellations;
int crc1;
u8 m_index[6];
p = test_buf;
wait_sync:
one_count = 0;
while (get_bit(&p)) {
one_count++;
}
if (one_count != 17)
goto wait_sync;
#ifdef DEBUG
printf("got CP sync\n");
#endif
frame_index = 0;
frame_count = 8;
crc1 = 1;
q = buf + 17;
while (frame_index < frame_count) {
printf("%2d: ", frame_index);
*q++ = 0;
for(i=0;i<16;i++) {
b = get_bit(&p);
*q++ = b;
printf("%d", b);
}
printf("\n");
if (frame_index == 6) {
/* compute the number of constellation to read */
for(i=0;i<4;i++) {
m_index[i] = get_bits(buf + 103 + i * 4, 4);
}
for(i=0;i<2;i++) {
m_index[4 + i] = get_bits(buf + 120 + i * 4, 4);
}
nb_constellations = 0;
for(i=0;i<6;i++) {
if (m_index[i] > nb_constellations) nb_constellations = m_index[i];
}
nb_constellations++;
if (buf[128])
nb_constellations++;
frame_count += 8 * nb_constellations;
}
if (frame_index == (frame_count - 1)) {
/* check the crc (it must be zero because we include the crc itself) */
crc1 = calc_crc(buf + 17, q - (buf+17));
}
if (get_bit(&p) != 0) {
printf("start bit expected\n");
goto wait_sync;
}
frame_index++;
}
if (crc1 != 0)
goto wait_sync;
v90_parse_CP(s, buf);
}
/* simple test of V90 algebraic computations */
/* Note: if ld != 0 and 3 <= S <= 4, the delay introduced with data[][]
is not correct */
void V90_test(void)
{
int i,j,n,l;
V90EncodeState v90_enc;
V90DecodeState v90_dec;
u8 data[5][48], data1[48];
s16 samples[6];
/* init modem state */
memset(&v90_enc, 0, sizeof(v90_enc));
v90_encode_init(&v90_enc);
memset(&v90_dec, 0, sizeof(v90_dec));
v90_decode_init(&v90_dec);
/* send the CP sequence which contains the modulation parameters */
v90_send_CP(&v90_dec, 1, 0);
/* "receive" it ! */
v90_receive_CP(&v90_enc);
/* number of data bits per mapping frame */
n = v90_enc.S + v90_enc.K;
/* transmit & receive 1000 mapping frames */
memset(data, 0, sizeof(data));
l = 0;
for(i=0;i<1000;i++) {
for(j=0;j<n;j++) data[l][j] = random() & 1;
v90_encode_mapping_frame(&v90_enc, samples, data[l]);
// for(j=0;j<6;j++) samples[j] += (random() % 32) - 16;
printf("%4d: ", i);
for(j=0;j<6;j++) printf("%6d,", samples[j]);
printf("\n");
v90_decode_mapping_frame(&v90_dec, data1, samples);
l = (l + 1) % (v90_dec.ld+1);
for(j=0;j<n;j++) {
if (data[l][j] != data1[j]) {
printf("error mapping frame=%d bit=%d\n", i, j);
}
}
}
}