From: baobrien Date: Wed, 4 May 2016 05:18:52 +0000 (+0000) Subject: Applied Jeroen's patch to add data channel support to 800AX X-Git-Url: http://git.whiteaudio.com/gitweb/?a=commitdiff_plain;h=19109f74c90c3b5c3f9e5517a032f92f2e1dcfeb;p=freetel-svn-tracking.git Applied Jeroen's patch to add data channel support to 800AX git-svn-id: https://svn.code.sf.net/p/freetel/code@2796 01035d8c-6547-0410-b346-abe4f91aad63 --- diff --git a/codec2-dev/src/c2dec.c b/codec2-dev/src/c2dec.c index 1e261ac6..bb990583 100644 --- a/codec2-dev/src/c2dec.c +++ b/codec2-dev/src/c2dec.c @@ -58,6 +58,7 @@ int main(int argc, char *argv[]) float ber, r, burst_length, burst_period, burst_timer, ber_est; unsigned char mask; int natural, dump, softdec, bit, ret; + int report_energy; char* opt_string = "h:"; struct option long_options[] = { @@ -70,6 +71,7 @@ int main(int argc, char *argv[]) #ifdef DUMP { "dump", required_argument, &dump, 1 }, #endif + { "energy", no_argument, NULL, 0 }, { "help", no_argument, NULL, 'h' }, { NULL, no_argument, NULL, 0 } }; @@ -119,6 +121,7 @@ int main(int argc, char *argv[]) burst_length = burst_period = 0.0; burst_timer = 0.0; dump = natural = softdec = 0; + report_energy = 0; codec2 = codec2_create(mode); nsam = codec2_samples_per_frame(codec2); @@ -147,7 +150,7 @@ int main(int argc, char *argv[]) nstart_bit = atoi(optarg); } else if(strcmp(long_options[option_index].name, "endbit") == 0) { nend_bit = atoi(optarg); - } else if(strcmp(long_options[option_index].name, "berfile") == 0) { + } else if(strcmp(long_options[option_index].name, "berfile") == 0) { if ((fber = fopen(optarg,"wt")) == NULL) { fprintf(stderr, "Error opening BER file: %s %s.\n", optarg, strerror(errno)); @@ -161,6 +164,9 @@ int main(int argc, char *argv[]) dump_on(optarg); } #endif + else if (strcmp(long_options[option_index].name, "energy") == 0) { + report_energy = 1; + } break; case 'h': @@ -269,6 +275,9 @@ int main(int argc, char *argv[]) codec2_set_softdec(codec2, softdec_bits); } + if (report_energy) + fprintf(stderr, "Energy: %1.3f\n", codec2_get_energy(codec2, bits)); + codec2_decode_ber(codec2, buf, bits, ber_est); fwrite(buf, sizeof(short), nsam, fout); @@ -303,7 +312,7 @@ void print_help(const struct option* long_options, int num_opts, char* argv[]) int i; char *option_parameters; fprintf(stderr, "\nc2dec - Codec 2 decoder and bit error simulation program\n" - "usage: %s 3200|2400|1400}1300|1200 InputFile OutputRawFile [OPTIONS]\n\n" + "usage: %s 3200|2400|1600|1400|1300|1200|700|700B InputFile OutputRawFile [OPTIONS]\n\n" "Options:\n", argv[0]); for(i=0; ideframer, FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits)); + if (f->mode != FREEDV_MODE_800XA) + fvhff_frame_data_bits(f->deframer, FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits)); + else + fvhff_frame_data_bits(f->deframer, FREEDV_HF_FRAME_B,(uint8_t*)(f->tx_bits)); /* Allocate floating point buffer for FSK mod */ tx_float = alloca(sizeof(float)*f->n_nom_modem_samples); /* do 4fsk mod */ - if(f->mode == FREEDV_MODE_2400A){ + if(f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_800XA){ fsk_mod(f->fsk,tx_float,(uint8_t*)(f->tx_bits)); /* Convert float samps to short */ for(i=0; in_nom_modem_samples; i++){ @@ -760,7 +763,10 @@ int freedv_data_ntxframes (struct freedv *f){ #ifndef CORTEX_M4 if (f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_2400B) { if (f->deframer->fdc) - return freedv_data_get_n_tx_frames(f->deframer->fdc); + return freedv_data_get_n_tx_frames(f->deframer->fdc, 8); + } else if (f->mode == FREEDV_MODE_800XA) { + if (f->deframer->fdc) + return freedv_data_get_n_tx_frames(f->deframer->fdc, 6); } #endif return 0; diff --git a/codec2-dev/src/freedv_data_channel.c b/codec2-dev/src/freedv_data_channel.c index 723412e4..32ba6eb7 100644 --- a/codec2-dev/src/freedv_data_channel.c +++ b/codec2-dev/src/freedv_data_channel.c @@ -82,6 +82,27 @@ static unsigned short fdc_crc(unsigned char *buffer, size_t len) return crc ^ 0xffff; } + +/* CRC4 0x03 polynomal */ +static unsigned char fdc_crc4(unsigned char *buffer, size_t len) +{ + unsigned char crc = 0x0f; + size_t i; + + for (i = 0; i < len; i++, buffer++) { + int shift; + + for (shift = 7; shift <= 0; shift--) { + crc <<= 1; + if ((*buffer >> shift) & 0x1) + crc |= 1; + if (crc & 0x10) + crc ^= 0x03; + } + } + + return crc & 0x0f; +} struct freedv_data_channel *freedv_data_channel_create(void) { @@ -118,8 +139,15 @@ void freedv_data_set_cb_tx(struct freedv_data_channel *fdc, freedv_data_callback fdc->cb_tx_state = state; } -void freedv_data_channel_rx_frame(struct freedv_data_channel *fdc, unsigned char data[8], int from_bit, int bcast_bit, int end_bits) +void freedv_data_channel_rx_frame(struct freedv_data_channel *fdc, unsigned char *data, size_t size, int from_bit, int bcast_bit, int crc_bit, int end_bits) { + int copy_bits; + if (end_bits) { + copy_bits = end_bits; + } else { + copy_bits = size; + } + /* New packet? */ if (fdc->packet_rx_cnt == 0) { /* Does the packet have a compressed from field? */ @@ -129,18 +157,34 @@ void freedv_data_channel_rx_frame(struct freedv_data_channel *fdc, unsigned char fdc->packet_rx_cnt += 6; } if (bcast_bit) { - /* Compressed to: fill in broadcast address */ - memcpy(fdc->packet_rx + fdc->packet_rx_cnt, fdc_header_bcast, sizeof(fdc_header_bcast)); - fdc->packet_rx_cnt += 6; + if (!from_bit) { + /* Copy from header and modify size and end_bits accordingly */ + memcpy(fdc->packet_rx + fdc->packet_rx_cnt, data, 6); + fdc->packet_rx_cnt += 6; + copy_bits -= 6; + if (copy_bits < 0) + copy_bits = 0; + data += 6; + } + /* Compressed to: fill in broadcast address */ + memcpy(fdc->packet_rx + fdc->packet_rx_cnt, fdc_header_bcast, sizeof(fdc_header_bcast)); + fdc->packet_rx_cnt += 6; } + if (crc_bit) { + unsigned char calc_crc = fdc_crc4(data, size); + if (calc_crc == end_bits) { + /* It is a single header field, remember it for later */ + memcpy(fdc->packet_rx + 6, data, 6); + memcpy(fdc->packet_rx, fdc_header_bcast, 6); + if (fdc->cb_rx) { + fdc->cb_rx(fdc->cb_rx_state, fdc->packet_rx, 12); + } + } + fdc->packet_rx_cnt = 0; + return; + } } - int copy_bits; - if (end_bits) { - copy_bits = end_bits; - } else { - copy_bits = 8; - } if (fdc->packet_rx_cnt + copy_bits >= FREEDV_DATA_CHANNEL_PACKET_MAX) { /* Something went wrong... this can not be a real packet */ fdc->packet_rx_cnt = 0; @@ -157,7 +201,7 @@ void freedv_data_channel_rx_frame(struct freedv_data_channel *fdc, unsigned char rx_crc |= fdc->packet_rx[fdc->packet_rx_cnt - 2]; if (rx_crc == calc_crc) { - if (fdc->packet_rx_cnt == 8) { + if (fdc->packet_rx_cnt == size) { /* It is a single header field, remember it for later */ memcpy(fdc->rx_header, fdc->packet_rx, 6); } @@ -179,10 +223,11 @@ void freedv_data_channel_rx_frame(struct freedv_data_channel *fdc, unsigned char } } -void freedv_data_channel_tx_frame(struct freedv_data_channel *fdc, unsigned char data[8], int *from_bit, int *bcast_bit, int *end_bits) +void freedv_data_channel_tx_frame(struct freedv_data_channel *fdc, unsigned char *data, size_t size, int *from_bit, int *bcast_bit, int *crc_bit, int *end_bits) { *from_bit = 0; *bcast_bit = 0; + *crc_bit = 0; if (!fdc->packet_tx_size) { fdc->packet_tx_cnt = 0; @@ -193,12 +238,23 @@ void freedv_data_channel_tx_frame(struct freedv_data_channel *fdc, unsigned char } if (!fdc->packet_tx_size) { /* Nothing to send, insert a header frame */ - memcpy(fdc->packet_tx, fdc->tx_header, 8); - fdc->packet_tx_size = 8; + memcpy(fdc->packet_tx, fdc->tx_header, size); + if (size < 8) { + *end_bits = fdc_crc4(fdc->tx_header, size); + *crc_bit = 1; + memcpy(data, fdc->tx_header, size); + + return; + } else { + fdc->packet_tx_size = size; + } } else { /* new packet */ unsigned short crc; unsigned char tmp[6]; + + *from_bit = !memcmp(fdc->packet_tx + 6, fdc->tx_header, 6); + *bcast_bit = !memcmp(fdc->packet_tx, fdc_header_bcast, 6); memcpy(tmp, fdc->packet_tx, 6); memcpy(fdc->packet_tx, fdc->packet_tx + 6, 6); @@ -211,22 +267,23 @@ void freedv_data_channel_tx_frame(struct freedv_data_channel *fdc, unsigned char fdc->packet_tx[fdc->packet_tx_size] = (crc >> 8) & 0xff; fdc->packet_tx_size++; - if (!memcmp(fdc->packet_tx, fdc->tx_header, 6)) { - *from_bit = 1; + if (*from_bit) { fdc->packet_tx_cnt = 6; - - if (!memcmp(fdc->packet_tx + 6, fdc_header_bcast, 6)) { - *bcast_bit = 1; - fdc->packet_tx_cnt += 6; - } - } + } else { + if (*bcast_bit) { + memcpy(fdc->packet_tx + 6, fdc->packet_tx, 6); + } + } + if (*bcast_bit) { + fdc->packet_tx_cnt += 6; + } } } if (fdc->packet_tx_size) { int copy = fdc->packet_tx_size - fdc->packet_tx_cnt; - if (copy > 8) { - copy = 8; + if (copy > size) { + copy = size; *end_bits = 0; } else { *end_bits = copy; @@ -246,8 +303,8 @@ void freedv_data_set_header(struct freedv_data_channel *fdc, unsigned char *head fdc->tx_header[7] = (crc >> 8) & 0xff; } -int freedv_data_get_n_tx_frames(struct freedv_data_channel *fdc) +int freedv_data_get_n_tx_frames(struct freedv_data_channel *fdc, size_t size) { - /* packet will be send in 8 byte frames */ - return (fdc->packet_tx_size + 7) / 8; + /* packet will be send in 'size' byte frames */ + return (fdc->packet_tx_size + size-1) / size; } diff --git a/codec2-dev/src/freedv_data_channel.h b/codec2-dev/src/freedv_data_channel.h index cae25149..e12f6dc3 100644 --- a/codec2-dev/src/freedv_data_channel.h +++ b/codec2-dev/src/freedv_data_channel.h @@ -61,10 +61,10 @@ void freedv_data_channel_destroy(struct freedv_data_channel *fdc); void freedv_data_set_cb_rx(struct freedv_data_channel *fdc, freedv_data_callback_rx cb, void *state); void freedv_data_set_cb_tx(struct freedv_data_channel *fdc, freedv_data_callback_tx cb, void *state); -void freedv_data_channel_rx_frame(struct freedv_data_channel *fdc, unsigned char data[8], int from_bit, int bcast_bit, int end_bits); -void freedv_data_channel_tx_frame(struct freedv_data_channel *fdc, unsigned char data[8], int *from_bit, int *bcast_bit, int *end_bits); +void freedv_data_channel_rx_frame(struct freedv_data_channel *fdc, unsigned char *data, size_t size, int from_bit, int bcast_bit, int crc_bit, int end_bits); +void freedv_data_channel_tx_frame(struct freedv_data_channel *fdc, unsigned char *data, size_t size, int *from_bit, int *bcast_bit, int *crc_bit, int *end_bits); void freedv_data_set_header(struct freedv_data_channel *fdc, unsigned char *header); -int freedv_data_get_n_tx_frames(struct freedv_data_channel *fdc); +int freedv_data_get_n_tx_frames(struct freedv_data_channel *fdc, size_t size); #endif /* _FREEDV_DATA_CHANNEL_H */ diff --git a/codec2-dev/src/freedv_rx.c b/codec2-dev/src/freedv_rx.c index f351452c..59d265e6 100644 --- a/codec2-dev/src/freedv_rx.c +++ b/codec2-dev/src/freedv_rx.c @@ -208,16 +208,16 @@ int main(int argc, char *argv[]) { /* deccode the speech ourself (or send it to elsewhere, e.g. network) */ if (nout) { unsigned char *enc_frame = encoded; - short *speech_frame = speech_out; - - nout = 0; - for (i = 0; i < codec_frames; i++) { - codec2_decode(c2, speech_frame, enc_frame); - enc_frame += bytes_per_codec_frame; - speech_frame += samples_per_frame; - nout += samples_per_frame; - } - } + short *speech_frame = speech_out; + + nout = 0; + for (i = 0; i < codec_frames; i++) { + codec2_decode(c2, speech_frame, enc_frame); + enc_frame += bytes_per_codec_frame; + speech_frame += samples_per_frame; + nout += samples_per_frame; + } + } } nin = freedv_nin(freedv); diff --git a/codec2-dev/src/freedv_tx.c b/codec2-dev/src/freedv_tx.c index f434f061..e56dd06a 100644 --- a/codec2-dev/src/freedv_tx.c +++ b/codec2-dev/src/freedv_tx.c @@ -75,7 +75,7 @@ void my_datatx(void *callback_state, unsigned char *packet, size_t *size) { int i; for (i = 0; i < 64; i++) packet[i] = i; - *size = i; + *size = i; } else { /* set size to zero, the freedv api will insert a header frame */ *size = 0; @@ -143,24 +143,23 @@ int main(int argc, char *argv[]) { } if (strcmp(argv[i], "--codectx") == 0) { int c2_mode; - + if (mode == FREEDV_MODE_700) { - c2_mode = CODEC2_MODE_700; - } else if ((mode == FREEDV_MODE_700B)|| (mode == FREEDV_MODE_800XA)) { + c2_mode = CODEC2_MODE_700; + } else if ((mode == FREEDV_MODE_700B)|| (mode == FREEDV_MODE_800XA)) { c2_mode = CODEC2_MODE_700B; } else { c2_mode = CODEC2_MODE_1300; } use_codectx = 1; - c2 = codec2_create(c2_mode); assert(c2 != NULL); } - if (strcmp(argv[i], "--datatx") == 0) { - unsigned char header[6] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc }; - freedv_set_data_header(freedv, header); - use_datatx = 1; - } + if (strcmp(argv[i], "--datatx") == 0) { + unsigned char header[6] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc }; + freedv_set_data_header(freedv, header); + use_datatx = 1; + } } } freedv_set_snr_squelch_thresh(freedv, -100.0); @@ -200,30 +199,30 @@ int main(int argc, char *argv[]) { unsigned char encoded[bytes_per_codec_frame * codec_frames]; unsigned char *enc_frame = encoded; short *speech_frame = speech_in; - float energy = 0; + float energy = 0; /* Encode the speech ourself (or get it from elsewhere, e.g. network) */ for (i = 0; i < codec_frames; i++) { codec2_encode(c2, enc_frame, speech_frame); energy += codec2_get_energy(c2, enc_frame); - enc_frame += bytes_per_codec_frame; + enc_frame += bytes_per_codec_frame; speech_frame += samples_per_frame; - } - energy /= codec_frames; - - /* Is the audio fragment quiet? */ - if (use_datatx && energy < 1.0) { + } + energy /= codec_frames; + fprintf(stderr,"energy:%f\n",energy); + /* Is the audio fragment quiet? */ + if (use_datatx && energy < 1.0) { /* Insert a frame with data instead of speech */ - freedv_datatx(freedv, mod_out); + freedv_datatx(freedv, mod_out); } else { /* Use the freedv_api to modulate already encoded frames */ freedv_codectx(freedv, mod_out, encoded); } - } + } fwrite(mod_out, sizeof(short), n_nom_modem_samples, fout); - /* if this is in a pipeline, we probably don't want the usual + /* if this is in a pipeline, we probably don't want the usual buffering to occur */ if (fout == stdout) fflush(stdout); diff --git a/codec2-dev/src/freedv_vhf_framing.c b/codec2-dev/src/freedv_vhf_framing.c index 63757f6f..11d59945 100644 --- a/codec2-dev/src/freedv_vhf_framing.c +++ b/codec2-dev/src/freedv_vhf_framing.c @@ -64,8 +64,11 @@ static const uint8_t A_blank[] = {1,0,1,0,0,1,1,1, /* Padding[0:3] Proto[0:3] 0,0,0,0,0,0,1,0, /* Voice[48:51] Proto[12:15] */ 0,1,1,1,0,0,1,0};/* Proto[16:19] Padding[4:7] */ -/* HF Type B UW */ +/* HF Type B voice UW */ static const uint8_t B_uw_v[] = {0,1,1,0,0,1,1,1}; + +/* HF Type B data UW */ +static const uint8_t B_uw_d[] = {1,1,1,1,0,0,1,0}; /* Blank HF type B frame */ static const uint8_t B_blank[] = {0,1,1,0,0,1,1,1, /* UW[0:7] */ @@ -165,6 +168,7 @@ void fvhff_frame_data_bits(struct freedv_vhf_deframer * def, int frame_type, int end_bits; int from_bit; int bcast_bit; + int crc_bit; /* Fill out frame with blank frame prototype */ for(i=0; i<4; i++) @@ -177,7 +181,7 @@ void fvhff_frame_data_bits(struct freedv_vhf_deframer * def, int frame_type, bits_out[40 + i] = A_uw_d[i]; if (def->fdc) - freedv_data_channel_tx_frame(def->fdc, data, &from_bit, &bcast_bit, &end_bits); + freedv_data_channel_tx_frame(def->fdc, data, 8, &from_bit, &bcast_bit, &crc_bit, &end_bits); else return; @@ -198,7 +202,40 @@ void fvhff_frame_data_bits(struct freedv_vhf_deframer * def, int frame_type, } for (i = 0; i < 4; i++) - bits_out[88 + i] = (end_bits >> (3-i)) & 0x1; + bits_out[88 + i] = (end_bits >> (3-i)) & 0x1; + } else if (frame_type == FREEDV_HF_FRAME_B){ + uint8_t data[6]; + int end_bits; + int from_bit; + int bcast_bit; + int crc_bit; + + /* Fill out frame with blank prototype */ + for(i=0; i<64; i++) + bits_out[i] = B_blank[i]; + + /* UW data */ + for (i=0; i < 8; i++) + bits_out[0 + i] = B_uw_d[i]; + + if (def->fdc) + freedv_data_channel_tx_frame(def->fdc, data, 6, &from_bit, &bcast_bit, &crc_bit, &end_bits); + else + return; + + bits_out[56] = from_bit; + bits_out[57] = bcast_bit; + bits_out[58] = crc_bit; + bits_out[59] = 0; /* unused */ + + /* Fill in data bits */ + ibit = 0; + for(i=8; i<56; i++){ /* First half */ + bits_out[i] = UNPACK_BIT_MSBFIRST(data,ibit); + ibit++; + } + for (i = 0; i < 4; i++) + bits_out[60 + i] = (end_bits >> (3-i)) & 0x1; } } @@ -318,7 +355,6 @@ static int fvhff_match_uw(struct freedv_vhf_deframer * def,uint8_t bits[],int to int uw_len = def->uw_size; int iuw,ibit; const uint8_t * uw[2]; - int can_be_data; int uw_offset; int diff[2] = { 0, 0 }; int i; @@ -328,16 +364,14 @@ static int fvhff_match_uw(struct freedv_vhf_deframer * def,uint8_t bits[],int to /* Set up parameters for the standard type of frame */ if(frame_type == FREEDV_VHF_FRAME_A){ uw[0] = A_uw_v; - uw[1] = A_uw_d; + uw[1] = A_uw_d; uw_len = 16; uw_offset = 40; - can_be_data = 1; } else if(frame_type == FREEDV_HF_FRAME_B){ uw[0] = B_uw_v; - uw[1] = B_uw_v; + uw[1] = B_uw_d; uw_len = 8; uw_offset = 0; - can_be_data = 0; } else { return 0; } @@ -356,7 +390,7 @@ static int fvhff_match_uw(struct freedv_vhf_deframer * def,uint8_t bits[],int to match[i] = diff[i] <= tol; } /* Pick the best matching UW */ - if (diff[0] < diff[1] || !can_be_data) { + if (diff[0] < diff[1]) { r = match[0]; *rdiff = diff[0]; *pt = FRAME_PAYLOAD_TYPE_VOICE; @@ -509,7 +543,49 @@ static void fvhff_extract_frame_data(struct freedv_vhf_deframer * def,uint8_t bi } if (def->fdc) { - freedv_data_channel_rx_frame(def->fdc, data, from_bit, bcast_bit, end_bits); + freedv_data_channel_rx_frame(def->fdc, data, 8, from_bit, bcast_bit, 0, end_bits); + } + } else if(frame_type == FREEDV_HF_FRAME_B){ + uint8_t data[6]; + int end_bits = 0; + int from_bit; + int bcast_bit; + int crc_bit; + + ibit = 0; + memset(data,0,6); + + /* Extract and pack first c2 frame, MSB first */ + iframe = bitptr+8; + if(iframe >= frame_size) iframe-=frame_size; + for(;ibit<48;ibit++){ + data[ibit>>3] |= (bits[iframe]&0x1)<<(7-(ibit&0x7)); + iframe++; + if(iframe >= frame_size) iframe=0; + } + + iframe = bitptr+56; + if(iframe >= frame_size) iframe-=frame_size; + from_bit = bits[iframe]; + iframe++; + if(iframe >= frame_size) iframe-=frame_size; + bcast_bit = bits[iframe]; + iframe++; + if(iframe >= frame_size) iframe-=frame_size; + crc_bit = bits[iframe]; + + /* Extract endbits value, MSB first*/ + iframe = bitptr+60; + ibit = 0; + if(iframe >= frame_size) iframe-=frame_size; + for(;ibit<4;ibit++){ + end_bits |= (bits[iframe]&0x1)<<(3-(ibit)); + iframe++; + if(iframe >= frame_size) iframe=0; + } + + if (def->fdc) { + freedv_data_channel_rx_frame(def->fdc, data, 7, from_bit, bcast_bit, crc_bit, end_bits); } } } diff --git a/codec2-dev/unittest/CMakeLists.txt b/codec2-dev/unittest/CMakeLists.txt index e365cd7b..a3713d56 100644 --- a/codec2-dev/unittest/CMakeLists.txt +++ b/codec2-dev/unittest/CMakeLists.txt @@ -72,6 +72,9 @@ target_link_libraries(tfmfsk m) add_executable(tdeframer tdeframer.c) target_link_libraries(tdeframer m codec2) +add_executable(tfreedv_data_channel tfreedv_data_channel.c) +target_link_libraries(tfreedv_data_channel codec2) + #add_executable(t48_8 t48_8.c ../src/fdmdv.c ../src/kiss_fft.c) #target_link_libraries(t48_8 codec2) diff --git a/codec2-dev/unittest/tfreedv_data_channel.c b/codec2-dev/unittest/tfreedv_data_channel.c new file mode 100644 index 00000000..b67fe9e6 --- /dev/null +++ b/codec2-dev/unittest/tfreedv_data_channel.c @@ -0,0 +1,277 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tfreedv_data_channel + AUTHOR......: Jeroen Vreeken + DATE CREATED: May 3 2016 + + Tests for the data channel code. + Data channel frame behaviour is tested with test vectors. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2016 Jeroen Vreeken + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include "freedv_data_channel.h" + +#include +#include + +unsigned char test_header[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }; + + +struct testvec { + char *testname; + + unsigned char *data; + size_t data_size; + + size_t frame_size; + + unsigned char *frame_data; + size_t frame_data_size; + + unsigned char *flags; +} testvec[] = { + { + "Regular packet, does not match header and no broadcast", + (unsigned char[]){ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12 + }, + 0x12, + 8, + (unsigned char[]){ + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x01, 0x02, + 0x03, 0x04, 0x05, 0x06, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x47, 0x6e + }, + 0x14, + (unsigned char[]){ 0x00, 0x00, 0x04 }, + }, + { + "Header", + NULL, + 0, + 8, + (unsigned char[]){ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x5a, 0x60 }, + 0x08, + (unsigned char[]){ 0x08 }, + }, + { + "Broadcast packet", + (unsigned char[]){ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, 0x22, + 0x33, 0x44, 0x55, 0x66, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11 + }, + 0x19, + 8, + (unsigned char[]){ + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x3c, 0xbe + }, + 0x0f, + (unsigned char[]){ 0xc0, 0x07 }, + }, + { + "Broadcast packet, header does not match", + (unsigned char[]){ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0x22, + 0xbb, 0xcc, 0xdd, 0xee, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11 + }, + 0x19, + 8, + (unsigned char[]){ + 0xaa, 0x22, + 0xbb, 0xcc, 0xdd, 0xee, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x1a, 0x68 + }, + 0x15, + (unsigned char[]){ 0x40, 0x00, 0x05 }, + }, + { + "Header 6 bytes", + NULL, + 0, + 6, + (unsigned char[]){ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }, + 0x06, + (unsigned char[]){ 0x2f }, + }, + { + "Broadcast packet, header does not match (6 byte frames)", + (unsigned char[]){ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0x22, + 0xbb, 0xcc, 0xdd, 0xee, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11 + }, + 0x19, + 6, + (unsigned char[]){ + 0xaa, 0x22, + 0xbb, 0xcc, 0xdd, 0xee, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x1a, 0x68 + }, + 0x15, + (unsigned char[]){ 0x40, 0x00, 0x00, 0x03 }, + }, +}; + + +static int ret = 0; +static int vector = 0; +static size_t frame_data_pos = 0; +static int rx_done = 0; + +void *tx_cb_arg = (void*)0xaa55; +void *rx_cb_arg = (void*)0xbb44; + +void tfreedv_data_callback_tx(void *arg, unsigned char *packet, size_t *size) +{ + if (tx_cb_arg != arg) { + ret++; + printf("FAIL: %s called with wrong argument value\n", __func__); + } + printf("--------------------------------------\n"); + printf("TX callback called for test %zd bytes data for test %d:\n'%s'\n", + testvec[vector].data_size, vector, + testvec[vector].testname); + + memcpy(packet, testvec[vector].data, testvec[vector].data_size); + *size = testvec[vector].data_size; + + return; +} + +void tfreedv_data_callback_rx(void *arg, unsigned char *packet, size_t size) +{ + if (rx_cb_arg != arg) { + ret++; + printf("FAIL: %s called with wrong argument value\n", __func__); + } + printf("RX callback called with %zd bytes\n", size); + + if (testvec[vector].data_size) { + size_t data_size = testvec[vector].data_size; + if (data_size != size) { + printf("FAIL: Received size does not match test vector: %zd != %zd\n", + size, data_size); + ret++; + } else { + size_t i; + for (i = 0; i < data_size; i++) { + if (packet[i] != testvec[vector].data[i]) { + printf("FAIL: byte %zd does not match 0x%02x != 0x%02x\n", + i, packet[i], testvec[vector].data[i]); + ret++; + } + } + } + } else { + if (size != 12) { + printf("FAIL: Received header is not 12 bytes: %zd\n", size); + ret++; + } else { + if (memcmp(packet+6, test_header, 6)) { + printf("FAIL: Header does not match!\n"); + ret++; + } + } + } + + rx_done = 1; +} + +int main(int argc, char **argv) +{ + struct freedv_data_channel *fdc; + + fdc = freedv_data_channel_create(); + + freedv_data_set_header(fdc, test_header); + freedv_data_set_cb_tx(fdc, tfreedv_data_callback_tx, tx_cb_arg); + freedv_data_set_cb_rx(fdc, tfreedv_data_callback_rx, rx_cb_arg); + + while (vector < sizeof(testvec)/sizeof(struct testvec)) { + size_t frame_size = testvec[vector].frame_size; + unsigned char frame[frame_size]; + int from, bcast, crc, end; + int i; + size_t check_size; + unsigned char flags; + + freedv_data_channel_tx_frame(fdc, frame, frame_size, &from, &bcast, &crc, &end); + + check_size = frame_size; + if (frame_data_pos + check_size > testvec[vector].frame_data_size) + check_size = testvec[vector].frame_data_size - frame_data_pos; + + flags = from * 0x80 + bcast * 0x40 + crc * 0x20 + end; + printf("0x%02x:", flags); + for (i = 0; i < check_size; i++) { + if (frame[i] != testvec[vector].frame_data[frame_data_pos + i]) { + printf(" [0x%02x!=0x%02x]", + frame[i], testvec[vector].frame_data[frame_data_pos + i]); + ret++; + } else { + printf(" 0x%02x", frame[i]); + } + } + printf("\n"); + + if (flags != testvec[vector].flags[frame_data_pos / frame_size]) { + printf("FAIL: Flags byte does not match 0x%02x != 0x%02x\n", + flags, testvec[vector].flags[frame_data_pos / frame_size]); + ret++; + } + + freedv_data_channel_rx_frame(fdc, frame, frame_size, from, bcast, crc, end); + + frame_data_pos += frame_size; + + if (frame_data_pos >= testvec[vector].frame_data_size) { + vector++; + frame_data_pos = 0; + if (!rx_done) { + printf("FAIL: RX callback not executed\n"); + ret++; + } + rx_done = 0; + } + } + + freedv_data_channel_destroy(fdc); + + printf("--------------------------------------\n"); + printf("tfreedv_data_channel test result: "); + if (ret) { + printf("Failed %d\n", ret); + } else { + printf("Passed\n"); + } + + return ret; +}