added txt support to freedv api
authordrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Mon, 4 Aug 2014 08:56:27 +0000 (08:56 +0000)
committerdrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Mon, 4 Aug 2014 08:56:27 +0000 (08:56 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@1785 01035d8c-6547-0410-b346-abe4f91aad63

codec2-dev/src/CMakeLists.txt
codec2-dev/src/freedv_api.c
codec2-dev/src/freedv_api.h
codec2-dev/src/freedv_rx.c
codec2-dev/src/freedv_tx.c
codec2-dev/src/varicode.c [new file with mode: 0644]
codec2-dev/src/varicode.h [new file with mode: 0644]
codec2-dev/src/varicode_table.h [new file with mode: 0644]

index eb1d70afe9505e786ae888a4de59bd41c30b864f..f84442ef26bf209e4441350dc65e67ded914fdf0 100644 (file)
@@ -184,6 +184,7 @@ set(CODEC2_SRCS
     codebookge.c
     golay23.c
     freedv_api.c
+    varicode.c
 )
 
 add_library(codec2 ${CODEC2_SRCS})
index 2c62334965c1fb2a504d3f4aae199742c308abab..69d2eaa2233bc6fd4fc5ab00f076279a55d8fb03 100644 (file)
@@ -8,7 +8,7 @@
   embedding FreeDV in other programs.
       
   TODO:
-    [ ] speex tx/rx works
+    [X] speex tx/rx works
     [ ] txt messages
     [ ] optional test tx framemode
                                                                        
@@ -39,6 +39,7 @@
 #include "codec2.h"
 #include "codec2_fdmdv.h"
 #include "golay23.h"
+#include "varicode.h"
 #include "freedv_api.h"
 
 /*---------------------------------------------------------------------------*\
@@ -96,6 +97,12 @@ struct freedv *freedv_open(int mode) {
         || (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;
@@ -129,14 +136,15 @@ void freedv_close(struct freedv *freedv) {
   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);
@@ -156,6 +164,27 @@ void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]) {
         }
     }
     
+    // 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:
@@ -219,6 +248,8 @@ int freedv_nin(struct freedv *f) {
   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
@@ -235,8 +266,10 @@ int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]) {
     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;
@@ -288,6 +321,24 @@ int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]) {
                 }
             }
 
+            // 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;
@@ -325,34 +376,3 @@ int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]) {
     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;
-}
-
index b126c4998c37d4194f568723b5400c8df08034a2..a79b1ee78f3e0bc54486a7405cc4d57575b02d5e 100644 (file)
 #define FREEDV_MODE_1600    0
 #define FREEDV_NSAMPLES   320
 
+#include "varicode.h"
 
 struct freedv {
-    int            mode;
-    void          *codec2;
-    struct FDMDV  *fdmdv;
-    unsigned char *packed_codec_bits;
-    int           *codec_bits;
-    int           *tx_bits;
-    int           *fdmdv_bits;
-    int           *rx_bits;
-    int            tx_sync_bit;
-    float          snr_thresh;
-    int            nin;
-};
+    int                  mode;
+    void                *codec2;
+    struct FDMDV        *fdmdv;
+    unsigned char       *packed_codec_bits;
+    int                 *codec_bits;
+    int                 *tx_bits;
+    int                 *fdmdv_bits;
+    int                 *rx_bits;
+    int                  tx_sync_bit;
+    float                snr_thresh;
+    int                  nin;
+    struct VARICODE_DEC  varicode_dec_states;
+    short                tx_varicode_bits[VARICODE_MAX_BITS];
+    int                  nvaricode_bits;
+    int                  varicode_bit_index;
+    
+    /* user defined function ptrs to produce and consume ASCII
+      characters using aux txt channel */
+
+    char (*freedv_get_next_tx_char)(void *callback_state);
+    void (*freedv_put_next_rx_char)(void *callback_state, char c);
 
+    void                *callback_state;
+
+};
 
 struct freedv *freedv_open(int mode);
 void freedv_close(struct freedv *freedv);
@@ -54,5 +67,4 @@ void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]);
 int freedv_nin(struct freedv *f);
 int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]);
 
-
 #endif
index 8208f8f4717e23db4f9b2e41d7886b7b343c88db..aab324246814578696a088012c4648c7889f018f 100644 (file)
 
 #include "freedv_api.h"
 
