int main(int argc, char *argv[]) {
    struct freedv *f;
    short          adc16k[FDMDV_OS_TAPS_16K+FREEDV_NSAMPLES_16K];
    short          dac16k[FREEDV_NSAMPLES_16K];
    short          adc8k[FREEDV_NSAMPLES];
    short          dac8k[FDMDV_OS_TAPS_8K+FREEDV_NSAMPLES];
    FILE          *fin, *fout, *ftotal;
    int            frame, nin_16k, nin, i, nout = 0;
    struct FDMDV_STATS  stats;
    PROFILE_VAR(fdmdv_16_to_8_start, freedv_rx_start, fdmdv_8_to_16_start);

    machdep_profile_init();

    f = freedv_open(FREEDV_MODE_1600);

    // Receive ---------------------------------------------------------------------

    frame = 0;

    fin = fopen("mod_16k.raw", "rb");
    if (fin == NULL) {
        printf("Error opening input file\n");
        exit(1);
    }

    fout = fopen("speechout_16k.raw", "wb");
    if (fout == NULL) {
        printf("Error opening output file\n");
        exit(1);
    }

    ftotal = fopen("total.txt", "wt");
    assert(ftotal != NULL);

    /* clear filter memories */

    for(i=0; i<FDMDV_OS_TAPS_16K; i++)
	adc16k[i] = 0.0;
    for(i=0; i<FDMDV_OS_TAPS_8K; i++)
	dac8k[i] = 0.0;
    
    nin = freedv_nin(f);
    nin_16k = 2*nin;
    nout = nin;
    while (fread(&adc16k[FDMDV_OS_TAPS_16K], sizeof(short), nin_16k, fin) == nin_16k) {

        PROFILE_SAMPLE(fdmdv_16_to_8_start);

        fdmdv_16_to_8_short(adc8k, &adc16k[FDMDV_OS_TAPS_16K], nin);

        PROFILE_SAMPLE_AND_LOG(freedv_rx_start, fdmdv_16_to_8_start, "  fdmdv_16_to_8");

        nout = freedv_rx(f, &dac8k[FDMDV_OS_TAPS_8K], adc8k);
        nin = freedv_nin(f); nin_16k = 2*nin;
        fdmdv_get_demod_stats(f->fdmdv, &stats);

        PROFILE_SAMPLE_AND_LOG(fdmdv_8_to_16_start, freedv_rx_start, "  freedv_rx");     

        fdmdv_8_to_16_short(dac16k, &dac8k[FDMDV_OS_TAPS_8K], nout);              

        PROFILE_SAMPLE_AND_LOG2(fdmdv_8_to_16_start, "  fdmdv_8_to_16");

        fprintf(ftotal, "%d\n", machdep_profile_sample() - fdmdv_16_to_8_start);
        machdep_profile_print_logged_samples();

        fwrite(dac16k, sizeof(short), 2*nout, fout);
        fdmdv_get_demod_stats(f->fdmdv, &stats);
        printf("frame: %d nin_16k: %d sync: %d SNR: %3.2f \n", 
               ++frame, nin_16k, stats.sync, (double)stats.snr_est);
    }

    fclose(fin);
    fclose(fout);
    fclose(ftotal);

    return 0;
}
Example #2
0
void per_frame_rx_processing(short  output_buf[], /* output buf of decoded speech samples          */
                             int   *n_output_buf, /* how many samples currently in output_buf[]    */
                             int    codec_bits[], /* current frame of bits for decoder             */
                             short  input_buf[],  /* input buf of modem samples input to demod     */ 
                             int   *n_input_buf   /* how many samples currently in input_buf[]     */
                             )
{
    int    sync_bit;
    COMP  rx_fdm[FDMDV_MAX_SAMPLES_PER_FRAME];
    int    rx_bits[FDMDV_BITS_PER_FRAME];
    unsigned char  packed_bits[BYTES_PER_CODEC_FRAME];
    float  rx_spec[FDMDV_NSPEC];
    int    i, nin_prev, bit, byte;
    int    next_state;

    assert(*n_input_buf <= (2*FDMDV_NOM_SAMPLES_PER_FRAME));

    /*
      This while loop will run the demod 0, 1 (nominal) or 2 times:

      0: when tx sample clock runs faster than rx, occasionally we
         will run out of samples

      1: normal, run decoder once, every 2nd frame output a frame of
         speech samples to D/A

      2: when tx sample clock runs slower than rx, occasionally we will
         have enough samples to run demod twice.

      With a +/- 10 Hz sample clock difference at FS=8000Hz (+/- 1250
      ppm), case 0 or 1 occured about once every 30 seconds.  This is
      no problem for the decoded audio.
    */

    while(*n_input_buf >= g_nin) {

        // demod per frame processing

        for(i=0; i<g_nin; i++) {
            rx_fdm[i].real = (float)input_buf[i]/FDMDV_SCALE;
            rx_fdm[i].imag = 0.0;
        }
        nin_prev = g_nin;
        fdmdv_demod(fdmdv, rx_bits, &sync_bit, rx_fdm, &g_nin);
        *n_input_buf -= nin_prev;
        assert(*n_input_buf >= 0);

        // shift input buffer

        for(i=0; i<*n_input_buf; i++)
            input_buf[i] = input_buf[i+nin_prev];

        // compute rx spectrum & get demod stats, and update GUI plot data

        fdmdv_get_rx_spectrum(fdmdv, rx_spec, rx_fdm, nin_prev);

        // Average rx spectrum data using a simple IIR low pass filter
        for(i = 0; i < FDMDV_NSPEC; i++) 
        {
            g_avmag[i] = BETA * g_avmag[i] + (1.0 - BETA) * rx_spec[i];
        }

        fdmdv_get_demod_stats(fdmdv, &stats);
        jni_update_stats(&stats, g_avmag);
        count++;

        /* 
           State machine to:

           + Mute decoded audio when out of sync.  The demod is synced
             when we are using the fine freq estimate and SNR is above
             a thresh.

           + Decode codec bits only if we have a 0,1 sync bit
             sequence.  Collects two frames of demod bits to decode
             one frame of codec bits.
        */

        next_state = g_state;
        switch (g_state) {
        case 0:
            /* mute output audio when out of sync */

            if (*n_output_buf < 2*codec2_samples_per_frame(codec2) - N8) {
                for(i=0; i<N8; i++)
                    output_buf[*n_output_buf + i] = 0;
                *n_output_buf += N8;
            }
            if (!(*n_output_buf <= (2*codec2_samples_per_frame(codec2)))) {
                LOGE("*n_output_buf <= (2*codec2_samples_per_frame(codec2))");
            }

            if ((stats.fest_coarse_fine == 1))// && (stats.snr_est > 3.0))
                next_state = 1;

            break;
        case 1:
            if (sync_bit == 0) {
                next_state = 2;

                /* first half of frame of codec bits */

                memcpy(codec_bits, rx_bits, FDMDV_BITS_PER_FRAME*sizeof(int));
            }
            else
                next_state = 1;

            if (stats.fest_coarse_fine == 0)
                next_state = 0;

            break;
        case 2:
            next_state = 1;

            if (stats.fest_coarse_fine == 0)
                next_state = 0;

            if (sync_bit == 1) {
                /* second half of frame of codec bits */

                memcpy(&codec_bits[FDMDV_BITS_PER_FRAME], rx_bits,
                        FDMDV_BITS_PER_FRAME*sizeof(int));

                // extract data bit

                int data_flag_index = codec2_get_spare_bit_index(codec2);
                assert(data_flag_index != -1); // not supported for all rates

                short abit = codec_bits[data_flag_index];
                char  ascii_out;

                int n_ascii = varicode_decode(&g_varicode_dec_states,
                        &ascii_out, &abit, 1, 1);
                assert((n_ascii == 0) || (n_ascii == 1));
                if (n_ascii) {
                    short ashort = ascii_out;
                    LOGD("%c", ashort);
                }

                // reconstruct missing bit we steal for data bit and decode
                // speech
                codec2_rebuild_spare_bit(codec2, codec_bits);

                /* pack bits, MSB received first  */

                bit = 7; byte = 0;
                memset(packed_bits, 0, BYTES_PER_CODEC_FRAME);
                for(i=0; i<BITS_PER_CODEC_FRAME; i++) {
                    packed_bits[byte] |= (codec_bits[i] << bit);
                    bit--;
                    if (bit < 0) {
                        bit = 7;
                        byte++;
                    }
                }
                assert(byte == BYTES_PER_CODEC_FRAME);

                /* add decoded speech to end of output buffer */

                if (*n_output_buf <= codec2_samples_per_frame(codec2)) {
                    codec2_decode(codec2, &output_buf[*n_output_buf], packed_bits);
                    *n_output_buf += codec2_samples_per_frame(codec2);
                }
                assert(*n_output_buf <= (2*codec2_samples_per_frame(codec2)));

            }
            break;
        }
        if (!!g_state != !!next_state) {
            jni_update_sync(g_state == 0);
        }
        g_state = next_state;
    }
}
int main(int argc, char *argv[])
{
    FILE         *fin, *fout;
    struct FDMDV *fdmdv;
    char         *packed_bits;
    int          *rx_bits;
    int          *codec_bits;
    COMP          rx_fdm[FDMDV_MAX_SAMPLES_PER_FRAME];
    short         rx_fdm_scaled[FDMDV_MAX_SAMPLES_PER_FRAME];
    int           i, bit, byte, c;
    int           nin, nin_prev;
    int           sync_bit = 0, reliable_sync_bit;
    int           sync = 0;
    int           f;
    FILE         *foct = NULL;
    struct FDMDV_STATS stats;
    COMP         *rx_fdm_log;
    int           rx_fdm_log_col_index;
    COMP         *rx_symbols_log;
    int           sync_log[MAX_FRAMES];
    float         rx_timing_log[MAX_FRAMES];
    float         foff_log[MAX_FRAMES];
    int           sync_bit_log[MAX_FRAMES];
    int           rx_bits_log[FDMDV_BITS_PER_FRAME*MAX_FRAMES];
    float         snr_est_log[MAX_FRAMES];
    float        *rx_spec_log;
    int           max_frames_reached;
    int           bits_per_fdmdv_frame;
    int           bits_per_codec_frame;
    int           bytes_per_codec_frame;
    int           Nc;

    if (argc < 2) {
	printf("usage: %s InputModemRawFile OutputBitFile [Nc [OctaveDumpFile]]\n", argv[0]);
	printf("e.g    %s hts1a_fdmdv.raw hts1a.c2\n", argv[0]);
	exit(1);
    }

    if (strcmp(argv[1], "-")  == 0) fin = stdin;
    else if ( (fin = fopen(argv[1],"rb")) == NULL ) {
	fprintf(stderr, "Error opening input modem sample file: %s: %s.\n",
         argv[1], strerror(errno));
	exit(1);
    }

    if (strcmp(argv[2], "-") == 0) fout = stdout;
    else if ( (fout = fopen(argv[2],"wb")) == NULL ) {
	fprintf(stderr, "Error opening output bit file: %s: %s.\n",
         argv[2], strerror(errno));
	exit(1);
    }

    if (argc >= 4) {
        Nc = atoi(argv[3]);
        if ((Nc % 2) != 0) {
            fprintf(stderr, "Error number of carriers must be a multiple of 2\n");
            exit(1);
        }
        if ((Nc < 2) || (Nc > FDMDV_NC_MAX) ) {
            fprintf(stderr, "Error number of carriers must be between 2 and %d\n",  FDMDV_NC_MAX);
            exit(1);
        }
    }
    else
        Nc = FDMDV_NC;
    
    fdmdv = fdmdv_create(Nc);

    bits_per_fdmdv_frame = fdmdv_bits_per_frame(fdmdv);
    bits_per_codec_frame = 2*fdmdv_bits_per_frame(fdmdv);
    assert((bits_per_codec_frame % 8) == 0); /* make sure integer number of bytes per frame */
    bytes_per_codec_frame = bits_per_codec_frame/8;

    /* malloc some buffers that are dependant on Nc */

    packed_bits = (char*)malloc(bytes_per_codec_frame); assert(packed_bits != NULL);
    rx_bits = (int*)malloc(sizeof(int)*bits_per_codec_frame); assert(rx_bits != NULL);
    codec_bits = (int*)malloc(2*sizeof(int)*bits_per_fdmdv_frame); assert(codec_bits != NULL);

    /* malloc some of the larger variables to prevent out of stack problems */

    rx_fdm_log = (COMP*)malloc(sizeof(COMP)*FDMDV_MAX_SAMPLES_PER_FRAME*MAX_FRAMES);
    assert(rx_fdm_log != NULL);
    rx_spec_log = (float*)malloc(sizeof(float)*FDMDV_NSPEC*MAX_FRAMES);
    assert(rx_spec_log != NULL);
    rx_symbols_log = (COMP*)malloc(sizeof(COMP)*(Nc+1)*MAX_FRAMES);
    assert(rx_fdm_log != NULL);

    f = 0;
    nin = FDMDV_NOM_SAMPLES_PER_FRAME;
    rx_fdm_log_col_index = 0;
    max_frames_reached = 0;

    while(fread(rx_fdm_scaled, sizeof(short), nin, fin) == nin)
    {
	for(i=0; i<nin; i++) {
	    rx_fdm[i].real = (float)rx_fdm_scaled[i]/FDMDV_SCALE;
            rx_fdm[i].imag = 0;
        }
	nin_prev = nin;
	fdmdv_demod(fdmdv, rx_bits, &reliable_sync_bit, rx_fdm, &nin);

	/* log data for optional Octave dump */

	if (f < MAX_FRAMES) {
	    fdmdv_get_demod_stats(fdmdv, &stats);

	    /* log modem states for later dumping to Octave log file */

	    memcpy(&rx_fdm_log[rx_fdm_log_col_index], rx_fdm, sizeof(COMP)*nin_prev);
	    rx_fdm_log_col_index += nin_prev;

	    for(c=0; c<Nc+1; c++)
		rx_symbols_log[f*(Nc+1)+c] = stats.rx_symbols[c];
	    foff_log[f] = stats.foff;
	    rx_timing_log[f] = stats.rx_timing;
	    sync_log[f] = stats.sync;
	    sync_bit_log[f] = sync_bit;
	    memcpy(&rx_bits_log[bits_per_fdmdv_frame*f], rx_bits, sizeof(int)*bits_per_fdmdv_frame);
	    snr_est_log[f] = stats.snr_est;

	    fdmdv_get_rx_spectrum(fdmdv, &rx_spec_log[f*FDMDV_NSPEC], rx_fdm, nin_prev);

	    f++;
	}
	
	if ((f == MAX_FRAMES) && !max_frames_reached) {
	    fprintf(stderr,"MAX_FRAMES exceed in Octave log, log truncated\n");
	    max_frames_reached = 1;
	}

        if (reliable_sync_bit)
            sync = 1;
        //printf("sync_bit: %d reliable_sync_bit: %d sync: %d\n", sync_bit, reliable_sync_bit, sync);

        if (sync == 0) {
            memcpy(codec_bits, rx_bits, bits_per_fdmdv_frame*sizeof(int));
            sync = 1;
        }
        else {
            memcpy(&codec_bits[bits_per_fdmdv_frame], rx_bits, bits_per_fdmdv_frame*sizeof(int));

            /* pack bits, MSB received first  */

            bit = 7; byte = 0;
            memset(packed_bits, 0, bytes_per_codec_frame);
            for(i=0; i<bits_per_codec_frame; i++) {
                packed_bits[byte] |= (codec_bits[i] << bit);
                bit--;
                if (bit < 0) {
                    bit = 7;
                    byte++;
                }
            }
            assert(byte == bytes_per_codec_frame);

            fwrite(packed_bits, sizeof(char), bytes_per_codec_frame, fout);
            sync = 0;
        }
           

	/* if this is in a pipeline, we probably don't want the usual
	   buffering to occur */

        if (fout == stdout) fflush(stdout);
        if (fin == stdin) fflush(stdin);         
    }

    /* Optional dump to Octave log file */

    if (argc == 5) {

	if ((foct = fopen(argv[4],"wt")) == NULL ) {
	    fprintf(stderr, "Error opening Octave dump file: %s: %s.\n",
		argv[4], strerror(errno));
	    exit(1);
	}
	octave_save_complex(foct, "rx_fdm_log_c", rx_fdm_log, 1, rx_fdm_log_col_index, FDMDV_MAX_SAMPLES_PER_FRAME);  
	octave_save_complex(foct, "rx_symbols_log_c", (COMP*)rx_symbols_log, Nc+1, f, MAX_FRAMES);  
	octave_save_float(foct, "foff_log_c", foff_log, 1, f, MAX_FRAMES);  
	octave_save_float(foct, "rx_timing_log_c", rx_timing_log, 1, f, MAX_FRAMES);  
	octave_save_int(foct, "sync_log_c", sync_log, 1, f);  
	octave_save_int(foct, "rx_bits_log_c", rx_bits_log, 1, bits_per_fdmdv_frame*f);
	octave_save_int(foct, "sync_bit_log_c", sync_bit_log, 1, f);  
	octave_save_float(foct, "snr_est_log_c", snr_est_log, 1, f, MAX_FRAMES);  
	octave_save_float(foct, "rx_spec_log_c", rx_spec_log, f, FDMDV_NSPEC, FDMDV_NSPEC);  
	fclose(foct);
    }

    //fdmdv_dump_osc_mags(fdmdv);

    fclose(fin);
    fclose(fout);
    free(rx_fdm_log);
    free(rx_spec_log);
    fdmdv_destroy(fdmdv);

    return 0;
}
void per_frame_rx_processing(short  output_buf[], /* output buf of decoded speech samples          */
                             int   *n_output_buf, /* how many samples currently in output_buf[]    */
                             int    codec_bits[], /* current frame of bits for decoder             */
                             short  input_buf[],  /* input buf of modem samples input to demod     */ 
                             int   *n_input_buf   /* how many samples currently in input_buf[]     */
                             )
{
    int    sync_bit;
    COMP   rx_fdm[FDMDV_MAX_SAMPLES_PER_FRAME];
    int    rx_bits[FDMDV_BITS_PER_FRAME];
    unsigned char  packed_bits[BYTES_PER_CODEC_FRAME];
    int    i, nin_prev, bit, byte;
    int    next_state;

    if (!(*n_input_buf <= (2*FDMDV_NOM_SAMPLES_PER_FRAME))) {
        fprintf(stderr, "Assert: *n_input_buf <= (2*FDMDV_NOM_SAMPLES_PER_FRAME)\n");
    }
   
    /*
      This while loop will run the demod 0, 1 (nominal) or 2 times:

      0: when tx sample clock runs faster than rx, occasionally we
         will run out of samples

      1: normal, run decoder once, every 2nd frame output a frame of
         speech samples to D/A

      2: when tx sample clock runs slower than rx, occasionally we will
         have enough samples to run demod twice.

      With a +/- 10 Hz sample clock difference at FS=8000Hz (+/- 1250
      ppm), case 0 or 1 occured about once every 30 seconds.  This is
      no problem for the decoded audio.
    */

    while(*n_input_buf >= g_nin) {

        // demod per frame processing

        for(i=0; i<g_nin; i++) {
            rx_fdm[i].real = (float)input_buf[i]/FDMDV_SCALE;
            rx_fdm[i].imag = 0.0;
        }

        nin_prev = g_nin;
        fdmdv_demod(fdmdv, rx_bits, &sync_bit, rx_fdm, &g_nin);
        *n_input_buf -= nin_prev;
        if (!(*n_input_buf >= 0)) {
            fprintf(stderr, "Assert: *n_input_buf >= 0\n");
        }

        // shift input buffer

        for(i=0; i<*n_input_buf; i++)
            input_buf[i] = input_buf[i+nin_prev];

#if 0
        // compute rx spectrum & get demod stats, and update GUI plot data

        fdmdv_get_rx_spectrum(fdmdv, rx_spec, rx_fdm, nin_prev);
#endif
        fdmdv_get_demod_stats(fdmdv, &stats);

        /* 
           State machine to:

           + Mute decoded audio when out of sync.  The demod is synced
             when we are using the fine freq estimate and SNR is above
             a thresh.

           + Decode codec bits only if we have a 0,1 sync bit
             sequence.  Collects two frames of demod bits to decode
             one frame of codec bits.
        */

        next_state = g_state;
        switch (g_state) {
        case 0:
            /* mute output audio when out of sync */

            if (*n_output_buf < 2*codec2_samples_per_frame(codec2) - N8) {
                for(i=0; i<N8; i++)
                    output_buf[*n_output_buf + i] = 0;
                *n_output_buf += N8;
            }
            if (!(*n_output_buf <= (2*codec2_samples_per_frame(codec2)))) {
                fprintf(stderr, "Assert: *n_output_buf <= (2*codec2_samples_per_frame(codec2))\n");
            }

            if ((stats.fest_coarse_fine == 1) && (stats.snr_est > 3.0))
                next_state = 1;

            break;
        case 1:
            if (sync_bit == 0) {
                next_state = 2;

                /* first half of frame of codec bits */

                memcpy(codec_bits, rx_bits, FDMDV_BITS_PER_FRAME*sizeof(int));
            }
            else
                next_state = 1;

            if (stats.fest_coarse_fine == 0)
                next_state = 0;

            break;
        case 2:
            next_state = 1;

            if (stats.fest_coarse_fine == 0)
                next_state = 0;

            if (sync_bit == 1) {
                /* second half of frame of codec bits */

                memcpy(&codec_bits[FDMDV_BITS_PER_FRAME], rx_bits, FDMDV_BITS_PER_FRAME*sizeof(int));

                // reconstruct missing bit we steal for data bit and decode speech

                codec2_rebuild_spare_bit(codec2, codec_bits);

                /* pack bits, MSB received first  */

                bit = 7; byte = 0;
                memset(packed_bits, 0, BYTES_PER_CODEC_FRAME);
                for(i=0; i<BITS_PER_CODEC_FRAME; i++) {
                    packed_bits[byte] |= (codec_bits[i] << bit);
                    bit--;
                    if (bit < 0) {
                        bit = 7;
                        byte++;
                    }
                }
                if (!(byte == BYTES_PER_CODEC_FRAME)) {
                    fprintf(stderr, "Assert: byte == BYTES_PER_CODEC_FRAME\n");
                }

                /* add decoded speech to end of output buffer */

                if (*n_output_buf <= codec2_samples_per_frame(codec2)) {
                    codec2_decode(codec2, &output_buf[*n_output_buf], packed_bits);
                    *n_output_buf += codec2_samples_per_frame(codec2);
                }
                if (!(*n_output_buf <= (2*codec2_samples_per_frame(codec2)))) {
                    fprintf(stderr, "Assert: *n_output_buf <= (2*codec2_samples_per_frame(codec2))\n");
                }

            }
            break;
        }
        g_state = next_state;
    }
}
Example #5
0
int main(int argc, char *argv[])
{
    FILE         *fin, *fout;
    struct FDMDV *fdmdv;
    char          packed_bits[BYTES_PER_CODEC_FRAME];
    int           rx_bits[FDMDV_BITS_PER_FRAME];
    int           codec_bits[2*FDMDV_BITS_PER_FRAME];
    COMP          rx_fdm[FDMDV_MAX_SAMPLES_PER_FRAME];
    short         rx_fdm_scaled[FDMDV_MAX_SAMPLES_PER_FRAME];
    int           i, bit, byte, c;
    int           nin, nin_prev;
    int           sync_bit;
    int           state, next_state;
    int           f;
    FILE         *foct = NULL;
    struct FDMDV_STATS stats;
    float        *rx_fdm_log;
    int           rx_fdm_log_col_index;
    COMP          rx_symbols_log[FDMDV_NSYM][MAX_FRAMES];
    int           coarse_fine_log[MAX_FRAMES];
    float         rx_timing_log[MAX_FRAMES];
    float         foff_log[MAX_FRAMES];
    int           sync_bit_log[MAX_FRAMES];
    int           rx_bits_log[FDMDV_BITS_PER_FRAME*MAX_FRAMES];
    float         snr_est_log[MAX_FRAMES];
    float        *rx_spec_log;
    int           max_frames_reached;

    if (argc < 3) {
	printf("usage: %s InputModemRawFile OutputBitFile [OctaveDumpFile]\n", argv[0]);
	printf("e.g    %s hts1a_fdmdv.raw hts1a.c2\n", argv[0]);
	exit(1);
    }

    if (strcmp(argv[1], "-")  == 0) fin = stdin;
    else if ( (fin = fopen(argv[1],"rb")) == NULL ) {
	fprintf(stderr, "Error opening input modem sample file: %s: %s.\n",
         argv[1], strerror(errno));
	exit(1);
    }

    if (strcmp(argv[2], "-") == 0) fout = stdout;
    else if ( (fout = fopen(argv[2],"wb")) == NULL ) {
	fprintf(stderr, "Error opening output bit file: %s: %s.\n",
         argv[2], strerror(errno));
	exit(1);
    }

    /* malloc some of the bigger variables to prevent out of stack problems */

    rx_fdm_log = (float*)malloc(sizeof(float)*FDMDV_MAX_SAMPLES_PER_FRAME*MAX_FRAMES);
    assert(rx_fdm_log != NULL);
    rx_spec_log = (float*)malloc(sizeof(float)*FDMDV_NSPEC*MAX_FRAMES);
    assert(rx_spec_log != NULL);

    fdmdv = fdmdv_create();
    f = 0;
    state = 0;
    nin = FDMDV_NOM_SAMPLES_PER_FRAME;
    rx_fdm_log_col_index = 0;
    max_frames_reached = 0;

    while(fread(rx_fdm_scaled, sizeof(short), nin, fin) == nin)
    {
	for(i=0; i<nin; i++) {
	    rx_fdm[i].real = (float)rx_fdm_scaled[i]/FDMDV_SCALE;
            rx_fdm[i].imag = 0;
        }
	nin_prev = nin;
	fdmdv_demod(fdmdv, rx_bits, &sync_bit, rx_fdm, &nin);

	/* log data for optional Octave dump */

	if (f < MAX_FRAMES) {
	    fdmdv_get_demod_stats(fdmdv, &stats);

	    /* log modem states for later dumping to Octave log file */

	    memcpy(&rx_fdm_log[rx_fdm_log_col_index], rx_fdm, sizeof(float)*nin_prev);
	    rx_fdm_log_col_index += nin_prev;

	    for(c=0; c<FDMDV_NSYM; c++)
		rx_symbols_log[c][f] = stats.rx_symbols[c];
	    foff_log[f] = stats.foff;
	    rx_timing_log[f] = stats.rx_timing;
	    coarse_fine_log[f] = stats.fest_coarse_fine;
	    sync_bit_log[f] = sync_bit;
	    memcpy(&rx_bits_log[FDMDV_BITS_PER_FRAME*f], rx_bits, sizeof(int)*FDMDV_BITS_PER_FRAME);
	    snr_est_log[f] = stats.snr_est;

	    fdmdv_get_rx_spectrum(fdmdv, &rx_spec_log[f*FDMDV_NSPEC], rx_fdm, nin_prev);

	    f++;
	}
	
	if ((f == MAX_FRAMES) && !max_frames_reached) {
	    fprintf(stderr,"MAX_FRAMES exceed in Octave log, log truncated\n");
	    max_frames_reached = 1;
	}

	/* state machine to output codec bits only if we have a 0,1
	   sync bit sequence */

	next_state = state;
	switch (state) {
	case 0:
	    if (sync_bit == 0) {
		next_state = 1;
		memcpy(codec_bits, rx_bits, FDMDV_BITS_PER_FRAME*sizeof(int));
	    }
	    else
		next_state = 0;
	    break;
	case 1:
	    if (sync_bit == 1) {
		memcpy(&codec_bits[FDMDV_BITS_PER_FRAME], rx_bits, FDMDV_BITS_PER_FRAME*sizeof(int));

		/* pack bits, MSB received first  */

		bit = 7; byte = 0;
		memset(packed_bits, 0, BYTES_PER_CODEC_FRAME);
		for(i=0; i<BITS_PER_CODEC_FRAME; i++) {
		    packed_bits[byte] |= (codec_bits[i] << bit);
		    bit--;
		    if (bit < 0) {
			bit = 7;
			byte++;
		    }
		}
		assert(byte == BYTES_PER_CODEC_FRAME);

		fwrite(packed_bits, sizeof(char), BYTES_PER_CODEC_FRAME, fout);
	    }
	    next_state = 0;
	    break;
	}	
	state = next_state;

	/* if this is in a pipeline, we probably don't want the usual
	   buffering to occur */

        if (fout == stdout) fflush(stdout);
        if (fin == stdin) fflush(stdin);         
    }

    /* Optional dump to Octave log file */

    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_float(foct, "rx_fdm_log_c", rx_fdm_log, 1, rx_fdm_log_col_index, FDMDV_MAX_SAMPLES_PER_FRAME);  
	    octave_save_complex(foct, "rx_symbols_log_c", (COMP*)rx_symbols_log, FDMDV_NSYM, f, MAX_FRAMES);  
	    octave_save_float(foct, "foff_log_c", foff_log, 1, f, MAX_FRAMES);  
	    octave_save_float(foct, "rx_timing_log_c", rx_timing_log, 1, f, MAX_FRAMES);  
	    octave_save_int(foct, "coarse_fine_log_c", coarse_fine_log, 1, f);  
	    octave_save_int(foct, "rx_bits_log_c", rx_bits_log, 1, FDMDV_BITS_PER_FRAME*f);
	    octave_save_int(foct, "sync_bit_log_c", sync_bit_log, 1, f);  
	    octave_save_float(foct, "snr_est_log_c", snr_est_log, 1, f, MAX_FRAMES);  
	    octave_save_float(foct, "rx_spec_log_c", rx_spec_log, f, FDMDV_NSPEC, FDMDV_NSPEC);  
	    fclose(foct);
	}
    }

    //fdmdv_dump_osc_mags(fdmdv);

    fclose(fin);
    fclose(fout);
    free(rx_fdm_log);
    free(rx_spec_log);
    fdmdv_destroy(fdmdv);

    return 0;
}