embedding FreeDV in other programs.
TODO:
- [ ] speex tx/rx works
+ [X] speex tx/rx works
[ ] txt messages
[ ] optional test tx framemode
#include "codec2.h"
#include "codec2_fdmdv.h"
#include "golay23.h"
+#include "varicode.h"
#include "freedv_api.h"
/*---------------------------------------------------------------------------*\
|| (f->tx_bits == NULL) || (f->rx_bits == NULL) || (f->fdmdv_bits == NULL))
return NULL;
+ varicode_decode_init(&f->varicode_dec_states, 0);
+ f->nvaricode_bits = 0;
+ f->varicode_bit_index = 0;
+ f->freedv_get_next_tx_char = NULL;
+ f->freedv_put_next_rx_char = NULL;
+
golay23_init();
return f;
Takes a frame of input speech samples, encodes and modulates them to produce
a frame of modem samples that can be sent to the transmitter.
- Both speech_in[] and mod_out[] are FREEDV_NSAMPLES long.
+ speech_in[] and mod_out[] are sampled at 8 kHz, 16 bit shorts,
+ and FREEDV_NSAMPLES long.
\*---------------------------------------------------------------------------*/
void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]) {
int bit, byte, i, j;
int bits_per_codec_frame, bits_per_fdmdv_frame;
- int data, codeword1;
+ int data, codeword1, data_flag_index;
COMP tx_fdm[2*FDMDV_NOM_SAMPLES_PER_FRAME];
bits_per_codec_frame = codec2_bits_per_frame(f->codec2);
}
}
+ // spare bit in frame that codec defines. Use this 1
+ // bit/frame to send txt messages
+
+ data_flag_index = codec2_get_spare_bit_index(f->codec2);
+ assert(data_flag_index != -1); // not supported for all rates
+
+ if (f->nvaricode_bits) {
+ f->codec_bits[data_flag_index] = f->tx_varicode_bits[f->varicode_bit_index++];
+ f->nvaricode_bits--;
+ }
+
+ if (f->nvaricode_bits == 0) {
+ /* get new char and encode */
+ char s[2];
+ if (f->freedv_get_next_tx_char != NULL) {
+ s[0] = (*f->freedv_get_next_tx_char)(f->callback_state);
+ f->nvaricode_bits = varicode_encode(f->tx_varicode_bits, s, VARICODE_MAX_BITS, 1, 0);
+ f->varicode_bit_index = 0;
+ }
+ }
+
if (f->mode == FREEDV_MODE_1600) {
/* Protect first 12 out of first 16 excitation bits with (23,12) Golay Code:
Takes a frame of samples from the radio receiver, demodulates them,
then decodes them, producing a frame of decoded speech samples.
+ Both demod_in[] and speech_out[] are 16 bit shorts sampled at 8 kHz.
+
To account for difference in the transmit and receive sample clock
frequencies, the number of demod_in[] samples is time varying. It
is the responsibility of the caller to pass the correct number of
COMP rx_fdm[FDMDV_MAX_SAMPLES_PER_FRAME];
int bits_per_codec_frame, bytes_per_codec_frame, bits_per_fdmdv_frame;
int reliable_sync_bit, i, j, bit, byte, nin_prev, nout;
- int recd_codeword, codeword1;
+ int recd_codeword, codeword1, data_flag_index, n_ascii, valid;
struct FDMDV_STATS fdmdv_stats;
+ short abit[1];
+ char ascii_out;
bits_per_codec_frame = codec2_bits_per_frame(f->codec2);
bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8;
}
}
+ // extract txt msg data bit ------------------------------------------------------------
+
+ data_flag_index = codec2_get_spare_bit_index(f->codec2);
+ assert(data_flag_index != -1); // not supported for all rates
+
+ abit[0] = f->codec_bits[data_flag_index];
+
+ n_ascii = varicode_decode(&f->varicode_dec_states, &ascii_out, abit, 1, 1);
+ assert((n_ascii == 0) || (n_asacii == 1));
+ if (n_ascii && (f->freedv_put_next_rx_char != NULL)) {
+ (*f->freedv_put_next_rx_char)(f->callback_state, ascii_out);
+ }
+
+ // reconstruct missing bit we steal for data bit and decode speech
+
+ valid = codec2_rebuild_spare_bit(f->codec2, f->codec_bits);
+ assert(valid != -1);
+
// pack bits, MSB received first
bit = 7;
return nout;
}
-/*---------------------------------------------------------------------------*\
-
- FUNCTION....: freedv_tx_text
- AUTHOR......: David Rowe
- DATE CREATED: 3 August 2014
-
- Attempt to transmit an ASCII character. Will return non-zero if
- character was accepted, zero if the (single character) buffer is
- full.
-
-\*---------------------------------------------------------------------------*/
-
-int freedv_tx_text(struct freedv *mode, char c) {
- return 0;
-}
-
-/*---------------------------------------------------------------------------*\
-
- FUNCTION....: freedv_tx_text
- AUTHOR......: David Rowe
- DATE CREATED: 3 August 2014
-
- Attempt to receive an ASCII character. Will return non-zero if a new
- character is available, zero otherwise.
-
-\*---------------------------------------------------------------------------*/
-
-int freedv_rx_text(struct freedv *mode, char *c) {
- return 0;
-}
-
--- /dev/null
+//==========================================================================
+// Name: varicode.h
+// Purpose: Varicode encoded and decode functions
+// Created: Nov 24, 2012
+// Authors: David Rowe
+//
+// To test:
+// $ gcc varicode.c -o varicode -DVARICODE_UNITTEST -Wall
+// $ ./varicode
+//
+// License:
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2.1,
+// 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 General Public License
+// along with this program; if not, see <http://www.gnu.org/licenses/>.
+//
+//==========================================================================
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "varicode.h"
+#include "varicode_table.h"
+
+
+/*
+ output is an unpacked array of bits of maximum size max_out. Note
+ unpacked arrays are a more suitable form for modulator input.
+
+ Code 1 covers the entire ASCII char set.
+*/
+
+int varicode_encode1(short varicode_out[], char ascii_in[], int max_out, int n_in) {
+ int n_out, index, n_zeros, v_len;
+ unsigned short byte1, byte2, packed;
+
+ n_out = 0;
+
+ while(n_in && (n_out < max_out)) {
+
+ assert((unsigned int)(*ascii_in) < 128);
+
+ index = 2*(unsigned int)(*ascii_in);
+ byte1 = varicode_table1[index];
+ byte2 = varicode_table1[index+1];
+ packed = (byte1 << 8) + byte2;
+
+ //printf("n_in: %d ascii_in: %c index: %d packed 0x%x\n", n_in, *ascii_in, index, packed);
+ ascii_in++;
+
+ n_zeros = 0;
+ v_len = 0;
+ while ((n_zeros < 2) && (n_out < max_out) && (v_len <= VARICODE_MAX_BITS)) {
+ if (packed & 0x8000) {
+ *varicode_out = 1;
+ n_zeros = 0;
+ }
+ else {
+ *varicode_out = 0;
+ n_zeros++;
+ }
+ //printf("packed: 0x%x *varicode_out: %d n_zeros: %d v_len: %d\n", packed, *varicode_out, n_zeros,v_len );
+ packed <<= 1;
+ varicode_out++;
+ n_out++;
+ v_len++;
+ }
+ assert(v_len <= VARICODE_MAX_BITS);
+
+ n_in--;
+ }
+
+ return n_out;
+}
+
+
+/*
+ Code 2 covers a subset, but is more efficient that Code 1 (282
+ compared to 1315 bits on unittest) Unsupported characters are
+ replaced by spaces. We encode/decode two bits at a time.
+*/
+
+int varicode_encode2(short varicode_out[], char ascii_in[], int max_out, int n_in) {
+ int n_out, n_zeros, v_len, i;
+ unsigned short packed;
+
+ n_out = 0;
+
+ while(n_in && (n_out < max_out)) {
+
+ packed = varicode_table2[0]; // default to space if char not found
+
+ // see if our character exists
+ for(i=0; i<sizeof(varicode_table2); i+=2) {
+ if (varicode_table2[i] == *ascii_in)
+ packed = (unsigned short)varicode_table2[i+1] << 8;
+ }
+
+ //printf("n_in: %d ascii_in: %c index: %d packed 0x%x\n", n_in, *ascii_in, index, packed);
+ ascii_in++;
+
+ n_zeros = 0;
+ v_len = 0;
+ while ((n_zeros < 2) && (n_out < max_out) && (v_len <= VARICODE_MAX_BITS)) {
+ if (packed & 0x8000)
+ varicode_out[0] = 1;
+ else
+ varicode_out[0] = 0;
+
+ if (packed & 0x4000)
+ varicode_out[1] = 1;
+ else
+ varicode_out[1] = 0;
+
+ if (packed & 0xc000)
+ n_zeros = 0;
+ else
+ n_zeros += 2;
+
+ //printf("packed: 0x%x *varicode_out: %d n_zeros: %d v_len: %d\n", packed, *varicode_out, n_zeros,v_len );
+ packed <<= 2;
+ varicode_out +=2;
+ n_out += 2;
+ v_len += 2;
+ }
+ assert(v_len <= VARICODE_MAX_BITS);
+
+ n_in--;
+ }
+
+ assert((n_out % 2) == 0); /* outputs two bits at a time */
+
+ return n_out;
+}
+
+
+int varicode_encode(short varicode_out[], char ascii_in[], int max_out, int n_in, int code_num) {
+
+ assert((code_num ==1) || (code_num ==2));
+
+ if (code_num == 1)
+ return varicode_encode1(varicode_out, ascii_in, max_out, n_in);
+ else
+ return varicode_encode2(varicode_out, ascii_in, max_out, n_in);
+}
+
+
+void varicode_decode_init(struct VARICODE_DEC *dec_states, int code_num)
+{
+ assert((code_num ==1) || (code_num == 2));
+
+ dec_states->state = 0;
+ dec_states->n_zeros = 0;
+ dec_states->v_len = 0;
+ dec_states->packed = 0;
+ dec_states->code_num = code_num;
+ dec_states->n_in = 0;
+ dec_states->in[0] = dec_states->in[1] = 0;
+}
+
+
+/* Code 1 decode function, accepts one bit at a time */
+
+static int decode_one_bit(struct VARICODE_DEC *s, char *single_ascii, short varicode_in, int long_code)
+{
+ int found=0, i;
+ unsigned short byte1, byte2;
+
+ //printf("decode_one_bit : state: %d varicode_in: %d packed: 0x%x n_zeros: %d\n",
+ // s->state, varicode_in, s->packed, s->n_zeros);
+
+ if (s->state == 0) {
+ if (!varicode_in)
+ return 0;
+ else
+ s->state = 1;
+ }
+
+ if (s->state == 1) {
+ if (varicode_in) {
+ s->packed |= (0x8000 >> s->v_len);
+ s->n_zeros = 0;
+ }
+ else {
+ s->n_zeros++;
+ }
+ s->v_len++;
+ found = 0;
+
+ /* end of character code */
+
+ if (s->n_zeros == 2) {
+ if (s->v_len) {
+ /* run thru table but note with bit errors we might not actually find a match */
+
+ byte1 = s->packed >> 8;
+ //printf("looking for byte1 : 0x%x ... ", byte1);
+ byte2 = s->packed & 0xff;
+
+ for(i=0; i<128; i++) {
+ if ((byte1 == varicode_table1[2*i]) && (byte2 == varicode_table1[2*i+1])) {
+ found = 1;
+ *single_ascii = i;
+ }
+ }
+ }
+ varicode_decode_init(s, s->code_num);
+ }
+
+ /* code can run too long if we have a bit error */
+
+ if (s->v_len > VARICODE_MAX_BITS)
+ varicode_decode_init(s, s->code_num);
+ }
+
+ return found;
+}
+
+
+/* Code 2 decode function, accepts two bits at a time */
+
+static int decode_two_bits(struct VARICODE_DEC *s, char *single_ascii, short varicode_in1, short varicode_in2)
+{
+ int found=0, i;
+ unsigned short byte1;
+
+ if (s->state == 0) {
+ if (!(varicode_in1 || varicode_in2))
+ return 0;
+ else
+ s->state = 1;
+ }
+
+ if (s->state == 1) {
+ if (varicode_in1)
+ s->packed |= (0x8000 >> s->v_len);
+ if (varicode_in2)
+ s->packed |= (0x4000 >> s->v_len);
+ if (varicode_in1 || varicode_in2)
+ s->n_zeros = 0;
+ else
+ s->n_zeros+=2;
+
+ s->v_len+=2;
+
+ found = 0;
+
+ /* end of character code */
+
+ if (s->n_zeros == 2) {
+ if (s->v_len) {
+ /* run thru table but note with bit errors we might not actually find a match */
+
+ byte1 = s->packed >> 8;
+ //printf("looking for byte1 : 0x%x ... ", byte1);
+ for(i=0; i<sizeof(varicode_table2); i+=2) {
+ //printf("byte1: 0x%x 0x%x\n", byte1, (unsigned char)varicode_table2[i+1]);
+ if (byte1 == (unsigned char)varicode_table2[i+1]) {
+ found = 1;
+ *single_ascii = varicode_table2[i];
+ //printf("found: %d i=%d char=%c ", found, i, *single_ascii);
+ }
+ }
+ }
+ varicode_decode_init(s, s->code_num);
+ }
+
+ /* code can run too long if we have a bit error */
+
+ if (s->v_len > VARICODE_MAX_BITS)
+ varicode_decode_init(s, s->code_num);
+ }
+
+ return found;
+}
+
+
+int varicode_decode1(struct VARICODE_DEC *dec_states, char ascii_out[], short varicode_in[], int max_out, int n_in) {
+ int output, n_out;
+ char single_ascii = 0;
+
+ n_out = 0;
+
+ //printf("varicode_decode: n_in: %d\n", n_in);
+
+ while(n_in && (n_out < max_out)) {
+ output = decode_one_bit(dec_states, &single_ascii, varicode_in[0], 0);
+ varicode_in++;
+ n_in--;
+
+ if (output) {
+ *ascii_out++ = single_ascii;
+ n_out++;
+ }
+ }
+
+ return n_out;
+}
+
+
+int varicode_decode2(struct VARICODE_DEC *dec_states, char ascii_out[], short varicode_in[], int max_out, int n_in) {
+ int output, n_out;
+ char single_ascii = 0;
+
+ n_out = 0;
+
+ //printf("varicode_decode2: n_in: %d varicode_in[0] %d dec_states->n_in: %d\n", n_in, varicode_in[0], dec_states->n_in);
+ //printf("%d ", varicode_in[0]);
+ while(n_in && (n_out < max_out)) {
+
+ // keep two bit buffer so we can process two at a time
+
+ dec_states->in[0] = dec_states->in[1];
+ dec_states->in[1] = varicode_in[0];
+ dec_states->n_in++;
+ varicode_in++;
+ n_in--;
+
+ if (dec_states->n_in == 2) {
+ output = decode_two_bits(dec_states, &single_ascii, dec_states->in[0], dec_states->in[1]);
+
+ dec_states->n_in = 0;
+
+ if (output) {
+ //printf(" output: %d single_ascii: 0x%x %c\n", output, (int)single_ascii, single_ascii);
+ *ascii_out++ = single_ascii;
+ n_out++;
+ }
+ }
+ }
+
+ return n_out;
+}
+
+
+int varicode_decode(struct VARICODE_DEC *dec_states, char ascii_out[], short varicode_in[], int max_out, int n_in) {
+ if (dec_states->code_num == 1)
+ return varicode_decode1(dec_states, ascii_out, varicode_in, max_out, n_in);
+ else
+ return varicode_decode2(dec_states, ascii_out, varicode_in, max_out, n_in);
+}
+
+
+#ifdef VARICODE_UNITTEST
+void test_varicode(int code_num) {
+ char *ascii_in;
+ short *varicode;
+ int i, n_varicode_bits_out, n_ascii_chars_out, length, half, n_out, j, len;
+ char *ascii_out;
+ struct VARICODE_DEC dec_states;
+
+ if (code_num == 1) {
+ printf("long code:\n");
+ length = sizeof(varicode_table1)/2;
+ }
+ else {
+ printf("short code:\n");
+ length = sizeof(varicode_table2)/2;
+ }
+ //length = 10;
+ ascii_in = (char*)malloc(length);
+ varicode = (short*)malloc(VARICODE_MAX_BITS*sizeof(short)*length);
+ ascii_out = (char*)malloc(length);
+
+ // 1. test all Varicode codes -------------------------------------------------------------
+
+ if (code_num == 1) {
+ for(i=0; i<length; i++)
+ ascii_in[i] = (char)i;
+ }
+ else {
+ for(i=0; i<length; i++)
+ ascii_in[i] = varicode_table2[2*i];
+ }
+ //printf(" ascii_in: %s\n", ascii_in);
+ n_varicode_bits_out = varicode_encode(varicode, ascii_in, VARICODE_MAX_BITS*length, length, code_num);
+
+ printf(" n_varicode_bits_out: %d\n", n_varicode_bits_out);
+ //for(i=0; i<n_varicode_bits_out; i++) {
+ // printf("%d \n", varicode[i]);
+ //}
+
+ // split decode in half to test how it preserves state between calls
+
+ varicode_decode_init(&dec_states, code_num);
+ half = n_varicode_bits_out/2;
+ n_ascii_chars_out = varicode_decode(&dec_states, ascii_out, varicode, length, half);
+ // printf(" n_ascii_chars_out: %d\n", n_ascii_chars_out);
+
+ n_ascii_chars_out += varicode_decode(&dec_states, &ascii_out[n_ascii_chars_out],
+ &varicode[half], length-n_ascii_chars_out, n_varicode_bits_out - half);
+ assert(n_ascii_chars_out == length);
+
+ printf(" n_ascii_chars_out: %d\n", n_ascii_chars_out);
+ printf(" average bits/character: %3.2f\n", (float)n_varicode_bits_out/n_ascii_chars_out);
+
+ //printf("ascii_out: %s\n", ascii_out);
+
+ if (memcmp(ascii_in, ascii_out, length) == 0)
+ printf(" Test 1 Pass\n");
+ else
+ printf(" Test 1 Fail\n");
+
+ // 2. Test some ascii with a run of zeros -----------------------------------------------------
+
+ sprintf(ascii_in, "CQ CQ CQ this is VK5DGR");
+
+ assert(strlen(ascii_in) < length);
+ if (code_num == 2)
+ for(i=0; i<strlen(ascii_in); i++)
+ ascii_in[i] = tolower(ascii_in[i]);
+
+ for(i=0; i<3; i++) {
+ n_varicode_bits_out = varicode_encode(varicode, ascii_in, VARICODE_MAX_BITS*length, strlen(ascii_in), code_num);
+ n_ascii_chars_out = varicode_decode(&dec_states, ascii_out, varicode, length, n_varicode_bits_out);
+ ascii_out[n_ascii_chars_out] = 0;
+
+ printf(" ascii_out: %s\n", ascii_out);
+ if (strcmp(ascii_in, ascii_out) == 0)
+ printf(" Test 2 Pass\n");
+ else
+ printf(" Test 2 Fail\n");
+
+ memset(varicode, 0, sizeof(short)*20);
+ n_ascii_chars_out = varicode_decode(&dec_states, ascii_out, varicode, length, 20);
+ assert(n_ascii_chars_out == 0);
+ }
+
+ // 3. Test receiving one bit at a time -----------------------------------------------------
+
+ sprintf(ascii_in, "s=vk5dgr qth=adelaide");
+ len = strlen(ascii_in);
+ ascii_in[len] = 13;
+ ascii_in[len+1] = 0;
+
+ assert(strlen(ascii_in) < length);
+ if (code_num == 2)
+ for(i=0; i<strlen(ascii_in); i++)
+ ascii_in[i] = tolower(ascii_in[i]);
+
+ for(i=0; i<3; i++) {
+ n_varicode_bits_out = varicode_encode(varicode, ascii_in, VARICODE_MAX_BITS*length, strlen(ascii_in), code_num);
+ printf("n_varicode_bits_out: %d\n", n_varicode_bits_out);
+
+ n_ascii_chars_out = 0;
+ for(j=0; j<n_varicode_bits_out; j++) {
+ n_out = varicode_decode(&dec_states, &ascii_out[n_ascii_chars_out], &varicode[j], 1, 1);
+ if (n_out)
+ n_ascii_chars_out++;
+ }
+ ascii_out[n_ascii_chars_out] = 0;
+
+ printf(" ascii_out: %s\n", ascii_out);
+ if (strcmp(ascii_in, ascii_out) == 0)
+ printf(" Test 3 Pass\n");
+ else
+ printf(" Test 3 Fail\n");
+ }
+
+ free(ascii_in);
+ free(ascii_out);
+ free(varicode);
+}
+
+int main(void) {
+ test_varicode(1);
+ test_varicode(2);
+ return 0;
+}
+#endif