diff --git a/repeater/src/lib/Makefile.am b/repeater/src/lib/Makefile.am index 7895a33..2a62d67 100644 --- a/repeater/src/lib/Makefile.am +++ b/repeater/src/lib/Makefile.am @@ -61,14 +61,14 @@ ourlib_LTLIBRARIES = _repeater.la _repeater_la_SOURCES = repeater\ repeater.cc \ repeater_fsk4_slicer_fb.cc \ - repeater_p25_frame_fb.cc \ - repeater_imbe_decode_fb.cc \ + repeater_p25_frame_assembler.cc \ repeater_pipe.cc \ repeater_ctcss_squelch_ff.cc \ repeater_squelch_base_ff.cc \ - repeater_gardner_symbol_recovery_cc.cc \ - repeater_costas_loop_cc.cc \ - bch.cc + repeater_gardner_costas_cc.cc \ + p25_framer.cc \ + bch.cc \ + rs.cc # magic flags _repeater_la_LDFLAGS = $(NO_UNDEFINED) -module -avoid-version @@ -85,11 +85,9 @@ repeater.cc repeater.py: repeater.i $(ALL_IFILES) # These headers get installed in ${prefix}/include/gnuradio grinclude_HEADERS = \ repeater_fsk4_slicer_fb.h \ - repeater_p25_frame_fb.h \ - repeater_imbe_decode_fb.h \ + repeater_p25_frame_assembler.h \ repeater_pipe.h \ - repeater_gardner_symbol_recovery_cc.h \ - repeater_costas_loop_cc.h \ + repeater_gardner_costas_cc.h \ repeater_ctcss_squelch_ff.h diff --git a/repeater/src/lib/p25_framer.cc b/repeater/src/lib/p25_framer.cc new file mode 100644 index 0000000..6f0a95a --- /dev/null +++ b/repeater/src/lib/p25_framer.cc @@ -0,0 +1,161 @@ +/* -*- c++ -*- */ +/* + * construct P25 frames out of raw dibits + * Copyright 2010, KA1RBI + */ + +#include +#include +#include +#include +#include +#include +#include + +static const int max_frame_lengths[16] = { +// lengths are in bits, not symbols + 792, // 0 - pdu + 0, 0, // 1, 2 - undef + 144, // 3 - tdu + 0, // 4 - undef + P25_VOICE_FRAME_SIZE, // 5 - ldu1 + 0, // 6 - undef + 720, // 7 - trunking (FIXME: are there systems with longer ones?) + 0, // 8 - undef + 0, // 9 - VSELP "voice PDU" + P25_VOICE_FRAME_SIZE, // a - ldu2 + 0, // b - undef + 0, // c - VSELP "voice PDU" + 0, 0, // d, e - undef + 432 // f - tdu +}; + +// constructor +p25_framer::p25_framer() : + reverse_p(0), + nid_syms(0), + next_bit(0), + nid_accum(0), + frame_size_limit(0), + symbols_received(0), + frame_body(P25_VOICE_FRAME_SIZE) +{ +} + +// destructor +p25_framer::~p25_framer () +{ +} + +// count no. of 1 bits in masked, xor'ed, FS, return true if < threshold +static inline bool check_frame_sync(uint64_t ch, int err_threshold) { + int errs=0; + for (int i=0; i < 48; i++) { + errs += (ch & 1); + ch = ch >> 1; + } + if (errs <= err_threshold) return 1; + return 0; +} + +/* + * Process the 64 bits after the frame sync, which should be the frame NID + * 1. verify bch and reject if bch cannot be decoded + * 2. extract NAC and DUID + * Returns false if decode failure, else true + */ +bool p25_framer::nid_codeword(uint64_t acc) { + bit_vector cw(64); + bool low = acc & 1; + // for bch, split bits into codeword vector + for (int i=0; i < 64; i++) { + acc >>= 1; + cw[i] = acc & 1; + } + + // do bch decode + int rc = bchDec(cw); + + // check if bch decode unsuccessful + if (rc < 0) { + return false; + } + + bch_errors = rc; + + // load corrected bch bits into acc + acc = 0; + for (int i=63; i>=0; i--) { + acc |= cw[i]; + acc <<= 1; + } + acc |= low; // FIXME + + nid_word = acc; // reconstructed NID + // extract nac and duid + nac = (acc >> 52) & 0xfff; + duid = (acc >> 48) & 0x00f; + + return true; +} + +/* + * rx_sym: called once per received symbol + * 1. looks for flags sequences + * 2. after flags detected (nid_syms > 0), accumulate 64-bit NID word + * 3. do BCH check on completed NID + * 4. after valid BCH check (next_bit > 0), accumulate frame data bits + * + * Returns true when complete frame received, else false + */ +bool p25_framer::rx_sym(uint8_t dibit) { + symbols_received++; + bool rc = false; + dibit ^= reverse_p; + // FIXME assert(dibit >= 0 && dibit <= 3) + nid_accum <<= 2; + nid_accum |= dibit; + if (nid_syms == 12) { + // ignore status dibit + nid_accum >>= 2; + } else if (nid_syms >= 33) { + // nid completely received + nid_syms = 0; + bool bch_rc = nid_codeword(nid_accum); + if (bch_rc) { // if ok to start accumulating frame data + next_bit = 48 + 64; + frame_size_limit = max_frame_lengths[duid]; + if (frame_size_limit <= next_bit) + // size isn't known a priori - + // fall back to max. size and wait for next FS + frame_size_limit = P25_VOICE_FRAME_SIZE; + } + } + if (nid_syms > 0) // if nid accumulation in progress + nid_syms++; // count symbols in nid + + if(check_frame_sync((nid_accum & P25_FRAME_SYNC_MASK) ^ P25_FRAME_SYNC_MAGIC, 6)) { + nid_syms = 1; + } + if(check_frame_sync((nid_accum & P25_FRAME_SYNC_MASK) ^ P25_FRAME_SYNC_REV_P, 0)) { + nid_syms = 1; + reverse_p ^= 0x02; // auto flip polarity reversal + fprintf(stderr, "Reversed FS polarity detected - autocorrecting\n"); + } + if (next_bit > 0) { + frame_body[next_bit++] = (dibit >> 1) & 1; + frame_body[next_bit++] = dibit & 1; + } + // dispose of received frame (if exists) and: + // 1. complete frame is received, or + // 2. flags is received + if ((next_bit > 0) && (next_bit >= frame_size_limit || nid_syms > 0)) { + if (nid_syms > 0) // if this was triggered by FS + next_bit -= 48; // FS has been added to body - remove it + p25_setup_frame_header(frame_body, nid_word); + frame_size = next_bit; + next_bit = 0; + rc = true; // set rc indicating frame available + } + return rc; +} diff --git a/repeater/src/lib/p25_framer.h b/repeater/src/lib/p25_framer.h new file mode 100644 index 0000000..8434ffb --- /dev/null +++ b/repeater/src/lib/p25_framer.h @@ -0,0 +1,46 @@ +/* -*- c++ -*- */ + +/* + * construct P25 frames out of raw dibits + * Copyright 2010, KA1RBI + * + * usage: after constructing, call rx_sym once per received dibit. + * frame fields are available for inspection when true is returned + */ + +#ifndef INCLUDED_P25_FRAMER_H +#define INCLUDED_P25_FRAMER_H + +class p25_framer; + +class p25_framer +{ +private: + typedef std::vector bit_vector; + // internal functions + bool nid_codeword(uint64_t acc); + // internal instance variables and state + uint8_t reverse_p; + int nid_syms; + uint32_t next_bit; + uint64_t nid_accum; + + uint32_t frame_size_limit; + +public: + p25_framer(); // constructor + ~p25_framer (); // destructor + bool rx_sym(uint8_t dibit) ; + + uint32_t symbols_received; + + // info from received frame + uint64_t nid_word; // received NID word + uint32_t nac; // extracted NAC + uint32_t duid; // extracted DUID + bit_vector frame_body; // all bits in frame + size_t frame_size; // number of bits in frame_body + uint32_t bch_errors; // number of errors detected in bch +}; + +#endif /* INCLUDED_P25_FRAMER_H */ diff --git a/repeater/src/lib/repeater.i b/repeater/src/lib/repeater.i index 145ec8f..ccb188f 100644 --- a/repeater/src/lib/repeater.i +++ b/repeater/src/lib/repeater.i @@ -9,12 +9,11 @@ #include "gnuradio_swig_bug_workaround.h" // mandatory bug fix #include "repeater_squelch_base_ff.h" #include "repeater_fsk4_slicer_fb.h" -#include "repeater_p25_frame_fb.h" -#include "repeater_imbe_decode_fb.h" +#include "repeater_p25_frame_assembler.h" #include "repeater_pipe.h" #include "repeater_ctcss_squelch_ff.h" -#include "repeater_gardner_symbol_recovery_cc.h" -#include "repeater_costas_loop_cc.h" +#include "repeater_gardner_costas_cc.h" +#include "rs.h" #include %} @@ -32,36 +31,24 @@ GR_SWIG_BLOCK_MAGIC(repeater,fsk4_slicer_fb); -repeater_fsk4_slicer_fb_sptr repeater_make_fsk4_slicer_fb (); +repeater_fsk4_slicer_fb_sptr repeater_make_fsk4_slicer_fb (const std::vector &slice_levels); class repeater_fsk4_slicer_fb : public gr_sync_block { private: - repeater_fsk4_slicer_fb (); + repeater_fsk4_slicer_fb (const std::vector &slice_levels); }; // ---------------------------------------------------------------- -GR_SWIG_BLOCK_MAGIC(repeater,p25_frame_fb); +GR_SWIG_BLOCK_MAGIC(repeater,p25_frame_assembler); -repeater_p25_frame_fb_sptr repeater_make_p25_frame_fb (); +repeater_p25_frame_assembler_sptr repeater_make_p25_frame_assembler (const char* udp_host, int port, int debug, bool do_imbe, bool do_output, bool do_msgq, gr_msg_queue_sptr queue); -class repeater_p25_frame_fb : public gr_sync_block +class repeater_p25_frame_assembler : public gr_sync_block { private: - repeater_p25_frame_fb (); -}; - -// ---------------------------------------------------------------- - -GR_SWIG_BLOCK_MAGIC(repeater,imbe_decode_fb); - -repeater_imbe_decode_fb_sptr repeater_make_imbe_decode_fb (); - -class repeater_imbe_decode_fb : public gr_sync_block -{ -private: - repeater_imbe_decode_fb (); + repeater_p25_frame_assembler (const char* udp_host, int port, int debug, bool do_imbe, bool do_output, bool do_msgq, gr_msg_queue_sptr queue); }; // ---------------------------------------------------------------- @@ -118,37 +105,12 @@ public: // ---------------------------------------------------------------- -GR_SWIG_BLOCK_MAGIC(repeater,gardner_symbol_recovery_cc); +GR_SWIG_BLOCK_MAGIC(repeater,gardner_costas_cc); -repeater_gardner_symbol_recovery_cc_sptr repeater_make_gardner_symbol_recovery_cc (float samples_per_symbol, float timing_error_gain); +repeater_gardner_costas_cc_sptr repeater_make_gardner_costas_cc (float samples_per_symbol, float gain_mu, float gain_omega, float alpha, float beta, float max_freq, float min_freq); -class repeater_gardner_symbol_recovery_cc : public gr_sync_block +class repeater_gardner_costas_cc : public gr_sync_block { private: - repeater_gardner_symbol_recovery_cc (float samples_per_symbol, float timing_error_gain); -}; - -// ---------------------------------------------------------------- - -GR_SWIG_BLOCK_MAGIC(repeater,costas_loop_cc); - -repeater_costas_loop_cc_sptr -repeater_make_costas_loop_cc (float alpha, float beta, - float max_freq, float min_freq, - int order - ) throw (std::invalid_argument); - - -class repeater_costas_loop_cc : public gr_sync_block -{ - private: - repeater_costas_loop_cc (float alpha, float beta, - float max_freq, float min_freq, int order); - - public: - void set_alpha(float alpha); - float alpha(); - void set_beta(float beta); - float beta(); - float freq(); + repeater_gardner_costas_cc (float samples_per_symbol, float gain_mu, float gain_omega, float alpha, float beta, float max_freq, float min_freq); }; diff --git a/repeater/src/lib/repeater_costas_loop_cc.cc b/repeater/src/lib/repeater_costas_loop_cc.cc deleted file mode 100644 index b208d10..0000000 --- a/repeater/src/lib/repeater_costas_loop_cc.cc +++ /dev/null @@ -1,189 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2006 Free Software Foundation, Inc. - * - * PI/4 DQPSK hack Copyright 2010 KA1RBI - * - * 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 -#include -#include -#include -#include - -#define M_TWOPI (2*M_PI) -static const gr_complex PT_45 = gr_expj( M_PI / 4.0 ); - -repeater_costas_loop_cc_sptr -repeater_make_costas_loop_cc (float alpha, float beta, - float max_freq, float min_freq, - int order - ) throw (std::invalid_argument) -{ - return repeater_costas_loop_cc_sptr (new repeater_costas_loop_cc (alpha, beta, - max_freq, min_freq, - order)); -} - -repeater_costas_loop_cc::repeater_costas_loop_cc (float alpha, float beta, - float max_freq, float min_freq, - int order - ) throw (std::invalid_argument) - : gr_sync_block ("costas_loop_cc", - gr_make_io_signature (1, 1, sizeof (gr_complex)), - gr_make_io_signature (1, 2, sizeof (gr_complex))), - d_alpha(alpha), d_beta(beta), - d_max_freq(max_freq), d_min_freq(min_freq), - d_phase(0), d_freq((max_freq+min_freq)/2), - d_order(order), d_phase_detector(0), - d_interp_counter(0), d_rot45(false) -{ - if (d_order < 0) { - d_rot45 = true; - d_order = 0 - d_order; - } - switch(d_order) { - case 2: - d_phase_detector = &repeater_costas_loop_cc::phase_detector_2; - break; - - case 4: - d_phase_detector = &repeater_costas_loop_cc::phase_detector_4; - break; - - default: - throw std::invalid_argument("order must be 2 or 4"); - break; - } -} - - -float -repeater_costas_loop_cc::phase_detector_4(gr_complex sample) const -{ - - return ((sample.real()>0 ? 1.0 : -1.0) * sample.imag() - - (sample.imag()>0 ? 1.0 : -1.0) * sample.real()); -} - -float -repeater_costas_loop_cc::phase_detector_2(gr_complex sample) const -{ - return (sample.real()*sample.imag()); -} - -void -repeater_costas_loop_cc::set_alpha(float alpha) -{ - d_alpha = alpha; -} - -void -repeater_costas_loop_cc::set_beta(float beta) -{ - d_beta = beta; -} - -int -repeater_costas_loop_cc::work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - const gr_complex *iptr = (gr_complex *) input_items[0]; - gr_complex *optr = (gr_complex *) output_items[0]; - gr_complex *foptr = (gr_complex *) output_items[1]; - - bool write_foptr = output_items.size() >= 2; - - float error; - gr_complex nco_out; - gr_complex rotated_sample; - - if (write_foptr) { - - for (int i = 0; i < noutput_items; i++){ - nco_out = gr_expj(-d_phase); - rotated_sample = optr[i] = iptr[i] * nco_out; - - if (d_rot45) { - if (d_interp_counter & 1) // every other symbol - rotated_sample = rotated_sample * PT_45; // rotate by +45 deg - d_interp_counter++; - } - - error = (*this.*d_phase_detector)(rotated_sample); - if (error > 1) - error = 1; - else if (error < -1) - error = -1; - - d_freq = d_freq + d_beta * error; - d_phase = d_phase + d_freq + d_alpha * error; - - while(d_phase>M_TWOPI) - d_phase -= M_TWOPI; - while(d_phase<-M_TWOPI) - d_phase += M_TWOPI; - - if (d_freq > d_max_freq) - d_freq = d_max_freq; - else if (d_freq < d_min_freq) - d_freq = d_min_freq; - - foptr[i] = gr_complex(d_freq,0); - } - } else { - for (int i = 0; i < noutput_items; i++){ - nco_out = gr_expj(-d_phase); - rotated_sample = optr[i] = iptr[i] * nco_out; - - if (d_rot45) { - if (d_interp_counter & 1) // every other symbol - rotated_sample = rotated_sample * PT_45; // rotate by +45 deg - d_interp_counter++; - } - - error = (*this.*d_phase_detector)(rotated_sample); - if (error > 1) - error = 1; - else if (error < -1) - error = -1; - - d_freq = d_freq + d_beta * error; - d_phase = d_phase + d_freq + d_alpha * error; - - while(d_phase>M_TWOPI) - d_phase -= M_TWOPI; - while(d_phase<-M_TWOPI) - d_phase += M_TWOPI; - - if (d_freq > d_max_freq) - d_freq = d_max_freq; - else if (d_freq < d_min_freq) - d_freq = d_min_freq; - - } - } - return noutput_items; -} diff --git a/repeater/src/lib/repeater_costas_loop_cc.h b/repeater/src/lib/repeater_costas_loop_cc.h deleted file mode 100644 index f82d0ec..0000000 --- a/repeater/src/lib/repeater_costas_loop_cc.h +++ /dev/null @@ -1,142 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2006 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_REPEATER_COSTAS_LOOP_CC_H -#define INCLUDED_REPEATER_COSTAS_LOOP_CC_H - -#include -#include -#include - - -/*! \brief A Costas loop carrier recovery module. - * \ingroup sync_blk - * - * The Costas loop locks to the center frequency of a signal and - * downconverts it to baseband. The second (order=2) order loop is - * used for BPSK where the real part of the output signal is the - * baseband BPSK signal and the imaginary part is the error - * signal. When order=4, it can be used for quadrature modulations - * where both I and Q (real and imaginary) are outputted. - * - * More details can be found online: - * - * J. Feigin, "Practical Costas loop design: Designing a simple and inexpensive - * BPSK Costas loop carrier recovery circuit," RF signal processing, pp. 20-36, - * 2002. - * - * http://rfdesign.com/images/archive/0102Feigin20.pdf - * - * \param alpha the loop gain used for phase adjustment - * \param beta the loop gain for frequency adjustments - * \param max_freq the maximum frequency deviation (radians/sample) the loop can handle - * \param min_freq the minimum frequency deviation (radians/sample) the loop can handle - * \param order the loop order, either 2 or 4 (enable PI/4 hack if order < 0) - */ -class repeater_costas_loop_cc; -typedef boost::shared_ptr repeater_costas_loop_cc_sptr; - - -repeater_costas_loop_cc_sptr -repeater_make_costas_loop_cc (float alpha, float beta, - float max_freq, float min_freq, - int order - ) throw (std::invalid_argument); - - -/*! - * \brief Carrier tracking PLL for QPSK - * \ingroup sync_blk - * input: complex; output: complex - *
The Costas loop can have two output streams: - * stream 1 is the baseband I and Q; - * stream 2 is the normalized frequency of the loop - * - * \p order must be 2 or 4. - */ -class repeater_costas_loop_cc : public gr_sync_block -{ - friend repeater_costas_loop_cc_sptr repeater_make_costas_loop_cc (float alpha, float beta, - float max_freq, float min_freq, - int order - ) throw (std::invalid_argument); - - float d_alpha, d_beta, d_max_freq, d_min_freq, d_phase, d_freq; - int d_order; - - repeater_costas_loop_cc (float alpha, float beta, - float max_freq, float min_freq, - int order - ) throw (std::invalid_argument); - - /*! \brief the phase detector circuit for fourth-order loops - * \param sample complex sample - * \return the phase error - */ - float phase_detector_4(gr_complex sample) const; // for QPSK - - /*! \brief the phase detector circuit for second-order loops - * \param sample a complex sample - * \return the phase error - */ - float phase_detector_2(gr_complex sample) const; // for BPSK - - - float (repeater_costas_loop_cc::*d_phase_detector)(gr_complex sample) const; - - unsigned int d_interp_counter; - bool d_rot45; - -public: - - /*! \brief set the first order gain - * \param alpha - */ - void set_alpha(float alpha); - - /*! \brief get the first order gain - * - */ - float alpha() const { return d_alpha; } - - /*! \brief set the second order gain - * \param beta - */ - void set_beta(float beta); - - /*! \brief get the second order gain - * - */ - float beta() const { return d_beta; } - - int work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - - /*! \brief returns the current NCO frequency in radians/sample - * - */ - float freq() const { return d_freq; } -}; - -#endif diff --git a/repeater/src/lib/repeater_fsk4_slicer_fb.cc b/repeater/src/lib/repeater_fsk4_slicer_fb.cc index bdea28c..1c6b944 100644 --- a/repeater/src/lib/repeater_fsk4_slicer_fb.cc +++ b/repeater/src/lib/repeater_fsk4_slicer_fb.cc @@ -33,15 +33,16 @@ #include #include +#include /* * Create a new instance of repeater_fsk4_slicer_fb and return * a boost shared_ptr. This is effectively the public constructor. */ repeater_fsk4_slicer_fb_sptr -repeater_make_fsk4_slicer_fb () +repeater_make_fsk4_slicer_fb (const std::vector &slice_levels) { - return repeater_fsk4_slicer_fb_sptr (new repeater_fsk4_slicer_fb ()); + return repeater_fsk4_slicer_fb_sptr (new repeater_fsk4_slicer_fb (slice_levels)); } /* @@ -61,12 +62,15 @@ static const int MAX_OUT = 1; // maximum number of output streams /* * The private constructor */ -repeater_fsk4_slicer_fb::repeater_fsk4_slicer_fb () +repeater_fsk4_slicer_fb::repeater_fsk4_slicer_fb (const std::vector &slice_levels) : gr_sync_block ("fsk4_slicer_fb", gr_make_io_signature (MIN_IN, MAX_IN, sizeof (float)), gr_make_io_signature (MIN_OUT, MAX_OUT, sizeof (unsigned char))) { - // nothing else required in this example + d_slice_levels[0] = slice_levels[0]; + d_slice_levels[1] = slice_levels[1]; + d_slice_levels[2] = slice_levels[2]; + d_slice_levels[3] = slice_levels[3]; } /* @@ -86,15 +90,33 @@ repeater_fsk4_slicer_fb::work (int noutput_items, unsigned char *out = (unsigned char *) output_items[0]; for (int i = 0; i < noutput_items; i++){ +#if 0 if (in[i] < -2.0) { out[i] = 3; } else if (in[i] < 0.0) { out[i] = 2; - } else if (in[i] < 2.0) { + } else if (in[i] < 2.0) { out[i] = 0; } else { out[i] = 1; } +#endif + uint8_t dibit; + float sym = in[i]; + if (d_slice_levels[3] < 0) { + dibit = 1; + if (d_slice_levels[3] <= sym && sym < d_slice_levels[0]) + dibit = 3; + } else { + dibit = 3; + if (d_slice_levels[2] <= sym && sym < d_slice_levels[3]) + dibit = 1; + } + if (d_slice_levels[0] <= sym && sym < d_slice_levels[1]) + dibit = 2; + if (d_slice_levels[1] <= sym && sym < d_slice_levels[2]) + dibit = 0; + out[i] = dibit; } // Tell runtime system how many output items we produced. diff --git a/repeater/src/lib/repeater_fsk4_slicer_fb.h b/repeater/src/lib/repeater_fsk4_slicer_fb.h index d74deed..a4ca0dc 100644 --- a/repeater/src/lib/repeater_fsk4_slicer_fb.h +++ b/repeater/src/lib/repeater_fsk4_slicer_fb.h @@ -46,7 +46,7 @@ typedef boost::shared_ptr repeater_fsk4_slicer_fb_sptr; * constructor is private. repeater_make_fsk4_slicer_fb is the public * interface for creating new instances. */ -repeater_fsk4_slicer_fb_sptr repeater_make_fsk4_slicer_fb (); +repeater_fsk4_slicer_fb_sptr repeater_make_fsk4_slicer_fb (const std::vector &slice_levels); /*! * \brief produce a stream of dibits, given a stream of floats in [-3,-1,1,3] @@ -60,9 +60,11 @@ private: // The friend declaration allows repeater_make_fsk4_slicer_fb to // access the private constructor. - friend repeater_fsk4_slicer_fb_sptr repeater_make_fsk4_slicer_fb (); + friend repeater_fsk4_slicer_fb_sptr repeater_make_fsk4_slicer_fb (const std::vector &slice_levels); - repeater_fsk4_slicer_fb (); // private constructor + repeater_fsk4_slicer_fb (const std::vector &slice_levels); // private constructor + + float d_slice_levels[4]; public: ~repeater_fsk4_slicer_fb (); // public destructor diff --git a/repeater/src/lib/repeater_gardner_symbol_recovery_cc.cc b/repeater/src/lib/repeater_gardner_costas_cc.cc similarity index 50% rename from repeater/src/lib/repeater_gardner_symbol_recovery_cc.cc rename to repeater/src/lib/repeater_gardner_costas_cc.cc index 875c958..95dbe9b 100644 --- a/repeater/src/lib/repeater_gardner_symbol_recovery_cc.cc +++ b/repeater/src/lib/repeater_gardner_costas_cc.cc @@ -30,54 +30,72 @@ #include #include #include -#include +#include #include #include #include #include +#define ENABLE_COSTAS_CQPSK_HACK 1 + +static const float M_TWOPI = 2 * M_PI; +#define VERBOSE_GARDNER 0 // Used for debugging symbol timing loop +#define VERBOSE_COSTAS 0 // Used for debugging phase and frequency tracking +static const gr_complex PT_45 = gr_expj( M_PI / 4.0 ); + // Public constructor -repeater_gardner_symbol_recovery_cc_sptr -repeater_make_gardner_symbol_recovery_cc(float samples_per_symbol, float timing_error_gain) +repeater_gardner_costas_cc_sptr +repeater_make_gardner_costas_cc(float samples_per_symbol, float gain_mu, float gain_omega, float alpha, float beta, float max_freq, float min_freq) { - return repeater_gardner_symbol_recovery_cc_sptr (new repeater_gardner_symbol_recovery_cc (samples_per_symbol, timing_error_gain)); + return repeater_gardner_costas_cc_sptr (new repeater_gardner_costas_cc (samples_per_symbol, gain_mu, gain_omega, alpha, beta, max_freq, min_freq)); } -repeater_gardner_symbol_recovery_cc::repeater_gardner_symbol_recovery_cc (float samples_per_symbol, float timing_error_gain) - : gr_block ("repeater_gardner_symbol_recovery_cc", +repeater_gardner_costas_cc::repeater_gardner_costas_cc (float samples_per_symbol, float gain_mu, float gain_omega, float alpha, float beta, float max_freq, float min_freq) + : gr_block ("repeater_gardner_costas_cc", gr_make_io_signature (1, 1, sizeof (gr_complex)), gr_make_io_signature (1, 1, sizeof (gr_complex))), d_mu(0), - d_gain_mu(timing_error_gain), + d_gain_omega(gain_omega), + d_gain_mu(gain_mu), d_last_sample(0), d_interp(new gri_mmse_fir_interpolator_cc()), - d_verbose(gr_prefs::singleton()->get_bool("repeater_gardner_symbol_recovery_cc", "verbose", false)), - d_dl_index(0) + d_verbose(gr_prefs::singleton()->get_bool("repeater_gardner_costas_cc", "verbose", false)), + d_dl(0), + d_dl_index(0), + d_alpha(alpha), d_beta(beta), + d_interp_counter(0), + d_theta(M_PI / 4.0), d_phase(0), d_freq(0), d_max_freq(max_freq) { set_omega(samples_per_symbol); - d_gain_omega = 0.25 * d_gain_mu * d_gain_mu; // FIXME: parameterize this set_relative_rate (1.0 / d_omega); set_history(d_twice_sps); // ensure extra input is available - d_dl = new gr_complex[d_twice_sps*2]; } -void repeater_gardner_symbol_recovery_cc::set_omega (float omega) { +void repeater_gardner_costas_cc::set_omega (float omega) { + if (d_dl) { + delete d_dl; + d_dl = 0; + } assert (omega >= 2.0); d_omega = omega; d_min_omega = omega*(1.0 - d_omega_rel); d_max_omega = omega*(1.0 + d_omega_rel); d_omega_mid = 0.5*(d_min_omega+d_max_omega); d_twice_sps = 2 * (int) ceilf(d_omega); + d_dl = new gr_complex[d_twice_sps*2]; } -repeater_gardner_symbol_recovery_cc::~repeater_gardner_symbol_recovery_cc () +repeater_gardner_costas_cc::~repeater_gardner_costas_cc () { delete d_interp; - delete d_dl; + if (d_dl) { + delete d_dl; + d_dl = 0; + } } void -repeater_gardner_symbol_recovery_cc::forecast(int noutput_items, gr_vector_int &ninput_items_required) +repeater_gardner_costas_cc::forecast(int noutput_items, gr_vector_int &ninput_items_required) { unsigned ninputs = ninput_items_required.size(); for (unsigned i=0; i < ninputs; i++) @@ -85,8 +103,60 @@ repeater_gardner_symbol_recovery_cc::forecast(int noutput_items, gr_vector_int & (int) ceil((noutput_items * d_omega) + d_interp->ntaps()); } +float // for QPSK +repeater_gardner_costas_cc::phase_error_detector_qpsk(gr_complex sample) +{ + float phase_error = 0; + if(fabsf(sample.real()) > fabsf(sample.imag())) { + if(sample.real() > 0) + phase_error = -sample.imag(); + else + phase_error = sample.imag(); + } + else { + if(sample.imag() > 0) + phase_error = sample.real(); + else + phase_error = -sample.real(); + } + + return phase_error; +} + +void +repeater_gardner_costas_cc::phase_error_tracking(gr_complex sample) +{ + float phase_error = 0; +#if ENABLE_COSTAS_CQPSK_HACK + if (d_interp_counter & 1) // every other symbol + sample = sample * PT_45; // rotate by +45 deg + d_interp_counter++; +#endif /* ENABLE_COSTAS_CQPSK_HACK */ + + // Make phase and frequency corrections based on sampled value + phase_error = phase_error_detector_qpsk(sample); + + d_freq += d_beta*phase_error; // adjust frequency based on error + d_phase += d_freq + d_alpha*phase_error; // adjust phase based on error + + // Make sure we stay within +-2pi + while(d_phase > M_TWOPI) + d_phase -= M_TWOPI; + while(d_phase < -M_TWOPI) + d_phase += M_TWOPI; + + // Limit the frequency range + d_freq = gr_branchless_clip(d_freq, d_max_freq); + +#if VERBOSE_COSTAS + printf("cl: phase_error: %f phase: %f freq: %f sample: %f+j%f constellation: %f+j%f\n", + phase_error, d_phase, d_freq, sample.real(), sample.imag(), + d_constellation[d_current_const_point].real(), d_constellation[d_current_const_point].imag()); +#endif +} + int -repeater_gardner_symbol_recovery_cc::general_work (int noutput_items, +repeater_gardner_costas_cc::general_work (int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) @@ -95,20 +165,32 @@ repeater_gardner_symbol_recovery_cc::general_work (int noutput_items, gr_complex *out = (gr_complex *) output_items[0]; int i=0, o=0; + gr_complex symbol, sample, nco; while((o < noutput_items) && (i < ninput_items[0])) { while((d_mu > 1.0) && (i < ninput_items[0])) { d_mu --; - d_dl[d_dl_index] = in[i]; - d_dl[d_dl_index + d_twice_sps] = in[i]; + d_phase += d_freq; + // Keep phase clamped and not walk to infinity + while(d_phase > M_TWOPI) + d_phase -= M_TWOPI; + while(d_phase < -M_TWOPI) + d_phase += M_TWOPI; + + nco = gr_expj(d_phase+d_theta); // get the NCO value for derotating the curr + symbol = in[i]; + sample = nco*symbol; // get the downconverted symbol + + d_dl[d_dl_index] = sample; + d_dl[d_dl_index + d_twice_sps] = sample; d_dl_index ++; d_dl_index = d_dl_index % d_twice_sps; i++; } - if(d_mu <= 1.0) { + if(i < ninput_items[0]) { float half_omega = d_omega / 2.0; int half_sps = (int) floorf(half_omega); float half_mu = d_mu + half_omega - (float) half_sps; @@ -116,6 +198,8 @@ repeater_gardner_symbol_recovery_cc::general_work (int noutput_items, half_mu -= 1.0; half_sps += 1; } + // at this point half_sps represents the whole part, and + // half_mu the fractional part, of the halfway mark. // locate two points, separated by half of one symbol time // interp_samp is (we hope) at the optimum sampling point gr_complex interp_samp_mid = d_interp->interpolate(&d_dl[ d_dl_index ], d_mu); @@ -130,9 +214,12 @@ repeater_gardner_symbol_recovery_cc::general_work (int noutput_items, 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 - - // printf("%f\t%f\t%f\n", symbol_error, d_mu, d_omega); +#if VERBOSE_GARDNER + printf("%f\t%f\t%f\n", symbol_error, d_mu, d_omega); +#endif d_mu += d_omega + d_gain_mu * symbol_error; // update mu based on loop error + + phase_error_tracking(interp_samp); out[o++] = interp_samp; } diff --git a/repeater/src/lib/repeater_gardner_symbol_recovery_cc.h b/repeater/src/lib/repeater_gardner_costas_cc.h similarity index 67% rename from repeater/src/lib/repeater_gardner_symbol_recovery_cc.h rename to repeater/src/lib/repeater_gardner_costas_cc.h index e5fe9f2..99649a8 100644 --- a/repeater/src/lib/repeater_gardner_symbol_recovery_cc.h +++ b/repeater/src/lib/repeater_gardner_costas_cc.h @@ -31,15 +31,15 @@ class gri_mmse_fir_interpolator_cc; -class repeater_gardner_symbol_recovery_cc; -typedef boost::shared_ptr repeater_gardner_symbol_recovery_cc_sptr; +class repeater_gardner_costas_cc; +typedef boost::shared_ptr repeater_gardner_costas_cc_sptr; // public constructor -repeater_gardner_symbol_recovery_cc_sptr -repeater_make_gardner_symbol_recovery_cc (float samples_per_symbol, float timing_error_gain); +repeater_gardner_costas_cc_sptr +repeater_make_gardner_costas_cc (float samples_per_symbol, float gain_mu, float gain_omega, float alpha, float beta, float max_freq, float min_freq); /*! - * \brief Gardner based repeater gardner_symbol_recovery block with complex input, complex output. + * \brief Gardner based repeater gardner_costas block with complex input, complex output. * \ingroup sync_blk * * This implements a Gardner discrete-time error-tracking synchronizer. @@ -48,10 +48,10 @@ repeater_make_gardner_symbol_recovery_cc (float samples_per_symbol, float timing * * includes some simplifying approximations KA1RBI */ -class repeater_gardner_symbol_recovery_cc : public gr_block +class repeater_gardner_costas_cc : public gr_block { public: - ~repeater_gardner_symbol_recovery_cc (); + ~repeater_gardner_costas_cc (); void forecast(int noutput_items, gr_vector_int &ninput_items_required); int general_work (int noutput_items, gr_vector_int &ninput_items, @@ -65,7 +65,7 @@ class repeater_gardner_symbol_recovery_cc : public gr_block protected: bool input_sample0(gr_complex, gr_complex& outp); bool input_sample(gr_complex, gr_complex& outp); - repeater_gardner_symbol_recovery_cc (float samples_per_symbol, float timing_error_gain); + repeater_gardner_costas_cc (float samples_per_symbol, float gain_mu, float gain_omega, float alpha, float beta, float max_freq, float min_freq); private: @@ -80,13 +80,24 @@ protected: gr_complex *d_dl; int d_dl_index; - float d_timing_error_gain; int d_twice_sps; float d_timing_error; - friend repeater_gardner_symbol_recovery_cc_sptr - repeater_make_gardner_symbol_recovery_cc (float samples_per_symbol, float timing_error_gain); + float d_alpha; + float d_beta; + uint32_t d_interp_counter; + + float d_theta; + float d_phase; + float d_freq; + float d_max_freq; + + friend repeater_gardner_costas_cc_sptr + repeater_make_gardner_costas_cc (float samples_per_symbol, float gain_mu, float gain_omega, float alpha, float beta, float max_freq, float min_freq); + + float phase_error_detector_qpsk(gr_complex sample); + void phase_error_tracking(gr_complex sample); }; diff --git a/repeater/src/lib/repeater_imbe_decode_fb.cc b/repeater/src/lib/repeater_imbe_decode_fb.cc deleted file mode 100644 index 38f940c..0000000 --- a/repeater/src/lib/repeater_imbe_decode_fb.cc +++ /dev/null @@ -1,251 +0,0 @@ -/* -*- 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. - */ -/* - * Copyright 2010, KA1RBI - */ -/* - * 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 -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Create a new instance of repeater_imbe_decode_fb and return - * a boost shared_ptr. This is effectively the public constructor. - */ -repeater_imbe_decode_fb_sptr -repeater_make_imbe_decode_fb () -{ - return repeater_imbe_decode_fb_sptr (new repeater_imbe_decode_fb ()); -} - -/* - * Specify constraints on number of input and output streams. - * This info is used to construct the input and output signatures - * (2nd & 3rd args to gr_block's constructor). The input and - * output signatures are used by the runtime system to - * check that a valid number and type of inputs and outputs - * are connected to this block. In this case, we accept - * only 1 input and 1 output. - */ -static const int MIN_IN = 1; // mininum number of input streams -static const int MAX_IN = 1; // maximum number of input streams -static const int MIN_OUT = 1; // minimum number of output streams -static const int MAX_OUT = 1; // maximum number of output streams - -/* - * The private constructor - */ -repeater_imbe_decode_fb::repeater_imbe_decode_fb () - : gr_block ("imbe_decode_fb", - gr_make_io_signature (MIN_IN, MAX_IN, sizeof (float)), - gr_make_io_signature (MIN_OUT, MAX_OUT, sizeof (unsigned char))), - reverse_p(0), - hdr_syms(0), - next_bit(0), - frame_body(P25_VOICE_FRAME_SIZE), - symbol_queue() -{ -} - -/* - * Our virtual destructor. - */ -repeater_imbe_decode_fb::~repeater_imbe_decode_fb () -{ - // nothing else required in this example -} - -/* - * Process the 64 bits after the frame sync, which should be the frame header - * 1. verify bch and reject if bch cannot be decoded - * 2. extract NAC and DUID - * Returns false if decode failure, else true - */ -bool repeater_imbe_decode_fb::header_codeword(uint64_t acc, uint32_t& nac, uint32_t& duid) { - bit_vector cw(64); - bool low = acc & 1; - // for bch, split bits into codeword vector - for (int i=0; i < 64; i++) { - acc >>= 1; - cw[i] = acc & 1; - } - - // do bch decode - int rc = bchDec(cw); - - // check if bch decode unsuccessful - if (rc < 0) { - return false; - } - - // load corrected bch bits into acc - acc = 0; - for (int i=63; i>=0; i--) { - acc |= cw[i]; - acc <<= 1; - } - acc |= low; // FIXME - - // extract nac and duid - nac = (acc >> 52) & 0xfff; - duid = (acc >> 48) & 0x00f; - hdr_word = acc; - printf ("nac %x duid %x errs %d\n", nac, duid, rc); - - return true; -} - -/* - * rx_sym: called once per received symbol - * 1. looks for flags sequences - * 2. after flags detected (hdr_syms > 0), accumulate 64-bit header word - * 3. check for voice frames (ldu1|2) - * 4. for ldu1|2 (next_bit > 0), accumulate all 1728 bits in frame - * 5. FIXME regenerate all fields in frame (flags, bch, hamming/golay, rs, pn) - * 6. place symbols into output symbol queue - */ -void repeater_imbe_decode_fb::rx_sym(uint8_t dibit) { - // FIXME assert(dibit >= 0 && dibit <= 3) - header_accum <<= 2; - header_accum |= dibit; - if (hdr_syms == 12) { - // ignore status dibit - header_accum >>= 2; - } else if (hdr_syms >= 33) { - // header completely received - bool bch_rc = header_codeword(header_accum, nac, duid); - // next_bit = 48 + 64 + 2; - if (bch_rc) { - next_bit = 48 + 64; - frame_size_limit = P25_VOICE_FRAME_SIZE; - } - hdr_syms = 0; - } - if (hdr_syms) - // count symbols in header - hdr_syms++; - if ((header_accum & P25_FRAME_SYNC_MASK) == P25_FRAME_SYNC_MAGIC) { - hdr_syms = 1; - } - if ((header_accum & P25_FRAME_SYNC_MASK) == P25_FRAME_SYNC_REV_P) { - reverse_p = 0x02; - hdr_syms = 1; - } - if (next_bit) { - frame_body[next_bit++] = (dibit >> 1) & 1; - frame_body[next_bit++] = dibit & 1; - } - // dispose of received frame (if exists) and: - // 1. complete frame is received, or - // 2. flags is received - if (next_bit && (next_bit >= frame_size_limit || hdr_syms == 1)) { - // dump_cw(frame_body, 216, stdout); - if (duid == 0x5 || duid == 0xa) { // if voice - ldu1 or ldu2 - for(size_t i = 0; i < nof_voice_codewords; ++i) { - voice_codeword cw(voice_codeword_sz); - uint32_t E0, ET; - uint32_t u[8]; - char s[128]; - imbe_deinterleave(frame_body, cw, i); - imbe_header_decode(cw, u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], E0, ET); - sprintf(s, "%x %x %x %x %x %x %x %x\n", u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7]); - for (size_t j=0; j < strlen(s); j++) { - symbol_queue.push_back(s[j]); - } - } - } - next_bit = 0; - } -} - -/* - * Slicer for normalized baseband FSK4 - */ -static inline uint8_t -slice (float s) { - uint8_t dibit; - if(s < -2.0) { - dibit = 3; - } else if(s < 0.0) { - dibit = 2; - } else if(s < 2.0) { - dibit = 0; - } else { - dibit = 1; - } - return dibit; -} - -void -repeater_imbe_decode_fb::forecast(int nof_output_items, gr_vector_int &nof_input_items_reqd) -{ - /* input rate: 4800, output rate: 1550 (31 * 50) */ - const size_t nof_inputs = nof_input_items_reqd.size(); - const int nof_samples_reqd = 3.1 * nof_output_items; - std::fill(&nof_input_items_reqd[0], &nof_input_items_reqd[nof_inputs], nof_samples_reqd); -} - - -int -repeater_imbe_decode_fb::general_work (int noutput_items, - gr_vector_int &ninput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - - const float *in = (const float *) input_items[0]; - unsigned char *out = (unsigned char *) output_items[0]; - - for (int i = 0; i < noutput_items; i++){ - rx_sym(slice(in[i]) ^ reverse_p); - } - int amt_produce = 0; - amt_produce = noutput_items; - if (amt_produce > (int)symbol_queue.size()) - amt_produce = symbol_queue.size(); - if (amt_produce > 0) { - copy(symbol_queue.begin(), symbol_queue.begin() + amt_produce, out); - symbol_queue.erase(symbol_queue.begin(), symbol_queue.begin() + amt_produce); - } - // printf ("work: ninp[0]: %d nout: %d size %d produce: %d surplus %d\n", ninput_items[0], noutput_items, symbol_queue.size(), amt_produce, surplus); - consume_each(noutput_items); - // Tell runtime system how many output items we produced. - return amt_produce; -} diff --git a/repeater/src/lib/repeater_p25_frame_assembler.cc b/repeater/src/lib/repeater_p25_frame_assembler.cc new file mode 100644 index 0000000..f3c4163 --- /dev/null +++ b/repeater/src/lib/repeater_p25_frame_assembler.cc @@ -0,0 +1,221 @@ +/* -*- 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. + */ +/* + * Copyright 2010, KA1RBI + */ +/* + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Create a new instance of repeater_p25_frame_assembler and return + * a boost shared_ptr. This is effectively the public constructor. + */ +repeater_p25_frame_assembler_sptr +repeater_make_p25_frame_assembler (const char* udp_host, int port, int debug, bool do_imbe, bool do_output, bool do_msgq, gr_msg_queue_sptr queue) +{ + return repeater_p25_frame_assembler_sptr (new repeater_p25_frame_assembler (udp_host, port, debug, do_imbe, do_output, do_msgq, queue)); +} + +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 + */ +repeater_p25_frame_assembler::repeater_p25_frame_assembler (const char* udp_host, int port, int debug, bool do_imbe, bool do_output, bool do_msgq, gr_msg_queue_sptr queue) + : gr_block ("p25_frame_assembler", + gr_make_io_signature (MIN_IN, MAX_IN, sizeof (char)), + gr_make_io_signature ((do_output) ? 1 : 0, (do_output) ? 1 : 0, (do_output) ? sizeof(char) : 0 )), + write_bufp(0), + write_sock(0), + d_udp_host(udp_host), + d_port(port), + d_debug(debug), + d_do_imbe(do_imbe), + d_do_output(do_output), + d_msg_queue(queue), + symbol_queue(), + framer(new p25_framer()) +{ + if (port > 0) + init_sock(d_udp_host, d_port); +} + +repeater_p25_frame_assembler::~repeater_p25_frame_assembler () +{ + if (write_sock > 0) + close(write_sock); + delete framer; +} + +void +repeater_p25_frame_assembler::forecast(int nof_output_items, gr_vector_int &nof_input_items_reqd) +{ + // for do_imbe=false: we output packed bytes (4:1 ratio) + // for do_imbe=true: input rate= 4800, output rate= 1600 = 32 * 50 (3:1) + const size_t nof_inputs = nof_input_items_reqd.size(); + int nof_samples_reqd = 4.0 * nof_output_items; + if (d_do_imbe) + nof_samples_reqd = 3.0 * nof_output_items; + std::fill(&nof_input_items_reqd[0], &nof_input_items_reqd[nof_inputs], nof_samples_reqd); +} + +int +repeater_p25_frame_assembler::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]; + 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) { + 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); + break; + case 0x05: // LDU 1 + ProcLDU1(framer->frame_body); + break; + case 0x0a: // LDU 2 + ProcLDU2(framer->frame_body); + break; + case 0x0f: // LDU 2 + ProcTDU(framer->frame_body); + break; + } + } + if (d_do_imbe && (framer->duid == 0x5 || framer->duid == 0xa)) { // if voice - ldu1 or ldu2 + for(size_t i = 0; i < nof_voice_codewords; ++i) { + voice_codeword cw(voice_codeword_sz); + uint32_t E0, ET; + uint32_t u[8]; + char s[128]; + imbe_deinterleave(framer->frame_body, cw, i); + // recover 88-bit IMBE voice code word + imbe_header_decode(cw, u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], E0, ET); + // output one 32-byte msg per 0.020 sec. + // also, 32*9 = 288 byte pkts (for use via UDP) + sprintf(s, "%03x %03x %03x %03x %03x %03x %03x %03x\n", u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7]); + if (d_do_output) { + for (size_t j=0; j < strlen(s); j++) { + symbol_queue.push_back(s[j]); + } + } + if (write_sock > 0) { + memcpy(&write_buf[write_bufp], s, strlen(s)); + write_bufp += strlen(s); + if (write_bufp >= 288) { // 9 * 32 = 288 + sendto(write_sock, write_buf, 288, 0, (struct sockaddr *)&write_sock_addr, sizeof(write_sock_addr)); + // FIXME check sendto() rc + write_bufp = 0; + } + } + } + } // end of imbe/voice + if (!d_do_imbe) { + // pack the bits into bytes, MSB first + size_t obuf_ct = 0; + uint8_t obuf[P25_VOICE_FRAME_SIZE/2]; + for (uint32_t i = 0; i < framer->frame_size; i += 8) { + uint8_t b = + (framer->frame_body[i+0] << 7) + + (framer->frame_body[i+1] << 6) + + (framer->frame_body[i+2] << 5) + + (framer->frame_body[i+3] << 4) + + (framer->frame_body[i+4] << 3) + + (framer->frame_body[i+5] << 2) + + (framer->frame_body[i+6] << 1) + + (framer->frame_body[i+7] ); + obuf[obuf_ct++] = b; + } + if (write_sock > 0) { + sendto(write_sock, obuf, obuf_ct, 0, (struct sockaddr*)&write_sock_addr, sizeof(write_sock_addr)); + } + if (d_do_output) { + for (size_t j=0; j < obuf_ct; j++) { + symbol_queue.push_back(obuf[j]); + } + } + } + } // end of complete frame + } + int amt_produce = 0; + amt_produce = noutput_items; + if (amt_produce > (int)symbol_queue.size()) + amt_produce = symbol_queue.size(); + if (amt_produce > 0) { + copy(symbol_queue.begin(), symbol_queue.begin() + amt_produce, out); + symbol_queue.erase(symbol_queue.begin(), symbol_queue.begin() + amt_produce); + } + // printf ("work: ninp[0]: %d nout: %d size %d produce: %d surplus %d\n", ninput_items[0], noutput_items, symbol_queue.size(), amt_produce, surplus); + consume_each(noutput_items); + // Tell runtime system how many output items we produced. + return amt_produce; +} + +void repeater_p25_frame_assembler::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); +} diff --git a/repeater/src/lib/repeater_imbe_decode_fb.h b/repeater/src/lib/repeater_p25_frame_assembler.h similarity index 57% rename from repeater/src/lib/repeater_imbe_decode_fb.h rename to repeater/src/lib/repeater_p25_frame_assembler.h index 12d4cc7..002c5f7 100644 --- a/repeater/src/lib/repeater_imbe_decode_fb.h +++ b/repeater/src/lib/repeater_p25_frame_assembler.h @@ -19,13 +19,18 @@ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ -#ifndef INCLUDED_REPEATER_IMBE_DECODE_H -#define INCLUDED_REPEATER_IMBE_DECODE_H +#ifndef INCLUDED_REPEATER_P25_FRAME_ASSEMBLER_H +#define INCLUDED_REPEATER_P25_FRAME_ASSEMBLER_H #include +#include +#include +#include +#include #include -class repeater_imbe_decode_fb; +class repeater_p25_frame_assembler; +class p25_framer; /* * We use boost::shared_ptr's instead of raw pointers for all access @@ -38,17 +43,17 @@ class repeater_imbe_decode_fb; * * As a convention, the _sptr suffix indicates a boost::shared_ptr */ -typedef boost::shared_ptr repeater_imbe_decode_fb_sptr; +typedef boost::shared_ptr repeater_p25_frame_assembler_sptr; typedef std::deque dibit_queue; /*! - * \brief Return a shared_ptr to a new instance of repeater_imbe_decode_fb. + * \brief Return a shared_ptr to a new instance of repeater_p25_frame_assembler. * - * To avoid accidental use of raw pointers, repeater_imbe_decode_fb's - * constructor is private. repeater_make_imbe_decode_fb is the public + * To avoid accidental use of raw pointers, repeater_p25_frame_assembler's + * constructor is private. repeater_make_p25_frame_assembler is the public * interface for creating new instances. */ -repeater_imbe_decode_fb_sptr repeater_make_imbe_decode_fb (); +repeater_p25_frame_assembler_sptr repeater_make_p25_frame_assembler (const char* udp_host, int port, int debug, bool do_imbe, bool do_output, bool do_msgq, gr_msg_queue_sptr queue); /*! * \brief produce a stream of dibits, given a stream of floats in [-3,-1,1,3] @@ -57,36 +62,39 @@ repeater_imbe_decode_fb_sptr repeater_make_imbe_decode_fb (); * This uses the preferred technique: subclassing gr_sync_block. */ -class repeater_imbe_decode_fb : public gr_block +class repeater_p25_frame_assembler : public gr_block { private: - // The friend declaration allows repeater_make_imbe_decode_fb to + // The friend declaration allows repeater_make_p25_frame_assembler to // access the private constructor. - friend repeater_imbe_decode_fb_sptr repeater_make_imbe_decode_fb (); + void init_sock(const char* udp_host, int udp_port); - repeater_imbe_decode_fb (); // private constructor + friend repeater_p25_frame_assembler_sptr repeater_make_p25_frame_assembler (const char* udp_host, int port, int debug, bool do_imbe, bool do_output, bool do_msgq, gr_msg_queue_sptr queue); + + repeater_p25_frame_assembler (const char* udp_host, int port, int debug, bool do_imbe, bool do_output, bool do_msgq, gr_msg_queue_sptr queue); // private constructor // internal functions typedef std::vector bit_vector; bool header_codeword(uint64_t acc, uint32_t& nac, uint32_t& duid); void proc_voice_unit(bit_vector& frame_body) ; - void rx_sym(uint8_t dibit) ; // internal instance variables and state - int reverse_p; - int hdr_syms; - uint32_t next_bit; - uint64_t hdr_word; - bit_vector frame_body; + int write_bufp; + int write_sock; + struct sockaddr_in write_sock_addr; + char write_buf[512]; + const char* d_udp_host; + int d_port; + int d_debug; + bool d_do_imbe; + bool d_do_output; + bool d_do_msgq; + gr_msg_queue_sptr d_msg_queue; dibit_queue symbol_queue; - - uint64_t header_accum; - uint32_t nac; - uint32_t duid; - uint32_t frame_size_limit; + p25_framer* framer; public: virtual void forecast(int nof_output_items, gr_vector_int &nof_input_items_reqd); - ~repeater_imbe_decode_fb (); // public destructor + ~repeater_p25_frame_assembler (); // public destructor // Where all the action really happens @@ -97,4 +105,4 @@ private: }; -#endif /* INCLUDED_REPEATER_IMBE_DECODE_H */ +#endif /* INCLUDED_REPEATER_P25_FRAME_ASSEMBLER_H */ diff --git a/repeater/src/lib/repeater_p25_frame_fb.cc b/repeater/src/lib/repeater_p25_frame_fb.cc deleted file mode 100644 index bbadd66..0000000 --- a/repeater/src/lib/repeater_p25_frame_fb.cc +++ /dev/null @@ -1,332 +0,0 @@ -/* -*- 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. - */ -/* - * Copyright 2010, KA1RBI - */ -/* - * 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 -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Create a new instance of repeater_p25_frame_fb and return - * a boost shared_ptr. This is effectively the public constructor. - */ -repeater_p25_frame_fb_sptr -repeater_make_p25_frame_fb () -{ - return repeater_p25_frame_fb_sptr (new repeater_p25_frame_fb ()); -} - -/* - * Specify constraints on number of input and output streams. - * This info is used to construct the input and output signatures - * (2nd & 3rd args to gr_block's constructor). The input and - * output signatures are used by the runtime system to - * check that a valid number and type of inputs and outputs - * are connected to this block. In this case, we accept - * only 1 input and 1 output. - */ -static const int MIN_IN = 1; // mininum number of input streams -static const int MAX_IN = 1; // maximum number of input streams -static const int MIN_OUT = 1; // minimum number of output streams -static const int MAX_OUT = 1; // maximum number of output streams - -/* - * The private constructor - */ -repeater_p25_frame_fb::repeater_p25_frame_fb () - : gr_block ("p25_frame_fb", - gr_make_io_signature (MIN_IN, MAX_IN, sizeof (float)), - gr_make_io_signature (MIN_OUT, MAX_OUT, sizeof (unsigned char))), - reverse_p(0), - hdr_syms(0), - next_bit(0), - frame_body(P25_VOICE_FRAME_SIZE), - symbol_queue(), - surplus(-864), - frame_size_limit(0), - last_sym_ct(0), - closed_repeater(1), - stats_sym_ct(0), - stats_bad_bch(0), - stats_good_bch(0), - stats_bch_errs(0), - stats_e0_errs(0), - stats_et_errs(0) -{ - set_history(864); // always behind by sizeof(voice data unit) - memset(stats_frames_received, 0, sizeof(stats_frames_received)); - memset(closed_nac_list, 0, sizeof(closed_nac_list)); - memset(stats_frame_history, 0, sizeof(stats_frame_history)); -} - -/* - * Our virtual destructor. - */ -repeater_p25_frame_fb::~repeater_p25_frame_fb () -{ - // nothing else required in this example -} - -static const int max_frame_lengths[16] = { - 792, // 0 - pdu - 0, 0, // 1, 2 - undef - 144, // 3 - tdu - 0, // 4 - undef - P25_VOICE_FRAME_SIZE, // 5 - ldu1 - 0, // 6 - undef - 0, // 7 - tsbk - 0, // 8 - undef - 0, // 9 - VSELP "voice PDU" - P25_VOICE_FRAME_SIZE, // a - ldu2 - 0, // b - undef - 0, // c - VSELP "voice PDU" - 0, 0, // d, e - undef - 432 // f - tdu -}; - -/* - * Record stats. If received NAC matches a previous entry, use it; - * otherwise overlay least-recent non-matching history entry (unless an empty - * one exists) - */ -void repeater_p25_frame_fb::record_frame(uint32_t nac, uint32_t duid) { - struct timeval tv; - struct timezone tz; - time_t t0=0; - suseconds_t u0=0; - int least_recent = 0; - int emptyp = -1; - int i; - - stats_frames_received[duid]++; - - gettimeofday(&tv, &tz); - for (i=0; i < FRAME_HISTORY_SIZE; i++) { - if (stats_frame_history[i].nac == nac) { - stats_frame_history[i].sec = tv.tv_sec; - stats_frame_history[i].usec = tv.tv_usec; - stats_frame_history[i].frame_ctr++; - return; - } - if (stats_frame_history[i].nac == 0) { - emptyp = i; - } - if (t0 == 0 || stats_frame_history[i].sec < t0 || - (stats_frame_history[i].sec == t0 && - stats_frame_history[i].usec < u0)) { - t0 = stats_frame_history[i].sec; - u0 = stats_frame_history[i].usec; - least_recent = i; - } - } - if (emptyp >= 0) - i = emptyp; - else - i = least_recent; - stats_frame_history[i].nac = nac; - stats_frame_history[i].sec = tv.tv_sec; - stats_frame_history[i].usec = tv.tv_usec; - stats_frame_history[i].frame_ctr++; -} - -/* - * Process the 64 bits after the frame sync, which should be the frame header - * 1. verify bch and reject if bch cannot be decoded - * 2. extract NAC and DUID - * Returns false if decode failure, else true - */ -bool repeater_p25_frame_fb::header_codeword(uint64_t acc, uint32_t& nac, uint32_t& duid) { - bit_vector cw(64); - bool low = acc & 1; - // for bch, split bits into codeword vector - for (int i=0; i < 64; i++) { - acc >>= 1; - cw[i] = acc & 1; - } - - // do bch decode - int rc = bchDec(cw); - - // check if bch decode unsuccessful - if (rc < 0) { - stats_bad_bch ++; - return false; - } - stats_good_bch ++; - if (rc > 0) { - // FIXME: stats_bch_err_counters[rc] ++; - stats_bch_errs ++; - } - - // load corrected bch bits into acc - acc = 0; - for (int i=63; i>=0; i--) { - acc |= cw[i]; - acc <<= 1; - } - acc |= low; // FIXME - - // extract nac and duid - nac = (acc >> 52) & 0xfff; - duid = (acc >> 48) & 0x00f; - hdr_word = acc; - record_frame(nac, duid); - printf ("nac %x duid %x errs %d sym %llu [%llu]\n", nac, duid, rc, stats_sym_ct, stats_sym_ct - last_sym_ct); - last_sym_ct = stats_sym_ct; - - return true; -} - -void repeater_p25_frame_fb::rebuild_frame(bit_vector& frame_body){ - if (duid == 0x5 || duid == 0xa) { - imbe_regenerate_voice_unit(frame_body); - } - // FIXME: handle other frame types -} - -/* - * rx_sym: called once per received symbol - * 1. looks for flags sequences - * 2. after flags detected (hdr_syms > 0), accumulate 64-bit header word - * 3. check for voice frames (ldu1|2) - * 4. for ldu1|2 (next_bit > 0), accumulate all 1728 bits in frame - * 5. FIXME regenerate all fields in frame (flags, bch, hamming/golay, rs, pn) - * 6. place symbols into output symbol queue - */ -void repeater_p25_frame_fb::rx_sym(uint8_t dibit) { - // FIXME assert(dibit >= 0 && dibit <= 3) - header_accum <<= 2; - header_accum |= dibit; - if (hdr_syms == 12) { - // ignore status dibit - header_accum >>= 2; - } else if (hdr_syms >= 33) { - // header completely received - bool bch_rc = header_codeword(header_accum, nac, duid); - // next_bit = 48 + 64 + 2; - if (bch_rc) { - next_bit = 48 + 64; - frame_size_limit = max_frame_lengths[duid]; - if (frame_size_limit <= next_bit) - frame_size_limit = P25_VOICE_FRAME_SIZE; - } - hdr_syms = 0; - } - if (hdr_syms) - // count symbols in header - hdr_syms++; - if ((header_accum & P25_FRAME_SYNC_MASK) == P25_FRAME_SYNC_MAGIC) { - hdr_syms = 1; - } - if ((header_accum & P25_FRAME_SYNC_MASK) == P25_FRAME_SYNC_REV_P) { - reverse_p = 0x02; - hdr_syms = 1; - } - if (next_bit) { - frame_body[next_bit++] = (dibit >> 1) & 1; - frame_body[next_bit++] = dibit & 1; - } - // dispose of received frame (if exists) and: - // 1. complete frame is received, or - // 2. flags is received - if (next_bit && (next_bit >= frame_size_limit || hdr_syms == 1)) { - // dump_cw(frame_body, 216, stdout); - rebuild_frame(frame_body); - p25_setup_frame_header(frame_body, hdr_word); - for (uint32_t i = 0; i < next_bit; i += 2) { - uint8_t new_dibit = (frame_body[i] << 1) + - frame_body[i+1]; - symbol_queue.push_back(new_dibit); - } - next_bit = 0; - } - ++stats_sym_ct; -} - -/* - * Slicer for normalized baseband FSK4 - */ -static inline uint8_t -slice (float s) { - uint8_t dibit; - if(s < -2.0) { - dibit = 3; - } else if(s < 0.0) { - dibit = 2; - } else if(s < 2.0) { - dibit = 0; - } else { - dibit = 1; - } - return dibit; -} - -int -repeater_p25_frame_fb::general_work (int noutput_items, - gr_vector_int &ninput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - - const float *in = (const float *) input_items[0]; - unsigned char *out = (unsigned char *) output_items[0]; - - for (int i = 0; i < noutput_items; i++){ - rx_sym(slice(in[i]) ^ reverse_p); - surplus++; - } - int amt_produce = 0; - if (surplus > 0) - amt_produce = surplus; - if (amt_produce > noutput_items) - amt_produce = noutput_items; - if (amt_produce > (int)symbol_queue.size()) - amt_produce = symbol_queue.size(); - if (amt_produce > 0) { - copy(symbol_queue.begin(), symbol_queue.begin() + amt_produce, out); - symbol_queue.erase(symbol_queue.begin(), symbol_queue.begin() + amt_produce); - surplus -= amt_produce; - } - // printf ("work: ninp[0]: %d nout: %d size %d produce: %d surplus %d\n", ninput_items[0], noutput_items, symbol_queue.size(), amt_produce, surplus); - consume_each(noutput_items); - // Tell runtime system how many output items we produced. - return amt_produce; -} diff --git a/repeater/src/lib/repeater_p25_frame_fb.h b/repeater/src/lib/repeater_p25_frame_fb.h deleted file mode 100644 index 2ed4326..0000000 --- a/repeater/src/lib/repeater_p25_frame_fb.h +++ /dev/null @@ -1,124 +0,0 @@ -/* -*- 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_P25_FRAME_H -#define INCLUDED_REPEATER_P25_FRAME_H - -#include -#include - -class repeater_p25_frame_fb; - -/* - * 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_p25_frame_fb_sptr; -typedef std::deque dibit_queue; - -/*! - * \brief Return a shared_ptr to a new instance of repeater_p25_frame_fb. - * - * To avoid accidental use of raw pointers, repeater_p25_frame_fb's - * constructor is private. repeater_make_p25_frame_fb is the public - * interface for creating new instances. - */ -repeater_p25_frame_fb_sptr repeater_make_p25_frame_fb (); - -/*! - * \brief produce a stream of dibits, given a stream of floats in [-3,-1,1,3] - * \ingroup block - * - * This uses the preferred technique: subclassing gr_sync_block. - */ - -struct _stats_frame_history { - uint32_t nac; - time_t sec; - suseconds_t usec; - uint64_t frame_ctr; -}; - -static const int FRAME_HISTORY_SIZE=5; -static const int MAX_NAC_LIST=5; - -class repeater_p25_frame_fb : public gr_block -{ -private: - // The friend declaration allows repeater_make_p25_frame_fb to - // access the private constructor. - - friend repeater_p25_frame_fb_sptr repeater_make_p25_frame_fb (); - - repeater_p25_frame_fb (); // private constructor - // internal functions - typedef std::vector bit_vector; - bool header_codeword(uint64_t acc, uint32_t& nac, uint32_t& duid); - void rebuild_frame(bit_vector& frame_body) ; - void proc_voice_unit(bit_vector& frame_body) ; - void rx_sym(uint8_t dibit) ; - void record_frame(uint32_t, uint32_t); - // internal instance variables and state - int reverse_p; - int hdr_syms; - uint32_t next_bit; - uint64_t hdr_word; - bit_vector frame_body; - dibit_queue symbol_queue; - int32_t surplus; - uint64_t header_accum; - uint32_t nac; - uint32_t duid; - uint32_t frame_size_limit; - uint64_t last_sym_ct; - uint32_t closed_nac_list[MAX_NAC_LIST]; - bool closed_repeater; - - // stats - uint64_t stats_sym_ct; - uint64_t stats_bad_bch; - uint64_t stats_good_bch; - uint64_t stats_bch_errs; - uint64_t stats_e0_errs; - uint64_t stats_et_errs; - uint64_t stats_frames_received[16]; - struct _stats_frame_history stats_frame_history[FRAME_HISTORY_SIZE]; - - public: - ~repeater_p25_frame_fb (); // 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_P25_FRAME_H */ diff --git a/repeater/src/lib/rs.cc b/repeater/src/lib/rs.cc new file mode 100644 index 0000000..5ae3785 --- /dev/null +++ b/repeater/src/lib/rs.cc @@ -0,0 +1,567 @@ + +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +/* + * Convert bit vector to hex dump format and print + */ +static inline void +dump_cw(const_bit_vector cw, int len, FILE* fp) // len in bytes +{ + int i, j; + for (i = 0; i < len; i++){ + int p = 0; + for (j = 0; j < 8; j++){ + p = (p << 1) + cw[ i*8 + j ]; + } + fprintf(fp, "%02x ", p); + if (!((i+1) % 16)) + fprintf(fp, "\n"); + } + fprintf(fp, "\n"); +} +#endif // DEBUG + +// this table used for HDU and also for TDU codeword mapping. +// FIXME: possible dup of GOLAY_CODEWORDS in hdu.cc ? +static const uint16_t hdu_codeword_bits[658] = { // 329 symbols = 324 + 5 pad + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, + 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, + 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 360, 361, + 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, + 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, + 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, + 426, 427, 428, 429, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, + 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, + 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, + 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, + 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 504, 505, 506, 507, 508, 509, + 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, + 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, + 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, + 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, + 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, + 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, + 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, + 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, + 640, 641, 642, 643, 644, 645, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, + 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, + 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, + 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 720, 721, 722, 723, + 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, + 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, + 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, + 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, + 788, 789 }; + +static const uint32_t gly23127DecTbl[2048] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 147459, + 1, 2, 2, 3, 2, 3, 3, 4268035, 2, 3, 3, 1574915, 3, 2097155, 294915, 4099, + 1, 2, 2, 3, 2, 3, 3, 147459, 2, 3, 3, 147459, 3, 147459, 147459, 147458, + 2, 3, 3, 32771, 3, 2051, 3149827, 786435, 3, 274435, 4194307, 2162691, 589827, 5275651, 10243, 147459, + 1, 2, 2, 3, 2, 3, 3, 2621443, 2, 3, 3, 8195, 3, 1118211, 294915, 4196355, + 2, 3, 3, 135171, 3, 2051, 294915, 1064963, 3, 4210691, 294915, 2162691, 294915, 663555, 294914, 294915, + 2, 3, 3, 5505027, 3, 2051, 65539, 45059, 3, 557059, 6147, 2162691, 6299651, 262147, 1572867, 147459, + 3, 2051, 548867, 2162691, 2051, 2050, 4325379, 2051, 1179651, 2162691, 2162691, 2162690, 20483, 2051, 294915, 2162691, + 1, 2, 2, 3, 2, 3, 3, 2621443, 2, 3, 3, 327683, 3, 43011, 5242883, 4099, + 2, 3, 3, 32771, 3, 1441795, 18435, 4099, 3, 4210691, 2236419, 4099, 589827, 4099, 4099, 4098, + 2, 3, 3, 32771, 3, 4198403, 270339, 1116163, 3, 3145731, 6147, 4726787, 589827, 262147, 2129923, 147459, + 3, 32771, 32771, 32770, 589827, 2121731, 4325379, 32771, 589827, 133123, 1327107, 32771, 589826, 589827, 589827, 4099, + 2, 3, 3, 2621443, 3, 2621443, 2621443, 2621442, 3, 4210691, 6147, 1212419, 131075, 262147, 90115, 2621443, + 3, 4210691, 1114115, 272387, 12291, 98307, 4325379, 2621443, 4210691, 4210690, 524291, 4210691, 3147779, 4210691, 294915, 4099, + 3, 204803, 6147, 16387, 1097731, 262147, 4325379, 2621443, 6147, 262147, 6146, 6147, 262147, 262146, 6147, 262147, + 2359299, 1576963, 4325379, 32771, 4325379, 2051, 4325378, 4325379, 40963, 4210691, 6147, 2162691, 589827, 262147, 4325379, 1056771, + 1, 2, 2, 3, 2, 3, 3, 268291, 2, 3, 3, 8195, 3, 2097155, 5242883, 622595, + 2, 3, 3, 32771, 3, 2097155, 655363, 1064963, 3, 2097155, 86019, 4587523, 2097155, 2097154, 10243, 2097155, + 2, 3, 3, 32771, 3, 1581059, 65539, 6291459, 3, 4261891, 2883587, 1052675, 36867, 262147, 10243, 147459, + 3, 32771, 32771, 32770, 4472835, 200707, 10243, 32771, 1179651, 540675, 10243, 32771, 10243, 2097155, 10242, 10243, + 2, 3, 3, 8195, 3, 4358147, 65539, 1064963, 3, 8195, 8195, 8194, 542723, 262147, 2232323, 8195, + 3, 851971, 6293507, 1064963, 12291, 1064963, 1064963, 1064962, 1179651, 38915, 524291, 8195, 4259843, 2097155, 294915, 1064963, + 3, 2117635, 65539, 657411, 65539, 262147, 65538, 65539, 1179651, 262147, 4243459, 8195, 262147, 262146, 65539, 262147, + 1179651, 4202499, 266243, 32771, 2654211, 2051, 65539, 1064963, 1179650, 1179651, 1179651, 2162691, 1179651, 262147, 10243, 4722691, + 2, 3, 3, 32771, 3, 81923, 5242883, 139267, 3, 659459, 5242883, 2115587, 5242883, 262147, 5242882, 5242883, + 3, 32771, 32771, 32770, 12291, 4720643, 2424835, 32771, 264195, 1122307, 524291, 32771, 180227, 2097155, 5242883, 4099, + 3, 32771, 32771, 32770, 2230275, 262147, 544771, 32771, 24579, 262147, 196611, 32771, 262147, 262146, 5242883, 262147, + 32771, 32770, 32770, 32769, 1048579, 32771, 32771, 32770, 6295555, 32771, 32771, 32770, 589827, 262147, 10243, 32771, + 3, 1050627, 409603, 4263939, 12291, 262147, 34819, 2621443, 2195459, 262147, 524291, 8195, 262147, 262146, 5242883, 262147, + 12291, 2228227, 524291, 32771, 12290, 12291, 12291, 1064963, 524291, 4210691, 524290, 524291, 12291, 262147, 524291, 198659, + 4718595, 262147, 3153923, 32771, 262147, 262146, 65539, 262147, 262147, 262146, 6147, 262147, 262146, 262145, 262147, 262146, + 83971, 32771, 32771, 32770, 12291, 262147, 4325379, 32771, 1179651, 262147, 524291, 32771, 262147, 262146, 2113539, 262147, + 1, 2, 2, 3, 2, 3, 3, 1081347, 2, 3, 3, 327683, 3, 2097155, 536579, 4196355, + 2, 3, 3, 135171, 3, 2097155, 18435, 786435, 3, 2097155, 4194307, 57347, 2097155, 2097154, 1245187, 2097155, + 2, 3, 3, 2107395, 3, 4198403, 65539, 786435, 3, 557059, 4194307, 1052675, 1312771, 73731, 2129923, 147459, + 3, 1130499, 4194307, 786435, 172035, 786435, 786435, 786434, 4194307, 133123, 4194306, 4194307, 20483, 2097155, 4194307, 786435, + 2, 3, 3, 135171, 3, 286723, 65539, 4196355, 3, 557059, 3162115, 4196355, 131075, 4196355, 4196355, 4196354, + 3, 135171, 135171, 135170, 5767171, 98307, 2105347, 135171, 75779, 1310723, 524291, 135171, 20483, 2097155, 294915, 4196355, + 3, 557059, 65539, 16387, 65539, 3276803, 65538, 65539, 557059, 557058, 401411, 557059, 20483, 557059, 65539, 4196355, + 2359299, 4202499, 1083395, 135171, 20483, 2051, 65539, 786435, 20483, 557059, 4194307, 2162691, 20482, 20483, 20483, 1056771, + 2, 3, 3, 327683, 3, 4198403, 18435, 139267, 3, 327683, 327683, 327682, 131075, 1589251, 2129923, 327683, + 3, 532483, 18435, 7340035, 18435, 98307, 18434, 18435, 1085443, 133123, 524291, 327683, 4464643, 2097155, 18435, 4099, + 3, 4198403, 1703939, 16387, 4198403, 4198402, 2129923, 4198403, 24579, 133123, 2129923, 327683, 2129923, 4198403, 2129922, 2129923, + 2359299, 133123, 77827, 32771, 1048579, 4198403, 18435, 786435, 133123, 133122, 4194307, 133123, 589827, 133123, 2129923, 1056771, + 3, 1050627, 4235267, 16387, 131075, 98307, 1314819, 2621443, 131075, 2109443, 524291, 327683, 131074, 131075, 131075, 4196355, + 2359299, 98307, 524291, 135171, 98307, 98306, 18435, 98307, 524291, 4210691, 524290, 524291, 131075, 98307, 524291, 1056771, + 2359299, 16387, 16387, 16386, 534531, 4198403, 65539, 16387, 5308419, 557059, 6147, 16387, 131075, 262147, 2129923, 1056771, + 2359298, 2359299, 2359299, 16387, 2359299, 98307, 4325379, 1056771, 2359299, 133123, 524291, 1056771, 20483, 1056771, 1056771, 1056770, + 2, 3, 3, 4734979, 3, 2097155, 65539, 139267, 3, 2097155, 165891, 1052675, 2097155, 2097154, 278531, 2097155, + 3, 2097155, 1318915, 67587, 2097155, 2097154, 4231171, 2097155, 2097155, 2097154, 524291, 2097155, 2097154, 2097153, 2097155, 2097154, + 3, 393219, 65539, 1052675, 65539, 51203, 65538, 65539, 24579, 1052675, 1052675, 1052674, 4849667, 2097155, 65539, 1052675, + 530435, 4202499, 2244611, 32771, 1048579, 2097155, 65539, 786435, 360451, 2097155, 4194307, 1052675, 2097155, 2097154, 10243, 2097155, + 3, 1050627, 65539, 2392067, 65539, 528387, 65538, 65539, 4460547, 212995, 524291, 8195, 1089539, 2097155, 65539, 4196355, + 49155, 4202499, 524291, 135171, 395267, 2097155, 65539, 1064963, 524291, 2097155, 524290, 524291, 2097155, 2097154, 524291, 2097155, + 65539, 4202499, 65538, 65539, 65538, 65539, 65537, 65538, 2099203, 557059, 65539, 1052675, 65539, 262147, 65538, 65539, + 4202499, 4202498, 65539, 4202499, 65539, 4202499, 65538, 65539, 1179651, 4202499, 524291, 280579, 20483, 2097155, 65539, 163843, + 3, 1050627, 2101251, 139267, 819203, 139267, 139267, 139266, 24579, 4227075, 524291, 327683, 71683, 2097155, 5242883, 139267, + 4390915, 282627, 524291, 32771, 1048579, 2097155, 18435, 139267, 524291, 2097155, 524290, 524291, 2097155, 2097154, 524291, 2097155, + 24579, 2686979, 4458499, 32771, 1048579, 4198403, 65539, 139267, 24578, 24579, 24579, 1052675, 24579, 262147, 2129923, 526339, + 1048579, 32771, 32771, 32770, 1048578, 1048579, 1048579, 32771, 24579, 133123, 524291, 32771, 1048579, 2097155, 397315, 4276227, + 1050627, 1050626, 524291, 1050627, 6307843, 1050627, 65539, 139267, 524291, 1050627, 524290, 524291, 131075, 262147, 524291, 53251, + 524291, 1050627, 524290, 524291, 12291, 98307, 524291, 4456451, 524290, 524291, 524289, 524290, 524291, 2097155, 524290, 524291, + 167939, 1050627, 65539, 16387, 65539, 262147, 65538, 65539, 24579, 262147, 524291, 6422531, 262147, 262146, 65539, 262147, + 2359299, 4202499, 524291, 32771, 1048579, 671747, 65539, 2103299, 524291, 69635, 524290, 524291, 4229123, 262147, 524291, 1056771, + 1, 2, 2, 3, 2, 3, 3, 1081347, 2, 3, 3, 8195, 3, 4980739, 2164739, 4099, + 2, 3, 3, 2375683, 3, 2051, 655363, 4099, 3, 229379, 4194307, 4099, 1073155, 4099, 4099, 4098, + 2, 3, 3, 593923, 3, 2051, 270339, 6291459, 3, 3145731, 4194307, 296963, 36867, 73731, 1572867, 147459, + 3, 2051, 4194307, 1187843, 2051, 2050, 114691, 2051, 4194307, 540675, 4194306, 4194307, 2490371, 2051, 4194307, 4099, + 2, 3, 3, 8195, 3, 2051, 4214787, 458755, 3, 8195, 8195, 8194, 131075, 2146307, 1572867, 8195, + 3, 2051, 1114115, 4751363, 2051, 2050, 2105347, 2051, 2625539, 1310723, 149507, 8195, 4259843, 2051, 294915, 4099, + 3, 2051, 2260995, 16387, 2051, 2050, 1572867, 2051, 344067, 4329475, 1572867, 8195, 1572867, 2051, 1572866, 1572867, + 2051, 2050, 266243, 2051, 2050, 2049, 2051, 2050, 40963, 2051, 4194307, 2162691, 2051, 2050, 1572867, 2051, + 2, 3, 3, 4327427, 3, 81923, 270339, 4099, 3, 3145731, 573443, 4099, 131075, 4099, 4099, 4098, + 3, 532483, 1114115, 4099, 6324227, 4099, 4099, 4098, 264195, 4099, 4099, 4098, 4099, 4098, 4098, 4097, + 3, 3145731, 270339, 16387, 270339, 688131, 270338, 270339, 3145731, 3145730, 196611, 3145731, 4212739, 3145731, 270339, 4099, + 151555, 4521987, 2623491, 32771, 1048579, 2051, 270339, 4099, 40963, 3145731, 4194307, 4099, 589827, 4099, 4099, 4098, + 3, 299011, 1114115, 16387, 131075, 5251075, 34819, 2621443, 131075, 591875, 6553603, 8195, 131074, 131075, 131075, 4099, + 1114115, 2228227, 1114114, 1114115, 802819, 2051, 1114115, 4099, 40963, 4210691, 1114115, 4099, 131075, 4099, 4099, 4098, + 4718595, 16387, 16387, 16386, 2166787, 2051, 270339, 16387, 40963, 3145731, 6147, 16387, 131075, 262147, 1572867, 4292611, + 40963, 2051, 1114115, 16387, 2051, 2050, 4325379, 2051, 40962, 40963, 40963, 917507, 40963, 2051, 2113539, 4099, + 2, 3, 3, 8195, 3, 81923, 655363, 6291459, 3, 8195, 8195, 8194, 36867, 1181699, 278531, 8195, + 3, 5246979, 655363, 67587, 655363, 303107, 655362, 655363, 264195, 540675, 3178499, 8195, 4259843, 2097155, 655363, 4099, + 3, 393219, 1067011, 6291459, 36867, 6291459, 6291459, 6291458, 36867, 540675, 196611, 8195, 36866, 36867, 36867, 6291459, + 2170883, 540675, 266243, 32771, 1048579, 2051, 655363, 6291459, 540675, 540674, 4194307, 540675, 36867, 540675, 10243, 1376259, + 3, 8195, 8195, 8194, 3407875, 528387, 34819, 8195, 8195, 8194, 8194, 8193, 4259843, 8195, 8195, 8194, + 49155, 2228227, 266243, 8195, 4259843, 2051, 655363, 1064963, 4259843, 8195, 8195, 8194, 4259842, 4259843, 4259843, 8195, + 4718595, 1146883, 266243, 8195, 155651, 2051, 65539, 6291459, 2099203, 8195, 8195, 8194, 36867, 262147, 1572867, 8195, + 266243, 2051, 266242, 266243, 2051, 2050, 266243, 2051, 1179651, 540675, 266243, 8195, 4259843, 2051, 2113539, 163843, + 3, 81923, 2101251, 1835011, 81923, 81922, 34819, 81923, 264195, 4227075, 196611, 8195, 2629635, 81923, 5242883, 4099, + 264195, 2228227, 4218883, 32771, 1048579, 81923, 655363, 4099, 264194, 264195, 264195, 4099, 264195, 4099, 4099, 4098, + 4718595, 14339, 196611, 32771, 1048579, 81923, 270339, 6291459, 196611, 3145731, 196610, 196611, 36867, 262147, 196611, 526339, + 1048579, 32771, 32771, 32770, 1048578, 1048579, 1048579, 32771, 264195, 540675, 196611, 32771, 1048579, 4333571, 2113539, 4099, + 4718595, 2228227, 34819, 8195, 34819, 81923, 34818, 34819, 1069059, 8195, 8195, 8194, 131075, 262147, 34819, 8195, + 2228227, 2228226, 1114115, 2228227, 12291, 2228227, 34819, 4456451, 264195, 2228227, 524291, 8195, 4259843, 1605635, 2113539, 4099, + 4718594, 4718595, 4718595, 16387, 4718595, 262147, 34819, 1183747, 4718595, 262147, 196611, 8195, 262147, 262146, 2113539, 262147, + 4718595, 2228227, 266243, 32771, 1048579, 2051, 2113539, 598019, 40963, 69635, 2113539, 5244931, 2113539, 262147, 2113538, 2113539, + 2, 3, 3, 1081347, 3, 1081347, 1081347, 1081346, 3, 22531, 4194307, 2752515, 131075, 73731, 278531, 1081347, + 3, 532483, 4194307, 67587, 331779, 4341763, 2105347, 1081347, 4194307, 1310723, 4194306, 4194307, 559107, 2097155, 4194307, 4099, + 3, 393219, 4194307, 16387, 2637827, 73731, 137219, 1081347, 4194307, 73731, 4194306, 4194307, 73731, 73730, 4194307, 73731, + 4194307, 2134019, 4194306, 4194307, 1048579, 2051, 4194307, 786435, 4194306, 4194307, 4194305, 4194306, 4194307, 73731, 4194306, 4194307, + 3, 6356995, 788483, 16387, 131075, 528387, 2105347, 1081347, 131075, 1310723, 102403, 8195, 131074, 131075, 131075, 4196355, + 49155, 1310723, 2105347, 135171, 2105347, 2051, 2105346, 2105347, 1310723, 1310722, 4194307, 1310723, 131075, 1310723, 2105347, 606211, + 1060867, 16387, 16387, 16386, 4489219, 2051, 65539, 16387, 2099203, 557059, 4194307, 16387, 131075, 73731, 1572867, 2363395, + 720899, 2051, 4194307, 16387, 2051, 2050, 2105347, 2051, 4194307, 1310723, 4194306, 4194307, 20483, 2051, 4194307, 163843, + 3, 532483, 2101251, 16387, 131075, 2361347, 4784131, 1081347, 131075, 4227075, 1058819, 327683, 131074, 131075, 131075, 4099, + 532483, 532482, 425987, 532483, 1048579, 532483, 18435, 4099, 2179075, 532483, 4194307, 4099, 131075, 4099, 4099, 4098, + 100355, 16387, 16387, 16386, 1048579, 4198403, 270339, 16387, 790531, 3145731, 4194307, 16387, 131075, 73731, 2129923, 526339, + 1048579, 532483, 4194307, 16387, 1048578, 1048579, 1048579, 2293763, 4194307, 133123, 4194306, 4194307, 1048579, 311299, 4194307, 4099, + 131075, 16387, 16387, 16386, 131074, 131075, 131075, 16387, 131074, 131075, 131075, 16387, 131073, 131074, 131074, 131075, + 4200451, 532483, 1114115, 16387, 131075, 98307, 2105347, 4456451, 131075, 1310723, 524291, 2131971, 131074, 131075, 131075, 4099, + 16387, 16386, 16386, 16385, 131075, 16387, 16387, 16386, 131075, 16387, 16387, 16386, 131074, 131075, 131075, 16387, + 2359299, 16387, 16387, 16386, 1048579, 2051, 561155, 16387, 40963, 69635, 4194307, 16387, 131075, 6815747, 329731, 1056771, + 3, 393219, 2101251, 67587, 4204547, 528387, 278531, 1081347, 1638403, 4227075, 278531, 8195, 278531, 2097155, 278530, 278531, + 49155, 67587, 67587, 67586, 1048579, 2097155, 655363, 67587, 143363, 2097155, 4194307, 67587, 2097155, 2097154, 278531, 2097155, + 393219, 393218, 565251, 393219, 1048579, 393219, 65539, 6291459, 2099203, 393219, 4194307, 1052675, 36867, 73731, 278531, 526339, + 1048579, 393219, 4194307, 67587, 1048578, 1048579, 1048579, 28675, 4194307, 540675, 4194306, 4194307, 1048579, 2097155, 4194307, 163843, + 49155, 528387, 5373955, 8195, 528387, 528386, 65539, 528387, 2099203, 8195, 8195, 8194, 131075, 528387, 278531, 8195, + 49154, 49155, 49155, 67587, 49155, 528387, 2105347, 4456451, 49155, 1310723, 524291, 8195, 4259843, 2097155, 1054723, 163843, + 2099203, 393219, 65539, 16387, 65539, 528387, 65538, 65539, 2099202, 2099203, 2099203, 8195, 2099203, 5259267, 65539, 163843, + 49155, 4202499, 266243, 3670019, 1048579, 2051, 65539, 163843, 2099203, 69635, 4194307, 163843, 794627, 163843, 163843, 163842, + 2101251, 4227075, 2101250, 2101251, 1048579, 81923, 2101251, 139267, 4227075, 4227074, 2101251, 4227075, 131075, 4227075, 278531, 526339, + 1048579, 532483, 2101251, 67587, 1048578, 1048579, 1048579, 4456451, 264195, 4227075, 524291, 1196035, 1048579, 2097155, 106499, 4099, + 1048579, 393219, 2101251, 16387, 1048578, 1048579, 1048579, 526339, 24579, 4227075, 196611, 526339, 1048579, 526339, 526339, 526338, + 1048578, 1048579, 1048579, 32771, 1048577, 1048578, 1048578, 1048579, 1048579, 69635, 4194307, 2367491, 1048578, 1048579, 1048579, 526339, + 335875, 1050627, 2101251, 16387, 131075, 528387, 34819, 4456451, 131075, 4227075, 524291, 8195, 131074, 131075, 131075, 3211267, + 49155, 2228227, 524291, 4456451, 1048579, 4456451, 4456451, 4456450, 524291, 69635, 524290, 524291, 131075, 26627, 524291, 4456451, + 4718595, 16387, 16387, 16386, 1048579, 2138115, 65539, 16387, 2099203, 69635, 1343491, 16387, 131075, 262147, 4206595, 526339, + 1048579, 69635, 141315, 16387, 1048578, 1048579, 1048579, 4456451, 69635, 69634, 524291, 69635, 1048579, 69635, 2113539, 163843 }; + +static const uint32_t hmg1063EncTbl[64] = { + 0, 12, 3, 15, 7, 11, 4, 8, 11, 7, 8, 4, 12, 0, 15, 3, + 13, 1, 14, 2, 10, 6, 9, 5, 6, 10, 5, 9, 1, 13, 2, 14, + 14, 2, 13, 1, 9, 5, 10, 6, 5, 9, 6, 10, 2, 14, 1, 13, + 3, 15, 0, 12, 4, 8, 7, 11, 8, 4, 11, 7, 15, 3, 12, 0 }; +static const uint32_t hmg1063DecTbl[16] = { + 0, 0, 1, 0, 2, 4, 8, 0, 16, 32, 64, 128, 256, 512, 1024, 0 }; + +static const uint32_t rsGFexp[64] = { + 1, 2, 4, 8, 16, 32, 3, 6, 12, 24, 48, 35, 5, 10, 20, 40, + 19, 38, 15, 30, 60, 59, 53, 41, 17, 34, 7, 14, 28, 56, 51, 37, + 9, 18, 36, 11, 22, 44, 27, 54, 47, 29, 58, 55, 45, 25, 50, 39, + 13, 26, 52, 43, 21, 42, 23, 46, 31, 62, 63, 61, 57, 49, 33, 0 }; +static const uint32_t rsGFlog[64] = { + 63, 0, 1, 6, 2, 12, 7, 26, 3, 32, 13, 35, 8, 48, 27, 18, + 4, 24, 33, 16, 14, 52, 36, 54, 9, 45, 49, 38, 28, 41, 19, 56, + 5, 62, 25, 11, 34, 31, 17, 47, 15, 23, 53, 51, 37, 44, 55, 40, + 10, 61, 46, 30, 50, 22, 39, 43, 29, 60, 42, 21, 20, 59, 57, 58 }; + +int hmg1063Dec (uint32_t Dat, uint32_t Par) { + assert ((Dat < 64) && (Par < 16)); + return Dat ^ hmg1063DecTbl [ hmg1063EncTbl[Dat] ^ Par]; +} + +int +rsDec (int nroots, int FirstInfo, uint8_t HB[]) { + +//RS (63,63-nroots,nroots+1) decoder where nroots = number of parity bits +// rsDec(8, 39) rsDec(16, 27) rsDec(12, 39) + + +int lambda[18] ;//Err+Eras Locator poly +int S[17] ;//syndrome poly +int b[18] ; +int t[18] ; +int omega[18] ; +int root[17] ; +int reg[18] ; +int locn[17] ; + +int i,j, count, r, el, SynError, DiscrR, q, DegOmega, tmp, num1, num2, den, DegLambda; + +//form the syndromes; i.e., evaluate HB(x) at roots of g(x) + +for (i = 0; i <= nroots - 1; i++) { + S[i] = HB[0]; +} +for (j = 1; j <= 62; j++) { + for (i = 0; i <= nroots - 1; i++) { + if (S[i] == 0) { + S[i] = HB[j]; + } else { + S[i] = HB[j] ^ rsGFexp[(rsGFlog[S[i]] + i + 1) % 63]; + } + } +} + + + +//convert syndromes to index form, checking for nonzero condition + +SynError = 0; + +for (i = 0; i <= nroots - 1; i++) { + SynError = SynError | S[i]; + S[i] = rsGFlog[S[i]]; +} + +if (SynError == 0) { + //if syndrome is zero, rsData[] is a codeword and there are + //no errors to correct. So return rsData[] unmodified + count = 0; + goto rsDecFinish; +} + +for (i = 1; i <= nroots; i++) { + lambda[i] = 0; +} +lambda[0] = 1; + +for (i = 0; i <= nroots; i++) { + b[i] = rsGFlog[lambda[i]]; +} + + + +//begin Berlekamp-Massey algorithm to determine error+erasure +//locator polynomial + +r = 0; +el = 0; +while ( r < nroots) { //r is the step number + r = r + 1; + //compute discrepancy at the r-th step in poly-form + DiscrR = 0; + for (i = 0; i <= r - 1; i++) { + if ((lambda[i] != 0) && (S[r - i - 1] != 63)) { + DiscrR = DiscrR ^ rsGFexp[(rsGFlog[lambda[i]] + S[r - i - 1]) % 63]; + } + } + DiscrR = rsGFlog[DiscrR] ;//index form + + if (DiscrR == 63) { + //shift elements upward one step + for (i = nroots; i >= 1; i += -1){b[i] = b[i - 1]; } b[0] = 63; + } else { + //t(x) <-- lambda(x) - DiscrR*x*b(x) + t[0] = lambda[0]; + for (i = 0; i <= nroots - 1; i++) { + if (b[i] != 63) { + t[i + 1] = lambda[i + 1] ^ rsGFexp[(DiscrR + b[i]) % 63]; + } else { + t[i + 1] = lambda[i + 1]; + } + } + if (2 * el <= r - 1) { + el = r - el; + //b(x) <-- inv(DiscrR) * lambda(x) + for (i = 0; i <= nroots; i++) { + if (lambda[i]) { b[i] = (rsGFlog[lambda[i]] - DiscrR + 63) % 63; } else { b[i] = 63; } + } + } else { + //shift elements upward one step + for (i = nroots; i >= 1; i += -1){b[i] = b[i - 1]; } b[0] = 63; + } + for (i = 0; i <= nroots; i++) { lambda[i] = t[i]; } + } +} /* end while() */ + + + +//convert lambda to index form and compute deg(lambda(x)) + +DegLambda = 0; +for (i = 0; i <= nroots; i++) { + lambda[i] = rsGFlog[lambda[i]]; + if (lambda[i] != 63) { DegLambda = i; } +} + + + +//Find roots of the error+erasure locator polynomial by Chien search + +for (i = 1; i <= nroots; i++) { reg[i] = lambda[i]; } +count = 0 ;//number of roots of lambda(x) +for (i = 1; i <= 63; i++) { + q = 1 ;//lambda[0] is always 0 + for (j = DegLambda; j >= 1; j += -1) { + if (reg[j] != 63) { + reg[j] = (reg[j] + j) % 63; + q = q ^ rsGFexp[reg[j]]; + } + } + if (q == 0) { //it is a root + //store root (index-form) and error location number + root[count] = i; + locn[count] = i - 1; + //if wehave max possible roots, abort search to save time + count = count + 1; if (count == DegLambda) { break; } + } +} + +if (DegLambda != count) { + //deg(lambda) unequal to number of roots => uncorrectable error detected + count = -1; + goto rsDecFinish; +} + + + +//compute err+eras evaluator poly omega(x) +// = s(x)*lambda(x) (modulo x**nroots). in index form. Also find deg(omega). + +DegOmega = 0; +for (i = 0; i <= nroots - 1; i++) { + tmp = 0; + if (DegLambda < i) { j = DegLambda; } else { j = i; } + for ( /* j = j */ ; j >= 0; j += -1) { + if ((S[i - j] != 63) && (lambda[j] != 63)) { + tmp = tmp ^ rsGFexp[(S[i - j] + lambda[j]) % 63]; + } + } + if (tmp) { DegOmega = i; } + omega[i] = rsGFlog[tmp]; +} +omega[nroots] = 63; + + +//compute error values in poly-form: +// num1 = omega(inv(X(l))) +// num2 = inv(X(l))**(FCR - 1) +// den = lambda_pr(inv(X(l))) + +for (j = count - 1; j >= 0; j += -1) { + num1 = 0; + for (i = DegOmega; i >= 0; i += -1) { + if (omega[i] != 63) { + num1 = num1 ^ rsGFexp[(omega[i] + i * root[j]) % 63]; + } + } + num2 = rsGFexp[0]; + den = 0; + + // lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] + if (DegLambda < nroots) { i = DegLambda; } else { i = nroots; } + for (i = i & ~1; i >= 0; i += -2) { + if (lambda[i + 1] != 63) { + den = den ^ rsGFexp[(lambda[i + 1] + i * root[j]) % 63]; + } + } + if (den == 0) { count = -1; goto rsDecFinish; } + + // apply error to data + if (num1 != 0) { + if (locn[j] < FirstInfo) { count = -1; goto rsDecFinish ; } //added by me + HB[locn[j]] = HB[locn[j]] ^ (rsGFexp[(rsGFlog[num1] + rsGFlog[num2] + 63 - rsGFlog[den]) % 63]); + } +} + + +rsDecFinish: +return (count); + +} + +/*********************************************************************/ +/*********************************************************************/ +/*********************************************************************/ + +uint32_t gly23127GetSyn (uint32_t pattern) { + +uint32_t aux = 0x400000; + +while(pattern & 0xFFFFF800) { + while ((aux & pattern) == 0) { + aux = aux >> 1; + } + pattern = pattern ^ (aux / 0x800 * 0xC75) ;//generator is C75 +} + +return pattern; + +} + +uint32_t gly24128Dec (uint32_t n) { + +//based on gly23127Dec + +uint32_t CW = n >> 1 ; //toss the parity bit +uint32_t correction = gly23127DecTbl[gly23127GetSyn(CW)]; +CW = (CW ^ correction) >> 11; + +return CW; + +} + +void ProcHDU(const_bit_vector A) { +int i, j, k, ec; +uint8_t HB[63]; // "hexbit" array + +//header code word is 324 dibits (padded by 5 trailing zero dibits) +// 324 dibits = 648 bits = 36 18-bit Golay codewords + +//do (18,6,8) shortened Golay decode - make 36 hexbits for rs dec +for (i = 0; i <= 26; i++) { + HB[i] = 0; +} +k = 0; +for (i = 0; i < 36; i ++) { // 36 codewords + uint32_t CW = 0; + for (j = 0; j < 18; j++) { // 18 bits / cw + CW = (CW << 1) + A [ hdu_codeword_bits[k++] ]; + } + HB[27 + i] = gly24128Dec(CW) & 63; +} + +//do (36,20,17) RS decode +ec = rsDec(16, 27, HB); +//120 info bits = 20 hexbits: (27..46) + //72 bits MI: (27..38) + // 8 bits MFID + // 8 bits ALGID + //16 bits KID + //16 bits TGID + +uint32_t MFID = HB[39] * 4 + (HB[40] >> 4); +uint32_t ALGID = (HB[40] & 15) * 16 + (HB[41] >> 2); +uint32_t KID = (HB[41] & 3) * 16384 + HB[42] * 256 + HB[43] * 4 + (HB[44] >> 4); +uint32_t TGID = (HB[44] & 15) * 4096 + HB[45] * 64 + HB[46]; + +fprintf (stderr, "HDU: rc %d mfid %x alg %x kid %x tgid %d\n", ec, MFID, ALGID, KID, TGID); + +} + +void ProcLLDU(const_bit_vector A, uint8_t HB[]) { +int i, j, k; +for (i = 0; i <= 38; i++) { + HB[i] = 0; +} +k = 0; +for (i = 0; i < 24; i ++) { // 24 10-bit codewords + uint32_t CW = 0; + for (j = 0; j < 10; j++) { // 10 bits / cw + CW = (CW << 1) + A [ imbe_ldu_ls_data_bits[k++] ]; + } + HB[39 + i] = hmg1063Dec( CW >> 4, CW & 0xF ); +} + +} + +void ProcLC(uint8_t HB[]) { + int ec = rsDec(12, 39, HB); + int pb = HB[39] >> 5; + int sf = (HB[39] & 16) >> 4; + int lco = (HB[39] and 15) * 4 + (HB[40] >> 4); + fprintf(stderr, "LC: rc %d pb %d sf %d lco %d\n", ec, pb, sf, lco); +} + +void ProcLDU1(const_bit_vector A) { + uint8_t HB[63]; // "hexbit" array + + ProcLLDU(A, HB); + ProcLC(HB); +} + +void ProcLDU2(const_bit_vector A) { + uint8_t HB[63]; // "hexbit" array + + ProcLLDU(A, HB); + int ec = rsDec(8, 39, HB); + + uint32_t ALGID = HB[51] * 4 + (HB[52] >> 4); + uint32_t KID = (HB[52] & 15) * 4096 + HB[53] * 64 + HB[54]; + + fprintf(stderr, "LDU2: rc %d ALGID %x KID %x MI ", ec, ALGID, KID); + for (int i = 39; i <= 50; i++) { + fprintf(stderr, "%02x ", HB[ i ]); + } + fprintf(stderr, "\n"); +} + +void ProcTDU(const_bit_vector A) { +uint8_t HB[63]; // "hexbit" array + +int i, j, k; +for (i = 0; i <= 38; i++) { + HB[i] = 0; +} +k = 0; +for (i = 0; i <= 22; i += 2) { + uint32_t CW = 0; + for (j = 0; j < 12; j++) { // 12 24-bit codewords + CW = (CW << 1) + A [ hdu_codeword_bits[k++] ]; + CW = (CW << 1) + A [ hdu_codeword_bits[k++] ]; + } + uint32_t D = gly24128Dec(CW); + HB[39 + i] = D >> 6; + HB[40 + i] = D & 63; +} +ProcLC(HB); +} + diff --git a/repeater/src/lib/rs.h b/repeater/src/lib/rs.h new file mode 100644 index 0000000..6b2544f --- /dev/null +++ b/repeater/src/lib/rs.h @@ -0,0 +1,15 @@ + +#ifndef INCLUDED_OP25_RS_H +#define INCLUDED_OP25_RS_H + +#include +#include +#include +#include + +void ProcHDU(const_bit_vector A); +void ProcTDU(const_bit_vector A); +void ProcLDU1(const_bit_vector A); +void ProcLDU2(const_bit_vector A); + +#endif