+void my_put_next_rx_char(void *callback_state, char c) {
+    fprintf(stderr, "%c", c);
+}
+
 int main(int argc, char *argv[]) {
     FILE          *fin, *fout;
     short          speech_out[FREEDV_NSAMPLES];
@@ -65,6 +69,8 @@ int main(int argc, char *argv[]) {
     freedv = freedv_open(FREEDV_MODE_1600);
     assert(freedv != NULL);
 
+    freedv->freedv_put_next_rx_char = &my_put_next_rx_char;
+
     /* Note we need to work out how many samples demod needs on each
        call (nin).  This is used to adjust for differences in the tx and rx
        sample clock frequencies.  Note also the number of output
@@ -75,6 +81,12 @@ int main(int argc, char *argv[]) {
         nout = freedv_rx(freedv, speech_out, demod_in);
         fwrite(speech_out, sizeof(short), nout, fout);
         nin = freedv_nin(freedv);
+
+       /* 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);         
     }
 
     freedv_close(freedv);
index 255d5c76e8918355327bf104747553884b4a4235..ac0cdca840102e6330d597d455159881570dd90b 100644 (file)
 
 #include "freedv_api.h"
 
+struct my_callback_state {
+    char  tx_str[80];
+    char *ptx_str;
+};
+
+char my_get_next_tx_char(void *callback_state) {
+    struct my_callback_state* pstate = (struct my_callback_state*)callback_state;
+    char  c = *pstate->ptx_str++;
+    
+    if (*pstate->ptx_str == 0) {
+        pstate->ptx_str = pstate->tx_str;
+    }
+    
+    return c;
+}
+
 int main(int argc, char *argv[]) {
-    FILE          *fin, *fout;
-    short          speech_in[FREEDV_NSAMPLES];
-    short          mod_out[FREEDV_NSAMPLES];
-    struct freedv *freedv;
+    FILE                     *fin, *fout;
+    short                     speech_in[FREEDV_NSAMPLES];
+    short                     mod_out[FREEDV_NSAMPLES];
+    struct freedv            *freedv;
+    struct my_callback_state  my_cb_state;
 
     if (argc < 3) {
        printf("usage: %s InputRawSpeechFile OutputModemRawFile\n", argv[0]);
@@ -62,9 +79,24 @@ int main(int argc, char *argv[]) {
     freedv = freedv_open(FREEDV_MODE_1600);
     assert(freedv != NULL);
 
+    /* set up callback for txt msg chars */
+
+    sprintf(my_cb_state.tx_str, "cq cq cq hello world\n");
+    my_cb_state.ptx_str = my_cb_state.tx_str;
+    freedv->callback_state = (void*)&my_cb_state;
+    freedv->freedv_get_next_tx_char = &my_get_next_tx_char;
+
+    /* OK main loop */
+
     while(fread(speech_in, sizeof(short), FREEDV_NSAMPLES, fin) == FREEDV_NSAMPLES) {
         freedv_tx(freedv, mod_out, speech_in);
         fwrite(mod_out, sizeof(short), FREEDV_NSAMPLES, fout);
+
+       /* 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);         
     }
 
     freedv_close(freedv);
diff --git a/codec2-dev/src/varicode.c b/codec2-dev/src/varicode.c
new file mode 100644 (file)
index 0000000..26de09a
--- /dev/null
@@ -0,0 +1,479 @@
+//==========================================================================
+// 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
diff --git a/codec2-dev/src/varicode.h b/codec2-dev/src/varicode.h
new file mode 100644 (file)
index 0000000..4834a95
--- /dev/null
@@ -0,0 +1,51 @@
+//==========================================================================
+// Name:            varicode.h
+// Purpose:         Varicode encoded and decode functions
+// Created:         Nov 24, 2012
+// Authors:         David Rowe
+// 
+// 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/>.
+//
+//==========================================================================
+
+#ifndef __VARICODE__
+#define __VARICODE__
+
+#ifdef __cplusplus
+extern "C" {
+
+#endif
+
+#define VARICODE_MAX_BITS (10+2) /* max varicode bits for each ascii character */
+                                 /* 10 bits for code plus 2 0 bits for inter-character space */
+
+struct VARICODE_DEC {
+    int            state;
+    int            n_zeros;
+    int            v_len;
+    unsigned short packed;
+    int            code_num;
+    int            n_in;
+    int            in[2];
+};
+    
+int varicode_encode(short varicode_out[], char ascii_in[], int max_out, int n_in, int code_num);
+void varicode_decode_init(struct VARICODE_DEC *dec_states, int code_num);
+int varicode_decode(struct VARICODE_DEC *dec_states, char ascii_out[], short varicode_in[], int max_out, int n_in);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/codec2-dev/src/varicode_table.h b/codec2-dev/src/varicode_table.h
new file mode 100644 (file)
index 0000000..08f38fd
--- /dev/null
@@ -0,0 +1,338 @@
+//==========================================================================
+// Name:            varicode_table.h
+// Purpose:         Varicode look up table
+// Created:         Nov 24, 2012
+// Authors:         Clint Turner, KA7OEI,  Peter Martinez, G3PLX
+// 
+// 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/>.
+//
+//==========================================================================
+
+/* The following table defines the PKS31 varicode.  There are 128 entries,
+corresponding to ASCII characters 0-127 with two bytes for each entry.  The bits
+for the varicode are to be shifted out MSB-first for both bytes, with the first byte
+in the table being the first one to be sent.
+
+More than one zero in sequence signifies the end of the character (i.e.
+two zeroes are the intercharacter sequence, so at least two zeroes should always be
+sent before the next character is sent.
+
+This file is constructed with information from the article "PSK31 Fundamentals"
+by Peter Martinez, G3PLX by Clint Turner, KA7OEI
+*/
+unsigned char const varicode_table1[256] =     {
+    0b10101010,
+    0b11000000, // 0 NUL
+    0b10110110,
+    0b11000000, // 1 SOH
+    0b10111011,
+    0b01000000, // 2 STX
+    0b11011101,
+    0b11000000, // 3 ETX
+    0b10111010,
+    0b11000000, // 4 EOT
+    0b11010111,
+    0b11000000, // 5 ENQ
+    0b10111011,
+    0b11000000, // 6 ACK
+    0b10111111,
+    0b01000000, // 7 BEL
+    0b10111111,
+    0b11000000, // 8 BS
+    0b11101111,
+    0b00000000, // 9 HT
+    0b11101000,
+    0b00000000, // 10 LF
+    0b11011011,
+    0b11000000, // 11 VT
+    0b10110111,
+    0b01000000, // 12 FF
+    0b11111000,
+    0b00000000, // 13 CR
+    0b11011101,
+    0b01000000, // 14 SO
+    0b11101010,
+    0b11000000, // 15 SI
+    0b10111101,
+    0b11000000, // 16 DLE
+    0b10111101,
+    0b01000000, // 17 DC1
+    0b11101011,
+    0b01000000, // 18 DC2
+    0b11101011,
+    0b11000000, // 19 DC3
+    0b11010110,
+    0b11000000, // 20 DC4
+    0b11011010,
+    0b11000000, // 21 NAK
+    0b11011011,
+    0b01000000, // 22 SYN
+    0b11010101,
+    0b11000000, // 23 ETB
+    0b11011110,
+    0b11000000, // 24 CAN
+    0b11011111,
+    0b01000000, // 25 EM
+    0b11101101,
+    0b11000000, // 26 SUB
+    0b11010101,
+    0b01000000, // 27 ESC
+    0b11010111,
+    0b01000000, // 28 FS
+    0b11101110,
+    0b11000000, // 29 GS
+    0b10111110,
+    0b11000000, // 30 RS
+    0b11011111,
+    0b11000000, // 31 US
+    0b10000000,
+    0b00000000, // 32 SP
+    0b11111111,
+    0b10000000, // 33 !
+    0b10101111,
+    0b10000000, // 34 "
+    0b11111010,
+    0b10000000, // 35 #
+    0b11101101,
+    0b10000000, // 36 $
+    0b10110101,
+    0b01000000, // 37 %
+    0b10101110,
+    0b11000000, // 38 &
+    0b10111111,
+    0b10000000, // 39 '
+    0b11111011,
+    0b00000000, // 40 (
+    0b11110111,
+    0b00000000, // 41 )
+    0b10110111,
+    0b10000000, // 42 *
+    0b11101111,
+    0b10000000, // 43 +
+    0b11101010,
+    0b00000000, // 44 ,
+    0b11010100,
+    0b00000000, // 45 -
+    0b10101110,
+    0b00000000, // 46 .
+    0b11010111,
+    0b10000000, // 47 /
+    0b10110111,
+    0b00000000, // 48 0
+    0b10111101,
+    0b00000000, // 49 1
+    0b11101101,
+    0b00000000, // 50 2
+    0b11111111,
+    0b00000000, // 51 3
+    0b10111011,
+    0b10000000, // 52 4
+    0b10101101,
+    0b10000000, // 53 5
+    0b10110101,
+    0b10000000, // 54 6
+    0b11010110,
+    0b10000000, // 55 7
+    0b11010101,
+    0b10000000, // 56 8
+    0b11011011,
+    0b10000000, // 57 9
+    0b11110101,
+    0b00000000, // 58 :
+    0b11011110,
+    0b10000000, // 59 ;
+    0b11110110,
+    0b10000000, // 60 <
+    0b10101010,
+    0b00000000, // 61 =
+    0b11101011,
+    0b10000000, // 62 >
+    0b10101011,
+    0b11000000, // 63 ?
+    0b10101111,
+    0b01000000, // 64 @
+    0b11111010,
+    0b00000000, // 65 A
+    0b11101011,
+    0b00000000, // 66 B
+    0b10101101,
+    0b00000000, // 67 C
+    0b10110101,
+    0b00000000, // 68 D
+    0b11101110,
+    0b00000000, // 69 E
+    0b11011011,
+    0b00000000, // 70 F
+    0b11111101,
+    0b00000000, // 71 G
+    0b10101010,
+    0b10000000, // 72 H
+    0b11111110,
+    0b00000000, // 73 I
+    0b11111110,
+    0b10000000, // 74 J
+    0b10111110,
+    0b10000000, // 75 K
+    0b11010111,
+    0b00000000, // 76 L
+    0b10111011,
+    0b00000000, // 77 M
+    0b11011101,
+    0b00000000, // 78 N
+    0b10101011,
+    0b00000000, // 79 O
+    0b11010101,
+    0b00000000, // 80 P
+    0b11101110,
+    0b10000000, // 81 Q
+    0b10101111,
+    0b00000000, // 82 R
+    0b11011110,
+    0b00000000, // 83 S
+    0b11011010,
+    0b00000000, // 84 T
+    0b10101011,
+    0b10000000, // 85 U
+    0b11011010,
+    0b10000000, // 86 V
+    0b10101110,
+    0b10000000, // 87 W
+    0b10111010,
+    0b10000000, // 88 X
+    0b10111101,
+    0b10000000, // 89 Y
+    0b10101011,
+    0b01000000, // 90 Z
+    0b11111011,
+    0b10000000, // 91 [
+    0b11110111,
+    0b10000000, // 92 "\"
+    0b11111101,
+    0b10000000, // 93 ]
+    0b10101111,
+    0b11000000, // 94 ^
+    0b10110110,
+    0b10000000, // 95 _ (underline)
+    0b10110111,
+    0b11000000, // 96 `
+    0b10110000,
+    0b00000000, // 97 a
+    0b10111110,
+    0b00000000, // 98 b
+    0b10111100,
+    0b00000000, // 99 c
+    0b10110100,
+    0b00000000, // 100 d
+    0b11000000,
+    0b00000000, // 101 e
+    0b11110100,
+    0b00000000, // 102 f
+    0b10110110,
+    0b00000000, // 103 g
+    0b10101100,
+    0b00000000, // 104 h
+    0b11010000,
+    0b00000000, // 105 i
+    0b11110101,
+    0b10000000, // 106 j
+    0b10111111,
+    0b00000000, // 107 k
+    0b11011000,
+    0b00000000, // 108 l
+    0b11101100,
+    0b00000000, // 109 m
+    0b11110000,
+    0b00000000, // 110 n
+    0b11100000,
+    0b00000000, // 111 o
+    0b11111100,
+    0b00000000, // 112 p
+    0b11011111,
+    0b10000000, // 113 q
+    0b10101000,
+    0b00000000, // 114 r
+    0b10111000,
+    0b00000000, // 115 s
+    0b10100000,
+    0b00000000, // 116 t
+    0b11011100,
+    0b00000000, // 117 u
+    0b11110110,
+    0b00000000, // 118 v
+    0b11010110,
+    0b00000000, // 119 w
+    0b11011111,
+    0b00000000, // 120 x
+    0b10111010,
+    0b00000000, // 121 y
+    0b11101010,
+    0b10000000, // 122 z
+    0b10101101,
+    0b11000000, // 123 {
+    0b11011101,
+    0b10000000, // 124 |
+    0b10101101,
+    0b01000000, // 125 }
+    0b10110101,
+    0b11000000, // 126 ~
+    0b11101101,
+    0b01000000, // 127 (del)
+};
+
+// This code was used on FDMDV version 1, and is more compact that Code 1, but only covers a subset
+// of the ASCII cahacter set
+char const varicode_table2[] = {
+
+    ' ' ,0b11000000,
+    13  ,0b01000000, // CR, end of message
+    '=' ,0b10000000, 
+    '1' ,0b11110000,
+    '2' ,0b01110000,
+    '3' ,0b10110000,
+    '4' ,0b11010000,
+    '5' ,0b01010000,
+    '6' ,0b10010000,
+    '7' ,0b11100000,
+    '8' ,0b01100000,
+    '9' ,0b10100000,
+    'a' ,0b11111100,
+    'b' ,0b01111100,
+    'c' ,0b10111100,
+    'd' ,0b11011100,
+    'e' ,0b01011100,
+    'f' ,0b10011100,
+    'g' ,0b11101100,
+    'h' ,0b01101100,
+    'i' ,0b10101100,
+    'j' ,0b11110100,
+    'k' ,0b01110100,
+    'l' ,0b10110100,
+    'm' ,0b11010100,
+    'n' ,0b01010100,
+    'o' ,0b10010100,
+    'p' ,0b11100100,
+    'q' ,0b01100100,
+    'r' ,0b10100100,
+    's' ,0b11111000,
+    't' ,0b01111000,
+    'u' ,0b10111000,
+    'v' ,0b11011000,
+    'w' ,0b01011000,
+    'x' ,0b10011000,
+    'y' ,0b11101000,
+    'z' ,0b01101000,
+    '0' ,0b10101000
+};
+