P25 Work-in-progress check-in.

This commit is contained in:
dennis.sheirer
2014-11-10 09:53:03 +00:00
parent 7b89c614e7
commit 46e3594c07
31 changed files with 2271 additions and 227 deletions

View File

@@ -8,7 +8,6 @@ import java.util.ArrayList;
import java.util.List;
import sample.real.RealSampleListener;
import dsp.filter.C4FMSymbolFilter;
public class C4FMDecoder implements RealSampleListener, Instrumentable
{

View File

@@ -0,0 +1,15 @@
package dsp.fsk;
import bits.BitSetBuffer;
public interface C4FMFrameListener
{
/**
* Listener interface to receive the output of the C4FMMessageFramer.
*
* @param buffer - framed message without the sync pattern
* @param inverted - flag indicating if the message was received inverted
*/
public void receive( BitSetBuffer buffer, boolean inverted );
}

View File

@@ -0,0 +1,69 @@
package dsp.fsk;
import sample.Broadcaster;
import sample.Listener;
import sample.real.RealSampleListener;
/**
* C4FM slicer to convert the output stream of the C4FMSymbolFilter into a
* stream of C4FM symbols.
*
* Supports registering listener(s) to receive normal and/or inverted symbol
* output streams.
*/
public class C4FMSlicer implements RealSampleListener
{
private static final float THRESHOLD = 2.0f;
private Broadcaster<C4FMSymbol> mBroadcaster = new Broadcaster<C4FMSymbol>();
/**
* Primary method for receiving output from the C4FMSymbolFilter. Slices
* (converts) the filtered sample value into a C4FMSymbol decision.
*/
@Override
public void receive( float sample )
{
if( sample > THRESHOLD )
{
dispatch( C4FMSymbol.SYMBOL_PLUS_3 );
}
else if( sample > 0 )
{
dispatch( C4FMSymbol.SYMBOL_PLUS_1 );
}
else if( sample > -THRESHOLD )
{
dispatch( C4FMSymbol.SYMBOL_MINUS_1 );
}
else
{
dispatch( C4FMSymbol.SYMBOL_MINUS_3 );
}
}
/**
* Dispatches the symbol decision to any registered listeners
*/
private void dispatch( C4FMSymbol symbol )
{
mBroadcaster.receive( symbol );
}
/**
* Registers the listener to receive the normal (non-inverted) C4FM symbol
* stream.
*/
public void addListener( Listener<C4FMSymbol> listener )
{
mBroadcaster.addListener( listener );
}
/**
* Removes the listener
*/
public void removeListener( Listener<C4FMSymbol> listener )
{
mBroadcaster.removeListener( listener );
}
}

View File

@@ -0,0 +1,44 @@
package dsp.fsk;
public enum C4FMSymbol
{
SYMBOL_PLUS_3( false, true ),
SYMBOL_PLUS_1( false, false ),
SYMBOL_MINUS_1( true, false ),
SYMBOL_MINUS_3( true, true );
private boolean mBit1;
private boolean mBit2;
private C4FMSymbol( boolean bit1, boolean bit2 )
{
mBit1 = bit1;
mBit2 = bit2;
}
public boolean getBit1()
{
return mBit1;
}
public boolean getBit2()
{
return mBit2;
}
public static C4FMSymbol inverted( C4FMSymbol symbol )
{
switch( symbol )
{
case SYMBOL_MINUS_1:
return SYMBOL_MINUS_3;
case SYMBOL_MINUS_3:
return SYMBOL_PLUS_3;
case SYMBOL_PLUS_1:
return SYMBOL_MINUS_1;
case SYMBOL_PLUS_3:
default:
return SYMBOL_PLUS_1;
}
}
}

View File

@@ -0,0 +1,321 @@
package dsp.fsk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.real.RealSampleListener;
import sample.real.RealSampleProvider;
public class C4FMSymbolFilter implements RealSampleListener,
RealSampleProvider
{
private final static Logger mLog =
LoggerFactory.getLogger( C4FMSymbolFilter.class );
private static final float TAPS[][] =
{
{ 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 1.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f }, // 0/128
{ -1.54700e-04f, 8.53777e-04f, -2.76968e-03f, 7.89295e-03f, 9.98534e-01f, -5.41054e-03f, 1.24642e-03f, -1.98993e-04f }, // 1/128
{ -3.09412e-04f, 1.70888e-03f, -5.55134e-03f, 1.58840e-02f, 9.96891e-01f, -1.07209e-02f, 2.47942e-03f, -3.96391e-04f }, // 2/128
{ -4.64053e-04f, 2.56486e-03f, -8.34364e-03f, 2.39714e-02f, 9.95074e-01f, -1.59305e-02f, 3.69852e-03f, -5.92100e-04f }, // 3/128
{ -6.18544e-04f, 3.42130e-03f, -1.11453e-02f, 3.21531e-02f, 9.93082e-01f, -2.10389e-02f, 4.90322e-03f, -7.86031e-04f }, // 4/128
{ -7.72802e-04f, 4.27773e-03f, -1.39548e-02f, 4.04274e-02f, 9.90917e-01f, -2.60456e-02f, 6.09305e-03f, -9.78093e-04f }, // 5/128
{ -9.26747e-04f, 5.13372e-03f, -1.67710e-02f, 4.87921e-02f, 9.88580e-01f, -3.09503e-02f, 7.26755e-03f, -1.16820e-03f }, // 6/128
{ -1.08030e-03f, 5.98883e-03f, -1.95925e-02f, 5.72454e-02f, 9.86071e-01f, -3.57525e-02f, 8.42626e-03f, -1.35627e-03f }, // 7/128
{ -1.23337e-03f, 6.84261e-03f, -2.24178e-02f, 6.57852e-02f, 9.83392e-01f, -4.04519e-02f, 9.56876e-03f, -1.54221e-03f }, // 8/128
{ -1.38589e-03f, 7.69462e-03f, -2.52457e-02f, 7.44095e-02f, 9.80543e-01f, -4.50483e-02f, 1.06946e-02f, -1.72594e-03f }, // 9/128
{ -1.53777e-03f, 8.54441e-03f, -2.80746e-02f, 8.31162e-02f, 9.77526e-01f, -4.95412e-02f, 1.18034e-02f, -1.90738e-03f }, // 10/128
{ -1.68894e-03f, 9.39154e-03f, -3.09033e-02f, 9.19033e-02f, 9.74342e-01f, -5.39305e-02f, 1.28947e-02f, -2.08645e-03f }, // 11/128
{ -1.83931e-03f, 1.02356e-02f, -3.37303e-02f, 1.00769e-01f, 9.70992e-01f, -5.82159e-02f, 1.39681e-02f, -2.26307e-03f }, // 12/128
{ -1.98880e-03f, 1.10760e-02f, -3.65541e-02f, 1.09710e-01f, 9.67477e-01f, -6.23972e-02f, 1.50233e-02f, -2.43718e-03f }, // 13/128
{ -2.13733e-03f, 1.19125e-02f, -3.93735e-02f, 1.18725e-01f, 9.63798e-01f, -6.64743e-02f, 1.60599e-02f, -2.60868e-03f }, // 14/128
{ -2.28483e-03f, 1.27445e-02f, -4.21869e-02f, 1.27812e-01f, 9.59958e-01f, -7.04471e-02f, 1.70776e-02f, -2.77751e-03f }, // 15/128
{ -2.43121e-03f, 1.35716e-02f, -4.49929e-02f, 1.36968e-01f, 9.55956e-01f, -7.43154e-02f, 1.80759e-02f, -2.94361e-03f }, // 16/128
{ -2.57640e-03f, 1.43934e-02f, -4.77900e-02f, 1.46192e-01f, 9.51795e-01f, -7.80792e-02f, 1.90545e-02f, -3.10689e-03f }, // 17/128
{ -2.72032e-03f, 1.52095e-02f, -5.05770e-02f, 1.55480e-01f, 9.47477e-01f, -8.17385e-02f, 2.00132e-02f, -3.26730e-03f }, // 18/128
{ -2.86289e-03f, 1.60193e-02f, -5.33522e-02f, 1.64831e-01f, 9.43001e-01f, -8.52933e-02f, 2.09516e-02f, -3.42477e-03f }, // 19/128
{ -3.00403e-03f, 1.68225e-02f, -5.61142e-02f, 1.74242e-01f, 9.38371e-01f, -8.87435e-02f, 2.18695e-02f, -3.57923e-03f }, // 20/128
{ -3.14367e-03f, 1.76185e-02f, -5.88617e-02f, 1.83711e-01f, 9.33586e-01f, -9.20893e-02f, 2.27664e-02f, -3.73062e-03f }, // 21/128
{ -3.28174e-03f, 1.84071e-02f, -6.15931e-02f, 1.93236e-01f, 9.28650e-01f, -9.53307e-02f, 2.36423e-02f, -3.87888e-03f }, // 22/128
{ -3.41815e-03f, 1.91877e-02f, -6.43069e-02f, 2.02814e-01f, 9.23564e-01f, -9.84679e-02f, 2.44967e-02f, -4.02397e-03f }, // 23/128
{ -3.55283e-03f, 1.99599e-02f, -6.70018e-02f, 2.12443e-01f, 9.18329e-01f, -1.01501e-01f, 2.53295e-02f, -4.16581e-03f }, // 24/128
{ -3.68570e-03f, 2.07233e-02f, -6.96762e-02f, 2.22120e-01f, 9.12947e-01f, -1.04430e-01f, 2.61404e-02f, -4.30435e-03f }, // 25/128
{ -3.81671e-03f, 2.14774e-02f, -7.23286e-02f, 2.31843e-01f, 9.07420e-01f, -1.07256e-01f, 2.69293e-02f, -4.43955e-03f }, // 26/128
{ -3.94576e-03f, 2.22218e-02f, -7.49577e-02f, 2.41609e-01f, 9.01749e-01f, -1.09978e-01f, 2.76957e-02f, -4.57135e-03f }, // 27/128
{ -4.07279e-03f, 2.29562e-02f, -7.75620e-02f, 2.51417e-01f, 8.95936e-01f, -1.12597e-01f, 2.84397e-02f, -4.69970e-03f }, // 28/128
{ -4.19774e-03f, 2.36801e-02f, -8.01399e-02f, 2.61263e-01f, 8.89984e-01f, -1.15113e-01f, 2.91609e-02f, -4.82456e-03f }, // 29/128
{ -4.32052e-03f, 2.43930e-02f, -8.26900e-02f, 2.71144e-01f, 8.83893e-01f, -1.17526e-01f, 2.98593e-02f, -4.94589e-03f }, // 30/128
{ -4.44107e-03f, 2.50946e-02f, -8.52109e-02f, 2.81060e-01f, 8.77666e-01f, -1.19837e-01f, 3.05345e-02f, -5.06363e-03f }, // 31/128
{ -4.55932e-03f, 2.57844e-02f, -8.77011e-02f, 2.91006e-01f, 8.71305e-01f, -1.22047e-01f, 3.11866e-02f, -5.17776e-03f }, // 32/128
{ -4.67520e-03f, 2.64621e-02f, -9.01591e-02f, 3.00980e-01f, 8.64812e-01f, -1.24154e-01f, 3.18153e-02f, -5.28823e-03f }, // 33/128
{ -4.78866e-03f, 2.71272e-02f, -9.25834e-02f, 3.10980e-01f, 8.58189e-01f, -1.26161e-01f, 3.24205e-02f, -5.39500e-03f }, // 34/128
{ -4.89961e-03f, 2.77794e-02f, -9.49727e-02f, 3.21004e-01f, 8.51437e-01f, -1.28068e-01f, 3.30021e-02f, -5.49804e-03f }, // 35/128
{ -5.00800e-03f, 2.84182e-02f, -9.73254e-02f, 3.31048e-01f, 8.44559e-01f, -1.29874e-01f, 3.35600e-02f, -5.59731e-03f }, // 36/128
{ -5.11376e-03f, 2.90433e-02f, -9.96402e-02f, 3.41109e-01f, 8.37557e-01f, -1.31581e-01f, 3.40940e-02f, -5.69280e-03f }, // 37/128
{ -5.21683e-03f, 2.96543e-02f, -1.01915e-01f, 3.51186e-01f, 8.30432e-01f, -1.33189e-01f, 3.46042e-02f, -5.78446e-03f }, // 38/128
{ -5.31716e-03f, 3.02507e-02f, -1.04150e-01f, 3.61276e-01f, 8.23188e-01f, -1.34699e-01f, 3.50903e-02f, -5.87227e-03f }, // 39/128
{ -5.41467e-03f, 3.08323e-02f, -1.06342e-01f, 3.71376e-01f, 8.15826e-01f, -1.36111e-01f, 3.55525e-02f, -5.95620e-03f }, // 40/128
{ -5.50931e-03f, 3.13987e-02f, -1.08490e-01f, 3.81484e-01f, 8.08348e-01f, -1.37426e-01f, 3.59905e-02f, -6.03624e-03f }, // 41/128
{ -5.60103e-03f, 3.19495e-02f, -1.10593e-01f, 3.91596e-01f, 8.00757e-01f, -1.38644e-01f, 3.64044e-02f, -6.11236e-03f }, // 42/128
{ -5.68976e-03f, 3.24843e-02f, -1.12650e-01f, 4.01710e-01f, 7.93055e-01f, -1.39767e-01f, 3.67941e-02f, -6.18454e-03f }, // 43/128
{ -5.77544e-03f, 3.30027e-02f, -1.14659e-01f, 4.11823e-01f, 7.85244e-01f, -1.40794e-01f, 3.71596e-02f, -6.25277e-03f }, // 44/128
{ -5.85804e-03f, 3.35046e-02f, -1.16618e-01f, 4.21934e-01f, 7.77327e-01f, -1.41727e-01f, 3.75010e-02f, -6.31703e-03f }, // 45/128
{ -5.93749e-03f, 3.39894e-02f, -1.18526e-01f, 4.32038e-01f, 7.69305e-01f, -1.42566e-01f, 3.78182e-02f, -6.37730e-03f }, // 46/128
{ -6.01374e-03f, 3.44568e-02f, -1.20382e-01f, 4.42134e-01f, 7.61181e-01f, -1.43313e-01f, 3.81111e-02f, -6.43358e-03f }, // 47/128
{ -6.08674e-03f, 3.49066e-02f, -1.22185e-01f, 4.52218e-01f, 7.52958e-01f, -1.43968e-01f, 3.83800e-02f, -6.48585e-03f }, // 48/128
{ -6.15644e-03f, 3.53384e-02f, -1.23933e-01f, 4.62289e-01f, 7.44637e-01f, -1.44531e-01f, 3.86247e-02f, -6.53412e-03f }, // 49/128
{ -6.22280e-03f, 3.57519e-02f, -1.25624e-01f, 4.72342e-01f, 7.36222e-01f, -1.45004e-01f, 3.88454e-02f, -6.57836e-03f }, // 50/128
{ -6.28577e-03f, 3.61468e-02f, -1.27258e-01f, 4.82377e-01f, 7.27714e-01f, -1.45387e-01f, 3.90420e-02f, -6.61859e-03f }, // 51/128
{ -6.34530e-03f, 3.65227e-02f, -1.28832e-01f, 4.92389e-01f, 7.19116e-01f, -1.45682e-01f, 3.92147e-02f, -6.65479e-03f }, // 52/128
{ -6.40135e-03f, 3.68795e-02f, -1.30347e-01f, 5.02377e-01f, 7.10431e-01f, -1.45889e-01f, 3.93636e-02f, -6.68698e-03f }, // 53/128
{ -6.45388e-03f, 3.72167e-02f, -1.31800e-01f, 5.12337e-01f, 7.01661e-01f, -1.46009e-01f, 3.94886e-02f, -6.71514e-03f }, // 54/128
{ -6.50285e-03f, 3.75341e-02f, -1.33190e-01f, 5.22267e-01f, 6.92808e-01f, -1.46043e-01f, 3.95900e-02f, -6.73929e-03f }, // 55/128
{ -6.54823e-03f, 3.78315e-02f, -1.34515e-01f, 5.32164e-01f, 6.83875e-01f, -1.45993e-01f, 3.96678e-02f, -6.75943e-03f }, // 56/128
{ -6.58996e-03f, 3.81085e-02f, -1.35775e-01f, 5.42025e-01f, 6.74865e-01f, -1.45859e-01f, 3.97222e-02f, -6.77557e-03f }, // 57/128
{ -6.62802e-03f, 3.83650e-02f, -1.36969e-01f, 5.51849e-01f, 6.65779e-01f, -1.45641e-01f, 3.97532e-02f, -6.78771e-03f }, // 58/128
{ -6.66238e-03f, 3.86006e-02f, -1.38094e-01f, 5.61631e-01f, 6.56621e-01f, -1.45343e-01f, 3.97610e-02f, -6.79588e-03f }, // 59/128
{ -6.69300e-03f, 3.88151e-02f, -1.39150e-01f, 5.71370e-01f, 6.47394e-01f, -1.44963e-01f, 3.97458e-02f, -6.80007e-03f }, // 60/128
{ -6.71985e-03f, 3.90083e-02f, -1.40136e-01f, 5.81063e-01f, 6.38099e-01f, -1.44503e-01f, 3.97077e-02f, -6.80032e-03f }, // 61/128
{ -6.74291e-03f, 3.91800e-02f, -1.41050e-01f, 5.90706e-01f, 6.28739e-01f, -1.43965e-01f, 3.96469e-02f, -6.79662e-03f }, // 62/128
{ -6.76214e-03f, 3.93299e-02f, -1.41891e-01f, 6.00298e-01f, 6.19318e-01f, -1.43350e-01f, 3.95635e-02f, -6.78902e-03f }, // 63/128
{ -6.77751e-03f, 3.94578e-02f, -1.42658e-01f, 6.09836e-01f, 6.09836e-01f, -1.42658e-01f, 3.94578e-02f, -6.77751e-03f }, // 64/128
{ -6.78902e-03f, 3.95635e-02f, -1.43350e-01f, 6.19318e-01f, 6.00298e-01f, -1.41891e-01f, 3.93299e-02f, -6.76214e-03f }, // 65/128
{ -6.79662e-03f, 3.96469e-02f, -1.43965e-01f, 6.28739e-01f, 5.90706e-01f, -1.41050e-01f, 3.91800e-02f, -6.74291e-03f }, // 66/128
{ -6.80032e-03f, 3.97077e-02f, -1.44503e-01f, 6.38099e-01f, 5.81063e-01f, -1.40136e-01f, 3.90083e-02f, -6.71985e-03f }, // 67/128
{ -6.80007e-03f, 3.97458e-02f, -1.44963e-01f, 6.47394e-01f, 5.71370e-01f, -1.39150e-01f, 3.88151e-02f, -6.69300e-03f }, // 68/128
{ -6.79588e-03f, 3.97610e-02f, -1.45343e-01f, 6.56621e-01f, 5.61631e-01f, -1.38094e-01f, 3.86006e-02f, -6.66238e-03f }, // 69/128
{ -6.78771e-03f, 3.97532e-02f, -1.45641e-01f, 6.65779e-01f, 5.51849e-01f, -1.36969e-01f, 3.83650e-02f, -6.62802e-03f }, // 70/128
{ -6.77557e-03f, 3.97222e-02f, -1.45859e-01f, 6.74865e-01f, 5.42025e-01f, -1.35775e-01f, 3.81085e-02f, -6.58996e-03f }, // 71/128
{ -6.75943e-03f, 3.96678e-02f, -1.45993e-01f, 6.83875e-01f, 5.32164e-01f, -1.34515e-01f, 3.78315e-02f, -6.54823e-03f }, // 72/128
{ -6.73929e-03f, 3.95900e-02f, -1.46043e-01f, 6.92808e-01f, 5.22267e-01f, -1.33190e-01f, 3.75341e-02f, -6.50285e-03f }, // 73/128
{ -6.71514e-03f, 3.94886e-02f, -1.46009e-01f, 7.01661e-01f, 5.12337e-01f, -1.31800e-01f, 3.72167e-02f, -6.45388e-03f }, // 74/128
{ -6.68698e-03f, 3.93636e-02f, -1.45889e-01f, 7.10431e-01f, 5.02377e-01f, -1.30347e-01f, 3.68795e-02f, -6.40135e-03f }, // 75/128
{ -6.65479e-03f, 3.92147e-02f, -1.45682e-01f, 7.19116e-01f, 4.92389e-01f, -1.28832e-01f, 3.65227e-02f, -6.34530e-03f }, // 76/128
{ -6.61859e-03f, 3.90420e-02f, -1.45387e-01f, 7.27714e-01f, 4.82377e-01f, -1.27258e-01f, 3.61468e-02f, -6.28577e-03f }, // 77/128
{ -6.57836e-03f, 3.88454e-02f, -1.45004e-01f, 7.36222e-01f, 4.72342e-01f, -1.25624e-01f, 3.57519e-02f, -6.22280e-03f }, // 78/128
{ -6.53412e-03f, 3.86247e-02f, -1.44531e-01f, 7.44637e-01f, 4.62289e-01f, -1.23933e-01f, 3.53384e-02f, -6.15644e-03f }, // 79/128
{ -6.48585e-03f, 3.83800e-02f, -1.43968e-01f, 7.52958e-01f, 4.52218e-01f, -1.22185e-01f, 3.49066e-02f, -6.08674e-03f }, // 80/128
{ -6.43358e-03f, 3.81111e-02f, -1.43313e-01f, 7.61181e-01f, 4.42134e-01f, -1.20382e-01f, 3.44568e-02f, -6.01374e-03f }, // 81/128
{ -6.37730e-03f, 3.78182e-02f, -1.42566e-01f, 7.69305e-01f, 4.32038e-01f, -1.18526e-01f, 3.39894e-02f, -5.93749e-03f }, // 82/128
{ -6.31703e-03f, 3.75010e-02f, -1.41727e-01f, 7.77327e-01f, 4.21934e-01f, -1.16618e-01f, 3.35046e-02f, -5.85804e-03f }, // 83/128
{ -6.25277e-03f, 3.71596e-02f, -1.40794e-01f, 7.85244e-01f, 4.11823e-01f, -1.14659e-01f, 3.30027e-02f, -5.77544e-03f }, // 84/128
{ -6.18454e-03f, 3.67941e-02f, -1.39767e-01f, 7.93055e-01f, 4.01710e-01f, -1.12650e-01f, 3.24843e-02f, -5.68976e-03f }, // 85/128
{ -6.11236e-03f, 3.64044e-02f, -1.38644e-01f, 8.00757e-01f, 3.91596e-01f, -1.10593e-01f, 3.19495e-02f, -5.60103e-03f }, // 86/128
{ -6.03624e-03f, 3.59905e-02f, -1.37426e-01f, 8.08348e-01f, 3.81484e-01f, -1.08490e-01f, 3.13987e-02f, -5.50931e-03f }, // 87/128
{ -5.95620e-03f, 3.55525e-02f, -1.36111e-01f, 8.15826e-01f, 3.71376e-01f, -1.06342e-01f, 3.08323e-02f, -5.41467e-03f }, // 88/128
{ -5.87227e-03f, 3.50903e-02f, -1.34699e-01f, 8.23188e-01f, 3.61276e-01f, -1.04150e-01f, 3.02507e-02f, -5.31716e-03f }, // 89/128
{ -5.78446e-03f, 3.46042e-02f, -1.33189e-01f, 8.30432e-01f, 3.51186e-01f, -1.01915e-01f, 2.96543e-02f, -5.21683e-03f }, // 90/128
{ -5.69280e-03f, 3.40940e-02f, -1.31581e-01f, 8.37557e-01f, 3.41109e-01f, -9.96402e-02f, 2.90433e-02f, -5.11376e-03f }, // 91/128
{ -5.59731e-03f, 3.35600e-02f, -1.29874e-01f, 8.44559e-01f, 3.31048e-01f, -9.73254e-02f, 2.84182e-02f, -5.00800e-03f }, // 92/128
{ -5.49804e-03f, 3.30021e-02f, -1.28068e-01f, 8.51437e-01f, 3.21004e-01f, -9.49727e-02f, 2.77794e-02f, -4.89961e-03f }, // 93/128
{ -5.39500e-03f, 3.24205e-02f, -1.26161e-01f, 8.58189e-01f, 3.10980e-01f, -9.25834e-02f, 2.71272e-02f, -4.78866e-03f }, // 94/128
{ -5.28823e-03f, 3.18153e-02f, -1.24154e-01f, 8.64812e-01f, 3.00980e-01f, -9.01591e-02f, 2.64621e-02f, -4.67520e-03f }, // 95/128
{ -5.17776e-03f, 3.11866e-02f, -1.22047e-01f, 8.71305e-01f, 2.91006e-01f, -8.77011e-02f, 2.57844e-02f, -4.55932e-03f }, // 96/128
{ -5.06363e-03f, 3.05345e-02f, -1.19837e-01f, 8.77666e-01f, 2.81060e-01f, -8.52109e-02f, 2.50946e-02f, -4.44107e-03f }, // 97/128
{ -4.94589e-03f, 2.98593e-02f, -1.17526e-01f, 8.83893e-01f, 2.71144e-01f, -8.26900e-02f, 2.43930e-02f, -4.32052e-03f }, // 98/128
{ -4.82456e-03f, 2.91609e-02f, -1.15113e-01f, 8.89984e-01f, 2.61263e-01f, -8.01399e-02f, 2.36801e-02f, -4.19774e-03f }, // 99/128
{ -4.69970e-03f, 2.84397e-02f, -1.12597e-01f, 8.95936e-01f, 2.51417e-01f, -7.75620e-02f, 2.29562e-02f, -4.07279e-03f }, // 100/128
{ -4.57135e-03f, 2.76957e-02f, -1.09978e-01f, 9.01749e-01f, 2.41609e-01f, -7.49577e-02f, 2.22218e-02f, -3.94576e-03f }, // 101/128
{ -4.43955e-03f, 2.69293e-02f, -1.07256e-01f, 9.07420e-01f, 2.31843e-01f, -7.23286e-02f, 2.14774e-02f, -3.81671e-03f }, // 102/128
{ -4.30435e-03f, 2.61404e-02f, -1.04430e-01f, 9.12947e-01f, 2.22120e-01f, -6.96762e-02f, 2.07233e-02f, -3.68570e-03f }, // 103/128
{ -4.16581e-03f, 2.53295e-02f, -1.01501e-01f, 9.18329e-01f, 2.12443e-01f, -6.70018e-02f, 1.99599e-02f, -3.55283e-03f }, // 104/128
{ -4.02397e-03f, 2.44967e-02f, -9.84679e-02f, 9.23564e-01f, 2.02814e-01f, -6.43069e-02f, 1.91877e-02f, -3.41815e-03f }, // 105/128
{ -3.87888e-03f, 2.36423e-02f, -9.53307e-02f, 9.28650e-01f, 1.93236e-01f, -6.15931e-02f, 1.84071e-02f, -3.28174e-03f }, // 106/128
{ -3.73062e-03f, 2.27664e-02f, -9.20893e-02f, 9.33586e-01f, 1.83711e-01f, -5.88617e-02f, 1.76185e-02f, -3.14367e-03f }, // 107/128
{ -3.57923e-03f, 2.18695e-02f, -8.87435e-02f, 9.38371e-01f, 1.74242e-01f, -5.61142e-02f, 1.68225e-02f, -3.00403e-03f }, // 108/128
{ -3.42477e-03f, 2.09516e-02f, -8.52933e-02f, 9.43001e-01f, 1.64831e-01f, -5.33522e-02f, 1.60193e-02f, -2.86289e-03f }, // 109/128
{ -3.26730e-03f, 2.00132e-02f, -8.17385e-02f, 9.47477e-01f, 1.55480e-01f, -5.05770e-02f, 1.52095e-02f, -2.72032e-03f }, // 110/128
{ -3.10689e-03f, 1.90545e-02f, -7.80792e-02f, 9.51795e-01f, 1.46192e-01f, -4.77900e-02f, 1.43934e-02f, -2.57640e-03f }, // 111/128
{ -2.94361e-03f, 1.80759e-02f, -7.43154e-02f, 9.55956e-01f, 1.36968e-01f, -4.49929e-02f, 1.35716e-02f, -2.43121e-03f }, // 112/128
{ -2.77751e-03f, 1.70776e-02f, -7.04471e-02f, 9.59958e-01f, 1.27812e-01f, -4.21869e-02f, 1.27445e-02f, -2.28483e-03f }, // 113/128
{ -2.60868e-03f, 1.60599e-02f, -6.64743e-02f, 9.63798e-01f, 1.18725e-01f, -3.93735e-02f, 1.19125e-02f, -2.13733e-03f }, // 114/128
{ -2.43718e-03f, 1.50233e-02f, -6.23972e-02f, 9.67477e-01f, 1.09710e-01f, -3.65541e-02f, 1.10760e-02f, -1.98880e-03f }, // 115/128
{ -2.26307e-03f, 1.39681e-02f, -5.82159e-02f, 9.70992e-01f, 1.00769e-01f, -3.37303e-02f, 1.02356e-02f, -1.83931e-03f }, // 116/128
{ -2.08645e-03f, 1.28947e-02f, -5.39305e-02f, 9.74342e-01f, 9.19033e-02f, -3.09033e-02f, 9.39154e-03f, -1.68894e-03f }, // 117/128
{ -1.90738e-03f, 1.18034e-02f, -4.95412e-02f, 9.77526e-01f, 8.31162e-02f, -2.80746e-02f, 8.54441e-03f, -1.53777e-03f }, // 118/128
{ -1.72594e-03f, 1.06946e-02f, -4.50483e-02f, 9.80543e-01f, 7.44095e-02f, -2.52457e-02f, 7.69462e-03f, -1.38589e-03f }, // 119/128
{ -1.54221e-03f, 9.56876e-03f, -4.04519e-02f, 9.83392e-01f, 6.57852e-02f, -2.24178e-02f, 6.84261e-03f, -1.23337e-03f }, // 120/128
{ -1.35627e-03f, 8.42626e-03f, -3.57525e-02f, 9.86071e-01f, 5.72454e-02f, -1.95925e-02f, 5.98883e-03f, -1.08030e-03f }, // 121/128
{ -1.16820e-03f, 7.26755e-03f, -3.09503e-02f, 9.88580e-01f, 4.87921e-02f, -1.67710e-02f, 5.13372e-03f, -9.26747e-04f }, // 122/128
{ -9.78093e-04f, 6.09305e-03f, -2.60456e-02f, 9.90917e-01f, 4.04274e-02f, -1.39548e-02f, 4.27773e-03f, -7.72802e-04f }, // 123/128
{ -7.86031e-04f, 4.90322e-03f, -2.10389e-02f, 9.93082e-01f, 3.21531e-02f, -1.11453e-02f, 3.42130e-03f, -6.18544e-04f }, // 124/128
{ -5.92100e-04f, 3.69852e-03f, -1.59305e-02f, 9.95074e-01f, 2.39714e-02f, -8.34364e-03f, 2.56486e-03f, -4.64053e-04f }, // 125/128
{ -3.96391e-04f, 2.47942e-03f, -1.07209e-02f, 9.96891e-01f, 1.58840e-02f, -5.55134e-03f, 1.70888e-03f, -3.09412e-04f }, // 126/128
{ -1.98993e-04f, 1.24642e-03f, -5.41054e-03f, 9.98534e-01f, 7.89295e-03f, -2.76968e-03f, 8.53777e-04f, -1.54700e-04f }, // 127/128
{ 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 1.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f }, // 128/128
};
private static final int NUMBER_FILTER_TAPS = 8;
private static final int NUMBER_FILTER_STEPS = 128;
private static final int SAMPLE_RATE = 48000;
private static final int SYMBOL_RATE = 4800;
/* Tracking loop gain constant */
private static final double K_SYMBOL_SPREAD = 0.0100;
/* Constraints on symbol spreading */
private static final float SYMBOL_SPREAD_MAX = 2.4f; // upper range limit: +20%
private static final float SYMBOL_SPREAD_MIN = 1.6f; // lower range limit: -20%
/* Symbol clock tracking loop gain */
private static final double K_SYMBOL_TIMING = 0.025;
/* Coarse and fine frequency tracking constants */
private static final double K_COARSE_FREQUENCY = 0.00125;
private static final double K_FINE_FREQUENCY = 0.125;
/* Frequency correction broadcast threshold */
private static final double COARSE_FREQUENCY_DEADBAND = 1.66;
/* 2.0 symbol spread gives -3, -1, 1, 3 */
private float mSymbolSpread = 2.0f;
private float mSymbolClock = 0.0f;
private float mSymbolTime = (float)SYMBOL_RATE / (float)SAMPLE_RATE;
private float mFineFrequencyCorrection = 0.0f;
private float mCoarseFrequencyCorrection = 0.0f;
private float mHistory[] = new float[ NUMBER_FILTER_TAPS ];
private int mHistoryLast = 0;
private RealSampleListener mListener;
/**
* C4FM Symbol Filter
*
* Sample Gain values - the gain of incoming sample values will critically
* impact the performance of this filter. Gain should be adjusted to
* optimize the value of mSymbolSpread toward a value of 2.0. If the value
* is less than 2.0, then increase gain. If the value is over 2.0, then
* decrease gain.
*
* During testing, the incoming samples were within the range of -0.5 <> 0.5
* and applying a gain of 11.0 to those samples achieved the best symbol
* spread, centered about 2.0 with +/- 0.5% deviations on a strong signal.
*/
public C4FMSymbolFilter()
{
}
@Override
public void receive( float sample )
{
mSymbolClock += mSymbolTime;
// mHistory[ mHistoryLast++ ] = sample;
mHistory[ mHistoryLast++ ] = sample * 10.5f;
mHistoryLast %= NUMBER_FILTER_TAPS;
if( mSymbolClock > 1.0f )
{
mSymbolClock -= 1.0f;
int imu = (int)Math.floor( 0.5 +
( (float)NUMBER_FILTER_STEPS * ( mSymbolClock / mSymbolTime ) ) );
if( imu >= NUMBER_FILTER_STEPS )
{
imu = NUMBER_FILTER_STEPS - 1;
}
int imu_p1 = imu + 1;
int j = mHistoryLast;
double interp = 0.0;
double interp_p1 = 0.0;
for( int i = 0; i < NUMBER_FILTER_TAPS; i++ )
{
interp += TAPS[ imu][ i ] * mHistory[ j ];
interp_p1 += TAPS[ imu_p1 ][ i ] * mHistory[ j ];
j = ( j + 1 ) % NUMBER_FILTER_TAPS;
}
/* Output symbol will be interpolated value corrected for symbol
* spread and frequency offset */
interp -= mFineFrequencyCorrection;
interp_p1 -= mFineFrequencyCorrection;
/* Correct output for symbol deviation (spread) */
float output = (float)( 2.0 * interp / mSymbolSpread );
/* Detect received symbol error: basically use a hard decision and
* subtract off expected position nominal symbol level which will be
* +/- 0.5 * symbol spread and +/- 1.5 symbol spread. Remember that
* nominal symbol spread will be 2.0 */
double symbolError;
if( interp < -mSymbolSpread )
{
/* symbol is -3: Expected at -1.5 * symbol spread */
symbolError = interp + ( 1.5 * mSymbolSpread );
mSymbolSpread -= ( symbolError * 0.5 * K_SYMBOL_SPREAD );
}
else if( interp < 0.0 )
{
/* symbol is -1: Expected at -0.5 * symbol_spread */
symbolError = interp + (0.5 * mSymbolSpread );
mSymbolSpread -= ( symbolError * K_SYMBOL_SPREAD );
}
else if( interp < mSymbolSpread )
{
/* symbol is +1: Expected at +0.5 * symbol_spread */
symbolError = interp - ( 0.5 * mSymbolSpread );
mSymbolSpread += ( symbolError * K_SYMBOL_SPREAD );
}
else
{
/* symbol is +3: Expected at +1.5 * symbol_spread */
symbolError = interp - ( 1.5 * mSymbolSpread );
mSymbolSpread += ( symbolError * 0.5 * K_SYMBOL_SPREAD );
}
/* Symbol clock tracking loop adjustment */
if( interp_p1 < interp )
{
mSymbolClock += symbolError * K_SYMBOL_TIMING;
}
else
{
mSymbolClock -= symbolError * K_SYMBOL_TIMING;
}
mSymbolSpread = Math.max( mSymbolSpread, SYMBOL_SPREAD_MIN );
mSymbolSpread = Math.min( mSymbolSpread, SYMBOL_SPREAD_MAX );
mCoarseFrequencyCorrection += ( ( mFineFrequencyCorrection -
mCoarseFrequencyCorrection ) * K_COARSE_FREQUENCY );
mFineFrequencyCorrection += ( symbolError * K_FINE_FREQUENCY );
/* send frequency correction */
if( Math.abs( mCoarseFrequencyCorrection ) > COARSE_FREQUENCY_DEADBAND )
{
mLog.debug( "Frequency Correction [" +
( mCoarseFrequencyCorrection > 0 ? "-" : "+" ) +
mCoarseFrequencyCorrection + "]" );
}
/* dispatch the interpolated value to the listener */
if( mListener != null )
{
mListener.receive( output );
}
}
}
@Override
public void setListener( RealSampleListener listener )
{
mListener = listener;
}
@Override
public void removeListener( RealSampleListener listener )
{
mListener = null;
}
}

View File

@@ -28,14 +28,13 @@ import java.util.List;
import sample.Listener;
import sample.real.RealSampleListener;
import dsp.Slicer;
import dsp.Slicer.Output;
import dsp.filter.DCRemovalFilter3;
import dsp.filter.Filters;
import dsp.filter.FloatHalfBandFilter;
import dsp.filter.FloatHalfBandNoDecimateFilter;
import dsp.filter.LTRPulseShapingFilter;
import dsp.filter.SquaringFilter;
import dsp.fsk.Slicer.Output;
public class LTRFSKDecoder implements RealSampleListener, Instrumentable
{

View File

@@ -0,0 +1,374 @@
package dsp.fsk;
import java.util.ArrayList;
import java.util.BitSet;
import message.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import bits.BitSetBuffer;
import bits.BitSetFullException;
import bits.SyncPatternMatcher;
import decode.p25.P25Interleave;
import decode.p25.TrellisHalfRate;
import decode.p25.message.P25Message;
import decode.p25.message.PDUMessage;
import decode.p25.message.TSBKMessage;
import decode.p25.message.TSBKMessageFactory;
import decode.p25.reference.DataUnitID;
public class P25MessageFramer implements Listener<C4FMSymbol>
{
private final static Logger mLog =
LoggerFactory.getLogger( P25MessageFramer.class );
private static final int[] STATUS_BITS =
new int[] { 22,92,162,232,302,372,442,512,582,652,722,792,862,932 };
private ArrayList<P25MessageAssembler> mAssemblers =
new ArrayList<P25MessageAssembler>();
public static final int TSBK_BEGIN = 64;
public static final int TSBK_END = 260;
public static final int TSBK_DECODED_END = 162;
private Listener<Message> mListener;
private SyncPatternMatcher mMatcher;
private boolean mInverted = false;
private TrellisHalfRate mHalfRate = new TrellisHalfRate();
/**
* Constructs a C4FM message framer to receive a stream of C4FM symbols and
* detect the sync pattern then capture the following stream of symbols up
* to the message length, and then broadcast that bit buffer to the registered
* listener.
*
* @param sync - sync pattern (maximum of 63 bits)
* @param messageLength - in bits
* @param inverted - optional flag to indicate the symbol stream should be
* uninverted prior to processing
*/
public P25MessageFramer( long sync, int messageLength, boolean inverted )
{
mMatcher = new SyncPatternMatcher( sync );
mInverted = inverted;
/**
* We use two message assemblers to catch any sync detections, so that
* if we have a false trigger then we still have a chance to catch the
* valid message. If we have two false triggers before the arrival of
* the actual message sync, then the third or subsequent real or false
* sync pattern will produce a debug message indicating no assemblers
* are available..
*/
mAssemblers.add( new P25MessageAssembler() );
mAssemblers.add( new P25MessageAssembler() );
}
private void dispatch( Message message )
{
if( mListener != null )
{
mListener.receive( message );
}
}
@Override
public void receive( C4FMSymbol symbol )
{
C4FMSymbol correctedSymbol = symbol;
mMatcher.receive( correctedSymbol.getBit1() );
mMatcher.receive( correctedSymbol.getBit2() );
for( P25MessageAssembler assembler: mAssemblers )
{
if( assembler.isActive() )
{
assembler.receive( correctedSymbol );
if( assembler.complete() )
{
assembler.reset();
}
}
}
/* Check for sync match and activate a message assembler, if we can
* find an inactive assembler. Otherwise, ignore and log the issue */
if( mMatcher.matches() )
{
boolean found = false;
for( P25MessageAssembler assembler: mAssemblers )
{
if( !assembler.isActive() )
{
assembler.setActive( true );
found = true;
break;
}
}
if( !found )
{
mLog.debug( "no inactive C4FM message assemblers available" );
}
}
}
public void setListener( Listener<Message> listener )
{
mListener = listener;
}
public void removeListener( Listener<Message> listener )
{
mListener = null;
}
private class P25MessageAssembler
{
private int mStatusIndicatorPointer = 0;
private BitSetBuffer mMessage;
private int mMessageLength;
private boolean mComplete = false;
private boolean mActive = false;
private DataUnitID mDUID = DataUnitID.NID;
public P25MessageAssembler()
{
mMessageLength = mDUID.getMessageLength();
mMessage = new BitSetBuffer( mMessageLength );
reset();
}
public void receive( C4FMSymbol symbol )
{
if( mActive )
{
/* Throw away status bits that are injected every 70 bits */
if( mMessage.pointer() == STATUS_BITS[ mStatusIndicatorPointer ] )
{
mStatusIndicatorPointer++;
}
else
{
try
{
mMessage.add( symbol.getBit1() );
mMessage.add( symbol.getBit2() );
}
catch( BitSetFullException e )
{
mComplete = true;
}
/* Check the message for complete */
if( mMessage.isFull() )
{
checkComplete();
}
}
}
}
public void reset()
{
mDUID = DataUnitID.NID;
mMessage.setSize( mDUID.getMessageLength() );
mMessage.clear();
mStatusIndicatorPointer = 0;
mComplete = false;
mActive = false;
}
public void setActive( boolean active )
{
mActive = active;
}
private void setDUID( DataUnitID id )
{
mDUID = id;
mMessageLength = id.getMessageLength();
mMessage.setSize( mMessageLength );
}
private void checkComplete()
{
switch( mDUID )
{
case NID:
int value = mMessage.getInt( P25Message.DUID );
DataUnitID duid = DataUnitID.fromValue( value );
if( duid != DataUnitID.UNKN )
{
setDUID( duid );
}
else
{
mComplete = true;
}
break;
case HDU:
mComplete = true;
dispatch( new P25Message( mMessage.copy(), mDUID ) );
break;
case LDU1:
mComplete = true;
dispatch( new P25Message( mMessage.copy(), mDUID ) );
break;
case LDU2:
mComplete = true;
dispatch( new P25Message( mMessage.copy(), mDUID ) );
break;
case PDU1:
int blocks = mMessage.getInt(
PDUMessage.PDU_HEADER_BLOCKS_TO_FOLLOW );
int padBlocks = mMessage.getInt( PDUMessage.PDU_HEADER_PAD_BLOCKS );
int blockCount = blocks + padBlocks;
if( blockCount == 24 || blockCount == 32 )
{
setDUID( DataUnitID.PDU2 );
}
else if( blockCount == 36 || blockCount == 48 )
{
setDUID( DataUnitID.PDU3 );
}
else
{
dispatch( new PDUMessage( mMessage.copy(), mDUID ) );
mComplete = true;
}
break;
case PDU2:
case PDU3:
mComplete = true;
dispatch( new P25Message( mMessage.copy(), mDUID ) );
break;
case TDU:
mComplete = true;
dispatch( new P25Message( mMessage.copy(), mDUID ) );
break;
case TDULC:
mComplete = true;
dispatch( new P25Message( mMessage.copy(), mDUID ) );
break;
case TSBK1:
/* Remove interleaving */
P25Interleave.deinterleave( mMessage, TSBK_BEGIN, TSBK_END );
/* Remove trellis encoding */
mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END );
/* Construct the message */
int tsbkSystem1 = mMessage.getInt( P25Message.NAC );
BitSetBuffer tsbkBuffer1 = new BitSetBuffer( mMessage.get(
TSBK_BEGIN, TSBK_DECODED_END ), 96 );
TSBKMessage tsbkMessage1 =
TSBKMessageFactory.getMessage( tsbkSystem1, tsbkBuffer1 );
if( tsbkMessage1.isLastBlock() )
{
mComplete = true;
}
else
{
setDUID( DataUnitID.TSBK2 );
mMessage.setPointer( TSBK_BEGIN );
}
dispatch( tsbkMessage1 );
break;
case TSBK2:
/* Remove interleaving */
P25Interleave.deinterleave( mMessage, TSBK_BEGIN, TSBK_END );
/* Remove trellis encoding */
mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END );
/* Construct the message */
int tsbkSystem2 = mMessage.getInt( P25Message.NAC );
BitSetBuffer tsbkBuffer2 = new BitSetBuffer( mMessage.get(
TSBK_BEGIN, TSBK_DECODED_END ), 98 );
TSBKMessage tsbkMessage2 =
TSBKMessageFactory.getMessage( tsbkSystem2, tsbkBuffer2 );
if( tsbkMessage2.isLastBlock() )
{
mComplete = true;
}
else
{
setDUID( DataUnitID.TSBK3 );
mMessage.setPointer( TSBK_BEGIN );
}
dispatch( tsbkMessage2 );
break;
case TSBK3:
/* Remove interleaving */
P25Interleave.deinterleave( mMessage, TSBK_BEGIN, TSBK_END );
/* Remove trellis encoding */
mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END );
/* Construct the message */
int tsbkSystem3 = mMessage.getInt( P25Message.NAC );
BitSetBuffer tsbkBuffer3 = new BitSetBuffer( mMessage.get(
TSBK_BEGIN, TSBK_DECODED_END ), 96 );
TSBKMessage tsbkMessage3 =
TSBKMessageFactory.getMessage( tsbkSystem3, tsbkBuffer3 );
mComplete = true;
dispatch( tsbkMessage3 );
break;
case UNKN:
mComplete = true;
dispatch( new P25Message( mMessage.copy(), mDUID ) );
break;
default:
mComplete = true;
break;
}
}
public void dispose()
{
mMessage = null;
mHalfRate.dispose();
}
/**
* Flag to indicate when this assembler has received all of the bits it
* is looking for (ie message length), and should then be removed from
* receiving any more bits
*/
public boolean complete()
{
return mComplete;
}
public boolean isActive()
{
return mActive;
}
}
}

