consolidate gardner/costas, update fsk4 slicer

git-svn-id: http://op25.osmocom.org/svn/trunk@216 65a5c917-d112-43f1-993d-58c26a4786be
This commit is contained in:
max 2010-07-17 22:14:02 +00:00
parent 79d7df78dc
commit bd8daf404c
17 changed files with 1225 additions and 1163 deletions

View File

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

View File

@ -0,0 +1,161 @@
/* -*- c++ -*- */
/*
* construct P25 frames out of raw dibits
* Copyright 2010, KA1RBI
*/
#include <vector>
#include <stdio.h>
#include <stdint.h>
#include <bch.h>
#include <sys/time.h>
#include <op25_p25_frame.h>
#include <p25_framer.h>
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;
}

View File

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

View File

@ -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 <stdexcept>
%}
@ -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<float> &slice_levels);
class repeater_fsk4_slicer_fb : public gr_sync_block
{
private:
repeater_fsk4_slicer_fb ();
repeater_fsk4_slicer_fb (const std::vector<float> &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);
};

View File

@ -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 <repeater_costas_loop_cc.h>
#include <gr_io_signature.h>
#include <gr_expj.h>
#include <gr_sincos.h>
#include <math.h>
#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;
}

View File

@ -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 <gr_sync_block.h>
#include <stdexcept>
#include <fstream>
/*! \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> 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
* <br>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

View File

@ -33,15 +33,16 @@
#include <repeater_fsk4_slicer_fb.h>
#include <gr_io_signature.h>
#include <stdio.h>
/*
* 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<float> &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<float> &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.

View File

@ -46,7 +46,7 @@ typedef boost::shared_ptr<repeater_fsk4_slicer_fb> 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<float> &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<float> &slice_levels);
repeater_fsk4_slicer_fb (); // private constructor
repeater_fsk4_slicer_fb (const std::vector<float> &slice_levels); // private constructor
float d_slice_levels[4];
public:
~repeater_fsk4_slicer_fb (); // public destructor

View File

@ -30,54 +30,72 @@
#include <gr_prefs.h>
#include <gr_math.h>
#include <gr_expj.h>
#include <repeater_gardner_symbol_recovery_cc.h>
#include <repeater_gardner_costas_cc.h>
#include <gri_mmse_fir_interpolator_cc.h>
#include <stdexcept>
#include <cstdio>
#include <string.h>
#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;
}

View File

@ -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> repeater_gardner_symbol_recovery_cc_sptr;
class repeater_gardner_costas_cc;
typedef boost::shared_ptr<repeater_gardner_costas_cc> 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);
};

View File

@ -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 <repeater_imbe_decode_fb.h>
#include <gr_io_signature.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <vector>
#include <bch.h>
#include <op25_imbe_frame.h>
#include <op25_p25_frame.h>
#include <repeater_imbe_decode_fb.h>
#include <sys/time.h>
/*
* 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;
}

View File

@ -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 <repeater_p25_frame_assembler.h>
#include <gr_io_signature.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <vector>
#include <bch.h>
#include <op25_imbe_frame.h>
#include <op25_p25_frame.h>
#include <p25_framer.h>
#include <repeater_p25_frame_assembler.h>
#include <sys/time.h>
#include <rs.h>
/*
* 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);
}

View File

@ -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 <gr_sync_block.h>
#include <gr_msg_queue.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <deque>
class repeater_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> repeater_imbe_decode_fb_sptr;
typedef boost::shared_ptr<repeater_p25_frame_assembler> repeater_p25_frame_assembler_sptr;
typedef std::deque<uint8_t> 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<bool> 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 */

View File

@ -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 <repeater_p25_frame_fb.h>
#include <gr_io_signature.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <vector>
#include <bch.h>
#include <op25_imbe_frame.h>
#include <op25_p25_frame.h>
#include <sys/time.h>
/*
* 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;
}

View File

@ -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 <gr_sync_block.h>
#include <deque>
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> repeater_p25_frame_fb_sptr;
typedef std::deque<uint8_t> 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<bool> 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 */

567
repeater/src/lib/rs.cc Normal file
View File

@ -0,0 +1,567 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <vector>
#include <assert.h>
#include <op25_imbe_frame.h>
#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);
}

15
repeater/src/lib/rs.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef INCLUDED_OP25_RS_H
#define INCLUDED_OP25_RS_H
#include <stdlib.h>
#include <stdint.h>
#include <vector>
#include <op25_imbe_frame.h>
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