/*************************** Fraunhofer IIS FDK Tools ********************** (C) Copyright Fraunhofer IIS (2005) All Rights Reserved Please be advised that this software and/or program delivery is Confidential Information of Fraunhofer and subject to and covered by the Fraunhofer IIS Software Evaluation Agreement between Google Inc. and Fraunhofer effective and in full force since March 1, 2012. You may use this software and/or program only under the terms and conditions described in the above mentioned Fraunhofer IIS Software Evaluation Agreement. Any other and/or further use requires a separate agreement. $Id$ Author(s): Haricharan Lakshman, Manuel Jander Description: Trigonometric functions fixed point fractional implementation. This software and/or program is protected by copyright law and international treaties. Any reproduction or distribution of this software and/or program, or any portion of it, may result in severe civil and criminal penalties, and will be prosecuted to the maximum extent possible under law. ******************************************************************************/ #include "FDK_trigFcts.h" #include "fixpoint_math.h" // #define CORDIC_SINCOS #define IMPROVE_ATAN2_ACCURACY 1 // 0 --> 59 dB SNR 1 --> 65 dB SNR #define MINSFTAB 7 #define MAXSFTAB 25 #if IMPROVE_ATAN2_ACCURACY static const FIXP_DBL f_atan_expand_range[MAXSFTAB-(MINSFTAB-1)] = { /***************************************************************************** * * Table holds fixp_atan() output values which are outside of input range * of fixp_atan() to improve SNR of fixp_atan2(). * * This Table might also be used in fixp_atan() [todo] so there a wider input * range can be covered, too. * * Matlab (generate table): * for scl = 7:25 % MINSFTAB .. MAXSFTAB * at=atan(0.5 *(2^scl)); % 0.5 because get in 'middle' area of current scale level 'scl' * at/2 % div at by ATO_SCALE * end * * Table divided by 2=ATO_SCALE <-- SF=ATO_SF *****************************************************************************/ FL2FXCONST_DBL(7.775862990872099e-001), FL2FXCONST_DBL(7.814919928673978e-001), FL2FXCONST_DBL(7.834450483314648e-001), FL2FXCONST_DBL(7.844216021392089e-001), FL2FXCONST_DBL(7.849098823026687e-001), FL2FXCONST_DBL(7.851540227918509e-001), FL2FXCONST_DBL(7.852760930873737e-001), FL2FXCONST_DBL(7.853371282415015e-001), FL2FXCONST_DBL(7.853676458193612e-001), FL2FXCONST_DBL(7.853829046083906e-001), FL2FXCONST_DBL(7.853905340029177e-001), FL2FXCONST_DBL(7.853943487001828e-001), FL2FXCONST_DBL(7.853962560488155e-001), FL2FXCONST_DBL(7.853972097231319e-001), FL2FXCONST_DBL(7.853976865602901e-001), FL2FXCONST_DBL(7.853979249788692e-001), FL2FXCONST_DBL(7.853980441881587e-001), FL2FXCONST_DBL(7.853981037928035e-001), FL2FXCONST_DBL(7.853981335951259e-001) // pi/4 = 0.785398163397448 = pi/2/ATO_SCALE }; #endif FIXP_DBL fixp_atan2(FIXP_DBL y, FIXP_DBL x) { FIXP_DBL q; FIXP_DBL at; // atan out FIXP_DBL at2; // atan2 out FIXP_DBL ret = FL2FXCONST_DBL(-1.0f); INT sf,sfo,stf; // --- division if (y > FL2FXCONST_DBL(0.0f)) { if (x > FL2FXCONST_DBL(0.0f)) { q = fDivNormHighPrec( y, x, &sf); // both pos. } else if (x < FL2FXCONST_DBL(0.0f)) { q = -fDivNormHighPrec( y,-x, &sf); // x neg. } else {//(x ==FL2FXCONST_DBL(0.0f)) q = FL2FXCONST_DBL(+1.0f); // y/x = pos/zero = +Inf sf = 0; } } else if (y < FL2FXCONST_DBL(0.0f)) { if (x > FL2FXCONST_DBL(0.0f)) { q = -fDivNormHighPrec(-y, x, &sf); // y neg. } else if (x < FL2FXCONST_DBL(0.0f)) { q = fDivNormHighPrec(-y,-x, &sf); // both neg. } else {//(x ==FL2FXCONST_DBL(0.0f)) q = FL2FXCONST_DBL(-1.0f); // y/x = neg/zero = -Inf sf = 0; } } else { // (y ==FL2FXCONST_DBL(0.0f)) q = FL2FXCONST_DBL(0.0f); sf = 0; } sfo = sf; //write2file(fpF__h,(float)y/(float)x); //write2file(fpC__h,(float) q * (float)FDKpow(2,sfo)); // SNR 86 dB But range not limited to [-64..64] which is the input range of fixp_atan() // --- atan() if ( sfo > ATI_SF ) { // --- could not calc fixp_atan() here bec of input data out of range // ==> therefore give back boundary values #if IMPROVE_ATAN2_ACCURACY if (sfo > MAXSFTAB) sfo = MAXSFTAB; #endif if ( q > FL2FXCONST_DBL(0.0f) ) { #if IMPROVE_ATAN2_ACCURACY at = +f_atan_expand_range[sfo-ATI_SF-1]; #else at = FL2FXCONST_DBL( +M_PI/2 / ATO_SCALE); #endif } else if ( q < FL2FXCONST_DBL(0.0f) ) { #if IMPROVE_ATAN2_ACCURACY at = -f_atan_expand_range[sfo-ATI_SF-1]; #else at = FL2FXCONST_DBL( -M_PI/2 / ATO_SCALE); #endif } else { // q== FL2FXCONST_DBL(0.0f) at = FL2FXCONST_DBL( 0.0f ); } }else{ // --- calc of fixp_atan() is possible; input data within range // ==> set q on fixed scale level as desired from fixp_atan() stf = sfo - ATI_SF; if (stf > 0) q = q << (INT)fMin( stf,DFRACT_BITS-1); else q = q >> (INT)fMin(-stf,DFRACT_BITS-1); //write2file(fpF__e,(float)y/(float)x); //write2file(fpC__e,(float)q * (float)FDKpow(2,ATI_SF)); // 88.9 dB at = fixp_atan(q); // ATO_SF } //write2file(fpF__g,(float)FDKatan( (float)y/(float)x )); //write2file(fpC__g,(float) at * (float)FDKpow(2,ATO_SF) ); // todo dB // --- atan2() at2 = at >> (AT2O_SF - ATO_SF); // now AT2O_SF for atan2 if ( x > FL2FXCONST_DBL(0.0f) ) { ret = at2; } else if ( x < FL2FXCONST_DBL(0.0f) ) { if ( y >= FL2FXCONST_DBL(0.0f) ) { ret = at2 + FL2FXCONST_DBL( M_PI / AT2O_SCALE); } else { ret = at2 - FL2FXCONST_DBL( M_PI / AT2O_SCALE); } } else { // x == 0 if ( y > FL2FXCONST_DBL(0.0f) ) { ret = FL2FXCONST_DBL( +M_PI/2 / AT2O_SCALE); } else if ( y < FL2FXCONST_DBL(0.0f) ) { ret = FL2FXCONST_DBL( -M_PI/2 / AT2O_SCALE); } else if ( y == FL2FXCONST_DBL(0.0f) ) { ret = FL2FXCONST_DBL(0.0f); } } //write2file(fpC__f,(float)ret * (float)FDKpow(2,AT2O_SF)); // 65 dB //write2file(fpF__f,(float)FDKatan2(y,x)); return ret; } FIXP_DBL fixp_atan(FIXP_DBL x) { INT sign; FIXP_DBL result, temp; // SNR of fixp_atan() = 56 dB FIXP_DBL ONEBY3P56 = (FIXP_DBL)0x26800000; // 1.0/3.56 in q31 FIXP_DBL P281 = (FIXP_DBL)0x00013000; // 0.281 in q18 FIXP_DBL ONEP571 = (FIXP_DBL)0x6487ef00; // 1.571 in q30 if (x < FIXP_DBL(0)) { sign = 1; x = - x ; } else { sign = 0; } /* calc of arctan */ if(x < ( Q(Q_ATANINP)-FL2FXCONST_DBL(0.00395)) ) { INT res_e; temp = fPow2(x); // q25 * q25 - (DFRACT_BITS-1) = q19 temp = fMult(temp, ONEBY3P56); // q19 * q31 - (DFRACT_BITS-1) = q19 temp = temp + Q(19); // q19 + q19 = q19 result = fDivNorm(x, temp, &res_e); result = scaleValue(result, (Q_ATANOUT-Q_ATANINP+19-DFRACT_BITS+1) + res_e ); } else if( x < FL2FXCONST_DBL(1.28/64.0) ) { FIXP_DBL delta_fix; FIXP_DBL PI_BY_4 = FL2FXCONST_DBL(3.1415926/4.0) >> 1; /* pi/4 in q30 */ delta_fix = (x - FL2FXCONST_DBL(1.0/64.0)) << 5; /* q30 */ result = PI_BY_4 + (delta_fix >> 1) - (fPow2Div2(delta_fix)); } else { INT res_e; temp = fPow2Div2(x); // q25 * q25 - (DFRACT_BITS-1) - 1 = q18 temp = temp + P281; // q18 + q18 = q18 result = fDivNorm(x, temp, &res_e); result = scaleValue(result, (Q_ATANOUT-Q_ATANINP+18-DFRACT_BITS+1) + res_e ); result = ONEP571 - result; // q30 + q30 = q30 } if (sign) { result = -result; } return(result); } #ifndef CORDIC_SINCOS #include "FDK_tools_rom.h" FIXP_DBL fixp_cos(FIXP_DBL x, int scale) { FIXP_DBL residual, error, sine, cosine; residual = fixp_sin_cos_residual_inline(x, scale, &sine, &cosine); error = fMult(sine, residual); return cosine - error; } FIXP_DBL fixp_sin(FIXP_DBL x, int scale) { FIXP_DBL residual, error, sine, cosine; residual = fixp_sin_cos_residual_inline(x, scale, &sine, &cosine); error = fMult(cosine, residual); return sine + error; } void fixp_cos_sin (FIXP_DBL x, int scale, FIXP_DBL *cos, FIXP_DBL *sin) { FIXP_DBL residual, error0, error1, sine, cosine; residual = fixp_sin_cos_residual_inline(x, scale, &sine, &cosine); error0 = fMult(sine, residual); error1 = fMult(cosine, residual); *cos = cosine - error0; *sin = sine + error1; } #else /* #ifndef CORDIC_SINCOS */ /* // Matlab function v = cordic(beta,n) % This function computes v = [cos(beta), sin(beta)] (beta in radians) % using n iterations. Increasing n will increase the precision. if beta < -pi/2 | beta > pi/2 if beta < 0 v = cordic(beta + pi, n); else v = cordic(beta - pi, n); end v = -v; % flip the sign for second or third quadrant return end % Initialization of tables of constants used by CORDIC % need a table of arctangents of negative powers of two, in radians: % angles = atan(2.^-(0:27)); angles = [ ... 0.78539816339745 0.46364760900081 0.24497866312686 0.12435499454676 ... 0.06241880999596 0.03123983343027 0.01562372862048 0.00781234106010 ... 0.00390623013197 0.00195312251648 0.00097656218956 0.00048828121119 ... 0.00024414062015 0.00012207031189 0.00006103515617 0.00003051757812 ... 0.00001525878906 0.00000762939453 0.00000381469727 0.00000190734863 ... 0.00000095367432 0.00000047683716 0.00000023841858 0.00000011920929 ... 0.00000005960464 0.00000002980232 0.00000001490116 0.00000000745058 ]; % and a table of products of reciprocal lengths of vectors [1, 2^-j]: Kvalues = [ ... 0.70710678118655 0.63245553203368 0.61357199107790 0.60883391251775 ... 0.60764825625617 0.60735177014130 0.60727764409353 0.60725911229889 ... 0.60725447933256 0.60725332108988 0.60725303152913 0.60725295913894 ... 0.60725294104140 0.60725293651701 0.60725293538591 0.60725293510314 ... 0.60725293503245 0.60725293501477 0.60725293501035 0.60725293500925 ... 0.60725293500897 0.60725293500890 0.60725293500889 0.60725293500888 ]; Kn = Kvalues(min(n, length(Kvalues))); % Initialize loop variables: v = [1;0]; % start with 2-vector cosine and sine of zero poweroftwo = 1; angle = angles(1); % Iterations for j = 0:n-1; if beta < 0 sigma = -1; else sigma = 1; end factor = sigma * poweroftwo; R = [1, -factor; factor, 1]; v = R * v; % 2-by-2 matrix multiply beta = beta - sigma * angle; % update the remaining angle poweroftwo = poweroftwo / 2; % update the angle from table, or eventually by just dividing by two if j+2 > length(angles) angle = angle / 2; else angle = angles(j+2); end end % Adjust length of output vector to be [cos(beta), sin(beta)]: v = v * Kn; return // C++ #define C1 #define C2 #define TYPE double TYPE fixp_cordic(TYPE in, int n, int type) { int i; TYPE c,s,a; TYPE x,y,v; TYPE b; c = 0.5; s = 0.0; b = 1.0; for (i=0; i M_PI/2) ) { if (in < 0) in = in + M_PI; else in = in - M_PI; if (sign == 0) sign = 1; else sign = 0; } v = fixp_cordic(in,n,0); if (sign) v = -v; return (v); } TYPE fixp_sin(TYPE in, int n, int scale) { TYPE v; INT sign = 0; while ( (in < -M_PI/2) || (in > M_PI/2) ) { if (in < 0) in = in + M_PI; else in = in - M_PI; if (sign == 0) sign = 1; else sign = 0; } v = fixp_cordic(in,n,1); if (sign) v = -v; return (v); } */ #define SF_C1 1 #define C1(x) FL2FXCONST_DBL(x/(1<> 1; s = FL2FXCONST_DBL(0.0f); for (i=0; i> i; y = c >> i; if (in < FL2FXCONST_DBL(0.0)) { c = c + x; s = s - y; in = in + a; } else { c = c - x; s = s + y; in = in - a; } } /* 1 headroom bit */ if (type == 0) return (c); else return (s); } /* in: argument scale: scalefactor of argument has to be in the range of 0, ... ,DFRACT_BITS-1 type: calculate cosine 0 calculate sine 1 */ static FIXP_DBL fixp_sincos(FIXP_DBL in, INT scale, INT type) { FIXP_DBL v; INT sign = 0; FIXP_DBL pi,pi_2; if (scale < 2) { in = in >> (2-scale); scale = 2; } pi = M_PI_4 >> (scale-2); pi_2 = M_PI_4 >> (scale-1); /* move signal into the range of -pi/2 to +pi/2 */ while ( (in < (-pi_2)) || (in > (pi_2)) ) { if (in < FL2FXCONST_DBL(0.0)) in = in + pi; else in = in - pi; sign = ~sign; } /* scale signal with 2/pi => unscaled signal is in the range of [-1.0,...,+1.0[ */ in = fMult(in,FL2FXCONST_DBL(2/M_PI)) << scale; v = fixp_cordic(in,type); if (sign) v = -v; /* compensate 1 headroom bit */ if ( v <= FL2FXCONST_DBL(-0.5f) ) v = (FIXP_DBL) (MINVAL_DBL+1); else v = SATURATE_LEFT_SHIFT(v,1,DFRACT_BITS); return (v); } /* in: argument scale: scalefactor of argument has to be in the range of 0, ... ,DFRACT_BITS-1 */ FIXP_DBL fixp_cos(FIXP_DBL in, INT scale) { FDK_ASSERT ((scale >= 0) && (scale < DFRACT_BITS)); if (in == FL2FXCONST_DBL(0.0f)) return /*FL2FXCONST_DBL(1.0)*/ (FIXP_DBL)MAXVAL_DBL; return (fixp_sincos(in,scale,0)); } /* in: argument scale: scalefactor of argument has to be in the range of 0, ... ,DFRACT_BITS-1 */ FIXP_DBL fixp_sin(FIXP_DBL in, INT scale) { FDK_ASSERT ((scale >= 0) && (scale < DFRACT_BITS)); if (in == FL2FXCONST_DBL(0.0f)) return FL2FXCONST_DBL(0.0f); return (fixp_sincos(in,scale,1)); } #endif /* #ifndef CORDIC_SINCOS */