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; } }
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; }