add chan_usrp interface

git-svn-id: http://op25.osmocom.org/svn/trunk@231 65a5c917-d112-43f1-993d-58c26a4786be
This commit is contained in:
max 2010-12-25 02:40:51 +00:00
parent 2ddec336ba
commit ed927312bc
13 changed files with 398187 additions and 17 deletions

View File

@ -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}

View File

@ -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

View File

@ -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");

View File

@ -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
};

View File

@ -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);
};

View File

@ -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;
}
}

View File

@ -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 */

View File

@ -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);
}

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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);
}

396688
repeater/src/lib/waveforms.h Normal file

File diff suppressed because it is too large Load Diff