151
src/dsp/fsk/Slicer.java Normal file
View File

@@ -0,0 +1,151 @@
package dsp.fsk;
import instrument.tap.stream.SymbolEventTap;
import java.util.BitSet;
import sample.Listener;
import dsp.fsk.SymbolEvent.Shift;
/**
* Symbol slicer with auto-aligning baud timing
*/
public class Slicer implements Listener<Boolean>
{
public enum Output{ NORMAL, INVERTED };
private BitSet mBitSet = new BitSet();
private int mSymbolLength;
private int mDecisionThreshold;
private int mSampleCounter;
private SymbolEventTap mSymbolEventTap;
private boolean mNormalOutput = true;
private Listener<Boolean> mListener;
public Slicer( Output output, int samplesPerSymbol )
{
mNormalOutput = ( output == Output.NORMAL );
mSymbolLength = samplesPerSymbol;
/* Round up */
mDecisionThreshold = (int)( mSymbolLength / 2 ) + ( mSymbolLength % 2 );
}
public void receive( Boolean sample )
{
if( mSampleCounter >= 0 )
{
if( sample )
{
mBitSet.set( mSampleCounter );
}
else
{
mBitSet.clear( mSampleCounter );
}
}
mSampleCounter++;
if( mSampleCounter >= mSymbolLength )
{
boolean decision = mBitSet.cardinality() >= mDecisionThreshold;
send( decision );
/* Shift timing left if the left bit in the bitset is opposite
* the decision and the right bit is the same */
if( ( mBitSet.get( 0 ) ^ decision ) &&
( !( mBitSet.get( mSymbolLength - 1 ) ^ decision ) ) )
{
sendTapEvent( mBitSet, Shift.LEFT, decision );
reset();
mSampleCounter--;
}
/* Shift timing right if the left bit is the same as the
* decision and the right bit is opposite */
else if( ( !( mBitSet.get( 0 ) ^ decision ) ) &&
( mBitSet.get( mSymbolLength - 1 ) ^ decision ) )
{
sendTapEvent( mBitSet, Shift.RIGHT, decision );
/* Last bit from previous symbol to pre-fill next symbol */
boolean previousSoftBit = mBitSet.get( mSymbolLength - 1 );
reset();
if( previousSoftBit )
{
mBitSet.set( 0 );
}
mSampleCounter++;
}
/* No shift */
else
{
sendTapEvent( mBitSet, Shift.NONE, decision );
reset();
}
}
}
/**
* Sends the bit decision to the listener
*/
private void send( boolean decision )
{
if( mListener != null )
{
mListener.receive( mNormalOutput ? decision : !decision );
}
}
private void reset()
{
mBitSet.clear();
mSampleCounter = 0;
}
public void setListener( Listener<Boolean> listener)
{
mListener = listener;
}
public void removeListener( Listener<Boolean> listener )
{
mListener = null;
}
/**
* Sends instrumentation tap event to all registered listeners
*/
private void sendTapEvent( BitSet bitset, Shift shift, boolean decision )
{
if( mSymbolEventTap != null )
{
SymbolEvent event =
new SymbolEvent( bitset.get( 0, mSymbolLength ),
mSymbolLength,
decision,
shift );
mSymbolEventTap.receive( event );
}
}
public void addTap( SymbolEventTap tap )
{
mSymbolEventTap = tap;
}
public void removeTap()
{
mSymbolEventTap = null;
}
}