From: baobrien Date: Sun, 21 Jan 2018 21:01:28 +0000 (+0000) Subject: Commiting current OFDM work; no major milestone hit; X-Git-Url: http://git.whiteaudio.com/gitweb/?a=commitdiff_plain;h=d1e7e4f334ea4fbf322f37da3a44cf2fdfe51e88;p=freetel-svn-tracking.git Commiting current OFDM work; no major milestone hit; git-svn-id: https://svn.code.sf.net/p/freetel/code@3391 01035d8c-6547-0410-b346-abe4f91aad63 --- diff --git a/codec2-dev/octave/ofdm_lib.m b/codec2-dev/octave/ofdm_lib.m index 32dd02f1..a618bda3 100644 --- a/codec2-dev/octave/ofdm_lib.m +++ b/codec2-dev/octave/ofdm_lib.m @@ -91,8 +91,8 @@ function [t_est foff_est] = coarse_sync(states, rx, rate_fs_pilot_samples) foff_est = foff_est_neg - fmax - 1; end + %printf("t_est: %d\n", t_est); if verbose > 1 - %printf("t_est: %d\n", t_est); figure(7); clf; plot(abs(corr)) figure(8) @@ -428,6 +428,166 @@ function [rx_bits states aphase_est_pilot_log rx_np rx_amp] = ofdm_demod(states, states.foff_est_hz = foff_est_hz; endfunction +function [rx_bits states aphase_est_pilot_log rx_np rx_amp] = ofdm_demod2(states, rxbuf_in) + ofdm_load_const; + + % insert latest input samples into rxbuf + + rxbuf(1:Nrxbuf-states.nin) = rxbuf(states.nin+1:Nrxbuf); + rxbuf(Nrxbuf-states.nin+1:Nrxbuf) = rxbuf_in; + + % get latest freq offset estimate + + woff_est = 2*pi*foff_est_hz/Fs; + + % update timing estimate -------------------------------------------------- + + delta_t = 0; + if timing_en + % update timing at start of every frame + + st = M+Ncp + Nsamperframe + 1 - floor(ftwindow_width/2) + (timing_est-1); + en = st + Nsamperframe-1 + M+Ncp + ftwindow_width-1; + + ft_est = coarse_sync(states, rxbuf(st:en) .* exp(-j*woff_est*(st:en)), rate_fs_pilot_samples); + timing_est = timing_est + ft_est - ceil(ftwindow_width/2); + + if verbose > 1 + printf(" ft_est: %2d timing_est: %2d sample_point: %2d\n", ft_est, timing_est, sample_point); + end + + % Black magic to keep sample_point inside cyclic prefix. Or something like that. + + delta_t = ft_est - ceil(ftwindow_width/2); + sample_point = max(timing_est+Ncp/4, sample_point); + sample_point = min(timing_est+Ncp, sample_point); + end + + % down convert at current timing instant---------------------------------- + + % todo: this cld be more efficent, as pilot r becomes r-Ns on next frame + + rx_sym = zeros(1+Ns+1+1, Nc+2); + + % previous pilot + + st = M+Ncp + Nsamperframe + (-Ns)*(M+Ncp) + 1 + sample_point; en = st + M - 1; + + for c=1:Nc+2 + acarrier = rxbuf(st:en) .* exp(-j*woff_est*(st:en)) .* conj(W(c,:)); + rx_sym(1,c) = sum(acarrier); + end + + % pilot - this frame - pilot + + for rr=1:Ns+1 + st = M+Ncp + Nsamperframe + (rr-1)*(M+Ncp) + 1 + sample_point; en = st + M - 1; + for c=1:Nc+2 + acarrier = rxbuf(st:en) .* exp(-j*woff_est*(st:en)) .* conj(W(c,:)); + rx_sym(rr+1,c) = sum(acarrier); + end + end + + % next pilot + + st = M+Ncp + Nsamperframe + (2*Ns)*(M+Ncp) + 1 + sample_point; en = st + M - 1; + for c=1:Nc+2 + acarrier = rxbuf(st:en) .* exp(-j*woff_est*(st:en)) .* conj(W(c,:)); + rx_sym(Ns+3,c) = sum(acarrier); + end + + % est freq err based on all carriers ------------------------------------ + + if foff_est_en + freq_err_rect = sum(rx_sym(2,:))' * sum(rx_sym(2+Ns,:)); + + % prevent instability in atan(im/re) when real part near 0 + + freq_err_rect += 1E-6; + + %printf("freq_err_rect: %f %f angle: %f\n", real(freq_err_rect), imag(freq_err_rect), angle(freq_err_rect)); + freq_err_hz = angle(freq_err_rect)*Rs/(2*pi*Ns); + foff_est_hz = foff_est_hz + foff_est_gain*freq_err_hz; + end + + % OK - now estimate and correct phase ---------------------------------- + + aphase_est_pilot = 10*ones(1,Nc+2); + aamp_est_pilot = zeros(1,Nc+2); + for c=2:Nc+1 + + % estimate phase using average of 6 pilots in a rect 2D window centred + % on this carrier + % PPP + % DDD + % DDD + % PPP + + cr = c-1:c+1; + aphase_est_pilot_rect = sum(rx_sym(2,cr)*pilots(cr)') + sum(rx_sym(2+Ns,cr)*pilots(cr)'); + + % use next step of pilots in past and future + + aphase_est_pilot_rect += sum(rx_sym(1,cr)*pilots(cr)'); + aphase_est_pilot_rect += sum(rx_sym(2+Ns+1,cr)*pilots(cr)'); + + aphase_est_pilot(c) = angle(aphase_est_pilot_rect); + aamp_est_pilot(c) = abs(aphase_est_pilot_rect/12); + end + + % correct phase offset using phase estimate, and demodulate + % bits, separate loop as it runs across cols (carriers) to get + % frame bit ordering correct + + aphase_est_pilot_log = []; + rx_bits = []; rx_np = []; rx_amp = []; + for rr=1:Ns-1 + for c=2:Nc+1 + if phase_est_en + rx_corr = rx_sym(rr+2,c) * exp(-j*aphase_est_pilot(c)); + else + rx_corr = rx_sym(rr+2,c); + end + rx_np = [rx_np rx_corr]; + rx_amp = [rx_amp aamp_est_pilot(c)]; + if bps == 1 + abit = real(rx_corr) > 0; + end + if bps == 2 + abit = qpsk_demod(rx_corr); + end + rx_bits = [rx_bits abit]; + end % c=2:Nc+1 + aphase_est_pilot_log = [aphase_est_pilot_log; aphase_est_pilot(2:Nc+1)]; + end + + % Adjust nin to take care of sample clock offset + + nin = Nsamperframe; + if timing_en + thresh = (M+Ncp)/8; + tshift = (M+Ncp)/4; + if timing_est > thresh + nin = Nsamperframe+tshift; + timing_est -= tshift; + sample_point -= tshift; + end + if timing_est < -thresh + nin = Nsamperframe-tshift; + timing_est += tshift; + sample_point += tshift; + end + end + + states.rx_sym = rx_sym; + states.rxbuf = rxbuf; + states.nin = nin; + states.timing_est = timing_est; + states.sample_point = sample_point; + states.delta_t = delta_t; + states.foff_est_hz = foff_est_hz; +endfunction + % Save test bits frame to a text file in the form of a C array % diff --git a/codec2-dev/octave/tofdm.m b/codec2-dev/octave/tofdm.m index 7fab0858..88a6dd66 100644 --- a/codec2-dev/octave/tofdm.m +++ b/codec2-dev/octave/tofdm.m @@ -6,7 +6,22 @@ % ------------------------------------------------------------------ 1; -function pass = run_ofdm_test(Nframes,sample_clock_offset_ppm,foff_hz) +function rx = ebno_awgn_channel(tx, Fs, Rb, EbNodB) + EbNo = 10^(EbNodB/10); + variance = Fs / (Rb*EbNo); + noise = sqrt(variance)*randn(1,length(tx)); + avg = sum(abs(tx))/length(tx) + rx = noise.*avg + tx; +end + +function nums = im_re_interleave(nim) + nums = zeros(1,length(nim)*2); + nums(1:2:length(nums)) = real(nim); + nums(2:2:length(nums)) = imag(nim); + +end + +function pass = run_ofdm_test(Nframes,sample_clock_offset_ppm,foff_hz,EbNodB = 100,nheadsamp = 0) %Nframes = 30; %sample_clock_offset_ppm = 100; %foff_hz = 5; @@ -20,10 +35,11 @@ function pass = run_ofdm_test(Nframes,sample_clock_offset_ppm,foff_hz) % --------------------------------------------------------------------- Ts = 0.018; Tcp = 0.002; Rs = 1/Ts; bps = 2; Nc = 16; Ns = 8; + Rb_real = 700; states = ofdm_init(bps, Rs, Tcp, Ns, Nc); ofdm_load_const; - rand('seed',1); + rand('seed',2); tx_bits = round(rand(1,Nbitsperframe)); % Run tx loop @@ -38,6 +54,15 @@ function pass = run_ofdm_test(Nframes,sample_clock_offset_ppm,foff_hz) rx_log = sample_clock_offset(tx_log, sample_clock_offset_ppm); rx_log = freq_shift(rx_log, foff_hz, Fs); + #rx_log(1:nheadsamp) = zeros(nheadsamp,1); + rx_log_l = length(rx_log) + rx_log = [zeros(1,nheadsamp), rx_log]; + rx_log = rx_log(1:rx_log_l); + rx_log = ebno_awgn_channel(rx_log, Fs, Rb_real, EbNodB); + + rx_vec = fopen("tofdm_rx_vec","wb+"); + fwrite(rx_vec,im_re_interleave(rx_log),"single"); + fclose(rx_vec); % Rx --------------------------------------------------------------- @@ -63,6 +88,7 @@ function pass = run_ofdm_test(Nframes,sample_clock_offset_ppm,foff_hz) states.sample_point = Ncp; end + %states.foff_est_hz = 10; for f=1:Nframes % insert samples at end of buffer, set to zero if no samples @@ -78,7 +104,7 @@ function pass = run_ofdm_test(Nframes,sample_clock_offset_ppm,foff_hz) end prx += lnew; - [rx_bits states aphase_est_pilot_log arx_np arx_amp] = ofdm_demod(states, rxbuf_in); + [rx_bits states aphase_est_pilot_log arx_np arx_amp] = ofdm_demod2(states, rxbuf_in); % log some states for comparison to C @@ -161,7 +187,14 @@ function pass = run_ofdm_test(Nframes,sample_clock_offset_ppm,foff_hz) pass = check(foff_hz_log, foff_hz_log_c', 'foff_est_hz') && pass; pass = check(rx_bits_log, rx_bits_log_c, 'rx_bits') && pass; + figure(fg++) + stem(timing_est_log-timing_est_log_c') + figure(fg++) + stem(phase_est_pilot_log-phase_est_pilot_log_c) end -run_ofdm_test(30,100,.1) \ No newline at end of file +% This works best on my machine -- Brady +graphics_toolkit('fltk') + +run_ofdm_test(60,100,.5,60 ,0) \ No newline at end of file diff --git a/codec2-dev/src/ofdm.c b/codec2-dev/src/ofdm.c index a3d5c1a2..bcca1eff 100644 --- a/codec2-dev/src/ofdm.c +++ b/codec2-dev/src/ofdm.c @@ -40,6 +40,18 @@ #include "kiss_fft.h" #include "modem_probe.h" +/* + * Optimization TODOs + * CPU: + * [ ] Remove cexpf() from loops (can be replaced with single cexpf() and multiplication in loop) + * [ ] Look for/fix potential aliasing slowdowns (ie using array[i] as accumulator) + * [ ] Possibly dd a bunch of const arrays and maybe figure out restrict + * [ ] Run a bit of profiling on ofdm + * + * Mem: + * [ ] Make RX debug logs optional or modem probes + */ + /* Concrete definition of 700D parameters */ const struct OFDM_CONFIG OFDM_CONFIG_700D_C = { .Nc = 16, @@ -51,6 +63,7 @@ const struct OFDM_CONFIG OFDM_CONFIG_700D_C = { .Ns = 8, .M = 144, .Ncp = 16, + .Fcenter = 1500, .FtWindowWidth = 11, .BitsPerFrame = 224, .SampsPerFrame = 1280, @@ -70,7 +83,8 @@ static complex float vector_sum(complex float *, int); static complex float qpsk_mod(int *); static void qpsk_demod(complex float, int *); static void ofdm_txframe(struct OFDM *, complex float [], complex float *); -static int coarse_sync(struct OFDM *, complex float *, int); +//static int coarse_sync(struct OFDM *, complex float *, int); +static int coarse_sync(struct OFDM *ofdm, complex float *rx, int length, float *foff_out); /* Defines */ @@ -162,13 +176,122 @@ static complex float vector_sum(complex float *a, int num_elements) { return sum; } +static int coarser_sync(struct OFDM *ofdm, complex float *rx, int length, float *foff_out, float *ratio_out) { + complex float csam; + const int Fs = ofdm->config.Fs; + const int M = ofdm->config.M; + const int Ncp = ofdm->config.Ncp; + const int SampsPerFrame = ofdm->config.SampsPerFrame; + const int Ncorr = length - (M + Ncp); + + float corr[Ncorr]; + float corr_t; + int NPSamp = M + Ncp; + int i, j; + float max_corr = 0; + float max_mag = 0; + float mag = 0.0f; + int t_est = 0; + int fmax = 60; + int pmax_i,nmax_i; + float pmax,nmax; + float foff_est; + + for (i = 0; i < Ncorr; i++) { + complex float temp = 0.0f + 0.0f * I; + + for (j = 0; j < (M + Ncp); j++) { + csam = conjf(ofdm->pilot_samples[j]); + temp = temp + (rx[i + j] * csam); + //temp = temp + (rx[i + j + SampsPerFrame] * csam); + } + + corr_t = cabsf(temp); + /* Magnitude of incoming samples */ + //mag = max( cabsf(rx[i]) , cabsf(rx[i+SampsPerFrame]) ); + mag = cabsf(rx[i]); + max_mag = max(mag, max_mag); + + /* find the max magnitude and its index */ + if(corr_t > max_corr){ + max_corr = corr_t; + t_est = i; + } + } + + if(ratio_out != NULL) + *ratio_out = max_corr/max_mag; + //fprintf(stderr,"Ratio is %f; mag is %f corr is %f\n",max_corr/max_mag,max_mag,max_corr); + + /* Skip coarse frequency est if we don't have anywhere to put result */ + if(foff_out == NULL){ + return t_est; + } + + /* Coarse frequency estimation */ + kiss_fft_cfg fftcfg = ofdm->sync_fft_cfg; + complex float fft_in[Fs]; + complex float fft_out[Fs]; + float C[Fs]; + /* Zero FFT input array */ + for(i = 0; i < Fs; i++){ + fft_in[i] = 0; + } + /* shift and copy in NPsam samples to front of buffer for FFT'ing */ + for(i = 0; i < NPSamp; i++){ + fft_in[i] = rx[i + t_est] * conjf(ofdm->pilot_samples[i]); + } + kiss_fft(fftcfg,(kiss_fft_cpx*)fft_in,(kiss_fft_cpx*)fft_out); + + /* Copy into output corr array, taking magnitude */ + /* TODO: May be able to skip sqrt() in abs, since we're just looking for a maximum point down the line */ + for(i = 0; i < Fs; i++){ + C[i] = cabsf(fft_out[i]); + } + + // /* shift and copy in NPsam samples to front of buffer for FFT'ing */ + // for(i = 0; i < NPSamp; i++){ + // fft_in[i] = rx[i + t_est + SampsPerFrame] * conjf(ofdm->pilot_samples[i]); + // } + // kiss_fft(fftcfg,(kiss_fft_cpx*)fft_in,(kiss_fft_cpx*)fft_out); + + // /* Add into output corr array */ + // for(i = 0; i < Fs; i++){ + // C[i] += cabsf(fft_out[i]); + // } + + pmax_i = 0; + nmax_i = Fs; + pmax = nmax = 0; + /* Search the positive and negative sides of the FFT to +/-fmax for peak */ + for(i = 0; i < fmax; i++){ + if(C[i] > pmax){ + pmax = C[i]; + pmax_i = i; + } + } + for(i = Fs-1; i > Fs-fmax; i--){ + if(C[i] > nmax){ + nmax = C[i]; + nmax_i = i; + } + } + foff_est = (pmax > nmax) ? pmax_i : (nmax_i-Fs+1); + + fprintf(stderr,"foff_est is %f, tpos is %d, ratio is %f\n",foff_est,t_est,max_mag/max_corr); + + *foff_out = foff_est; + + return t_est; +} + /* * Correlates the OFDM pilot symbol samples with a window of received * samples to determine the most likely timing offset. Combines two * frames pilots so we need at least Nsamperframe+M+Ncp samples in rx. */ -static int coarse_sync(struct OFDM *ofdm, complex float *rx, int length) { +static int coarse_sync(struct OFDM *ofdm, complex float *rx, int length, float *foff_out) { complex float csam; const int Fs = ofdm->config.Fs; const int M = ofdm->config.M; @@ -177,8 +300,17 @@ static int coarse_sync(struct OFDM *ofdm, complex float *rx, int length) { const int Ncorr = length - (SampsPerFrame + (M + Ncp)); float corr[Ncorr]; + float corr_t; int NPSamp = M + Ncp; int i, j; + float max_corr = 0; + float max_mag = 0; + float mag = 0.0f; + int t_est = 0; + int fmax = 60; + int pmax_i,nmax_i; + float pmax,nmax; + float foff_est; for (i = 0; i < Ncorr; i++) { complex float temp = 0.0f + 0.0f * I; @@ -189,23 +321,24 @@ static int coarse_sync(struct OFDM *ofdm, complex float *rx, int length) { temp = temp + (rx[i + j + SampsPerFrame] * csam); } - corr[i] = cabsf(temp); - } - - /* find the max magnitude and its index */ - - float mag = 0.0f; - int t_est = 0; - - for (i = 0; i < Ncorr; i++) { - if (corr[i] > mag) { - mag = corr[i]; + corr_t = cabsf(temp); + /* Magnitude of incoming samples */ + mag = max( cabsf(rx[i]) , cabsf(rx[i+SampsPerFrame]) ); + max_mag = max(mag, max_mag); + + /* find the max magnitude and its index */ + if(corr_t > max_corr){ + max_corr = corr_t; t_est = i; } } + /* Skip coarse frequency est if we don't have anywhere to put result */ + if(foff_out == NULL){ + return t_est; + } + /* Coarse frequency estimation */ - /* TODO: Move FFT config to ofdm init and ofdm struct */ kiss_fft_cfg fftcfg = ofdm->sync_fft_cfg; complex float fft_in[Fs]; complex float fft_out[Fs]; @@ -237,11 +370,8 @@ static int coarse_sync(struct OFDM *ofdm, complex float *rx, int length) { C[i] += cabsf(fft_out[i]); } - int fmax = 30; - int pmax_i,nmax_i; - float pmax,nmax; - float foff_est; - pmax_i = nmax_i = 0; + pmax_i = 0; + nmax_i = Fs; pmax = nmax = 0; /* Search the positive and negative sides of the FFT to +/-fmax for peak */ for(i = 0; i < fmax; i++){ @@ -249,13 +379,18 @@ static int coarse_sync(struct OFDM *ofdm, complex float *rx, int length) { pmax = C[i]; pmax_i = i; } - if(C[Fs-i-1] > nmax){ + } + for(i = Fs-1; i > Fs-fmax; i--){ + if(C[i] > nmax){ nmax = C[i]; nmax_i = i; } } - foff_est = (pmax > nmax) ? pmax_i : (nmax_i - fmax); - //fprintf(stderr,"foff_est is %f\n",foff_est); + foff_est = (pmax > nmax) ? pmax_i : (nmax_i-Fs+1); + //foff_est = pmax_i; + //`fprintf(stderr,"foff_est is %f, tpos is %d\n",foff_est,t_est); + + *foff_out = foff_est; return t_est; } @@ -337,15 +472,18 @@ static void ofdm_txframe(struct OFDM *ofdm, complex float tx[], */ void **alloc_doubleary(size_t sx,size_t sy,size_t elem){ size_t i; + /* Allocate pointer array */ char ** ary = malloc(sizeof(void*) * sx); if(ary == NULL){ return NULL; } + /* Allocate data array. */ *ary = malloc(elem * sx * sy); if(*ary == NULL){ free(ary); return NULL; } + /* Fill out pointer array to point to rows of data array */ for(i=0; iconfig,(void*)config,sizeof(struct OFDM_CONFIG)); const int Fs = ofdm->config.Fs; - const int Rs = ofdm->config.Rs; + const float Rs = ofdm->config.Rs; const int Nc = ofdm->config.Nc; const int M = ofdm->config.M; const int Ncp = ofdm->config.Ncp; @@ -417,6 +555,9 @@ struct OFDM *ofdm_create(const struct OFDM_CONFIG * config) { ofdm->rxbuf = malloc(sizeof(complex float) * ofdm->config.RxBufSize); if(ofdm->rxbuf == NULL) goto ofdm_fail_cleanup; + ofdm->coarse_rxbuf = malloc(sizeof(complex float) * ofdm->config.RxBufSize); + if(ofdm->coarse_rxbuf == NULL) goto ofdm_fail_cleanup; + ofdm->w = malloc(sizeof(float) * (Nc+2)); if(ofdm->w == NULL) goto ofdm_fail_cleanup; @@ -436,8 +577,10 @@ struct OFDM *ofdm_create(const struct OFDM_CONFIG * config) { } /* carrier tables for up and down conversion */ - - int Nlower = floorf(( (float)(Fcenter) - Rs * ((float)Nc / 2)) / Rs); + float Fcf = (float)Fcenter; + float Ncf = (float)Nc; + float Fsf = (float)Fs; + int Nlower = floorf(( Fcf - Rs * (Ncf / 2)) / Rs); for (i = 0, j = Nlower; i < (Nc + 2); i++, j++) { /* @@ -445,7 +588,7 @@ struct OFDM *ofdm_create(const struct OFDM_CONFIG * config) { * j = 1 kHz to 2 kHz (1.5 kHz center) */ - ofdm->w[i] = TAU * (float) j / ((float)Fs / Rs); + ofdm->w[i] = TAU * (float) j / (Fsf / Rs); } ofdm->W = (complex float **) alloc_doubleary(Nc+2,M,sizeof(complex float)); @@ -466,6 +609,10 @@ struct OFDM *ofdm_create(const struct OFDM_CONFIG * config) { ofdm->rx_sym[i][j] = 0.0f + 0.0f * I; } } + + for(i = 0; i < ofdm->config.RxBufSize; i++){ + ofdm->rxbuf[i] = 0; + } /* default settings of options and states */ @@ -479,6 +626,8 @@ struct OFDM *ofdm_create(const struct OFDM_CONFIG * config) { ofdm->sample_point = 0; ofdm->timing_est = 0; ofdm->nin = SampsPerFrame; + ofdm->sync_count = 0; + ofdm->frame_point = 0; /* create the OFDM waveform */ @@ -511,6 +660,7 @@ struct OFDM *ofdm_create(const struct OFDM_CONFIG * config) { free_nc(ofdm->pilots); free_nc(ofdm->pilot_samples); free_nc(ofdm->rxbuf); + free_nc(ofdm->coarse_rxbuf); free_nc(ofdm->w); free_nc(ofdm->rx_amp); free_nc(ofdm->aphase_est_pilot_log); @@ -588,7 +738,12 @@ void ofdm_mod(struct OFDM *ofdm, COMP result[], const int *tx_bits) { const int BitsPerFrame = ofdm->config.BitsPerFrame; int length = BitsPerFrame / Bps; - complex float tx[SampsPerFrame]; + + + /* Note: COMP and complex float have the same memory layout */ + /* see iso/iec 9899:1999 sec 6.2.5.13 */ + complex float * tx = (complex float *)&result[0]; + complex float tx_sym_lin[length]; int dibit[2]; int s, i; @@ -612,10 +767,38 @@ void ofdm_mod(struct OFDM *ofdm, COMP result[], const int *tx_bits) { ofdm_txframe(ofdm, tx, tx_sym_lin); /* convert to comp */ - - for (i = 0; i < SampsPerFrame; i++) { + /* Note: COMP and complex float have the same memory layout */ + /* see iso/iec 9899:1999 sec 6.2.5.13 */ + /*for (i = 0; i < SampsPerFrame; i++) { result[i].real = crealf(tx[i]); result[i].imag = cimagf(tx[i]); + }*/ +} + +/* + * Like ofdm_demod, but handles sync and large frame offset + */ +void ofdm_demod_coarse(struct OFDM *ofdm, int *rx_bits, COMP *rxbuf_in){ + const int SampsPerFrame = ofdm->config.SampsPerFrame; + const int RxBufSize = ofdm->config.RxBufSize; + const int Fs = ofdm->config.Fs; + + int sync = ofdm->sync_count; + int frame_point = ofdm->frame_point; + int nin = ofdm->nin; + complex float * coarse_rxbuf = ofdm->coarse_rxbuf; + complex float rxbuf_temp[RxBufSize]; + + /* NCO for freq. shifting */ + complex float shift_nco = 1; + complex float shift_nco_dph = 0; + + /* Copy in nin samples to end of buffer */ + memmove(&coarse_rxbuf[0],&coarse_rxbuf[nin],nin); + memcpy (&coarse_rxbuf[RxBufSize - nin],rxbuf_in,nin); + + if(!sync) { + } } @@ -639,12 +822,19 @@ void ofdm_demod(struct OFDM *ofdm, int *rx_bits, COMP *rxbuf_in) { const int SampsPerFrame = ofdm->config.SampsPerFrame; const int FtWindowWidth = ofdm->config.FtWindowWidth; const int RxBufSize = ofdm->config.RxBufSize; + int nin = ofdm->nin; float aphase_est_pilot[Nc + 2]; float aamp_est_pilot[Nc + 2]; float freq_err_hz; int i, j, k, rr, st, en, ft_est; + complex float * rxbuf = ofdm->rxbuf; + + + //memmove(&rxbuf[0],&rxbuf[nin],nin); + //memcpy (&rxbuf[RxBufSize - nin],rxbuf_in,nin); + /* shift the buffer left based on nin */ for (i = 0, j = ofdm->nin; i < (RxBufSize - ofdm->nin); i++, j++) { @@ -652,16 +842,19 @@ void ofdm_demod(struct OFDM *ofdm, int *rx_bits, COMP *rxbuf_in) { } /* insert latest input samples onto tail of rxbuf */ - + /* Note: COMP and complex float have the same memory layout */ + /* see iso/iec 9899:1999 sec 6.2.5.13 */ for (i = (RxBufSize - ofdm->nin), j = 0; i < RxBufSize; i++, j++) { ofdm->rxbuf[i] = rxbuf_in[j].real + rxbuf_in[j].imag * I; } + /* * get user and calculated freq offset */ float woff_est = TAU * ofdm->foff_est_hz / (float)(Fs); + float freq_offset = -1; /* update timing estimate -------------------------------------------------- */ @@ -683,12 +876,18 @@ void ofdm_demod(struct OFDM *ofdm, int *rx_bits, COMP *rxbuf_in) { work[j] = ofdm->rxbuf[i] * cexpf(-I * woff_est * i); } - ft_est = coarse_sync(ofdm, work, (en - st)); + ft_est = coarse_sync(ofdm, work, (en - st), &freq_offset); + + coarser_sync(ofdm, work, (en - st), &freq_offset, NULL); + + //ofdm->foff_est_hz += (freq_offset/2); ofdm->timing_est += (ft_est - ceilf(FtWindowWidth / 2)); if (ofdm->verbose > 1) { fprintf(stdout, " ft_est: %2d timing_est: %2d sample_point: %2d\n", ft_est, ofdm->timing_est, ofdm->sample_point); } + + //fprintf(stderr," freq_est: %f timing_est: %d sample_pt: %d\n",freq_offset,ofdm->timing_est,ofdm->sample_point); /* Black magic to keep sample_point inside cyclic prefix. Or something like that. */ @@ -877,7 +1076,7 @@ void ofdm_demod(struct OFDM *ofdm, int *rx_bits, COMP *rxbuf_in) { freq_err_rect = freq_err_rect + 1E-6f; - freq_err_hz = cargf(freq_err_rect) * Rs / (TAU * Ns); + freq_err_hz = cargf(freq_err_rect) * Rs / (TAU * (float)Ns); ofdm->foff_est_hz += (ofdm->foff_est_gain * freq_err_hz); } diff --git a/codec2-dev/src/ofdm_internal.h b/codec2-dev/src/ofdm_internal.h index 8f886207..bf1f1e83 100644 --- a/codec2-dev/src/ofdm_internal.h +++ b/codec2-dev/src/ofdm_internal.h @@ -45,93 +45,51 @@ extern "C" { #define TAU (2.0f * M_PI) /* mathematical constant */ -//#define OFDM_NCX 16 /* N Carriers */ -//#define OFDM_TS 0.018f /* Symbol time */ -//#define OFDM_RS (1.0f / OFDM_TS) /* Symbol rate */ -//#define OFDM_FS 8000.0f /* Sample rate */ -//#define OFDM_BPS 2 /* Bits per symbol */ -//#define OFDM_TCP 0.002f /* ? */ -//#define OFDM_NS 8 /* Symbols per frame (number of rows incl pilot) */ -//#define OFDM_CENTRE 1500.0f /* Center frequency */ - -/* To prevent C99 warning */ - -//#define OFDM_M 144 /* Samples per bare symbol (?) */ -//#define OFDM_NCP 16 /* Samples per cyclic prefix */ -// -//#ifdef OLD_STYLE -///* This will produce a warning in C99 as (int) makes these variable */ -// -//#define OFDM_M ((int)(OFDM_FS / OFDM_RS)) -//#define OFDM_NCP ((int)(OFDM_TCP * OFDM_FS)) -//#endif - -///* ? */ -//#define OFDM_FTWINDOWWIDTH 11 -///* Bits per frame (duh) */ -//#define OFDM_BITSPERFRAME ((OFDM_NS - 1) * (OFDM_NCX * OFDM_BPS)) -///* Rows per frame */ -//#define OFDM_ROWSPERFRAME (OFDM_BITSPERFRAME / (OFDM_NCX * OFDM_BPS)) -///* Samps per frame */ -//#define OFDM_SAMPLESPERFRAME (OFDM_NS * (OFDM_M + OFDM_NCP)) -// -//#define OFDM_MAX_SAMPLESPERFRAME (OFDM_SAMPLESPERFRAME + (OFDM_M + OFDM_NCP)/4) -//#define OFDM_RXBUF (3 * OFDM_SAMPLESPERFRAME + 3 * (OFDM_M + OFDM_NCP)) - - -/* Dummy struct for now, will contain constant configuration for OFDM modem */ struct OFDM_CONFIG{ - int32_t Nc; - float Ts; - float Rs; - int32_t Fs; - int32_t bps; - int32_t Tcp; - int32_t Ns; - int32_t Fcenter; - int32_t M; - int32_t Ncp; - int32_t FtWindowWidth; - int32_t BitsPerFrame; - int32_t SampsPerFrame; - int32_t SampsPerFrameMax; - int32_t RxBufSize; - int32_t RowsPerFrame; + int32_t Nc; /* N carriers*/ + float Ts; /* Symbol time (S) (no CP) */ + float Rs; /* Symbol rate (S) (no CP) */ + int32_t Fs; /* Sample rate */ + int32_t bps; /* Bits per symbol */ + int32_t Tcp; /* Cyclic Prefix time (S) */ + int32_t Ns; /* Symbols per frame, including pilot */ + int32_t Fcenter; /* Center frequency */ + int32_t M; /* Samples per symbol (no CP) */ + int32_t Ncp; /* Samples of cyclic prefix */ + int32_t FtWindowWidth; /* ? */ + int32_t BitsPerFrame; /* Bits in a frame */ + int32_t SampsPerFrame; /* Samples in a frame */ + int32_t SampsPerFrameMax; /* Maximum samples per demod cycle */ + int32_t RxBufSize; /* RX buffer size */ + int32_t RowsPerFrame; /* Symbol depth per frame, no prefix */ }; struct OFDM { - struct OFDM_CONFIG config; - float foff_est_gain; - float foff_est_hz; + struct OFDM_CONFIG config; /* OFDM configuration (see above) */ + float foff_est_gain; /* ? */ + float foff_est_hz; /* Estimated frequency offset */ - int verbose; - int sample_point; - int timing_est; - int nin; + int verbose; /* Printing verbosity level */ + int sample_point; /* Fine timing offset */ + int timing_est; /* Ditto (?) (Coarse timing, maybe?) */ + int nin; /* Samples expected next demod cycle */ + int frame_point; + int sync_count; - bool timing_en; - bool foff_est_en; - bool phase_est_en; - - //complex float pilot_samples[OFDM_M + OFDM_NCP]; - //complex float W[OFDM_NCX + 2][OFDM_M]; - //complex float rxbuf[OFDM_RXBUF]; - //complex float pilots[OFDM_NC + 2]; - //float w[OFDM_NC + 2]; + bool timing_en; /* Enable fine timing estimation */ + bool foff_est_en; /* Enable frequency offset estimation/tracking */ + bool phase_est_en; /* Enable Phase offset estimation */ complex float ** W; complex float * pilots; complex float * pilot_samples; complex float * rxbuf; float * w; - kiss_fft_cfg sync_fft_cfg; + kiss_fft_cfg sync_fft_cfg; - /* Demodulator data */ + complex float * coarse_rxbuf; - //complex float rx_sym[OFDM_NS + 3][OFDM_NCX + 2]; - //complex float rx_np[OFDM_ROWSPERFRAME * OFDM_NCX]; - //float rx_amp[OFDM_ROWSPERFRAME * OFDM_NCX]; - //float aphase_est_pilot_log[OFDM_ROWSPERFRAME * OFDM_NCX]; + /* Demodulator data */ complex float ** rx_sym; float * rx_amp; diff --git a/codec2-dev/src/tdma.c b/codec2-dev/src/tdma.c index 8be2a8a2..b3cee92d 100644 --- a/codec2-dev/src/tdma.c +++ b/codec2-dev/src/tdma.c @@ -439,6 +439,7 @@ void tdma_rx_pilot_sync(tdma_t * tdma){ /* Check to see if the bits are outside of the demod buffer. If so, adjust and re-demod*/ /* Disabled for now; will re-enable when worked out better in head */ + /* No, this is enabled. I have it more or less worked out */ if(f_valid && !repeat_demod){ if((f_start < bits_per_sym) || ((f_start+(bits_per_sym*frame_size)) > (bits_per_sym*(slot_size+1)))){ repeat_demod = true; @@ -462,10 +463,11 @@ void tdma_rx_pilot_sync(tdma_t * tdma){ }while(repeat_demod); i32 single_slot_offset = slot->slot_local_frame_offset; - /* Flag indicating wether or not we should call the callback */ + /* Flag indicating whether or not we should call the callback */ bool do_frame_found_call = false; /* Do single slot state machine */ + /* TODO: think about/play around with other fsk_clear_estimators() positions */ if( slot->state == rx_sync){ do_frame_found_call = true; if(!f_valid){ /* on bad UW, increment bad uw count and possibly unsync */ @@ -526,6 +528,10 @@ void tdma_rx_pilot_sync(tdma_t * tdma){ #endif /* If we are in master sync mode, don't adjust demod timing. we ARE demod timing */ + /* TODO: check if any slots are in TX mode and if any have master sync. If we are + TXing and don't have master sync, lock sync offset. Otherwise, TX frames jitter + wildly */ + if(tdma->state != master_sync){ /* Update slot offset to compensate for frame centering */ /* Also check to see if any slots are master. If so, take timing from them */ @@ -552,6 +558,7 @@ void tdma_rx_pilot_sync(tdma_t * tdma){ } } } + /* Check for zero here; otherwise we get div-by-zero errors */ offset_total = offset_slots>0 ? offset_total/offset_slots:0; /* Use master slot for timing if available, otherwise take average of all frames */ if(master_max >= mode.mastersat_min){ @@ -588,6 +595,9 @@ void tdma_rx_pilot_sync(tdma_t * tdma){ } } +/* Attempt at 'plot modem' search for situations where no synchronization is had */ +/* This currently preforms worse than just running tdma_rx_pilot_sync on every frame */ +/* It may still be needed to acquire first frames -- more testing is needed */ void tdma_rx_no_sync(tdma_t * tdma, COMP * samps, u64 timestamp){ fprintf(stderr,"searching for pilot\n"); struct TDMA_MODE_SETTINGS mode = tdma->settings; diff --git a/codec2-dev/src/tdma.h b/codec2-dev/src/tdma.h index 4b3fe832..b3333c68 100644 --- a/codec2-dev/src/tdma.h +++ b/codec2-dev/src/tdma.h @@ -25,6 +25,15 @@ along with this program; if not, see . */ +/* + TODO: Lock sync point while transmitting in non-master mode and no master + slot is detected +*/ + +/* + TODO: Further testing and refinement with julia test suite +*/ + #ifndef __CODEC_2_TDMA_H #define __CODEC_2_TDMA_H @@ -103,7 +112,7 @@ struct TDMA_MODE_SETTINGS { u32 pilot_sync_tol; /* UW errors allowed for a valid pilot sync */ u32 first_sync_tol; /* UW errors allowed for a valid first frame sync */ u32 frame_sync_tol; /* UW errors allowed to maintain a frame sync */ - u32 frame_sync_baduw_tol; /* How many bad UWs before calling a frame unsynced */ + u32 frame_sync_baduw_tol; /* How many bad UWs before calling a slot unsynced */ i32 mastersat_max; /* Maximum count for master detection counter */ i32 mastersat_min; /* Minimum count before frame considered 'master' */ i32 loss_of_sync_frames; /* How many bad frames before going from 'sync' to 'no_sync' for entire modem */ diff --git a/codec2-dev/unittest/tofdm.c b/codec2-dev/unittest/tofdm.c index 61c16de3..b4ad2504 100644 --- a/codec2-dev/unittest/tofdm.c +++ b/codec2-dev/unittest/tofdm.c @@ -46,7 +46,7 @@ #define OFDM_NC 16 -#define NFRAMES 30 +#define NFRAMES 60 //#define SAMPLE_CLOCK_OFFSET_PPM 100 //#define FOFF_HZ 5.0f @@ -202,8 +202,8 @@ int main(int argc, char *argv[]) /* tx vector logging */ - memcpy(&tx_bits_log[OFDM_BITSPERFRAME*f], test_bits_ofdm, sizeof(int)*OFDM_BITSPERFRAME); - memcpy(&tx_log[samples_per_frame*f], tx, sizeof(COMP)*samples_per_frame); + memcpy(&tx_bits_log[OFDM_BITSPERFRAME*f], test_bits_ofdm, sizeof(int)*OFDM_BITSPERFRAME); + memcpy(&tx_log[samples_per_frame*f], tx, sizeof(COMP)*samples_per_frame); } /* --------------------------------------------------------*\ @@ -216,6 +216,12 @@ int main(int argc, char *argv[]) freq_shift(rx_log, rx_log, foff_hz, &foff_phase_rect, samples_per_frame * NFRAMES); + FILE *cplin = fopen("tofdm_rx_vec","r"); + size_t s = fread(rx_log,sizeof(COMP),samples_per_frame*NFRAMES,cplin); + fclose(cplin); + fprintf(stderr,"Read %d\n",s); + + /* --------------------------------------------------------*\ Demod \*---------------------------------------------------------*/ @@ -243,6 +249,7 @@ int main(int argc, char *argv[]) ofdm_set_timing_enable(ofdm, true); ofdm_set_foff_est_enable(ofdm, true); ofdm_set_phase_est_enable(ofdm, true); + //ofdm->foff_est_hz = 10; for(f=0; f