blob: 2fd8ea50decabac143512fa6b154861a814fc9e1 [file] [log] [blame]
/*SH0
*******************************************************************************
** **
** Copyright (c) 2008 - 2008 Quantenna Communications Inc **
** All Rights Reserved **
** **
** Author : Quantenna **
** Date : 12/04/08 **
** File : qdrv_math.c **
** Description : **
** **
*******************************************************************************
** **
** Redistribution and use in source and binary forms, with or without **
** modification, are permitted provided that the following conditions **
** are met: **
** 1. Redistributions of source code must retain the above copyright **
** notice, this list of conditions and the following disclaimer. **
** 2. Redistributions in binary form must reproduce the above copyright **
** notice, this list of conditions and the following disclaimer in the **
** documentation and/or other materials provided with the distribution. **
** 3. The name of the author may not be used to endorse or promote products **
** derived from this software without specific prior written permission. **
** **
** Alternatively, this software may be distributed under the terms of the **
** GNU General Public License ("GPL") version 2, or (at your option) any **
** later version as published by the Free Software Foundation. **
** **
** In the case this software is distributed under the GPL license, **
** you should have received a copy of the GNU General Public License **
** along with this software; if not, write to the Free Software **
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA **
** **
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR **
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES**
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. **
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, **
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT **
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,**
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY **
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT **
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF **
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **
** **
*******************************************************************************
EH0*/
#include <qtn/qtn_math.h>
#include <qtn/muc_phy_stats.h>
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b) (((a) > (b)) ? (b) : (a))
#endif
#define NUM_VALID_ONE_STREAM 1
#define NUM_VALID_TWO_STREAM 2
#define NUM_VALID_THREE_STREAM 3
#define NUM_VALID_FOUR_STREAM 4
#define NUM_BITS_FOR_FRACTION 4
#define NUM_BITS_EVM_MANT_ONE 2048
#define NUM_BITS_EVM_MANT_SHIFT 12
#define NUM_BITS_EVM_EXP_SHIFT 16
#define NUM_BITS_GUARD_TINY_SHIFT 7
static const u_int16_t lut_1024_10log10[] = {
#include "./log_table/1024_10log10_table.txt"
};
u_int8_t highest_one_bit_pos(u_int32_t val)
{
u_int32_t shift;
u_int32_t pos = 0;
if (val == 0) return 0;
shift = (val & 0xFFFF0000) ? 16 : 0; val >>= shift; pos |= shift;
shift = (val & 0xFF00 ) ? 8 : 0; val >>= shift; pos |= shift;
shift = (val & 0xF0 ) ? 4 : 0; val >>= shift; pos |= shift;
shift = (val & 0xC ) ? 2 : 0; val >>= shift; pos |= shift;
shift = (val & 0x2 ) ? 1 : 0; pos |= shift;
pos++;
return (u_int8_t) pos;
}
u_int32_t rshift_round(u_int32_t x, int shift)
{
u_int32_t z;
if (shift == 0)
return x;
z = x >> (shift - 1);
z += (z & 1);
z >>= 1;
return z;
}
int linear_to_10log10(u_int32_t x, int8_t nbit_frac_in, int8_t nbit_frac_out)
{
u_int8_t shift;
if (x <= 0)
return (int)0x80000001; // 10*log10(0) = -infinity
shift = MAX(highest_one_bit_pos(x) - 8, 0);
//printk("shift = %d , x = %d, lut_1024_10log10[(x >> shift) - 1] = %d \n\n", shift, x, lut_1024_10log10[(x >> shift) - 1]);
// y = round((1024*10*log10(x/2^shift) + (shift-nbit_frac_in)*1024*10*log10(2)) / 2^(10-nbit_frac_out))
// 49321 = round(16*1024*10*log10(2))
return rshift_round((int)(lut_1024_10log10[(x >> shift) - 1] + (((shift - nbit_frac_in) * 49321) >> 4)), 10 - nbit_frac_out) ;
}
int divide_by_16_x_10000(int x)
{
return (x * 625); //10000/16
};
u_int16_t conv_linear_mantissa(long val, short se)
{
u_int16_t evm_out;
short sht_count;
long shift_mask;
if (val == 0) return 0;
if (se < 1 || se > 32)
return 0; // se is in [1:32]
shift_mask = 0x000007FF; // MSB mask for 2048
if (se > NUM_BITS_EVM_MANT_SHIFT ) {
sht_count = se - NUM_BITS_EVM_MANT_SHIFT ;
shift_mask = shift_mask << sht_count;
evm_out = (u_int16_t) (val & shift_mask) >> sht_count;
} else {
sht_count = NUM_BITS_EVM_MANT_SHIFT - se ;
shift_mask = shift_mask >> sht_count;
evm_out = (u_int16_t) (val & shift_mask) << sht_count;
}
return (evm_out);
}
void average_evm_db(const uint32_t *evm_array, int n_sym, int *evm_int, int *evm_frac)
{
int man[4], exp[4], evm_exp_val[4];
int y, x, evm_4bit_fraction, fraction, db_sign = 0;
int k, valid_evm_cnt = 0, min_exp_val =0, guard_bit_shift = 0;
int evm_exp_sum=0, evm_exp_mul=0, evm_mant_sum=0, evm_mant_mul=0;
long evm_tmp_sum=0, evm_tmp_mul=0, linear_evm_val[4] ={0,0,0,0};
if (n_sym < 3)
return;
for ( k = 0; k < 4; k++) {
if (evm_array[k] != MUC_PHY_ERR_SUM_NOT_AVAIL) {// invalid EVM values
valid_evm_cnt++;
man[k] = (u_int32_t)(evm_array[k] >> 5);
exp[k] = (evm_array[k] - man[k] * 32);
man[k] += NUM_BITS_EVM_MANT_ONE;
if ( exp[k] > NUM_BITS_EVM_EXP_SHIFT )
linear_evm_val[k] = (man[k]) << (exp[k] - NUM_BITS_EVM_EXP_SHIFT );
else
linear_evm_val[k] = (man[k]) >> (NUM_BITS_EVM_EXP_SHIFT - exp[k]);
exp[k] = exp[k] - NUM_BITS_EVM_EXP_SHIFT;
//printk("reg=%x, man = %d, exp = %d, n_sym=%d\n", evm_array[k], man[k], exp[k], n_sym);
}
}
if ( valid_evm_cnt == NUM_VALID_ONE_STREAM ) { // only one stream
y = linear_to_10log10((man[0]), 0, NUM_BITS_FOR_FRACTION) +
((exp[0] - 11) * linear_to_10log10(2, 0, NUM_BITS_FOR_FRACTION)) ;
} else {
switch (valid_evm_cnt) {
case NUM_VALID_TWO_STREAM: //log(b+a) - log(a*b)
// get log(a + b)
evm_mant_sum = (long) (linear_evm_val[0]);
evm_mant_sum += (long) (linear_evm_val[1]);
evm_exp_sum = (int) highest_one_bit_pos(evm_mant_sum );
evm_mant_sum = conv_linear_mantissa(evm_mant_sum, evm_exp_sum);
evm_exp_sum -= NUM_BITS_EVM_MANT_SHIFT ;
// get log(a*b)
evm_tmp_mul = (long) (man[0]*man[1]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_exp_mul = (int) highest_one_bit_pos( evm_mant_mul );
evm_mant_mul = conv_linear_mantissa(evm_mant_mul, evm_exp_mul);
evm_exp_mul = (1) + (evm_exp_mul - NUM_BITS_EVM_MANT_SHIFT ); // 1 time shift 12 bits
evm_exp_mul += (exp[0]+exp[1]);
break;
case NUM_VALID_THREE_STREAM: // log(bc+ab+ac) -log(abc)
// get 3 terms product: bc
evm_tmp_mul = (long) (man[1]*man[2]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_exp_val[0] = 1+(exp[1]+exp[2]);
min_exp_val = MIN(evm_exp_val[0], min_exp_val);
linear_evm_val[0] = (evm_mant_mul);
// get 3 terms product: ac
evm_tmp_mul = (long) (man[0]*man[2]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_exp_val[1] = 1+(exp[0]+exp[2]);
min_exp_val = MIN(evm_exp_val[1], min_exp_val);
linear_evm_val[1] = (evm_mant_mul);
// get 3 terms product: ab
evm_tmp_mul = (long) (man[0]*man[1]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_exp_val[2] = 1+(exp[0]+exp[1]);
min_exp_val = MIN(evm_exp_val[2], min_exp_val);
linear_evm_val[2] = (evm_mant_mul);
// check the tiny input cases
guard_bit_shift = min_exp_val + NUM_BITS_EVM_MANT_SHIFT;
if ( guard_bit_shift < NUM_BITS_GUARD_TINY_SHIFT )
// min 7 bits for log table
guard_bit_shift = (-min_exp_val - NUM_BITS_EVM_MANT_SHIFT) + NUM_BITS_GUARD_TINY_SHIFT;
else guard_bit_shift = 0;
// left-shift up to bit 30
guard_bit_shift = MIN(guard_bit_shift, (30-NUM_BITS_EVM_MANT_SHIFT));
for ( k = 0; k < valid_evm_cnt ; k++) {
linear_evm_val[k] = (long)(linear_evm_val[k]) << guard_bit_shift;
if ( evm_exp_val[k] >= 0 )
linear_evm_val[k] <<= (evm_exp_val[k]);
else
linear_evm_val[k] >>= (-evm_exp_val[k]);
}
// get summatioon of t2 terms products: bc + ac +ab
evm_tmp_sum = (long) (linear_evm_val[0]);
evm_tmp_sum += (long) (linear_evm_val[1]);
evm_tmp_sum += (long) (linear_evm_val[2]);
evm_exp_sum = (int) highest_one_bit_pos( evm_tmp_sum );
evm_mant_sum = conv_linear_mantissa(evm_tmp_sum, evm_exp_sum);
evm_exp_sum -= (NUM_BITS_EVM_MANT_SHIFT + guard_bit_shift);
// get 3 term products : a*b*c
evm_tmp_mul = (long) (man[0]*man[1]) ;
evm_mant_mul = (int) ( evm_tmp_mul) >> NUM_BITS_EVM_MANT_SHIFT ;
evm_tmp_mul = (long) (evm_mant_mul * man[2]);
evm_mant_mul = (int) ( evm_tmp_mul) >> NUM_BITS_EVM_MANT_SHIFT ;
evm_exp_mul = (int) highest_one_bit_pos( evm_mant_mul );
evm_mant_mul = conv_linear_mantissa(evm_mant_mul, evm_exp_mul);
evm_exp_mul = (2) + (evm_exp_mul - NUM_BITS_EVM_MANT_SHIFT ); // 2 times shift 12 bits
evm_exp_mul += (exp[0]+exp[1]+exp[2]);
break;
case NUM_VALID_FOUR_STREAM: // log(bcd+acd+abd+abc) -log(abcd)
// get 3 terms product: bcd
evm_tmp_mul = (long) (man[1]*man[2]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_tmp_mul = (long) evm_mant_mul * (man[3]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_exp_val[0] = 2+(exp[1]+exp[2]+exp[3]);
min_exp_val = MIN(evm_exp_val[0], min_exp_val);
linear_evm_val[0] = (long) (evm_mant_mul);
// get 3 terms product: acd
evm_tmp_mul = (long) (man[0]*man[2]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_tmp_mul = (long) evm_mant_mul * (man[3]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_exp_val[1] = 2+(exp[0]+exp[2]+exp[3]);
min_exp_val = MIN(evm_exp_val[1], min_exp_val);
linear_evm_val[1] = (long) (evm_mant_mul);
// get 3 terms product: abd
evm_tmp_mul = (long) (man[0]*man[1]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_tmp_mul = (long) evm_mant_mul * (man[3]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_exp_val[2] = 2+(exp[0]+exp[1]+exp[3]);
min_exp_val = MIN(evm_exp_val[2], min_exp_val);
linear_evm_val[2] = (long) (evm_mant_mul);
// get 3 terms product: abc
evm_tmp_mul = (long) (man[0]*man[1]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_tmp_mul = (long) evm_mant_mul * (man[2]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_exp_val[3] = 2+(exp[0]+exp[1]+exp[2]);
min_exp_val = MIN(evm_exp_val[3], min_exp_val);
linear_evm_val[3] = (long) (evm_mant_mul);
// check the tiny input cases
guard_bit_shift = min_exp_val + NUM_BITS_EVM_MANT_SHIFT;
if ( guard_bit_shift < NUM_BITS_GUARD_TINY_SHIFT )
// min 7 bits for log table
guard_bit_shift = (-min_exp_val - NUM_BITS_EVM_MANT_SHIFT) + NUM_BITS_GUARD_TINY_SHIFT;
else guard_bit_shift = 0;
// left-shift up to bit 30
guard_bit_shift = MIN(guard_bit_shift, (30-NUM_BITS_EVM_MANT_SHIFT));
for ( k = 0; k < valid_evm_cnt ; k++) {
linear_evm_val[k] = (long)(linear_evm_val[k]) << guard_bit_shift;
if ( evm_exp_val[k] >= 0 )
linear_evm_val[k] <<= (evm_exp_val[k]);
else
linear_evm_val[k] >>= (-evm_exp_val[k]);
}
// summation of 3 term products : bcd+acd+abd+abc
evm_mant_sum = (long) (linear_evm_val[0]);
evm_mant_sum += (long) (linear_evm_val[1]);
evm_mant_sum += (long) (linear_evm_val[2]);
evm_mant_sum += (long) (linear_evm_val[3]);
evm_exp_sum = (int) highest_one_bit_pos( evm_mant_sum );
evm_mant_sum = conv_linear_mantissa(evm_mant_sum, evm_exp_sum);
evm_exp_sum -= (NUM_BITS_EVM_MANT_SHIFT + guard_bit_shift);
// get 4 terms products : a*b*c*d
evm_tmp_mul = (long) (man[0]*man[1]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_tmp_mul = (long) evm_mant_mul * (man[2]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_tmp_mul = (long) evm_mant_mul * (man[3]);
evm_mant_mul = (int) evm_tmp_mul >> NUM_BITS_EVM_MANT_SHIFT ;
evm_exp_mul = (int) highest_one_bit_pos( evm_mant_mul );
evm_mant_mul = conv_linear_mantissa(evm_mant_mul, evm_exp_mul);
evm_exp_mul = (3) + (evm_exp_mul - NUM_BITS_EVM_MANT_SHIFT ); // 3 times shift 12 bits
evm_exp_mul += (exp[0]+exp[1]+exp[2]+exp[3]);
break;
}
//printk("sum = (%d, %d)\n", evm_mant_sum, evm_exp_sum);
//printk("prod = (%d, %d)\n", (int) evm_mant_mul, evm_exp_mul);
y = -linear_to_10log10((evm_mant_sum + NUM_BITS_EVM_MANT_ONE), 0, NUM_BITS_FOR_FRACTION);
y -= (evm_exp_sum) * linear_to_10log10(2, 0, NUM_BITS_FOR_FRACTION);
y += linear_to_10log10((evm_mant_mul + NUM_BITS_EVM_MANT_ONE), 0, NUM_BITS_FOR_FRACTION);
y += (evm_exp_mul) * linear_to_10log10(2, 0, NUM_BITS_FOR_FRACTION);
}
x = linear_to_10log10(n_sym-3, 0, NUM_BITS_FOR_FRACTION);
evm_4bit_fraction = y - x;
// fix bug: negative dB shift error
if ( evm_4bit_fraction < 0 ) {
db_sign = 1;
evm_4bit_fraction = ABS(evm_4bit_fraction);
}
y = (evm_4bit_fraction >> NUM_BITS_FOR_FRACTION);
fraction = evm_4bit_fraction - (y << NUM_BITS_FOR_FRACTION);
if ( db_sign ==1 )
*evm_int = -y;
else
*evm_int = y;
*evm_frac = divide_by_16_x_10000(fraction);
//printk("int = %d, frac = %d\n", *evm_int, *evm_frac);
}
void convert_evm_db(u_int32_t evm_reg, int n_sym, int *evm_int, int *evm_frac)
{
int man, exp, log_table_index, y, x, evm_4bit_fraction, fraction,db_sign = 0;
if (n_sym < 3)
return;
man = (u_int32_t)(evm_reg >> 5);
exp = (evm_reg - man * 32);
//printk("man = %d, exp = %d\n", man, exp);
log_table_index = (2048 + man) ;
y = linear_to_10log10(log_table_index, 0, NUM_BITS_FOR_FRACTION);
y += ((exp - 16) * linear_to_10log10(2, 0, NUM_BITS_FOR_FRACTION));
y -= (11 * linear_to_10log10(2, 0, NUM_BITS_FOR_FRACTION));
x = linear_to_10log10(n_sym-3, 0, NUM_BITS_FOR_FRACTION);
//printk("y = %d, x = %d\n", y, x);
evm_4bit_fraction = y - x;
// fix bug: negative dB shift error
if ( evm_4bit_fraction < 0 ) {
db_sign = 1;
evm_4bit_fraction = ABS(evm_4bit_fraction);
}
y = (evm_4bit_fraction >> NUM_BITS_FOR_FRACTION);
fraction = evm_4bit_fraction - (y << NUM_BITS_FOR_FRACTION);
if ( db_sign==1 )
*evm_int = -y;
else
*evm_int = y;
*evm_frac = divide_by_16_x_10000(fraction);
//printk("int = %d, frac = %d\n", *evm_int, *evm_frac);
}
#ifdef FLOAT_SUPPORT
inline double pow_int(int x, int y)
{
unsigned int n;
double z;
if (y >= 0)
n = (unsigned int)y;
else
n = (unsigned int)(-y);
for (z = 1; ; x *= x) {
if ((n & 1) != 0)
z *= x;
if ((n >>= 1) == 0)
return (y < 0 ? 1 / z : z);
}
};
#endif //#ifdef FLOAT_SUPPORT