int main(int argc, char*argv[]) { // options unsigned int k=4; // samples/symbol unsigned int m=3; // filter delay [symbols] float BT = 0.3f; // bandwidth-time product // read properties from command line int dopt; while ((dopt = getopt(argc,argv,"uhk:m:b:")) != EOF) { switch (dopt) { case 'u': case 'h': usage(); return 0; case 'k': k = atoi(optarg); break; case 'm': m = atoi(optarg); break; case 'b': BT = atof(optarg); break; default: exit(1); } } // validate input if (k < 2) { fprintf(stderr,"error: %s, k must be at least 2\n", argv[0]); exit(1); } else if (m < 1) { fprintf(stderr,"error: %s, m must be at least 1\n", argv[0]); exit(1); } else if (BT <= 0.0f || BT >= 1.0f) { fprintf(stderr,"error: %s, BT must be in (0,1)\n", argv[0]); exit(1); } unsigned int i; // derived values unsigned int h_len = 2*k*m+1; // filter length // arrays float ht[h_len]; // transmit filter coefficients float hr[h_len]; // recieve filter coefficients // design transmit filter liquid_firdes_gmsktx(k,m,BT,0.0f,ht); // print results to screen printf("gmsk transmit filter:\n"); for (i=0; i<h_len; i++) printf(" ht(%3u) = %12.8f;\n", i+1, ht[i]); // // start of filter design procedure // float beta = BT; // prototype filter cut-off float delta = 1e-2f; // filter design correction factor // temporary arrays float h_primef[h_len]; // temporary buffer for real coefficients float g_primef[h_len]; // float complex h_tx[h_len]; // impulse response of transmit filter float complex h_prime[h_len]; // impulse response of 'prototype' filter float complex g_prime[h_len]; // impulse response of 'gain' filter float complex h_hat[h_len]; // impulse response of receive filter float complex H_tx[h_len]; // frequency response of transmit filter float complex H_prime[h_len]; // frequency response of 'prototype' filter float complex G_prime[h_len]; // frequency response of 'gain' filter float complex H_hat[h_len]; // frequency response of receive filter // create 'prototype' matched filter // for now use raised-cosine liquid_firdes_nyquist(LIQUID_NYQUIST_RCOS,k,m,beta,0.0f,h_primef); // create 'gain' filter to improve stop-band rejection float fc = (0.7f + 0.1*beta) / (float)k; float As = 60.0f; liquid_firdes_kaiser(h_len, fc, As, 0.0f, g_primef); // copy to fft input buffer, shifting appropriately for (i=0; i<h_len; i++) { h_prime[i] = h_primef[ (i+k*m)%h_len ]; g_prime[i] = g_primef[ (i+k*m)%h_len ]; h_tx[i] = ht[ (i+k*m)%h_len ]; } // run ffts fft_run(h_len, h_prime, H_prime, FFT_FORWARD, 0); fft_run(h_len, g_prime, G_prime, FFT_FORWARD, 0); fft_run(h_len, h_tx, H_tx, FFT_FORWARD, 0); #if 0 // print results for (i=0; i<h_len; i++) printf("Ht(%3u) = %12.8f + j*%12.8f;\n", i+1, crealf(H_tx[i]), cimagf(H_tx[i])); for (i=0; i<h_len; i++) printf("H_prime(%3u) = %12.8f + j*%12.8f;\n", i+1, crealf(H_prime[i]), cimagf(H_prime[i])); for (i=0; i<h_len; i++) printf("G_prime(%3u) = %12.8f + j*%12.8f;\n", i+1, crealf(G_prime[i]), cimagf(G_prime[i])); #endif // find minimum of reponses float H_tx_min = 0.0f; float H_prime_min = 0.0f; float G_prime_min = 0.0f; for (i=0; i<h_len; i++) { if (i==0 || crealf(H_tx[i]) < H_tx_min) H_tx_min = crealf(H_tx[i]); if (i==0 || crealf(H_prime[i]) < H_prime_min) H_prime_min = crealf(H_prime[i]); if (i==0 || crealf(G_prime[i]) < G_prime_min) G_prime_min = crealf(G_prime[i]); } // compute 'prototype' response, removing minima, and add correction factor for (i=0; i<h_len; i++) { // compute response necessary to yeild prototype response (not exact, but close) H_hat[i] = crealf(H_prime[i] - H_prime_min + delta) / crealf(H_tx[i] - H_tx_min + delta); // include additional term to add stop-band suppression H_hat[i] *= crealf(G_prime[i] - G_prime_min) / crealf(G_prime[0]); } // compute ifft and copy response fft_run(h_len, H_hat, h_hat, FFT_REVERSE, 0); for (i=0; i<h_len; i++) hr[i] = crealf( h_hat[(i+k*m+1)%h_len] ) / (float)(k*h_len); // // end of filter design procedure // // print results to screen printf("gmsk receive filter:\n"); for (i=0; i<h_len; i++) printf(" hr(%3u) = %12.8f;\n", i+1, hr[i]); // compute isi float rxy0 = liquid_filter_crosscorr(ht,h_len, hr,h_len, 0); float isi_rms = 0.0f; for (i=1; i<2*m; i++) { float e = liquid_filter_crosscorr(ht,h_len, hr,h_len, i*k) / rxy0; isi_rms += e*e; } isi_rms = sqrtf(isi_rms / (float)(2*m-1)); printf("\n"); printf("ISI (RMS) = %12.8f dB\n", 20*log10f(isi_rms)); // // export output file // FILE*fid = fopen(OUTPUT_FILENAME,"w"); fprintf(fid,"%% %s : auto-generated file\n", OUTPUT_FILENAME); fprintf(fid,"clear all;\n"); fprintf(fid,"close all;\n"); fprintf(fid,"\n\n"); fprintf(fid,"k = %u;\n", k); fprintf(fid,"m = %u;\n", m); fprintf(fid,"beta = %f;\n", BT); fprintf(fid,"h_len = 2*k*m+1;\n"); fprintf(fid,"nfft = 1024;\n"); fprintf(fid,"ht = zeros(1,h_len);\n"); fprintf(fid,"hp = zeros(1,h_len);\n"); fprintf(fid,"hr = zeros(1,h_len);\n"); // print results for (i=0; i<h_len; i++) fprintf(fid,"ht(%3u) = %12.4e;\n", i+1, ht[i] / k); for (i=0; i<h_len; i++) fprintf(fid,"hr(%3u) = %12.4e;\n", i+1, hr[i] * k); for (i=0; i<h_len; i++) fprintf(fid,"hp(%3u) = %12.4e;\n", i+1, h_primef[i]); fprintf(fid,"hc = k*conv(ht,hr);\n"); // plot results fprintf(fid,"f = [0:(nfft-1)]/nfft - 0.5;\n"); fprintf(fid,"Ht = 20*log10(abs(fftshift(fft(ht, nfft))));\n"); fprintf(fid,"Hp = 20*log10(abs(fftshift(fft(hp/k,nfft))));\n"); fprintf(fid,"Hr = 20*log10(abs(fftshift(fft(hr, nfft))));\n"); fprintf(fid,"Hc = 20*log10(abs(fftshift(fft(hc/k,nfft))));\n"); fprintf(fid,"\n"); fprintf(fid,"figure;\n"); fprintf(fid,"plot(f,Ht,'LineWidth',1,'Color',[0.00 0.25 0.50],...\n"); fprintf(fid," f,Hp,'LineWidth',1,'Color',[0.80 0.80 0.80],...\n"); fprintf(fid," f,Hr,'LineWidth',1,'Color',[0.00 0.50 0.25],...\n"); fprintf(fid," f,Hc,'LineWidth',2,'Color',[0.50 0.00 0.00],...\n"); fprintf(fid," [-0.5/k 0.5/k], [1 1]*20*log10(0.5),'or');\n"); fprintf(fid,"legend('transmit','prototype','receive','composite','alias points',1);\n"); fprintf(fid,"xlabel('Normalized Frequency');\n"); fprintf(fid,"ylabel('PSD');\n"); fprintf(fid,"grid on;\n"); fprintf(fid,"axis([-0.5 0.5 -100 20]);\n"); fprintf(fid,"\n"); fprintf(fid,"figure;\n"); fprintf(fid,"tr = [ -k*m:k*m]/k;\n"); fprintf(fid,"tc = [-2*k*m:2*k*m]/k;\n"); fprintf(fid,"ic = [0:k:(4*k*m)]+1;\n"); fprintf(fid,"subplot(2,1,1);\n"); fprintf(fid," plot(tr,ht,'-x', tr,hr,'-x');\n"); fprintf(fid," legend('transmit','receive',1);\n"); fprintf(fid," xlabel('Time');\n"); fprintf(fid," ylabel('GMSK Tx/Rx Filters');\n"); fprintf(fid," grid on;\n"); fprintf(fid," axis([-2*m 2*m floor(5*min([hr ht]))/5 ceil(5*max([hr ht]))/5]);\n"); fprintf(fid,"subplot(2,1,2);\n"); fprintf(fid," plot(tc,hc,'-x', tc(ic),hc(ic),'or');\n"); fprintf(fid," xlabel('Time');\n"); fprintf(fid," ylabel('GMSK Composite Response');\n"); fprintf(fid," grid on;\n"); fprintf(fid," axis([-2*m 2*m -0.2 1.2]);\n"); fprintf(fid," axis([-2*m 2*m floor(5*min(hc))/5 ceil(5*max(hc))/5]);\n"); fclose(fid); printf("results written to %s.\n", OUTPUT_FILENAME); return 0; }
// Design GMSK receive filter // _k : samples/symbol // _m : symbol delay // _beta : rolloff factor (0 < beta <= 1) // _dt : fractional sample delay // _h : output coefficient buffer (length: 2*k*m+1) void liquid_firdes_gmskrx(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h) { // validate input if ( _k < 1 ) { fprintf(stderr,"error: liquid_firdes_gmskrx(): k must be greater than 0\n"); exit(1); } else if ( _m < 1 ) { fprintf(stderr,"error: liquid_firdes_gmskrx(): m must be greater than 0\n"); exit(1); } else if ( (_beta < 0.0f) || (_beta > 1.0f) ) { fprintf(stderr,"error: liquid_firdes_gmskrx(): beta must be in [0,1]\n"); exit(1); } else; unsigned int k = _k; unsigned int m = _m; float BT = _beta; // internal options float beta = BT; // prototype filter cut-off float delta = 1e-3f; // filter design correction factor liquid_firfilt_type prototype = LIQUID_FIRFILT_KAISER; // Nyquist prototype unsigned int i; // derived values unsigned int h_len = 2*k*m+1; // filter length // arrays float ht[h_len]; // transmit filter coefficients float hr[h_len]; // recieve filter coefficients // design transmit filter liquid_firdes_gmsktx(k,m,BT,0.0f,ht); // // start of filter design procedure // // 'internal' arrays float h_primef[h_len]; // temporary buffer for real 'prototype' coefficients float g_primef[h_len]; // temporary buffer for real 'gain' coefficient float complex h_tx[h_len]; // impulse response of transmit filter float complex h_prime[h_len]; // impulse response of 'prototype' filter float complex g_prime[h_len]; // impulse response of 'gain' filter float complex h_hat[h_len]; // impulse response of receive filter float complex H_tx[h_len]; // frequency response of transmit filter float complex H_prime[h_len]; // frequency response of 'prototype' filter float complex G_prime[h_len]; // frequency response of 'gain' filter float complex H_hat[h_len]; // frequency response of receive filter // create 'prototype' matched filter liquid_firdes_nyquist(prototype,k,m,beta,0.0f,h_primef); // create 'gain' filter to improve stop-band rejection float fc = (0.7f + 0.1*beta) / (float)k; float As = 60.0f; liquid_firdes_kaiser(h_len, fc, As, 0.0f, g_primef); // copy to fft input buffer, shifting appropriately for (i=0; i<h_len; i++) { h_prime[i] = h_primef[ (i+k*m)%h_len ]; g_prime[i] = g_primef[ (i+k*m)%h_len ]; h_tx[i] = ht[ (i+k*m)%h_len ]; } // run ffts fft_run(h_len, h_prime, H_prime, LIQUID_FFT_FORWARD, 0); fft_run(h_len, g_prime, G_prime, LIQUID_FFT_FORWARD, 0); fft_run(h_len, h_tx, H_tx, LIQUID_FFT_FORWARD, 0); // find minimum of reponses float H_tx_min = 0.0f; float H_prime_min = 0.0f; float G_prime_min = 0.0f; for (i=0; i<h_len; i++) { if (i==0 || crealf(H_tx[i]) < H_tx_min) H_tx_min = crealf(H_tx[i]); if (i==0 || crealf(H_prime[i]) < H_prime_min) H_prime_min = crealf(H_prime[i]); if (i==0 || crealf(G_prime[i]) < G_prime_min) G_prime_min = crealf(G_prime[i]); } // compute 'prototype' response, removing minima, and add correction factor for (i=0; i<h_len; i++) { // compute response necessary to yeild prototype response (not exact, but close) H_hat[i] = crealf(H_prime[i] - H_prime_min + delta) / crealf(H_tx[i] - H_tx_min + delta); // include additional term to add stop-band suppression H_hat[i] *= crealf(G_prime[i] - G_prime_min) / crealf(G_prime[0]); } // compute ifft and copy response fft_run(h_len, H_hat, h_hat, LIQUID_FFT_BACKWARD, 0); for (i=0; i<h_len; i++) hr[i] = crealf( h_hat[(i+k*m+1)%h_len] ) / (float)(k*h_len); // copy result, scaling by (samples/symbol)^2 for (i=0; i<h_len; i++) _h[i] = hr[i]*_k*_k; }
int main(int argc, char*argv[]) { // options unsigned int k = 8; // filter samples/symbol unsigned int bps= 1; // number of bits/symbol float h = 0.5f; // modulation index (h=1/2 for MSK) unsigned int num_data_symbols = 20; // number of data symbols float SNRdB = 80.0f; // signal-to-noise ratio [dB] float cfo = 0.0f; // carrier frequency offset float cpo = 0.0f; // carrier phase offset float tau = 0.0f; // fractional symbol offset enum { TXFILT_SQUARE=0, TXFILT_RCOS_FULL, TXFILT_RCOS_HALF, TXFILT_GMSK, } tx_filter_type = TXFILT_SQUARE; float gmsk_bt = 0.35f; // GMSK bandwidth-time factor int dopt; while ((dopt = getopt(argc,argv,"ht:k:b:H:B:n:s:F:P:T:")) != EOF) { switch (dopt) { case 'h': usage(); return 0; case 't': if (strcmp(optarg,"square")==0) { tx_filter_type = TXFILT_SQUARE; } else if (strcmp(optarg,"rcos-full")==0) { tx_filter_type = TXFILT_RCOS_FULL; } else if (strcmp(optarg,"rcos-half")==0) { tx_filter_type = TXFILT_RCOS_HALF; } else if (strcmp(optarg,"gmsk")==0) { tx_filter_type = TXFILT_GMSK; } else { fprintf(stderr,"error: %s, unknown filter type '%s'\n", argv[0], optarg); exit(1); } break; case 'k': k = atoi(optarg); break; case 'b': bps = atoi(optarg); break; case 'H': h = atof(optarg); break; case 'B': gmsk_bt = atof(optarg); break; case 'n': num_data_symbols = atoi(optarg); break; case 's': SNRdB = atof(optarg); break; case 'F': cfo = atof(optarg); break; case 'P': cpo = atof(optarg); break; case 'T': tau = atof(optarg); break; default: exit(1); } } unsigned int i; // derived values unsigned int num_symbols = num_data_symbols; unsigned int num_samples = k*num_symbols; unsigned int M = 1 << bps; // constellation size float nstd = powf(10.0f, -SNRdB/20.0f); // arrays unsigned char sym_in[num_symbols]; // input symbols float phi[num_samples]; // transmitted phase float complex x[num_samples]; // transmitted signal float complex y[num_samples]; // received signal float complex z[num_samples]; // output... //unsigned char sym_out[num_symbols]; // output symbols unsigned int ht_len = 0; unsigned int tx_delay = 0; float * ht = NULL; switch (tx_filter_type) { case TXFILT_SQUARE: // regular MSK ht_len = k; tx_delay = 1; ht = (float*) malloc(ht_len *sizeof(float)); for (i=0; i<ht_len; i++) ht[i] = h * M_PI / (float)k; break; case TXFILT_RCOS_FULL: // full-response raised-cosine pulse ht_len = k; tx_delay = 1; ht = (float*) malloc(ht_len *sizeof(float)); for (i=0; i<ht_len; i++) ht[i] = h * M_PI / (float)k * (1.0f - cosf(2.0f*M_PI*i/(float)ht_len)); break; case TXFILT_RCOS_HALF: // partial-response raised-cosine pulse ht_len = 3*k; tx_delay = 2; ht = (float*) malloc(ht_len *sizeof(float)); for (i=0; i<ht_len; i++) ht[i] = 0.0f; for (i=0; i<2*k; i++) ht[i+k/2] = h * 0.5f * M_PI / (float)k * (1.0f - cosf(2.0f*M_PI*i/(float)(2*k))); break; case TXFILT_GMSK: ht_len = 2*k*3+1+k; tx_delay = 4; ht = (float*) malloc(ht_len *sizeof(float)); for (i=0; i<ht_len; i++) ht[i] = 0.0f; liquid_firdes_gmsktx(k,3,gmsk_bt,0.0f,&ht[k/2]); for (i=0; i<ht_len; i++) ht[i] *= h * 2.0f / (float)k; break; default: fprintf(stderr,"error: %s, invalid tx filter type\n", argv[0]); exit(1); } for (i=0; i<ht_len; i++) printf("ht(%3u) = %12.8f;\n", i+1, ht[i]); firinterp_rrrf interp_tx = firinterp_rrrf_create(k, ht, ht_len); // generate symbols and interpolate // phase-accumulating filter (trapezoidal integrator) float b[2] = {0.5f, 0.5f}; if (tx_filter_type == TXFILT_SQUARE) { // square filter: rectangular integration with one sample of delay b[0] = 0.0f; b[1] = 1.0f; } float a[2] = {1.0f, -1.0f}; iirfilt_rrrf integrator = iirfilt_rrrf_create(b,2,a,2); float theta = 0.0f; for (i=0; i<num_symbols; i++) { sym_in[i] = rand() % M; float v = 2.0f*sym_in[i] - (float)(M-1); // +/-1, +/-3, ... +/-(M-1) firinterp_rrrf_execute(interp_tx, v, &phi[k*i]); // accumulate phase unsigned int j; for (j=0; j<k; j++) { iirfilt_rrrf_execute(integrator, phi[i*k+j], &theta); x[i*k+j] = cexpf(_Complex_I*theta); } } iirfilt_rrrf_destroy(integrator); // push through channel for (i=0; i<num_samples; i++) { // add carrier frequency/phase offset y[i] = x[i]*cexpf(_Complex_I*(cfo*i + cpo)); // add noise y[i] += nstd*(randnf() + _Complex_I*randnf())*M_SQRT1_2; } // create decimator unsigned int m = 3; float bw = 0.0f; float beta = 0.0f; firfilt_crcf decim_rx = NULL; switch (tx_filter_type) { case TXFILT_SQUARE: //bw = 0.9f / (float)k; bw = 0.4f; decim_rx = firfilt_crcf_create_kaiser(2*k*m+1, bw, 60.0f, 0.0f); firfilt_crcf_set_scale(decim_rx, 2.0f * bw); break; case TXFILT_RCOS_FULL: if (M==2) { decim_rx = firfilt_crcf_create_rnyquist(LIQUID_FIRFILT_GMSKRX,k,m,0.5f,0); firfilt_crcf_set_scale(decim_rx, 1.33f / (float)k); } else { decim_rx = firfilt_crcf_create_rnyquist(LIQUID_FIRFILT_GMSKRX,k/2,2*m,0.9f,0); firfilt_crcf_set_scale(decim_rx, 3.25f / (float)k); } break; case TXFILT_RCOS_HALF: if (M==2) { decim_rx = firfilt_crcf_create_rnyquist(LIQUID_FIRFILT_GMSKRX,k,m,0.3f,0); firfilt_crcf_set_scale(decim_rx, 1.10f / (float)k); } else { decim_rx = firfilt_crcf_create_rnyquist(LIQUID_FIRFILT_GMSKRX,k/2,2*m,0.27f,0); firfilt_crcf_set_scale(decim_rx, 2.90f / (float)k); } break; case TXFILT_GMSK: bw = 0.5f / (float)k; // TODO: figure out beta value here beta = (M == 2) ? 0.8*gmsk_bt : 1.0*gmsk_bt; decim_rx = firfilt_crcf_create_rnyquist(LIQUID_FIRFILT_GMSKRX,k,m,beta,0); firfilt_crcf_set_scale(decim_rx, 2.0f * bw); break; default: fprintf(stderr,"error: %s, invalid tx filter type\n", argv[0]); exit(1); } printf("bw = %f\n", bw); // run receiver unsigned int n=0; unsigned int num_errors = 0; unsigned int num_symbols_checked = 0; float complex z_prime = 0.0f; for (i=0; i<num_samples; i++) { // push through filter firfilt_crcf_push(decim_rx, y[i]); firfilt_crcf_execute(decim_rx, &z[i]); // decimate output if ( (i%k)==0 ) { // compute instantaneous frequency scaled by modulation index float phi_hat = cargf(conjf(z_prime) * z[i]) / (h * M_PI); // estimate transmitted symbol float v = (phi_hat + (M-1.0))*0.5f; unsigned int sym_out = ((int) roundf(v)) % M; // save current point z_prime = z[i]; // print result to screen printf("%3u : %12.8f + j%12.8f, <f=%8.4f : %8.4f> (%1u)", n, crealf(z[i]), cimagf(z[i]), phi_hat, v, sym_out); if (n >= m+tx_delay) { num_errors += (sym_out == sym_in[n-m-tx_delay]) ? 0 : 1; num_symbols_checked++; printf(" (%1u)\n", sym_in[n-m-tx_delay]); } else { printf("\n"); } n++; } } // print number of errors printf("errors : %3u / %3u\n", num_errors, num_symbols_checked); // destroy objects firinterp_rrrf_destroy(interp_tx); firfilt_crcf_destroy(decim_rx); // compute power spectral density of transmitted signal unsigned int nfft = 1024; float psd[nfft]; spgramcf periodogram = spgramcf_create_kaiser(nfft, nfft/2, 8.0f); spgramcf_estimate_psd(periodogram, y, num_samples, psd); spgramcf_destroy(periodogram); // // export results // FILE * fid = fopen(OUTPUT_FILENAME,"w"); fprintf(fid,"%% %s : auto-generated file\n", OUTPUT_FILENAME); fprintf(fid,"clear all\n"); fprintf(fid,"close all\n"); fprintf(fid,"k = %u;\n", k); fprintf(fid,"h = %f;\n", h); fprintf(fid,"num_symbols = %u;\n", num_symbols); fprintf(fid,"num_samples = %u;\n", num_samples); fprintf(fid,"nfft = %u;\n", nfft); fprintf(fid,"delay = %u; %% receive filter delay\n", tx_delay); fprintf(fid,"x = zeros(1,num_samples);\n"); fprintf(fid,"y = zeros(1,num_samples);\n"); fprintf(fid,"z = zeros(1,num_samples);\n"); fprintf(fid,"phi = zeros(1,num_samples);\n"); for (i=0; i<num_samples; i++) { fprintf(fid,"x(%4u) = %12.8f + j*%12.8f;\n", i+1, crealf(x[i]), cimagf(x[i])); fprintf(fid,"y(%4u) = %12.8f + j*%12.8f;\n", i+1, crealf(y[i]), cimagf(y[i])); fprintf(fid,"z(%4u) = %12.8f + j*%12.8f;\n", i+1, crealf(z[i]), cimagf(z[i])); fprintf(fid,"phi(%4u) = %12.8f;\n", i+1, phi[i]); } // save PSD fprintf(fid,"psd = zeros(1,nfft);\n"); for (i=0; i<nfft; i++) fprintf(fid,"psd(%4u) = %12.8f;\n", i+1, psd[i]); fprintf(fid,"t=[0:(num_samples-1)]/k;\n"); fprintf(fid,"i = 1:k:num_samples;\n"); fprintf(fid,"figure;\n"); fprintf(fid,"subplot(3,4,1:3);\n"); fprintf(fid," plot(t,real(x),'-', t(i),real(x(i)),'bs','MarkerSize',4,...\n"); fprintf(fid," t,imag(x),'-', t(i),imag(x(i)),'gs','MarkerSize',4);\n"); fprintf(fid," axis([0 num_symbols -1.2 1.2]);\n"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('x(t)');\n"); fprintf(fid," grid on;\n"); fprintf(fid,"subplot(3,4,5:7);\n"); fprintf(fid," plot(t-delay,real(z),'-', t(i)-delay,real(z(i)),'bs','MarkerSize',4,...\n"); fprintf(fid," t-delay,imag(z),'-', t(i)-delay,imag(z(i)),'gs','MarkerSize',4);\n"); fprintf(fid," axis([0 num_symbols -1.2 1.2]);\n"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('\"matched\" filter output');\n"); fprintf(fid," grid on;\n"); // plot I/Q constellations fprintf(fid,"subplot(3,4,4);\n"); fprintf(fid," plot(real(y),imag(y),'-',real(y(i)),imag(y(i)),'rs','MarkerSize',3);\n"); fprintf(fid," xlabel('I');\n"); fprintf(fid," ylabel('Q');\n"); fprintf(fid," axis([-1 1 -1 1]*1.2);\n"); fprintf(fid," axis square;\n"); fprintf(fid," grid on;\n"); fprintf(fid,"subplot(3,4,8);\n"); fprintf(fid," plot(real(z),imag(z),'-',real(z(i)),imag(z(i)),'rs','MarkerSize',3);\n"); fprintf(fid," xlabel('I');\n"); fprintf(fid," ylabel('Q');\n"); fprintf(fid," axis([-1 1 -1 1]*1.2);\n"); fprintf(fid," axis square;\n"); fprintf(fid," grid on;\n"); // plot PSD fprintf(fid,"f = [0:(nfft-1)]/nfft - 0.5;\n"); fprintf(fid,"subplot(3,4,9:12);\n"); fprintf(fid," plot(f,psd,'LineWidth',1.5);\n"); fprintf(fid," axis([-0.5 0.5 -40 20]);\n"); fprintf(fid," xlabel('Normalized Frequency [f/F_s]');\n"); fprintf(fid," ylabel('PSD [dB]');\n"); fprintf(fid," grid on;\n"); #if 0 fprintf(fid,"figure;\n"); fprintf(fid," %% compute instantaneous received frequency\n"); fprintf(fid," freq_rx = arg( conj(z(:)) .* circshift(z(:),-1) )';\n"); fprintf(fid," freq_rx(1:(k*delay)) = 0;\n"); fprintf(fid," freq_rx(end) = 0;\n"); fprintf(fid," %% compute instantaneous tx/rx phase\n"); if (tx_filter_type == TXFILT_SQUARE) { fprintf(fid," theta_tx = filter([0 1],[1 -1],phi)/(h*pi);\n"); fprintf(fid," theta_rx = filter([0 1],[1 -1],freq_rx)/(h*pi);\n"); } else { fprintf(fid," theta_tx = filter([0.5 0.5],[1 -1],phi)/(h*pi);\n"); fprintf(fid," theta_rx = filter([0.5 0.5],[1 -1],freq_rx)/(h*pi);\n"); } fprintf(fid," %% plot instantaneous tx/rx phase\n"); fprintf(fid," plot(t, theta_tx,'-b', t(i), theta_tx(i),'sb',...\n"); fprintf(fid," t-delay,theta_rx,'-r', t(i)-delay,theta_rx(i),'sr');\n"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('instantaneous phase/(h \\pi)');\n"); fprintf(fid," legend('transmitted','syms','received/filtered','syms','location','northwest');\n"); fprintf(fid," grid on;\n"); #else // plot filter response fprintf(fid,"ht_len = %u;\n", ht_len); fprintf(fid,"ht = zeros(1,ht_len);\n"); for (i=0; i<ht_len; i++) fprintf(fid,"ht(%4u) = %12.8f;\n", i+1, ht[i]); fprintf(fid,"gt1 = filter([0.5 0.5],[1 -1],ht) / (pi*h);\n"); fprintf(fid,"gt2 = filter([0.0 1.0],[1 -1],ht) / (pi*h);\n"); fprintf(fid,"tfilt = [0:(ht_len-1)]/k - delay + 0.5;\n"); fprintf(fid,"figure;\n"); fprintf(fid,"plot(tfilt,ht, '-x','MarkerSize',4,...\n"); fprintf(fid," tfilt,gt1,'-x','MarkerSize',4,...\n"); fprintf(fid," tfilt,gt2,'-x','MarkerSize',4);\n"); fprintf(fid,"axis([tfilt(1) tfilt(end) -0.1 1.1]);\n"); fprintf(fid,"legend('pulse','trap. int.','rect. int.','location','northwest');\n"); fprintf(fid,"grid on;\n"); #endif fclose(fid); printf("results written to '%s'\n", OUTPUT_FILENAME); // free allocated filter memory free(ht); return 0; }
// Design (root-)Nyquist filter from prototype // _type : filter type (e.g. LIQUID_FIRFILT_RRRC) // _k : samples/symbol // _m : symbol delay // _beta : excess bandwidth factor, _beta in [0,1] // _dt : fractional sample delay // _h : output coefficient buffer (length: 2*k*m+1) void liquid_firdes_prototype(liquid_firfilt_type _type, unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h) { // compute filter parameters unsigned int h_len = 2*_k*_m + 1; // length float fc = 0.5f / (float)_k; // cut-off frequency float df = _beta / (float)_k; // transition bandwidth float As = estimate_req_filter_As(df,h_len); // stop-band attenuation // Parks-McClellan algorithm parameters float bands[6] = { 0.0f, fc-0.5f*df, fc, fc, fc+0.5f*df, 0.5f}; float des[3] = { (float)_k, 0.5f*_k, 0.0f }; float weights[3] = {1.0f, 1.0f, 1.0f}; liquid_firdespm_wtype wtype[3] = { LIQUID_FIRDESPM_FLATWEIGHT, LIQUID_FIRDESPM_FLATWEIGHT, LIQUID_FIRDESPM_FLATWEIGHT}; switch (_type) { // Nyquist filter prototypes case LIQUID_FIRFILT_KAISER: liquid_firdes_kaiser(h_len, fc, As, _dt, _h); break; case LIQUID_FIRFILT_PM: // WARNING: input timing offset is ignored here firdespm_run(h_len, 3, bands, des, weights, wtype, LIQUID_FIRDESPM_BANDPASS, _h); break; case LIQUID_FIRFILT_RCOS: liquid_firdes_rcos(_k, _m, _beta, _dt, _h); break; case LIQUID_FIRFILT_FEXP: liquid_firdes_fexp(_k, _m, _beta, _dt, _h); break; case LIQUID_FIRFILT_FSECH: liquid_firdes_fsech(_k, _m, _beta, _dt, _h); break; case LIQUID_FIRFILT_FARCSECH: liquid_firdes_farcsech(_k, _m, _beta, _dt, _h); break; // root-Nyquist filter prototypes case LIQUID_FIRFILT_ARKAISER: liquid_firdes_arkaiser(_k, _m, _beta, _dt, _h); break; case LIQUID_FIRFILT_RKAISER: liquid_firdes_rkaiser(_k, _m, _beta, _dt, _h); break; case LIQUID_FIRFILT_RRC: liquid_firdes_rrcos(_k, _m, _beta, _dt, _h); break; case LIQUID_FIRFILT_hM3: liquid_firdes_hM3(_k, _m, _beta, _dt, _h); break; case LIQUID_FIRFILT_GMSKTX: liquid_firdes_gmsktx(_k, _m, _beta, _dt, _h); break; case LIQUID_FIRFILT_GMSKRX: liquid_firdes_gmskrx(_k, _m, _beta, _dt, _h); break; case LIQUID_FIRFILT_RFEXP: liquid_firdes_rfexp(_k, _m, _beta, _dt, _h); break; case LIQUID_FIRFILT_RFSECH: liquid_firdes_rfsech(_k, _m, _beta, _dt, _h); break; case LIQUID_FIRFILT_RFARCSECH: liquid_firdes_rfarcsech(_k, _m, _beta, _dt, _h); break; default: fprintf(stderr,"error: liquid_firdes_prototype(), invalid root-Nyquist filter type '%d'\n", _type); exit(1); } }
// // output debugging file // void gmskdem_debug_print(gmskdem _q, const char * _filename) { // open output filen for writing FILE * fid = fopen(_filename,"w"); if (!fid) { fprintf(stderr,"error: gmskdem_debug_print(), could not open '%s' for writing\n", _filename); exit(1); } fprintf(fid,"%% %s : auto-generated file\n", _filename); fprintf(fid,"clear all\n"); fprintf(fid,"close all\n"); #if DEBUG_GMSKDEM // unsigned int i; float * r; fprintf(fid,"n = %u;\n", DEBUG_BUFFER_LEN); fprintf(fid,"k = %u;\n", _q->k); fprintf(fid,"m = %u;\n", _q->m); fprintf(fid,"t = [0:(n-1)]/k;\n"); // plot receive filter response fprintf(fid,"ht = zeros(1,2*k*m+1);\n"); float ht[_q->h_len]; liquid_firdes_gmsktx(_q->k, _q->m, _q->BT, 0.0f, ht); for (i=0; i<_q->h_len; i++) fprintf(fid,"ht(%4u) = %12.4e;\n", i+1, ht[i]); #if GMSKDEM_USE_EQUALIZER float hr[_q->h_len]; eqlms_rrrf_get_weights(_q->eq, hr); for (i=0; i<_q->h_len; i++) fprintf(fid,"hr(%4u) = %12.4e * %u;\n", i+1, hr[i], _q->k); #else for (i=0; i<_q->h_len; i++) fprintf(fid,"hr(%4u) = %12.4e;\n", i+1, _q->h[i]); #endif fprintf(fid,"hc = conv(ht,hr)/k;\n"); fprintf(fid,"nfft = 1024;\n"); fprintf(fid,"f = [0:(nfft-1)]/nfft - 0.5;\n"); fprintf(fid,"Ht = 20*log10(abs(fftshift(fft(ht/k, nfft))));\n"); fprintf(fid,"Hr = 20*log10(abs(fftshift(fft(hr/k, nfft))));\n"); fprintf(fid,"Hc = 20*log10(abs(fftshift(fft(hc/k, nfft))));\n"); fprintf(fid,"figure;\n"); fprintf(fid,"plot(f,Ht, f,Hr, f,Hc,'-k','LineWidth',2);\n"); fprintf(fid,"axis([-0.5 0.5 -50 10]);\n"); fprintf(fid,"xlabel('Normalized Frequency');\n"); fprintf(fid,"ylabel('Power Spectral Density [dB]');\n"); fprintf(fid,"legend('transmit','receive','composite',1);\n"); fprintf(fid,"grid on;\n"); fprintf(fid,"mfout = zeros(1,n);\n"); windowf_read(_q->debug_mfout, &r); for (i=0; i<DEBUG_BUFFER_LEN; i++) fprintf(fid,"mfout(%5u) = %12.4e;\n", i+1, r[i]); fprintf(fid,"i0 = 1; %%mod(k+n,k)+k;\n"); fprintf(fid,"isym = i0:k:n;\n"); fprintf(fid,"figure;\n"); fprintf(fid,"plot(t,mfout,'-', t(isym),mfout(isym),'o','MarkerSize',4);\n"); fprintf(fid,"grid on;\n"); #endif fclose(fid); printf("gmskdem: internal debugging written to '%s'\n", _filename); }
// design transmit filter void cpfskmod_firdes(unsigned int _k, unsigned int _m, float _beta, int _type, float * _ht, unsigned int _ht_len) { unsigned int i; // create filter based on specified type switch(_type) { case LIQUID_CPFSK_SQUARE: // square pulse if (_ht_len != _k) { fprintf(stderr,"error: cpfskmodem_firdes(), invalid filter length (square)\n"); exit(1); } for (i=0; i<_ht_len; i++) _ht[i] = 1.0f; break; case LIQUID_CPFSK_RCOS_FULL: // full-response raised-cosine pulse if (_ht_len != _k) { fprintf(stderr,"error: cpfskmodem_firdes(), invalid filter length (rcos)\n"); exit(1); } for (i=0; i<_ht_len; i++) _ht[i] = 1.0f - cosf(2.0f*M_PI*i/(float)_ht_len); break; case LIQUID_CPFSK_RCOS_PARTIAL: // full-response raised-cosine pulse if (_ht_len != 3*_k) { fprintf(stderr,"error: cpfskmodem_firdes(), invalid filter length (rcos)\n"); exit(1); } // initialize with zeros for (i=0; i<_ht_len; i++) _ht[i] = 0.0f; // adding raised-cosine pulse with half-symbol delay for (i=0; i<2*_k; i++) _ht[i+_k/2] = 1.0f - cosf(2.0f*M_PI*i/(float)(2*_k)); break; case LIQUID_CPFSK_GMSK: // Gauss minimum-shift keying pulse if (_ht_len != 2*_k*_m + _k + 1) { fprintf(stderr,"error: cpfskmodem_firdes(), invalid filter length (gmsk)\n"); exit(1); } // initialize with zeros for (i=0; i<_ht_len; i++) _ht[i] = 0.0f; // adding Gauss pulse with half-symbol delay liquid_firdes_gmsktx(_k,_m,_beta,0.0f,&_ht[_k/2]); break; default: fprintf(stderr,"error: cpfskmodem_firdes(), invalid filter type '%d'\n", _type); exit(1); } // normalize pulse area to unity float ht_sum = 0.0f; for (i=0; i<_ht_len; i++) ht_sum += _ht[i]; for (i=0; i<_ht_len; i++) _ht[i] *= 1.0f / ht_sum; }