The following example illustrates the ideas in fixed-point arithmetic. If we want to implement any DSP algorithm from floating point implementation to fixed-point we need to understand the effects of quantization.
The FIR filter is implemented in C++ using IT++ library. Let us look at the output of the code before looking into the code.
Three point averager FIR Filter
$$ y[n] = \sum_{k=0}^2 h[k]x[n-k]$$
Lets implement this filter using Q1.3 format arithmetic.
$$ h[n] = [\frac{1}{3}, \frac{1}{3}, \frac{1}{3}]$$
$$x[n] = [\frac{1}{5}, \frac{2}{5}, \frac{3}{5}, \frac{4}{5}]$$
$$ (ideal) y[n] = [0.0667, 0.2, 0.4, 0.6, 0.4667, 0.2667] $$
Quantize the values of h[n] to a Q1.3 format with rounding to the nearest available value.
$$ h[n] = [0.3333, 0.3333, 0.3333]$$
$$ Q(h[n]) = [0.375, 0.375, 0.375]$$
$$ Q(h[n])_2 = [0.011, 0.011, 0.011]$$
Q1.3 format can represent values from -1 to +$\frac{7}{8} $ in steps of $\frac{1}{8}$. So, the nearest available value to $\frac{1}{3}$ is $\frac{3}{8}$.
Bear in mind, that the last representation in binary format the binary point we showed is just for our sake, the actual values are just integers, so it's our responsibility to keep scaling factor all the time.
$$ Q(h[n])_2 = [0011, 0011, 0011]<scale = 3>$$
$$ Q(h[n]) = [3, 3, 3]<scale = 3>$$
Here we represent scaling factor in angular braces<>.
$ z<n> == \frac{z}{2^n} $
The input values in Q1.3 format
$$ x[n] = [0.2, 0.4, 0.6, 0.8] $$
$$ Q(x[n]) = [0.25, 0.375, 0.625, 0.75] $$
$$ Q(x[n])_2 = [0.010, 0.011, 0.101, 0.110] $$
$$ Q(x[n]) = [2, 3, 5, 6] <3>$$
Computing y[0]
$$ y[0]_{(2)} = h[0] x[0] $$
$$ 0.011 \times 0.010 = 00.000110 = 0.001 (rounded) $$
$$ y[0]_{(10)} = 0.125 $$
The intermediate result is in Q2.6 format, and the final value is quantized to Q1.3 format using rounding.
Other outputs are also computed in the same way.
$$(computed ) y[n] = [0.125, 0.25, 0.5, 0.625, 0.5, 0.25] $$
where as
$$ (ideal) y[n] = [0.0667, 0.2, 0.4, 0.6, 0.4667, 0.2667] $$
h[n] = [0.333333 , 0.333333 , 0.333333 , ] Q(h[n]) = [0.375 , 0.375 , 0.375 , ] Q(h[n]) = [3<3> , 3<3> , 3<3> , ] x[n] = [0.2 , 0.4 , 0.6 , 0.8 , ] Q(x[n]) = [0.25 , 0.375 , 0.625 , 0.75 , ] Q(x[n]) = [2<3> , 3<3> , 5<3> , 6<3> , ] calculated - Q1.3 y[n] = [0.125 , 0.25 , 0.5 , 0.625 , 0.5 , 0.25 , ] actual y[n] = [0.0666667 , 0.2 , 0.4 , 0.6 , 0.466667 , 0.266667 , ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | /*********************************************************************************************** Author: Srinivas Siripurapu Email: srinivas.siripurapu@outlook.com Description: Demonstration of fixed-point implementation of three-point averager FIR Filter Library used: it++ ***********************************************************************************************/ #include <itpp/itbase.h> #include <itpp/itfixed.h> using namespace itpp; using namespace std; int main() { vec h ("0.3333333 0.3333333 0.3333333 "); vec x ("0.2000 0.4000 0.6000 0.8000 "); cout << "h[n] = ["; for(int i=0; i < length(h); i++) cout << h(i)<<" , "; cout <<"]\n"; Vec<Fix> hfx; set_fix(hfx,h,3,RND); // Q1.3 Format RND cout << "Q(h[n]) = ["; for(int i=0; i < length(h); i++) cout << hfx(i).unfix()<<" , "; cout <<"]\n"; cout << "Q(h[n]) = ["; for(int i=0; i < length(h); i++) cout << hfx(i)<<" , "; cout <<"]\n"; cout << "\nx[n] = ["; for(int i=0; i < length(x); i++) cout << x(i)<<" , "; cout <<"]\n"; Vec<Fix> xfx; set_fix(xfx,x,3,RND); // Q1.3 Format RND cout << "Q(x[n]) = ["; for(int i=0; i < length(x); i++) cout << xfx(i).unfix()<<" , "; cout <<"]\n"; cout << "Q(x[n]) = ["; for(int i=0; i < length(x); i++) cout << xfx(i)<<" , "; cout <<"]\n"; // Calculating 3 tap moving average output y vec y(length(h)+length(x)-1); Vec<Fix> yfx(length(y)); Fix accfx; double acc = 0.0; for(int i=0; i < length(yfx); i++) { acc = 0; accfx = 0.0; for(int k=0; k < 3; k++) { if ( i-k >= 0 && i-k < length(x)) { acc += h(k)*x(i-k); accfx += hfx(k) * xfx(i-k); } } y(i) = acc; yfx(i) = accfx; yfx(i).rshift(3,RND); } cout << "\ncalculated - Q1.3 \ny[n] = ["; for(int i=0; i < length(y); i++) cout << yfx(i).unfix()<<" , "; cout <<"]\n"; cout << "actual \ny[n] = ["; for(int i=0; i < length(y); i++) cout << y(i)<<" , "; cout <<"]\n"; return 0; } |
No comments:
Post a Comment