diff --git a/op25/gr-op25_repeater/include/op25_repeater/CMakeLists.txt b/op25/gr-op25_repeater/include/op25_repeater/CMakeLists.txt index 0fbd32c..b93833a 100644 --- a/op25/gr-op25_repeater/include/op25_repeater/CMakeLists.txt +++ b/op25/gr-op25_repeater/include/op25_repeater/CMakeLists.txt @@ -25,6 +25,7 @@ install(FILES vocoder.h gardner_costas_cc.h p25_frame_assembler.h + frame_assembler.h ambe_encoder_sb.h fsk4_slicer_fb.h DESTINATION include/op25_repeater ) diff --git a/op25/gr-op25_repeater/include/op25_repeater/frame_assembler.h b/op25/gr-op25_repeater/include/op25_repeater/frame_assembler.h new file mode 100644 index 0000000..086a1ba --- /dev/null +++ b/op25/gr-op25_repeater/include/op25_repeater/frame_assembler.h @@ -0,0 +1,59 @@ +/* -*- c++ -*- */ +/* + * Copyright 2017 Max H. Parke 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. + * + * 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_OP25_REPEATER_FRAME_ASSEMBLER_H +#define INCLUDED_OP25_REPEATER_FRAME_ASSEMBLER_H + +#include +#include +#include + +namespace gr { + namespace op25_repeater { + + /*! + * \brief <+description of block+> + * \ingroup op25_repeater + * + */ + class OP25_REPEATER_API frame_assembler : virtual public gr::block + { + public: + typedef boost::shared_ptr sptr; + + /*! + * \brief Return a shared_ptr to a new instance of op25_repeater::frame_assembler. + * + * To avoid accidental use of raw pointers, op25_repeater::frame_assembler's + * constructor is in a private implementation + * class. op25_repeater::frame_assembler::make is the public interface for + * creating new instances. + */ + static sptr make(const char* options, int debug, gr::msg_queue::sptr queue); + virtual void set_xormask(const char*p) {} + virtual void set_slotid(int slotid) {} + }; + + } // namespace op25_repeater +} // namespace gr + +#endif /* INCLUDED_OP25_REPEATER_FRAME_ASSEMBLER_H */ + diff --git a/op25/gr-op25_repeater/lib/CMakeLists.txt b/op25/gr-op25_repeater/lib/CMakeLists.txt index 361f266..8e73154 100644 --- a/op25/gr-op25_repeater/lib/CMakeLists.txt +++ b/op25/gr-op25_repeater/lib/CMakeLists.txt @@ -32,6 +32,7 @@ list(APPEND op25_repeater_sources vocoder_impl.cc gardner_costas_cc_impl.cc p25_frame_assembler_impl.cc + frame_assembler_impl.cc fsk4_slicer_fb_impl.cc ) list(APPEND op25_repeater_sources @@ -52,6 +53,9 @@ list(APPEND op25_repeater_sources ambe.c mbelib.c ambe_encoder.cc + rx_sync.cc + op25_audio.cc + CCITTChecksumReverse.cpp ) add_library(gnuradio-op25_repeater SHARED ${op25_repeater_sources}) diff --git a/op25/gr-op25_repeater/lib/frame_assembler_impl.cc b/op25/gr-op25_repeater/lib/frame_assembler_impl.cc new file mode 100644 index 0000000..8760a1f --- /dev/null +++ b/op25/gr-op25_repeater/lib/frame_assembler_impl.cc @@ -0,0 +1,96 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Max H. Parke 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. + * + * 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 +#include "frame_assembler_impl.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace gr { + namespace op25_repeater { + + void frame_assembler_impl::set_xormask(const char*p) { + } + + void frame_assembler_impl::set_slotid(int slotid) { + } + + frame_assembler::sptr + frame_assembler::make(const char* options, int debug, gr::msg_queue::sptr queue) + { + return gnuradio::get_initial_sptr + (new frame_assembler_impl(options, debug, queue)); + } + + /* + * The private constructor + */ + + /* + * Our virtual destructor. + */ + frame_assembler_impl::~frame_assembler_impl() + { + } + +static const int MIN_IN = 1; // mininum number of input streams +static const int MAX_IN = 1; // maximum number of input streams + +/* + * The private constructor + */ + frame_assembler_impl::frame_assembler_impl(const char* options, int debug, gr::msg_queue::sptr queue) + : gr::block("frame_assembler", + gr::io_signature::make (MIN_IN, MAX_IN, sizeof (char)), + gr::io_signature::make (0, 0, 0)), + d_msg_queue(queue), + d_sync(options, debug) +{ +} + +int +frame_assembler_impl::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + + const uint8_t *in = (const uint8_t *) input_items[0]; + + for (int i=0; i + +#include +#include +#include +#include +#include +#include + +#include "rx_sync.h" + +typedef std::deque dibit_queue; + +namespace gr { + namespace op25_repeater { + + class frame_assembler_impl : public frame_assembler + { + private: + int d_debug; + gr::msg_queue::sptr d_msg_queue; + rx_sync d_sync; + + // internal functions + + void queue_msg(int duid); + void set_xormask(const char*p) ; + void set_slotid(int slotid) ; + + public: + + public: + frame_assembler_impl(const char* options, int debug, gr::msg_queue::sptr queue); + ~frame_assembler_impl(); + + // 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); + }; + + } // namespace op25_repeater +} // namespace gr + +#endif /* INCLUDED_OP25_REPEATER_FRAME_ASSEMBLER_IMPL_H */ diff --git a/op25/gr-op25_repeater/lib/rx_sync.cc b/op25/gr-op25_repeater/lib/rx_sync.cc new file mode 100644 index 0000000..1ca7f8e --- /dev/null +++ b/op25/gr-op25_repeater/lib/rx_sync.cc @@ -0,0 +1,377 @@ +// P25 Decoder (C) Copyright 2013, 2014, 2015, 2016, 2017 Max H. Parke KA1RBI +// +// This file is part of OP25 +// +// OP25 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. +// +// OP25 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 OP25; see the file COPYING. If not, write to the Free +// Software Foundation, Inc., 51 Franklin Street, Boston, MA +// 02110-1301, USA. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rx_sync.h" + +#include "bit_utils.h" + +#include "check_frame_sync.h" + +#include "p25p2_vf.h" +#include "mbelib.h" +#include "ambe.h" +#include "rs.h" +#include "crc16.h" + +#include "ysf_const.h" +#include "dmr_const.h" +#include "p25_frame.h" +#include "op25_imbe_frame.h" +#include "software_imbe_decoder.h" +#include "op25_audio.h" + +namespace gr{ + namespace op25_repeater{ + +void rx_sync::cbuf_insert(const uint8_t c) { + d_cbuf[d_cbuf_idx] = c; + d_cbuf[d_cbuf_idx + CBUF_SIZE] = c; + d_cbuf_idx = (d_cbuf_idx + 1) % CBUF_SIZE; +} + +void rx_sync::sync_reset(void) { + d_threshold = 0; + d_shift_reg = 0; + d_unmute_until[0] = 0; + d_unmute_until[1] = 0; +} + +static int ysf_decode_fich(const uint8_t src[100], uint8_t dest[32]) { // input is 100 dibits, result is 32 bits +// return -1 on decode error, else 0 + static const int pc[] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1}; + uint8_t buf[100]; + for (int i=0; i<20; i++) { + for (int j=0; j<5; j++) { + buf[j+i*5] = src[i+j*20]; + } + } + uint8_t dr = 0; + uint8_t ans[100]; + /* fake trellis decode */ + /* TODO: make less fake */ + for (int i=0; i<100; i++) { + uint8_t sym = buf[i]; + uint8_t d0 = ((dr << 1) | 0) & 0x1f; + uint8_t r0 = (pc[ d0 & 0x19 ] << 1) + pc[ d0 & 0x17]; + uint8_t d1 = ((dr << 1) | 1) & 0x1f; + uint8_t r1 = (pc[ d1 & 0x19 ] << 1) + pc[ d1 & 0x17]; + if (sym == r0) { + ans[i] = 0; + dr = d0; + } else if (sym == r1) { + ans[i] = 1; + dr = d1; + } else { + return -1; /* decode error */ + } + } + uint8_t fich_bits[12*4]; + store_i(gly24128Dec(load_i(ans+24*0, 24)), fich_bits+12*0, 12); + store_i(gly24128Dec(load_i(ans+24*1, 24)), fich_bits+12*1, 12); + store_i(gly24128Dec(load_i(ans+24*2, 24)), fich_bits+12*2, 12); + store_i(gly24128Dec(load_i(ans+24*3, 24)), fich_bits+12*3, 12); + uint16_t crc_result = crc16(fich_bits, 48); + if (crc_result != 0) + return -1; // crc failure + memcpy(dest, fich_bits, 32); + return 0; +} + +void rx_sync::ysf_sync(const uint8_t dibitbuf[], bool& ysf_fullrate, bool& unmute) { + uint8_t fich_buf[32]; + int rc = ysf_decode_fich(dibitbuf+20, fich_buf); + if (rc == 0) { + uint32_t fich = load_i(fich_buf, 32); + uint32_t dt = (fich >> 8) & 3; + d_shift_reg = dt; + } + switch(d_shift_reg) { + case 0: // voice/data mode 1 + unmute = false; + break; + case 1: // data mode + unmute = false; + break; + case 2: // voice/data mode 2 + unmute = true; + ysf_fullrate = false; + break; + case 3: // voice fr mode + unmute = true; + ysf_fullrate = true; + break; + } + if (d_debug > 5 && !unmute) + fprintf(stderr, "ysf_sync: muting audio: dt: %d, rc: %d\n", d_shift_reg, rc); +} + +void rx_sync::dmr_sync(const uint8_t bitbuf[], int& current_slot, bool& unmute) { + static const int slot_ids[] = {0, 1, 0, 0, 1, 1, 0, 1}; + int tact; + int chan; + int fstype; + uint8_t tactbuf[sizeof(cach_tact_bits)]; + + for (size_t i=0; i>2) & 1; + d_shift_reg = (d_shift_reg << 1) + chan; + current_slot = slot_ids[d_shift_reg & 7]; + + uint64_t sync = load_reg64(bitbuf + (MODE_DATA[RX_TYPE_DMR].sync_offset << 1), MODE_DATA[RX_TYPE_DMR].sync_len); + if (check_frame_sync(DMR_VOICE_SYNC_MAGIC ^ sync, d_threshold, MODE_DATA[RX_TYPE_DMR].sync_len)) + fstype = 1; + else if (check_frame_sync(DMR_IDLE_SYNC_MAGIC ^ sync, d_threshold, MODE_DATA[RX_TYPE_DMR].sync_len)) + fstype = 2; + else + fstype = 0; + if (fstype > 0) + d_expires = d_symbol_count + MODE_DATA[d_current_type].expiration; + if (fstype == 1) { + if (!d_unmute_until[current_slot] && d_debug > 5) + fprintf(stderr, "unmute slot %d\n", current_slot); + d_unmute_until[current_slot] = d_symbol_count + MODE_DATA[d_current_type].expiration; + } else if (fstype == 2) { + if (d_unmute_until[current_slot] && d_debug > 5) + fprintf(stderr, "mute slot %d\n", current_slot); + d_unmute_until[current_slot] = 0; + } + if (d_unmute_until[current_slot] <= d_symbol_count) { + d_unmute_until[current_slot] = 0; + } + unmute = d_unmute_until[current_slot] > 0; +} + +rx_sync::rx_sync(const char * options, int debug) : // constructor + d_symbol_count(0), + d_sync_reg(0), + d_cbuf_idx(0), + d_current_type(RX_TYPE_NONE), + d_rx_count(0), + d_expires(0), + d_stereo(false), + d_debug(debug), + d_audio(options, debug) +{ + mbe_initMbeParms (&cur_mp[0], &prev_mp[0], &enh_mp[0]); + mbe_initMbeParms (&cur_mp[1], &prev_mp[1], &enh_mp[1]); + sync_reset(); +} + +rx_sync::~rx_sync() // destructor +{ +} + + +void rx_sync::codeword(const uint8_t* cw, const enum codeword_types codeword_type, int slot_id) { + static const int x=4; + static const int y=26; + static const uint8_t majority[8] = {0,0,0,1,0,1,1,1}; + + int b[9]; + uint8_t buf[4*26]; + uint8_t tmp_codeword [144]; + uint32_t E0, ET; + uint32_t u[8]; + bool do_fullrate = false; + bool do_silence = false; + voice_codeword fullrate_cw(voice_codeword_sz); + + switch(codeword_type) { + case CODEWORD_DMR: + interleaver.process_vcw(cw, b); + if (b[0] < 120) + mbe_dequantizeAmbe2250Parms(&cur_mp[slot_id], &prev_mp[slot_id], b); + break; + case CODEWORD_DSTAR: + interleaver.decode_dstar(cw, b, false); + if (b[0] < 120) + mbe_dequantizeAmbe2400Parms(&cur_mp[slot_id], &prev_mp[slot_id], b); + break; + case CODEWORD_YSF_HALFRATE: // 104 bits + for (int i=0; i= 120) { + do_silence = true; + } else { + d_software_decoder[slot_id].decode_tap(cur_mp[slot_id].L, 0, cur_mp[slot_id].w0, &cur_mp[slot_id].Vl[1], &cur_mp[slot_id].Ml[1]); + } + } + audio_samples *samples = d_software_decoder[slot_id].audio(); + float snd; + int16_t samp_buf[NSAMP_OUTPUT]; + for (int i=0; i < NSAMP_OUTPUT; i++) { + if ((!do_silence) && samples->size() > 0) { + snd = samples->front(); + samples->pop_front(); + } else { + snd = 0; + } + if (do_fullrate) + snd *= 32768.0; + samp_buf[i] = snd; + } + output(samp_buf, slot_id); +} + +void rx_sync::output(int16_t * samp_buf, const ssize_t slot_id) { + if (!d_stereo) { + d_audio.send_audio_channel(samp_buf, NSAMP_OUTPUT * sizeof(int16_t), slot_id); + return; + } +} + +void rx_sync::rx_sym(const uint8_t sym) +{ + uint8_t bitbuf[864*2]; + enum rx_types sync_detected = RX_TYPE_NONE; + int current_slot; + bool unmute; + uint8_t tmpcw[144]; + bool ysf_fullrate; + + d_symbol_count ++; + d_sync_reg = (d_sync_reg << 2) | (sym & 3); + for (int i = 1; i < RX_N_TYPES; i++) { + if (check_frame_sync(MODE_DATA[i].sync ^ d_sync_reg, (i == d_current_type) ? d_threshold : 0, MODE_DATA[i].sync_len)) { + sync_detected = (enum rx_types) i; + break; + } + } + cbuf_insert(sym); + if (d_current_type == RX_TYPE_NONE && sync_detected == RX_TYPE_NONE) + return; + d_rx_count ++; + if (sync_detected != RX_TYPE_NONE) { + if (d_current_type != sync_detected) { + d_current_type = sync_detected; + d_expires = d_symbol_count + MODE_DATA[d_current_type].expiration; + d_rx_count = 0; + } + if (d_rx_count != MODE_DATA[d_current_type].sync_offset + (MODE_DATA[d_current_type].sync_len >> 1)) { + if (d_debug) + fprintf(stderr, "resync at count %d for protocol %s\n", d_rx_count, MODE_DATA[d_current_type].type); + sync_reset(); + d_rx_count = MODE_DATA[d_current_type].sync_offset + (MODE_DATA[d_current_type].sync_len >> 1); + } else { + d_threshold = std::min(d_threshold + 1, 2); + } + d_expires = d_symbol_count + MODE_DATA[d_current_type].expiration; + } + if (d_symbol_count >= d_expires) { + if (d_debug) + fprintf(stderr, "%s: timeout, symbol %d\n", MODE_DATA[d_current_type].type, d_symbol_count); + d_current_type = RX_TYPE_NONE; + return; + } + if (d_rx_count < MODE_DATA[d_current_type].fragment_len) + return; + d_rx_count = 0; + int start_idx = d_cbuf_idx + CBUF_SIZE - MODE_DATA[d_current_type].fragment_len; + assert (start_idx >= 0); + uint8_t * symbol_ptr = d_cbuf+start_idx; + uint8_t * bit_ptr = symbol_ptr; + if (d_current_type != RX_TYPE_DSTAR) { + dibits_to_bits(bitbuf, symbol_ptr, MODE_DATA[d_current_type].fragment_len); + bit_ptr = bitbuf; + } + switch (d_current_type) { + case RX_TYPE_NONE: + break; + case RX_TYPE_P25: + for (unsigned int codeword_ct=0; codeword_ct < nof_voice_codewords; codeword_ct++) { + for (unsigned int i=0; i +#include +#include +#include +#include +#include +#include +#include + +#include "bit_utils.h" +#include "check_frame_sync.h" + +#include "p25p2_vf.h" +#include "mbelib.h" +#include "ambe.h" + +#include "ysf_const.h" +#include "dmr_const.h" +#include "p25_frame.h" +#include "op25_imbe_frame.h" +#include "software_imbe_decoder.h" +#include "op25_audio.h" + +namespace gr{ + namespace op25_repeater{ + +static const uint64_t DSTAR_FRAME_SYNC_MAGIC = 0x444445101440LL; // expanded into dibits + +enum rx_types { + RX_TYPE_NONE=0, + RX_TYPE_P25, + RX_TYPE_DMR, + RX_TYPE_DSTAR, + RX_TYPE_YSF, + RX_N_TYPES +}; // also used as array index + +static const struct _mode_data { + const char * type; + int sync_len; + int sync_offset; + int fragment_len; // symbols + int expiration; + uint64_t sync; +} MODE_DATA[RX_N_TYPES] = { + {"NONE", 0,0,0,0,0}, + {"P25", 48,0,864,1728, P25_FRAME_SYNC_MAGIC}, + {"DMR", 48,66,144,1728, DMR_VOICE_SYNC_MAGIC}, + {"DSTAR", 48,72,96,2016*2, DSTAR_FRAME_SYNC_MAGIC}, + {"YSF", 40,0,480,480*2, YSF_FRAME_SYNC_MAGIC} +}; // index order must match rx_types enum + +enum codeword_types { + CODEWORD_P25P1, + CODEWORD_P25P2, + CODEWORD_DMR, + CODEWORD_DSTAR, + CODEWORD_YSF_FULLRATE, + CODEWORD_YSF_HALFRATE +}; + +class rx_sync { +public: + void rx_sym(const uint8_t sym); + void sync_reset(void); + rx_sync(const char * options, int debug); + ~rx_sync(); +private: + void cbuf_insert(const uint8_t c); + void dmr_sync(const uint8_t bitbuf[], int& current_slot, bool& unmute); + void ysf_sync(const uint8_t dibitbuf[], bool& ysf_fullrate, bool& unmute); + void codeword(const uint8_t* cw, const enum codeword_types codeword_type, int slot_id); + void output(int16_t * samp_buf, const ssize_t slot_id); + static const int CBUF_SIZE=864; + static const int NSAMP_OUTPUT = 160; + + unsigned int d_symbol_count; + uint64_t d_sync_reg; + uint8_t d_cbuf[CBUF_SIZE*2]; + unsigned int d_cbuf_idx; + enum rx_types d_current_type; + int d_threshold; + int d_rx_count; + unsigned int d_expires; + int d_shift_reg; + unsigned int d_unmute_until[2]; + p25p2_vf interleaver; + mbe_parms cur_mp[2]; + mbe_parms prev_mp[2]; + mbe_parms enh_mp[2]; + software_imbe_decoder d_software_decoder[2]; + std::deque d_output_queue[2]; + bool d_stereo; + int d_debug; + op25_audio d_audio; +}; + + } // end namespace op25_repeater +} // end namespace gr +#endif // INCLUDED_RX_SYNC_H