13.4.1. Bitmélység, lebegőpontos- és fixpontos formátum Beágyazott rendszereknél jellemző, a PC programozással ellentétben, hogy külön figyelmet kell szentelni a bitmélységnek és a lebegőpontos (floating poing) - fixpontos (fixed point) adatformátumokra és konvertálásokra. Ez azért van, mert az MC-k és DSPk aritmetikája (ALU egysége) sok esetben nem 64bites lebegőpontos, hanem 16 vagy 32 bites, és fixpontos. Persze 16bites maggal is lehet 32 és 64 bites műveleteket végezni, továbbá fixpontos maggal is lehet lebegőpontos műveleteket számoltatni, de az általában többszörösen (8x 50x) lassúbb,ugyanis több szekvenciális utasítással kell őket kiszámolni. Viszont ezeknél a rendszereknél a műveletek zöménél nincs is szükség nagy bitmélységre, és lebegőpontos számításra. Például ha az AD/DA konverterek 8-10-12-16 bitesek, és fixpontosak, akkor fölösleges lenne ezeket a számokat 32 vagy 64biten tárolni, lebegőpontossá alakítani, és akképpen műveleteket végezni velük. Szükségképpen oda kell figyelni a kisebb bitmélységű műveleteknél a kerekítési és integrációs hibákra. A fixpontos számoknál pedig a skálázásra. Ott a tört számokat Q formátumokban szokták értelmezni. Ez azt jelenti, hogy meg van határozva hogy hány bit az egész rész, és hány bit marad a tört résznek. Például 1Q15 formátumnál az egész résznek csak 1 bit van (1 bit vagy előjel bit, a tört résznek 15 bit), akkor a szám [- 1 1] tartományon belül kell, hogy maradjon a számítások alatt. Ez különösebben problémát okoz az IIR szűrőknél, ezért is találták ki a biquad szerkezetet. Típusok Lebegőpontos típusokból legrövidebb 32bites létezik (float) a 64bites mellett (double). Míg fixpontosból létezik 8 bites (char), 16 bites (int), 32 bites (long int), 64 bites (long long). Megjegyzés: 32 bites processzorban az int is 32 bites. Javasolt minden esetben leellenőrizni az adott környezetben a használt változók helyes deklarálását. Integrációs hibák, példa A következőkben egy példát láthatunk az integrációs hibákra a különböző formátumokra. Példánkban veszünk egy dt=0,0001 számot, melyet 1000 szer összeadunk, és leellenőrizzünk, hogy mennyivel tér el a dt*1000=0,1 pontos értékről. Figyeljük meg nem csak az integrált összeg tér el a pontos értéktől, hanem már a dt változó értéke sem tudta pontosan felvenni a 0,0001 értéket. %% example parameters clc; clear; format long; DT = 0.0001; N = 1000; T = DT*N; %% 64bit floating point disp('64bit floating point') ti = 0; for i = 1:N, ti = ti + DT; end
disp('dt = '); disp(dt); disp('ti = '); disp(ti); disp('ti - t = '); disp(ti-t); %% 32bit floating point disp('32bit floating point') dt = single(dt); ti = single(0); for i = 1:N, ti = ti + dt; end disp('dt = '); disp(dt); disp('ti = '); disp(ti); disp('ti - t = '); disp(ti-t); %% 32bit fixed point disp('32bit fixed point') intmax = double(intmax('int32')); dt = int32(dt*intmax); ti = int32(0); for i = 1:N, ti = ti + dt; end disp('dt = '); disp(double(dt)/intmax); disp('ti = '); disp(double(ti)/intmax); disp('ti - t = '); disp(double(ti)/intmax - T); %% 16bit fixed point disp('16bit fixed point') intmax = double(intmax('int16')); dt = int16(dt*intmax); ti = int16(0); for i = 1:N, ti = ti + dt; end disp('dt = '); disp(double(dt)/intmax); disp('ti = '); disp(double(ti)/intmax); disp('ti - t = '); disp(double(ti)/intmax - T); A következő táblázat mutatja az eredményeket Aritmetikai forma Alapszám (dt) Integrált szám (ti) Hiba (ti t) Hiba nagyságrend 64bit floating point 1.000000000000000e-04 0.100000000000002 1.831867990631508e-15 10-15, 10-12 % 32bit floating point 9.999999747378752e-05 0.100001119077206 1.119077205652408e-06 10-6, 10-3 % 32bit fixed point (Q31) 9.999983017332844e-05 0.099999830173328-1.698266715588481e-07 10-7, 10-4 % 16bit fixed point (Q15) 9.155552842799158e-05 0.091555528427992-0.008444471572008 10-2, 10%
FFT példa Mikrovezérlőknél sem ritka feladat, hogy a digitális jeleket spektrálisan kell elemezni. Legtöbbször valamilyen FFT alapú spektrumot szoktak alkalmazni, mivel relatív kicsi a erőforrás igénye a többihez módszerhez képest. Ingyen letölthető és jól használható a C-ben irt Kiss FFT, ezért példánkban ezt fogjuk tesztelni. mivel jeleket analizálni Matlabban a legkézenfekvőbb, ezért innen fogjuk meghívni a Kiss FFT-t. Első lépésként meg kell írni a C-MEX interfészt (kiss_fft_m.c), majd azt lefordítani MEX fájlba. A következő képen vannak a forrásfájlok rendezve, a Kiss FFT forráskódja a kissfft_c könyvtárban: Kiss_fft_m.c // includes #include<mex.h> #include "kiss_fft.h" #include "tools\kiss_fftr.h" // function declaration doublemagnitude(kiss_fft_scalar real,kiss_fft_scalar imag,int nx); double kissfftscalar_to_double(kiss_fft_scalar x); kiss_fft_scalar double_to_kissfftscalar(double x); // MEX main function voidmexfunction( int nlhs, mxarray *plhs[], int nrhs, const mxarray *prhs[] ) { // declaration double *x, *s, temp; int i, nx, ns; kiss_fft_scalar *xfft; kiss_fft_cpx* scpx;
kiss_fftr_cfg cfgfftr; // check inputs if(nrhs!=1) mexerrmsgtxt("1 input required."); if(!mxisdouble(prhs[0]) mxiscomplex(prhs[0])!mxgetn(prhs[0])==1) mexerrmsgtxt("input 1 must be a noncomplex [Mx1] double."); // pointers to inputs x = mxgetpr(prhs[0]); // signal nx = (int)mxgetm(prhs[0]); // signal size ns = (int)(nx/2+1); // pointers to outputs plhs[0] = mxcreatedoublematrix(ns,1, mxreal); s = mxgetpr(plhs[0]); // debug print mexprintf("kiss fft, "); #ifdef FIXED_POINT # if (FIXED_POINT == 32) mexprintf("fixed point 32bit\n"); # else mexprintf("fixed point 16bit\n"); # endif #else mexprintf("floating point 32bit\n"); #endif // dynamic variable allocation scpx = calloc(nx, sizeof(scpx)); xfft = calloc(nx, sizeof(xfft)); cfgfftr = kiss_fftr_alloc(nx,0,0,0); // calculation for(i=0; i<nx; i++){ xfft[i] = double_to_kissfftscalar(x[i]); kiss_fftr(cfgfftr,xfft,scpx); for(i=0; i<ns; i++){ temp = magnitude(scpx[i].r,scpx[i].i,nx); s[i] = temp; s[0] = s[0]/2; // remove half band correction // free
KISS_FFT_FREE( cfgfftr ); free( xfft ); free( scpx ); // sub functions #define Q15_MAX_VALUE 32767 #define Q31_MAX_VALUE 2147483647 kiss_fft_scalar double_to_kissfftscalar(double x) { #ifdef FIXED_POINT # if (FIXED_POINT == 32) // define kiss_fft_scalar int32_t kiss_fft_scalar y = (kiss_fft_scalar)(x*q31_max_value); # else // define kiss_fft_scalar int16_t kiss_fft_scalar y = (kiss_fft_scalar)(x*q15_max_value); # endif #else // define kiss_fft_scalar float kiss_fft_scalar y = (kiss_fft_scalar)x; #endif return y; double kissfftscalar_to_double(kiss_fft_scalar x) { #ifdef FIXED_POINT # if (FIXED_POINT == 32) // define kiss_fft_scalar int32_t double y = (double)((double)x/q31_max_value ); # else // define kiss_fft_scalar int16_t double y = (double)((double)x/q15_max_value ); # endif #else // define kiss_fft_scalar float double y = (double)x;
#endif return y; double magnitude(kiss_fft_scalar real,kiss_fft_scalar imag,int nx) { double cpxf, mag; cpxf = kissfftscalar_to_double(real); mag = cpxf * cpxf; cpxf = kissfftscalar_to_double(imag); mag += cpxf * cpxf; mag = 2*sqrt(mag); // 2* - half band correction #ifndef FIXED_POINT mag = mag/nx; #endif return mag; Ahogy látható 3 módban is fordíthatjuk az interfész C programot, amelyet a setup.h átírásával/cserélésével automatizáltunk. A három féle fordításhoz irtunk egy Matlab szkriptet, amely beállítja a setup.h t, és meghívja a MEX compáljert. start_build.m clc; clear % define path of KISS FFT C source code srcpath = '..\kissfft_c\'; % define MEX output names outnames = {'kiss_fft_m_f32','kiss_fft_m_i32','kiss_fft_m_i16'; % define setup.h names for every mode setupnames = {'setup_f32.h','setup_i32.h','setup_i16.h'; for i = 1:length(outNames) % replace setup.h in source code (switch modes f32/i32/i16) copyfile(setupnames{i,[srcpath 'setup.h']); % assembly mex compile command mexcmd = ['mex -I' srcpath ' kiss_fft_m.c ' srcpath 'tools\kiss_fftr.c ' srcpath 'kiss_fft.c -output 'outnames{i]; disp(mexcmd); % evaluate command eval(mexcmd);
% check existence of result MEX file if exist([outnames{i '.mexw64'],'file') disp('build succeeded!') else disp('build failed!'); end end Végül teszteljük a MEX programokat egy példa jellel: test_kiss_fft_m.m clc; clear; close all; %% generate test signal: 0.1 * DC + 0.4 * 30Hz sinus + 0.05 * 190Hz sinus fs = 1000; t = [1:2^10]'./fs; x = 0.1 + 0.4 * sin(t*2*pi*30) + 0.05 * sin(t*2*pi*190); %% windowing win = hann(length(x)); xwin = x.*win; %% calculate spectrum with kiss_fft_m ps(:,1) = matlab_fft(xwin); casename{1 = 'MATLAB f64'; ps(:,2) = kiss_fft_m_f32(xwin); casename{2 = 'KISS f32'; ps(:,3) = kiss_fft_m_i32(xwin); casename{3 = 'KISS i32'; ps(:,4) = kiss_fft_m_i16(xwin); casename{4 = 'KISS i16'; %% windowing gain effect correction ps = ps / mean(win); %% plot compare spectrum fa = [1:size(ps,1)].* (fs/length(x)); semilogy(fa,ps,'linewidth',2); grid on; legend(casename); Ahol a Matlab FFT megfelelője: matlab_fft.m function sp = matlab_fft(x) n = length(x); s = fft(x); hn = n/2+1; sp = 2*abs(s(1:hn))/n; sp(1) = sp(1)/2;
end Eredményként a következő ábrát kapjuk, ahol láthatóak az FFT eredményei közötti eltérések a különböző módok között: Következtetések: F64 F32 I32 I16 Végig folyamatos (10-11 tartományban is), ehhez viszonyítjuk a többi eredményt 10-8 érték körül már eltéréseket mutat, 10-8 alatt pedig erősen zajos 10-8 érték körüleléri a kvantizációs határát (0,1,2, értékeket), és lépcsőzetes lesz 10-4 alatt már eléri a nullát, az alatt nem képes mutatni semmit.