Implement transceiver interface

This change introduces a new block 'TRX Interface', which is aimed
to provide an interface for external applications, such as Osmocom
MS side stack implementation - OsmocomBB. Currently one allows to
exchange raw GSM bursts between GR-GSM and other applications.

Moreover, there is a new 'trx.py' application, which implements a
simple follow graph, where all demodulated bursts are being sent
to external application via UDP link provided by 'TRX Interface'.
OsmoTRX (Osmocom's fork of OpenBTS transceiver) like control
interface is used to initialize, configure, start and stop the
application. Messages on this interface are human readable ASCII
strings, which contain a command and some related parameters.
This commit is contained in:
Vadim Yanitskiy 2017-06-16 21:00:29 +07:00
parent 8f121c1e7c
commit 89fc14ba44
15 changed files with 1062 additions and 0 deletions

157
apps/trx/ctrl_if.py Normal file
View File

@ -0,0 +1,157 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# GR-GSM based transceiver
# Transceiver UDP interface
#
# (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
#
# All Rights Reserved
#
# This program 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 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import socket
import select
class UDPServer:
def __init__(self, remote_addr, remote_port, bind_port):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.bind(('0.0.0.0', bind_port))
self.sock.setblocking(0)
# Save remote info
self.remote_addr = remote_addr
self.remote_port = remote_port
def loop(self):
r_event, w_event, x_event = select.select([self.sock], [], [])
# Check for incoming data
if self.sock in r_event:
data, addr = self.sock.recvfrom(128)
self.handle_rx(data)
def shutdown(self):
self.sock.close();
def send(self, data):
self.sock.sendto(data, (self.remote_addr, self.remote_port))
def handle_rx(self, data):
raise NotImplementedError
class CTRLInterface(UDPServer):
def __init__(self, remote_addr, remote_port, bind_port, radio_if):
print("[i] Init TRX CTRL interface")
UDPServer.__init__(self, remote_addr, remote_port, bind_port)
self.tb = radio_if
def shutdown(self):
print("[i] Shutdown TRX CTRL interface")
UDPServer.shutdown(self)
def handle_rx(self, data):
if self.verify_req(data):
request = self.prepare_req(data)
self.parse_cmd(request)
else:
print("[!] Wrong data on CTRL interface")
def verify_req(self, data):
# Verify command signature
return data.startswith("CMD")
def prepare_req(self, data):
# Strip signature, paddings and \0
request = data[4:].strip().strip("\0")
# Split into a command and arguments
request = request.split(" ")
# Now we have something like ["TXTUNE", "941600"]
return request
def verify_cmd(self, request, cmd, argc):
# Check if requested command matches
if request[0] != cmd:
return False
# And has enough arguments
if len(request) - 1 != argc:
return False
# Check if all arguments are numeric
for v in request[1:]:
if not v.isdigit():
return False
return True
def parse_cmd(self, request):
response_code = "0"
# Power control
if self.verify_cmd(request, "POWERON", 0):
print("[i] Recv POWERON cmd")
if not self.tb.trx_started:
if self.tb.check_available():
print("[i] Starting transceiver...")
self.tb.trx_started = True
self.tb.start()
else:
print("[!] Transceiver isn't ready to start")
response_code = "-1"
else:
print("[!] Transceiver already started!")
response_code = "-1"
elif self.verify_cmd(request, "POWEROFF", 0):
print("[i] Recv POWEROFF cmd")
print("[i] Stopping transceiver...")
self.tb.trx_started = False
# TODO: flush all buffers between blocks
self.tb.stop()
elif self.verify_cmd(request, "SETRXGAIN", 1):
print("[i] Recv SETRXGAIN cmd")
# TODO: check gain value
gain = int(request[1])
self.tb.set_gain(gain)
# Tuning Control
elif self.verify_cmd(request, "RXTUNE", 1):
print("[i] Recv RXTUNE cmd")
# TODO: check freq range
freq = int(request[1]) * 1000
self.tb.set_fc(freq)
elif self.verify_cmd(request, "TXTUNE", 1):
print("[i] Recv TXTUNE cmd")
# TODO: is not implemented yet
# Misc
elif self.verify_cmd(request, "ECHO", 0):
print("[i] Recv ECHO cmd")
# Wrong / unknown command
else:
print("[!] Wrong request on CTRL interface")
response_code = "-1"
# Anyway, we need to respond
self.send_response(request, response_code)
def send_response(self, request, response_code):
# Include status code, for example ["TXTUNE", "0", "941600"]
request.insert(1, response_code)
# Add the response signature, and join back to string
response = "RSP " + " ".join(request) + "\0"
# Now we have something like "RSP TXTUNE 0 941600"
self.send(response)

184
apps/trx/radio_if.py Normal file
View File

@ -0,0 +1,184 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# GR-GSM based transceiver
# Follow graph implementation
#
# (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
#
# All Rights Reserved
#
# This program 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 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import pmt
import time
import grgsm
import osmosdr
from math import pi
from gnuradio.eng_option import eng_option
from gnuradio import eng_notation
from gnuradio.filter import firdes
from gnuradio import blocks
from gnuradio import gr
class RadioInterface(gr.top_block):
# PHY specific variables
samp_rate = 2000000
shiftoff = 400e3
subdev_spec = "" # TODO: use it
device_args = ""
fc = 941.6e6 # TODO: set ARFCN to 0?
gain = 30
ppm = 0
# Application state flags
trx_started = False
fc_set = False
def __init__(self, phy_args, phy_subdev_spec,
phy_sample_rate, phy_gain, phy_ppm,
trx_remote_addr, trx_base_port):
print("[i] Init Radio interface")
# TRX block specific variables
self.trx_remote_addr = trx_remote_addr
self.trx_base_port = trx_base_port
# PHY specific variables
self.subdev_spec = phy_subdev_spec
self.samp_rate = phy_sample_rate
self.device_args = phy_args
self.gain = phy_gain
self.ppm = phy_ppm
gr.top_block.__init__(self, "GR-GSM TRX")
shift_fc = self.fc - self.shiftoff
##################################################
# PHY Definition
##################################################
self.phy = osmosdr.source(
args = "numchan=%d %s" % (1, self.device_args))
self.phy.set_bandwidth(250e3 + abs(self.shiftoff), 0)
self.phy.set_center_freq(shift_fc, 0)
self.phy.set_sample_rate(self.samp_rate)
self.phy.set_freq_corr(self.ppm, 0)
self.phy.set_iq_balance_mode(2, 0)
self.phy.set_dc_offset_mode(2, 0)
self.phy.set_gain_mode(False, 0)
self.phy.set_gain(self.gain, 0)
self.phy.set_if_gain(20, 0)
self.phy.set_bb_gain(20, 0)
self.phy.set_antenna("", 0)
##################################################
# GR-GSM Magic
##################################################
self.blocks_rotator = blocks.rotator_cc(
-2 * pi * self.shiftoff / self.samp_rate)
self.gsm_input = grgsm.gsm_input(
ppm = self.ppm, osr = 4, fc = self.fc,
samp_rate_in = self.samp_rate)
self.gsm_receiver = grgsm.receiver(4, ([0]), ([]))
self.gsm_clck_ctrl = grgsm.clock_offset_control(
shift_fc, self.samp_rate, osr = 4)
# TODO: implement configurable TS filter
self.gsm_ts_filter = grgsm.burst_timeslot_filter(0)
self.gsm_trx_if = grgsm.trx(self.trx_remote_addr,
str(self.trx_base_port))
##################################################
# Connections
##################################################
self.connect((self.phy, 0), (self.blocks_rotator, 0))
self.connect((self.blocks_rotator, 0), (self.gsm_input, 0))
self.connect((self.gsm_input, 0), (self.gsm_receiver, 0))
self.msg_connect((self.gsm_receiver, 'measurements'),
(self.gsm_clck_ctrl, 'measurements'))
self.msg_connect((self.gsm_clck_ctrl, 'ctrl'),
(self.gsm_input, 'ctrl_in'))
self.msg_connect((self.gsm_receiver, 'C0'),
(self.gsm_ts_filter, 'in'))
self.msg_connect((self.gsm_ts_filter, 'out'),
(self.gsm_trx_if, 'bursts'))
def check_available(self):
return self.fc_set
def shutdown(self):
print("[i] Shutdown Radio interface")
self.stop()
self.wait()
def get_args(self):
return self.args
def set_args(self, args):
self.args = args
def get_fc(self):
return self.fc
def set_fc(self, fc):
self.phy.set_center_freq(fc - self.shiftoff, 0)
self.gsm_input.set_fc(fc)
self.fc_set = True
self.fc = fc
def get_gain(self):
return self.gain
def set_gain(self, gain):
self.phy.set_gain(gain, 0)
self.gain = gain
def get_ppm(self):
return self.ppm
def set_ppm(self, ppm):
self.rtlsdr_source_0.set_freq_corr(ppm, 0)
self.ppm = ppm
def get_samp_rate(self):
return self.samp_rate
def set_samp_rate(self, samp_rate):
self.blocks_rotator.set_phase_inc(
-2 * pi * self.shiftoff / samp_rate)
self.gsm_input.set_samp_rate_in(samp_rate)
self.phy.set_sample_rate(samp_rate)
self.samp_rate = samp_rate
def get_shiftoff(self):
return self.shiftoff
def set_shiftoff(self, shiftoff):
self.blocks_rotator.set_phase_inc(
-2 * pi * shiftoff / self.samp_rate)
self.phy.set_bandwidth(250e3 + abs(shiftoff), 0)
self.phy.set_center_freq(self.fc - shiftoff, 0)
self.shiftoff = shiftoff

150
apps/trx/trx.py Executable file
View File

@ -0,0 +1,150 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# GR-GSM based transceiver
#
# (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
#
# All Rights Reserved
#
# This program 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 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import signal
import getopt
import sys
from ctrl_if import CTRLInterface
from radio_if import RadioInterface
COPYRIGHT = \
"Copyright (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
"License GPLv2+: GNU GPL version 2 or later " \
"<http://gnu.org/licenses/gpl.html>\n" \
"This is free software: you are free to change and redistribute it.\n" \
"There is NO WARRANTY, to the extent permitted by law.\n"
class Application:
# Application variables
remote_addr = "127.0.0.1"
base_port = 5700
# PHY specific
phy_sample_rate = 2000000
phy_subdev_spec = False
phy_gain = 30
phy_args = ""
phy_ppm = 0
def __init__(self):
self.print_copyright()
self.parse_argv()
# Set up signal handlers
signal.signal(signal.SIGINT, self.sig_handler)
def run(self):
# Init Radio interface
self.radio = RadioInterface(self.phy_args, self.phy_subdev_spec,
self.phy_sample_rate, self.phy_gain, self.phy_ppm,
self.remote_addr, self.base_port)
# Init TRX CTRL interface
self.server = CTRLInterface(self.remote_addr,
self.base_port + 101, self.base_port + 1, self.radio)
print("[i] Init complete")
# Enter main loop
while True:
self.server.loop()
def shutdown(self):
print("[i] Shutting down...")
self.server.shutdown()
self.radio.shutdown()
def print_copyright(self):
print(COPYRIGHT)
def print_help(self):
s = " Usage: " + sys.argv[0] + " [options]\n\n" \
" Some help...\n" \
" -h --help this text\n\n"
# TRX specific
s += " TRX interface specific\n" \
" -s --remote-addr Set remote address (default 127.0.0.1)\n" \
" -p --base-port Set base port number (default 5700)\n\n"
# PHY specific
s += " Radio interface specific\n" \
" -a --device-args Set device arguments\n" \
" -s --sample-rate Set PHY sample rate (default 2000000)\n" \
" -S --subdev-spec Set PHY sub-device specification\n" \
" -g --gain Set PHY gain (default 30)\n" \
" --ppm Set PHY frequency correction (default 0)\n"
print(s)
def parse_argv(self):
try:
opts, args = getopt.getopt(sys.argv[1:],
"a:p:i:s:S:g:h",
["help", "remote-addr=", "base-port=", "device-args=",
"gain=", "subdev-spec=", "sample-rate=", "ppm="])
except getopt.GetoptError as err:
# Print(help and exit)
self.print_help()
print("[!] " + str(err))
sys.exit(2)
for o, v in opts:
if o in ("-h", "--help"):
self.print_help()
sys.exit(2)
# TRX specific
elif o in ("-i", "--remote-addr"):
self.remote_addr = v
elif o in ("-p", "--base-port"):
if int(v) >= 0 and int(v) <= 65535:
self.base_port = int(v)
else:
print("[!] The port number should be in range [0-65536]")
sys.exit(2)
# PHY specific
elif o in ("-a", "--device-args"):
self.phy_args = v
elif o in ("-g", "--gain"):
self.phy_gain = int(v)
elif o in ("-S", "--subdev-spec"):
self.phy_subdev_spec = v
elif o in ("-s", "--sample-rate"):
self.phy_sample_rate = int(v)
elif o in ("--ppm"):
self.phy_ppm = int(v)
def sig_handler(self, signum, frame):
print("Signal %d received" % signum)
if signum is signal.SIGINT:
self.shutdown()
sys.exit(0)
def main():
Application().run()
if __name__ == '__main__':
main()

View File

@ -23,6 +23,7 @@ add_subdirectory(demapping)
add_subdirectory(receiver)
add_subdirectory(flow_control)
add_subdirectory(misc_utils)
add_subdirectory(trx_interface)
install(FILES
gsm_block_tree.xml DESTINATION share/gnuradio/grc/blocks
)

View File

@ -0,0 +1,22 @@
# Copyright 2011,2012 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 3, 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., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
install(FILES
gsm_trx.xml DESTINATION share/gnuradio/grc/blocks
)

View File

@ -0,0 +1,68 @@
<?xml version="1.0"?>
<block>
<name>TRX interface</name>
<key>grgsm_trx_interface</key>
<import>import grgsm</import>
<make>grgsm.trx($remote_addr, $base_port)</make>
<param>
<name>base_port</name>
<key>base_port</key>
<value>5700</value>
<type>string</type>
</param>
<param>
<name>remote_addr</name>
<key>remote_addr</key>
<value>127.0.0.1</value>
<type>string</type>
</param>
<sink>
<name>bursts</name>
<type>message</type>
</sink>
<doc>
OsmoTRX like UDP interface for external applications.
There are three UDP sockets: CLCK, CTRL and DATA.
Give a base port B (5700 by default), the SCH clock
interface is at port P=B. The TRX-side control interface
is on port P=B+100+1 and the data interface is on an odd
numbered port P=B+100+2.
Indications on the SCH Clock Interface (CLCK)
The SCH clock interface is output only (from the radio).
CLOCK gives the current value of the BTS clock to
be used by external applications. The indications are
sent whenever a transmission packet arrives that is too
late or too early.
Commands on Control Interface (CTRL)
The control interface uses a command-response protocol.
Commands are NULL-terminated ASCII strings. Each command
has a corresponding response. This interface isn't handled
by this particular block, and should be implemented outside.
Messages on the Data Interface
Messages on the data interface carry one radio burst per
UDP message.
Received Data Burst:
1 byte timeslot index
4 bytes GSM frame number, big endian
1 byte RSSI in -dBm
2 bytes correlator timing offset in 1/256 symbol steps,
2's-comp, big endian
148 bytes soft symbol estimates, 0 -&gt; definite "0",
255 -&gt; definite "1"
Transmit Data Burst:
1 byte timeslot index
4 bytes GSM frame number, big endian
1 byte transmit level wrt ARFCN max, -dB (attenuation)
148 bytes output symbol values, 0 &amp; 1
</doc>
</block>

View File

@ -33,3 +33,4 @@ add_subdirectory(receiver)
add_subdirectory(misc_utils)
add_subdirectory(qa_utils)
add_subdirectory(flow_control)
add_subdirectory(trx_interface)

View File

@ -0,0 +1,25 @@
# Copyright 2011,2012 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 3, 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., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
########################################################################
# Install public header files
########################################################################
install(FILES
trx.h DESTINATION include/grgsm/trx_interface
)

View File

@ -0,0 +1,59 @@
/* -*- c++ -*- */
/* @file
* @author Vadim Yanitskiy <axilirator@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef INCLUDED_GRGSM_TRX_H
#define INCLUDED_GRGSM_TRX_H
#include <grgsm/api.h>
#include <gnuradio/sync_block.h>
namespace gr {
namespace grgsm {
/*!
* \brief <+description of block+>
* \ingroup grgsm
*
*/
class GRGSM_API trx : virtual public gr::block
{
public:
typedef boost::shared_ptr<trx> sptr;
/*!
* \brief Return a shared_ptr to a new instance of grgsm::trx.
*
* To avoid accidental use of raw pointers, grgsm::trx's
* constructor is in a private implementation
* class. grgsm::trx::make is the public interface for
* creating new instances.
*/
static sptr make(
const std::string &remote_addr,
const std::string &base_port);
};
} // namespace grgsm
} // namespace gr
#endif /* INCLUDED_GRGSM_TRX_H */

