From: drowe67 Date: Sun, 8 Apr 2018 21:32:27 +0000 (+0000) Subject: horus_api coded, compiles OK, but not tested X-Git-Url: http://git.whiteaudio.com/gitweb/?a=commitdiff_plain;h=d9b28fe28c515dc7cece8d60b633ad769d75df5d;p=freetel-svn-tracking.git horus_api coded, compiles OK, but not tested git-svn-id: https://svn.code.sf.net/p/freetel/code@3454 01035d8c-6547-0410-b346-abe4f91aad63 --- diff --git a/codec2-dev/src/horus_api.c b/codec2-dev/src/horus_api.c new file mode 100644 index 00000000..c829a8d9 --- /dev/null +++ b/codec2-dev/src/horus_api.c @@ -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 . +*/ + +#include +#include +#include + +#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; iuw[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; iuw[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; irx_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; imax_packet_len; i++) { + rx_bits_mapped[i] = 2*hstates->rx_bits[i] - 1; + } + + /* look for UW */ + + mx = 0; mx_ind = 0; + for(i=0; ifsk->Nbits; i++) { + + /* calculate correlation between bit stream and UW */ + + corr = 0; + for(j=0; juw_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; irx_bits[i] <= 1); + char_dec |= hstates->rx_bits[i] * (1< (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; irx_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; irx_bits[i] = hstates->rx_bits[j]; + } + + /* demodulate latest bits */ + + COMP demod_in_comp[hstates->fsk->nin]; + for (i=0; ifsk->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); +} + diff --git a/codec2-dev/src/horus_api.h b/codec2-dev/src/horus_api.h index 9c60559b..515bb545 100644 --- a/codec2-dev/src/horus_api.h +++ b/codec2-dev/src/horus_api.h @@ -32,25 +32,36 @@ #ifndef __HORUS_API__ -#define HORUS_MODE_RAW 0 -#define HORUS_MODE_BINARY 1 -#define HORUS_MODE_RTTY 2 +#include +#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