C and Octave versions of fdmdv_demod giving same results with real world signals...
authordrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Wed, 2 May 2012 08:15:50 +0000 (08:15 +0000)
committerdrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Wed, 2 May 2012 08:15:50 +0000 (08:15 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@390 01035d8c-6547-0410-b346-abe4f91aad63

codec2-dev/octave/README_fdmdv.txt
codec2-dev/octave/fdmdv_demod.m
codec2-dev/octave/tfdmdv.m
codec2-dev/src/fdmdv.c
codec2-dev/src/fdmdv.h
codec2-dev/src/fdmdv_demod.c
codec2-dev/src/fdmdv_mod.c
codec2-dev/unittest/tfdmdv.c

index e9e45202df40cac5d4ac4271601bc48446b7cc0a..23a10bae79ac524f867a90397bac5389841a2abb 100644 (file)
@@ -57,7 +57,9 @@ TODO
        $  ./fdmdv_get_test_bits - 14000 | ./fdmdv_mod - - | ./fdmdv_demod - - demod_dump.txt | ./fdmdv_put_test_bits -
 
 [ ] PAPR idea
-    + automatically weak phases to reduce PAPR, e.g. slow variations in freq...
+    + automatically tweak phases to reduce PAPR, e.g. slow variations in freq...
+[ ] implement SNR and ppm measurements
+[ ] add help to each octave script & C program
 
 Tests
 
@@ -67,3 +69,4 @@ Tests
 [ ] mod_dqpsk_good_4dB_8008hz.raw
 [ ] mod_dqpsk_moderate_4dB_8008hz.raw
 [ ] mod_dqpsk_moderate_4dB_7992hz.raw
+[ ] time ....
index 7a21d0dc38cd8ce1871e4e40c3f07de500991208..b30b497c7c32fdecf7b9dca23d36bc5229019c2a 100644 (file)
@@ -50,15 +50,16 @@ function fdmdv_demod(rawfilename, nbits, pngname)
     % obtain nin samples of the test input signal
     
     for i=1:nin
-      rx_fdm(i) = fread(fin, 1, "short")/gain; 
+      rx_fdm(i) = fread(fin, 1, "short")/gain;
     end
-
+    
     rx_fdm_log = [rx_fdm_log rx_fdm(1:nin)];
 
     % frequency offset estimation and correction
 
     [pilot prev_pilot pilot_lut_index prev_pilot_lut_index] = get_pilot(pilot_lut_index, prev_pilot_lut_index, nin);
     [foff_coarse S1 S2] = rx_est_freq_offset(rx_fdm, pilot, prev_pilot, nin);
+    
     if track == 0
       foff  = foff_coarse;
     end
@@ -76,6 +77,7 @@ function fdmdv_demod(rawfilename, nbits, pngname)
     rx_filt = rx_filter(rx_baseband, nin);
 
     [rx_symbols rx_timing] = rx_est_timing(rx_filt, rx_baseband, nin);
+    
     rx_timing_log = [rx_timing_log rx_timing];
     nin = M;
     if rx_timing > 2*M/P
index 34216ee323f2900086949580f1c76e73b4b312a7..d554f4117feee20fb1f3e5356f05567f99121817 100644 (file)
@@ -22,6 +22,9 @@ prev_rx_symbols = ones(Nc+1,1);
 foff_phase_rect = 1;
 coarse_fine = 0;
 fest_state = 0;
+channel = [];
+channel_count = 0;
+next_nin = M;
 
 % Octave outputs we want to collect for comparison to C version
 
@@ -46,6 +49,7 @@ rx_symbols_log = [];
 rx_bits_log = []; 
 sync_bit_log = [];  
 coarse_fine_log = [];
+nin_log = [];
 
 for f=1:frames
 
@@ -61,13 +65,29 @@ for f=1:frames
   tx_fdm = fdm_upconvert(tx_baseband);
   tx_fdm_log = [tx_fdm_log tx_fdm];
 
-  rx_fdm = real(tx_fdm);
+  % channel
+
+  nin = next_nin;
+  %nin = 120;
+  %nin = M;
+  %if (f == 3)
+  %  nin = 120;
+  %elseif (f == 4)
+  %  nin = 200;
+  %else
+  %  nin = M;
+  %end
+  channel = [channel real(tx_fdm)];
+  channel_count += M;
+  rx_fdm = channel(1:nin);
+  channel = channel(nin+1:channel_count);
+  channel_count -= nin;
 
   % demodulator
 
-  [pilot prev_pilot pilot_lut_index prev_pilot_lut_index] = get_pilot(pilot_lut_index, prev_pilot_lut_index, M);
+  [pilot prev_pilot pilot_lut_index prev_pilot_lut_index] = get_pilot(pilot_lut_index, prev_pilot_lut_index, nin);
 
-  [foff_coarse S1 S2] = rx_est_freq_offset(rx_fdm, pilot, prev_pilot, M);
+  [foff_coarse S1 S2] = rx_est_freq_offset(rx_fdm, pilot, prev_pilot, nin);
   if coarse_fine == 0
     foff = foff_coarse;
   end
@@ -82,23 +102,32 @@ for f=1:frames
 
   foff_rect = exp(j*2*pi*foff/Fs);
 
-  for i=1:M
+  for i=1:nin
     foff_phase_rect *= foff_rect';
     rx_fdm_fcorr(i) = rx_fdm(i)*foff_phase_rect;
   end
 
-  rx_baseband = fdm_downconvert(rx_fdm_fcorr, M);
+  rx_baseband = fdm_downconvert(rx_fdm_fcorr, nin);
   rx_baseband_log = [rx_baseband_log rx_baseband];
 
-  rx_filt = rx_filter(rx_baseband, M);
+  rx_filt = rx_filter(rx_baseband, nin);
   rx_filt_log = [rx_filt_log rx_filt];
 
-  [rx_symbols rx_timing env] = rx_est_timing(rx_filt, rx_baseband, M);
+  [rx_symbols rx_timing env] = rx_est_timing(rx_filt, rx_baseband, nin);
   env_log = [env_log env];
 
   rx_timing_log = [rx_timing_log rx_timing];
   rx_symbols_log = [rx_symbols_log rx_symbols];
 
+  next_nin = M;
+  if rx_timing > 2*M/P
+     next_nin += M/P;
+  end
+  if rx_timing < 0;
+     next_nin -= M/P;
+  end
+  nin_log = [nin_log nin];
+
   [rx_bits sync_bit foff_fine] = qpsk_to_bits(prev_rx_symbols, rx_symbols, 'dqpsk');
   prev_rx_symbols = rx_symbols;
   rx_bits_log = [rx_bits_log rx_bits]; 
@@ -174,11 +203,13 @@ plot_sig_and_error(4, 212, imag(pilot_lut_c), imag(pilot_lut - pilot_lut_c), 'pi
 
 % rx_est_freq_offset()
 
-plot_sig_and_error(5, 211, real(pilot_baseband1_log), real(pilot_baseband1_log - pilot_baseband1_log_c), 'pilot baseband1 real' )
-plot_sig_and_error(5, 212, real(pilot_baseband2_log), real(pilot_baseband2_log - pilot_baseband2_log_c), 'pilot baseband2 real' )
+st=1;  en = 3*Npilotbaseband;
+plot_sig_and_error(5, 211, real(pilot_baseband1_log(st:en)), real(pilot_baseband1_log(st:en) - pilot_baseband1_log_c(st:en)), 'pilot baseband1 real' )
+plot_sig_and_error(5, 212, real(pilot_baseband2_log(st:en)), real(pilot_baseband2_log(st:en) - pilot_baseband2_log_c(st:en)), 'pilot baseband2 real' )
 
-plot_sig_and_error(6, 211, real(pilot_lpf1_log), real(pilot_lpf1_log - pilot_lpf1_log_c), 'pilot lpf1 real' )
-plot_sig_and_error(6, 212, real(pilot_lpf2_log), real(pilot_lpf2_log - pilot_lpf2_log_c), 'pilot lpf2 real' )
+st=1;  en = 3*Npilotlpf;
+plot_sig_and_error(6, 211, real(pilot_lpf1_log(st:en)), real(pilot_lpf1_log(st:en) - pilot_lpf1_log_c(st:en)), 'pilot lpf1 real' )
+plot_sig_and_error(6, 212, real(pilot_lpf2_log(st:en)), real(pilot_lpf2_log(st:en) - pilot_lpf2_log_c(st:en)), 'pilot lpf2 real' )
 
 plot_sig_and_error(7, 211, real(S1_log), real(S1_log - S1_log_c), 'S1 real' )
 plot_sig_and_error(7, 212, imag(S1_log), imag(S1_log - S1_log_c), 'S1 imag' )
@@ -199,7 +230,8 @@ plot_sig_and_error(11, 212, imag(rx_baseband_log(c,:)), imag(rx_baseband_log(c,:
 plot_sig_and_error(12, 211, real(rx_filt_log(c,:)), real(rx_filt_log(c,:) - rx_filt_log_c(c,:)), 'Rx filt real' )
 plot_sig_and_error(12, 212, imag(rx_filt_log(c,:)), imag(rx_filt_log(c,:) - rx_filt_log_c(c,:)), 'Rx filt imag' )
 
-plot_sig_and_error(13, 211, env_log, env_log - env_log_c, 'env' )
+st=1; en=3*Nt*P;
+plot_sig_and_error(13, 211, env_log(st:en), env_log(st:en) - env_log_c(st:en), 'env' )
 stem_sig_and_error(13, 212, real(rx_symbols_log(c,:)), real(rx_symbols_log(c,:) - rx_symbols_log_c(c,:)), 'rx symbols' )
 
 st=10*28;
@@ -208,6 +240,7 @@ plot_sig_and_error(14, 211, rx_timing_log, rx_timing_log - rx_timing_log_c, 'Rx
 stem_sig_and_error(14, 212, sync_bit_log_c, sync_bit_log - sync_bit_log_c, 'Sync bit', [1 n -1.5 1.5])
 
 stem_sig_and_error(15, 211, rx_bits_log_c(st:en), rx_bits_log(st:en) - rx_bits_log_c(st:en), 'RX bits', [1 en-st -1.5 1.5])
+stem_sig_and_error(15, 212, nin_log_c, nin_log - nin_log_c, 'nin')
 
 % ---------------------------------------------------------------------------------------
 % AUTOMATED CHECKS ------------------------------------------
@@ -253,5 +286,6 @@ check(rx_symbols_log, rx_symbols_log_c, 'rx_symbols');
 check(rx_bits_log, rx_bits_log_c, 'rx bits');
 check(sync_bit_log, sync_bit_log_c, 'sync bit');
 check(coarse_fine_log, coarse_fine_log_c, 'coarse_fine');
+check(nin_log, nin_log_c, 'nin');
 
 printf("\npasses: %d fails: %d\n", passes, fails);
index 096dbd8c1f08c69d01cae23746f8bf2afe34edc5..f710e6bf53ac623020cb0590b7202ee23219ea79 100644 (file)
@@ -120,7 +120,8 @@ struct FDMDV *fdmdv_create(void)
     float         carrier_freq;
 
     assert(FDMDV_BITS_PER_FRAME == NC*NB);
-    assert(FDMDV_SAMPLES_PER_FRAME == M);
+    assert(FDMDV_NOM_SAMPLES_PER_FRAME == M);
+    assert(FDMDV_MAX_SAMPLES_PER_FRAME == (M+M/P));
 
     f = (struct FDMDV*)malloc(sizeof(struct FDMDV));
     if (f == NULL)
@@ -700,7 +701,7 @@ void fdm_downconvert(COMP rx_baseband[NC+1][M+M/P], COMP rx_fdm[], COMP phase_rx
 
     /* maximum number of input samples to demod */
 
-    assert(nin < (M+M/P));
+    assert(nin <= (M+M/P));
 
     /* Nc/2 tones below centre freq */
   
@@ -713,15 +714,15 @@ void fdm_downconvert(COMP rx_baseband[NC+1][M+M/P], COMP rx_fdm[], COMP phase_rx
     /* Nc/2 tones above centre freq */
 
     for (c=NC/2; c<NC; c++) 
-       for (i=0; i<M; i++) {
+       for (i=0; i<nin; i++) {
            phase_rx[c] = cmult(phase_rx[c], freq[c]);
            rx_baseband[c][i] = cmult(rx_fdm[i], cconj(phase_rx[c]));
        }
 
-    /* add centre pilot tone  */
+    /* centre pilot tone  */
 
     c = NC;
-    for (i=0; i<M; i++) {
+    for (i=0; i<nin; i++) {
        phase_rx[c] = cmult(phase_rx[c],  freq[c]);
        rx_baseband[c][i] = cmult(rx_fdm[i], cconj(phase_rx[c]));
     }
@@ -812,7 +813,7 @@ float rx_est_timing(COMP rx_symbols[],
     */
 
     adjust = P - nin*P/M;
-
+    
     /* update buffer of NT rate P filtered symbols */
     
     for(c=0; c<NC+1; c++) 
@@ -849,6 +850,8 @@ float rx_est_timing(COMP rx_symbols[],
        M/4 part was adjusted by experiment, I know not why.... */
     
     rx_timing = atan2(x.imag, x.real)*M/(2*PI) + M/4;
+    //printf("%f  %f\n", x.real, x.imag);
+    
     if (rx_timing > M)
        rx_timing -= M;
     if (rx_timing < -M)
@@ -1100,7 +1103,9 @@ void fdmdv_demod(struct FDMDV *fdmdv, int rx_bits[], int *sync_bit, float rx_fdm
  
     /* freq offset estimation and correction */
 
+    
     foff_coarse = rx_est_freq_offset(fdmdv, rx_fdm, *nin);
+    
     if (fdmdv->coarse_fine == COARSE)
        fdmdv->foff = foff_coarse;
     freq_shift(rx_fdm_fcorr, rx_fdm, fdmdv->foff, &fdmdv->foff_rect, &fdmdv->foff_phase_rect, *nin);
@@ -1110,6 +1115,17 @@ void fdmdv_demod(struct FDMDV *fdmdv, int rx_bits[], int *sync_bit, float rx_fdm
     fdm_downconvert(rx_baseband, rx_fdm_fcorr, fdmdv->phase_rx, fdmdv->freq, *nin);
     rx_filter(rx_filt, rx_baseband, fdmdv->rx_filter_memory, *nin);
     fdmdv->rx_timing = rx_est_timing(rx_symbols, rx_filt, rx_baseband, fdmdv->rx_filter_mem_timing, env, fdmdv->rx_baseband_mem_timing, *nin);  
+    
+    /* adjust number of input samples to keep timing within bounds */
+
+    *nin = M;
+
+    if (fdmdv->rx_timing > 2*M/P)
+       *nin += M/P;
+    
+    if (fdmdv->rx_timing < 0)
+       *nin -= M/P;
+    
     foff_fine = qpsk_to_bits(rx_bits, sync_bit, fdmdv->phase_difference, fdmdv->prev_rx_symbols, rx_symbols);
     memcpy(fdmdv->prev_rx_symbols, rx_symbols, sizeof(COMP)*(NC+1));
 
@@ -1132,18 +1148,26 @@ void fdmdv_demod(struct FDMDV *fdmdv, int rx_bits[], int *sync_bit, float rx_fdm
 void fdmdv_get_demod_stats(struct FDMDV *fdmdv, struct FDMDV_STATS *fdmdv_stats)
 {
     int   c;
+    COMP  pi_on_4;
+
+    pi_on_4.real = cos(PI/4.0);
+    pi_on_4.imag = sin(PI/4.0);
 
     fdmdv_stats->snr = 0.0; /* TODO - implement SNR estimation */
     fdmdv_stats->fest_coarse_fine = fdmdv->coarse_fine;
     fdmdv_stats->foff = fdmdv->foff;
-    fdmdv_stats->rx_timing = fdmdv->rx_timing/M;
+    fdmdv_stats->rx_timing = fdmdv->rx_timing;
     fdmdv_stats->clock_offset = 0.0; /* TODO - implement clock offset estimation */
 
     assert((NC+1) == FDMDV_NSYM);
 
-    for(c=0; c<NC+1; c++) {
+    for(c=0; c<NC; c++) {
        fdmdv_stats->rx_symbols[c] = fdmdv->phase_difference[c];
     }
+    
+    /* place pilots somewhere convenient on scatter diagram */
+
+    fdmdv_stats->rx_symbols[NC] = cmult(fdmdv->phase_difference[NC], pi_on_4);
 
 }
 
index 346349621cd1831e8166af5f6d0d98f90ea8bb3c..0f8a0a962922b22db86608a2501bf17117e0ef9d 100644 (file)
@@ -41,10 +41,12 @@ extern "C" {
 
 #include "comp.h"
 
-#define FDMDV_BITS_PER_FRAME      28  /* odd/even frames 56 bits, 1400 bit/s  */
-#define FDMDV_SAMPLES_PER_FRAME  160  /* 8000 Hz sample rate                  */
-#define FDMDV_SCALE             1000  /* suggested scaling for 16 bit shorts  */
-#define FDMDV_NSYM                15
+#define FDMDV_BITS_PER_FRAME          28  /* 20ms frames, 1400 bit/s                                        */
+#define FDMDV_NOM_SAMPLES_PER_FRAME  160  /* modulator output samples/frame and nominal demod samples/frame */
+                                          /* at 8000 Hz sample rate                                         */
+#define FDMDV_MAX_SAMPLES_PER_FRAME  200  /* max demod samples/frame, use this to allocate storage          */
+#define FDMDV_SCALE                 1000  /* suggested scaling for 16 bit shorts                            */
+#define FDMDV_NSYM                    15
 
 struct FDMDV;
     
@@ -53,7 +55,7 @@ struct FDMDV_STATS {
     COMP   rx_symbols[FDMDV_NSYM]; /* latest received symbols, for scatter plot          */ 
     int    fest_coarse_fine;       /* freq est state, 0-coarse 1-fine                    */ 
     float  foff;                   /* estimated freq offset in Hz                        */       
-    float  rx_timing;              /* timing offset -1..1 as fraction of symbol period   */
+    float  rx_timing;              /* estimated optimum timing offset in samples         */
     float  clock_offset;           /* Estimated tx/rx sample clock offset in ppm         */
 };
 
index ce44536a2b67b743f43d0f57b7b4a3d723aa5e75..cfb2667afd06f22f80cf811737b49b1fb94c38de 100644 (file)
@@ -53,8 +53,8 @@ int main(int argc, char *argv[])
     char          packed_bits[BYTES_PER_CODEC_FRAME];
     int           rx_bits[FDMDV_BITS_PER_FRAME];
     int           codec_bits[2*FDMDV_BITS_PER_FRAME];
-    float         rx_fdm[FDMDV_SAMPLES_PER_FRAME];
-    short         rx_fdm_scaled[FDMDV_SAMPLES_PER_FRAME];
+    float         rx_fdm[FDMDV_MAX_SAMPLES_PER_FRAME];
+    short         rx_fdm_scaled[FDMDV_MAX_SAMPLES_PER_FRAME];
     int           i, bit, byte, c;
     int           nin;
     int           sync_bit;
@@ -91,12 +91,12 @@ int main(int argc, char *argv[])
     fdmdv = fdmdv_create();
     frames = 0;
     state = 0;
-    nin = FDMDV_SAMPLES_PER_FRAME;
+    nin = FDMDV_NOM_SAMPLES_PER_FRAME;
 
     while(fread(rx_fdm_scaled, sizeof(short), nin, fin) == nin)
     {
-       for(i=0; i<FDMDV_SAMPLES_PER_FRAME; i++)
-           rx_fdm[i] = rx_fdm_scaled[i]/FDMDV_SCALE;
+       for(i=0; i<nin; i++)
+           rx_fdm[i] = (float)rx_fdm_scaled[i]/FDMDV_SCALE;
        fdmdv_demod(fdmdv, rx_bits, &sync_bit, rx_fdm, &nin);
 
        /* log data for optional Octave dump */
@@ -160,17 +160,22 @@ int main(int argc, char *argv[])
 
     /* Optional dump to Octave log file */
 
-    if ( strcmp(argv[3],"|") && (foct = fopen(argv[3],"wt")) == NULL ) {
-       fprintf(stderr, "Error opening Octave dump file: %s: %s.\n",
-               argv[3], strerror(errno));
-       exit(1);
-    }
-    else {
-       octave_save_complex(foct, "rx_symbols_log_c", (COMP*)rx_symbols_log, FDMDV_NSYM, MAX_FRAMES, MAX_FRAMES);  
-       octave_save_float(foct, "foff_log_c", foff_log, 1, MAX_FRAMES);  
-       octave_save_float(foct, "rx_timing_log_c", rx_timing_log, 1, MAX_FRAMES);  
-       octave_save_int(foct, "coarse_fine_log_c", coarse_fine_log, 1, MAX_FRAMES);  
-       fclose(foct);
+    if (argc == 4) {
+
+       /* make sure 3rd arg is not just the pipe command */
+
+       if (strcmp(argv[3],"|")) {
+           if ((foct = fopen(argv[3],"wt")) == NULL ) {
+               fprintf(stderr, "Error opening Octave dump file: %s: %s.\n",
+                       argv[3], strerror(errno));
+               exit(1);
+           }
+           octave_save_complex(foct, "rx_symbols_log_c", (COMP*)rx_symbols_log, FDMDV_NSYM, MAX_FRAMES, MAX_FRAMES);  
+           octave_save_float(foct, "foff_log_c", foff_log, 1, MAX_FRAMES);  
+           octave_save_float(foct, "rx_timing_log_c", rx_timing_log, 1, MAX_FRAMES);  
+           octave_save_int(foct, "coarse_fine_log_c", coarse_fine_log, 1, MAX_FRAMES);  
+           fclose(foct);
+       }
     }
 
     fclose(fin);
index bce6b53c88a00c8e93514cc3ba3018c677124a7c..604282bd3781aebb8bd83520012da7bdf91d05a1 100644 (file)
@@ -47,8 +47,8 @@ int main(int argc, char *argv[])
     struct FDMDV *fdmdv;
     char          packed_bits[BYTES_PER_CODEC_FRAME];
     int           tx_bits[2*FDMDV_BITS_PER_FRAME];
-    COMP          tx_fdm[2*FDMDV_SAMPLES_PER_FRAME];
-    short         tx_fdm_scaled[2*FDMDV_SAMPLES_PER_FRAME];
+    COMP          tx_fdm[2*FDMDV_NOM_SAMPLES_PER_FRAME];
+    short         tx_fdm_scaled[2*FDMDV_NOM_SAMPLES_PER_FRAME];
     int           frames;
     int           i, bit, byte;
     int           sync_bit;
@@ -97,15 +97,15 @@ int main(int argc, char *argv[])
        fdmdv_mod(fdmdv, tx_fdm, tx_bits, &sync_bit);
        assert(sync_bit == 1);
 
-       fdmdv_mod(fdmdv, &tx_fdm[FDMDV_SAMPLES_PER_FRAME], &tx_bits[FDMDV_BITS_PER_FRAME], &sync_bit);
+       fdmdv_mod(fdmdv, &tx_fdm[FDMDV_NOM_SAMPLES_PER_FRAME], &tx_bits[FDMDV_BITS_PER_FRAME], &sync_bit);
        assert(sync_bit == 0);
 
        /* scale and save to disk as shorts */
 
-       for(i=0; i<2*FDMDV_SAMPLES_PER_FRAME; i++)
+       for(i=0; i<2*FDMDV_NOM_SAMPLES_PER_FRAME; i++)
            tx_fdm_scaled[i] = FDMDV_SCALE * tx_fdm[i].real;
 
-       fwrite(tx_fdm_scaled, sizeof(short), 2*FDMDV_SAMPLES_PER_FRAME, fout);
+       fwrite(tx_fdm_scaled, sizeof(short), 2*FDMDV_NOM_SAMPLES_PER_FRAME, fout);
 
        /* if this is in a pipeline, we probably don't want the usual
           buffering to occur */
index 63370014755c4d2f1bfd73f8405d004a1dfadfc5..a592e1350335e76e98f2bbed0af26cc5c09be58e 100644 (file)
@@ -40,6 +40,7 @@
 #include "octave.h"
 
 #define FRAMES 25
+#define CHANNEL_BUF_SIZE (10*M)
 
 int main(int argc, char *argv[])
 {
@@ -48,9 +49,11 @@ int main(int argc, char *argv[])
     COMP          tx_symbols[NC+1];
     COMP          tx_baseband[NC+1][M];
     COMP          tx_fdm[M];
+    float         channel[CHANNEL_BUF_SIZE];
+    int           channel_count;
     float         rx_fdm[M+M/P];
     float         foff_coarse;
-    int           nin;
+    int           nin, next_nin;
     COMP          rx_fdm_fcorr[M+M/P];
     COMP          rx_baseband[NC+1][M+M/P];
     COMP          rx_filt[NC+1][P+1];
@@ -85,11 +88,14 @@ int main(int argc, char *argv[])
     float         foff_fine_log[FRAMES];
     int           sync_bit_log[FRAMES];
     int           coarse_fine_log[FRAMES];
+    int           nin_log[FRAMES];
 
     FILE         *fout;
-    int           f,c,i;
+    int           f,c,i,j;
 
     fdmdv = fdmdv_create();
+    next_nin = M;
+    channel_count = 0;
 
     rx_baseband_log_col_index = 0;
     rx_filt_log_col_index = 0;
@@ -106,10 +112,37 @@ int main(int argc, char *argv[])
        tx_filter(tx_baseband, tx_symbols, fdmdv->tx_filter_memory);
        fdm_upconvert(tx_fdm, tx_baseband, fdmdv->phase_tx, fdmdv->freq);
 
+       /* --------------------------------------------------------*\
+                                 Channel
+       \*---------------------------------------------------------*/
+
+       nin = next_nin;
+       /*
+       if (f == 2)
+           nin = 120;
+       if (f == 3)
+           nin = 200;
+       if ((f !=2) && (f != 3))
+            nin = M;
+       */
+       /* add M tx samples to end of buffer */
+
+       assert((channel_count + M) < CHANNEL_BUF_SIZE);
        for(i=0; i<M; i++)
-           rx_fdm[i] = tx_fdm[i].real;
-       nin = M;
+           channel[channel_count+i] = tx_fdm[i].real;
+       channel_count += M;
+
+       /* take nin samples from start of buffer */
+
+       for(i=0; i<nin; i++)
+           rx_fdm[i] = channel[i];
 
+       /* shift buffer back */
+
+       for(i=0,j=nin; j<channel_count; i++,j++)
+           channel[i] = channel[j];
+       channel_count -= nin;
        /* --------------------------------------------------------*\
                                Demodulator
        \*---------------------------------------------------------*/
@@ -128,6 +161,15 @@ int main(int argc, char *argv[])
        rx_timing = rx_est_timing(rx_symbols, rx_filt, rx_baseband, fdmdv->rx_filter_mem_timing, env, fdmdv->rx_baseband_mem_timing, nin);       
        foff_fine = qpsk_to_bits(rx_bits, &sync_bit, fdmdv->phase_difference, fdmdv->prev_rx_symbols, rx_symbols);
        memcpy(fdmdv->prev_rx_symbols, rx_symbols, sizeof(COMP)*(NC+1));
+       
+       next_nin = M;
+       
+       if (rx_timing > 2*M/P)
+           next_nin += M/P;
+    
+       if (rx_timing < 0)
+           next_nin -= M/P;
+       
        fdmdv->coarse_fine = freq_state(sync_bit, &fdmdv->fest_state);
        fdmdv->foff  -= TRACK_COEFF*foff_fine;
 
@@ -164,15 +206,16 @@ int main(int argc, char *argv[])
        /* rx filtering */
 
        for(c=0; c<NC+1; c++) {
-           for(i=0; i<(P*M)/nin; i++)
+           for(i=0; i<(P*nin)/M; i++)
                rx_filt_log[c][rx_filt_log_col_index + i] = rx_filt[c][i]; 
        }
-       rx_filt_log_col_index += (P*M)/nin;
+       rx_filt_log_col_index += (P*nin)/M;
 
        /* timing estimation */
 
        memcpy(&env_log[NT*P*f], env, sizeof(float)*NT*P);
        rx_timing_log[f] = rx_timing;
+       nin_log[f] = nin;
        for(c=0; c<NC+1; c++)
            rx_symbols_log[c][f] = rx_symbols[c];
        
@@ -216,6 +259,7 @@ int main(int argc, char *argv[])
     octave_save_float(fout, "foff_fine_log_c", foff_fine_log, 1, FRAMES);  
     octave_save_int(fout, "sync_bit_log_c", sync_bit_log, 1, FRAMES);  
     octave_save_int(fout, "coarse_fine_log_c", coarse_fine_log, 1, FRAMES);  
+    octave_save_int(fout, "nin_log_c", nin_log, 1, FRAMES);  
     fclose(fout);
 
     fdmdv_destroy(fdmdv);