View File

@ -69,6 +69,8 @@ list(APPEND grgsm_sources
qa_utils/message_source_impl.cc
qa_utils/message_sink_impl.cc
decryption/decryption_impl.cc
trx_interface/udp_socket.cc
trx_interface/trx_impl.cc
)

View File

@ -0,0 +1,154 @@
/* -*- c++ -*- */
/* @file
* @author Vadim Yanitskiy <axilirator@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gnuradio/io_signature.h>
#include <boost/lexical_cast.hpp>
#include "udp_socket.h"
#include "trx_impl.h"
namespace gr {
namespace grgsm {
trx::sptr
trx::make(
const std::string &remote_addr,
const std::string &base_port)
{
int base_port_int = boost::lexical_cast<int> (base_port);
return gnuradio::get_initial_sptr
(new trx_impl(remote_addr, base_port_int));
}
/*
* The private constructor
*/
trx_impl::trx_impl(const std::string &remote_addr, int base_port)
: gr::block("trx",
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(0, 0, 0))
{
message_port_register_in(pmt::mp("bursts"));
set_msg_handler(pmt::mp("bursts"),
boost::bind(&trx_impl::handle_dl_burst, this, _1));
// Prepare port numbers
std::string clck_src_port = boost::lexical_cast<std::string> (base_port + 0);
std::string clck_dst_port = boost::lexical_cast<std::string> (base_port + 100);
std::string data_src_port = boost::lexical_cast<std::string> (base_port + 2);
std::string data_dst_port = boost::lexical_cast<std::string> (base_port + 102);
// Init DATA interface
d_data_sock = new udp_socket(remote_addr, data_src_port, data_dst_port);
d_clck_sock = new udp_socket(remote_addr, clck_src_port, clck_dst_port);
}
/*
* Our virtual destructor.
*/
trx_impl::~trx_impl()
{
// Release all UDP sockets and free memory
delete d_data_sock;
delete d_clck_sock;
}
/*
* Create an UDP payload with clock indication
*/
void
trx_impl::clck_ind_send(uint32_t frame_nr)
{
char buf[20];
size_t n;
n = snprintf(buf, 20, "IND CLOCK %u", frame_nr);
d_clck_sock->udp_send((uint8_t *) buf, n + 1);
}
/*
* Create an UDP payload with burst bits
* and some channel data.
*/
void
trx_impl::burst_pack(pmt::pmt_t msg, uint8_t *buf)
{
pmt::pmt_t header_plus_burst = pmt::cdr(msg);
// Extract GSMTAP header from message
gsmtap_hdr *header = (gsmtap_hdr *)
pmt::blob_data(header_plus_burst);
// Pack timeslot index
buf[0] = header->timeslot;
// Extract frame number
uint32_t frame_nr = be32toh(header->frame_number);
// HACK: send clock indications every 51-th frame
if (frame_nr % 51 == 0)
clck_ind_send(frame_nr);
// Pack frame number
buf[1] = (frame_nr >> 24) & 0xff;
buf[2] = (frame_nr >> 16) & 0xff;
buf[3] = (frame_nr >> 8) & 0xff;
buf[4] = (frame_nr >> 0) & 0xff;
// Pack RSSI (-dBm)
buf[5] = -(uint8_t) header->signal_dbm;
// Pack correlator timing offset (TOA)
// FIXME: where to find this value?
buf[6] = 0;
buf[7] = 0;
// Extract bits {0..1} from message
// Despite GR-GSM uses int8_t, they are not real sbits {-127..127}
uint8_t *burst = (uint8_t *)
(pmt::blob_data(header_plus_burst)) + sizeof(gsmtap_hdr);
// Convert to transceiver interface specific bits {255..0}
for (int i = 0; i < 148; i++)
buf[8 + i] = burst[i] ? 255 : 0;
}
void
trx_impl::handle_dl_burst(pmt::pmt_t msg)
{
// 8 bytes of header + 148 bytes of burst
uint8_t buf[156];
// Compose a new UDP payload with burst
burst_pack(msg, buf);
// Send a burst
d_data_sock->udp_send(buf, 156);
}
} /* namespace grgsm */
} /* namespace gr */

