Updates pull request from Roman Tsypuk to convert the build system from

ant to gradle.  Removes linux and windows application launch scripts and
produces an executable jar with all resources included.
This commit is contained in:
Denny
2017-12-17 07:28:03 -05:00
parent a1535c4157
commit 9316599dec
981 changed files with 8368 additions and 9049 deletions

View File

@@ -0,0 +1,302 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package io.github.dsheirer.dsp.fsk;
import io.github.dsheirer.buffer.BooleanAveragingBuffer;
import io.github.dsheirer.instrument.Instrumentable;
import io.github.dsheirer.instrument.tap.Tap;
import io.github.dsheirer.instrument.tap.TapGroup;
import io.github.dsheirer.instrument.tap.stream.SymbolEventTap;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.real.RealBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.github.dsheirer.dsp.symbol.SymbolEvent;
import io.github.dsheirer.dsp.symbol.SymbolEvent.Shift;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
/**
* Binary Frequency Shift Keying (FSK) decoder. Implements a BFSK correlation
* decoder. Provides normal or inverted decoded output. Requires the symbol
* rate to be an integer multiple of the sample rate.
*
* Implements a correlation decoder that correlates a modulated FSK signal upon
* against a one-baud delayed version of itself, converting all float samples to
* binary (0 or 1), and uses XOR to produce the correlation value. Low pass
* filtering smoothes the correlated output.
*
* Automatically aligns to symbol timing during symbol transitions (0 to 1, or
* 1 to 0) by inspecting the samples at the symbol edges and advancing or
* retarding symbol window to maintain continuous symbol alignment.
*
* Use a DC-removal filter prior to this decoder to ensure samples don't have
* a DC component.
*
* Implements instrumentable interface, so that slice events can be received
* externally to analyze decoder performance.
*/
public class FSK2Decoder implements Instrumentable, Listener<RealBuffer>
{
private final static Logger mLog = LoggerFactory.getLogger(FSK2Decoder.class);
public enum Output
{
NORMAL, INVERTED
}
/* Instrumentation taps */
private static final String INSTRUMENT_DECISION =
"Tap Point: FSK2 Symbol Decision";
private List<TapGroup> mAvailableTaps;
private ArrayList<SymbolEventTap> mTaps = new ArrayList<SymbolEventTap>();
private Listener<Boolean> mListener;
private BooleanAveragingBuffer mDelayBuffer;
private BooleanAveragingBuffer mLowPassFilter;
private Slicer mSlicer;
private boolean mNormalOutput;
private int mSamplesPerSymbol;
private int mSymbolRate;
public FSK2Decoder(int sampleRate, int symbolRate, Output output)
{
/* Ensure we're using an integral symbolRate of the sampleRate */
assert (sampleRate % symbolRate == 0);
mSamplesPerSymbol = (int) (sampleRate / symbolRate);
mNormalOutput = (output == Output.NORMAL);
mSymbolRate = symbolRate;
mDelayBuffer = new BooleanAveragingBuffer(mSamplesPerSymbol);
mLowPassFilter = new BooleanAveragingBuffer(mSamplesPerSymbol);
mSlicer = new Slicer(mSamplesPerSymbol);
}
/**
* Disposes of all references to prepare for garbage collection
*/
public void dispose()
{
mListener = null;
}
/**
* Primary sample input
*/
@Override
public void receive(RealBuffer buffer)
{
for(float sample : buffer.getSamples())
{
/* Square the sample. Greater than zero is a 1 (true) and less than
* zero is a 0 (false) */
boolean bitSample = (sample >= 0.0f);
/* Feed the delay buffer and fetch the one-baud delayed sample */
boolean delayedBitSample = mDelayBuffer.get(bitSample);
/* Correlation: xor current bit with delayed bit */
boolean softBit = bitSample ^ delayedBitSample;
/* Low pass filter to smooth the correlated values */
boolean filteredSoftBit = mLowPassFilter.getAverage(softBit);
/* Send the filtered correlated bit to the slicer */
mSlicer.receive(filteredSoftBit);
}
}
/**
* Registers a listener to receive the decoded FSK bits
*/
public void setListener(Listener<Boolean> listener)
{
mListener = listener;
}
/**
* Removes the listener
*/
public void removeListener(Listener<Boolean> listener)
{
mListener = null;
}
/**
* Symbol slicer with auto-aligning baud timing
*/
public class Slicer
{
private BitSet mBitSet = new BitSet();
private int mSymbolLength;
private int mDecisionThreshold;
private int mSampleCounter;
public Slicer(int samplesPerSymbol)
{
mSymbolLength = samplesPerSymbol;
mDecisionThreshold = (int) (mSymbolLength / 2);
/* Adjust for an odd number of samples per baud */
if(mSymbolLength % 2 == 1)
{
mDecisionThreshold++;
}
}
public void receive(boolean softBit)
{
if(mSampleCounter >= 0)
{
if(softBit)
{
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;
}
/**
* Sends instrumentation tap event to all registered listeners
*/
private void sendTapEvent(BitSet bitset, Shift shift, boolean decision)
{
for(SymbolEventTap tap : mTaps)
{
SymbolEvent event =
new SymbolEvent(bitset.get(0, mSymbolLength),
mSymbolLength,
decision,
shift);
tap.receive(event);
}
}
}
/**
* Get instrumentation taps
*/
@Override
public List<TapGroup> getTapGroups()
{
if(mAvailableTaps == null)
{
mAvailableTaps = new ArrayList<TapGroup>();
TapGroup tapGroup = new TapGroup("FSK2 Decoder");
tapGroup.add(new SymbolEventTap(INSTRUMENT_DECISION, 0, .025f));
mAvailableTaps.add(tapGroup);
}
return mAvailableTaps;
}
/**
* Add instrumentation tap
*/
@Override
public void registerTap(Tap tap)
{
if(tap instanceof SymbolEventTap && !mTaps.contains(tap))
{
mTaps.add((SymbolEventTap) tap);
}
}
/**
* Remove instrumentation tap
*/
@Override
public void unregisterTap(Tap tap)
{
mTaps.remove(tap);
}
}

View File

@@ -0,0 +1,253 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package io.github.dsheirer.dsp.fsk;
import io.github.dsheirer.dsp.filter.Filters;
import io.github.dsheirer.dsp.filter.FloatHalfBandNoDecimateFilter;
import io.github.dsheirer.dsp.filter.LTRPulseShapingFilter;
import io.github.dsheirer.dsp.filter.SquaringFilter;
import io.github.dsheirer.instrument.Instrumentable;
import io.github.dsheirer.instrument.tap.Tap;
import io.github.dsheirer.instrument.tap.TapGroup;
import io.github.dsheirer.instrument.tap.stream.BinaryTap;
import io.github.dsheirer.instrument.tap.stream.FloatBufferTap;
import io.github.dsheirer.instrument.tap.stream.FloatTap;
import io.github.dsheirer.instrument.tap.stream.SymbolEventTap;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.real.RealBuffer;
import io.github.dsheirer.dsp.filter.FloatHalfBandFilter;
import io.github.dsheirer.dsp.filter.Window.WindowType;
import io.github.dsheirer.dsp.filter.cic.RealPrimeCICDecimate;
import io.github.dsheirer.dsp.filter.dc.IIRSinglePoleDCRemovalFilter;
import io.github.dsheirer.dsp.symbol.Slicer;
import io.github.dsheirer.dsp.symbol.Slicer.Output;
import java.util.ArrayList;
import java.util.List;
public class LTRFSKDecoder implements Listener<RealBuffer>, Instrumentable
{
private RealPrimeCICDecimate mCICDecimator;
private FloatHalfBandFilter mHBFilter1;
private FloatHalfBandFilter mHBFilter2;
private FloatHalfBandFilter mHBFilter3;
private FloatHalfBandFilter mHBFilter4;
private FloatHalfBandFilter mHBFilter5;
private FloatHalfBandNoDecimateFilter mHBFilter6;
private IIRSinglePoleDCRemovalFilter mDCFilter;
private SquaringFilter mSquaringFilter;
private LTRPulseShapingFilter mPulseShaper;
private Slicer mSlicer;
private List<TapGroup> mAvailableTaps;
private final String TAP_F1_F2 = "LTR FSK Demod Filter1 >< Filter2";
private final String TAP_F2_F3 = "LTR FSK Demod Filter2 >< Filter3";
private final String TAP_F3_F4 = "LTR FSK Demod Filter3 >< Filter4";
private final String TAP_F4_F5 = "LTR FSK Demod Filter4 >< Filter5";
private final String TAP_F5_F6 = "LTR FSK Demod Filter5 >< Filter6";
private final String TAP_F6_DT = "LTR FSK Demod Filter6 >< DC Filter";
private final String TAP_CIC = "LTR FSK Demod CIC Decimator";
private final String TAP_DT_SF1 = "LTR FSK Demod DC Filter >< Squaring Filter";
private final String TAP_SF1_PS = "LTR FSK Demod Squaring Filter >< Pulse Shaper";
private final String TAP_PS_SLICER = "LTR FSK Demod Pulse Shaper >< Slicer";
private final String TAP_SYMBOL_EVENT = "LTR FSK Demod Symbol Event";
/**
* Implements a Logic Trunked Radio sub-audible 300 baud FSK signaling
* decoder. Expects a 48000 sample rate input.
*/
public LTRFSKDecoder()
{
mCICDecimator = new RealPrimeCICDecimate( 32, 1, 375, 60, WindowType.HAMMING, 2 );
mHBFilter1 = new FloatHalfBandFilter(
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f );
mHBFilter2 = new FloatHalfBandFilter(
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f );
mHBFilter1.setListener( mHBFilter2 );
mHBFilter3 = new FloatHalfBandFilter(
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f );
mHBFilter2.setListener( mHBFilter3 );
mHBFilter4 = new FloatHalfBandFilter(
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f );
mHBFilter3.setListener( mHBFilter4 );
mHBFilter5 = new FloatHalfBandFilter(
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f );
mHBFilter4.setListener( mHBFilter5 );
mHBFilter6 = new FloatHalfBandNoDecimateFilter(
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f );
mHBFilter5.setListener( mHBFilter6 );
mDCFilter = new IIRSinglePoleDCRemovalFilter( 0.9946f );
mHBFilter6.setListener( mDCFilter );
mSquaringFilter = new SquaringFilter();
mDCFilter.setListener( mSquaringFilter );
mPulseShaper = new LTRPulseShapingFilter();
mSquaringFilter.setListener( mPulseShaper );
mSlicer = new Slicer( Output.NORMAL, 5 );
mPulseShaper.setListener( mSlicer );
}
@Override
public void receive( RealBuffer buffer )
{
for( float sample: buffer.getSamples() )
{
mHBFilter1.receive( sample );
}
mCICDecimator.receive(buffer);
}
public void addListener( Listener<Boolean> listener )
{
mSlicer.setListener( listener );
}
public void removeListener( Listener<Boolean> listener )
{
mSlicer.removeListener( listener );
}
@Override
public List<TapGroup> getTapGroups()
{
if( mAvailableTaps == null )
{
mAvailableTaps = new ArrayList<TapGroup>();
TapGroup group = new TapGroup( "LTR FSK Decoder" );
group.add( new FloatTap( TAP_F1_F2, 0, 1.0f ) );
group.add( new FloatTap( TAP_F2_F3, 8, 0.5f ) );
group.add( new FloatTap( TAP_F3_F4, 12, 0.25f ) );
group.add( new FloatTap( TAP_F4_F5, 14, 0.125f ) );
group.add( new FloatTap( TAP_F5_F6, 15, 0.0625f ) );
group.add( new FloatTap( TAP_F6_DT, 31, 0.0625f ) );
group.add( new FloatBufferTap( TAP_CIC, 31, 0.0625f ) );
group.add( new FloatTap( TAP_DT_SF1, 31, 0.0625f ) );
group.add( new BinaryTap( TAP_SF1_PS, 33, 0.0625f ) );
group.add( new BinaryTap( TAP_PS_SLICER, 33, 0.0625f ) );
group.add( new SymbolEventTap( TAP_SYMBOL_EVENT, 7, 0.0125f ) );
mAvailableTaps.add( group );
}
return mAvailableTaps;
}
@Override
public void registerTap( Tap tap )
{
switch( tap.getName() )
{
case TAP_CIC:
mCICDecimator.setListener( (FloatBufferTap)tap );
break;
case TAP_F1_F2:
mHBFilter1.setListener( (FloatTap)tap );
((FloatTap)tap).setListener( mHBFilter2 );
break;
case TAP_F2_F3:
mHBFilter2.setListener( (FloatTap)tap );
((FloatTap)tap).setListener( mHBFilter3 );
break;
case TAP_F3_F4:
mHBFilter3.setListener( (FloatTap)tap );
((FloatTap)tap).setListener( mHBFilter4 );
break;
case TAP_F4_F5:
mHBFilter4.setListener( (FloatTap)tap );
((FloatTap)tap).setListener( mHBFilter5 );
break;
case TAP_F5_F6:
mHBFilter5.setListener( (FloatTap)tap );
((FloatTap)tap).setListener( mHBFilter6 );
break;
case TAP_F6_DT:
mHBFilter6.setListener( (FloatTap)tap );
((FloatTap)tap).setListener( mDCFilter );
break;
case TAP_DT_SF1:
mDCFilter.setListener( (FloatTap)tap );
((FloatTap)tap).setListener( mSquaringFilter );
break;
case TAP_SF1_PS:
mSquaringFilter.setListener( (BinaryTap)tap );
((BinaryTap)tap).setListener( mPulseShaper );
break;
case TAP_PS_SLICER:
mPulseShaper.setListener( (BinaryTap)tap );
((BinaryTap)tap).setListener( mSlicer );
break;
case TAP_SYMBOL_EVENT:
mSlicer.addTap( (SymbolEventTap)tap );
break;
}
}
@Override
public void unregisterTap( Tap tap )
{
switch( tap.getName() )
{
case TAP_CIC:
mCICDecimator.removeListener();
break;
case TAP_F1_F2:
mHBFilter1.setListener( mHBFilter2 );
break;
case TAP_F2_F3:
mHBFilter2.setListener( mHBFilter3 );
break;
case TAP_F3_F4:
mHBFilter3.setListener( mHBFilter4 );
break;
case TAP_F4_F5:
mHBFilter4.setListener( mHBFilter5 );
break;
case TAP_F5_F6:
mHBFilter5.setListener( mHBFilter6 );
break;
case TAP_F6_DT:
mHBFilter6.setListener( mDCFilter );
break;
case TAP_DT_SF1:
mDCFilter.setListener( mSquaringFilter );
break;
case TAP_SF1_PS:
mSquaringFilter.setListener( mPulseShaper );
break;
case TAP_PS_SLICER:
mPulseShaper.setListener( mSlicer );
break;
case TAP_SYMBOL_EVENT:
mSlicer.removeTap();
break;
}
}
}