aboutsummaryrefslogtreecommitdiffstats
path: root/gr-gmr1/lib
diff options
context:
space:
mode:
Diffstat (limited to 'gr-gmr1/lib')
-rw-r--r--gr-gmr1/lib/CMakeLists.txt77
-rw-r--r--gr-gmr1/lib/cxvec_compat.h43
-rw-r--r--gr-gmr1/lib/gsmtap_sink_impl.cc98
-rw-r--r--gr-gmr1/lib/gsmtap_sink_impl.h57
-rw-r--r--gr-gmr1/lib/rach_demod_impl.cc243
-rw-r--r--gr-gmr1/lib/rach_demod_impl.h62
-rw-r--r--gr-gmr1/lib/rach_detect_fft_impl.cc399
-rw-r--r--gr-gmr1/lib/rach_detect_fft_impl.h97
8 files changed, 1076 insertions, 0 deletions
diff --git a/gr-gmr1/lib/CMakeLists.txt b/gr-gmr1/lib/CMakeLists.txt
new file mode 100644
index 0000000..9f7573a
--- /dev/null
+++ b/gr-gmr1/lib/CMakeLists.txt
@@ -0,0 +1,77 @@
+# Copyright 2011 Free Software Foundation, Inc.
+#
+# 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.
+
+########################################################################
+# Setup library
+########################################################################
+include(GrPlatform) #define LIB_SUFFIX
+
+include_directories(${Boost_INCLUDE_DIR})
+link_directories(${Boost_LIBRARY_DIRS})
+
+list(APPEND gmr1_sources
+ ../../src/sdr/dkab.c
+ ../../src/sdr/fcch.c
+ ../../src/sdr/nb.c
+ ../../src/sdr/pi4cxpsk.c
+ ../../src/l1/a5.c
+ ../../src/l1/bcch.c
+ ../../src/l1/ccch.c
+ ../../src/l1/conv.c
+ ../../src/l1/crc.c
+ ../../src/l1/facch3.c
+ ../../src/l1/facch9.c
+ ../../src/l1/interleave.c
+ ../../src/l1/punct.c
+ ../../src/l1/rach.c
+ ../../src/l1/scramb.c
+ ../../src/l1/tch3.c
+ ../../src/l1/tch9.c
+ ../../src/gsmtap.c
+ gsmtap_sink_impl.cc
+ rach_demod_impl.cc
+ rach_detect_fft_impl.cc
+)
+
+add_library(gnuradio-gmr1 SHARED ${gmr1_sources})
+target_link_libraries(gnuradio-gmr1
+ ${Boost_LIBRARIES}
+ ${FFTW3F_LIBRARIES}
+ ${LIBOSMOCORE_LIBRARIES}
+ ${LIBOSMODSP_LIBRARIES}
+ ${GNURADIO_ALL_LIBRARIES}
+)
+set_target_properties(gnuradio-gmr1 PROPERTIES DEFINE_SYMBOL "gnuradio_gmr1_EXPORTS")
+
+########################################################################
+# Install built library files
+########################################################################
+install(TARGETS gnuradio-gmr1
+ LIBRARY DESTINATION lib${LIB_SUFFIX} # .so/.dylib file
+ ARCHIVE DESTINATION lib${LIB_SUFFIX} # .lib file
+ RUNTIME DESTINATION bin # .dll file
+)
+
+########################################################################
+# Build and register unit test
+########################################################################
+find_package(Boost COMPONENTS unit_test_framework)
+
+include(GrTest)
+set(GR_TEST_TARGET_DEPS gnuradio-gmr1)
+#turn each test cpp file into an executable with an int main() function
+add_definitions(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN)
diff --git a/gr-gmr1/lib/cxvec_compat.h b/gr-gmr1/lib/cxvec_compat.h
new file mode 100644
index 0000000..8457b89
--- /dev/null
+++ b/gr-gmr1/lib/cxvec_compat.h
@@ -0,0 +1,43 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2014 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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.
+ *
+ * This software 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 software; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_GR_GMR1_CXVEC_COMPAT_H
+#define INCLUDED_GR_GMR1_CXVEC_COMPAT_H
+
+#include <complex.h>
+#include <gnuradio/types.h>
+
+#define GCC_VERSION ( \
+ __GNUC__ * 10000 + \
+ __GNUC_MINOR__ * 100 + \
+ __GNUC_PATCHLEVEL__ \
+)
+
+#if GCC_VERSION >= 40800
+# define complex _Complex
+# undef _GLIBCXX_HAVE_COMPLEX_H
+#endif
+
+extern "C" {
+#include <osmocom/dsp/cxvec.h>
+#include <osmocom/dsp/cxvec_math.h>
+}
+
+#endif /* INCLUDED_GR_GMR1_CXVEC_COMPAT_H */
diff --git a/gr-gmr1/lib/gsmtap_sink_impl.cc b/gr-gmr1/lib/gsmtap_sink_impl.cc
new file mode 100644
index 0000000..6a53f8a
--- /dev/null
+++ b/gr-gmr1/lib/gsmtap_sink_impl.cc
@@ -0,0 +1,98 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2014 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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.
+ *
+ * This software 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 software; 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 <string.h>
+
+#include <gnuradio/io_signature.h>
+
+#include "gsmtap_sink_impl.h"
+
+extern "C" {
+#include <osmocom/core/gsmtap.h>
+#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/gmr1/gsmtap.h>
+}
+
+
+namespace gr {
+ namespace gmr1 {
+
+gsmtap_sink::sptr
+gsmtap_sink::make(const std::string &host, uint16_t port)
+{
+ return gnuradio::get_initial_sptr(
+ new gsmtap_sink_impl(host, port)
+ );
+}
+
+gsmtap_sink_impl::gsmtap_sink_impl(const std::string &host, uint16_t port)
+ : gr::block("gsmtap_sink",
+ io_signature::make(0, 0, 0),
+ io_signature::make(0, 0, 0)),
+ d_host(host), d_port(port)
+{
+ this->d_gti = gsmtap_source_init(host.c_str(), port, 0);
+ gsmtap_source_add_sink(this->d_gti);
+
+ message_port_register_in(PDU_PORT_ID);
+ set_msg_handler(PDU_PORT_ID, boost::bind(&gsmtap_sink_impl::send_pdu, this, _1));
+}
+
+gsmtap_sink_impl::~gsmtap_sink_impl()
+{
+ /* FIXME: No destroy */
+}
+
+
+std::string
+gsmtap_sink_impl::host() const
+{
+ return this->d_host;
+}
+
+uint16_t
+gsmtap_sink_impl::port() const
+{
+ return this->d_port;
+}
+
+
+void
+gsmtap_sink_impl::send_pdu(pmt::pmt_t pdu)
+{
+ pmt::pmt_t meta = pmt::car(pdu);
+ pmt::pmt_t vector = pmt::cdr(pdu);
+ size_t len = pmt::length(vector);
+ size_t offset(0);
+ const uint8_t* rach = (const uint8_t*) pmt::uniform_vector_elements(vector, offset);
+
+ /* Send to GSMTap */
+ gsmtap_sendmsg(this->d_gti, gmr1_gsmtap_makemsg(
+ GSMTAP_GMR1_RACH,
+ 0, 0, rach, len
+ ));
+}
+
+ } // namespace gmr1
+} // namespace gr
diff --git a/gr-gmr1/lib/gsmtap_sink_impl.h b/gr-gmr1/lib/gsmtap_sink_impl.h
new file mode 100644
index 0000000..21bcc03
--- /dev/null
+++ b/gr-gmr1/lib/gsmtap_sink_impl.h
@@ -0,0 +1,57 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2014 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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.
+ *
+ * This software 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 software; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+
+#ifndef INCLUDED_GR_GMR1_GSMTAP_SINK_IMPL_H
+#define INCLUDED_GR_GMR1_GSMTAP_SINK_IMPL_H
+
+#include <gnuradio/gmr1/gsmtap_sink.h>
+
+struct gsmtap_inst;
+
+namespace gr {
+ namespace gmr1 {
+
+ /*!
+ * \brief
+ * \ingroup gmr1
+ */
+ class gsmtap_sink_impl : public gsmtap_sink
+ {
+ private:
+ std::string d_host;
+ uint16_t d_port;
+
+ struct gsmtap_inst *d_gti;
+
+ void send_pdu(pmt::pmt_t pdu);
+
+ public:
+ gsmtap_sink_impl(const std::string &host, uint16_t port);
+ virtual ~gsmtap_sink_impl();
+
+ std::string host() const;
+ uint16_t port() const;
+ };
+
+ } // namespace gmr1
+} // namespace gr
+
+#endif /* INCLUDED_GR_GMR1_GSMTAP_SINK_IMPL_H */
diff --git a/gr-gmr1/lib/rach_demod_impl.cc b/gr-gmr1/lib/rach_demod_impl.cc
new file mode 100644
index 0000000..8d0960c
--- /dev/null
+++ b/gr-gmr1/lib/rach_demod_impl.cc
@@ -0,0 +1,243 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2014 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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.
+ *
+ * This software 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 software; 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 <stdio.h>
+
+#include <gnuradio/io_signature.h>
+
+#include "rach_demod_impl.h"
+
+
+#include "cxvec_compat.h"
+
+extern "C" {
+#include <osmocom/core/bits.h>
+#include <osmocom/gmr1/l1/rach.h>
+#include <osmocom/gmr1/sdr/pi4cxpsk.h>
+#include <osmocom/gmr1/sdr/nb.h>
+}
+
+
+namespace gr {
+ namespace gmr1 {
+
+rach_demod::sptr
+rach_demod::make(int sps, int etoa, const std::string& len_tag_key)
+{
+ return gnuradio::get_initial_sptr(
+ new rach_demod_impl(sps, etoa, len_tag_key)
+ );
+}
+
+rach_demod_impl::rach_demod_impl(int sps, int etoa, const std::string& len_tag_key)
+ : gr::tagged_stream_block("rach_demod",
+ io_signature::make(1, 1, sizeof(gr_complex)),
+ io_signature::make(0, 0, 0),
+ len_tag_key),
+ d_sps(sps), d_etoa(etoa)
+{
+ message_port_register_out(PDU_PORT_ID);
+}
+
+rach_demod_impl::~rach_demod_impl()
+{
+ /* Nothing to do */
+}
+
+
+int
+rach_demod_impl::sps() const
+{
+ return this->d_sps;
+}
+
+int
+rach_demod_impl::etoa() const
+{
+ return this->d_etoa;
+}
+
+
+/*
+ * Attempts to detect a burst assuming a given TOA
+ */
+float
+rach_demod_impl::estimate(struct osmo_cxvec *burst, int etoa, float *cw_freq)
+{
+ const int guard = 2 * this->d_sps;
+ struct osmo_cxvec _cw1, *cw1 = &_cw1;
+ struct osmo_cxvec _cw2, *cw2 = &_cw2;
+ float complex c1, c2;
+ float r;
+ int i;
+
+ /* Map the CW1 and CW2 part of the sync sequence */
+ osmo_cxvec_init_from_data(cw1,
+ burst->data + etoa + this->d_sps * 127,
+ this->d_sps * 32
+ );
+
+ osmo_cxvec_init_from_data(cw2,
+ burst->data + etoa + this->d_sps * 191,
+ this->d_sps * 32
+ );
+
+ /* Compute the average rotation speed per sample of cw1 & cw2 */
+ r = 0.0f;
+
+ for (i=guard; i<(cw1->len-1-guard); i++)
+ {
+ r += cargf(cw1->data[i+1] * conjf(cw1->data[i]));
+ r += cargf(cw2->data[i+1] * conjf(cw2->data[i]));
+ }
+
+ r /= 2.0f * (cw1->len-1-2*guard);
+
+ /* Compute the correlation value using that speed to revert rotation */
+ c1 = c2 = 0.0f;
+
+ for (i=0; i<cw1->len; i++)
+ {
+ float complex e = cexpf(I * i * -r);
+ c1 += cw1->data[i] * e;
+ c2 += cw2->data[i] * e;
+ }
+
+ /* Return the power & cw freq */
+ *cw_freq = r;
+
+ return cabsf(c1) + cabsf(c2);
+}
+
+int
+rach_demod_impl::process(struct osmo_cxvec *burst, float freq_corr, uint8_t *rach, uint8_t *sb_mask)
+{
+ sbit_t ebits[494];
+ int sync_id;
+ float toa, freq_err;
+ int crc[2], conv;
+ int rv;
+
+ /* Demodulate to soft bits */
+ rv = gmr1_pi4cxpsk_demod(
+ &gmr1_rach_burst,
+ burst, this->d_sps, freq_corr,
+ ebits, &sync_id, &toa, &freq_err
+ );
+
+ /* Decode */
+ rv = gmr1_rach_decode(rach, ebits, 0x00, &conv, crc, sb_mask);
+
+ /* Debug */
+ //printf("rv: %d | crc: %d %d | conv: %d | sb_mask: %02hhx\n", rv, crc[0], crc[1], conv, *sb_mask);
+
+ return crc[1];
+}
+
+
+int
+rach_demod_impl::work(int noutput_items,
+ gr_vector_int &ninput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ const gr_complex *in = reinterpret_cast<const gr_complex *>(input_items[0]);
+ struct osmo_cxvec _burst, *burst = &_burst;
+ uint8_t rach[18], sb_mask;
+ float peak_corr, peak_cw_freq;
+ int ws, peak_etoa, etoa;
+ int rv;
+
+ /* Init a burst with the data */
+ osmo_cxvec_init_from_data(burst, (float complex *)in, ninput_items[0]);
+ ws = burst->len - gmr1_rach_burst.len * this->d_sps + 1;
+
+ /* Scan all possible TOA for the best fit */
+ peak_corr = 0.0f;
+
+ for (etoa=0; etoa<ws; etoa++)
+ {
+ float cw_freq, corr;
+
+ corr = this->estimate(burst, etoa, &cw_freq);
+
+ if (corr > peak_corr) {
+ peak_corr = corr;
+ peak_etoa = etoa;
+ peak_cw_freq = cw_freq;
+ }
+ }
+
+ /* Narrow down the window to 6 symbols around position we found */
+ if (peak_etoa < (3 * this->d_sps))
+ peak_etoa = 3 * this->d_sps;
+ if (peak_etoa > (ws - 3 * this->d_sps))
+ peak_etoa = ws - 3 * this->d_sps;
+
+ burst->data += peak_etoa - 3 * this->d_sps;
+ burst->len = (gmr1_rach_burst.len + 6) * this->d_sps;
+
+ /* Attempt demodulation and decoding */
+ rv = this->process(burst, -((d_sps * peak_cw_freq) - (M_PIf / 4.0f)), rach, &sb_mask);
+
+ /* Send as PDU */
+ if (!rv)
+ {
+ std::vector<tag_t> tags;
+ std::vector<tag_t>::iterator tags_itr;
+ pmt::pmt_t pdu_meta, pdu_vector, msg;
+
+ /* Grab the tags into a dict */
+ get_tags_in_range(tags, 0,
+ nitems_read(0),
+ nitems_read(0) + ninput_items[0]
+ );
+
+ pdu_meta = pmt::make_dict();
+ for (tags_itr = tags.begin(); tags_itr != tags.end(); tags_itr++) {
+ pdu_meta = dict_add(pdu_meta, (*tags_itr).key, (*tags_itr).value);
+ }
+
+ /* Add the guessed SB_Mask */
+ pdu_meta = dict_add(pdu_meta,
+ pmt::string_to_symbol("sb_mask"),
+ pmt::from_long(sb_mask)
+ );
+
+ /* Build vector with the data bytes */
+ pdu_vector = blocks::pdu::make_pdu_vector(blocks::pdu::byte_t, rach, 18);
+
+ /* Builds message */
+ msg = pmt::cons(pdu_meta, pdu_vector);
+
+ /* Send it out */
+ message_port_pub(PDU_PORT_ID, msg);
+ }
+
+ /* Consume the burst */
+ return ninput_items[0];
+}
+
+ } // namespace gmr1
+} // namespace gr
diff --git a/gr-gmr1/lib/rach_demod_impl.h b/gr-gmr1/lib/rach_demod_impl.h
new file mode 100644
index 0000000..64d1afb
--- /dev/null
+++ b/gr-gmr1/lib/rach_demod_impl.h
@@ -0,0 +1,62 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2014 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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.
+ *
+ * This software 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 software; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+
+#ifndef INCLUDED_GR_GMR1_RACH_DEMOD_IMPL_H
+#define INCLUDED_GR_GMR1_RACH_DEMOD_IMPL_H
+
+#include <gnuradio/gmr1/rach_demod.h>
+
+struct osmo_cxvec;
+
+namespace gr {
+ namespace gmr1 {
+
+ /*!
+ * \brief
+ * \ingroup gmr1
+ */
+ class rach_demod_impl : public rach_demod
+ {
+ private:
+ int d_sps;
+ int d_etoa;
+
+ float estimate(struct osmo_cxvec *burst, int etoa, float *cw_freq);
+ int process(struct osmo_cxvec *burst, float freq_corr, uint8_t *rach, uint8_t *sb_mask);
+
+ public:
+ rach_demod_impl(int sps, int etoa,
+ const std::string& len_tag_key);
+ virtual ~rach_demod_impl();
+
+ int sps() const;
+ int etoa() const;
+
+ int work(int noutput_items,
+ gr_vector_int &ninput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+ };
+
+ } // namespace gmr1
+} // namespace gr
+
+#endif /* INCLUDED_GR_GMR1_RACH_DEMOD_IMPL_H */
diff --git a/gr-gmr1/lib/rach_detect_fft_impl.cc b/gr-gmr1/lib/rach_detect_fft_impl.cc
new file mode 100644
index 0000000..5f7d047
--- /dev/null
+++ b/gr-gmr1/lib/rach_detect_fft_impl.cc
@@ -0,0 +1,399 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2014 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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.
+ *
+ * This software 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 software; 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 <stdio.h>
+
+#include <gnuradio/io_signature.h>
+#include <gnuradio/fft/window.h>
+#include <volk/volk.h>
+
+#include "rach_detect_fft_impl.h"
+
+
+namespace gr {
+ namespace gmr1 {
+
+rach_detect_fft::sptr
+rach_detect_fft::make(
+ int fft_size, int overlap_ratio, float threshold,
+ int burst_length, int burst_offset, float freq_offset,
+ const std::string& len_tag_key)
+{
+ return gnuradio::get_initial_sptr(
+ new rach_detect_fft_impl(
+ fft_size, overlap_ratio, threshold,
+ burst_length, burst_offset, freq_offset,
+ len_tag_key
+ )
+ );
+}
+
+rach_detect_fft_impl::rach_detect_fft_impl(
+ int fft_size, int overlap_ratio, float threshold,
+ int burst_length, int burst_offset, float freq_offset,
+ const std::string& len_tag_key)
+ : gr::block("rach_detect_fft",
+ io_signature::make(1, 1, sizeof(gr_complex)),
+ io_signature::make(1, 1, sizeof(gr_complex))),
+ d_fft_size(fft_size), d_overlap_ratio(overlap_ratio),
+ d_threshold(threshold),
+ d_burst_length(burst_length), d_burst_offset(burst_offset),
+ d_freq_offset(freq_offset),
+ d_freq_tag_key(pmt::string_to_symbol("freq")),
+ d_len_tag_key(pmt::string_to_symbol(len_tag_key)),
+ d_burst_length_pmt(pmt::from_long(burst_length))
+{
+ this->d_fft = new gr::fft::fft_complex(this->d_fft_size, true, 1);
+
+ this->d_buf = (gr_complex *) volk_malloc(this->d_fft_size * sizeof(gr_complex), 128);
+ this->d_win = (float *) volk_malloc(this->d_fft_size * sizeof(float), 128);
+ this->d_pwr = (float *) volk_malloc(this->d_fft_size * sizeof(float), 128);
+ this->d_avg = (float *) volk_malloc(this->d_fft_size * sizeof(float), 128);
+
+ memset(this->d_avg, 0x00, this->d_fft_size * sizeof(float));
+
+ this->d_in_pos = 0;
+ this->d_out_pos = 0;
+
+ std::vector<float> win = gr::fft::window::blackmanharris(this->d_fft_size);
+ memcpy(this->d_win, &win[0], this->d_fft_size * sizeof(float));
+
+ this->set_history(burst_length + 1);
+}
+
+rach_detect_fft_impl::~rach_detect_fft_impl()
+{
+ volk_free(this->d_avg);
+ volk_free(this->d_pwr);
+ volk_free(this->d_win);
+ volk_free(this->d_buf);
+
+ delete this->d_fft;
+}
+
+
+void
+rach_detect_fft_impl::peak_detect(uint64_t position)
+{
+ std::vector<peak>::iterator it1, it2;
+ const int avg_hwin = 15;
+ const int avg_win = (avg_hwin * 2) + 1;
+ float sum;
+ int i;
+
+ /* Prime the moving average */
+ sum = 0.0f;
+ for (i=0; i<avg_win; i++)
+ sum += this->d_pwr[i];
+
+ /* Do a scan and compare with avg with peak */
+ for (i=avg_hwin; i<this->d_fft_size-avg_hwin; i++)
+ {
+ /* Is this a peak ? */
+ if (this->d_pwr[i] > (this->d_threshold * sum / (float)avg_win) &&
+ this->d_pwr[i] > (this->d_threshold * this->d_avg[i]))
+ {
+ bool merged = false;
+
+ /* Attempt merge with existing */
+ for (it1=this->d_peaks_l1.begin(); it1!=this->d_peaks_l1.end() && !merged; it1++)
+ {
+ merged |= it1->merge(position, i);
+ }
+
+#if 0
+ printf("%lld %d %d (%f %f %f)\n", position, i, merged, this->d_pwr[i], sum / (float)avg_win, this->d_avg[i]);
+#endif
+
+ /* No match, insert new peak */
+ if (!merged)
+ this->d_peaks_l1.push_back(peak(position, i));
+ }
+
+ /* Update moving average */
+ sum += this->d_pwr[i+avg_hwin+1] - this->d_pwr[i-avg_hwin];
+ }
+
+ /* Scan for expired peak at Level 1 */
+ for (it1=this->d_peaks_l1.begin(); it1!=this->d_peaks_l1.end();)
+ {
+ /* Expired ? */
+ if (it1->expired(position - 20))
+ {
+ bool matched = false;
+
+ /* Scan Level 2 for a match at the right distance */
+ for (it2=this->d_peaks_l2.begin(); it2 != this->d_peaks_l2.end() && !matched; it2++)
+ {
+ if ((fabsf(it1->bin() - it2->bin()) < 1.0f) &&
+ (it1->d_time[1] - it2->d_time[0] <= 4 * this->d_overlap_ratio) &&
+ (it1->d_time[1] - it2->d_time[0] >= 2 * this->d_overlap_ratio))
+ {
+ matched = true;
+
+#if 0
+ printf("Match: (%llu %llu %d %d) (%llu %llu %d %d)\n",
+ it1->d_time[0], it1->d_time[1], it1->d_bin[0], it1->d_bin[1],
+ it2->d_time[0], it2->d_time[1], it2->d_bin[0], it2->d_bin[1]
+ );
+#endif
+
+ this->d_peaks_pending.push_back(*it1);
+ this->d_peaks_l2.erase(it2);
+ }
+ }
+
+ /* No match, insert in Level 2 */
+ if (!matched)
+ this->d_peaks_l2.push_back(*it1);
+
+ /* Remove from Level 1 */
+ it1 = this->d_peaks_l1.erase(it1);
+ } else {
+ /* Just move on */
+ it1++;
+ }
+ }
+
+ /* Scan for expired peaks at Level 2 */
+ for (it1=this->d_peaks_l2.begin(); it1!=this->d_peaks_l2.end();)
+ {
+ if (it1->expired(position - 40)) {
+ it1 = this->d_peaks_l2.erase(it1);
+ } else {
+ it1++;
+ }
+ }
+}
+
+
+int
+rach_detect_fft_impl::general_work(
+ int noutput_items,
+ gr_vector_int& ninput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ int read, max_read;
+
+ /* Buffers pointer */
+ const gr_complex *sig_in = reinterpret_cast<const gr_complex *>(input_items[0]);
+ gr_complex *burst_out = reinterpret_cast<gr_complex *>(output_items[0]);
+ gr_complex *fft_in = this->d_fft->get_inbuf();
+ gr_complex *fft_out = this->d_fft->get_outbuf();
+
+ /* Skip over history */
+ sig_in += this->history() - 1;
+
+ /* If there is pending output, process this first */
+ if (!this->d_peaks_pending.empty())
+ {
+ peak pk = this->d_peaks_pending.back();
+ int to_copy = this->d_burst_length - this->d_out_pos;
+
+ if (to_copy > noutput_items)
+ to_copy = noutput_items;
+
+ if (to_copy == 0)
+ return 0;
+
+ if (this->d_out_pos == 0)
+ {
+ float phase_inc;
+
+ /* Configure the rotator */
+ phase_inc = - (2.0f * (float)M_PI / this->d_fft_size) * (
+ fmodf(
+ pk.bin() + (float)(this->d_fft_size / 2),
+ (float)this->d_fft_size
+ ) - (float)(this->d_fft_size / 2)
+ );
+
+ phase_inc += this->d_freq_offset;
+
+ this->d_r.set_phase_incr( exp(gr_complex(0, phase_inc)) );
+
+ /* Burst start */
+ add_item_tag(
+ 0,
+ this->nitems_written(0),
+ this->d_len_tag_key,
+ this->d_burst_length_pmt
+ );
+
+ /* Burst angular frequency */
+ add_item_tag(
+ 0,
+ this->nitems_written(0),
+ this->d_freq_tag_key,
+ pmt::from_double(phase_inc)
+ );
+ }
+
+ this->d_r.rotateN(
+ burst_out,
+ sig_in + this->d_out_pos - this->history() + 1,
+ to_copy
+ );
+
+ this->d_out_pos += to_copy;
+
+ if (this->d_out_pos == this->d_burst_length)
+ {
+ this->d_peaks_pending.pop_back();
+ this->d_out_pos = 0;
+ }
+
+ return to_copy;
+ }
+
+ /* Process as much input as we can */
+ max_read = ninput_items[0] - this->history() + 1;
+
+ for (read=0; read<max_read;)
+ {
+ int n_adv = this->d_fft_size / this->d_overlap_ratio;
+ int n_reuse = this->d_fft_size - n_adv;
+ int n_fill;
+
+ /* Fill our internal buffer */
+ n_fill = this->d_fft_size - this->d_in_pos;
+ if (n_fill > max_read - read)
+ n_fill = max_read - read;
+
+ memcpy(this->d_buf + this->d_in_pos,
+ sig_in + read,
+ n_fill * sizeof(gr_complex));
+
+ read += n_fill;
+ this->d_in_pos += n_fill;
+
+ if (this->d_in_pos != this->d_fft_size)
+ break;
+
+ /* Apply window */
+ volk_32fc_32f_multiply_32fc(
+ fft_in,
+ this->d_buf,
+ this->d_win,
+ this->d_fft_size
+ );
+
+ /* Compute FFT */
+ this->d_fft->execute();
+
+ /* Compute the squared power */
+ volk_32fc_magnitude_squared_32f(this->d_pwr, fft_out, this->d_fft_size);
+
+ /* Compute the per-bin IIR average */
+ for (int i=0; i<this->d_fft_size; i++)
+ {
+ const float alpha = 0.01f, one_minus_alpha = 1.0f - alpha;
+ this->d_avg[i] = (this->d_avg[i] * one_minus_alpha)
+ + (this->d_pwr[i] * alpha);
+ }
+
+ /* Run the peak detection */
+ this->peak_detect(
+ (this->nitems_read(0) + read) / (this->d_fft_size / this->d_overlap_ratio)
+ );
+
+ /* Handle overlap */
+ if (this->d_overlap_ratio > 1) {
+ memmove(this->d_buf, this->d_buf + n_adv, n_reuse * sizeof(gr_complex));
+ this->d_in_pos = n_reuse;
+ } else {
+ this->d_in_pos = 0;
+ }
+
+ /* If we have anything pending, don't continue */
+ if (!this->d_peaks_pending.empty())
+ break;
+ }
+
+ /* We read some stuff */
+ this->consume_each(read);
+
+ return 0;
+}
+
+
+rach_detect_fft_impl::peak::peak(uint64_t time, int bin)
+{
+ this->d_time[0] = this->d_time[1] = time;
+ this->d_bin[0] = this->d_bin[1] = bin;
+}
+
+
+bool
+rach_detect_fft_impl::peak::merge(uint64_t time, int bin)
+{
+ /* Check if mergeable */
+ if (bin > (this->d_bin[1] + 2))
+ return false;
+
+ if (bin < (this->d_bin[0] - 2))
+ return false;
+
+ if (time > (this->d_time[1] + 2))
+ return false;
+
+ if (time < (this->d_time[0] - 2))
+ return false;
+
+ /* Do the merge */
+ if (bin > this->d_bin[1])
+ this->d_bin[1] = bin;
+ else if (bin < this->d_bin[0])
+ this->d_bin[0] = bin;
+
+ if (time > this->d_time[1])
+ this->d_time[1] = time;
+ else if (time < this->d_time[0])
+ this->d_time[0] = time;
+
+ return true;
+}
+
+bool
+rach_detect_fft_impl::peak::expired(uint64_t time_limit) const
+{
+ return time_limit > this->d_time[1];
+}
+
+uint64_t
+rach_detect_fft_impl::peak::time() const
+{
+ return this->d_time[0] + ((this->d_time[1] - this->d_time[0]) >> 1);
+}
+
+float
+rach_detect_fft_impl::peak::bin() const
+{
+ return (float)this->d_bin[0] + ((float)(this->d_bin[1] - this->d_bin[0]) / 2.0f);
+}
+
+
+ } // namespace gmr1
+} // namespace gr
diff --git a/gr-gmr1/lib/rach_detect_fft_impl.h b/gr-gmr1/lib/rach_detect_fft_impl.h
new file mode 100644
index 0000000..415c6df
--- /dev/null
+++ b/gr-gmr1/lib/rach_detect_fft_impl.h
@@ -0,0 +1,97 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2014 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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.
+ *
+ * This software 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 software; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+
+#ifndef INCLUDED_GR_GMR1_RACH_DETECT_FFT_IMPL_H
+#define INCLUDED_GR_GMR1_RACH_DETECT_FFT_IMPL_H
+
+#include <gnuradio/gmr1/rach_detect_fft.h>
+
+#include <gnuradio/blocks/rotator.h>
+#include <gnuradio/fft/fft.h>
+
+namespace gr {
+ namespace gmr1 {
+
+ /*!
+ * \brief
+ * \ingroup gmr1
+ */
+ class rach_detect_fft_impl : public rach_detect_fft
+ {
+ private:
+
+ class peak
+ {
+ public:
+ uint64_t d_time[2];
+ int d_bin[2];
+
+ peak(uint64_t time, int bin);
+ bool merge(uint64_t time, int bin);
+
+ bool expired(uint64_t time_limit) const;
+ uint64_t time() const;
+ float bin() const;
+ };
+
+ int d_fft_size;
+ int d_overlap_ratio;
+ float d_threshold;
+ int d_burst_length;
+ int d_burst_offset;
+ float d_freq_offset;
+
+ pmt::pmt_t d_freq_tag_key;
+ pmt::pmt_t d_len_tag_key;
+ pmt::pmt_t d_burst_length_pmt;
+
+ gr::fft::fft_complex *d_fft;
+ gr_complex *d_buf;
+ float *d_win;
+ float *d_pwr;
+ float *d_avg;
+
+ int d_in_pos;
+ std::vector<peak> d_peaks_l1;
+ std::vector<peak> d_peaks_l2;
+
+ int d_out_pos;
+ gr::blocks::rotator d_r;
+ std::vector<peak> d_peaks_pending;
+
+ void peak_detect(uint64_t position);
+
+ public:
+ rach_detect_fft_impl(int fft_size, int overlap_ratio, float threshold,
+ int burst_length, int burst_offset, float freq_offset,
+ const std::string& len_tag_key);
+ virtual ~rach_detect_fft_impl();
+
+ int general_work(int noutput_items,
+ gr_vector_int& ninput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+ };
+
+ } // namespace gmr1
+} // namespace gr
+
+#endif /* INCLUDED_GR_GMR1_RACH_DETECT_FFT_IMPL_H */