View File

@ -0,0 +1,54 @@
/* -*- c++ -*- */
/* @file
* @author Vadim Yanitskiy <axilirator@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef INCLUDED_GRGSM_TRX_IMPL_H
#define INCLUDED_GRGSM_TRX_IMPL_H
#include <stddef.h>
#include <grgsm/gsmtap.h>
#include <grgsm/trx_interface/trx.h>
namespace gr {
namespace grgsm {
class trx_impl : public trx
{
private:
udp_socket *d_data_sock;
udp_socket *d_clck_sock;
void clck_ind_send(uint32_t frame_nr);
void burst_pack(pmt::pmt_t msg, uint8_t *buf);
public:
trx_impl(const std::string &remote_addr, int base_port);
~trx_impl();
void handle_dl_burst(pmt::pmt_t msg);
};
} // namespace grgsm
} // namespace gr
#endif /* INCLUDED_GRGSM_TRX_IMPL_H */

View File

@ -0,0 +1,117 @@
/* -*- c++ -*- */
/*
* Copyright 2013 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 3, 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., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gnuradio/thread/thread.h>
#include <gnuradio/io_signature.h>
#include <gnuradio/blocks/pdu.h>
#include <pmt/pmt.h>
#include <boost/lexical_cast.hpp>
#include "udp_socket.h"
using boost::asio::ip::udp;
namespace gr {
namespace grgsm {
udp_socket::udp_socket(
const std::string &remote_addr,
const std::string &src_port,
const std::string &dst_port)
{
// Resolve remote host address
udp::resolver resolver(d_io_service);
udp::resolver::query rx_query(
udp::v4(), remote_addr, src_port,
boost::asio::ip::resolver_query_base::passive);
udp::resolver::query tx_query(
udp::v4(), remote_addr, dst_port,
boost::asio::ip::resolver_query_base::passive);
d_udp_endpoint_rx = *resolver.resolve(rx_query);
d_udp_endpoint_tx = *resolver.resolve(tx_query);
// Create a socket
d_udp_socket.reset(new udp::socket(d_io_service, d_udp_endpoint_rx));
// Setup read handler
d_udp_socket->async_receive_from(
boost::asio::buffer(d_rxbuf), d_udp_endpoint_rx,
boost::bind(&udp_socket::handle_udp_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
// Start server
d_thread = gr::thread::thread(
boost::bind(&udp_socket::run_io_service, this));
}
udp_socket::~udp_socket()
{
// Stop server
d_io_service.stop();
d_thread.interrupt();
d_thread.join();
}
void
udp_socket::run_io_service(void)
{
d_io_service.run();
}
void
udp_socket::udp_send(uint8_t *data, size_t len)
{
d_udp_socket->send_to(
boost::asio::buffer(data, len),
d_udp_endpoint_tx);
}
void
udp_socket::handle_udp_read(
const boost::system::error_code& error,
size_t bytes_transferred)
{
if (error)
return;
pmt::pmt_t vector = pmt::init_u8vector(bytes_transferred,
(const uint8_t *) &d_rxbuf[0]);
pmt::pmt_t pdu = pmt::cons(pmt::PMT_NIL, vector);
// TODO: call handler here
d_udp_socket->async_receive_from(
boost::asio::buffer(d_rxbuf), d_udp_endpoint_rx,
boost::bind(&udp_socket::handle_udp_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
} /* namespace blocks */
}/* namespace gr */

