horus_api coded, compiles OK, but not tested
authordrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Sun, 8 Apr 2018 21:32:27 +0000 (21:32 +0000)
committerdrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Sun, 8 Apr 2018 21:32:27 +0000 (21:32 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@3454 01035d8c-6547-0410-b346-abe4f91aad63

codec2-dev/src/horus_api.c [new file with mode: 0644]
codec2-dev/src/horus_api.h

diff --git a/codec2-dev/src/horus_api.c b/codec2-dev/src/horus_api.c
new file mode 100644 (file)
index 0000000..c829a8d
--- /dev/null
@@ -0,0 +1,359 @@
+/*---------------------------------------------------------------------------*\
+
+  FILE........: horus_api.c
+  AUTHOR......: David Rowe
+  DATE CREATED: March 2018
+
+  Library of API functions that implement High Altitude Balloon (HAB)
+  telemetry modems and protocols for Project Horus.  May also be useful for
+  other HAB projects.
+
+\*---------------------------------------------------------------------------*/
+
+/*
+  Copyright (C) 2018 David Rowe
+
+  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.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 Lesser General Public License
+  along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "horus_api.h"
+#include "fsk.h"
+#include "horus_l2.h"
+
+#define MAX_UW_LENGTH                  100
+#define HORUS_API_VERSION                1    /* unique number that is bumped if API changes */
+#define HORUS_BINARY_NUM_BITS          360    /* fixed number of bytes in binary payload     */
+#define HORUS_BINARY_NUM_PAYLOAD_BYTES  22    /* fixed number of bytes in binary payload     */
+
+struct horus {
+    int         mode;                
+    struct FSK *fsk;                 /* states for FSK modem              */
+    int         Fs;                  /* sample rate in Hz                 */
+    int         mFSK;                /* number of FSK tones               */
+    int         Rs;                  /* symbol rate in Hz                 */
+    int8_t      uw[MAX_UW_LENGTH];   /* unique word bits mapped to +/-1   */
+    int         uw_thresh;           /* threshold for UW detection        */
+    int         uw_len;              /* length of unique word             */
+    int         max_packet_len;      /* max length of a telemetry packet  */
+    uint8_t    *rx_bits;             /* buffer of received bits           */
+    int         rx_bits_len;         /* length of rx_bits buffer          */
+};
+
+/* Unique word for Horus RTTY 7 bit '$' character, 3 sync bits,
+   repeated 5 times */
+
+int8_t uw_horus_rtty[] = {
+  0,0,1,0,0,1,0,1,1,0,
+  0,0,1,0,0,1,0,1,1,0,
+  0,0,1,0,0,1,0,1,1,0,
+  0,0,1,0,0,1,0,1,1,0,
+  0,0,1,0,0,1,0,1,1,0
+};
+
+/* Unique word for Horus Binary */
+
+int8_t uw_horus_binary[] = {
+    0,0,1,0,0,1,0,0,
+    0,0,1,0,0,1,0,0    
+};
+
+struct horus *horus_open (int mode) {
+    int i;
+    assert((mode == HORUS_MODE_RTTY) || (mode == HORUS_MODE_BINARY));
+
+    struct horus *hstates = (struct horus *)malloc(sizeof(struct horus));
+    assert(hstates != NULL);
+
+    hstates->Fs = 48000; hstates->Rs = 100;
+
+    if (mode == HORUS_MODE_RTTY) {
+        hstates->mFSK = 2;
+        hstates->max_packet_len = 1000;
+
+        /* map UW to make it easier to search for */
+
+        for (i=0; i<sizeof(uw_horus_rtty); i++) {
+            hstates->uw[i] = 2*uw_horus_rtty[i] - 1;
+        }        
+        hstates->uw_len = sizeof(uw_horus_rtty);
+        hstates->uw_thresh = sizeof(uw_horus_rtty) - 2;  /* allow a few bit errors in UW detection */
+    }
+
+    if (mode == HORUS_MODE_BINARY) {
+        hstates->mFSK = 4;
+        hstates->max_packet_len = HORUS_BINARY_NUM_BITS;
+        for (i=0; i<sizeof(uw_horus_binary); i++) {
+            hstates->uw[i] = 2*uw_horus_binary[i] - 1;
+        }
+        hstates->uw_len = sizeof(uw_horus_binary);
+        hstates->uw_thresh = sizeof(uw_horus_binary) - 2; /* allow a few bit errors in UW detection */
+    }
+   
+    hstates->fsk = fsk_create(hstates->Fs, hstates->Rs, hstates->mFSK, 1000, 2*hstates->Rs);
+
+    /* allocate enough room for two packets so we know there will be
+       one complete packet if we find a UW at start */
+    
+    hstates->rx_bits_len = 2*hstates->max_packet_len;
+    hstates->rx_bits = (uint8_t*)malloc(hstates->rx_bits_len);
+    assert(hstates->rx_bits != NULL);
+    for(i=0; i<hstates->rx_bits_len; i++) {
+        hstates->rx_bits[i] = 0;
+    }
+    
+    return hstates;
+}
+
+void horus_close (struct horus *hstates) {
+    assert(hstates != NULL);
+    fsk_destroy(hstates->fsk);
+    free(hstates->rx_bits);
+    free(hstates);
+}
+
+uint32_t horus_nin(struct horus *hstates) {
+    assert(hstates != NULL);
+    return fsk_nin(hstates->fsk);
+}
+
+int horus_find_uw(struct horus *hstates) {
+    int i, j, corr, mx, mx_ind;
+    int rx_bits_mapped[hstates->rx_bits_len];
+    
+    /* map rx_bits to +/-1 for UW search */
+
+    for(i=0; i<hstates->max_packet_len; i++) {
+        rx_bits_mapped[i] = 2*hstates->rx_bits[i] - 1;
+    }
+    
+    /* look for UW  */
+
+    mx = 0; mx_ind = 0;
+    for(i=0; i<hstates->fsk->Nbits; i++) {
+
+        /* calculate correlation between bit stream and UW */
+        
+        corr = 0;
+        for(j=0; j<hstates->uw_len; j++) {
+            corr += hstates->rx_bits[i+j]*hstates->uw[j];
+        }
+
+        /* peak pick maximum */
+        
+        if (corr > mx) {
+            mx = corr;
+            mx_ind = i;
+        }
+    }
+
+    if (mx >= hstates->uw_thresh) {
+        return mx_ind;
+    } else {
+        return -1;
+    }
+}
+
+int hex2int(char ch) {
+    if (ch >= '0' && ch <= '9')
+        return ch - '0';
+    if (ch >= 'A' && ch <= 'F')
+        return ch - 'A' + 10;
+    if (ch >= 'a' && ch <= 'f')
+        return ch - 'a' + 10;
+    return -1;
+}
+
+
+int extract_horus_rtty(struct horus *hstates, char ascii_out[], int uw_loc) {
+    const int nfield = 7;                               /* 7 bit ASCII                    */
+    const int npad   = 3;                               /* 3 sync bits between characters */
+    int st = uw_loc + hstates->uw_len;                  /* first bit of first char        */
+    int en = uw_loc + hstates->max_packet_len - nfield; /* last bit of max length packet  */
+
+    int      i, j, endpacket, nout, crc_ok;
+    uint8_t  char_dec;
+    char    *pout, *ptx_crc;
+    uint16_t rx_crc, tx_crc;
+
+    pout = ascii_out; nout = 0; crc_ok = 0; endpacket = 0;
+    
+    for (i=st; i<en; i+=nfield+npad) {
+
+        /* assemble char LSB to MSB */
+
+        char_dec = 0;
+        for(j=0; j<nfield; j++) {
+            assert(hstates->rx_bits[i] <= 1);
+            char_dec |= hstates->rx_bits[i] * (1<<j);
+        }
+        
+        /*  if we find a '*' that's the end of the packet for RX CRC calculations */
+
+        if (!endpacket && (char_dec == 42)) {
+            endpacket = 1;
+            rx_crc = horus_l2_gen_crc16(ascii_out, nout);
+            ptx_crc = pout + 2; /* start of tx CRC */
+        }
+
+        /* build up output array, really only need up to tx crc but
+           may end up going further */
+        
+        *pout++ = (char)char_dec;
+        nout++;
+        
+    }
+
+    /* if we found the end of packet flag and have enough chars to computer checksum ... */
+    
+    if (endpacket && (pout > (ptx_crc+3))) {
+        tx_crc = 0;
+        for(i=0; i<4; i++) {
+            tx_crc <<= 4;
+            tx_crc += hex2int(ptx_crc[i]);
+        }
+        crc_ok = (tx_crc == rx_crc);
+        *(ptx_crc-1) = 0;  /* terminate ASCII string */
+    }
+    else {
+        *ascii_out = 0;
+    }
+
+    /* make sure we don't overrun storage */
+    
+    assert(nout <= horus_get_max_ascii_out_len(hstates));
+    
+    return crc_ok;
+}
+
+
+int extract_horus_binary(struct horus *hstates, char hex_out[], int uw_loc) {
+    const int nfield = 8;                               /* 8 bit binary                   */
+    int st = uw_loc + hstates->uw_len;                  /* first bit of first char        */
+    int en = uw_loc + hstates->max_packet_len - nfield; /* last bit of max length packet  */
+
+    int      i, j, nout;
+    uint8_t  rxpacket[hstates->max_packet_len];
+    uint8_t  rxbyte, *pout;
+    /* convert bits to a packet of bytes */
+    
+    pout = rxpacket; nout = 0;
+    
+    for (i=st; i<en; i+=nfield) {
+
+        /* assemble bytes MSB to LSB */
+
+        rxbyte = 0;
+        for(j=0; j<nfield; j++) {
+            assert(hstates->rx_bits[i] <= 1);
+            rxbyte <<= 1;
+            rxbyte |= hstates->rx_bits[i];
+        }
+        
+        /* build up output array */
+        
+        *pout++ = rxbyte;
+        nout++;
+    }
+
+    uint8_t payload_bytes[HORUS_BINARY_NUM_PAYLOAD_BYTES];
+    horus_l2_decode_rx_packet(payload_bytes, rxpacket, HORUS_BINARY_NUM_PAYLOAD_BYTES);
+
+    return 1;
+}
+
+
+int horus_rx(struct horus *hstates, char ascii_out[], short demod_in[]) {
+    int i, j, uw_loc, valid_packet;
+    
+    assert(hstates != NULL);
+
+    /* shift buffer of bits to make room for new bits */
+
+    int Nbits = hstates->fsk->Nbits;
+    int max_packet_len = hstates->max_packet_len;
+    for(i=0,j=max_packet_len-Nbits; i<Nbits; i++,j++) {
+        hstates->rx_bits[i] = hstates->rx_bits[j];
+    }
+                   
+    /* demodulate latest bits */
+
+    COMP demod_in_comp[hstates->fsk->nin];
+    for (i=0; i<hstates->fsk->nin; i++) {
+        demod_in_comp[i].real = demod_in[i];
+        demod_in_comp[i].imag = 0;
+    }
+    fsk_demod(hstates->fsk, &hstates->rx_bits[max_packet_len-Nbits], demod_in_comp);
+    
+    /* UW search to see if we can find the start of a packet in the buffer */
+    
+    if ((uw_loc = horus_find_uw(hstates)) != -1) {
+
+        /* OK we have found a unique word, and therefore the start of
+           a packet, so lets try to extract valid packets */
+
+        if (hstates->mode == HORUS_MODE_RTTY) {
+            valid_packet = extract_horus_rtty(hstates, ascii_out, uw_loc);
+        }
+        if (hstates->mode == HORUS_MODE_BINARY) {
+            valid_packet = extract_horus_binary(hstates, ascii_out, uw_loc);
+        }
+    }
+     
+    return valid_packet;
+}
+
+int horus_get_version(void) {
+    return HORUS_API_VERSION;
+}
+
+int horus_get_mode(struct horus *hstates) {
+    assert(hstates != NULL);
+    return hstates->mode;
+}
+
+int horus_get_max_ascii_out_len(struct horus *hstates) {
+    if (hstates->mode == HORUS_MODE_RTTY) {
+        return hstates->max_packet_len/10;     /* 7 bit ASCII, plus 3 sync bits */
+    }
+    if (hstates->mode == HORUS_MODE_BINARY) {
+        return HORUS_BINARY_NUM_PAYLOAD_BYTES;
+    }
+}
+
+void horus_get_modem_stats(struct horus *hstates, int *sync, float *snr_est) {
+    struct MODEM_STATS stats;
+    assert(hstates != NULL);
+
+    /* TODO set sync if UW found "recently", but WTF is recently? Maybe need a little state 
+       machine to "blink" sync when we get a packet */
+
+    *sync = 0;
+    
+    /* TODO SNR is actually an Eb/No est for FSK - should we scale
+       Eb/No for SNR in 3kHz? */       
+
+    fsk_get_demod_stats(hstates->fsk, &stats);
+    *snr_est = stats.snr_est;
+}
+
+void horus_get_modem_extended_stats (struct horus *hstates, struct MODEM_STATS *stats) {
+    assert(hstates != NULL);
+    fsk_get_demod_stats(hstates->fsk, stats);
+}
+
index 9c60559bd8c87f4991c17edc05fc73e239811de6..515bb5459169c8ca13eb4ab1f6a92481fb30739b 100644 (file)
 
 #ifndef __HORUS_API__
 
-#define HORUS_MODE_RAW         0
-#define HORUS_MODE_BINARY      1
-#define HORUS_MODE_RTTY        2
+#include <stdint.h>
+#include "modem_stats.h"
+      
+#define HORUS_MODE_BINARY            0
+#define HORUS_MODE_RTTY              1
 
+struct horus;
 struct MODEM_STATS;
 
 struct horus *horus_open  (int mode);
 void          horus_close (struct horus *hstates);
 
-int           horus_nin   (struct horus *hstates);
-int           horus_rx    (struct horus *hstates, char frame_out[], short demod_in[]);
+/* call before horus_rx() to determine how many shorts to pass in */
 
+uint32_t      horus_nin   (struct horus *hstates);
+
+/* returns 1 if ascii_out[] is valid */
+      
+int           horus_rx    (struct horus *hstates, char ascii_out[], short demod_in[]);
+
+/* functions to get information from API  */
+      
 int           horus_get_version              (void);
 int           horus_get_mode                 (struct horus *hstates);
 void          horus_get_modem_stats          (struct horus *hstates, int *sync, float *snr_est);
 void          horus_get_modem_extended_stats (struct horus *hstates, struct MODEM_STATS *stats);
 
-int           horus_set_modem (struct horus *hstates, int Rs, int mFSK);
-int           horus_set_raw (struct horus *hstates, int *uw, int nuw_bits, int nbits_per_frame);
+/* how much storage you need for ascii_out[] */
+      
+int           horus_get_max_ascii_out_len    (struct horus *hstates);
 
 #endif