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:
parent
79d7df78dc
commit
bd8daf404c
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
Reference in New Issue