View File

@ -0,0 +1,65 @@
/* -*- c++ -*- */
/*
* Copyright 2013 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 3, 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., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDED_GRGSM_TRX_UDP_SOCKET_H
#define INCLUDED_GRGSM_TRX_UDP_SOCKET_H
#include <gnuradio/thread/thread.h>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <pmt/pmt.h>
namespace gr {
namespace grgsm {
class udp_socket
{
private:
boost::asio::io_service d_io_service;
std::vector<char> d_rxbuf;
gr::thread::thread d_thread;
bool d_started;
bool d_finished;
boost::asio::ip::udp::endpoint d_udp_endpoint_rx;
boost::asio::ip::udp::endpoint d_udp_endpoint_tx;
boost::shared_ptr<boost::asio::ip::udp::socket> d_udp_socket;
void handle_udp_read(const boost::system::error_code& error,
size_t bytes_transferred);
void run_io_service(void);
public:
udp_socket(
const std::string &remote_addr,
const std::string &src_port,
const std::string &dst_port);
~udp_socket();
void udp_send(uint8_t *data, size_t len);
};
} /* namespace blocks */
} /* namespace gr */
#endif /* INCLUDED_GRGSM_TRX_UDP_SOCKET_H */

View File

@ -41,6 +41,7 @@
#include "grgsm/misc_utils/message_file_source.h"
#include "grgsm/misc_utils/msg_to_tag.h"
#include "grgsm/misc_utils/controlled_fractional_resampler_cc.h"
#include "grgsm/trx_interface/trx.h"
%}
%include "grgsm/receiver/receiver.h"
@ -117,3 +118,5 @@ GR_SWIG_BLOCK_MAGIC2(gsm, burst_source);
GR_SWIG_BLOCK_MAGIC2(gsm, message_source);
%include "grgsm/qa_utils/message_sink.h"
GR_SWIG_BLOCK_MAGIC2(gsm, message_sink);
%include "grgsm/trx_interface/trx.h"
GR_SWIG_BLOCK_MAGIC2(grgsm, trx);