add chan_usrp interface
git-svn-id: http://op25.osmocom.org/svn/trunk@231 65a5c917-d112-43f1-993d-58c26a4786be
This commit is contained in:
parent
2ddec336ba
commit
ed927312bc
|
@ -54,8 +54,6 @@ GR_OMNITHREAD
|
|||
CFLAGS="${CFLAGS} $PTHREAD_CFLAGS"
|
||||
CXXFLAGS="${CXXFLAGS} $PTHREAD_CFLAGS"
|
||||
|
||||
CXXFLAGS="${CXXFLAGS} -I../../../imbe_vocoder/src/lib"
|
||||
|
||||
if test "x$CXX_FOR_BUILD" = x
|
||||
then
|
||||
CXX_FOR_BUILD=${CXX}
|
||||
|
|
|
@ -65,13 +65,16 @@ _repeater_la_SOURCES = repeater\
|
|||
repeater_pipe.cc \
|
||||
repeater_ctcss_squelch_ff.cc \
|
||||
repeater_squelch_base_ff.cc \
|
||||
repeater_gardner_costas_cc.cc \
|
||||
repeater_vocoder.cc \
|
||||
repeater_gardner_costas_cc.cc \
|
||||
repeater_chan_usrp.cc \
|
||||
repeater_chan_usrp_rx.cc \
|
||||
p25_framer.cc \
|
||||
bch.cc \
|
||||
rs.cc
|
||||
|
||||
# magic flags
|
||||
_repeater_la_CXXFLAGS = -I ../../../imbe_vocoder/src/lib
|
||||
_repeater_la_LDFLAGS = $(NO_UNDEFINED) -module -avoid-version -L../../../imbe_vocoder/src/lib/ -limbe_vocoder
|
||||
|
||||
# link the library against some comon swig runtime code and the
|
||||
|
@ -84,13 +87,7 @@ repeater.cc repeater.py: repeater.i $(ALL_IFILES)
|
|||
$(SWIG) $(SWIGPYTHONARGS) -module repeater -o repeater.cc $<
|
||||
|
||||
# These headers get installed in ${prefix}/include/gnuradio
|
||||
grinclude_HEADERS = \
|
||||
repeater_fsk4_slicer_fb.h \
|
||||
repeater_p25_frame_assembler.h \
|
||||
repeater_pipe.h \
|
||||
repeater_gardner_costas_cc.h \
|
||||
repeater_vocoder.h \
|
||||
repeater_ctcss_squelch_ff.h
|
||||
# grinclude_HEADERS =
|
||||
|
||||
|
||||
# These swig headers get installed in ${prefix}/include/gnuradio/swig
|
||||
|
|
|
@ -0,0 +1,648 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2006, Digium, Inc.
|
||||
*
|
||||
* Copyright (C) 2008, Jim Dixon
|
||||
* Jim Dixon <jim@lambdatel.com>
|
||||
*
|
||||
* USRP interface Copyright (C) 2010, KA1RBI
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief GNU Radio interface
|
||||
*
|
||||
* \author Jim Dixon <jim@lambdatel.com>, KA1RBI <ikj1234i at yahoo dot-com>
|
||||
*
|
||||
* \ingroup channel_drivers
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
***/
|
||||
|
||||
/* Version 0.1, 12/15/2010
|
||||
|
||||
Channel connection for Asterisk to GNU Radio/USRP
|
||||
|
||||
Its invoked as usrp/HISIP:HISPORT[:MYPORT]
|
||||
|
||||
HISIP is the IP address (or FQDN) of the GR app
|
||||
HISPORT is the UDP socket of the GR app
|
||||
MYPORT (optional) is the UDP socket that Asterisk listens on for this channel
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision: 1 $")
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <search.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/app.h"
|
||||
|
||||
#include "chan_usrp.h"
|
||||
|
||||
#define MAX_RXKEY_TIME 4
|
||||
#define KEEPALIVE_TIME 50 * 7
|
||||
|
||||
#define BLOCKING_FACTOR 4
|
||||
#define SSO sizeof(unsigned long)
|
||||
|
||||
#define QUEUE_OVERLOAD_THRESHOLD 25
|
||||
|
||||
static const char tdesc[] = "USRP Driver";
|
||||
|
||||
/* Only linear is allowed */
|
||||
static int prefformat = AST_FORMAT_SLINEAR;
|
||||
|
||||
static char context[AST_MAX_EXTENSION] = "default";
|
||||
static char type[] = "usrp";
|
||||
|
||||
/* usrp creates private structures on demand */
|
||||
|
||||
struct usrp_rxq {
|
||||
struct usrp_rxq *qe_forw;
|
||||
struct usrp_rxq *qe_back;
|
||||
char buf[USRP_VOICE_FRAME_SIZE];
|
||||
} ;
|
||||
|
||||
struct usrp_pvt {
|
||||
int usrp; /* open UDP socket */
|
||||
struct ast_channel *owner; /* Channel we belong to, possibly NULL */
|
||||
char app[16]; /* Our app */
|
||||
char stream[80]; /* Our stream */
|
||||
struct sockaddr_in si_other; /* for UDP sending */
|
||||
char txkey;
|
||||
int rxkey;
|
||||
int keepalive;
|
||||
struct ast_frame fr; /* "null" frame */
|
||||
char txbuf[(USRP_VOICE_FRAME_SIZE * BLOCKING_FACTOR) + SSO];
|
||||
int txindex;
|
||||
struct usrp_rxq rxq;
|
||||
unsigned long rxseq;
|
||||
unsigned long txseq;
|
||||
struct ast_module_user *u; /*! for holding a reference to this module */
|
||||
unsigned long writect;
|
||||
unsigned long readct;
|
||||
unsigned long send_seqno;
|
||||
int warned;
|
||||
int unkey_owed;
|
||||
};
|
||||
|
||||
static struct ast_channel *usrp_request(const char *type, int format, void *data, int *cause);
|
||||
static int usrp_call(struct ast_channel *ast, char *dest, int timeout);
|
||||
static int usrp_hangup(struct ast_channel *ast);
|
||||
static struct ast_frame *usrp_xread(struct ast_channel *ast);
|
||||
static int usrp_xwrite(struct ast_channel *ast, struct ast_frame *frame);
|
||||
static int usrp_indicate(struct ast_channel *ast, int cond, const void *data, size_t datalen);
|
||||
static int usrp_digit_begin(struct ast_channel *c, char digit);
|
||||
static int usrp_digit_end(struct ast_channel *c, char digit, unsigned int duratiion);
|
||||
static int usrp_text(struct ast_channel *c, const char *text);
|
||||
|
||||
|
||||
static const struct ast_channel_tech usrp_tech = {
|
||||
.type = type,
|
||||
.description = tdesc,
|
||||
.capabilities = AST_FORMAT_SLINEAR,
|
||||
.requester = usrp_request,
|
||||
.call = usrp_call,
|
||||
.hangup = usrp_hangup,
|
||||
.read = usrp_xread,
|
||||
.write = usrp_xwrite,
|
||||
.indicate = usrp_indicate,
|
||||
.send_text = usrp_text,
|
||||
.send_digit_begin = usrp_digit_begin,
|
||||
.send_digit_end = usrp_digit_end,
|
||||
};
|
||||
|
||||
#define MAX_CHANS 16
|
||||
static struct usrp_pvt *usrp_channels[MAX_CHANS];
|
||||
|
||||
static int handle_usrp_show(int fd, int argc, char *argv[])
|
||||
{
|
||||
char s[256];
|
||||
struct usrp_pvt *p;
|
||||
struct ast_channel *chan;
|
||||
int i;
|
||||
int ci, di;
|
||||
// ast_cli(fd, "handle_usrp_show\n");
|
||||
for (i=0; i<MAX_CHANS; i++) {
|
||||
p = usrp_channels[i];
|
||||
if (p) {
|
||||
chan = p->owner;
|
||||
ci = 0;
|
||||
di = 0;
|
||||
if (chan) {
|
||||
ci = 1;
|
||||
if (AST_LIST_EMPTY(&chan->readq))
|
||||
di = 1;
|
||||
else
|
||||
di = 2;
|
||||
}
|
||||
sprintf(s, "%s txkey %-3s rxkey %d read %lu write %lu", p->stream, (p->txkey) ? "yes" : "no", p->rxkey, p->readct, p->writect);
|
||||
ast_cli(fd, "%s\n", s);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ast_cli_entry cli_usrp_show = {
|
||||
{ "usrp", "show", NULL },
|
||||
handle_usrp_show, NULL,
|
||||
NULL };
|
||||
|
||||
static int usrp_call(struct ast_channel *ast, char *dest, int timeout)
|
||||
{
|
||||
struct usrp_pvt *p;
|
||||
|
||||
p = ast->tech_pvt;
|
||||
|
||||
if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
|
||||
ast_log(LOG_WARNING, "usrp_call called on %s, neither down nor reserved\n", ast->name);
|
||||
return -1;
|
||||
}
|
||||
/* When we call, it just works, really, there's no destination... Just
|
||||
ring the phone and wait for someone to answer */
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Calling %s on %s\n", dest, ast->name);
|
||||
|
||||
ast_setstate(ast,AST_STATE_UP);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usrp_destroy(struct usrp_pvt *p)
|
||||
{
|
||||
if (p->usrp)
|
||||
close(p->usrp);
|
||||
ast_module_user_remove(p->u);
|
||||
ast_free(p);
|
||||
}
|
||||
|
||||
static struct usrp_pvt *usrp_alloc(void *data)
|
||||
{
|
||||
struct usrp_pvt *p;
|
||||
int flags = 0;
|
||||
char stream[256];
|
||||
struct sockaddr_in si_me;
|
||||
struct hostent *host;
|
||||
struct ast_hostent ah;
|
||||
int o_slot;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(hisip);
|
||||
AST_APP_ARG(hisport);
|
||||
AST_APP_ARG(myport);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) return NULL;
|
||||
|
||||
AST_NONSTANDARD_APP_ARGS(args,data,':');
|
||||
|
||||
if ((!args.hisip) || (!args.hisip[0])) args.hisip = "127.0.0.1";
|
||||
if ((!args.hisport) || (!args.hisport[0])) args.hisport = "1234";
|
||||
if ((!args.myport) || (!args.myport[0])) args.myport = args.hisport;
|
||||
|
||||
p = ast_malloc(sizeof(struct usrp_pvt));
|
||||
if (p) {
|
||||
memset(p, 0, sizeof(struct usrp_pvt));
|
||||
|
||||
sprintf(stream,"%s:%d",args.hisip,atoi(args.hisport));
|
||||
strcpy(p->stream,stream);
|
||||
p->rxq.qe_forw = &p->rxq;
|
||||
p->rxq.qe_back = &p->rxq;
|
||||
|
||||
memset(&ah,0,sizeof(ah));
|
||||
host = ast_gethostbyname(args.hisip,&ah);
|
||||
if (!host)
|
||||
{
|
||||
ast_log(LOG_WARNING, "Unable to find host %s\n", args.hisip);
|
||||
ast_free(p);
|
||||
return NULL;
|
||||
}
|
||||
memset((char *) &p->si_other, 0, sizeof(p->si_other));
|
||||
p->si_other.sin_addr = *(struct in_addr *)host->h_addr;
|
||||
p->si_other.sin_family = AF_INET;
|
||||
p->si_other.sin_port = htons(atoi(args.hisport));
|
||||
|
||||
if ((p->usrp=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
|
||||
{
|
||||
ast_log(LOG_WARNING, "Unable to create new socket for usrp connection\n");
|
||||
ast_free(p);
|
||||
return(NULL);
|
||||
|
||||
}
|
||||
|
||||
memset((char *) &si_me, 0, sizeof(si_me));
|
||||
si_me.sin_family = AF_INET;
|
||||
si_me.sin_port = htons(atoi(args.myport));
|
||||
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (!strncmp(ast_inet_ntoa(p->si_other.sin_addr),"127.",4))
|
||||
si_me.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
if (bind(p->usrp, &si_me, sizeof(si_me))==-1)
|
||||
{
|
||||
ast_log(LOG_WARNING, "Unable to bind port for usrp connection\n");
|
||||
ast_free(p);
|
||||
return(NULL);
|
||||
|
||||
}
|
||||
if (!p->usrp) {
|
||||
ast_log(LOG_WARNING, "Unable to allocate new usrp stream '%s' with flags %d\n", stream, flags);
|
||||
ast_free(p);
|
||||
return NULL;
|
||||
}
|
||||
// TODO: do we need locking for this?
|
||||
for (o_slot=0; o_slot<MAX_CHANS; o_slot++) {
|
||||
if (!usrp_channels[o_slot]) break;
|
||||
}
|
||||
if (o_slot >= MAX_CHANS) {
|
||||
ast_log(LOG_WARNING, "Unable to find empty usrp_channels[] entry\n");
|
||||
return NULL;
|
||||
}
|
||||
usrp_channels[o_slot] = p;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static int usrp_hangup(struct ast_channel *ast)
|
||||
{
|
||||
struct usrp_pvt *p;
|
||||
int i;
|
||||
p = ast->tech_pvt;
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "usrp_hangup(%s)\n", ast->name);
|
||||
if (!ast->tech_pvt) {
|
||||
ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
|
||||
return 0;
|
||||
}
|
||||
// TODO: do we need locking for this?
|
||||
for (i=0; i<MAX_CHANS; i++) {
|
||||
if (usrp_channels[i] == p) {
|
||||
usrp_channels[i] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= MAX_CHANS)
|
||||
ast_log(LOG_WARNING, "Unable to delete usrp_channels[] entry\n");
|
||||
usrp_destroy(p);
|
||||
ast->tech_pvt = NULL;
|
||||
ast_setstate(ast, AST_STATE_DOWN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usrp_indicate(struct ast_channel *ast, int cond, const void *data, size_t datalen)
|
||||
{
|
||||
struct usrp_pvt *p = ast->tech_pvt;
|
||||
struct _chan_usrp_bufhdr bufhdr;
|
||||
|
||||
switch (cond) {
|
||||
case AST_CONTROL_RADIO_KEY:
|
||||
p->txkey = 1;
|
||||
break;
|
||||
case AST_CONTROL_RADIO_UNKEY:
|
||||
p->txkey = 0;
|
||||
break;
|
||||
case AST_CONTROL_HANGUP:
|
||||
return -1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
if (p->unkey_owed) {
|
||||
p->unkey_owed = 0;
|
||||
// tx was unkeyed - notify remote end
|
||||
memset(&bufhdr, 0, sizeof(struct _chan_usrp_bufhdr));
|
||||
memcpy(bufhdr.eye, "USRP", 4);
|
||||
bufhdr.seq = htonl(p->send_seqno++);
|
||||
if (sendto(p->usrp,&bufhdr, sizeof(bufhdr),
|
||||
0,&p->si_other,sizeof(p->si_other)) == -1) {
|
||||
if (!p->warned) {
|
||||
ast_log(LOG_WARNING, "sendto: %d\n", errno);
|
||||
p->warned = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usrp_text(struct ast_channel *ast, const char *text)
|
||||
{
|
||||
ast_log(LOG_DEBUG, "chan_usrp: FIXME: usrp_text: %s\n", text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usrp_digit_begin(struct ast_channel *ast, char digit)
|
||||
{
|
||||
ast_log(LOG_DEBUG, "chan_usrp: FIXME: usrp_digit_begin\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usrp_digit_end(struct ast_channel *ast, char digit, unsigned int duratiion)
|
||||
{
|
||||
ast_log(LOG_DEBUG, "chan_usrp: FIXME: usrp_digit_end\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ast_frame *usrp_xread(struct ast_channel *ast)
|
||||
{
|
||||
|
||||
struct usrp_pvt *p = ast->tech_pvt;
|
||||
char buf[512];
|
||||
struct sockaddr_in si_them;
|
||||
unsigned int themlen;
|
||||
unsigned long seq;
|
||||
int n;
|
||||
int datalen;
|
||||
struct ast_frame fr;
|
||||
struct usrp_rxq *qp;
|
||||
struct _chan_usrp_bufhdr *bufhdrp = (struct _chan_usrp_bufhdr *) buf;
|
||||
char *bufdata = &buf[ sizeof(struct _chan_usrp_bufhdr) ];
|
||||
|
||||
p->readct++;
|
||||
|
||||
themlen = sizeof(struct sockaddr_in);
|
||||
if ((n = recvfrom(p->usrp,buf,sizeof(buf),0,&si_them,&themlen)) == -1)
|
||||
{
|
||||
ast_log(LOG_WARNING,"Cannot recvfrom()");
|
||||
return NULL;
|
||||
}
|
||||
#if 0
|
||||
if (memcmp(&si_them.sin_addr,&p->si_other.sin_addr,sizeof(si_them.sin_addr)))
|
||||
{
|
||||
ast_log(LOG_NOTICE,"Received packet from %s, expecting it from %s\n",
|
||||
ast_inet_ntoa(si_them.sin_addr),ast_inet_ntoa(p->si_other.sin_addr));
|
||||
p->fr.frametype = 0;
|
||||
p->fr.subclass = 0;
|
||||
p->fr.datalen = 0;
|
||||
p->fr.samples = 0;
|
||||
p->fr.data = NULL;
|
||||
p->fr.src = type;
|
||||
p->fr.offset = 0;
|
||||
p->fr.mallocd=0;
|
||||
p->fr.delivery.tv_sec = 0;
|
||||
p->fr.delivery.tv_usec = 0;
|
||||
return &p->fr;
|
||||
}
|
||||
#endif
|
||||
if (n < sizeof(struct _chan_usrp_bufhdr)) {
|
||||
ast_log(LOG_NOTICE,"Received packet length %d too short\n", n);
|
||||
} else {
|
||||
datalen = n - sizeof(struct _chan_usrp_bufhdr);
|
||||
if (memcmp(bufhdrp->eye, "USRP", 4))
|
||||
{
|
||||
ast_log(LOG_NOTICE,"Received packet from %s with invalid data\n",
|
||||
ast_inet_ntoa(si_them.sin_addr));
|
||||
} else {
|
||||
seq = ntohl(bufhdrp->seq);
|
||||
if (seq != p->rxseq && seq != 0 && p->rxseq != 0) {
|
||||
fprintf(stderr, "repeater_chan_usrp: possible data loss, expected seq %lu received %lu\n", p->rxseq, seq);
|
||||
}
|
||||
p->rxseq = seq + 1;
|
||||
// TODO: add DTMF, TEXT processing
|
||||
if (datalen == USRP_VOICE_FRAME_SIZE) {
|
||||
qp = ast_malloc(sizeof(struct usrp_rxq));
|
||||
if (!qp)
|
||||
{
|
||||
ast_log(LOG_NOTICE,"Cannot malloc for qp\n");
|
||||
} else {
|
||||
memcpy(qp->buf,bufdata,USRP_VOICE_FRAME_SIZE);
|
||||
insque((struct qelem *) qp,(struct qelem *) p->rxq.qe_back);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fr.datalen = 0;
|
||||
fr.samples = 0;
|
||||
fr.frametype = 0;
|
||||
fr.subclass = 0;
|
||||
fr.data = 0;
|
||||
fr.src = type;
|
||||
fr.offset = 0;
|
||||
fr.mallocd=0;
|
||||
fr.delivery.tv_sec = 0;
|
||||
fr.delivery.tv_usec = 0;
|
||||
|
||||
return &p->fr;
|
||||
|
||||
}
|
||||
|
||||
static int usrp_xwrite(struct ast_channel *ast, struct ast_frame *frame)
|
||||
{
|
||||
struct usrp_pvt *p = ast->tech_pvt;
|
||||
struct ast_frame fr;
|
||||
struct usrp_rxq *qp;
|
||||
int n;
|
||||
char buf[USRP_VOICE_FRAME_SIZE + AST_FRIENDLY_OFFSET + SSO];
|
||||
|
||||
// buffer for constructing frame, plus two ptrs: hdr and data
|
||||
char sendbuf[ sizeof(struct _chan_usrp_bufhdr) + USRP_VOICE_FRAME_SIZE];
|
||||
struct _chan_usrp_bufhdr *bufhdrp = (struct _chan_usrp_bufhdr *) sendbuf;
|
||||
char *bufdata = &sendbuf[ sizeof(struct _chan_usrp_bufhdr) ];
|
||||
|
||||
if (ast->_state != AST_STATE_UP) {
|
||||
/* Don't try tos end audio on-hook */
|
||||
return 0;
|
||||
}
|
||||
/* Write a frame of (presumably voice) data */
|
||||
if (frame->frametype != AST_FRAME_VOICE) return 0;
|
||||
|
||||
if (!(frame->subclass & (AST_FORMAT_SLINEAR))) {
|
||||
ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (frame->datalen > USRP_VOICE_FRAME_SIZE) {
|
||||
ast_log(LOG_WARNING, "Frame datalen %d exceeds limit\n", frame->datalen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if something in rx queue */
|
||||
if (p->rxq.qe_forw != &p->rxq)
|
||||
{
|
||||
for(n = 0,qp = p->rxq.qe_forw; qp != &p->rxq; qp = qp->qe_forw)
|
||||
{
|
||||
n++;
|
||||
}
|
||||
if (n > QUEUE_OVERLOAD_THRESHOLD)
|
||||
{
|
||||
while(p->rxq.qe_forw != &p->rxq)
|
||||
{
|
||||
qp = p->rxq.qe_forw;
|
||||
remque((struct qelem *)qp);
|
||||
free(qp);
|
||||
}
|
||||
if (p->rxkey) p->rxkey = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!p->rxkey)
|
||||
{
|
||||
fr.datalen = 0;
|
||||
fr.samples = 0;
|
||||
fr.frametype = AST_FRAME_CONTROL;
|
||||
fr.subclass = AST_CONTROL_RADIO_KEY;
|
||||
fr.data = 0;
|
||||
fr.src = type;
|
||||
fr.offset = 0;
|
||||
fr.mallocd=0;
|
||||
fr.delivery.tv_sec = 0;
|
||||
fr.delivery.tv_usec = 0;
|
||||
ast_queue_frame(ast,&fr);
|
||||
}
|
||||
p->rxkey = MAX_RXKEY_TIME;
|
||||
qp = p->rxq.qe_forw;
|
||||
remque((struct qelem *) qp);
|
||||
memcpy(buf + AST_FRIENDLY_OFFSET,qp->buf,USRP_VOICE_FRAME_SIZE);
|
||||
ast_free(qp);
|
||||
|
||||
fr.datalen = USRP_VOICE_FRAME_SIZE;
|
||||
fr.samples = 160;
|
||||
fr.frametype = AST_FRAME_VOICE;
|
||||
fr.subclass = AST_FORMAT_SLINEAR;
|
||||
fr.data = buf + AST_FRIENDLY_OFFSET;
|
||||
fr.src = type;
|
||||
fr.offset = AST_FRIENDLY_OFFSET;
|
||||
fr.mallocd=0;
|
||||
fr.delivery.tv_sec = 0;
|
||||
fr.delivery.tv_usec = 0;
|
||||
ast_queue_frame(ast,&fr);
|
||||
}
|
||||
}
|
||||
if (p->rxkey == 1)
|
||||
{
|
||||
fr.datalen = 0;
|
||||
fr.samples = 0;
|
||||
fr.frametype = AST_FRAME_CONTROL;
|
||||
fr.subclass = AST_CONTROL_RADIO_UNKEY;
|
||||
fr.data = 0;
|
||||
fr.src = type;
|
||||
fr.offset = 0;
|
||||
fr.mallocd=0;
|
||||
fr.delivery.tv_sec = 0;
|
||||
fr.delivery.tv_usec = 0;
|
||||
ast_queue_frame(ast,&fr);
|
||||
}
|
||||
if (p->rxkey) p->rxkey--;
|
||||
|
||||
if (!p->txkey) return 0;
|
||||
|
||||
p->writect++;
|
||||
p->unkey_owed = 1;
|
||||
memcpy(bufdata, frame->data, frame->datalen);
|
||||
memset(bufhdrp, 0, sizeof(struct _chan_usrp_bufhdr));
|
||||
memcpy(bufhdrp->eye, "USRP", 4);
|
||||
bufhdrp->seq = htonl(p->send_seqno++);
|
||||
bufhdrp->keyup = htonl(1);
|
||||
if (sendto(p->usrp,&sendbuf,frame->datalen + sizeof(struct _chan_usrp_bufhdr),
|
||||
0,&p->si_other,sizeof(p->si_other)) == -1) {
|
||||
if (!p->warned) {
|
||||
ast_log(LOG_WARNING, "sendto: %d\n", errno);
|
||||
p->warned = 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ast_channel *usrp_new(struct usrp_pvt *i, int state)
|
||||
{
|
||||
struct ast_channel *tmp;
|
||||
tmp = ast_channel_alloc(1, state, 0, 0, "", "s", context, 0, "usrp/%s", i->stream);
|
||||
if (tmp) {
|
||||
tmp->tech = &usrp_tech;
|
||||
tmp->fds[0] = i->usrp;
|
||||
tmp->nativeformats = prefformat;
|
||||
tmp->rawreadformat = prefformat;
|
||||
tmp->rawwriteformat = prefformat;
|
||||
tmp->writeformat = prefformat;
|
||||
tmp->readformat = prefformat;
|
||||
if (state == AST_STATE_RING)
|
||||
tmp->rings = 1;
|
||||
tmp->tech_pvt = i;
|
||||
ast_copy_string(tmp->context, context, sizeof(tmp->context));
|
||||
ast_copy_string(tmp->exten, "s", sizeof(tmp->exten));
|
||||
ast_string_field_set(tmp, language, "");
|
||||
i->owner = tmp;
|
||||
i->u = ast_module_user_add(tmp);
|
||||
if (state != AST_STATE_DOWN) {
|
||||
if (ast_pbx_start(tmp)) {
|
||||
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
|
||||
ast_hangup(tmp);
|
||||
}
|
||||
}
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
static struct ast_channel *usrp_request(const char *type, int format, void *data, int *cause)
|
||||
{
|
||||
int oldformat;
|
||||
struct usrp_pvt *p;
|
||||
struct ast_channel *tmp = NULL;
|
||||
|
||||
oldformat = format;
|
||||
format &= (AST_FORMAT_SLINEAR);
|
||||
if (!format) {
|
||||
ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat);
|
||||
return NULL;
|
||||
}
|
||||
p = usrp_alloc(data);
|
||||
if (p) {
|
||||
tmp = usrp_new(p, AST_STATE_DOWN);
|
||||
if (!tmp)
|
||||
usrp_destroy(p);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
/* First, take us out of the channel loop */
|
||||
ast_channel_unregister(&usrp_tech);
|
||||
ast_cli_unregister(&cli_usrp_show);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
ast_cli_register(&cli_usrp_show);
|
||||
/* Make sure we can register our channel type */
|
||||
if (ast_channel_register(&usrp_tech)) {
|
||||
ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "USRP Channel Module");
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2010, KA1RBI
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* It 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; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#define USRP_VOICE_FRAME_SIZE (160*sizeof(short)) // 0.02 * 8k
|
||||
|
||||
enum { USRP_TYPE_VOICE=0, USRP_TYPE_DTMF, USRP_TYPE_TEXT };
|
||||
|
||||
// udp data header
|
||||
struct _chan_usrp_bufhdr {
|
||||
char eye[4]; // verification string
|
||||
uint32_t seq; // sequence counter
|
||||
uint32_t memory; // memory ID or zero (default)
|
||||
uint32_t keyup; // tracks PTT state
|
||||
uint32_t talkgroup; // trunk TG id
|
||||
uint32_t type; // see above enum
|
||||
uint32_t mpxid; // for future use
|
||||
uint32_t reserved; // for future use
|
||||
};
|
|
@ -14,6 +14,8 @@
|
|||
#include "repeater_ctcss_squelch_ff.h"
|
||||
#include "repeater_gardner_costas_cc.h"
|
||||
#include "repeater_vocoder.h"
|
||||
#include "repeater_chan_usrp.h"
|
||||
#include "repeater_chan_usrp_rx.h"
|
||||
#include "rs.h"
|
||||
#include <stdexcept>
|
||||
|
||||
|
@ -90,12 +92,12 @@ public:
|
|||
|
||||
GR_SWIG_BLOCK_MAGIC(repeater,ctcss_squelch_ff);
|
||||
|
||||
repeater_ctcss_squelch_ff_sptr repeater_make_ctcss_squelch_ff (int rate, float freq, float level=5.0, int len=0, int ramp=0, bool gate=false);
|
||||
repeater_ctcss_squelch_ff_sptr repeater_make_ctcss_squelch_ff (int rate, float freq, float level, int len, int ramp, bool gate);
|
||||
|
||||
class repeater_ctcss_squelch_ff : public repeater_squelch_base_ff
|
||||
{
|
||||
private:
|
||||
repeater_ctcss_squelch_ff ();
|
||||
repeater_ctcss_squelch_ff (int rate, float freq, float level, int len, int ramp, bool gate);
|
||||
|
||||
public:
|
||||
std::vector<float> squelch_range() const;
|
||||
|
@ -128,3 +130,28 @@ class repeater_vocoder : public gr_block
|
|||
private:
|
||||
repeater_vocoder (bool encode_flag, bool verbose_flag, int stretch_amt, char* udp_host, int udp_port, bool raw_vectors_flag);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
GR_SWIG_BLOCK_MAGIC(repeater,chan_usrp);
|
||||
repeater_chan_usrp_sptr repeater_make_chan_usrp (int listen_port, bool do_imbe, bool do_complex, bool do_float, float gain, int decim, gr_msg_queue_sptr queue);
|
||||
|
||||
class repeater_chan_usrp : public gr_block
|
||||
{
|
||||
private:
|
||||
|
||||
repeater_chan_usrp (int listen_port, bool do_imbe, bool do_complex, bool do_float, float gain, int decim, gr_msg_queue_sptr queue); // private constructor
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
GR_SWIG_BLOCK_MAGIC(repeater,chan_usrp_rx);
|
||||
repeater_chan_usrp_rx_sptr repeater_make_chan_usrp_rx (const char* udp_host, int port, int debug);
|
||||
|
||||
class repeater_chan_usrp_rx : public gr_block
|
||||
{
|
||||
private:
|
||||
|
||||
repeater_chan_usrp_rx (const char* udp_host, int port, int debug); // private constructor
|
||||
|
||||
public:
|
||||
void unkey(void);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,393 @@
|
|||
/* -*- c++ -*- */
|
||||
/*
|
||||
* Copyright 2004 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GNU Radio
|
||||
*
|
||||
* GNU Radio is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
***********************************************************************
|
||||
*
|
||||
* chan_usrp - interface from asterisk app_rpt
|
||||
* "Fast" PI/4 DQPSK TX using a direct table lookup method
|
||||
*
|
||||
* Copyright 2010, KA1RBI
|
||||
*
|
||||
* This GR source block generates a complex output stream (at 320K rate)
|
||||
* for direct connection to the USRP sink block. The input is a dibit
|
||||
* stream obtained from chan_usrp via a UDP channel.
|
||||
*
|
||||
* Complex output waveforms are generated directly from the dibit stream,
|
||||
* at a rate of 66.6667 SPS (nominal input rate=4800, output rate=320000).
|
||||
* The output waveform chosen for a given input symbol depends on the value
|
||||
* of that input symbol plus the values of the prior and following symbols.
|
||||
*
|
||||
* (Other output rates than 320k are available; 320k occurs when decimation=3)
|
||||
*
|
||||
* This results in 2 ** (3*2) = 64 distinct possible waveforms. These are
|
||||
* initially encoded at a SPS value of 200 (corresponding to a 960K sampling
|
||||
* rate) and are scaled to standard values (-3/-1/+1/+3).
|
||||
*
|
||||
* This TX requires a separate version of the 64 waveforms for each of eight
|
||||
* possible phases. So, at program init time we generate the actual complex
|
||||
* waveforms by
|
||||
* - level-shifting each waveform according to the selected phase 0 to 7
|
||||
* - clipping the resulting values (in range -4 to +4)
|
||||
* - convert (rescale) to angle in radians
|
||||
* - map the angles in radians to values in complex plane
|
||||
* - multiply complex values by gain (real constant), complex samples result
|
||||
*
|
||||
* The complex output mode is used if do_complex is set to true. However
|
||||
* other output modes are possible.
|
||||
* -If do_imbe=true, outputs a dibit stream containing IMBE encoded voice
|
||||
* -Else, outputs raw audio (signed int16 format @ 8K)
|
||||
*/
|
||||
|
||||
static const int phase_table[4] = {+1, +3, -1, -3};
|
||||
|
||||
/*
|
||||
* config.h is generated by configure. It contains the results
|
||||
* of probing for features, options etc. It should be the first
|
||||
* file included in your .cc file.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#define INCLUDE_TX_WAVEFORMS
|
||||
#include <repeater_chan_usrp.h>
|
||||
#include <gr_io_signature.h>
|
||||
#include <gr_prefs.h>
|
||||
#include <gr_math.h>
|
||||
#include <gr_expj.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <repeater_chan_usrp.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include <op25_p25_frame.h>
|
||||
#include <op25_imbe_frame.h>
|
||||
|
||||
#include "chan_usrp.h"
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a<b)?a:b)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Create a new instance of repeater_chan_usrp and return
|
||||
* a boost shared_ptr. This is effectively the public constructor.
|
||||
*/
|
||||
repeater_chan_usrp_sptr
|
||||
repeater_make_chan_usrp (int listen_port, bool do_imbe, bool do_complex, bool do_float, float gain, int decim, gr_msg_queue_sptr queue)
|
||||
{
|
||||
return repeater_chan_usrp_sptr (new repeater_chan_usrp (listen_port, do_imbe, do_complex, do_float, gain, decim, queue));
|
||||
}
|
||||
|
||||
/*
|
||||
* The private constructor
|
||||
*/
|
||||
repeater_chan_usrp::repeater_chan_usrp (int listen_port, bool do_imbe, bool do_complex, bool do_float, float gain, int decim, gr_msg_queue_sptr queue)
|
||||
: gr_block ("chan_usrp",
|
||||
gr_make_io_signature (0, 0, 0),
|
||||
gr_make_io_signature (1, 1, (do_float) ? sizeof(float) : ((do_complex) ? sizeof(gr_complex) : ((do_imbe) ? sizeof(char) : sizeof(int16_t))))),
|
||||
d_do_imbe(do_imbe),
|
||||
d_expected_seq(0),
|
||||
read_sock(0),
|
||||
warned_select(false),
|
||||
codeword_ct(0),
|
||||
frame_cnt(0),
|
||||
d_keyup_state(false),
|
||||
d_timeout_time(0),
|
||||
d_timeout_value(4), // in sec.
|
||||
f_body(P25_VOICE_FRAME_SIZE),
|
||||
d_msgq(queue),
|
||||
d_gain(gain),
|
||||
d_decim(decim),
|
||||
d_phase(0),
|
||||
d_next_samp(0),
|
||||
d_current_sym(0),
|
||||
d_active_dibit(0),
|
||||
d_shift_reg(0),
|
||||
d_muted(0),
|
||||
d_do_complex(do_complex),
|
||||
d_do_float(do_float)
|
||||
{
|
||||
int i, j, k;
|
||||
float t;
|
||||
float *fp;
|
||||
if (d_do_complex) {
|
||||
for (i = 0; i < N_PHASES; i++) {
|
||||
for (j = 0; j < N_WAVEFORMS; j++) {
|
||||
for (k = 0; k < N_SPS+1; k++) {
|
||||
#ifdef USE_ALT_RC_FILTER
|
||||
t = rc_syms[j][k] - N_PHASES_2 + (float)i;
|
||||
#else
|
||||
t = rrc_syms[j][k] - N_PHASES_2 + (float)i;
|
||||
#endif
|
||||
if (t < -N_PHASES_2) t += N_PHASES;
|
||||
if (t > N_PHASES_2) t -= N_PHASES;
|
||||
t *= (M_PI / 4.0);
|
||||
fp = (float*)&waveforms[i][j][k];
|
||||
fp[0] = d_gain * cos(t);
|
||||
fp[1] = d_gain * sin(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (listen_port > 0)
|
||||
init_sock(listen_port);
|
||||
}
|
||||
|
||||
repeater_chan_usrp::~repeater_chan_usrp ()
|
||||
{
|
||||
if (read_sock > 0)
|
||||
close(read_sock);
|
||||
}
|
||||
|
||||
void repeater_chan_usrp::append_imbe_codeword(bit_vector& frame_body, int16_t frame_vector[], unsigned int& codeword_ct)
|
||||
{
|
||||
voice_codeword cw(voice_codeword_sz);
|
||||
// construct 144-bit codeword from 88 bits of parameters
|
||||
imbe_header_encode(cw, frame_vector[0], frame_vector[1], frame_vector[2], frame_vector[3], frame_vector[4], frame_vector[5], frame_vector[6], frame_vector[7]);
|
||||
|
||||
// add codeword to voice data unit
|
||||
imbe_interleave(frame_body, cw, codeword_ct);
|
||||
|
||||
// after the ninth and final codeword added, dispose of frame
|
||||
if (++codeword_ct >= nof_voice_codewords) {
|
||||
static const uint64_t hws[2] = { 0x293555ef2c653437LL, 0x293aba93bec26a2bLL };
|
||||
p25_setup_frame_header(frame_body, hws[frame_cnt & 1]);
|
||||
for (size_t i = 0; i < sizeof(imbe_ldu_ls_data_bits) / sizeof(imbe_ldu_ls_data_bits[0]); i++) {
|
||||
frame_body[imbe_ldu_ls_data_bits[i]] = 0;
|
||||
}
|
||||
// finally, output the frame
|
||||
for (uint32_t i = 0; i < P25_VOICE_FRAME_SIZE; i += 2) {
|
||||
uint8_t dibit =
|
||||
(frame_body[i+0] << 1) +
|
||||
(frame_body[i+1] );
|
||||
output_queue.push_back(dibit);
|
||||
}
|
||||
codeword_ct = 0;
|
||||
frame_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
repeater_chan_usrp::general_work (int noutput_items,
|
||||
gr_vector_int &ninput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items)
|
||||
{
|
||||
int rc;
|
||||
struct sockaddr saddr;
|
||||
char rcvbuf[1024];
|
||||
socklen_t addrlen;
|
||||
int16_t *bufdata;
|
||||
struct _chan_usrp_bufhdr *bufhdrp;
|
||||
int16_t frame_vector[8];
|
||||
unsigned int rseq;
|
||||
|
||||
for (;;) { // read all pending msgs
|
||||
addrlen = sizeof(saddr);
|
||||
rc = recvfrom(read_sock, rcvbuf, sizeof(rcvbuf), MSG_DONTWAIT,
|
||||
&saddr, &addrlen);
|
||||
if (rc < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
||||
break;
|
||||
if (rc < 0 && !warned_select) {
|
||||
fprintf(stderr, "repeater_chan_usrp: recvfrom: %d\n", errno);
|
||||
warned_select = 1;
|
||||
}
|
||||
if (rc < 0)
|
||||
break;
|
||||
if (rc != sizeof(struct _chan_usrp_bufhdr) &&
|
||||
rc != (sizeof(struct _chan_usrp_bufhdr) + USRP_VOICE_FRAME_SIZE)) {
|
||||
fprintf(stderr, "repeater_chan_usrp: error: received size %d invalid\n", rc) ;
|
||||
continue;
|
||||
}
|
||||
bufhdrp = (struct _chan_usrp_bufhdr*)rcvbuf;
|
||||
bufdata = (int16_t*) &rcvbuf[ sizeof(struct _chan_usrp_bufhdr) ];
|
||||
if (ntohl(bufhdrp->keyup) != d_keyup_state) {
|
||||
// tx keyup state change has occurred
|
||||
#if 0
|
||||
fprintf(stderr, "repeater_chan_usrp: keyup state %d was %d\n", ntohl(bufhdrp->keyup), d_keyup_state);
|
||||
#endif
|
||||
d_keyup_state = ntohl(bufhdrp->keyup);
|
||||
// TODO queue hdu / tdu(s)
|
||||
#if 0
|
||||
if (d_msgq) {
|
||||
gr_message_sptr msg = gr_make_message(0,d_keyup_state,0,0);
|
||||
d_msgq->handle(msg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
rseq = ntohl(bufhdrp->seq);
|
||||
if (rseq != d_expected_seq && rseq != 0 && d_expected_seq != 0) {
|
||||
fprintf(stderr, "repeater_chan_usrp: possible data loss, expected seq %d received %d\n", d_expected_seq, rseq);
|
||||
}
|
||||
d_expected_seq = rseq + 1;
|
||||
if (bufhdrp->keyup) {
|
||||
d_timeout_time = time(NULL) + d_timeout_value;
|
||||
}
|
||||
if ((unsigned int)rc < sizeof(struct _chan_usrp_bufhdr) + USRP_VOICE_FRAME_SIZE)
|
||||
continue;
|
||||
if (d_do_imbe || d_do_complex || d_do_float) {
|
||||
vocoder.imbe_encode(frame_vector, bufdata);
|
||||
append_imbe_codeword(f_body, frame_vector, codeword_ct);
|
||||
} else {
|
||||
for (unsigned int i=0; i < (USRP_VOICE_FRAME_SIZE>>1); i++) {
|
||||
output_queue_s.push_back(bufdata[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (d_do_complex) {
|
||||
gr_complex *out = (gr_complex*) output_items[0];
|
||||
int o=0;
|
||||
uint8_t new_dibit;
|
||||
|
||||
while((o < noutput_items)) {
|
||||
if (d_muted) {
|
||||
if (output_queue.size())
|
||||
output_queue.clear();
|
||||
out[o++] = gr_complex(0, 0);
|
||||
continue;
|
||||
}
|
||||
if (!d_current_sym) {
|
||||
if (!output_queue.size()) {
|
||||
out[o++] = gr_complex(0, 0);
|
||||
continue;
|
||||
}
|
||||
new_dibit = output_queue.front();
|
||||
output_queue.pop_front();
|
||||
// assert(new_dibit <= 3);
|
||||
// shift reg holds three sequential dibits
|
||||
d_shift_reg = ((d_shift_reg << 2) + new_dibit) & (N_WAVEFORMS-1);
|
||||
// get pointer to next symbol waveform to be transmitted
|
||||
d_current_sym = waveforms[d_phase][d_shift_reg];
|
||||
// update phase based on current active dibit
|
||||
d_phase += phase_table[d_active_dibit];
|
||||
// clamp phase
|
||||
if (d_phase < 0) d_phase += N_PHASES;
|
||||
if (d_phase > (N_PHASES-1)) d_phase -= N_PHASES;
|
||||
// save for next pass
|
||||
d_active_dibit = new_dibit;
|
||||
}
|
||||
out[o++] = d_current_sym[d_next_samp];
|
||||
d_next_samp += d_decim;
|
||||
if (d_next_samp >= N_SPS) {
|
||||
d_next_samp -= N_SPS;
|
||||
d_current_sym = NULL;
|
||||
}
|
||||
}
|
||||
return noutput_items;
|
||||
} else if (d_do_float) {
|
||||
float *out = (float*) output_items[0];
|
||||
int o=0;
|
||||
uint8_t new_dibit;
|
||||
|
||||
while((o < noutput_items)) {
|
||||
if (d_muted) {
|
||||
if (output_queue.size())
|
||||
output_queue.clear();
|
||||
out[o++] = 0.0;
|
||||
continue;
|
||||
}
|
||||
if (!d_current_fsym) {
|
||||
if (!output_queue.size()) {
|
||||
out[o++] = 0.0;
|
||||
continue;
|
||||
}
|
||||
new_dibit = output_queue.front();
|
||||
output_queue.pop_front();
|
||||
// assert(new_dibit <= 3);
|
||||
// shift reg holds 8 sequential dibits
|
||||
d_shift_reg = ((d_shift_reg << 2) + new_dibit) & (N_FLOAT_WAVEFORMS-1);
|
||||
// get pointer to next symbol waveform to be transmitted
|
||||
d_current_fsym = float_waveforms[d_shift_reg];
|
||||
}
|
||||
out[o++] = d_current_fsym[d_next_samp];
|
||||
d_next_samp += d_decim;
|
||||
if (d_next_samp >= N_FLOAT_SPS) {
|
||||
d_next_samp -= N_FLOAT_SPS;
|
||||
d_current_fsym = NULL;
|
||||
}
|
||||
}
|
||||
return noutput_items;
|
||||
} else if (d_do_imbe) {
|
||||
char *out = (char*) output_items[0];
|
||||
// if no output available at all, output zeros
|
||||
if (output_queue.empty()) {
|
||||
for (int i = 0; i < noutput_items; i++) {
|
||||
out[i] = 0;
|
||||
// out[i] = i & 3;
|
||||
}
|
||||
return noutput_items;
|
||||
}
|
||||
int amt_move = min(output_queue.size(), (unsigned int)noutput_items);
|
||||
for (int i = 0; i < amt_move; i++) {
|
||||
out[i] = output_queue[i];
|
||||
// out[i] = i & 3;
|
||||
}
|
||||
output_queue.erase(output_queue.begin(), output_queue.begin() + amt_move);
|
||||
return amt_move;
|
||||
} else {
|
||||
int16_t *out = (int16_t*) output_items[0];
|
||||
// if no output available at all, output zeros
|
||||
if (output_queue_s.empty()) {
|
||||
for (int i = 0; i < noutput_items; i++) {
|
||||
out[i] = 0;
|
||||
}
|
||||
return noutput_items;
|
||||
}
|
||||
int amt_move = min(output_queue_s.size(), (unsigned int)noutput_items);
|
||||
for (int i = 0; i < amt_move; i++) {
|
||||
out[i] = output_queue_s[i];
|
||||
}
|
||||
output_queue_s.erase(output_queue_s.begin(), output_queue_s.begin() + amt_move);
|
||||
return amt_move;
|
||||
}
|
||||
}
|
||||
|
||||
void repeater_chan_usrp::init_sock(int udp_port)
|
||||
{
|
||||
memset (&read_sock_addr, 0, sizeof(read_sock_addr));
|
||||
read_sock = socket(PF_INET, SOCK_DGRAM, 17); // UDP socket
|
||||
if (read_sock < 0) {
|
||||
fprintf(stderr, "op25_chan_usrp: socket: %d\n", errno);
|
||||
read_sock = 0;
|
||||
return;
|
||||
}
|
||||
read_sock_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
read_sock_addr.sin_family = AF_INET;
|
||||
read_sock_addr.sin_port = htons(udp_port);
|
||||
int rc = bind(read_sock, (struct sockaddr*)&read_sock_addr, sizeof(read_sock_addr));
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "op25_chan_usrp: bind: %d\n", errno);
|
||||
close(read_sock);
|
||||
read_sock = 0;
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/* -*- c++ -*- */
|
||||
/*
|
||||
* Copyright 2004 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GNU Radio
|
||||
*
|
||||
* GNU Radio is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef INCLUDED_REPEATER_CHAN_USRP_H
|
||||
#define INCLUDED_REPEATER_CHAN_USRP_H
|
||||
|
||||
#include <gr_sync_block.h>
|
||||
#include <gr_msg_queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <deque>
|
||||
|
||||
class repeater_chan_usrp;
|
||||
|
||||
typedef std::vector<bool> bit_vector;
|
||||
|
||||
#include <imbe_vocoder.h>
|
||||
#include "waveforms.h"
|
||||
|
||||
static const int N_PHASES = 8; // no. of points in PI/4 constellation
|
||||
static const int N_PHASES_2 = N_PHASES >> 1;
|
||||
|
||||
/*
|
||||
* We use boost::shared_ptr's instead of raw pointers for all access
|
||||
* to gr_blocks (and many other data structures). The shared_ptr gets
|
||||
* us transparent reference counting, which greatly simplifies storage
|
||||
* management issues. This is especially helpful in our hybrid
|
||||
* C++ / Python system.
|
||||
*
|
||||
* See http://www.boost.org/libs/smart_ptr/smart_ptr.htm
|
||||
*
|
||||
* As a convention, the _sptr suffix indicates a boost::shared_ptr
|
||||
*/
|
||||
typedef boost::shared_ptr<repeater_chan_usrp> repeater_chan_usrp_sptr;
|
||||
typedef std::deque<uint8_t> dibit_queue;
|
||||
|
||||
/*!
|
||||
* \brief Return a shared_ptr to a new instance of repeater_chan_usrp.
|
||||
*
|
||||
* To avoid accidental use of raw pointers, repeater_chan_usrp's
|
||||
* constructor is private. repeater_make_chan_usrp is the public
|
||||
* interface for creating new instances.
|
||||
*/
|
||||
repeater_chan_usrp_sptr repeater_make_chan_usrp (int listen_port, bool do_imbe, bool do_complex, bool do_float, float gain, int decim, gr_msg_queue_sptr queue);
|
||||
|
||||
class repeater_chan_usrp : public gr_block
|
||||
{
|
||||
private:
|
||||
// The friend declaration allows repeater_make_chan_usrp to
|
||||
// access the private constructor.
|
||||
|
||||
void init_sock(int udp_port);
|
||||
|
||||
friend repeater_chan_usrp_sptr repeater_make_chan_usrp (int listen_port, bool do_imbe, bool do_complex, bool do_float, float gain, int decim, gr_msg_queue_sptr queue);
|
||||
|
||||
repeater_chan_usrp (int listen_port, bool do_imbe, bool do_complex, bool do_float, float gain, int decim, gr_msg_queue_sptr queue); // private constructor
|
||||
// internal functions
|
||||
void check_read(void);
|
||||
void append_imbe_codeword(bit_vector& frame_body, int16_t frame_vector[], unsigned int& codeword_ct);
|
||||
|
||||
// internal data
|
||||
bool d_do_imbe;
|
||||
unsigned int d_expected_seq;
|
||||
int read_sock;
|
||||
struct sockaddr_in read_sock_addr;
|
||||
bool warned_select;
|
||||
|
||||
unsigned int codeword_ct;
|
||||
|
||||
unsigned int frame_cnt;
|
||||
bool d_keyup_state;
|
||||
time_t d_timeout_time;
|
||||
unsigned int d_timeout_value;
|
||||
bit_vector f_body;
|
||||
imbe_vocoder vocoder;
|
||||
|
||||
std::deque<uint8_t> output_queue;
|
||||
std::deque<int16_t> output_queue_s;
|
||||
|
||||
gr_msg_queue_sptr d_msgq;
|
||||
|
||||
float d_gain;
|
||||
int d_decim;
|
||||
int d_phase;
|
||||
int d_next_samp;
|
||||
gr_complex *d_current_sym;
|
||||
const float *d_current_fsym;
|
||||
int d_active_dibit;
|
||||
int d_shift_reg;
|
||||
bool d_muted;
|
||||
bool d_do_complex;
|
||||
bool d_do_float;
|
||||
|
||||
gr_complex waveforms[N_PHASES][N_WAVEFORMS][N_SPS+1];
|
||||
public:
|
||||
~repeater_chan_usrp (); // public destructor
|
||||
|
||||
// Where all the action really happens
|
||||
|
||||
int general_work (int noutput_items,
|
||||
gr_vector_int &ninput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items);
|
||||
|
||||
};
|
||||
|
||||
#endif /* INCLUDED_REPEATER_CHAN_USRP_H */
|
|
@ -0,0 +1,152 @@
|
|||
/* -*- c++ -*- */
|
||||
/*
|
||||
* Copyright 2004 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GNU Radio
|
||||
*
|
||||
* GNU Radio is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
static const int phase_table[4] = {+1, +3, -1, -3};
|
||||
|
||||
/*
|
||||
* config.h is generated by configure. It contains the results
|
||||
* of probing for features, options etc. It should be the first
|
||||
* file included in your .cc file.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gr_io_signature.h>
|
||||
#include <gr_prefs.h>
|
||||
#include <gr_math.h>
|
||||
#include <gr_expj.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <repeater_chan_usrp_rx.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a<b)?a:b)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Create a new instance of repeater_chan_usrp_rx and return
|
||||
* a boost shared_ptr. This is effectively the public constructor.
|
||||
*/
|
||||
repeater_chan_usrp_rx_sptr
|
||||
repeater_make_chan_usrp_rx (const char* udp_host, int port, int debug)
|
||||
{
|
||||
return repeater_chan_usrp_rx_sptr (new repeater_chan_usrp_rx(udp_host, port, debug));
|
||||
}
|
||||
|
||||
/*
|
||||
* The private constructor
|
||||
*/
|
||||
repeater_chan_usrp_rx::repeater_chan_usrp_rx (const char* udp_host, int port, int debug)
|
||||
: gr_block ("chan_usrp_rx",
|
||||
gr_make_io_signature (1, 1, sizeof(short)),
|
||||
gr_make_io_signature (0, 0, 0)),
|
||||
d_udp_host(udp_host),
|
||||
d_port(port),
|
||||
d_debug(debug),
|
||||
d_sampbuf_ct(0),
|
||||
d_sendseq(0)
|
||||
{
|
||||
init_sock(udp_host, port);
|
||||
}
|
||||
|
||||
repeater_chan_usrp_rx::~repeater_chan_usrp_rx ()
|
||||
{
|
||||
if (write_sock > 0)
|
||||
close(write_sock);
|
||||
}
|
||||
|
||||
static int sends, samps;
|
||||
|
||||
int
|
||||
repeater_chan_usrp_rx::general_work (int noutput_items,
|
||||
gr_vector_int &ninput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items)
|
||||
{
|
||||
struct timeval tv;
|
||||
const int16_t *in = (const int16_t *) input_items[0];
|
||||
int16_t *sampbuf = (int16_t *) &write_buf[sizeof(struct _chan_usrp_bufhdr)];
|
||||
struct _chan_usrp_bufhdr *bufhdrp = (struct _chan_usrp_bufhdr*) write_buf;
|
||||
gettimeofday(&tv, NULL);
|
||||
// fprintf(stderr, "sec %lu usec %lu output %d\n", tv.tv_sec, tv.tv_usec, noutput_items);
|
||||
|
||||
int consumed = 0;
|
||||
|
||||
for (int i = 0; i < noutput_items; i++) {
|
||||
int16_t samp = in[i];
|
||||
sampbuf[d_sampbuf_ct++] = samp;
|
||||
consumed++;
|
||||
if (d_sampbuf_ct >= (USRP_VOICE_FRAME_SIZE>>1)) {
|
||||
d_sampbuf_ct = 0;
|
||||
memset(bufhdrp, 0, sizeof(struct _chan_usrp_bufhdr));
|
||||
memcpy(bufhdrp->eye, "USRP", 4);
|
||||
bufhdrp->seq = htonl(d_sendseq++);
|
||||
bufhdrp->keyup = htonl(1);
|
||||
int rc = sendto(write_sock, write_buf, sizeof(struct _chan_usrp_bufhdr) + USRP_VOICE_FRAME_SIZE, 0, (struct sockaddr *)&write_sock_addr, sizeof(write_sock_addr));
|
||||
// gettimeofday(&tv, NULL);
|
||||
// fprintf(stderr, "rc %d sends %d samps %u sec %lu usec %lu\n", rc, sends++, samps, tv.tv_sec, tv.tv_usec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
consume_each(consumed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void repeater_chan_usrp_rx::unkey(void)
|
||||
{
|
||||
struct _chan_usrp_bufhdr bufhdr;
|
||||
memset(&bufhdr, 0, sizeof(struct _chan_usrp_bufhdr));
|
||||
memcpy(bufhdr.eye, "USRP", 4);
|
||||
bufhdr.seq = htonl(d_sendseq++); // FIXME: need to lock this?
|
||||
sendto(write_sock, &bufhdr, sizeof(struct _chan_usrp_bufhdr), 0, (struct sockaddr *)&write_sock_addr, sizeof(write_sock_addr));
|
||||
}
|
||||
|
||||
void repeater_chan_usrp_rx::init_sock(const char* udp_host, int udp_port)
|
||||
{
|
||||
memset (&write_sock_addr, 0, sizeof(write_sock_addr));
|
||||
write_sock = socket(PF_INET, SOCK_DGRAM, 17); // UDP socket
|
||||
if (write_sock < 0) {
|
||||
fprintf(stderr, "op25_imbe_vocoder: socket: %d\n", errno);
|
||||
write_sock = 0;
|
||||
return;
|
||||
}
|
||||
if (!inet_aton(udp_host, &write_sock_addr.sin_addr)) {
|
||||
fprintf(stderr, "op25_imbe_vocoder: inet_aton: bad IP address\n");
|
||||
close(write_sock);
|
||||
write_sock = 0;
|
||||
return;
|
||||
}
|
||||
write_sock_addr.sin_family = AF_INET;
|
||||
write_sock_addr.sin_port = htons(udp_port);
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/* -*- c++ -*- */
|
||||
/*
|
||||
* Copyright 2004 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GNU Radio
|
||||
*
|
||||
* GNU Radio is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef INCLUDED_REPEATER_CHAN_USRP_RX_H
|
||||
#define INCLUDED_REPEATER_CHAN_USRP_RX_H
|
||||
|
||||
#include <gr_sync_block.h>
|
||||
#include <gr_msg_queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <deque>
|
||||
|
||||
#include "chan_usrp.h"
|
||||
|
||||
class repeater_chan_usrp_rx;
|
||||
|
||||
typedef std::vector<bool> bit_vector;
|
||||
|
||||
|
||||
/*
|
||||
* We use boost::shared_ptr's instead of raw pointers for all access
|
||||
* to gr_blocks (and many other data structures). The shared_ptr gets
|
||||
* us transparent reference counting, which greatly simplifies storage
|
||||
* management issues. This is especially helpful in our hybrid
|
||||
* C++ / Python system.
|
||||
*
|
||||
* See http://www.boost.org/libs/smart_ptr/smart_ptr.htm
|
||||
*
|
||||
* As a convention, the _sptr suffix indicates a boost::shared_ptr
|
||||
*/
|
||||
typedef boost::shared_ptr<repeater_chan_usrp_rx> repeater_chan_usrp_rx_sptr;
|
||||
typedef std::deque<uint8_t> dibit_queue;
|
||||
|
||||
/*!
|
||||
* \brief Return a shared_ptr to a new instance of repeater_chan_usrp_rx.
|
||||
*
|
||||
* To avoid accidental use of raw pointers, repeater_chan_usrp_rx's
|
||||
* constructor is private. repeater_make_chan_usrp_rx is the public
|
||||
* interface for creating new instances.
|
||||
*/
|
||||
repeater_chan_usrp_rx_sptr repeater_make_chan_usrp_rx (const char* udp_host, int port, int debug);
|
||||
|
||||
class repeater_chan_usrp_rx : public gr_block
|
||||
{
|
||||
private:
|
||||
// The friend declaration allows repeater_make_chan_usrp_rx to
|
||||
// access the private constructor.
|
||||
|
||||
void init_sock(const char* udp_host, int udp_port);
|
||||
|
||||
friend repeater_chan_usrp_rx_sptr repeater_make_chan_usrp_rx (const char* udp_host, int port, int debug);
|
||||
|
||||
repeater_chan_usrp_rx (const char* udp_host, int port, int debug);
|
||||
// internal functions
|
||||
|
||||
// internal data
|
||||
int write_sock;
|
||||
struct sockaddr_in write_sock_addr;
|
||||
|
||||
const char* d_udp_host;
|
||||
const int d_port;
|
||||
const int d_debug;
|
||||
|
||||
unsigned int d_sampbuf_ct;
|
||||
unsigned int d_sendseq;
|
||||
|
||||
char write_buf[ sizeof(struct _chan_usrp_bufhdr) + USRP_VOICE_FRAME_SIZE*sizeof(int16_t) ];
|
||||
|
||||
public:
|
||||
~repeater_chan_usrp_rx (); // public destructor
|
||||
|
||||
void unkey(void);
|
||||
|
||||
// Where all the action really happens
|
||||
|
||||
int general_work (int noutput_items,
|
||||
gr_vector_int &ninput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items);
|
||||
|
||||
};
|
||||
|
||||
#endif /* INCLUDED_REPEATER_CHAN_USRP_RX_H */
|
|
@ -30,7 +30,7 @@ class repeater_ctcss_squelch_ff;
|
|||
typedef boost::shared_ptr<repeater_ctcss_squelch_ff> repeater_ctcss_squelch_ff_sptr;
|
||||
|
||||
repeater_ctcss_squelch_ff_sptr
|
||||
repeater_make_ctcss_squelch_ff(int rate, float freq, float level=0.01, int len=0, int ramp=0, bool gate=false);
|
||||
repeater_make_ctcss_squelch_ff(int rate, float freq, float level, int len, int ramp, bool gate);
|
||||
|
||||
/*!
|
||||
* \brief gate or zero output if ctcss tone not present
|
||||
|
|
|
@ -209,13 +209,14 @@ repeater_gardner_costas_cc::general_work (int noutput_items,
|
|||
float error_imag = (d_last_sample.imag() - interp_samp.imag()) * interp_samp_mid.imag();
|
||||
d_last_sample = interp_samp; // save for next time
|
||||
float symbol_error = error_real + error_imag; // Gardner loop error
|
||||
if (isnan(symbol_error)) symbol_error = 0.0;
|
||||
if (symbol_error < -1.0) symbol_error = -1.0;
|
||||
if (symbol_error > 1.0) symbol_error = 1.0;
|
||||
|
||||
d_omega = d_omega + d_gain_omega * symbol_error; // update omega based on loop error
|
||||
d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_rel); // make sure we don't walk away
|
||||
#if VERBOSE_GARDNER
|
||||
printf("%f\t%f\t%f\n", symbol_error, d_mu, d_omega);
|
||||
printf("%f\t%f\t%f\t%f\t%f\n", symbol_error, d_mu, d_omega, error_real, error_imag);
|
||||
#endif
|
||||
d_mu += d_omega + d_gain_mu * symbol_error; // update mu based on loop error
|
||||
|
||||
|
|
|
@ -111,15 +111,17 @@ repeater_p25_frame_assembler::general_work (int noutput_items,
|
|||
{
|
||||
|
||||
const uint8_t *in = (const uint8_t *) input_items[0];
|
||||
unsigned char *out = (unsigned char *) output_items[0];
|
||||
|
||||
for (int i = 0; i < noutput_items; i++){
|
||||
if(framer->rx_sym(in[i])) { // complete frame was detected
|
||||
if (d_debug > 0) {
|
||||
if (d_debug > 0 && framer->duid == 0x00) {
|
||||
ProcHDU(framer->frame_body);
|
||||
}
|
||||
if (d_debug > 10) {
|
||||
fprintf (stderr, "NAC 0x%X DUID 0x%X symbols %d BCH errors %d\n", framer->nac, framer->duid, framer->frame_size >> 1, framer->bch_errors);
|
||||
switch(framer->duid) {
|
||||
case 0x00: // Header DU
|
||||
ProcHDU(framer->frame_body);
|
||||
// see above ProcHDU(framer->frame_body);
|
||||
break;
|
||||
case 0x05: // LDU 1
|
||||
ProcLDU1(framer->frame_body);
|
||||
|
@ -192,6 +194,7 @@ repeater_p25_frame_assembler::general_work (int noutput_items,
|
|||
if (amt_produce > (int)symbol_queue.size())
|
||||
amt_produce = symbol_queue.size();
|
||||
if (amt_produce > 0) {
|
||||
unsigned char *out = (unsigned char *) output_items[0];
|
||||
copy(symbol_queue.begin(), symbol_queue.begin() + amt_produce, out);
|
||||
symbol_queue.erase(symbol_queue.begin(), symbol_queue.begin() + amt_produce);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue